Fix #779: detail panel header padding regression from #755 #402
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and release OpenSAK | |
| # Runs on version tags e.g. v1.0.0 | |
| # Create release: git tag v1.0.0 && git push origin v1.0.0 | |
| # Manual trigger: GitHub → Actions → Run workflow | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| env: | |
| PYTHON_VERSION: "3.11" | |
| # Opt into Homebrew's upcoming trust enforcement (default in 5.2/6.0) so the | |
| # macOS jobs silently ignore the runners' pre-installed untrusted taps | |
| # (aws/tap, azure/bicep) instead of printing a migration notice. create-dmg | |
| # lives in the always-trusted core tap, so the build is unaffected. | |
| HOMEBREW_REQUIRE_TAP_TRUST: "1" | |
| jobs: | |
| # ============================================================ | |
| # Tests — single gate; every build job runs only after this | |
| # ============================================================ | |
| test: | |
| uses: ./.github/workflows/tests.yml | |
| # ============================================================ | |
| # Windows .exe | |
| # ============================================================ | |
| build-windows: | |
| name: "Build Windows .exe" | |
| runs-on: windows-latest | |
| needs: test | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e . pyinstaller | |
| - name: Fetch boundary baseline | |
| run: python scripts/fetch_boundary_baseline.py | |
| - name: Build .exe | |
| run: pyinstaller opensak.spec --clean --noconfirm | |
| env: | |
| PYTHONPATH: src | |
| - name: Create ZIP | |
| shell: powershell | |
| run: Compress-Archive -Path dist/OpenSAK -DestinationPath dist/OpenSAK-${{ github.ref_name }}-Windows.zip | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-Windows | |
| path: dist/OpenSAK-${{ github.ref_name }}-Windows.zip | |
| retention-days: 7 | |
| # ============================================================ | |
| # Linux AppImage | |
| # ============================================================ | |
| build-linux: | |
| name: "Build Linux AppImage" | |
| runs-on: ubuntu-22.04 | |
| needs: test | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install system packages | |
| run: | | |
| # Same mitigations as .github/actions/setup-qt/action.yml — | |
| # confirmed root cause (from a cancelled run's raw log) was | |
| # apt-get silently blocking on the dpkg/apt lock, still held by | |
| # a background apt-daily/unattended-upgrades process right after | |
| # runner boot, with zero output until cancelled. Clear/wait for | |
| # that lock first, then route around a potentially degraded | |
| # regional mirror as a second layer of defence. | |
| sudo systemctl stop apt-daily.service apt-daily-upgrade.service \ | |
| apt-daily.timer apt-daily-upgrade.timer unattended-upgrades \ | |
| 2>/dev/null || true | |
| sudo killall -q apt apt-get unattended-upgrade 2>/dev/null || true | |
| while sudo fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \ | |
| sudo fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do | |
| echo "Waiting for another apt process to release the lock..." | |
| sleep 2 | |
| done | |
| sudo dpkg --configure -a || true | |
| sudo sed -i \ | |
| -e 's|http://azure\.archive\.ubuntu\.com/ubuntu/|http://archive.ubuntu.com/ubuntu/|g' \ | |
| -e 's|http://azure\.archive\.ubuntu\.com/ubuntu|http://archive.ubuntu.com/ubuntu|g' \ | |
| /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null || true | |
| APT_OPTS="-o Acquire::Retries=3 -o Acquire::http::Timeout=15 -o Acquire::https::Timeout=15" | |
| for attempt in 1 2 3; do | |
| if sudo apt-get update -qq $APT_OPTS && \ | |
| sudo apt-get install -y $APT_OPTS \ | |
| libxcb-cursor0 libxcb-xinerama0 libxkbcommon-x11-0 libglib2.0-0 \ | |
| libegl1 libgl1-mesa-dev libdbus-1-3 xvfb libfuse2 imagemagick; then | |
| exit 0 | |
| fi | |
| echo "::warning::apt-get attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| done | |
| echo "::error::apt-get failed after 3 attempts" | |
| exit 1 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e . pyinstaller | |
| - name: Fetch boundary baseline | |
| run: python scripts/fetch_boundary_baseline.py | |
| - name: Build Linux binary | |
| run: xvfb-run -a pyinstaller opensak.spec --clean --noconfirm | |
| env: | |
| PYTHONPATH: src | |
| - name: Download appimagetool | |
| run: | | |
| wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool | |
| chmod +x appimagetool | |
| - name: Build AppDir | |
| run: python3 scripts/make_appdir.py | |
| - name: Build AppImage | |
| run: ARCH=x86_64 ./appimagetool --appimage-extract-and-run AppDir OpenSAK-${{ github.ref_name }}-Linux-x86_64.AppImage | |
| env: | |
| ARCH: x86_64 | |
| - name: Verify output and create fallback tar.gz | |
| run: | | |
| if [ -f OpenSAK-${{ github.ref_name }}-Linux-x86_64.AppImage ]; then | |
| echo "AppImage OK" | |
| ls -lh OpenSAK-${{ github.ref_name }}-Linux-x86_64.AppImage | |
| chmod +x OpenSAK-${{ github.ref_name }}-Linux-x86_64.AppImage | |
| else | |
| echo "AppImage failed - creating tar.gz fallback" | |
| tar -czf OpenSAK-${{ github.ref_name }}-Linux-x86_64.tar.gz -C dist OpenSAK/ | |
| fi | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-Linux | |
| path: | | |
| OpenSAK-${{ github.ref_name }}-Linux-x86_64.AppImage | |
| OpenSAK-${{ github.ref_name }}-Linux-x86_64.tar.gz | |
| retention-days: 7 | |
| # ============================================================ | |
| # macOS arm64 (Apple Silicon — M1/M2/M3/M4) | |
| # ============================================================ | |
| build-macos-arm64: | |
| name: "Build macOS arm64 (Apple Silicon)" | |
| runs-on: macos-latest | |
| needs: test | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e . pyinstaller | |
| - name: Fetch boundary baseline | |
| run: python scripts/fetch_boundary_baseline.py | |
| - name: Build macOS .app (arm64) | |
| run: pyinstaller opensak.spec --clean --noconfirm | |
| env: | |
| PYTHONPATH: src | |
| # Code signing / notarization only run when the certificate secret is | |
| # present, so the build still succeeds unsigned on forks and PRs that | |
| # don't have access to the OpenSAK org's secrets. `if:` conditions | |
| # can't reference `secrets` directly (GitHub Actions restriction), so | |
| # the check is done once here and reused via this step's output. | |
| - name: Check for Apple signing secrets | |
| id: signing | |
| run: echo "enabled=${{ secrets.APPLE_CERTIFICATE_P12_BASE64 != '' }}" >> "$GITHUB_OUTPUT" | |
| - name: Import signing certificate | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-import-certificate | |
| with: | |
| p12-base64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }} | |
| p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| - name: Codesign app bundle | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-codesign | |
| with: | |
| target-path: dist/OpenSAK.app | |
| - name: Create .dmg (arm64) | |
| run: | | |
| brew install create-dmg | |
| create-dmg \ | |
| --volname "OpenSAK" \ | |
| --window-size 500 300 \ | |
| --icon-size 100 \ | |
| --app-drop-link 350 150 \ | |
| OpenSAK-${{ github.ref_name }}-macOS-arm64.dmg \ | |
| dist/OpenSAK.app \ | |
| || (cd dist && zip -r ../OpenSAK-${{ github.ref_name }}-macOS-arm64.zip OpenSAK.app && echo "Fallback ZIP created") | |
| - name: Codesign .dmg | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-codesign | |
| with: | |
| target-path: OpenSAK-${{ github.ref_name }}-macOS-arm64.dmg | |
| - name: Notarize .dmg | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-notarize | |
| with: | |
| target-path: OpenSAK-${{ github.ref_name }}-macOS-arm64.dmg | |
| apple-id: ${{ secrets.APPLE_ID }} | |
| app-specific-password: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| team-id: ${{ secrets.APPLE_TEAM_ID }} | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-macOS-arm64 | |
| path: | | |
| OpenSAK-${{ github.ref_name }}-macOS-arm64.dmg | |
| OpenSAK-${{ github.ref_name }}-macOS-arm64.zip | |
| retention-days: 7 | |
| # ============================================================ | |
| # macOS x86_64 (Intel) | |
| # ============================================================ | |
| build-macos-x86: | |
| name: "Build macOS x86_64 (Intel)" | |
| runs-on: macos-15-intel | |
| needs: test | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e . pyinstaller | |
| - name: Fetch boundary baseline | |
| run: python scripts/fetch_boundary_baseline.py | |
| - name: Build macOS .app (x86_64) | |
| run: pyinstaller opensak.spec --clean --noconfirm | |
| env: | |
| PYTHONPATH: src | |
| - name: Check for Apple signing secrets | |
| id: signing | |
| run: echo "enabled=${{ secrets.APPLE_CERTIFICATE_P12_BASE64 != '' }}" >> "$GITHUB_OUTPUT" | |
| - name: Import signing certificate | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-import-certificate | |
| with: | |
| p12-base64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }} | |
| p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| - name: Codesign app bundle | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-codesign | |
| with: | |
| target-path: dist/OpenSAK.app | |
| - name: Create .dmg (x86_64) | |
| run: | | |
| brew install create-dmg | |
| create-dmg \ | |
| --volname "OpenSAK" \ | |
| --window-size 500 300 \ | |
| --icon-size 100 \ | |
| --app-drop-link 350 150 \ | |
| OpenSAK-${{ github.ref_name }}-macOS-x86_64.dmg \ | |
| dist/OpenSAK.app \ | |
| || (cd dist && zip -r ../OpenSAK-${{ github.ref_name }}-macOS-x86_64.zip OpenSAK.app && echo "Fallback ZIP created") | |
| - name: Codesign .dmg | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-codesign | |
| with: | |
| target-path: OpenSAK-${{ github.ref_name }}-macOS-x86_64.dmg | |
| - name: Notarize .dmg | |
| if: steps.signing.outputs.enabled == 'true' | |
| uses: ./.github/actions/macos-notarize | |
| with: | |
| target-path: OpenSAK-${{ github.ref_name }}-macOS-x86_64.dmg | |
| apple-id: ${{ secrets.APPLE_ID }} | |
| app-specific-password: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| team-id: ${{ secrets.APPLE_TEAM_ID }} | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-macOS-x86_64 | |
| path: | | |
| OpenSAK-${{ github.ref_name }}-macOS-x86_64.dmg | |
| OpenSAK-${{ github.ref_name }}-macOS-x86_64.zip | |
| retention-days: 7 | |
| # ============================================================ | |
| # Create GitHub Release | |
| # ============================================================ | |
| create-release: | |
| name: "Create GitHub Release" | |
| runs-on: ubuntu-latest | |
| needs: [build-windows, build-linux, build-macos-arm64, build-macos-x86] | |
| if: startsWith(github.ref, 'refs/tags/') | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-Windows | |
| path: release-files/ | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-Linux | |
| path: release-files/ | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-macOS-arm64 | |
| path: release-files/ | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: OpenSAK-${{ github.ref_name }}-macOS-x86_64 | |
| path: release-files/ | |
| - name: List release files | |
| run: ls -lh release-files/ | |
| - name: Extract version from tag | |
| id: version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ steps.version.outputs.VERSION }} | |
| name: "OpenSAK ${{ steps.version.outputs.VERSION }}" | |
| draft: false | |
| prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} | |
| body: | | |
| ## OpenSAK ${{ steps.version.outputs.VERSION }} | |
| ### Download | |
| | Platform | File | Instructions | | |
| |----------|------|--------------| | |
| | 🪟 Windows | `OpenSAK-${{ steps.version.outputs.VERSION }}-Windows.zip` | Unzip and double-click `OpenSAK.exe` | | |
| | 🐧 Linux | `OpenSAK-${{ steps.version.outputs.VERSION }}-Linux-x86_64.AppImage` | See instructions below | | |
| | 🍎 macOS Apple Silicon (M1/M2/M3/M4) | `OpenSAK-${{ steps.version.outputs.VERSION }}-macOS-arm64.dmg` | Open and drag OpenSAK to Applications | | |
| | 🍎 macOS Intel | `OpenSAK-${{ steps.version.outputs.VERSION }}-macOS-x86_64.dmg` | Open and drag OpenSAK to Applications | | |
| > **Not sure which Mac you have?** Click the Apple menu () → "About This Mac". | |
| > If it says "Apple M1/M2/M3/M4" choose **arm64**. If it says "Intel" choose **x86_64**. | |
| ### Linux | |
| ```bash | |
| chmod +x OpenSAK-${{ steps.version.outputs.VERSION }}-Linux-x86_64.AppImage | |
| ./OpenSAK-${{ steps.version.outputs.VERSION }}-Linux-x86_64.AppImage | |
| ``` | |
| Or right-click → Properties → Allow executing as program, then double-click. | |
| ### Windows | |
| 1. Download `OpenSAK-${{ steps.version.outputs.VERSION }}-Windows.zip` | |
| 2. Right-click → Extract All | |
| 3. Open the extracted folder and double-click `OpenSAK.exe` | |
| ### macOS | |
| 1. Download the correct .dmg for your Mac (arm64 or x86_64) | |
| 2. Open the .dmg and drag OpenSAK to your Applications folder — the app is signed and notarized by Apple, so it opens normally on first launch | |
| --- | |
| See [CHANGELOG.md](https://github.com/OpenSAK-Org/OpenSAK/blob/${{ steps.version.outputs.VERSION }}/CHANGELOG.md) for details. | |
| files: release-files/**/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |