v0.20.0 #49
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 Binary | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| build-tauri: | |
| permissions: | |
| contents: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos-latest | |
| arch: universal | |
| rust_targets: "" | |
| tauri_args: --target universal-apple-darwin | |
| - platform: ubuntu-22.04 | |
| arch: amd64 | |
| rust_targets: x86_64-unknown-linux-gnu | |
| tauri_args: "" | |
| - platform: ubuntu-22.04-arm | |
| arch: arm64 | |
| rust_targets: aarch64-unknown-linux-gnu | |
| tauri_args: "" | |
| - platform: windows-latest | |
| arch: x64 | |
| rust_targets: x86_64-pc-windows-msvc | |
| tauri_args: "" | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Add Rust targets | |
| if: matrix.rust_targets != '' | |
| run: rustup target add ${{ matrix.rust_targets }} | |
| - name: Add macOS cross-compilation targets | |
| if: matrix.platform == 'macos-latest' | |
| run: | | |
| rustup target add aarch64-apple-darwin | |
| rustup target add x86_64-apple-darwin | |
| - name: Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: ". -> target" | |
| key: ${{ matrix.platform }} | |
| - name: Install dependencies (Ubuntu) | |
| if: startsWith(matrix.platform, 'ubuntu') | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-dev librsvg2-dev patchelf libgtk-3-dev libayatana-appindicator3-dev xdg-utils | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "npm" | |
| cache-dependency-path: clipper/package-lock.json | |
| - name: Install shared UI dependencies | |
| working-directory: packages/clipper-ui | |
| run: npm ci | |
| - name: Install frontend dependencies | |
| working-directory: clipper | |
| run: npm ci | |
| # macOS code signing setup | |
| - name: Import Apple Developer Certificate (macOS) | |
| if: matrix.platform == 'macos-latest' | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| # Create a temporary keychain | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| # Import certificate to keychain | |
| CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12 | |
| echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH | |
| security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
| security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| # Verify the certificate was imported | |
| security find-identity -v $KEYCHAIN_PATH | |
| - name: Build Tauri app | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # macOS code signing environment variables | |
| APPLE_SIGNING_IDENTITY: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_SIGNING_IDENTITY || '' }} | |
| APPLE_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_ID || '' }} | |
| APPLE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_PASSWORD || '' }} | |
| APPLE_TEAM_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_TEAM_ID || '' }} | |
| # Updater signing keys (for auto-update functionality) | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| with: | |
| projectPath: clipper | |
| tauriScript: npx tauri | |
| args: ${{ matrix.tauri_args }} | |
| # Cleanup keychain (macOS) | |
| - name: Cleanup keychain (macOS) | |
| if: matrix.platform == 'macos-latest' && always() | |
| run: | | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| if [ -f "$KEYCHAIN_PATH" ]; then | |
| security delete-keychain $KEYCHAIN_PATH | |
| fi | |
| # Find and upload artifacts | |
| - name: Find and rename bundle files | |
| id: find-bundles | |
| shell: bash | |
| run: | | |
| mkdir -p release-artifacts | |
| # Debug: List all files in target directory that might be relevant | |
| echo "=== Debug: Searching for all potential artifacts ===" | |
| echo "Looking for .tar.gz files:" | |
| find target -name "*.tar.gz" -type f 2>/dev/null || true | |
| echo "Looking for .sig files:" | |
| find target -name "*.sig" -type f 2>/dev/null || true | |
| echo "Looking for .nsis.zip files:" | |
| find target -name "*.nsis.zip" -type f 2>/dev/null || true | |
| echo "=== End debug output ===" | |
| if [ "${{ matrix.platform }}" == "macos-latest" ]; then | |
| DMG_PATH=$(find target -name "*.dmg" -type f | head -1) | |
| if [ -n "$DMG_PATH" ]; then | |
| cp "$DMG_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_macos_universal.dmg" | |
| fi | |
| # Also include app.zip for users who prefer manual installation | |
| APP_PATH=$(find target -path "*/bundle/macos/Clipper.app" -type d | head -1) | |
| if [ -n "$APP_PATH" ]; then | |
| APP_DIR=$(dirname "$APP_PATH") | |
| (cd "$APP_DIR" && zip -r -y "$OLDPWD/release-artifacts/Clipper_${{ github.ref_name }}_macos_universal.app.zip" "Clipper.app") | |
| fi | |
| # Copy updater artifacts (tar.gz and signature) | |
| # Tauri generates .app.tar.gz for macOS updater | |
| UPDATER_TAR=$(find target -name "*.app.tar.gz" -type f | head -1) | |
| UPDATER_SIG=$(find target -name "*.app.tar.gz.sig" -type f | head -1) | |
| if [ -n "$UPDATER_TAR" ]; then | |
| echo "Found macOS updater tar.gz: $UPDATER_TAR" | |
| cp "$UPDATER_TAR" "release-artifacts/Clipper_${{ github.ref_name }}_macos_universal.app.tar.gz" | |
| else | |
| echo "WARNING: macOS updater tar.gz not found" | |
| fi | |
| if [ -n "$UPDATER_SIG" ]; then | |
| echo "Found macOS updater signature: $UPDATER_SIG" | |
| cp "$UPDATER_SIG" "release-artifacts/Clipper_${{ github.ref_name }}_macos_universal.app.tar.gz.sig" | |
| else | |
| echo "WARNING: macOS updater signature not found" | |
| fi | |
| elif [[ "${{ matrix.platform }}" == ubuntu* ]]; then | |
| DEB_PATH=$(find target -name "*.deb" -type f | head -1) | |
| RPM_PATH=$(find target -name "*.rpm" -type f | head -1) | |
| APPIMAGE_PATH=$(find target -name "*.AppImage" -type f | head -1) | |
| if [ -n "$DEB_PATH" ]; then | |
| cp "$DEB_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_${{ matrix.arch }}.deb" | |
| fi | |
| if [ -n "$RPM_PATH" ]; then | |
| cp "$RPM_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_${{ matrix.arch }}.rpm" | |
| fi | |
| if [ -n "$APPIMAGE_PATH" ]; then | |
| cp "$APPIMAGE_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_${{ matrix.arch }}.AppImage" | |
| fi | |
| # Copy updater signature for the .AppImage file | |
| # Tauri generates *.AppImage.sig for Linux updater when TAURI_SIGNING_PRIVATE_KEY is set | |
| UPDATER_SIG=$(find target -name "*.AppImage.sig" -type f | head -1) | |
| if [ -n "$UPDATER_SIG" ]; then | |
| echo "Found Linux updater signature: $UPDATER_SIG" | |
| cp "$UPDATER_SIG" "release-artifacts/Clipper_${{ github.ref_name }}_${{ matrix.arch }}.AppImage.sig" | |
| else | |
| echo "WARNING: Linux updater signature not found - will sign manually in publish step" | |
| fi | |
| elif [ "${{ matrix.platform }}" == "windows-latest" ]; then | |
| MSI_PATH=$(find target -name "*.msi" -type f | head -1) | |
| NSIS_PATH=$(find target -name "*-setup.exe" -type f | head -1) | |
| if [ -n "$MSI_PATH" ]; then | |
| cp "$MSI_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_x64.msi" | |
| fi | |
| if [ -n "$NSIS_PATH" ]; then | |
| cp "$NSIS_PATH" "release-artifacts/Clipper_${{ github.ref_name }}_x64-setup.exe" | |
| fi | |
| # Copy updater signature for the .exe file | |
| # Tauri generates *-setup.exe.sig for Windows updater when TAURI_SIGNING_PRIVATE_KEY is set | |
| UPDATER_SIG=$(find target -name "*-setup.exe.sig" -type f | head -1) | |
| if [ -n "$UPDATER_SIG" ]; then | |
| echo "Found Windows updater signature: $UPDATER_SIG" | |
| cp "$UPDATER_SIG" "release-artifacts/Clipper_${{ github.ref_name }}_x64-setup.exe.sig" | |
| else | |
| echo "WARNING: Windows updater signature not found - will sign manually in publish step" | |
| fi | |
| fi | |
| echo "=== Final artifacts ===" | |
| ls -la release-artifacts/ | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ matrix.platform }} | |
| path: release-artifacts/* | |
| if-no-files-found: error | |
| publish-release: | |
| needs: [build-tauri] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: release-* | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -la artifacts/ | |
| - name: Setup Node.js for signing | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Sign updater artifacts if missing signatures | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| # Install Tauri CLI for signing | |
| npm install -g @tauri-apps/cli | |
| # Sign Linux amd64 if signature is missing | |
| if [ -f "artifacts/Clipper_${VERSION}_amd64.AppImage" ] && [ ! -f "artifacts/Clipper_${VERSION}_amd64.AppImage.sig" ]; then | |
| echo "Signing Linux amd64 updater artifact..." | |
| npx tauri signer sign "artifacts/Clipper_${VERSION}_amd64.AppImage" --private-key "$TAURI_SIGNING_PRIVATE_KEY" --password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" | |
| fi | |
| # Sign Linux arm64 if signature is missing | |
| if [ -f "artifacts/Clipper_${VERSION}_arm64.AppImage" ] && [ ! -f "artifacts/Clipper_${VERSION}_arm64.AppImage.sig" ]; then | |
| echo "Signing Linux arm64 updater artifact..." | |
| npx tauri signer sign "artifacts/Clipper_${VERSION}_arm64.AppImage" --private-key "$TAURI_SIGNING_PRIVATE_KEY" --password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" | |
| fi | |
| # Sign Windows if signature is missing | |
| if [ -f "artifacts/Clipper_${VERSION}_x64-setup.exe" ] && [ ! -f "artifacts/Clipper_${VERSION}_x64-setup.exe.sig" ]; then | |
| echo "Signing Windows updater artifact..." | |
| npx tauri signer sign "artifacts/Clipper_${VERSION}_x64-setup.exe" --private-key "$TAURI_SIGNING_PRIVATE_KEY" --password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" | |
| fi | |
| echo "=== Signature files after signing ===" | |
| ls -la artifacts/*.sig 2>/dev/null || echo "No .sig files found" | |
| - name: Generate latest.json for auto-updater | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| VERSION_NUM="${VERSION#v}" # Remove 'v' prefix | |
| RELEASE_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| REPO_URL="https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}" | |
| # Debug: List all downloaded artifacts | |
| echo "=== Downloaded artifacts ===" | |
| ls -la artifacts/ | |
| echo "=== Signature files ===" | |
| ls -la artifacts/*.sig 2>/dev/null || echo "No .sig files found" | |
| # Read signatures from .sig files | |
| MACOS_SIG="" | |
| LINUX_AMD64_SIG="" | |
| LINUX_ARM64_SIG="" | |
| WINDOWS_SIG="" | |
| if [ -f "artifacts/Clipper_${VERSION}_macos_universal.app.tar.gz.sig" ]; then | |
| MACOS_SIG=$(cat "artifacts/Clipper_${VERSION}_macos_universal.app.tar.gz.sig") | |
| echo "Found macOS signature" | |
| else | |
| echo "Missing macOS signature: artifacts/Clipper_${VERSION}_macos_universal.app.tar.gz.sig" | |
| fi | |
| if [ -f "artifacts/Clipper_${VERSION}_amd64.AppImage.sig" ]; then | |
| LINUX_AMD64_SIG=$(cat "artifacts/Clipper_${VERSION}_amd64.AppImage.sig") | |
| echo "Found Linux amd64 signature" | |
| else | |
| echo "Missing Linux amd64 signature: artifacts/Clipper_${VERSION}_amd64.AppImage.sig" | |
| fi | |
| if [ -f "artifacts/Clipper_${VERSION}_arm64.AppImage.sig" ]; then | |
| LINUX_ARM64_SIG=$(cat "artifacts/Clipper_${VERSION}_arm64.AppImage.sig") | |
| echo "Found Linux arm64 signature" | |
| else | |
| echo "Missing Linux arm64 signature: artifacts/Clipper_${VERSION}_arm64.AppImage.sig" | |
| fi | |
| if [ -f "artifacts/Clipper_${VERSION}_x64-setup.exe.sig" ]; then | |
| WINDOWS_SIG=$(cat "artifacts/Clipper_${VERSION}_x64-setup.exe.sig") | |
| echo "Found Windows signature" | |
| else | |
| echo "Missing Windows signature: artifacts/Clipper_${VERSION}_x64-setup.exe.sig" | |
| fi | |
| # Create latest.json using jq for proper JSON generation | |
| jq -n \ | |
| --arg version "$VERSION_NUM" \ | |
| --arg notes "See the release notes at https://github.com/${{ github.repository }}/releases/tag/${VERSION}" \ | |
| --arg pub_date "$RELEASE_DATE" \ | |
| --arg macos_sig "$MACOS_SIG" \ | |
| --arg macos_url "${REPO_URL}/Clipper_${VERSION}_macos_universal.app.tar.gz" \ | |
| --arg linux_amd64_sig "$LINUX_AMD64_SIG" \ | |
| --arg linux_amd64_url "${REPO_URL}/Clipper_${VERSION}_amd64.AppImage" \ | |
| --arg linux_arm64_sig "$LINUX_ARM64_SIG" \ | |
| --arg linux_arm64_url "${REPO_URL}/Clipper_${VERSION}_arm64.AppImage" \ | |
| --arg windows_sig "$WINDOWS_SIG" \ | |
| --arg windows_url "${REPO_URL}/Clipper_${VERSION}_x64-setup.exe" \ | |
| '{ | |
| version: $version, | |
| notes: $notes, | |
| pub_date: $pub_date, | |
| platforms: { | |
| "darwin-aarch64": { signature: $macos_sig, url: $macos_url }, | |
| "darwin-x86_64": { signature: $macos_sig, url: $macos_url }, | |
| "linux-x86_64": { signature: $linux_amd64_sig, url: $linux_amd64_url }, | |
| "linux-aarch64": { signature: $linux_arm64_sig, url: $linux_arm64_url }, | |
| "windows-x86_64": { signature: $windows_sig, url: $windows_url } | |
| } | |
| }' > artifacts/latest.json | |
| echo "=== Generated latest.json ===" | |
| cat artifacts/latest.json | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| draft: false | |
| files: artifacts/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |