Show date in tray reset timestamps #51
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 | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| branches: | |
| - '**' | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Build without creating release' | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| # Run on: tags, workflow_dispatch, or commits containing [build] in message | |
| if: > | |
| startsWith(github.ref, 'refs/tags/v') || | |
| github.event_name == 'workflow_dispatch' || | |
| contains(github.event.head_commit.message, '[build]') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: macos-latest | |
| target: darwin-aarch64 | |
| - os: macos-15 | |
| target: darwin-x86_64 | |
| - os: ubuntu-latest | |
| target: linux-x86_64 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: src-tauri -> target | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Install Linux build dependencies | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev \ | |
| libjavascriptcoregtk-4.1-dev \ | |
| libsoup-3.0-dev \ | |
| libappindicator3-dev \ | |
| librsvg2-dev \ | |
| libxss-dev \ | |
| libfuse2 \ | |
| patchelf \ | |
| pkg-config \ | |
| build-essential | |
| sudo apt-get install -y libfuse2t64 || true | |
| - name: Enable AppImage extract-and-run (no FUSE) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: echo "APPIMAGE_EXTRACT_AND_RUN=1" >> "$GITHUB_ENV" | |
| - name: Validate macOS signing secrets | |
| if: startsWith(matrix.target, 'darwin') | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| set -euo pipefail | |
| missing=0 | |
| require_secret() { | |
| if [[ -z "${2:-}" ]]; then | |
| echo "Missing required secret: $1" | |
| missing=1 | |
| fi | |
| } | |
| require_secret "APPLE_CERTIFICATE" "${APPLE_CERTIFICATE:-}" | |
| require_secret "APPLE_CERTIFICATE_PASSWORD" "${APPLE_CERTIFICATE_PASSWORD:-}" | |
| require_secret "KEYCHAIN_PASSWORD" "${KEYCHAIN_PASSWORD:-}" | |
| require_secret "APPLE_ID" "${APPLE_ID:-}" | |
| require_secret "APPLE_ID_PASSWORD" "${APPLE_ID_PASSWORD:-}" | |
| require_secret "APPLE_TEAM_ID" "${APPLE_TEAM_ID:-}" | |
| if [[ "$missing" -ne 0 ]]; then exit 1; fi | |
| - name: Import macOS signing certificate | |
| if: startsWith(matrix.target, 'darwin') | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12 | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| security list-keychains -d user -s build.keychain | |
| security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain | |
| rm certificate.p12 | |
| cert_info="$(security find-identity -v -p codesigning build.keychain | grep -m 1 'Developer ID Application' || true)" | |
| if [[ -z "${cert_info:-}" ]]; then | |
| echo "Failed to find 'Developer ID Application' identity" | |
| security find-identity -v -p codesigning build.keychain || true | |
| exit 1 | |
| fi | |
| cert_id="$(echo "$cert_info" | awk -F'\"' '{print $2}')" | |
| echo "APPLE_SIGNING_IDENTITY=$cert_id" >> "$GITHUB_ENV" | |
| - name: Build bundles | |
| run: bunx tauri build --ci | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| APPLE_ID: ${{ startsWith(matrix.target, 'darwin') && secrets.APPLE_ID || '' }} | |
| APPLE_ID_PASSWORD: ${{ startsWith(matrix.target, 'darwin') && secrets.APPLE_ID_PASSWORD || '' }} | |
| APPLE_TEAM_ID: ${{ startsWith(matrix.target, 'darwin') && secrets.APPLE_TEAM_ID || '' }} | |
| - name: Notarize + staple macOS artifacts | |
| if: startsWith(matrix.target, 'darwin') | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| set -euo pipefail | |
| app_path="$(find "src-tauri/target/release/bundle" -type d -name 'Claudometer.app' -print -quit || true)" | |
| if [[ -z "${app_path:-}" ]]; then | |
| echo "Failed to locate Claudometer.app" | |
| exit 1 | |
| fi | |
| dmg_path="$(find "src-tauri/target/release/bundle" -type f -name '*.dmg' -print -quit || true)" | |
| if [[ -n "${dmg_path:-}" ]]; then | |
| payload="$dmg_path" | |
| else | |
| payload_dir="$(mktemp -d)" | |
| payload="$payload_dir/Claudometer.app.zip" | |
| ditto -c -k --keepParent "$app_path" "$payload" | |
| fi | |
| xcrun notarytool submit "$payload" --wait \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$APPLE_ID_PASSWORD" \ | |
| --team-id "$APPLE_TEAM_ID" | |
| xcrun stapler staple -v "$app_path" | |
| if [[ -n "${dmg_path:-}" ]]; then | |
| xcrun stapler staple -v "$dmg_path" | |
| fi | |
| - name: Verify macOS signing | |
| if: startsWith(matrix.target, 'darwin') | |
| run: | | |
| set -euo pipefail | |
| app_path="$(find "src-tauri/target/release/bundle" -type d -name 'Claudometer.app' -print -quit)" | |
| codesign --verify --deep --strict --verbose=2 "$app_path" | |
| spctl -a -vv --type exec "$app_path" | |
| dmg_path="$(find "src-tauri/target/release/bundle" -type f -name '*.dmg' -print -quit || true)" | |
| if [[ -n "${dmg_path:-}" ]]; then | |
| xcrun stapler validate -v "$dmg_path" | |
| fi | |
| - name: Collect artifacts | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "artifacts/${{ matrix.target }}" | |
| find "src-tauri/target/release/bundle" -type f \( \ | |
| -name '*.AppImage' -o -name '*.AppImage.sig' -o \ | |
| -name '*.deb' -o -name '*.deb.sig' -o \ | |
| -name '*.rpm' -o -name '*.rpm.sig' -o \ | |
| -name '*.dmg' -o -name '*.dmg.sig' -o \ | |
| -name '*.app.tar.gz' -o -name '*.app.tar.gz.sig' \ | |
| \) -exec cp {} "artifacts/${{ matrix.target }}/" \; | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bundles-${{ matrix.target }} | |
| path: artifacts/${{ matrix.target }} | |
| retention-days: 7 | |
| create-release: | |
| name: Create Release | |
| needs: [build] | |
| if: startsWith(github.ref, 'refs/tags/v') && !inputs.dry_run | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Prepare release assets | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-assets | |
| for platform_dir in artifacts/bundles-*; do | |
| platform=$(basename "$platform_dir" | sed 's/bundles-//') | |
| for file in "$platform_dir"/*; do | |
| filename=$(basename "$file") | |
| if [[ "$filename" == "Claudometer.app.tar.gz" ]]; then | |
| cp "$file" "release-assets/Claudometer-${platform}.app.tar.gz" | |
| elif [[ "$filename" == "Claudometer.app.tar.gz.sig" ]]; then | |
| cp "$file" "release-assets/Claudometer-${platform}.app.tar.gz.sig" | |
| else | |
| cp "$file" "release-assets/$filename" | |
| fi | |
| done | |
| done | |
| echo "Release assets:" | |
| ls -la release-assets/ | |
| - name: Generate latest.json | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| linux_payload="$(ls -1 release-assets/*.AppImage | head -n 1)" | |
| linux_sig="${linux_payload}.sig" | |
| mac_arm_payload="release-assets/Claudometer-darwin-aarch64.app.tar.gz" | |
| mac_arm_sig="${mac_arm_payload}.sig" | |
| mac_x64_payload="release-assets/Claudometer-darwin-x86_64.app.tar.gz" | |
| mac_x64_sig="${mac_x64_payload}.sig" | |
| bun run scripts/generate-latest-json.ts \ | |
| --repo "$REPO" \ | |
| --tag "$TAG" \ | |
| --platform "linux-x86_64:${linux_payload}:${linux_sig}" \ | |
| --platform "darwin-aarch64:${mac_arm_payload}:${mac_arm_sig}" \ | |
| --platform "darwin-x86_64:${mac_x64_payload}:${mac_x64_sig}" \ | |
| --out latest.json | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Claudometer ${{ github.ref_name }} | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| files: | | |
| release-assets/* | |
| latest.json | |
| body: | | |
| ## Claudometer ${{ github.ref_name }} | |
| ### Downloads | |
| | Platform | Download | | |
| |----------|----------| | |
| | macOS (Apple Silicon) | `Claudometer-darwin-aarch64.dmg` | | |
| | macOS (Intel) | `Claudometer-darwin-x86_64.dmg` | | |
| | Linux (x86_64) | `Claudometer_*.AppImage` or `.deb` | | |
| ### Installation | |
| **macOS**: Download the `.dmg`, open it, and drag Claudometer to Applications. | |
| **Linux**: Download the `.AppImage` and make it executable (`chmod +x`), or install the `.deb` package. | |
| build-summary: | |
| name: Build Summary | |
| needs: [build] | |
| if: always() && needs.build.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Summary | |
| run: | | |
| echo "## Build Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Platform | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| darwin-aarch64 | ${{ needs.build.result == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| darwin-x86_64 | ${{ needs.build.result == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| linux-x86_64 | ${{ needs.build.result == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ github.ref }}" == refs/tags/v* ]]; then | |
| echo "📦 **Release will be created** (tag detected)" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "ℹ️ **No release** - push a tag (e.g., \`git tag v1.0.0 && git push --tags\`) to create a release" >> $GITHUB_STEP_SUMMARY | |
| fi |