fix: resolve shell interpretation errors in release workflow #29
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: Release Build | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to release (e.g., v1.0.0)' | |
| required: true | |
| default: 'v1.0.0' | |
| env: | |
| PYTHON_VERSION: '3.11' | |
| NODE_VERSION: '18' | |
| jobs: | |
| # ============================================================================ | |
| # macOS Build (Apple Silicon) | |
| # ============================================================================ | |
| build-macos: | |
| runs-on: macos-14 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install system dependencies (Homebrew) | |
| run: | | |
| brew install cmake dlib imagemagick | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-apple-darwin | |
| - name: Create Python virtual environment | |
| run: | | |
| cd backend | |
| python -m venv .venv | |
| - name: Install backend requirements | |
| run: | | |
| cd backend | |
| source .venv/bin/activate | |
| python -m pip install --upgrade pip setuptools wheel | |
| pip install -r requirements.txt | |
| pip install pyinstaller==6.11.1 | |
| - name: Build Python backend with PyInstaller | |
| run: | | |
| cd backend | |
| source .venv/bin/activate | |
| python -m PyInstaller backend_server.spec | |
| - name: Install frontend requirements | |
| run: | | |
| cd frontend | |
| npm install | |
| - name: Copy backend bundle for Tauri | |
| run: | | |
| cd frontend | |
| node ensure-backend.js | |
| - name: Build Tauri application | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: '' | |
| run: | | |
| cd frontend | |
| npm run tauri build | |
| - name: Find macOS build artifacts | |
| id: find_files | |
| run: | | |
| cd frontend/src-tauri/target/release/bundle | |
| # Find the DMG file | |
| DMG_FILE=$(find dmg -name "*.dmg" 2>/dev/null | head -1) | |
| if [ -n "$DMG_FILE" ]; then | |
| DMG_PATH="$(pwd)/$DMG_FILE" | |
| DMG_NAME=$(basename "$DMG_FILE") | |
| echo "dmg_path=$DMG_PATH" >> $GITHUB_OUTPUT | |
| echo "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT | |
| echo "Found DMG: $DMG_PATH" | |
| fi | |
| # Find the .app.tar.gz file (for updater) | |
| TARBALL=$(find macos -name "*.app.tar.gz" 2>/dev/null | head -1) | |
| if [ -n "$TARBALL" ]; then | |
| TARBALL_PATH="$(pwd)/$TARBALL" | |
| TARBALL_NAME=$(basename "$TARBALL") | |
| echo "tarball_path=$TARBALL_PATH" >> $GITHUB_OUTPUT | |
| echo "tarball_name=$TARBALL_NAME" >> $GITHUB_OUTPUT | |
| echo "Found tarball: $TARBALL_PATH" | |
| fi | |
| # Find the signature file | |
| SIG_FILE=$(find macos -name "*.app.tar.gz.sig" 2>/dev/null | head -1) | |
| if [ -n "$SIG_FILE" ]; then | |
| SIG_PATH="$(pwd)/$SIG_FILE" | |
| SIGNATURE=$(cat "$SIG_PATH") | |
| echo "sig_path=$SIG_PATH" >> $GITHUB_OUTPUT | |
| echo "signature=$SIGNATURE" >> $GITHUB_OUTPUT | |
| echo "Found signature: $SIG_PATH" | |
| fi | |
| - name: Generate macOS checksums | |
| run: | | |
| cd frontend/src-tauri/target/release/bundle | |
| # Generate checksums for DMG (in dmg folder so artifact upload finds it) | |
| if [ -f "${{ steps.find_files.outputs.dmg_path }}" ]; then | |
| shasum -a 256 "${{ steps.find_files.outputs.dmg_path }}" | awk '{print $1 " " FILENAME}' FILENAME="${{ steps.find_files.outputs.dmg_name }}" > dmg/checksums-macos.txt | |
| echo "Generated checksums:" | |
| cat dmg/checksums-macos.txt | |
| else | |
| echo "No DMG found for checksums" | |
| fi | |
| - name: Create macOS Gatekeeper fix script | |
| run: | | |
| cat > "Fix_Local_Lens.command" << 'SCRIPT' | |
| #!/bin/bash | |
| # Local Lens - macOS Gatekeeper Fix | |
| # Double-click this file after copying Local Lens to Applications | |
| echo "" | |
| echo "🔧 Local Lens - Fixing macOS Security Block" | |
| echo "============================================" | |
| echo "" | |
| APP_PATH="/Applications/Local Lens.app" | |
| USER_APP_PATH="$HOME/Applications/Local Lens.app" | |
| if [ -d "$APP_PATH" ]; then | |
| TARGET="$APP_PATH" | |
| echo "Found app in /Applications" | |
| elif [ -d "$USER_APP_PATH" ]; then | |
| TARGET="$USER_APP_PATH" | |
| echo "Found app in ~/Applications" | |
| else | |
| echo "❌ Local Lens not found in Applications!" | |
| echo "" | |
| echo "Please:" | |
| echo "1. Drag 'Local Lens.app' to your Applications folder first" | |
| echo "2. Then run this script again" | |
| echo "" | |
| read -p "Press Enter to close..." | |
| exit 1 | |
| fi | |
| echo "Fixing: $TARGET" | |
| echo "" | |
| echo "Step 1/3: Removing quarantine flag..." | |
| xattr -cr "$TARGET" | |
| echo "Step 2/3: Applying ad-hoc signature..." | |
| codesign --force --deep --sign - "$TARGET" 2>/dev/null || echo " (signature step skipped)" | |
| echo "Step 3/3: Setting executable permissions..." | |
| chmod -R +x "$TARGET/Contents/MacOS/" | |
| chmod -R +x "$TARGET/Contents/Resources/backend_server_bundle/" 2>/dev/null || true | |
| echo "" | |
| echo "✅ Done! You can now open Local Lens normally." | |
| echo "" | |
| read -p "Press Enter to close..." | |
| SCRIPT | |
| chmod +x "Fix_Local_Lens.command" | |
| echo "Created Fix_Local_Lens.command" | |
| - name: Upload macOS artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-artifacts | |
| path: | | |
| frontend/src-tauri/target/release/bundle/dmg/*.dmg | |
| frontend/src-tauri/target/release/bundle/dmg/checksums-macos.txt | |
| frontend/src-tauri/target/release/bundle/macos/*.app.tar.gz | |
| frontend/src-tauri/target/release/bundle/macos/*.app.tar.gz.sig | |
| Fix_Local_Lens.command | |
| retention-days: 5 | |
| # ============================================================================ | |
| # Windows Build | |
| # ============================================================================ | |
| build-windows: | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| # Backend: Create virtual environment and install requirements | |
| - name: Create Python virtual environment | |
| run: | | |
| cd backend | |
| python -m venv venv | |
| - name: Activate venv and install backend requirements | |
| run: | | |
| cd backend | |
| .\venv\Scripts\activate | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install pyinstaller | |
| # Backend: Build with PyInstaller using your spec file | |
| - name: Build Python backend with PyInstaller | |
| run: | | |
| cd backend | |
| .\venv\Scripts\activate | |
| python -m PyInstaller backend_server.spec | |
| # Frontend: Install requirements in src-tauri folder | |
| - name: Install frontend requirements | |
| run: | | |
| cd frontend | |
| npm install | |
| # Run ensure-backend.js to copy and rename the backend executable | |
| - name: Copy and rename backend executable | |
| run: | | |
| cd frontend | |
| node ensure-backend.js | |
| - name: Install Tauri requirements | |
| run: | | |
| cd frontend/src-tauri | |
| # Install any additional Tauri-specific requirements if needed | |
| # Currently using npm install from frontend folder covers this | |
| # Build the final application | |
| - name: Build Tauri application | |
| env: | |
| # Pass the Base64 secret DIRECTLY here. | |
| # Tauri will handle the decoding automatically. | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: '' | |
| run: | | |
| cd frontend | |
| npm run tauri build | |
| # Generate checksums for verification | |
| - name: Generate checksums | |
| run: | | |
| cd frontend\src-tauri\target\release\bundle | |
| $files = Get-ChildItem -Recurse -Include "*.msi", "*.exe" | Where-Object { $_.Directory.Name -eq "msi" -or $_.Directory.Name -eq "nsis" } | |
| $checksums = @() | |
| foreach ($file in $files) { | |
| $hash = Get-FileHash -Path $file.FullName -Algorithm SHA256 | |
| $checksums += "$($hash.Hash.ToLower()) $($file.Name)" | |
| } | |
| $checksums | Out-File -FilePath "checksums-windows.txt" -Encoding utf8 | |
| Write-Output "Generated checksums:" | |
| Get-Content "checksums-windows.txt" | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-artifacts | |
| path: | | |
| frontend/src-tauri/target/release/bundle/msi/*.msi | |
| frontend/src-tauri/target/release/bundle/msi/*.msi.sig | |
| frontend/src-tauri/target/release/bundle/nsis/*.exe | |
| frontend/src-tauri/target/release/bundle/nsis/*.exe.sig | |
| frontend/src-tauri/target/release/bundle/checksums-windows.txt | |
| retention-days: 5 | |
| - name: Build Summary | |
| run: | | |
| Write-Output "✅ Windows build completed successfully!" | |
| Write-Output "📦 Artifacts uploaded for release job" | |
| # ============================================================================ | |
| # Create GitHub Release (after both builds complete) | |
| # ============================================================================ | |
| create-release: | |
| runs-on: ubuntu-latest | |
| needs: [build-windows, build-macos] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Windows artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: windows-artifacts | |
| path: artifacts/windows | |
| - name: Download macOS artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: macos-artifacts | |
| path: artifacts/macos | |
| - name: List downloaded artifacts | |
| run: | | |
| echo "=== Windows Artifacts ===" | |
| find artifacts/windows -type f | |
| echo "" | |
| echo "=== macOS Artifacts ===" | |
| find artifacts/macos -type f | |
| - name: Get version | |
| id: get_version | |
| run: | | |
| tag="${{ github.ref_name }}" | |
| version="${tag#v}" | |
| echo "version=$version" >> $GITHUB_OUTPUT | |
| echo "Version: $version" | |
| - name: Extract release notes | |
| id: release_notes | |
| run: | | |
| version="${{ steps.get_version.outputs.version }}" | |
| changelog_path="CHANGELOG.md" | |
| if [ -f "$changelog_path" ]; then | |
| # Extract notes for this version | |
| notes=$(awk "/^## \[$version\]/{flag=1; next} /^## \[/{flag=0} flag" "$changelog_path" | sed '/^$/d') | |
| if [ -n "$notes" ]; then | |
| echo "Found release notes for version $version" | |
| # Use delimiter for multiline output | |
| echo "notes<<EOF" >> $GITHUB_OUTPUT | |
| echo "$notes" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| else | |
| echo "No specific notes found, using default" | |
| echo "notes=Release $version - See CHANGELOG.md for details" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "CHANGELOG.md not found, using default notes" | |
| echo "notes=Release $version" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Find artifact files | |
| id: find_files | |
| run: | | |
| # Windows artifacts | |
| MSI_FILE=$(find artifacts/windows -name "*.msi" ! -name "*.sig" 2>/dev/null | head -1) | |
| MSI_SIG=$(find artifacts/windows -name "*.msi.sig" 2>/dev/null | head -1) | |
| EXE_FILE=$(find artifacts/windows -name "*.exe" ! -name "*.sig" 2>/dev/null | head -1) | |
| CHECKSUMS_WIN=$(find artifacts/windows -name "checksums-windows.txt" 2>/dev/null | head -1) | |
| # macOS artifacts | |
| DMG_FILE=$(find artifacts/macos -name "*.dmg" 2>/dev/null | head -1) | |
| TARBALL=$(find artifacts/macos -name "*.app.tar.gz" 2>/dev/null | head -1) | |
| TARBALL_SIG=$(find artifacts/macos -name "*.app.tar.gz.sig" 2>/dev/null | head -1) | |
| CHECKSUMS_MAC=$(find artifacts/macos -name "checksums-macos.txt" 2>/dev/null | head -1) | |
| FIX_SCRIPT=$(find artifacts/macos -name "Fix_Local_Lens.command" 2>/dev/null | head -1) | |
| echo "msi_path=$MSI_FILE" >> $GITHUB_OUTPUT | |
| echo "msi_sig_path=$MSI_SIG" >> $GITHUB_OUTPUT | |
| echo "exe_path=$EXE_FILE" >> $GITHUB_OUTPUT | |
| echo "checksums_win_path=$CHECKSUMS_WIN" >> $GITHUB_OUTPUT | |
| echo "dmg_path=$DMG_FILE" >> $GITHUB_OUTPUT | |
| echo "tarball_path=$TARBALL" >> $GITHUB_OUTPUT | |
| echo "tarball_sig_path=$TARBALL_SIG" >> $GITHUB_OUTPUT | |
| echo "checksums_mac_path=$CHECKSUMS_MAC" >> $GITHUB_OUTPUT | |
| echo "fix_script_path=$FIX_SCRIPT" >> $GITHUB_OUTPUT | |
| # Get DMG SHA256 for Homebrew cask | |
| if [ -n "$DMG_FILE" ] && [ -f "$DMG_FILE" ]; then | |
| DMG_SHA256=$(shasum -a 256 "$DMG_FILE" | awk '{print $1}') | |
| echo "dmg_sha256=$DMG_SHA256" >> $GITHUB_OUTPUT | |
| echo "DMG SHA256: $DMG_SHA256" | |
| fi | |
| # Read signatures for latest.json | |
| if [ -n "$MSI_SIG" ] && [ -f "$MSI_SIG" ]; then | |
| WIN_SIG=$(cat "$MSI_SIG") | |
| echo "win_signature=$WIN_SIG" >> $GITHUB_OUTPUT | |
| fi | |
| if [ -n "$TARBALL_SIG" ] && [ -f "$TARBALL_SIG" ]; then | |
| MAC_SIG=$(cat "$TARBALL_SIG") | |
| echo "mac_signature=$MAC_SIG" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Generate latest.json for Tauri updater | |
| run: | | |
| version="${{ steps.get_version.outputs.version }}" | |
| tag="${{ github.ref_name }}" | |
| repo="${{ github.repository }}" | |
| pub_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| win_sig="${{ steps.find_files.outputs.win_signature }}" | |
| mac_sig="${{ steps.find_files.outputs.mac_signature }}" | |
| # Get release notes using heredoc to avoid backtick interpretation | |
| cat > /tmp/notes.txt << 'NOTES_EOF' | |
| ${{ steps.release_notes.outputs.notes }} | |
| NOTES_EOF | |
| notes=$(cat /tmp/notes.txt) | |
| # Use jq to generate valid JSON with proper escaping | |
| jq -n \ | |
| --arg version "$version" \ | |
| --arg notes "$notes" \ | |
| --arg pub_date "$pub_date" \ | |
| --arg win_sig "$win_sig" \ | |
| --arg win_url "https://github.com/$repo/releases/download/$tag/Local_Lens_${tag}_x64_en-US.msi" \ | |
| --arg mac_sig "$mac_sig" \ | |
| --arg mac_url "https://github.com/$repo/releases/download/$tag/Local_Lens.app.tar.gz" \ | |
| '{ | |
| version: $version, | |
| notes: $notes, | |
| pub_date: $pub_date, | |
| platforms: { | |
| "windows-x86_64": { | |
| signature: $win_sig, | |
| url: $win_url | |
| }, | |
| "darwin-aarch64": { | |
| signature: $mac_sig, | |
| url: $mac_url | |
| } | |
| } | |
| }' > latest.json | |
| echo "Generated latest.json:" | |
| cat latest.json | |
| - name: Generate Homebrew Cask formula | |
| run: | | |
| version="${{ steps.get_version.outputs.version }}" | |
| sha256="${{ steps.find_files.outputs.dmg_sha256 }}" | |
| cat > local-lens.rb << EOF | |
| cask "local-lens" do | |
| version "$version" | |
| sha256 "$sha256" | |
| url "https://github.com/${{ github.repository }}/releases/download/v#{version}/Local_Lens_v#{version}_aarch64.dmg", | |
| verified: "github.com/${{ github.repository }}/" | |
| name "Local Lens" | |
| desc "AI-powered offline photo organizer with face recognition" | |
| homepage "https://github.com/${{ github.repository }}" | |
| livecheck do | |
| url :url | |
| strategy :github_latest | |
| end | |
| auto_updates true | |
| depends_on arch: :arm64 | |
| app "Local Lens.app" | |
| postflight do | |
| system_command "/usr/bin/xattr", | |
| args: ["-cr", "#{appdir}/Local Lens.app"], | |
| sudo: false | |
| end | |
| zap trash: [ | |
| "~/.config/LocalLens", | |
| ] | |
| caveats <<~EOS | |
| Local Lens is not notarized by Apple. | |
| If you see a security warning, right-click the app → Open → Click "Open" | |
| EOS | |
| end | |
| EOF | |
| echo "Generated Homebrew Cask formula:" | |
| cat local-lens.rb | |
| - name: Create Release | |
| id: create_release | |
| uses: actions/create-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| release_name: Local Lens ${{ github.ref_name }} | |
| draft: true | |
| prerelease: false | |
| body: | | |
| ## Local Lens ${{ github.ref_name }} | |
| ${{ steps.release_notes.outputs.notes }} | |
| --- | |
| ### Installation | |
| #### macOS via Homebrew (Recommended) | |
| brew install ashesbloom/locallens/local-lens | |
| Homebrew automatically handles Gatekeeper - no extra steps needed! | |
| #### Windows | |
| 1. Download the .msi (enterprise) or .exe (individual) installer | |
| 2. Run the installer | |
| 3. Launch Local Lens from the Start Menu | |
| #### macOS Manual Install (DMG) | |
| 1. Download the DMG file from this release | |
| 2. Open the DMG and drag Local Lens to Applications | |
| 3. Fix Gatekeeper block (choose one): | |
| - Download and double-click Fix_Local_Lens.command | |
| - Right-click the app, Open, click Open in the dialog | |
| - Terminal: xattr -cr "/Applications/Local Lens.app" | |
| --- | |
| ### macOS Security Note | |
| Local Lens is not notarized with Apple (requires $99/year developer fee). | |
| macOS may show a damaged or unidentified developer warning. | |
| This is normal for open-source apps. | |
| The Fix_Local_Lens.command script automatically removes quarantine, | |
| applies an ad-hoc signature, and sets permissions. | |
| --- | |
| ### Security and Verification | |
| - Source Code: Available at this tag | |
| - Build Process: See Actions tab for build logs | |
| - Checksums: SHA256 checksums provided for verification | |
| # Upload Windows artifacts | |
| - name: Upload MSI installer | |
| if: steps.find_files.outputs.msi_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.msi_path }} | |
| asset_name: Local_Lens_${{ github.ref_name }}_x64_en-US.msi | |
| asset_content_type: application/x-msi | |
| - name: Upload EXE installer | |
| if: steps.find_files.outputs.exe_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.exe_path }} | |
| asset_name: Local_Lens_${{ github.ref_name }}_x64-setup.exe | |
| asset_content_type: application/x-msdownload | |
| - name: Upload Windows checksums | |
| if: steps.find_files.outputs.checksums_win_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.checksums_win_path }} | |
| asset_name: checksums-windows.txt | |
| asset_content_type: text/plain | |
| # Upload macOS artifacts | |
| - name: Upload DMG | |
| if: steps.find_files.outputs.dmg_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.dmg_path }} | |
| asset_name: Local_Lens_${{ github.ref_name }}_aarch64.dmg | |
| asset_content_type: application/x-apple-diskimage | |
| - name: Upload macOS app tarball (for updater) | |
| if: steps.find_files.outputs.tarball_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.tarball_path }} | |
| asset_name: Local_Lens.app.tar.gz | |
| asset_content_type: application/gzip | |
| - name: Upload macOS checksums | |
| if: steps.find_files.outputs.checksums_mac_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.checksums_mac_path }} | |
| asset_name: checksums-macos.txt | |
| asset_content_type: text/plain | |
| - name: Upload macOS fix script | |
| if: steps.find_files.outputs.fix_script_path | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: ${{ steps.find_files.outputs.fix_script_path }} | |
| asset_name: Fix_Local_Lens.command | |
| asset_content_type: application/x-sh | |
| - name: Upload Homebrew Cask formula | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: local-lens.rb | |
| asset_name: local-lens.rb | |
| asset_content_type: text/x-ruby | |
| # Upload latest.json for Tauri auto-updater | |
| - name: Upload latest.json | |
| uses: actions/upload-release-asset@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| asset_path: latest.json | |
| asset_name: latest.json | |
| asset_content_type: application/json | |
| - name: Release Summary | |
| run: | | |
| echo "✅ Release created successfully!" | |
| echo "📦 Uploaded artifacts:" | |
| echo " Windows: MSI, EXE, checksums" | |
| echo " macOS: DMG, app tarball, checksums, Fix_Local_Lens.command" | |
| echo "📋 latest.json generated for auto-updater (Windows + macOS)" | |
| echo "🍺 local-lens.rb generated for Homebrew tap" | |
| echo "🔒 Release created as draft - review and publish manually" |