Skip to content

feat(chrome-extension): automate signed release packaging - #394

Merged
V3RON merged 1 commit into
mainfrom
automate-chrome-extension-release
Aug 12, 2026
Merged

feat(chrome-extension): automate signed release packaging#394
V3RON merged 1 commit into
mainfrom
automate-chrome-extension-release

Conversation

@V3RON

@V3RON V3RON commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Description

Automates building, signing, and publishing the Chrome extension as part of the existing release pipeline (scripts/release/release.mjs, triggered by .github/workflows/release.yml). Previously this was done by hand — the .crx was inconsistently attached to past releases (missing from most, including the current latest), and manifest.json's version had drifted from the package version.

On stable and rc releases, after the version bump and tag push:

  • manifest.json's version is synced from the package's version (sanitized to a plain dotted-integer form for rc prerelease versions, since Chrome rejects semver prerelease suffixes).
  • The extension is packed and signed into a .crx via a new pack:crx script (packages/chrome-extension/scripts/pack-crx.mjs, using crx3), using a private key supplied as the CHROME_EXTENSION_PEM_BASE64 GitHub Actions secret (base64-encoded, decoded to a per-run temp file and removed afterward).
  • The GitHub Release for the pushed tag is created (or, if a release already exists for that tag, the asset is uploaded to it) with the signed .crx attached, marked prerelease for rc tags.

Reusing the same signing key on every release keeps the extension ID stable, which matters for existing installs' self-update flow (src/update-checker.ts polls manifest.json on main directly to detect available updates).

Related Issue

N/A — no existing issue for this change.

Context

The signing key only ever existed on the maintainer's machine, and nothing in the repo created the GitHub Release itself (the release script only pushed a tag). This change folds packaging, signing, and Release creation into the same job that already runs the release, rather than adding a second tag-triggered workflow, so build/version/tag/publish/release-creation stay atomic within one job.

Requires a manual setup step: base64-encode the existing .pem and add it as the CHROME_EXTENSION_PEM_BASE64 secret in the repo's GitHub Actions settings before the next release run.

Testing

Automated:

  • pnpm --filter @rozenite/chrome-extension build
  • pnpm --filter @rozenite/chrome-extension lint
  • pnpm --filter @rozenite/chrome-extension typecheck
  • pnpm release:plan — confirms the changeset is detected
  • pnpm lint:all / pnpm typecheck:all (via the pre-push hook, turbo run lint typecheck across all 63 tasks) — all passed

Manual:

  • Ran pnpm build then CHROME_EXTENSION_PEM_PATH=<disposable-test-key> pnpm pack:crx locally — produced a valid signed build/rozenite.crx (verified CRX3 magic bytes).
  • Verified pack:crx fails loudly (rather than silently minting a new key) when CHROME_EXTENSION_PEM_PATH points at a nonexistent file.
  • Verified the manifest-version sanitizer strips prerelease suffixes (e.g. 2.1.0-rc.02.1.0) so rc builds don't ship an invalid, uninstallable manifest version.

Sign and attach a .crx to GitHub Releases automatically as part of the
existing release pipeline, and keep manifest.json's version in sync with
the package version, instead of relying on a manual local build/sign/upload
step (which was inconsistently followed and left version metadata stale).
@V3RON
V3RON merged commit eabe827 into main Aug 12, 2026
4 checks passed
@V3RON
V3RON deleted the automate-chrome-extension-release branch August 12, 2026 19:32
V3RON added a commit that referenced this pull request Aug 13, 2026
…404)

## Description

Fixes the release automation added in #394: after
`syncChromeExtensionManifestVersion()` rewrites
`packages/chrome-extension/manifest.json`'s `version` field via
`JSON.stringify(manifest, null, 2)`, the file's short arrays
(`permissions`, `resources`, `matches`) get expanded onto multiple
lines, which doesn't match oxfmt's formatting (it collapses arrays that
fit on one line). Since `commitVersionChanges()` runs a normal `git
commit`, lefthook's `pre-commit` hook runs `oxfmt --check` on the staged
file and fails, aborting the release commit.

See the failed run:
https://github.com/callstackincubator/rozenite/actions/runs/31677909005/job/94376470953

## Related Issue

N/A — no existing issue for this change.

## Context

Rather than hand-matching oxfmt's array-collapsing rules in the manual
`JSON.stringify` output, `syncChromeExtensionManifestVersion()` now runs
`oxfmt --write` on the file immediately after writing it, so the staged
file is already correctly formatted regardless of future oxfmt config
changes.

## Testing

- Simulated a version bump against the current `manifest.json` and
confirmed the pre-existing `JSON.stringify` output fails `oxfmt --check`
(arrays expand to multi-line) while the fixed code (`oxfmt --write`
after writing) passes.
- `pnpm exec oxfmt --check scripts/release/release.mjs`
- `pnpm exec eslint scripts/release/release.mjs` — pre-existing
unrelated `no-undef` errors for `console`/`Buffer` confirmed present
before this change too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant