11 KiB
GitHub Firefox Updates Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Publish signed Firefox XPI releases to GitHub Releases and let installed alpha extensions discover them through a GitHub-hosted update manifest.
Architecture: Firefox keeps one immutable update_url targeting the current GitHub Release's updates.json asset. A small Node helper owns the tag, asset URL and update-manifest formatting so it has focused unit tests. The existing local signing command remains local; an explicit publishing command uploads its two artifacts to the public GitHub release.
Tech Stack: Manifest V2 JSON, Node.js CommonJS tests, Bash, web-ext, GitHub CLI (gh).
Global Constraints
- Publish repository:
mirivlad/verstak-browser-extension. - Firefox addon ID:
verstak-bridge@verstak.app. - Initial GitHub release:
v2.0.2; package and both browser manifests are version2.0.2. - Every alpha GitHub release is ordinary/latest, not a prerelease.
- XPI remains Mozilla-signed as an unlisted/self-distributed addon before upload.
npm run release:firefoxremains a local sign-and-package action.npm run publish:firefoxis the only command allowed to create or modify a GitHub Release.- Generated
release/output remains untracked.
Task 1: Make GitHub update metadata testable
Files:
- Create:
scripts/firefox-github-release.js - Create:
scripts/test-firefox-github-release.js - Modify:
package.json:8-15
Interfaces:
-
Produces
releaseTag(version),releaseAssetURL(version, assetName), andupdateManifest(addonID, version, assetName)fromscripts/firefox-github-release.js. -
updateManifestreturns the object written asupdates.jsonand uses the fixed GitHub repository. -
Later shell scripts invoke
node scripts/firefox-github-release.js write-updates <addon-id> <version> <asset-name> <output-path>. -
Step 1: Write the failing metadata test
Create scripts/test-firefox-github-release.js with:
const assert = require('assert');
const release = require('./firefox-github-release');
const version = '2.0.2';
const asset = 'verstak-firefox-2.0.2.xpi';
assert.equal(release.releaseTag(version), 'v2.0.2');
assert.equal(
release.releaseAssetURL(version, asset),
'https://github.com/mirivlad/verstak-browser-extension/releases/download/v2.0.2/verstak-firefox-2.0.2.xpi',
);
assert.deepEqual(release.updateManifest('verstak-bridge@verstak.app', version, asset), {
addons: {
'verstak-bridge@verstak.app': {
updates: [{
version: '2.0.2',
update_link: 'https://github.com/mirivlad/verstak-browser-extension/releases/download/v2.0.2/verstak-firefox-2.0.2.xpi',
}],
},
},
});
console.log('Firefox GitHub release metadata tests passed');
- Step 2: Run the focused test and verify it fails
Run: node scripts/test-firefox-github-release.js
Expected: failure because scripts/firefox-github-release.js does not yet exist.
- Step 3: Implement the metadata helper
Create scripts/firefox-github-release.js with these exact exports and CLI:
const fs = require('fs');
const REPOSITORY = 'mirivlad/verstak-browser-extension';
function releaseTag(version) { return `v${version}`; }
function releaseAssetURL(version, assetName) {
return `https://github.com/${REPOSITORY}/releases/download/${releaseTag(version)}/${assetName}`;
}
function updateManifest(addonID, version, assetName) {
return { addons: { [addonID]: { updates: [{ version, update_link: releaseAssetURL(version, assetName) }] } } };
}
if (require.main === module) {
const [, , command, addonID, version, assetName, outputPath] = process.argv;
if (command !== 'write-updates' || !addonID || !version || !assetName || !outputPath) process.exitCode = 2;
else fs.writeFileSync(outputPath, `${JSON.stringify(updateManifest(addonID, version, assetName), null, 2)}\n`);
}
module.exports = { REPOSITORY, releaseTag, releaseAssetURL, updateManifest };
- Step 4: Register and run the test
Append node scripts/test-firefox-github-release.js to the existing test
script in package.json, then run:
npm test
Expected: all existing tests and Firefox GitHub release metadata tests passed.
- Step 5: Commit and push
git add package.json scripts/firefox-github-release.js scripts/test-firefox-github-release.js
git commit -m "test: cover Firefox GitHub release metadata"
git push origin main
Task 2: Point Firefox and local release artifacts at GitHub
Files:
- Modify:
firefox/manifest.json:8-17 - Modify:
scripts/release-firefox-xpi.sh:29-70 - Test:
scripts/test-firefox-github-release.js
Interfaces:
-
Firefox reads
https://github.com/mirivlad/verstak-browser-extension/releases/latest/download/updates.json. -
release-firefox-xpi.shinvokes the Task 1 helper to write a versioned update manifest. -
It continues producing
release/firefox/verstak-firefox-<version>.xpiandrelease/firefox/updates.jsonlocally. -
Step 1: Extend the failing test with the stable manifest URL
Add this assertion to scripts/test-firefox-github-release.js before its final
success message:
const firefoxManifest = require('../firefox/manifest.json');
assert.equal(
firefoxManifest.browser_specific_settings.gecko.update_url,
'https://github.com/mirivlad/verstak-browser-extension/releases/latest/download/updates.json',
);
- Step 2: Run the focused test and verify it fails
Run: node scripts/test-firefox-github-release.js
Expected: assertion failure because the manifest still uses mirv.top.
- Step 3: Replace the update endpoint and JSON writer
In firefox/manifest.json, replace only the Gecko update_url value with:
"update_url": "https://github.com/mirivlad/verstak-browser-extension/releases/latest/download/updates.json"
In scripts/release-firefox-xpi.sh, remove UPDATE_BASE_URL and replace the
heredoc that writes updates.json with:
node scripts/firefox-github-release.js write-updates \
"$ADDON_ID" "$VERSION" "$RELEASE_XPI" "$RELEASE_DIR/updates.json"
- Step 4: Run targeted checks
Run:
node scripts/test-firefox-github-release.js
bash -n scripts/release-firefox-xpi.sh
npm test
npm run build
Expected: all commands succeed; dist/firefox/manifest.json contains the
GitHub update URL after the build.
- Step 5: Commit and push
git add firefox/manifest.json scripts/release-firefox-xpi.sh scripts/test-firefox-github-release.js
git commit -m "feat: host Firefox updates on GitHub Releases"
git push origin main
Task 3: Add explicit GitHub publishing and public instructions
Files:
- Create:
scripts/publish-firefox-github-release.sh - Modify:
package.json:8-15 - Modify:
README.md:42-68 - Test:
scripts/publish-firefox-github-release.sh
Interfaces:
-
npm run publish:firefoxcallsscripts/publish-firefox-github-release.sh. -
The script runs the existing local release command, then uploads
verstak-firefox-<version>.xpiandupdates.jsonto release tagv<version>inmirivlad/verstak-browser-extension. -
A pre-existing tag receives asset replacement through
gh release upload --clobber. -
Step 1: Write the shell-contract test
Create a Node test that reads the publish script and asserts the mandatory commands are present:
const assert = require('assert');
const fs = require('fs');
const source = fs.readFileSync('scripts/publish-firefox-github-release.sh', 'utf8');
assert.match(source, /gh auth status/);
assert.match(source, /gh release create/);
assert.match(source, /gh release upload/);
assert.match(source, /--clobber/);
assert.match(source, /--latest/);
Place those assertions in scripts/test-firefox-github-release.js and run the
test. Expected: it fails before the publish script exists.
- Step 2: Implement an explicit idempotent publisher
Create scripts/publish-firefox-github-release.sh with this behavior:
- Set
ROOT_DIR,cdto it and rungh auth status. - Run
./scripts/release-firefox-xpi.sh. - Read
VERSIONfromdist/firefox/manifest.json, setTAG="v${VERSION}",REPOSITORY="mirivlad/verstak-browser-extension", and locate the two local release assets by exact names. - Run
gh release view "$TAG" --repo "$REPOSITORY"; when it is absent, rungh release create "$TAG" "$XPI" "$UPDATES" --repo "$REPOSITORY" --title "Verstak Browser Extension $VERSION" --generate-notes --latest --target "$(git rev-parse HEAD)". - When the release already exists, run
gh release upload "$TAG" "$XPI" "$UPDATES" --repo "$REPOSITORY" --clobber. - Print the GitHub release URL with
gh release view "$TAG" --repo "$REPOSITORY" --json url --jq .url.
- Step 3: Register the command and document it
Add to package.json:
"publish:firefox": "./scripts/publish-firefox-github-release.sh"
Rewrite the Firefox Release README section so it documents:
-
the public GitHub Releases page;
-
VERSTAK_BROWSER_ENV=/path/to/.env npm run publish:firefox; -
that signing remains unlisted with Mozilla but distribution and automatic updates use GitHub Release assets;
-
that the first
v2.0.2publish bootstraps thelatest/download/updates.jsonendpoint. -
Step 4: Run checks without publishing
Run:
bash -n scripts/publish-firefox-github-release.sh
node scripts/test-firefox-github-release.js
npm test
npm run build
git diff --check
Expected: all commands succeed. Do not invoke npm run publish:firefox in this
task; it creates a public GitHub Release and needs available AMO credentials.
- Step 5: Commit and push
git add README.md package.json scripts/publish-firefox-github-release.sh scripts/test-firefox-github-release.js
git commit -m "feat: publish Firefox releases to GitHub"
git push origin main
Task 4: Bootstrap and verify the first public update release
Files:
- Generated:
release/firefox/verstak-firefox-2.0.2.xpi(untracked) - Generated:
release/firefox/updates.json(untracked)
Interfaces:
-
Consumes the Task 3
npm run publish:firefoxcommand and valid AMO signing credentials. -
Produces GitHub Release tag
v2.0.2and public XPI/update-manifest assets. -
Step 1: Verify release credentials before write operations
Run:
gh auth status
test -n "${VERSTAK_BROWSER_ENV:-}" || test -f .env
Expected: authenticated GitHub CLI and a configured environment file path for the existing AMO signing command.
- Step 2: Publish the first release
Run:
npm run publish:firefox
Expected: Mozilla signing succeeds, GitHub Release v2.0.2 is latest, and it
contains verstak-firefox-2.0.2.xpi plus updates.json.
- Step 3: Verify public delivery
Run:
gh release view v2.0.2 --repo mirivlad/verstak-browser-extension --json isLatest,url,assets
curl -fsSL https://github.com/mirivlad/verstak-browser-extension/releases/latest/download/updates.json
Expected: the release is latest and the JSON contains addon ID
verstak-bridge@verstak.app, version 2.0.2, and the versioned GitHub XPI
asset URL.
- Step 4: Record release verification without committing assets
Run:
git status --short
Expected: no generated release/ assets are staged or committed.