Skip to content

Releasing

Tom Bouquet edited this page Nov 2, 2026 · 2 revisions

This page covers how a change on main becomes a tagged GitHub release with a Chrome zip and a Firefox zip. Read it before you merge a pull request you want to ship, or when a release comes out wrong. For the checks each pull request passes first, see Development.

Release flow

Release Please owns the version number, the changelog and the GitHub release. Nobody bumps the version by hand. A release goes through these stages:

  1. A pull request with a Conventional Commits title merges into main. The repo allows only squash merges, so the pull request title becomes the commit message Release Please reads.
  2. On that push, the Release Please workflow opens or updates a release pull request titled chore(main): release <version>. The release pull request bumps version in package.json, updates .release-please-manifest.json, and adds the new section to CHANGELOG.md.
  3. When you merge the release pull request, the same workflow tags v<version> and creates the GitHub release, with the changelog section as its notes.
  4. A second job in the workflow builds the extension from the tag for both browsers and attaches screen-tools-v<version>.zip (Chrome) and screen-tools-v<version>-firefox.zip to the release.

Merge the release pull request when you want to ship. Until then it collects every releasable commit merged after the last release.

Commit types and version bumps

Release Please picks the next version from the commit types since the last tag:

Commit type Version bump Changelog section
feat: Minor Features
fix: Patch Bug Fixes
feat!: or BREAKING CHANGE: Major ⚠ BREAKING CHANGES
chore:, docs: and the rest None Left out

The changelog shows the commit subject, so write the pull request title as the line users read. The extension bundles CHANGELOG.md at build time: the home page that opens after an update shows the section for the installed version, or the latest section when a dev build is ahead of the changelog, and the changelog page shows the whole file. See src/pages/home.tsx and src/pages/changelog.tsx.

Configuration files

Two files at the repo root configure Release Please:

  • release-please-config.json sets one package at the repo root with the node release type, so the bump lands in package.json. Tags take the form v1.4.0, with no component prefix, and the changelog goes to CHANGELOG.md. The first release was 1.0.0.
  • .release-please-manifest.json holds the version of the latest release. Release Please updates it in each release pull request, so don't edit it.

WXT copies version from package.json into the extension manifest, so the release pull request is the only change a version needs.

Release workflow

.github/workflows/release-please.yml runs on every push to main and has two jobs:

  • release-please runs googleapis/release-please-action with the two configuration files. It outputs release_created and tag_name.
  • upload-zip runs only when release_created is true. It checks out the tag, installs with the frozen lockfile, and runs pnpm zip and pnpm zip:firefox. It then renames .output/screen-tools-<version>-chrome.zip to screen-tools-<tag>.zip and .output/screen-tools-<version>-firefox.zip to screen-tools-<tag>-firefox.zip, and uploads both with gh release upload --clobber. v1.5.0 is the first release with a Firefox zip.

The workflow needs contents: write to create tags and releases and pull-requests: write to open the release pull request. Both are set in the workflow file, so the repo needs no extra token.

The Chrome zip is what users install: they unzip it and load the folder unpacked, so they need neither Node nor a clone. The Firefox zip is the package to load in Firefox or submit to addons.mozilla.org. The README's Install and Updating sections cover the user's side, and Installation and Updating covers it in more detail.

Upload service variables

The zip steps pass the upload services' build variables from the repository's Actions variables (vars), not secrets, because they're public identifiers:

Variable Passed to Holds
WXT_GOOGLE_CLIENT_ID Both zips Google OAuth client ID
WXT_DROPBOX_APP_KEY Both zips Dropbox app key
WXT_EXTENSION_KEY Chrome zip only The Chrome Web Store public key

The key pins the Chrome extension ID, so the OAuth redirect URL is the same wherever a user unpacks the zip. Firefox gets its ID from browser_specific_settings in the manifest, so the Firefox manifest never includes the key. When a variable is missing, the build still succeeds and that service shows as "Not available in this build" in Settings. Check the variables are set under Settings → Secrets and variables → Actions → Variables before a release that should offer uploads. Development covers the same variables for local builds.

Immutable releases

Keep immutable releases turned off in the repo settings. Release Please publishes the release before the upload-zip job builds the zips, and GitHub rejects uploads to an immutable release, so the release ships with no zips. v1.0.0 has no zip for this reason, and every release from v1.0.1 on has one.

If a release ends up without its zips, check the upload-zip job log first. To attach them by hand, build from the tag with the upload variables set in .env.local or the shell, and upload both:

git checkout v<version>
pnpm install --frozen-lockfile
pnpm zip
pnpm zip:firefox
mv .output/screen-tools-*-chrome.zip screen-tools-v<version>.zip
mv .output/screen-tools-*-firefox.zip screen-tools-v<version>-firefox.zip
gh release upload v<version> screen-tools-v<version>.zip screen-tools-v<version>-firefox.zip --clobber

Clone this wiki locally