Skip to content

Add release infrastructure for reproducible, versioned extension builds#1462

Merged
igrigorik merged 2 commits into
masterfrom
claude/analyze-agentboard-release-6RCcR
Mar 28, 2026
Merged

Add release infrastructure for reproducible, versioned extension builds#1462
igrigorik merged 2 commits into
masterfrom
claude/analyze-agentboard-release-6RCcR

Conversation

@igrigorik

Copy link
Copy Markdown
Owner

PROBLEM

Before this change the repo had no structured release process. Versions
were maintained in two places (package.json at 0.8.0, manifest.json at
0.9.5) with no enforcement that they agree. Building for Chrome Web Store
submission was identical to a local dev build — no minification, no
validation. Packaging was a one-liner shell zip with no versioning in the
filename and no safety checks. There was no mechanism to create GitHub
releases. The CI pipeline uploaded raw dist/ as an artifact, which is not
directly submittable to the Chrome Web Store.

DESIGN DECISIONS

Version single-source-of-truth: package.json is the sole authority for
the extension version. The checked-in manifest.json carries "0.0.0" as a
placeholder. At build time, build.mjs reads the version from package.json
and writes it into dist/manifest.json. This eliminates version drift and
means npm version patch|minor|major is the only version-bumping gesture
needed. The canonical version was set to 0.9.5 (the manifest value, which
is what users see in the Chrome Web Store).

Two build modes: npm run build produces unminified output for local
development and debugging. npm run build:release (RELEASE=1 env var)
enables esbuild minification and is what CI and the release pipeline use.
Both modes inject the version identically. Source maps are off in both
modes by default; developers can flip the flag locally.

Structured packaging (scripts/package-release.js): Creates
release/videospeed-{version}.zip at maximum compression. Before zipping
it validates that the manifest version in dist/ matches package.json,
excludes source maps and .DS_Store, and warns if the zip exceeds the
Chrome Web Store 128 MB limit. Uses the archiver library for portable,
deterministic zip creation instead of shelling out to the zip CLI.

GitHub release automation (scripts/github-release.js): Creates a draft
GitHub release via the gh CLI, attaches the versioned zip, and
auto-generates release notes from the commit log since the previous tag.
Draft status is intentional — it requires manual review before publishing,
which is the right safety tradeoff for a single-maintainer project. The
script validates that gh is installed and authenticated, and that the git
tag exists, before attempting anything.

Pre-push quality gate (.husky/pre-push): Runs lint and the full test
suite before every push. This catches issues earlier than CI and
complements the existing pre-commit hook (which only runs lint-staged on
changed files). The tradeoff is slower pushes (~30s), but for this
project size that is acceptable.

CI produces release-ready artifacts: The CI workflow now runs
build:release instead of build, then runs package-release.js to produce
the versioned zip. The uploaded artifact is the same zip that would be
submitted to the Chrome Web Store, so any CI run on master produces a
directly usable release candidate.

INTENDED RELEASE WORKFLOW

  1. npm version patch|minor|major (bumps package.json, creates commit)
  2. npm run release (clean → test → build:release → zip)
  3. git tag v{version} && git push origin v{version}
  4. npm run release:github (draft release with zip on GitHub)
  5. Review and publish the draft on GitHub
  6. Upload the same zip to Chrome Web Store

FILES CHANGED

manifest.json → version set to "0.0.0" (build-time placeholder)
package.json → version 0.9.5, new scripts, archiver dep
scripts/build.mjs → version injection, RELEASE mode support
scripts/package-release.js → NEW: validated zip packaging
scripts/github-release.js → NEW: draft GitHub release creation
.husky/pre-push → NEW: lint + test gate
.github/workflows/ci.yml → release build + versioned zip artifact
.gitignore → added release/

claude added 2 commits March 28, 2026 01:50
PROBLEM

Before this change the repo had no structured release process. Versions
were maintained in two places (package.json at 0.8.0, manifest.json at
0.9.5) with no enforcement that they agree. Building for Chrome Web Store
submission was identical to a local dev build — no minification, no
validation. Packaging was a one-liner shell zip with no versioning in the
filename and no safety checks. There was no mechanism to create GitHub
releases. The CI pipeline uploaded raw dist/ as an artifact, which is not
directly submittable to the Chrome Web Store.

DESIGN DECISIONS

Version single-source-of-truth: package.json is the sole authority for
the extension version. The checked-in manifest.json carries "0.0.0" as a
placeholder. At build time, build.mjs reads the version from package.json
and writes it into dist/manifest.json. This eliminates version drift and
means `npm version patch|minor|major` is the only version-bumping gesture
needed. The canonical version was set to 0.9.5 (the manifest value, which
is what users see in the Chrome Web Store).

Two build modes: `npm run build` produces unminified output for local
development and debugging. `npm run build:release` (RELEASE=1 env var)
enables esbuild minification and is what CI and the release pipeline use.
Both modes inject the version identically. Source maps are off in both
modes by default; developers can flip the flag locally.

Structured packaging (scripts/package-release.js): Creates
release/videospeed-{version}.zip at maximum compression. Before zipping
it validates that the manifest version in dist/ matches package.json,
excludes source maps and .DS_Store, and warns if the zip exceeds the
Chrome Web Store 128 MB limit. Uses the archiver library for portable,
deterministic zip creation instead of shelling out to the zip CLI.

GitHub release automation (scripts/github-release.js): Creates a draft
GitHub release via the gh CLI, attaches the versioned zip, and
auto-generates release notes from the commit log since the previous tag.
Draft status is intentional — it requires manual review before publishing,
which is the right safety tradeoff for a single-maintainer project. The
script validates that gh is installed and authenticated, and that the git
tag exists, before attempting anything.

Pre-push quality gate (.husky/pre-push): Runs lint and the full test
suite before every push. This catches issues earlier than CI and
complements the existing pre-commit hook (which only runs lint-staged on
changed files). The tradeoff is slower pushes (~30s), but for this
project size that is acceptable.

CI produces release-ready artifacts: The CI workflow now runs
build:release instead of build, then runs package-release.js to produce
the versioned zip. The uploaded artifact is the same zip that would be
submitted to the Chrome Web Store, so any CI run on master produces a
directly usable release candidate.

INTENDED RELEASE WORKFLOW

  1. npm version patch|minor|major  (bumps package.json, creates commit)
  2. npm run release                (clean → test → build:release → zip)
  3. git tag v{version} && git push origin v{version}
  4. npm run release:github          (draft release with zip on GitHub)
  5. Review and publish the draft on GitHub
  6. Upload the same zip to Chrome Web Store

FILES CHANGED

  manifest.json              → version set to "0.0.0" (build-time placeholder)
  package.json               → version 0.9.5, new scripts, archiver dep
  scripts/build.mjs          → version injection, RELEASE mode support
  scripts/package-release.js → NEW: validated zip packaging
  scripts/github-release.js  → NEW: draft GitHub release creation
  .husky/pre-push            → NEW: lint + test gate
  .github/workflows/ci.yml   → release build + versioned zip artifact
  .gitignore                 → added release/
docs/release.md covers versioning, build modes, release steps,
and quality gates for anyone onboarding to the release process.
@igrigorik
igrigorik merged commit c66fc3d into master Mar 28, 2026
2 checks passed
@igrigorik
igrigorik deleted the claude/analyze-agentboard-release-6RCcR branch March 28, 2026 01:54
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.

2 participants