chore(release): remove updater pubkey/endpoint override + gate signed artifacts#912
Conversation
… artifacts The release pipeline was injecting `plugins.updater.pubkey` and `plugins.updater.endpoints` from `UPDATER_PUBLIC_KEY` / `UPDATER_ENDPOINT` env vars (sourced from GitHub secrets/vars) at build time, on top of the static `app/src-tauri/tauri.conf.json`. This caused a real outage class: if the build-time `UPDATER_PUBLIC_KEY` ever drifted out of sync with the `TAURI_SIGNING_PRIVATE_KEY` secret used to sign artifacts, every signed installer was rejected by its own embedded pubkey at install time — surfacing as opaque "bad keys" errors. Changes: - `scripts/prepareTauriConfig.js`: drop the pubkey/endpoint override. The function now just flips `bundle.createUpdaterArtifacts: true` when `WITH_UPDATER=true` and emits the Windows DigiCert sign command when `KEYPAIR_ALIAS` is set. Pubkey + endpoint live in the static `app/src-tauri/tauri.conf.json` — rotate via commit + review, not silent secret swaps. - `app/src-tauri/tauri.conf.json`: set `bundle.createUpdaterArtifacts: false` as the safe default. Workflows that don't have access to `TAURI_SIGNING_PRIVATE_KEY` (PR build matrices, the test workflow) inherit this and skip artifact signing entirely instead of failing. `release.yml` opts in via `WITH_UPDATER=true`. - `.github/workflows/release.yml`: drop `UPDATER_PUBLIC_KEY` validation and the `UPDATER_PUBLIC_KEY` / `UPDATER_ENDPOINT` / `UPDATER_REPO` env vars from the config-overrides step. Drop the redundant `WITH_UPDATER` env from the tauri-build step (only the config-overrides step runs `prepareTauriConfig.js`). - `.github/workflows/build-windows.yml`: drop the now-unused `WITH_UPDATER='false'` env var. Absence is the default. - `scripts/ci-secrets.example.json`: drop `UPDATER_PUBLIC_KEY` and `UPDATER_ENDPOINT` to match the pruned env-var surface. Action item before the next release: confirm the GH Actions secret `TAURI_SIGNING_PRIVATE_KEY` (or fallback `UPDATER_PRIVATE_KEY`) holds the private key matching the static pubkey (`7494D291DAB5B3E1`, set by tinyhumansai#910).
📝 WalkthroughWalkthroughThis pull request consolidates updater configuration management by removing environment variable injection from GitHub workflows, disabling updater artifact generation in the Tauri bundler configuration, and refactoring the configuration preparation script to only emit bundle overrides when necessary. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)
393-396:⚠️ Potential issue | 🟠 MajorAdd a fail-fast keypair/parity verification step.
Line [393] verifies key presence only; it does not prove the secret key matches the static updater pubkey in
app/src-tauri/tauri.conf.json. That can still produce installer/update signature rejection at runtime.Suggested workflow hook
- name: Validate signing prerequisites shell: bash env: @@ if [ -z "$TAURI_SIGNING_PRIVATE_KEY" ]; then echo "Missing TAURI_SIGNING_PRIVATE_KEY (or fallback UPDATER_PRIVATE_KEY)." exit 1 fi + - name: Verify updater signing key matches static pubkey + shell: bash + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY || secrets.UPDATER_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.UPDATER_PRIVATE_KEY_PASSWORD }} + run: | + bash scripts/release/verify-updater-keypair.sh🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 393 - 396, Add a fail-fast parity check in the release workflow that, after confirming TAURI_SIGNING_PRIVATE_KEY is present, derives its public key and compares it to the static updater public key stored in app/src-tauri/tauri.conf.json; if the derived public key does not match the configured updater pubkey, exit with a clear error. Locate the check near the existing TAURI_SIGNING_PRIVATE_KEY presence guard in release.yml, use the TAURI_SIGNING_PRIVATE_KEY environment variable for derivation, read the updater public key value from tauri.conf.json, and fail the job when the two keys differ so installer/update signature mismatches are caught at CI time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/prepareTauriConfig.js`:
- Around line 32-35: The signCommand currently interpolates
process.env.KEYPAIR_ALIAS unquoted inside bundle.windows.signCommand which can
break when the alias contains spaces or special chars; update the template in
scripts/prepareTauriConfig.js so signCommand uses a quoted alias (e.g.
--keypair-alias="${process.env.KEYPAIR_ALIAS}") and ensure any embedded quotes
in process.env.KEYPAIR_ALIAS are escaped before interpolation to avoid breaking
the shell command.
---
Outside diff comments:
In @.github/workflows/release.yml:
- Around line 393-396: Add a fail-fast parity check in the release workflow
that, after confirming TAURI_SIGNING_PRIVATE_KEY is present, derives its public
key and compares it to the static updater public key stored in
app/src-tauri/tauri.conf.json; if the derived public key does not match the
configured updater pubkey, exit with a clear error. Locate the check near the
existing TAURI_SIGNING_PRIVATE_KEY presence guard in release.yml, use the
TAURI_SIGNING_PRIVATE_KEY environment variable for derivation, read the updater
public key value from tauri.conf.json, and fail the job when the two keys differ
so installer/update signature mismatches are caught at CI time.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4da36792-3510-4122-8650-6ecf38f9df8b
📒 Files selected for processing (5)
.github/workflows/build-windows.yml.github/workflows/release.ymlapp/src-tauri/tauri.conf.jsonscripts/ci-secrets.example.jsonscripts/prepareTauriConfig.js
💤 Files with no reviewable changes (1)
- scripts/ci-secrets.example.json
| if (process.env.KEYPAIR_ALIAS) { | ||
| bundle.windows = { | ||
| signCommand: `smctl.exe sign --keypair-alias=${process.env.KEYPAIR_ALIAS} --input %1`, | ||
| }; |
There was a problem hiding this comment.
Quote KEYPAIR_ALIAS in signCommand to avoid command parsing breakage.
Line [34] interpolates the alias unquoted; aliases with spaces/special characters can break Windows signing command parsing.
Suggested patch
if (process.env.KEYPAIR_ALIAS) {
+ const escapedAlias = process.env.KEYPAIR_ALIAS.replace(/"/g, '\\"');
bundle.windows = {
- signCommand: `smctl.exe sign --keypair-alias=${process.env.KEYPAIR_ALIAS} --input %1`,
+ signCommand: `smctl.exe sign --keypair-alias="${escapedAlias}" --input %1`,
};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (process.env.KEYPAIR_ALIAS) { | |
| bundle.windows = { | |
| signCommand: `smctl.exe sign --keypair-alias=${process.env.KEYPAIR_ALIAS} --input %1`, | |
| }; | |
| if (process.env.KEYPAIR_ALIAS) { | |
| const escapedAlias = process.env.KEYPAIR_ALIAS.replace(/"/g, '\\"'); | |
| bundle.windows = { | |
| signCommand: `smctl.exe sign --keypair-alias="${escapedAlias}" --input %1`, | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/prepareTauriConfig.js` around lines 32 - 35, The signCommand
currently interpolates process.env.KEYPAIR_ALIAS unquoted inside
bundle.windows.signCommand which can break when the alias contains spaces or
special chars; update the template in scripts/prepareTauriConfig.js so
signCommand uses a quoted alias (e.g.
--keypair-alias="${process.env.KEYPAIR_ALIAS}") and ensure any embedded quotes
in process.env.KEYPAIR_ALIAS are escaped before interpolation to avoid breaking
the shell command.
Summary
Two related fixes to the release pipeline that together eliminate a real outage class ("bad keys" install failures) and stop PR/test builds from failing when they don't have signing secrets.
1. Drop the updater pubkey/endpoint override (the "bad keys" bug)
scripts/prepareTauriConfig.jswas injectingplugins.updater.pubkeyandplugins.updater.endpointsat build time from theUPDATER_PUBLIC_KEY/UPDATER_ENDPOINT/UPDATER_REPOenv vars sourced from GitHub secrets/vars, on top of the staticapp/src-tauri/tauri.conf.json.If the build-time
UPDATER_PUBLIC_KEYever drifted out of sync withTAURI_SIGNING_PRIVATE_KEY(e.g. one secret rotated, the other not) every signed installer was rejected by its own embedded pubkey at install time — surfacing as opaque "bad keys" errors.The static
tauri.conf.jsonalready carries the canonical pubkey (7494D291DAB5B3E1after #910) and endpoint (https://github.com/tinyhumansai/openhuman/releases/latest/download/latest.json), so removing the override leaves the build pointed at the same place — just without the silent-drift risk. Key rotations now go through commit + review, which is also better auditability.2. Gate signed artifact creation behind
WITH_UPDATERStatic
bundle.createUpdaterArtifacts: falseis now the safe default. PR build matrices (build.yml,build-windows.yml) andtest.ymldon't have access toTAURI_SIGNING_PRIVATE_KEY, so without this gate the bundler would either fail or silently emit unsigned.app.tar.gz/.sigfiles. Onlyrelease.ymlsetsWITH_UPDATER=true, whichprepareTauriConfig.jsthen translates intobundle.createUpdaterArtifacts: truefor that one pipeline.Changes
scripts/prepareTauriConfig.js— removes theUPDATER_PUBLIC_KEY/UPDATER_ENDPOINT/UPDATER_REPOblock. Function now only flipscreateUpdaterArtifactswhenWITH_UPDATER=trueand emits the Windows DigiCertsignCommandwhenKEYPAIR_ALIASis set.app/src-tauri/tauri.conf.json—bundle.createUpdaterArtifacts: true→false(safe default)..github/workflows/release.yml— dropsUPDATER_PUBLIC_KEYvalidation; drops theUPDATER_PUBLIC_KEY/UPDATER_ENDPOINT/UPDATER_REPOenv vars from the config-overrides step; drops the redundantWITH_UPDATERenv from the tauri-build step (onlyprepareTauriConfig.jsreads it, and that runs in the config-overrides step)..github/workflows/build-windows.yml— drops the now-unused explicitWITH_UPDATER='false'.scripts/ci-secrets.example.json— dropsUPDATER_PUBLIC_KEY(secret) andUPDATER_ENDPOINT(var) to match the pruned env-var surface.Action item for the repo owner before the next release
Confirm the GH Actions secret
TAURI_SIGNING_PRIVATE_KEY(or legacy fallbackUPDATER_PRIVATE_KEY) holds the private key matching the static pubkey7494D291DAB5B3E1(set by #910). If it still holds the old key, signing will succeed in CI butupdate.download_and_install()will fail signature verification on the user's machine.You can safely delete these GH Actions secrets/vars too — nothing reads them anymore:
secrets.UPDATER_PUBLIC_KEY/vars.UPDATER_PUBLIC_KEYvars.UPDATER_ENDPOINTTest plan
node -e "import('./scripts/prepareTauriConfig.js').then(m => console.log(JSON.stringify(m.default(), null, 2)))"with no env →{}.WITH_UPDATER=true→{ bundle: { createUpdaterArtifacts: true } }.KEYPAIR_ALIAS=foo→{ bundle: { windows: { signCommand: '… --keypair-alias=foo …' } } }.UPDATER_PUBLIC_KEY/UPDATER_ENDPOINT/UPDATER_REPO/UPDATER_GIST_URLoutside of comments / changelogs.Relationship to other open updater PRs
createUpdaterArtifacts: truein tauri.conf.json, which conflicts with this PR's flip tofalse. Resolution = take this PR'sfalse(and letWITH_UPDATER=truefrom release.yml flip it on for releases as designed).7494D291DAB5B3E1) — already merged on main; this PR builds on it.Summary by CodeRabbit
Refactor
Chores