Skip to content

Release

Release #29

Workflow file for this run

name: Release
# Push a version tag (e.g. v0.1.0) to build the app and publish a GitHub Release.
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
release:
# Only build/publish for version tags. workflow_dispatch from a branch would
# otherwise set the version/tag to the branch name and publish a malformed
# release (and fail the Homebrew step on the resulting 404).
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
strategy:
# One platform's failure must not cancel the other — a broken Windows
# bundler shouldn't block the macOS .dmg from publishing (and vice versa).
fail-fast: false
matrix:
include:
- os: macos-latest
target: universal-apple-darwin
rust-targets: aarch64-apple-darwin,x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
rust-targets: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-targets }}
- name: Cache Rust
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: Install frontend deps
run: pnpm install
- name: Build & publish release
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --- Code signing + notarization (optional, macOS only) ---
# UNSIGNED BUILD BY DEFAULT. Do NOT uncomment these until the matching
# secrets actually exist: tauri's bundler treats an empty
# APPLE_CERTIFICATE env var as "a certificate is present" and then
# fails on `security import` ("SecKeychainItemImport ... not valid").
#
# Once you have an Apple Developer ID, add these in repo
# Settings → Secrets and uncomment to get signed + notarized builds:
#
# APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} # base64 of Developer ID .p12
# APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} # .p12 export password
# APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} # "Developer ID Application: Name (TEAMID)"
# APPLE_ID: ${{ secrets.APPLE_ID }} # Apple ID email
# APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} # app-specific password
# APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} # 10-char Team ID
# Windows builds are also unsigned — users get a SmartScreen warning
# on first launch, see README "Install on Windows".
with:
tagName: ${{ github.ref_name }}
releaseName: "Tokenscope ${{ github.ref_name }}"
releaseBody: |
Menu-bar / system-tray dashboard for Claude CLI token usage.
**macOS**: download `Tokenscope_*_universal.dmg` (Apple Silicon + Intel). Unsigned — right-click → **Open** on first launch, or install via `brew install --cask hdusy/tokenscope/tokenscope`.
**Windows**: download `Tokenscope_*_x64-setup.exe`. Unsigned — on first launch click **More info → Run anyway** to dismiss SmartScreen.
# Publish immediately (not a draft): the asset's public download URL
# must be live for the Homebrew Cask step below to fetch it and for
# `brew install` to work.
releaseDraft: false
prerelease: false
args: --target ${{ matrix.target }}
# Append an auto-generated changelog (PRs/commits since the previous tag)
# to the release notes. Best-effort: never fail the release over notes.
# Only run once (on the macOS leg) — both jobs publish to the same release.
- name: Add changelog to release
if: matrix.os == 'macos-latest'
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gen=$(gh api --method POST \
"repos/${{ github.repository }}/releases/generate-notes" \
-f tag_name="${{ github.ref_name }}" --jq '.body' || true)
cur=$(gh release view "${{ github.ref_name }}" --json body --jq '.body' || true)
{
printf '%s\n' "$cur"
if [ -n "$gen" ]; then printf '\n%s\n' "$gen"; fi
} > release-body.md
gh release edit "${{ github.ref_name }}" --notes-file release-body.md
- name: Update Homebrew Cask
if: matrix.os == 'macos-latest'
env:
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
VERSION="${GITHUB_REF_NAME#v}"
DMG_NAME="Tokenscope_${VERSION}_universal.dmg"
DOWNLOAD_URL="https://github.com/HduSy/tokenscope/releases/download/${GITHUB_REF_NAME}/${DMG_NAME}"
# Wait for the release asset to be available, then compute sha256.
# -f makes curl fail (non-zero) on a 404 so we keep retrying instead
# of hashing GitHub's error page and writing a bogus checksum.
SHA256=""
for i in $(seq 1 10); do
if curl -fsSL "$DOWNLOAD_URL" -o /tmp/tokenscope.dmg; then
SHA256=$(shasum -a 256 /tmp/tokenscope.dmg | cut -d' ' -f1)
break
fi
echo "Retry $i: asset not ready yet..."
sleep 10
done
if [ -z "$SHA256" ]; then
echo "::error::Release asset $DMG_NAME never became available"; exit 1
fi
# Clone the tap repo and update the cask
git clone https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/HduSy/homebrew-tokenscope.git /tmp/homebrew-tap
cat > /tmp/homebrew-tap/Casks/tokenscope.rb <<CASK
cask "tokenscope" do
version "${VERSION}"
sha256 "${SHA256}"
url "${DOWNLOAD_URL}"
name "Tokenscope"
desc "macOS menu-bar dashboard for Claude CLI token usage"
homepage "https://github.com/HduSy/tokenscope"
depends_on macos: ">= :catalina"
app "Tokenscope.app"
# Unsigned/unnotarized build: strip the quarantine flag Homebrew
# adds so the app opens without the "Apple cannot verify" prompt.
postflight do
system_command "/usr/bin/xattr",
args: ["-cr", "#{appdir}/Tokenscope.app"],
sudo: false
end
end
CASK
cd /tmp/homebrew-tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/tokenscope.rb
git diff --cached --quiet || git commit -m "Update cask to v${VERSION}"
git push