Release Process
May 31, 2026 · View on GitHub
Overview
Releases are automated. When a commit lands on main with a new version in package.json, CI creates the git tag, publishes to npm, and creates a GitHub Release. You just need to bump the version, get it onto main, and CI does the rest.
There are two ways to release:
npm run release— guided script that runs preflight checks, squash-merges your feature branch into main, and pushes. One command, fully automated. Best for solo maintainers.- Manual PR workflow — push the branch, create a PR via
gh pr create, wait for CI, merge viagh pr merge. Better for teams or when you want a review step.
Both end the same way: a push to main triggers CI which handles tagging and publishing.
Option A: npm run release
From your feature branch with everything committed:
npm run release
The script will:
- Verify you're on a feature branch (not main)
- Check for clean working tree
- Run
build:all(core + standalone offline editor) - Run
verify:release(npm pack + standalone size/import checks) - Run
test:standalone:e2e(Playwright smoke on the bundled editor) - Build
quikdown-airgap-vX.Y.Z.zip - Run unit tests (
npm test) - Auto-commit any badge/dist drift (including standalone bundles)
- Squash-merge into main and push
Normal dev / pre-commit does not build standalone. Husky runs lint + npm test only. The ~60s standalone Rollup step runs here, in publish CI, and when you explicitly run build:standalone.
CI takes over from there.
Option B: Manual PR workflow
1. Create a feature branch
git checkout main
git pull
git checkout -b feature/your-change-name
2. Make changes, bump version
# Make your changes, then bump the version
npm version patch --no-git-tag-version # or minor / major
# Day-to-day dev (no standalone build)
npm run build
npm test
# Before release (or let `npm run release` do it)
npm run build:all
npm run verify:release
npm run test:standalone:e2e
3. Commit
git add -A
git commit -m "v1.2.5: description of changes"
The pre-commit hook runs npm run lint and npm test automatically. If either fails, the commit is blocked — fix the issue and try again.
4. Push and create a PR
git push -u origin feature/your-change-name
gh pr create \
--title "v1.2.5: short description" \
--body "- change 1
- change 2
- change 3"
5. Wait for CI, then merge
CI runs the build job (lint, test, build verification). Branch protection requires this to pass.
# Check CI status
gh pr checks
# Merge when green
gh pr merge --squash
6. CI handles the rest
After merge to main, CI automatically:
- Runs normal
build+ tests on the PR (no standalone — fast loop) - On version bump: tags and triggers
publish.yml - Publish workflow runs
build:all,verify:release, standalone Playwright smoke, unit tests, npm publish - GitHub Release attaches standalone
.min.js,.gz,quikdown-airgap-vX.Y.Z.zip, all.d.tstype definitions, and CSS themes
7. Verify
# Check the release appeared
gh release list --limit 3
# Check npm
npm view quikdown version
If something goes wrong
Publish job failed after tagging
The tag exists but npm/GitHub Release didn't happen. Use the manual fallback:
gh workflow run publish.yml -f tag=v1.2.5
Pre-commit hook is too slow
The hook runs full lint + tests. This is intentional — it prevents broken commits. If you need to bypass it temporarily (not recommended):
git commit --no-verify -m "wip: temporary"
Version already exists on npm
tools/checkVersion.cjs catches this before tagging. Bump to the next version and try again.
What CI does (detail)
| When | What runs |
|---|---|
| PR / push (ci.yml) | npm run build, npm test, dist + .d.ts file checks, verify:package — no standalone |
| PR e2e-smoke (ci.yml) | @smoke-tagged Playwright tests — blocking (editor init, content editing, HTML whitelist, BD sync) |
| PR e2e (ci.yml) | Full Playwright suite — non-blocking (continue-on-error: true) |
| Pre-commit (husky) | lint, npm test — no standalone, no build |
npm run release (local) | build:all, verify:release, test:standalone:e2e, air-gap zip, npm test |
| publish.yml | Same release gates + npm publish + GitHub Release assets (bundles + .d.ts + CSS + air-gap zip) |
Verification scripts
| Script | What it checks | When it runs |
|---|---|---|
npm run verify:package | All exports.types paths exist, npm pack dry-run | Every PR (ci.yml) |
npm run verify:release | verify:package + standalone bundle size/imports + npm pack | release.sh + publish.yml |
npm run verify:types | TypeScript consumer fixture compiles (tsc --noEmit) | Optional — run locally before release |
Standalone (offline) editor
The quikdown_edit_standalone bundle (~7.7 MB / ~1.0 MB gzipped) ships on every release for air-gapped deployments. It is not part of the normal debug cycle.
| Check | Command |
|---|---|
| Build | npm run build:standalone or npm run build:all |
| Verify (size, self-contained, npm pack) | npm run verify:release |
| Browser smoke | npm run test:standalone:e2e |
| Air-gap zip | npm run build:airgap |
| Docs | docs/standalone-editor.md |
Branch protection
mainrequires thebuildstatus check to pass before merge- Direct pushes to
mainare discouraged — use PRs v*tags are created by CI, not manually