Skip to content

Build, Release and Announce #590

Build, Release and Announce

Build, Release and Announce #590

name: "Build, Release and Announce"
permissions:
contents: write
on:
push:
branches:
- main
workflow_dispatch:
# טריגר לפרסום בלבד
release:
types: [published, prereleased]
jobs:
build_windows:
if: github.event_name != 'release' && github.event_name != 'pull_request'
runs-on: windows-2022
# env:
# RUSTUP_HOME: ${{ github.workspace }}\.rustup
# CARGO_HOME: ${{ github.workspace }}\.cargo
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Create Google Calendar Credentials
shell: pwsh
run: |
$clientId = '${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}'
$clientSecret = '${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}'
if ([string]::IsNullOrWhiteSpace($clientId) -or [string]::IsNullOrWhiteSpace($clientSecret)) {
throw "Google Calendar secrets are missing. Please set GOOGLE_CALENDAR_CLIENT_ID and GOOGLE_CALENDAR_CLIENT_SECRET."
}
$credentialsContent = @"
class GoogleCalendarCredentials {
static const String clientId = '$clientId';
static const String clientSecret = '$clientSecret';
}
"@
$credentialsContent | Out-File -FilePath "lib/tools/calendar/services/google_calendar_credentials.dart" -Encoding UTF8
Write-Host "Google Calendar credentials file created"
# - name: Install Rust (stable)
# uses: dtolnay/rust-toolchain@stable
# - name: Cache cargo registry and git
# uses: actions/cache@v4
# with:
# path: |
# ${{ env.CARGO_HOME }}\registry
# ${{ env.CARGO_HOME }}\git
# key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# restore-keys: |
# ${{ runner.os }}-cargo-
# - name: Add cargo to PATH & verify toolchain
# shell: pwsh
# run: |
# "$env:CARGO_HOME\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
# rustc -Vv
# cargo -V
# rustup show
- name: Install Inno Setup (project script + fallback)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (Test-Path .\installer\install_inno_setup.ps1) {
Write-Host "Running local installer script..."
.\installer\install_inno_setup.ps1
} else {
Write-Host "Local installer script not found, skipping."
}
if (-not (Get-Command iscc -ErrorAction SilentlyContinue)) {
if (Get-Command winget -ErrorAction SilentlyContinue) {
Write-Host "Installing Inno Setup via winget..."
winget install -e --id JRSoftware.InnoSetup --silent --accept-package-agreements --accept-source-agreements
} else {
Write-Host "winget not found, installing via Chocolatey..."
choco install innosetup -y --no-progress
}
}
$iscc = (Get-ChildItem "C:\Program Files*\Inno Setup*\ISCC.exe" -Recurse -ErrorAction SilentlyContinue |
Select-Object -First 1 -ExpandProperty FullName)
if (-not $iscc) { throw "ISCC.exe not found after installation" }
"ISCC=$iscc" | Out-File -FilePath $env:GITHUB_ENV -Append
Write-Host "ISCC resolved to: $iscc"
- name: Prepare Flutter dependencies for Windows release
shell: pwsh
run: |
flutter pub get
# Prevent bundling the PDFium WASM assets into non-Web release builds.
dart run pdfrx:remove_wasm_modules
- name: Build Flutter Windows app
shell: pwsh
run: |
flutter build windows --release
- name: Zip Windows build
shell: pwsh
run: |
$relDir = Get-ChildItem -Directory build\windows |
Where-Object { Test-Path "$($_.FullName)\runner\Release" } |
Select-Object -First 1 -ExpandProperty FullName
if (-not $relDir) { throw 'Release directory not found' }
Compress-Archive -Path "$relDir\runner\Release\*" -DestinationPath otzaria-windows.zip
- name: Check if commit message is a version tag
id: check_version
shell: pwsh
run: |
$commitMessage = "${{ github.event.head_commit.message }}"
if ([string]::IsNullOrWhiteSpace($commitMessage)) {
Write-Host "Commit message not available in event payload; using checked-out HEAD commit message instead"
$commitMessage = git log -1 --pretty=%B
}
$commitMessage = $commitMessage.Split([Environment]::NewLine)[0].Trim()
Write-Host "Commit message: $commitMessage"
# בדיקה אם ההודעה היא גרסה במבנה X.Y.Z (מספרים בלבד עם נקודות)
if ($commitMessage -match '^\d+\.\d+\.\d+$') {
Write-Host "✓ Commit message is a version tag: $commitMessage"
echo "is_version=true" >> $env:GITHUB_OUTPUT
} else {
Write-Host "✗ Commit message is not a version tag"
echo "is_version=false" >> $env:GITHUB_OUTPUT
}
- name: Download library assets for full installer
if: true
shell: pwsh
run: |
Write-Host "Downloading bundled library assets for full installer..."
$ErrorActionPreference = "Stop"
try {
# הורדת מסד הספרייה הראשי
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/Otzaria/SeforimLibrary/releases/latest"
$dbAsset = $latestRelease.assets | Where-Object { $_.name -eq "seforim.db.zst" }
if (-not $dbAsset) {
Write-Host "::error::Could not find seforim.db.zst in latest release"
exit 1
}
Write-Host "Library version: $($latestRelease.tag_name)"
Write-Host "Downloading from: $($dbAsset.browser_download_url)"
Write-Host "Size: $([math]::Round($dbAsset.size / 1MB, 2)) MB"
if (Test-Path "installer\library_db") {
Remove-Item -Path "installer\library_db" -Recurse -Force
}
New-Item -ItemType Directory -Path "installer\library_db" -Force | Out-Null
# הורדת ה-DB הדחוס שה-installer יחלץ בזמן ההתקנה
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri $dbAsset.browser_download_url -OutFile "installer\library_db\seforim.db.zst" -UseBasicParsing
Write-Host "Compressed library DB downloaded successfully"
# הורדת מסד הקטלוגים החיצוני
Write-Host "Downloading external catalog DB for full installer from Otzaria/otzar-HB_catalog..."
$catalogRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/Otzaria/otzar-HB_catalog/releases/latest"
$catalogAsset = $catalogRelease.assets | Where-Object { $_.name -eq "otzar-HB_catalog.db.zst" }
if (-not $catalogAsset) {
Write-Host "::error::Could not find otzar-HB_catalog.db.zst in latest release"
exit 1
}
Write-Host "Catalog version: $($catalogRelease.tag_name)"
Write-Host "Downloading from: $($catalogAsset.browser_download_url)"
Write-Host "Size: $([math]::Round($catalogAsset.size / 1MB, 2)) MB"
Invoke-WebRequest -Uri $catalogAsset.browser_download_url -OutFile "installer\library_db\otzar-HB_catalog.db.zst" -UseBasicParsing
Write-Host "Compressed external catalog DB downloaded successfully"
# הורדת ארכיון ה-PDFים של תלמוד בבלי
$talmudArchiveUrl = "https://github.com/Otzaria/otzaria-library/releases/latest/download/talmud_bavli_latest.tar.zst"
Write-Host "Downloading bundled Talmud Bavli PDFs for full installer..."
Write-Host "Downloading from: $talmudArchiveUrl"
Invoke-WebRequest -Uri $talmudArchiveUrl -OutFile "installer\library_db\talmud_bavli_latest.tar.zst" -UseBasicParsing
Write-Host "Compressed Talmud Bavli PDFs downloaded successfully"
# הורדת כלי החילוץ כדי שה-installer יוכל לחלץ את קבצי הספרייה בזמן ההתקנה
$zstdRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/facebook/zstd/releases/latest"
$zstdAsset = $zstdRelease.assets | Where-Object { $_.name -like "zstd-*-win64.zip" } | Select-Object -First 1
$sevenZipRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/ip7z/7zip/releases/latest"
$sevenZipBootstrap = $sevenZipRelease.assets | Where-Object { $_.name -eq "7zr.exe" } | Select-Object -First 1
$sevenZipExtra = $sevenZipRelease.assets | Where-Object { $_.name -like "7z*-extra.7z" } | Select-Object -First 1
if (-not $zstdAsset) {
Write-Host "::error::Could not find win64 zstd asset in latest release"
exit 1
}
if ((-not $sevenZipBootstrap) -or (-not $sevenZipExtra)) {
Write-Host "::error::Could not find 7-Zip bootstrap assets in latest release"
exit 1
}
Write-Host "Downloading zstd from: $($zstdAsset.browser_download_url)"
Invoke-WebRequest -Uri $zstdAsset.browser_download_url -OutFile "installer\zstd-win64.zip" -UseBasicParsing
if (Test-Path "installer\zstd_tmp") {
Remove-Item -Path "installer\zstd_tmp" -Recurse -Force
}
Expand-Archive -Path "installer\zstd-win64.zip" -DestinationPath "installer\zstd_tmp" -Force
$zstdExe = Get-ChildItem -Path "installer\zstd_tmp" -Filter "zstd.exe" -Recurse | Select-Object -First 1 -ExpandProperty FullName
if (-not $zstdExe) {
Write-Host "::error::zstd.exe was not found inside downloaded archive"
exit 1
}
Copy-Item -Path $zstdExe -Destination "installer\zstd.exe" -Force
Write-Host "Downloading 7zr from: $($sevenZipBootstrap.browser_download_url)"
Invoke-WebRequest -Uri $sevenZipBootstrap.browser_download_url -OutFile "installer\7zr.exe" -UseBasicParsing
Write-Host "Downloading 7-Zip extra package from: $($sevenZipExtra.browser_download_url)"
Invoke-WebRequest -Uri $sevenZipExtra.browser_download_url -OutFile "installer\7zip-extra.7z" -UseBasicParsing
if (Test-Path "installer\7zip_tmp") {
Remove-Item -Path "installer\7zip_tmp" -Recurse -Force
}
& "installer\7zr.exe" x "installer\7zip-extra.7z" "-oinstaller\7zip_tmp" -y
if ($LASTEXITCODE -ne 0) {
Write-Host "::error::Failed to extract 7-Zip extra package"
exit 1
}
$sevenZipExe = Get-ChildItem -Path "installer\7zip_tmp" -Filter "7za.exe" -Recurse | Select-Object -First 1 -ExpandProperty FullName
if (-not $sevenZipExe) {
Write-Host "::error::7za.exe was not found inside 7-Zip extra package"
exit 1
}
Copy-Item -Path $sevenZipExe -Destination "installer\7za.exe" -Force
Remove-Item -Path "installer\zstd-win64.zip" -Force
Remove-Item -Path "installer\zstd_tmp" -Recurse -Force
Remove-Item -Path "installer\7zip-extra.7z" -Force
Remove-Item -Path "installer\7zip_tmp" -Recurse -Force
Remove-Item -Path "installer\7zr.exe" -Force
Write-Host "zstd.exe and 7za.exe prepared successfully"
}
catch {
Write-Host "::error::Failed to download library: $_"
exit 1
}
- name: Download Visual C++ Redistributable for full installer
if: true
shell: pwsh
run: |
Write-Host "Downloading Visual C++ Redistributable for full installer..."
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/abbodi1406/vcredist/releases/latest"
$asset = $latestRelease.assets | Where-Object { $_.name -eq "VisualCppRedist_AIO_x86_x64.exe" }
if (-not $asset) {
Write-Host "::error::Could not find VisualCppRedist_AIO_x86_x64.exe in latest release"
exit 1
}
Write-Host "Downloading from: $($asset.browser_download_url)"
Write-Host "Size: $([math]::Round($asset.size / 1MB, 2)) MB"
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile "installer\VisualCppRedist_AIO_x86_x64.exe"
Write-Host "Visual C++ Redistributable downloaded successfully"
- name: Build Inno Setup installer
shell: pwsh
run: |
Write-Host "Building FULL installer with library and VC++ Redistributable..."
& "$env:ISCC" installer\otzaria_full.iss
if ($LASTEXITCODE -ne 0) {
throw "FULL installer build failed"
}
# ניקוי קבצי הספרייה והחולץ לאחר בניית ה-FULL installer
Write-Host "Cleaning up bundled DB assets to free disk space..."
if (Test-Path "installer\library_db") {
Remove-Item -Path "installer\library_db" -Recurse -Force
}
if (Test-Path "installer\zstd.exe") {
Remove-Item -Path "installer\zstd.exe" -Force
}
if (Test-Path "installer\7za.exe") {
Remove-Item -Path "installer\7za.exe" -Force
}
if (Test-Path "installer\zstd_tmp") {
Remove-Item -Path "installer\zstd_tmp" -Recurse -Force
}
if (Test-Path "installer\zstd-win64.zip") {
Remove-Item -Path "installer\zstd-win64.zip" -Force
}
if (Test-Path "installer\7zip_tmp") {
Remove-Item -Path "installer\7zip_tmp" -Recurse -Force
}
if (Test-Path "installer\7zip-extra.7z") {
Remove-Item -Path "installer\7zip-extra.7z" -Force
}
if (Test-Path "installer\7zr.exe") {
Remove-Item -Path "installer\7zr.exe" -Force
}
Write-Host "Bundled DB assets cleaned up successfully"
Write-Host "Building REGULAR installer (without VC++ Redistributable)..."
& "$env:ISCC" installer\otzaria.iss
if ($LASTEXITCODE -ne 0) {
throw "Regular installer build failed"
}
- name: Upload Windows installer
uses: actions/upload-artifact@v4
with:
name: otzaria-windows-installer
path: installer/otzaria-*-windows*.exe
- name: Upload Windows ZIP
uses: actions/upload-artifact@v4
with:
name: otzaria-windows-zip
path: otzaria-windows.zip
build_linux:
if: github.event_name != 'release' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
env:
RUSTUP_HOME: ${{ github.workspace }}/.rustup
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Check if commit message is a version tag
id: check_version
shell: bash
run: |
COMMIT_MESSAGE='${{ github.event.head_commit.message }}'
if [ -z "$COMMIT_MESSAGE" ]; then
echo "Commit message not available in event payload; using checked-out HEAD commit message instead"
COMMIT_MESSAGE=$(git log -1 --pretty=%B)
fi
COMMIT_MESSAGE=$(printf '%s' "$COMMIT_MESSAGE" | head -n 1 | xargs)
echo "Commit message: $COMMIT_MESSAGE"
if printf '%s' "$COMMIT_MESSAGE" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "is_version=true" >> "$GITHUB_OUTPUT"
else
echo "is_version=false" >> "$GITHUB_OUTPUT"
fi
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Create Google Calendar Credentials
shell: bash
run: |
CLIENT_ID='${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}'
CLIENT_SECRET='${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}'
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "::error::Google Calendar secrets are missing. Please set GOOGLE_CALENDAR_CLIENT_ID and GOOGLE_CALENDAR_CLIENT_SECRET."
exit 1
fi
cat > lib/tools/calendar/services/google_calendar_credentials.dart << EOF
class GoogleCalendarCredentials {
static const String clientId = '$CLIENT_ID';
static const String clientSecret = '$CLIENT_SECRET';
}
EOF
echo "Google Calendar credentials file created"
- name: Install Rust (stable) with rustup
uses: dtolnay/rust-toolchain@stable
- name: Add cargo to PATH and verify toolchain
run: |
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
rustc -Vv
cargo -V
rustup show
- name: Cache cargo registry and git
uses: actions/cache@v4
with:
path: |
${{ env.CARGO_HOME }}/registry
${{ env.CARGO_HOME }}/git
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install Linux build dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y \
build-essential ninja-build libgtk-3-dev libblkid1 liblzma5 \
clang cmake git pkg-config liblzma-dev rpm patchelf curl xz-utils zstd \
libcurl4-openssl-dev
# Fallback אם מסיבה כלשהי cargo לא קיים (למשל כשל זמני ב-rustup)
if ! command -v cargo >/dev/null 2>&1; then
sudo apt-get install -y rustc cargo
fi
- name: Download library assets for Linux FULL bundle
if: true
shell: bash
run: |
set -euo pipefail
mkdir -p full_installer/library_db
curl -L --fail https://github.com/Otzaria/SeforimLibrary/releases/latest/download/seforim.db.zst -o full_installer/library_db/seforim.db.zst
curl -L --fail https://github.com/Otzaria/otzar-HB_catalog/releases/latest/download/otzar-HB_catalog.db.zst -o full_installer/library_db/otzar-HB_catalog.db.zst
curl -L --fail https://github.com/Otzaria/otzaria-library/releases/latest/download/talmud_bavli_latest.tar.zst -o full_installer/library_db/talmud_bavli_latest.tar.zst
- name: Install flutter_distributor
run: dart pub global activate flutter_distributor
- name: Prepare Flutter dependencies for Linux release
shell: bash
run: |
set -euo pipefail
flutter pub get
# Prevent bundling the PDFium WASM assets into non-Web release builds.
dart run pdfrx:remove_wasm_modules
- name: Build Flutter Linux app (with retry)
run: |
set -e
tries=0
until [ $tries -ge 3 ]
do
if flutter build linux; then
break
fi
tries=$((tries+1))
echo "flutter build linux failed. retry $tries/3 after short sleep..."
sleep 15
done
if [ $tries -ge 3 ]; then
echo "::error::flutter build linux failed after 3 attempts"
exit 1
fi
- name: Build and Patch Linux DEB package
shell: bash
run: |
set -e
# 1. הרץ את פקודת הבנייה, אבל תפוס את הכישלון כדי שנוכל לתקן אחריו
echo "Attempting to build with flutter_distributor, it might fail..."
flutter_distributor release --name=production --jobs=release-linux-deb || true
# 2. מצא את קובץ ה-control שנוצר. אם לא נמצא, זה כישלון אמיתי
CONTROL_FILE=$(find dist -type f -name "control" | head -n 1)
if [ -z "$CONTROL_FILE" ]; then
echo "::error::Fatal: Could not find the DEBIAN/control file. The distributor failed before creating it."
exit 1
fi
echo "Found control file to patch at: $CONTROL_FILE"
echo "--- Control file BEFORE fix ---"
cat "$CONTROL_FILE"
# 3. תקן את כל הבעיות האפשריות בקובץ
# המרת סיומות שורה (CRLF -> LF)
sed -i 's/\r$//' "$CONTROL_FILE"
# הסרת שורת Depends ריקה
sed -i '/^Depends:\s*$/d' "$CONTROL_FILE"
# ודא שיש שורת סיום (newline) בסוף הקובץ
if [ -n "$(tail -c1 "$CONTROL_FILE")" ]; then
echo >> "$CONTROL_FILE"
fi
echo "--- Control file AFTER fix ---"
cat "$CONTROL_FILE"
# 4. בנה את חבילת ה-deb מחדש מהקבצים המתוקנים
DEB_DIR=$(dirname "$CONTROL_FILE" | xargs dirname)
DEB_OUTPUT_FILE="dist/$(basename "$DEB_DIR" | sed 's/-linux_deb$//')-linux.deb"
echo "Re-packaging from directory: $DEB_DIR into $DEB_OUTPUT_FILE"
dpkg-deb --build --root-owner-group "$DEB_DIR" "$DEB_OUTPUT_FILE"
echo "Successfully patched and rebuilt the .deb package."
- name: Build and Patch Linux RPM package
shell: bash
run: |
set -e
# 1. הרץ את פקודת הבנייה הבעייתית, ותפוס את הכישלון
echo "Attempting to build with flutter_distributor, expecting it to fail..."
flutter_distributor release --name=production --jobs=release-linux-rpm || true
# 2. מצא את קובץ ה-spec הפגום שהכלי יצר
SPEC_FILE=$(find dist -type f -name "*.spec" | head -n 1)
if [ -z "$SPEC_FILE" ]; then
echo "::error::Fatal: Could not find the .spec file to patch."
exit 1
fi
echo "Found spec file to patch at: $SPEC_FILE"
echo "--- Spec file BEFORE fix ---"
cat "$SPEC_FILE"
# 3. תקן את הקובץ: מחק את שורת ה-Requires הריקה
sed -i '/^Requires:\s*$/d' "$SPEC_FILE"
echo "--- Spec file AFTER fix ---"
cat "$SPEC_FILE"
# 4. בנה את חבילת ה-rpm מחדש, תוך שימוש בנתיב מוחלט
RPM_TOP_DIR_REL=$(dirname "$SPEC_FILE" | xargs dirname)
RPM_TOP_DIR_ABS="$(pwd)/$RPM_TOP_DIR_REL"
echo "Re-packaging using absolute top directory: $RPM_TOP_DIR_ABS"
rpmbuild --define "_topdir $RPM_TOP_DIR_ABS" -bb "$SPEC_FILE"
echo "Successfully patched and rebuilt the .rpm package."
- name: Find and prepare Linux packages
id: find_packages
run: |
DEB_FILE=$(find dist -maxdepth 1 -name "*-linux.deb" | head -n 1)
if [ -n "$DEB_FILE" ]; then
echo "deb_file=$DEB_FILE" >> $GITHUB_OUTPUT
echo "deb_name=$(basename $DEB_FILE)" >> $GITHUB_OUTPUT
fi
RPM_FILE=$(find dist -type f -name "*.rpm" | head -n 1)
if [ -n "$RPM_FILE" ]; then
echo "rpm_file=$RPM_FILE" >> $GITHUB_OUTPUT
echo "rpm_name=$(basename $RPM_FILE)" >> $GITHUB_OUTPUT
fi
mkdir -p linux-build
cp -r build/linux/x64/release/bundle/* linux-build/
- name: Upload Linux DEB package
if: steps.find_packages.outputs.deb_file != ''
uses: actions/upload-artifact@v4
with:
name: otzaria-linux-deb
path: ${{ steps.find_packages.outputs.deb_file }}
- name: Upload Linux RPM package
if: steps.find_packages.outputs.rpm_file != ''
uses: actions/upload-artifact@v4
with:
name: otzaria-linux-rpm
path: ${{ steps.find_packages.outputs.rpm_file }}
- name: Upload linux build (raw)
uses: actions/upload-artifact@v4
with:
name: otzaria-linux-raw
path: linux-build/*
- name: Create Linux FULL portable bundle
if: true
shell: bash
run: |
set -euo pipefail
BUNDLE_ROOT="full_installer/otzaria-linux-full"
rm -rf "$BUNDLE_ROOT"
mkdir -p "$BUNDLE_ROOT/app" "$BUNDLE_ROOT/אוצריא"
cp -r linux-build/. "$BUNDLE_ROOT/app/"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/seforim.db.zst > "$BUNDLE_ROOT/אוצריא/seforim.db"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/otzar-HB_catalog.db.zst > "$BUNDLE_ROOT/אוצריא/otzar-HB_catalog.db"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/talmud_bavli_latest.tar.zst | tar -xf - -C "$BUNDLE_ROOT/אוצריא"
cat > "$BUNDLE_ROOT/run-otzaria.sh" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
exec ./app/otzaria
EOF
chmod +x "$BUNDLE_ROOT/run-otzaria.sh"
cat > "$BUNDLE_ROOT/README.txt" << 'EOF'
Otzaria Linux FULL bundle
This portable bundle includes the app and the bundled library.
Start the app using ./run-otzaria.sh so the app can detect the bundled library folder.
EOF
tar -czf otzaria-linux-full.tar.gz -C full_installer otzaria-linux-full
- name: Upload Linux FULL bundle
if: true
uses: actions/upload-artifact@v4
with:
name: otzaria-linux-full
path: otzaria-linux-full.tar.gz
build_android:
if: github.event_name != 'release' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
env:
RUSTUP_HOME: ${{ github.workspace }}/.rustup
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Check if commit message is a version tag
id: check_version
shell: bash
run: |
COMMIT_MESSAGE='${{ github.event.head_commit.message }}'
if [ -z "$COMMIT_MESSAGE" ]; then
echo "Commit message not available in event payload; using checked-out HEAD commit message instead"
COMMIT_MESSAGE=$(git log -1 --pretty=%B)
fi
COMMIT_MESSAGE=$(printf '%s' "$COMMIT_MESSAGE" | head -n 1 | xargs)
echo "Commit message: $COMMIT_MESSAGE"
if printf '%s' "$COMMIT_MESSAGE" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "is_version=true" >> "$GITHUB_OUTPUT"
else
echo "is_version=false" >> "$GITHUB_OUTPUT"
fi
- name: Free up disk space
run: |
echo "Disk space before cleanup:"
df -h
# מחיקת קבצים מיותרים כדי לפנות מקום
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
echo "Disk space after cleanup:"
df -h
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Add cargo to PATH & verify toolchain
run: |
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
rustc -Vv
cargo -V
rustup show
- name: Cache Rust
uses: actions/cache@v4
with:
path: |
${{ env.CARGO_HOME }}/registry
${{ env.CARGO_HOME }}/git
${{ env.RUSTUP_HOME }}
key: ${{ runner.os }}-rust-android-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-rust-android-
- name: Pre-install Android Rust targets (with retry)
run: |
set -e
# פונקציה להתקנת target עם retry
install_target_with_retry() {
local target=$1
local max_attempts=3
local attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Installing $target (attempt $attempt/$max_attempts)..."
if rustup target add --toolchain stable "$target"; then
echo "✓ Successfully installed $target"
return 0
fi
echo "Failed to install $target, cleaning up..."
# ניקוי קבצים זמניים שעלולים לגרום לבעיה
rm -rf ~/.rustup/downloads/*.partial || true
rm -rf ~/.rustup/tmp/* || true
attempt=$((attempt + 1))
if [ $attempt -le $max_attempts ]; then
echo "Waiting 10 seconds before retry..."
sleep 10
fi
done
echo "::error::Failed to install $target after $max_attempts attempts"
return 1
}
# התקנת כל ה-targets הנדרשים
install_target_with_retry "armv7-linux-androideabi"
install_target_with_retry "aarch64-linux-android"
install_target_with_retry "x86_64-linux-android"
install_target_with_retry "i686-linux-android"
echo "All Android Rust targets installed successfully"
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Pub
uses: actions/cache@v4
with:
path: |
~/.pub-cache
key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pub-
- name: Create Google Calendar Credentials
shell: bash
run: |
CLIENT_ID='${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}'
CLIENT_SECRET='${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}'
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "::error::Google Calendar secrets are missing. Please set GOOGLE_CALENDAR_CLIENT_ID and GOOGLE_CALENDAR_CLIENT_SECRET."
exit 1
fi
cat > lib/tools/calendar/services/google_calendar_credentials.dart << EOF
class GoogleCalendarCredentials {
static const String clientId = '$CLIENT_ID';
static const String clientSecret = '$CLIENT_SECRET';
}
EOF
echo "Google Calendar credentials file created"
- name: Setup Android signing
shell: bash
env:
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android/app/keystore/otzaria-keystore.jks
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
set -e
mkdir -p android/app/keystore
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore/otzaria-keystore.jks
chmod 600 android/app/keystore/otzaria-keystore.jks
# Verify keystore file exists
echo "Verifying keystore setup..."
ls -la android/app/keystore/
if [ ! -f android/app/keystore/otzaria-keystore.jks ]; then
echo "::error::Keystore file was not created!"
exit 1
fi
echo "✓ Keystore file exists at: android/app/keystore/otzaria-keystore.jks"
echo "✓ Environment variables set for Gradle signing"
- run: sudo apt install ninja-build
- name: Gradle performance flags
run: |
mkdir -p ~/.gradle
cat >> ~/.gradle/gradle.properties << 'EOF'
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
kotlin.incremental=true
EOF
- name: Ensure Android SDK Platform 36
run: |
set -e
if [ ! -d "${ANDROID_SDK_ROOT}/platforms/android-36" ]; then
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-36"
fi
if [ ! -d "${ANDROID_SDK_ROOT}/build-tools/36.0.0" ]; then
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "build-tools;36.0.0"
fi
- name: Download library assets for Android FULL bundle
if: true
shell: bash
run: |
set -euo pipefail
mkdir -p full_installer/library_db
curl -L --fail https://github.com/Otzaria/SeforimLibrary/releases/latest/download/seforim.db.zst -o full_installer/library_db/seforim.db.zst
curl -L --fail https://github.com/Otzaria/otzar-HB_catalog/releases/latest/download/otzar-HB_catalog.db.zst -o full_installer/library_db/otzar-HB_catalog.db.zst
curl -L --fail https://github.com/Otzaria/otzaria-library/releases/latest/download/talmud_bavli_latest.tar.zst -o full_installer/library_db/talmud_bavli_latest.tar.zst
- name: Build Android APK
env:
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android/app/keystore/otzaria-keystore.jks
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
flutter pub get
# Prevent bundling the PDFium WASM assets into non-Web release builds.
dart run pdfrx:remove_wasm_modules
flutter build apk
# ניקוי מיידי של קבצים מיותרים מהבנייה (לא ה-APK הסופי)
rm -rf build/app/intermediates || true
rm -rf build/app/.transforms || true
echo "Disk space after intermediate cleanup:"
df -h
- name: Upload apk
uses: actions/upload-artifact@v4
with:
name: otzaria-android.apk
path: build/app/outputs/flutter-apk/app-release.apk
- name: Create Android FULL bundle
if: true
shell: bash
run: |
set -euo pipefail
BUNDLE_ROOT="full_installer/otzaria-android-full"
rm -rf "$BUNDLE_ROOT"
mkdir -p "$BUNDLE_ROOT/library_db"
cp build/app/outputs/flutter-apk/app-release.apk "$BUNDLE_ROOT/otzaria-android.apk"
cp full_installer/library_db/* "$BUNDLE_ROOT/library_db/"
cat > "$BUNDLE_ROOT/README.txt" << 'EOF'
Otzaria Android FULL bundle
This bundle includes the APK and the offline library payload.
The APK itself cannot preinstall external library files automatically.
Use this bundle when distributing the APK together with the library payload.
EOF
cd full_installer
zip -r ../otzaria-android-full.zip otzaria-android-full
- name: Upload Android FULL bundle
if: true
uses: actions/upload-artifact@v4
with:
name: otzaria-android-full
path: otzaria-android-full.zip
- name: Final cleanup
if: always()
run: |
# ניקוי סופי של כל קבצי build וcache (נשמר Cache לריצות הבאות)
rm -rf build || true
echo "Disk space after final cleanup:"
df -h
build_macos:
if: github.event_name != 'release' && github.event_name != 'pull_request'
runs-on: macos-latest
env:
# אחסון יציב בתוך ה-workspace כדי למנוע תקלות rename של rustup
RUSTUP_HOME: ${{ github.workspace }}/.rustup
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Check if commit message is a version tag
id: check_version
shell: bash
run: |
COMMIT_MESSAGE='${{ github.event.head_commit.message }}'
if [ -z "$COMMIT_MESSAGE" ]; then
echo "Commit message not available in event payload; using checked-out HEAD commit message instead"
COMMIT_MESSAGE=$(git log -1 --pretty=%B)
fi
COMMIT_MESSAGE=$(printf '%s' "$COMMIT_MESSAGE" | head -n 1 | xargs)
echo "Commit message: $COMMIT_MESSAGE"
if printf '%s' "$COMMIT_MESSAGE" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "is_version=true" >> "$GITHUB_OUTPUT"
else
echo "is_version=false" >> "$GITHUB_OUTPUT"
fi
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Create Google Calendar Credentials
shell: bash
run: |
CLIENT_ID='${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}'
CLIENT_SECRET='${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}'
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "::error::Google Calendar secrets are missing. Please set GOOGLE_CALENDAR_CLIENT_ID and GOOGLE_CALENDAR_CLIENT_SECRET."
exit 1
fi
cat > lib/tools/calendar/services/google_calendar_credentials.dart << EOF
class GoogleCalendarCredentials {
static const String clientId = '$CLIENT_ID';
static const String clientSecret = '$CLIENT_SECRET';
}
EOF
echo "Google Calendar credentials file created"
# התקנת Rust יציבה לפני flutter build
- name: Install Rust (stable)
# מחליף את ההתקנה האוטומטית של cargokit
uses: dtolnay/rust-toolchain@stable
- name: Add cargo to PATH & verify toolchain
run: |
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
rustc -Vv
cargo -V
rustup show
# (רשות אבל מומלץ) קאשינג לספריות cargo כדי לזרז בניות
- name: Cache cargo registry and git
uses: actions/cache@v4
with:
path: |
${{ env.CARGO_HOME }}/registry
${{ env.CARGO_HOME }}/git
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Prepare Flutter dependencies for macOS release
run: |
flutter pub get
# Prevent bundling the PDFium WASM assets into non-Web release builds.
dart run pdfrx:remove_wasm_modules
- name: Build Flutter macOS app (Universal Binary)
run: |
# Build Universal Binary that supports both Intel (x86_64) and Apple Silicon (arm64)
flutter build macos --release
- name: Download library assets for macOS FULL bundle
if: true
shell: bash
run: |
set -euo pipefail
mkdir -p full_installer/library_db
curl -L --fail https://github.com/Otzaria/SeforimLibrary/releases/latest/download/seforim.db.zst -o full_installer/library_db/seforim.db.zst
curl -L --fail https://github.com/Otzaria/otzar-HB_catalog/releases/latest/download/otzar-HB_catalog.db.zst -o full_installer/library_db/otzar-HB_catalog.db.zst
curl -L --fail https://github.com/Otzaria/otzaria-library/releases/latest/download/talmud_bavli_latest.tar.zst -o full_installer/library_db/talmud_bavli_latest.tar.zst
- name: Verify Universal Binary
run: |
APP_PATH="build/macos/Build/Products/Release/otzaria.app/Contents/MacOS/otzaria"
if [ -f "$APP_PATH" ]; then
echo "Checking architectures in binary:"
lipo -info "$APP_PATH" || file "$APP_PATH"
else
echo "Warning: Binary not found at expected path"
find build/macos/Build/Products/Release -name "otzaria" -o -name "*.app"
fi
- name: Create DMG installer
run: |
# Install create-dmg if not available
if ! command -v create-dmg &> /dev/null; then
brew install create-dmg
fi
APP_NAME="otzaria"
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
DMG_NAME="${APP_NAME}-macos.dmg"
# Create DMG with drag-to-Applications shortcut
create-dmg \
--volname "Otzaria Installer" \
--volicon "${APP_PATH}/Contents/Resources/AppIcon.icns" \
--window-pos 200 120 \
--window-size 600 400 \
--icon-size 100 \
--icon "${APP_NAME}.app" 175 190 \
--hide-extension "${APP_NAME}.app" \
--app-drop-link 425 190 \
--no-internet-enable \
"${DMG_NAME}" \
"${APP_PATH}" \
|| hdiutil create -volname "Otzaria" -srcfolder "${APP_PATH}" -ov -format UDZO "${DMG_NAME}"
- name: Create macOS FULL portable bundle
if: true
shell: bash
run: |
set -euo pipefail
if ! command -v zstd &> /dev/null; then
brew install zstd
fi
BUNDLE_ROOT="full_installer/otzaria-macos-full"
rm -rf "$BUNDLE_ROOT"
mkdir -p "$BUNDLE_ROOT/אוצריא"
cp -R build/macos/Build/Products/Release/otzaria.app "$BUNDLE_ROOT/otzaria.app"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/seforim.db.zst > "$BUNDLE_ROOT/אוצריא/seforim.db"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/otzar-HB_catalog.db.zst > "$BUNDLE_ROOT/אוצריא/otzar-HB_catalog.db"
zstd -d -c --long=31 --memory=2048MB full_installer/library_db/talmud_bavli_latest.tar.zst | tar -xf - -C "$BUNDLE_ROOT/אוצריא"
cat > "$BUNDLE_ROOT/Run Otzaria.command" << 'EOF'
#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")"
exec "./otzaria.app/Contents/MacOS/otzaria"
EOF
chmod +x "$BUNDLE_ROOT/Run Otzaria.command"
cat > "$BUNDLE_ROOT/README.txt" << 'EOF'
Otzaria macOS FULL bundle
This portable bundle includes the app and the bundled library.
Start the app using Run Otzaria.command so the app can detect the bundled library folder.
EOF
ditto -c -k --sequesterRsrc --keepParent "$BUNDLE_ROOT" otzaria-macos-full.zip
- name: Upload macOS DMG
if: always()
uses: actions/upload-artifact@v4
with:
name: otzaria-macos.dmg
path: otzaria-macos.dmg
- name: Upload macOS FULL bundle
if: true
uses: actions/upload-artifact@v4
with:
name: otzaria-macos-full
path: otzaria-macos-full.zip
create_release:
if: "!cancelled() && github.event_name != 'release' && github.event_name != 'pull_request'"
needs: [build_windows, build_linux, build_android, build_macos]
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Get version from pubspec.yaml
id: version
run: |
VERSION=$(grep '^version:' pubspec.yaml | cut -d ' ' -f 2 | cut -d '+' -f 1)
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Validate semver format
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'; then
echo "::error::Version '$VERSION' is not valid semver format"
exit 1
fi
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "tag=$VERSION" >> $GITHUB_OUTPUT
echo "prerelease=false" >> $GITHUB_OUTPUT
echo "title=Otzaria $VERSION" >> $GITHUB_OUTPUT
else
# Use proper semver pre-release format with dots instead of hyphens
BRANCH_NAME="${{ github.ref_name }}"
# Replace any invalid characters in branch name for semver
CLEAN_BRANCH=$(echo "$BRANCH_NAME" | sed 's/[^A-Za-z0-9.-]/-/g')
echo "tag=$VERSION+${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "prerelease=true" >> $GITHUB_OUTPUT
echo "title=Otzaria $VERSION (Preview from ${{ github.ref_name }})" >> $GITHUB_OUTPUT
fi
- name: Get commit message
id: commit
run: echo "message=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT
- name: Organize release files
run: |
mkdir -p release-files
# Windows installer
if [ -d "artifacts/otzaria-windows-installer" ]; then
cp artifacts/otzaria-windows-installer/*.exe release-files/ || true
fi
# Windows ZIP
if [ -d "artifacts/otzaria-windows-zip" ]; then
cp artifacts/otzaria-windows-zip/*.zip release-files/ || true
fi
# Linux DEB package
if [ -d "artifacts/otzaria-linux-deb" ]; then
cp artifacts/otzaria-linux-deb/*.deb release-files/ || true
fi
# Linux RPM package
if [ -d "artifacts/otzaria-linux-rpm" ]; then
cp artifacts/otzaria-linux-rpm/*.rpm release-files/ || true
fi
# Linux raw build (fallback)
if [ -d "artifacts/otzaria-linux-raw" ] && [ ! -f release-files/*.deb ]; then
cd artifacts/otzaria-linux-raw
zip -r ../../release-files/otzaria-linux-raw.zip *
cd ../..
fi
# Linux FULL bundle
if [ -d "artifacts/otzaria-linux-full" ]; then
cp artifacts/otzaria-linux-full/*.tar.gz release-files/ || true
fi
# Android APK
if [ -d "artifacts/otzaria-android.apk" ]; then
cp artifacts/otzaria-android.apk/*.apk release-files/ || true
fi
# Android FULL bundle
if [ -d "artifacts/otzaria-android-full" ]; then
cp artifacts/otzaria-android-full/*.zip release-files/ || true
fi
# macOS DMG
if [ -d "artifacts/otzaria-macos.dmg" ]; then
cp artifacts/otzaria-macos.dmg/*.dmg release-files/ || true
fi
# macOS FULL bundle
if [ -d "artifacts/otzaria-macos-full" ]; then
cp artifacts/otzaria-macos-full/*.zip release-files/ || true
fi
# List files for debugging
echo "Release files:"
ls -la release-files/
- name: Generate release notes
env:
RELEASE_TITLE: ${{ steps.version.outputs.title }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
COMMIT_MESSAGE: ${{ steps.commit.outputs.message }}
run: |
set -euo pipefail
shopt -s nullglob
RELEASE_URL="https://github.com/${{ github.repository }}/releases/download/${RELEASE_TAG}"
windows_installers=()
windows_portable=()
windows_full=()
android_apk=()
android_full=()
linux_deb=()
linux_rpm=()
linux_portable=()
linux_full=()
mac_dmg=()
mac_full=()
emit_link_group() {
local label="$1"
shift
for file in "$@"; do
printf -- "- [%s](%s/%s) - \`%s\`\n" "$label" "$RELEASE_URL" "$file" "$file" >> release-body.md
done
}
for path in release-files/*; do
[ -f "$path" ] || continue
file=$(basename "$path")
lower=$(printf '%s' "$file" | tr '[:upper:]' '[:lower:]')
case "$lower" in
*windows-full*.exe)
windows_full+=("$file")
;;
*windows*.exe)
windows_installers+=("$file")
;;
*windows*.zip)
windows_portable+=("$file")
;;
*android-full*.zip)
android_full+=("$file")
;;
*.apk)
android_apk+=("$file")
;;
*linux-full*.tar.gz|*linux-full*.tar.*)
linux_full+=("$file")
;;
*.deb)
linux_deb+=("$file")
;;
*.rpm)
linux_rpm+=("$file")
;;
*linux-raw*.zip|*linux*.zip)
linux_portable+=("$file")
;;
*.dmg)
mac_dmg+=("$file")
;;
*macos-full*.zip)
mac_full+=("$file")
;;
*)
echo "Ignoring non-user-facing release file in notes: $file"
;;
esac
done
safe_commit_message=$(printf '%s' "$COMMIT_MESSAGE" \
| tr '\r\n' ' ' \
| sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' \
| sed \
-e 's/\\/\\\\/g' \
-e 's/`/\\`/g' \
-e 's/\*/\\*/g' \
-e 's/_/\\_/g' \
-e 's/\[/\\[/g' \
-e 's/\]/\\]/g')
cat > release-body.md <<EOF
# ${RELEASE_TITLE}
> **מה להוריד?**
>
> אם אתם רק מעדכנים את האפליקציה, ברוב המקרים תרצו את **גרסת תוכנה בלבד**.
>
> אם אתם צריכים חבילה להפצה או להתקנה לא־מקוונת, בחרו **גרסת ספרייה מלאה (FULL)**.
>
> קובצי הספרייה זמינים גם בנפרד כאן:
> [seforim.db.zst](https://github.com/Otzaria/SeforimLibrary/releases/latest/download/seforim.db.zst),
> [otzar-HB_catalog.db.zst](https://github.com/Otzaria/otzar-HB_catalog/releases/latest/download/otzar-HB_catalog.db.zst)
>
> קובצי ה־PDF של תלמוד בבלי זמינים גם בנפרד כאן:
> [talmud_bavli_latest.tar.zst](https://github.com/Otzaria/otzaria-library/releases/latest/download/talmud_bavli_latest.tar.zst)
EOF
if [ ${#windows_installers[@]} -gt 0 ] || [ ${#windows_portable[@]} -gt 0 ] || [ ${#windows_full[@]} -gt 0 ]; then
printf "## ווינדוס\n\n" >> release-body.md
if [ ${#windows_installers[@]} -gt 0 ] || [ ${#windows_portable[@]} -gt 0 ]; then
printf "### גרסת תוכנה בלבד\n\n" >> release-body.md
emit_link_group "מתקין לווינדוס" "${windows_installers[@]}"
emit_link_group "גרסה ניידת לווינדוס" "${windows_portable[@]}"
printf "\n" >> release-body.md
fi
if [ ${#windows_full[@]} -gt 0 ]; then
printf "### גרסת ספרייה מלאה\n\n" >> release-body.md
emit_link_group "חבילה מלאה לווינדוס" "${windows_full[@]}"
printf "\n" >> release-body.md
fi
fi
if [ ${#android_apk[@]} -gt 0 ] || [ ${#android_full[@]} -gt 0 ]; then
printf "## אנדרואיד\n\n" >> release-body.md
if [ ${#android_apk[@]} -gt 0 ]; then
printf "### גרסת תוכנה בלבד\n\n" >> release-body.md
emit_link_group "APK לאנדרואיד" "${android_apk[@]}"
printf "\n" >> release-body.md
fi
if [ ${#android_full[@]} -gt 0 ]; then
printf "### גרסת ספרייה מלאה\n\n" >> release-body.md
emit_link_group "חבילה מלאה לאנדרואיד" "${android_full[@]}"
printf "\n> **הבהרה:** חבילת ה־FULL לאנדרואיד כוללת את ה־APK יחד עם חומרי הספרייה, ומיועדת להפצה או להתקנה ידנית.\n\n" >> release-body.md
fi
fi
if [ ${#linux_deb[@]} -gt 0 ] || [ ${#linux_rpm[@]} -gt 0 ] || [ ${#linux_portable[@]} -gt 0 ] || [ ${#linux_full[@]} -gt 0 ]; then
printf "## לינוקס\n\n" >> release-body.md
if [ ${#linux_deb[@]} -gt 0 ] || [ ${#linux_rpm[@]} -gt 0 ] || [ ${#linux_portable[@]} -gt 0 ]; then
printf "### גרסת תוכנה בלבד\n\n" >> release-body.md
emit_link_group "חבילת DEB ללינוקס" "${linux_deb[@]}"
emit_link_group "חבילת RPM ללינוקס" "${linux_rpm[@]}"
emit_link_group "גרסה ניידת ללינוקס" "${linux_portable[@]}"
printf "\n" >> release-body.md
fi
if [ ${#linux_full[@]} -gt 0 ]; then
printf "### גרסת ספרייה מלאה\n\n" >> release-body.md
emit_link_group "חבילה מלאה ללינוקס" "${linux_full[@]}"
printf "\n" >> release-body.md
fi
fi
if [ ${#mac_dmg[@]} -gt 0 ] || [ ${#mac_full[@]} -gt 0 ]; then
printf "## מק\n\n" >> release-body.md
if [ ${#mac_dmg[@]} -gt 0 ]; then
printf "### גרסת תוכנה בלבד\n\n" >> release-body.md
emit_link_group "מתקין DMG למק" "${mac_dmg[@]}"
printf "\n" >> release-body.md
fi
if [ ${#mac_full[@]} -gt 0 ]; then
printf "### גרסת ספרייה מלאה\n\n" >> release-body.md
emit_link_group "חבילה מלאה למק" "${mac_full[@]}"
printf "\n" >> release-body.md
fi
fi
cat >> release-body.md <<EOF
<details>
<summary>מידע טכני</summary>
- נבנה מ־commit ${GITHUB_SHA::7}
- תיאור commit: ${safe_commit_message}
- ענף: ${{ github.ref_name }}
</details>
EOF
echo "Generated release notes:"
cat release-body.md
- name: Create Release
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.GH_RELEASE_TOKEN }}
tag_name: ${{ steps.version.outputs.tag }}
name: ${{ steps.version.outputs.title }}
prerelease: ${{ steps.version.outputs.prerelease }}
files: release-files/*
body_path: release-body.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --- הכרזה על גרסה שפורסמה ---
announce_release:
# תנאי לריצה רק כשיש אירוע של פרסום release
if: github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0 # חובה כדי לאפשר גישה לכל ההיסטוריה והתגים
fetch-tags: true
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies for webhook script
run: pip install bs4 requests pyluach
- name: Build commit list since previous release
id: changelog
shell: bash
run: |
set -euo pipefail
CURRENT_TAG="${{ github.event.release.tag_name }}"
git fetch --tags --force
# מצא את התג הקודם (הכי עדכני שהוא לא התג הנוכחי)
PREV_TAG=$(git for-each-ref --sort=-creatordate --format '%(refname:strip=2)' refs/tags | grep -v "^$CURRENT_TAG$" | head -n1 || true)
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..$CURRENT_TAG"
# צור רשימה של כותרות הcommits בטווח
COMMITS=$(git log --pretty=format:'- %s' "$RANGE" || true)
else
# אם אין תג קודם, קח רק את הcommit של התג הנוכחי
SHA=$(git rev-list -n 1 "$CURRENT_TAG")
COMMITS=$(git log --pretty=format:'- %s' "${SHA}^..${SHA}" || true)
fi
if [ -z "${COMMITS// /}" ]; then
COMMITS="(לא נמצאו כותרות commits חדשות להצגה)"
fi
# העבר את רשימת הcommits והתג הקודם לשלבים הבאים
{
echo "prev_tag=${PREV_TAG}"
echo "commits<<EOF"
echo "$COMMITS"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Run announcement script (Mitmachim + Yemot)
env:
# פרטי ה-Release נלקחים מאירוע ה-Webhook של GitHub
RELEASE_TAG: ${{ github.event.release.tag_name }}
RELEASE_NAME: ${{ github.event.release.name }}
RELEASE_URL: ${{ github.event.release.html_url }}
GITHUB_EVENT_PATH: ${{ github.event_path }} # חיוני כדי שהסקריפט ימצא את קבצי ההורדה
# הרכבת גוף ההודעה: התיאור מה-Release + רשימת הקומיטים שיצרנו
RELEASE_BODY: |
${{ github.event.release.body }}
השינויים מאז הגרסה הקודמת${{ steps.changelog.outputs.prev_tag && format(' ({0})', steps.changelog.outputs.prev_tag) || '' }}:
${{ steps.changelog.outputs.commits }}
# סודות להתחברות לשירותים החיצוניים
USER_NAME: ${{ secrets.USER_NAME }}
PASSWORD: ${{ secrets.PASSWORD }}
TOKEN_YEMOT: ${{ secrets.TOKEN_YEMOT }}
run: |
python webhooks/main.py