Merge pull request #166 from Arylmera/develop #42
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-tauri | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["v4.*"] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to attach artifacts to (e.g. v4.0.0-rc.1)" | |
| required: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| tag: | |
| name: auto-tag on version bump | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write | |
| outputs: | |
| tag: ${{ steps.maybe-tag.outputs.tag }} | |
| tagged: ${{ steps.maybe-tag.outputs.tagged }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| - id: maybe-tag | |
| name: Tag if Cargo.toml version is new | |
| run: | | |
| ver=$(grep '^version' crates/token-dashboard-tauri/Cargo.toml | head -1 | sed -E 's/.*"([^"]+)".*/\1/') | |
| tag="v$ver" | |
| if [ -z "$ver" ]; then | |
| echo "could not parse version" >&2 | |
| exit 1 | |
| fi | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then | |
| echo "tag $tag already exists on origin — skipping" | |
| echo "tagged=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$tag" -m "Release $tag" | |
| git push origin "$tag" | |
| echo "tagged=true" >> "$GITHUB_OUTPUT" | |
| # Tags pushed via GITHUB_TOKEN do NOT trigger downstream workflow | |
| # runs, so the build/release jobs below chain off this job's | |
| # `tagged` output instead of waiting for the tag-push event. | |
| build: | |
| name: build (${{ matrix.label }}) | |
| needs: [tag] | |
| if: | | |
| always() && ( | |
| startsWith(github.ref, 'refs/tags/v4.') | |
| || github.event_name == 'workflow_dispatch' | |
| || needs.tag.outputs.tagged == 'true' | |
| ) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Cargo workspaces put bundle outputs in the workspace-root | |
| # `target/`, NOT inside the crate that owns tauri.conf.json. | |
| # macOS: build both arches explicitly with --target so the DMG | |
| # filenames carry the correct arch suffix (a plain `cargo tauri | |
| # build` on macos-14 was producing an `_x64.dmg`, locking out | |
| # Apple Silicon installs via scripts/install.sh). | |
| - os: windows-2022 | |
| label: windows | |
| artifact_glob: | | |
| target/release/bundle/msi/*.msi | |
| - os: macos-14 | |
| label: macos-arm64 | |
| rust_target: aarch64-apple-darwin | |
| artifact_glob: | | |
| target/aarch64-apple-darwin/release/bundle/dmg/*.dmg | |
| target/aarch64-apple-darwin/release/bundle/macos/*.app.tar.gz | |
| - os: macos-14 | |
| label: macos-x64 | |
| rust_target: x86_64-apple-darwin | |
| artifact_glob: | | |
| target/x86_64-apple-darwin/release/bundle/dmg/*.dmg | |
| target/x86_64-apple-darwin/release/bundle/macos/*.app.tar.gz | |
| - os: ubuntu-22.04 | |
| label: linux | |
| artifact_glob: | | |
| target/release/bundle/deb/*.deb | |
| target/release/bundle/appimage/*.AppImage | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust_target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: ". -> target" | |
| key: ${{ matrix.label }} | |
| save-if: ${{ startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' }} | |
| - name: System deps (linux) | |
| if: matrix.os == 'ubuntu-22.04' | |
| uses: awalsh128/cache-apt-pkgs-action@v1 | |
| with: | |
| # libgtk-3-dev is a transitive dep of libwebkit2gtk-4.1-dev, but | |
| # cache-apt-pkgs-action only restores explicitly-listed packages on | |
| # a cache hit — so gdk-3.0.pc went missing and gdk-sys failed to | |
| # build. List it explicitly. Bump `version` to invalidate the stale | |
| # cache that lacks it. | |
| packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev | |
| version: 1.1 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Build frontend bundle | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm run build | |
| - name: Install tauri-cli (prebuilt) | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: tauri-cli | |
| - name: Tauri build | |
| working-directory: crates/token-dashboard-tauri | |
| shell: bash | |
| # bundle_dmg.sh on macos-14 (Apple Silicon) flakes intermittently | |
| # when hdiutil's attach/detach races with Spotlight indexing. Build | |
| # the .app first (cheap to redo), then retry the DMG bundle up to | |
| # 3 times. Linux/Windows take the single-shot path. | |
| run: | | |
| set -e | |
| target_flag="" | |
| if [ -n "${{ matrix.rust_target }}" ]; then | |
| target_flag="--target ${{ matrix.rust_target }}" | |
| fi | |
| if [ "${{ runner.os }}" = "macOS" ]; then | |
| cargo tauri build $target_flag --bundles app | |
| attempt=1 | |
| until cargo tauri build $target_flag --bundles dmg; do | |
| if [ $attempt -ge 3 ]; then | |
| echo "bundle_dmg.sh failed after $attempt attempts" >&2 | |
| exit 1 | |
| fi | |
| echo "bundle_dmg.sh failed (attempt $attempt) — retrying after 15s" | |
| attempt=$((attempt + 1)) | |
| sleep 15 | |
| done | |
| else | |
| cargo tauri build $target_flag | |
| fi | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: token-dashboard-${{ matrix.label }} | |
| path: ${{ matrix.artifact_glob }} | |
| if-no-files-found: error | |
| release: | |
| name: attach to GitHub Release | |
| needs: [tag, build] | |
| runs-on: ubuntu-22.04 | |
| if: | | |
| always() && needs.build.result == 'success' && ( | |
| startsWith(github.ref, 'refs/tags/v4.') | |
| || needs.tag.outputs.tagged == 'true' | |
| || (github.event_name == 'workflow_dispatch' && inputs.tag != '') | |
| ) | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| - name: Create / update release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.tag.outputs.tag || inputs.tag || github.ref_name }} | |
| files: dist/**/* | |
| fail_on_unmatched_files: false | |
| generate_release_notes: true | |
| winget: | |
| name: Submit to winget-pkgs | |
| needs: [tag, release] | |
| if: | | |
| always() && needs.release.result == 'success' && ( | |
| startsWith(github.ref, 'refs/tags/v4.') | |
| || needs.tag.outputs.tagged == 'true' | |
| || (github.event_name == 'workflow_dispatch' && inputs.tag != '') | |
| ) | |
| runs-on: windows-latest | |
| # Non-blocking: winget-releaser only updates EXISTING manifests. The | |
| # initial Arylmera.TokenDashboard submission must be done manually via | |
| # `wingetcreate new` (one-time). Until merged into microsoft/winget-pkgs, | |
| # this job will fail — that should not mark the release red. | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve tag/version | |
| id: v | |
| shell: bash | |
| run: | | |
| tag="${{ needs.tag.outputs.tag || inputs.tag || github.ref_name }}" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=${tag#v}" >> "$GITHUB_OUTPUT" | |
| - name: Check WINGET_TOKEN | |
| id: check | |
| shell: bash | |
| env: | |
| WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }} | |
| run: | | |
| if [ -z "${WINGET_TOKEN:-}" ]; then | |
| echo "WINGET_TOKEN not set — skipping winget submission." | |
| echo "has_token=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_token=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Submit manifest | |
| if: steps.check.outputs.has_token == 'true' | |
| uses: vedantmgoyal2009/winget-releaser@v2 | |
| with: | |
| identifier: Arylmera.TokenDashboard | |
| version: ${{ steps.v.outputs.version }} | |
| installers-regex: 'Token\.Dashboard_.*_x64_en-US\.msi$' | |
| release-tag: ${{ steps.v.outputs.tag }} | |
| token: ${{ secrets.WINGET_TOKEN }} | |
| max-versions-to-keep: 5 | |
| fork-user: Arylmera | |
| homebrew: | |
| name: Update Homebrew tap | |
| needs: [tag, release] | |
| if: | | |
| always() && needs.release.result == 'success' && ( | |
| startsWith(github.ref, 'refs/tags/v4.') | |
| || needs.tag.outputs.tagged == 'true' | |
| || (github.event_name == 'workflow_dispatch' && inputs.tag != '') | |
| ) | |
| runs-on: ubuntu-22.04 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve tag/version | |
| id: v | |
| run: | | |
| tag="${{ needs.tag.outputs.tag || inputs.tag || github.ref_name }}" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=${tag#v}" >> "$GITHUB_OUTPUT" | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| - name: Update Homebrew tap | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| VERSION: ${{ steps.v.outputs.version }} | |
| TAP_REPO: Arylmera/homebrew-token-dashboard | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${HOMEBREW_TAP_TOKEN:-}" ]; then | |
| echo "HOMEBREW_TAP_TOKEN secret not set — skipping tap update." | |
| exit 0 | |
| fi | |
| # The cask is arch-aware: the URL resolves to the aarch64 DMG on | |
| # Apple Silicon and the x64 DMG on Intel, so each arch needs its | |
| # own sha256. Hashing only one arch (the old bug) guaranteed a | |
| # checksum mismatch for the other half of macOS users. | |
| dmg_arm=$(find dist -type f -name '*aarch64*.dmg' | head -1 || true) | |
| dmg_intel=$(find dist -type f \( -name '*x64*.dmg' -o -name '*x86_64*.dmg' \) | head -1 || true) | |
| if [ -z "$dmg_arm" ] || [ ! -f "$dmg_arm" ] || [ -z "$dmg_intel" ] || [ ! -f "$dmg_intel" ]; then | |
| echo "Missing one or both macOS DMGs — skipping tap update." >&2 | |
| find dist -maxdepth 3 -name '*.dmg' -print || true | |
| exit 0 | |
| fi | |
| sha_arm=$(sha256sum "$dmg_arm" | awk '{print $1}') | |
| sha_intel=$(sha256sum "$dmg_intel" | awk '{print $1}') | |
| echo "arm64 DMG: $dmg_arm sha256: $sha_arm" | |
| echo "x64 DMG: $dmg_intel sha256: $sha_intel" | |
| tmp=$(mktemp -d) | |
| git clone --depth=1 \ | |
| "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/${TAP_REPO}.git" \ | |
| "$tmp/tap" | |
| mkdir -p "$tmp/tap/Casks" | |
| cp Casks/token-dashboard.rb "$tmp/tap/Casks/token-dashboard.rb" | |
| sed -i.bak -E \ | |
| -e "s/^ version \".*\"$/ version \"${VERSION}\"/" \ | |
| -e "s/^ sha256 arm: +\".*\",$/ sha256 arm: \"${sha_arm}\",/" \ | |
| -e "s/^ intel: +\".*\"$/ intel: \"${sha_intel}\"/" \ | |
| "$tmp/tap/Casks/token-dashboard.rb" | |
| rm -f "$tmp/tap/Casks/token-dashboard.rb.bak" | |
| cd "$tmp/tap" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add Casks/token-dashboard.rb | |
| if git diff --cached --quiet; then | |
| echo "Cask already up to date." | |
| else | |
| git commit -m "token-dashboard ${VERSION}" | |
| # Empty repos have no default branch yet; push to main explicitly. | |
| git branch -M main | |
| git push -u origin main | |
| fi |