inCrate button disabled #302
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 macOS | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| jobs: | |
| build: | |
| runs-on: macos-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python (pinned) | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13.7' | |
| cache: 'pip' | |
| - name: "Install dependencies (release: with hash checking per 2.8)" | |
| run: | | |
| echo "=== DEBUG: Installing dependencies ===" | |
| python --version | |
| python -m pip install --upgrade pip | |
| if [ "${{ startsWith(github.ref, 'refs/tags/v') }}" = "true" ]; then | |
| echo "Release build: using pip --require-hashes (2.8)" | |
| python -m pip install "pip<26" | |
| python -m pip install pip-tools | |
| python scripts/generate_requirements_hashes.py | |
| pip install -r requirements-build-hashed.txt --require-hashes | |
| else | |
| echo "Non-release build: installing from requirements-build.txt" | |
| pip install -r requirements-build.txt | |
| fi | |
| echo "Installing build requirements (runtime deps for GUI app)..." | |
| echo "PyInstaller version (pinned from requirements-build.txt):" | |
| pyinstaller --version | |
| echo "Installing Playwright browsers (for testing, not bundled)..." | |
| python -m playwright install chromium | |
| echo "Verifying installed packages match requirements.txt runtime dependencies..." | |
| echo "Core packages:" | |
| pip list | grep -E "PySide6|requests|aiohttp|beautifulsoup4|ddgs|rapidfuzz|dateutil|pyyaml|tqdm|requests-cache|playwright|selenium|openpyxl|pyinstaller" | |
| echo "Checking for missing dependencies..." | |
| required=("PySide6" "requests" "aiohttp" "beautifulsoup4" "ddgs" "rapidfuzz" "python-dateutil" "pyyaml" "tqdm" "requests-cache" "playwright" "selenium" "openpyxl") | |
| installed=$(pip list --format=json | python -c "import sys, json; print('\n'.join(p['name'].lower() for p in json.load(sys.stdin)))") | |
| missing=() | |
| for pkg in "${required[@]}"; do | |
| if ! printf "%s\n" "$installed" | grep -qx "${pkg,,}"; then | |
| missing+=("$pkg") | |
| fi | |
| done | |
| if [ ${#missing[@]} -gt 0 ]; then | |
| echo "⚠️ Warning: Missing packages: ${missing[*]}" | |
| else | |
| echo "✅ All required runtime dependencies are installed" | |
| fi | |
| - name: Generate third-party licenses file (Step 11) | |
| run: | | |
| python scripts/generate_licenses.py --output THIRD_PARTY_LICENSES.txt | |
| if [ ! -f THIRD_PARTY_LICENSES.txt ]; then | |
| echo "ERROR: License bundle generation failed - THIRD_PARTY_LICENSES.txt not created" | |
| exit 1 | |
| fi | |
| if ! grep -q "Package:" THIRD_PARTY_LICENSES.txt; then | |
| echo "ERROR: License bundle appears empty or invalid" | |
| exit 1 | |
| fi | |
| - name: Sync version from git tag | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| echo "Syncing version.py from git tag: ${{ github.ref_name }}" | |
| python scripts/sync_version.py --tag ${{ github.ref_name }} | |
| continue-on-error: false | |
| - name: Set build info | |
| run: python scripts/set_build_info.py | |
| env: | |
| GITHUB_RUN_NUMBER: ${{ github.run_number }} | |
| GITHUB_RUN_ID: ${{ github.run_id }} | |
| - name: Process Info.plist | |
| run: python scripts/process_info_plist.py | |
| - name: Validate version | |
| run: python scripts/validate_version.py | |
| - name: Generate application icons | |
| run: | | |
| echo "=== Generating application icons (taskbar/dock) ===" | |
| python scripts/generate_icons.py | |
| if [ -f build/icon.icns ]; then | |
| echo "[OK] icon.icns created for macOS" | |
| elif [ -f build/icon.ico ]; then | |
| echo "[OK] icon.ico created (ICNS may need manual conversion on macOS)" | |
| else | |
| echo "ERROR: No icon file found after generate_icons.py" | |
| exit 1 | |
| fi | |
| - name: Build with PyInstaller | |
| run: | | |
| python scripts/build_pyinstaller.py | |
| - name: Import signing certificate | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| env: | |
| MACOS_CERT_P12: ${{ secrets.MACOS_SIGNING_CERT_P12 }} | |
| MACOS_CERT_PASSWORD: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }} | |
| run: | | |
| if [ -z "$MACOS_CERT_P12" ] || [ -z "$MACOS_CERT_PASSWORD" ]; then | |
| echo "Skipping certificate import: secrets not available" | |
| exit 0 | |
| fi | |
| # Create keychain | |
| security create-keychain -p "" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "" build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| # Import certificate | |
| echo "$MACOS_CERT_P12" | base64 --decode > cert.p12 | |
| security import cert.p12 -k build.keychain -P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain | |
| # Clean up certificate file | |
| rm -f cert.p12 | |
| - name: Sign app | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| env: | |
| APPLE_DEVELOPER_ID: ${{ secrets.APPLE_DEVELOPER_ID }} | |
| run: | | |
| if [ -z "$APPLE_DEVELOPER_ID" ]; then | |
| echo "Skipping app signing: APPLE_DEVELOPER_ID not available" | |
| exit 0 | |
| fi | |
| bash scripts/sign_macos.sh dist/CuePoint.app | |
| - name: Create DMG | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| id: create_dmg | |
| run: | | |
| echo "=== DEBUG: Creating DMG ===" | |
| echo "Current directory: $(pwd)" | |
| echo "Checking for existing DMG files..." | |
| ls -la dist/*.dmg 2>/dev/null || echo "No existing DMG files found" | |
| echo "Checking for mounted volumes..." | |
| hdiutil info | grep -A 5 "CuePoint" || echo "No CuePoint volumes mounted" | |
| echo "Cleaning up any existing mounts..." | |
| # Unmount any existing CuePoint volumes | |
| for vol in $(hdiutil info | grep -i "cuepoint" | awk '{print $1}' || true); do | |
| echo "Unmounting volume: $vol" | |
| hdiutil detach "$vol" -force 2>/dev/null || true | |
| done | |
| sleep 2 | |
| VERSION=$(python -c "import sys; sys.path.insert(0, 'src'); from cuepoint.version import __version__; print(__version__)") | |
| echo "Version: $VERSION" | |
| echo "Running: bash scripts/create_dmg.sh \"$VERSION\"" | |
| bash scripts/create_dmg.sh "$VERSION" | |
| # Verify DMG was created | |
| echo "Verifying DMG was created..." | |
| DMG_FILE=$(ls dist/CuePoint-v*.dmg 2>/dev/null | head -1) | |
| if [ -z "$DMG_FILE" ]; then | |
| echo "Error: DMG file not found after creation" | |
| echo "Contents of dist/:" | |
| ls -la dist/ || true | |
| exit 1 | |
| fi | |
| echo "DMG created: $DMG_FILE" | |
| echo "DMG size: $(ls -lh "$DMG_FILE" | awk '{print $5}')" | |
| echo "dmg_file=$DMG_FILE" >> $GITHUB_OUTPUT | |
| - name: Generate build metadata | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: python scripts/generate_build_metadata.py --output dist/build_info.json | |
| env: | |
| GITHUB_RUN_ID: ${{ github.run_id }} | |
| - name: Notarize DMG | |
| if: startsWith(github.ref, 'refs/tags/v') && steps.create_dmg.outcome == 'success' | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_NOTARYTOOL_ISSUER_ID: ${{ secrets.APPLE_NOTARYTOOL_ISSUER_ID }} | |
| APPLE_NOTARYTOOL_KEY_ID: ${{ secrets.APPLE_NOTARYTOOL_KEY_ID }} | |
| APPLE_NOTARYTOOL_KEY: ${{ secrets.APPLE_NOTARYTOOL_KEY }} | |
| run: | | |
| if [ -z "$APPLE_NOTARYTOOL_ISSUER_ID" ] || [ -z "$APPLE_NOTARYTOOL_KEY_ID" ] || [ -z "$APPLE_NOTARYTOOL_KEY" ]; then | |
| echo "Skipping notarization: secrets not available" | |
| exit 0 | |
| fi | |
| DMG_FILE=$(ls dist/CuePoint-v*.dmg | head -1) | |
| bash scripts/notarize_macos.sh "$DMG_FILE" | |
| - name: Verify version embedding | |
| run: python scripts/verify_version_embedding.py | |
| - name: Verify installer (checksums and signatures, Design 2.38) | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| python scripts/generate_checksums.py --output dist/SHA256SUMS.txt --algorithms sha256 | |
| python scripts/verify_installer.py --dir dist/ --checksums dist/SHA256SUMS.txt | |
| python scripts/validate_signatures.py --dir dist/ | |
| - name: Verify DMG exists before upload | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| DMG_COUNT=$(ls dist/*.dmg 2>/dev/null | wc -l | tr -d ' ') | |
| if [ "$DMG_COUNT" -eq 0 ]; then | |
| echo "Error: No DMG files found in dist/" | |
| ls -la dist/ || true | |
| exit 1 | |
| fi | |
| echo "Found $DMG_COUNT DMG file(s):" | |
| ls -lh dist/*.dmg | |
| - name: Upload artifact | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| id: upload_dmg | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-dmg | |
| path: dist/ | |
| retention-days: 30 | |
| if-no-files-found: error | |
| - name: Verify artifact upload | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| if [ "${{ steps.upload_dmg.outcome }}" != "success" ]; then | |
| echo "Error: Artifact upload failed" | |
| exit 1 | |
| fi | |
| echo "Artifact uploaded successfully" |