mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-10-21 22:16:30 +00:00
ci: scope the updater signing key to signing steps and split the legacy publish job
The signing key moves from job-level to step-level env, so npm install scripts, setup actions and crate build scripts never see it. Linux compiles without the key and signs in a separate tauri bundle step. The legacy rollback builds and signs under a read-only, unpersisted token with the locked Tauri CLI, and a separate job holds the write token to publish.
This commit is contained in:
1 parent
81f29f2e4f
commit
9a9b01ff07
4 files changed
+94
-28
No files matched your search
@@ -6,6 +6,10 @@ name: Release legacy
|
||||
# the signed `latest.json`, and Tauri Linux clients find no entry for their
|
||||
# platform, which they report as up to date. The Win32 app accepts the
|
||||
# `--wait-pid <pid> --updated-to X` relaunch the Tauri updater uses.
|
||||
#
|
||||
# The build job holds a read-only token that is never written to disk, and
|
||||
# only the steps that check or use the updater signing key see it. The write
|
||||
# token lives in the publish job, which builds nothing.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -31,11 +35,9 @@ jobs:
|
||||
win-x64:
|
||||
name: Windows x64 (legacy)
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
sha: ${{ steps.sha.outputs.sha }}
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
defaults:
|
||||
@@ -43,6 +45,8 @@ jobs:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Check the inputs and the signing key
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
run: |
|
||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::error::version '$VERSION' is not X.Y.Z."
|
||||
@@ -60,13 +64,19 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
# The legacy ref predates the release script, so take it from the
|
||||
# default branch.
|
||||
# The legacy ref predates package.json and the release script, so take
|
||||
# both from the default branch.
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
path: current
|
||||
persist-credentials: false
|
||||
|
||||
- name: Record the commit the release points at
|
||||
id: sha
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
@@ -75,6 +85,12 @@ jobs:
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: npm
|
||||
cache-dependency-path: current/package-lock.json
|
||||
|
||||
# The Tauri CLI that signs the exe comes from the reviewed lockfile,
|
||||
# not from a version resolved at run time.
|
||||
- run: npm ci --prefix current
|
||||
|
||||
# `cargo update --workspace` rewrites only the package's own version
|
||||
# in Cargo.lock, so the locked build below keeps every dependency.
|
||||
@@ -90,21 +106,55 @@ jobs:
|
||||
|
||||
- run: cargo build --release --locked
|
||||
|
||||
# The legacy ref has no package.json, so the Tauri CLI comes from npx.
|
||||
# `--no` refuses to download a CLI that the lockfile did not install.
|
||||
- name: Sign the exe
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
exe=target/release/claude-code-usage-bubble.exe
|
||||
npx --yes @tauri-apps/cli@2 signer sign --app-version "$VERSION" "$exe"
|
||||
npm exec --prefix current --no -- tauri signer sign --app-version "$VERSION" "$exe"
|
||||
test -s "$exe.sig"
|
||||
mkdir -p dist
|
||||
cp "$exe" "$exe.sig" dist/
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: release-legacy-win-x64
|
||||
path: |
|
||||
target/release/claude-code-usage-bubble.exe
|
||||
target/release/claude-code-usage-bubble.exe.sig
|
||||
retention-days: 7
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
needs: win-x64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
TARGET: ${{ needs.win-x64.outputs.sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: release-legacy-win-x64
|
||||
path: dist
|
||||
|
||||
- name: Write the Windows-only latest.json and check the asset set
|
||||
run: node current/scripts/make-latest-json.js --windows-only "$TAG" dist
|
||||
run: node scripts/make-latest-json.js --windows-only "$TAG" dist
|
||||
|
||||
- name: Create the release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DRAFT: ${{ inputs.draft }}
|
||||
run: |
|
||||
if [ "$DRAFT" = true ]; then
|
||||
@@ -112,5 +162,5 @@ jobs:
|
||||
else
|
||||
mode=(--latest)
|
||||
fi
|
||||
gh release create "$TAG" dist/* --target "$(git rev-parse HEAD)" \
|
||||
gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --target "$TARGET" \
|
||||
--title "$TAG" --generate-notes "${mode[@]}"
|
||||
@@ -76,12 +76,14 @@ jobs:
|
||||
name: Windows x64
|
||||
needs: prepare
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
# The updater signing key decides what every installed client will
|
||||
# install, so only the steps that check or use it see it: never
|
||||
# `npm ci` install scripts, the setup actions or crate build scripts.
|
||||
steps:
|
||||
- name: Refuse a missing signing key
|
||||
shell: bash
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
run: |
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY//[[:space:]]/}" ]; then
|
||||
echo "::error::The TAURI_SIGNING_PRIVATE_KEY secret is empty; an unsigned build cannot be released."
|
||||
@@ -113,6 +115,8 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
exe=target/release/claude-code-usage-bubble.exe
|
||||
npx tauri signer sign --app-version "$VERSION" "$exe"
|
||||
@@ -131,11 +135,11 @@ jobs:
|
||||
name: Linux x64
|
||||
needs: prepare
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
# As on Windows, only the steps that check or use the signing key see it.
|
||||
steps:
|
||||
- name: Refuse a missing signing key
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
run: |
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY//[[:space:]]/}" ]; then
|
||||
echo "::error::The TAURI_SIGNING_PRIVATE_KEY secret is empty; an unsigned build cannot be released."
|
||||
@@ -163,12 +167,18 @@ jobs:
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Build the AppImage and deb with updater signatures
|
||||
# Compile without the key, then bundle and sign with it: `tauri bundle`
|
||||
# packages the binary already built and runs no cargo build scripts.
|
||||
- run: npm run tauri build -- --no-bundle -- --locked
|
||||
|
||||
- name: Bundle the AppImage and deb with updater signatures
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: >-
|
||||
npm run tauri build --
|
||||
npm run tauri bundle --
|
||||
--bundles appimage,deb
|
||||
--config '{"bundle":{"createUpdaterArtifacts":true}}'
|
||||
-- --locked
|
||||
|
||||
# The bundler names outputs after productName, which has spaces, and
|
||||
# GitHub rewrites spaces in asset names, so the URLs in latest.json
|
||||
|
||||
@@ -28,7 +28,8 @@ below matters: bump Cargo *before* you tag.
|
||||
The build jobs need the `TAURI_SIGNING_PRIVATE_KEY` and
|
||||
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD` secrets and a non-empty
|
||||
`plugins.updater.pubkey` in `tauri.conf.json`; the workflow fails
|
||||
before building without them.
|
||||
before building without them. Only the steps that sign see the key:
|
||||
dependency installs and compilation run without it.
|
||||
|
||||
## Testing without a real tag
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ host has no patchelf and no display.
|
||||
- Triggers: tag `v*.*.*`, or `workflow_dispatch` with a `tag` input (a draft release). Keep the tag-equals-`Cargo.toml`-version check.
|
||||
- Every build job runs `npm ci` and passes
|
||||
`--config '{"bundle":{"createUpdaterArtifacts":true}}'` where bundles are
|
||||
built, with `TAURI_SIGNING_PRIVATE_KEY` and `_PASSWORD` in the environment.
|
||||
built. `TAURI_SIGNING_PRIVATE_KEY` and `_PASSWORD` are step-level env on
|
||||
the steps that check or use them only, never job-level, so `npm ci`
|
||||
install scripts, setup actions and crate build scripts never see the key.
|
||||
A signing step fails hard when the key variable is empty.
|
||||
- Keep the step that fails before any build when `plugins.updater.pubkey` in
|
||||
`tauri.conf.json` is empty. `build.rs` only checks the key when
|
||||
@@ -44,7 +46,8 @@ host has no patchelf and no display.
|
||||
- Upload the exe and its `.sig`.
|
||||
- **linux-x64** job (`ubuntu-22.04`):
|
||||
- Install the Phase 01 apt list plus `patchelf`.
|
||||
- `npm run tauri build -- --bundles appimage,deb --config …`.
|
||||
- `npm run tauri build -- --no-bundle -- --locked` without the key, then
|
||||
`npm run tauri bundle -- --bundles appimage,deb --config …` with it.
|
||||
- Rename every output to space-free kebab names before upload, because the
|
||||
bundler names them after `productName` and GitHub rewrites spaces in asset
|
||||
names: `claude-code-usage-bubble_<ver>_amd64.AppImage`, the matching
|
||||
@@ -67,10 +70,12 @@ host has no patchelf and no display.
|
||||
rollback for Tauri clients; `workflow_dispatch` with `ref`, `version`, `tag`
|
||||
and a `draft` input that defaults to `true`): checks out `ref`, patches the
|
||||
`Cargo.toml` version, runs `cargo build --release --locked`, signs the exe
|
||||
with `tauri signer sign` (CLI from `npx --yes @tauri-apps/cli@2`, since the
|
||||
legacy ref has no `package.json`), runs `make-latest-json.js --windows-only`
|
||||
with `tauri signer sign` (CLI from `npm ci --prefix current` on the default
|
||||
branch's lockfile, since the legacy ref has no `package.json`), runs `make-latest-json.js --windows-only`
|
||||
(from the current default branch) and publishes `claude-code-usage-bubble.exe`,
|
||||
its `.sig` and the Windows-only `latest.json`. Tauri Linux clients get
|
||||
its `.sig` and the Windows-only `latest.json`. The build job runs with
|
||||
`contents: read` and `persist-credentials: false` and uploads an artifact; a
|
||||
separate publish job holds `contents: write`, as in `release.yml`. Tauri Linux clients get
|
||||
`TargetNotFound`, which Phase 09 maps to Up to date. The v0.5.0 sources accept
|
||||
the Tauri relaunch args `--wait-pid <pid> --updated-to X`
|
||||
(`git show v0.5.0:src/main.rs`, lines 42 and 54).
|
||||
|
||||
Reference in new issue
Block a user