Release Gerillass
The repository and the npm package are separate things. Fixing main changes
nothing for anyone using the library — the fix reaches users only when a new
version is published. Never stop at the git tag.
Take the target version from the user. If they did not name one, propose one and confirm before touching anything:
- patch for dependency, security and bugfix work. This is what almost every release here has been, including dependency restructuring (see 1.2.6, 1.3.3).
- minor for a new mixin or function.
- major for a change that breaks a call that worked, such as a removed member or a changed argument order. 2.0.0 was the module migration; 3.0.0 replaced the gradient mixins.
Every release publishes to both registries, npm (step 6) and RubyGems (step 6b). Every major release also rewrites the site's announcement band (step 3c). Both are rules the maintainer set; do not skip either.
1. Verify the tree before anything else
npm test # must pass: specs, smoke test and the manifest suite
yarn audit # must be zero across all severities
node tools/audit.js # the SILENT bucket must be empty
node tools/check-docs.js # the docs must not claim a count the repo lacks
node tools/check-wiki.js # names what the wiki page has to cover
git status --short # know exactly what is going out
The library must no longer trip the two deprecations the module migration was about. This must print nothing:
npx sass --load-path=scss test/smoke.scss 2>&1 >/dev/null | grep -E 'DEPRECATION.*(import|global-builtin)'
A plain grep DEPRECATION will still find something, and that is expected: Sass
is deprecating its own if(), which the library calls in 21 places. Use npx,
not a system sass — an older one has nothing to say about if() and will make
a stale tree look clean.
If dependencies in package.json is non-empty, stop and fix that first — it is
the single most consequential defect this package can ship. See CLAUDE.md.
2. Bump and document
Edit version in package.json, then add an entry at the top of
CHANGELOG.md, directly above the previous version heading. Match the existing
bullet style exactly:
## 1.3.4
- **Security:** ...
- **Added:** ...
- **Updated:** ...
- **Removed:** ...
- **Fixed:** ...
Write what changed for a user of the library, not what changed in the repo.
3. Confirm the tarball
npm pack --dry-run
llms.txt is generated too, and its links are the one thing no test can check,
because they point at sites this repository does not build:
node tools/check-links.js
Run it on every release, not only ones that add a member: a page published
on the documentation site since the last release should stop linking to source.
v2.1.0 shipped with line-clamp pointing at a page that did not exist, because
this step was a loop to copy and it was skipped.
Expect 114 files, an 82 kB tarball (3.0.0): everything under scss/, the generated
gerillass.json and SKILL.md, plus README.md, LICENSE.md and
package.json. If test/, meta/, tools/, yarn.lock or
node_modules appear, .npmignore is broken — stop and fix it.
The source zip and tarball GitHub attaches to the release are a different
package. They are built with git archive from the tag, and .npmignore does
not reach them; export-ignore in .gitattributes does. They are meant to hold
the library's buildable source, not only what npm ships, so check them against
their own list:
node tools/check-archive.js
It must pass before tagging. An unexpected entry means something was committed
that .gitattributes does not keep out, which is how site/ was headed for the
first release after 2.1.0 without anyone deciding it.
3b. Write the wiki page for the other repositories
A release is not finished when npm has it. gerillass.com and
docs.gerillass.com are separate repositories whose sessions cannot see this
one, and a changelog does not tell them which control on which page to touch.
Add wiki/vX.Y.Z.md, following wiki/README.md and an existing file.
node tools/check-wiki.js lists exactly which members it has to name, worked
out from the manifest at the previous tag, and fails until the page covers all
of them. A hook runs it too, so bumping the version in step 2 before writing
the page will block. Do not
restate the API: gerillass.json already holds it and is verified. Write what
is not in there.
If the release adds or changes a member, the page must cover:
- the playground member
<select>ongerillass.com, which needs every new mixin adding by hand, and the version<select>, which needs the new version; - which documentation pages to write or update on
docs.gerillass.com; - the measurement behind each member. This is the part with no other home, and it is what a documentation page should be built on.
3c. The site, which advertises the release
gerillass.com and its documentation live under site/, and several places on
it name the version. A release that updates the library and not these leaves
the site advertising the old one, or linking to a download that does not exist.
| Where | How it follows the version | What to do |
|---|---|---|
| Header badge under the logo | site/src/release.js reads VERSION from the root package.json |
nothing to edit; confirm it after a build |
| Both download buttons | DOWNLOAD_URL in the same file, archive/refs/tags/vX.Y.Z.zip |
the link is a 404 until the tag is pushed, so deploy the site only after step 4 |
| Playground version menu | reads the npm registry; FALLBACK in site/src/components/Playground/versions.js is the seed used when that fails |
add the version to versions and latest after npm has it (step 7), not before |
| Stargazers row | a committed snapshot | npm run supporters --prefix site |
| Playground starter snippets | demos.json, generated from site/content/docs/*.mdx and the root gerillass.json |
npm run playground-demos --prefix site when a documented example or a page description changed, and whenever a mixin is added |
| Documentation pages | site/content/docs/*.mdx |
compile every example against the previous tag and HEAD; for each member whose behaviour changed, read its argument table and footnotes for claims that are no longer true |
| Announcement banner | hand-written in site/src/components/Announcement/index.jsx |
every major release: name the version, say in one sentence what breaks, link MIGRATION.md. A rule since 17 September 2026. Leave it for a minor or patch |
Confirm the badge and the download link in the build before tagging:
npm run build --prefix site
grep -o "X.Y.Z" site/dist/index.html | sort | uniq -c
grep -o 'archive/refs/tags/v[^"]*' site/dist/index.html
After the tag is pushed, check that the link resolves:
git ls-remote --tags https://github.com/selfishprimate/gerillass.git | grep vX.Y.Z
4. Commit, tag, push
git add package.json CHANGELOG.md
git commit # subject: "Release vX.Y.Z"
git tag -a vX.Y.Z -m "vX.Y.Z" # tag format is vX.Y.Z — no dot after the v
git push origin main
git push origin vX.Y.Z
The default branch is main, protected against force-push and deletion but open
to direct pushes.
5. GitHub release
gh release create vX.Y.Z --repo selfishprimate/gerillass \
--title "vX.Y.Z" --latest --notes-file <file>
Reuse the changelog entry as the notes, and end with a compare link:
https://github.com/selfishprimate/gerillass/compare/vPREV...vX.Y.Z
6. Publish to npm
npm whoami # must print selfishprimate
npm publish
The account has 2FA enabled, so npm publish fails with EOTP. The
one-time code is a credential — do not ask for it and do not type it. Tell the
maintainer to run this themselves:
npm publish --otp=<code>
6b. Publish to RubyGems
The gem is the same release: gerillass.gemspec reads its version from
package.json, so there is nothing to bump. It ships scss/, gerillass.json,
SKILL.md, lib/, LICENSE.md and README.md and has no runtime
dependencies. The two agent files are committed build output and the gem takes
them as they are.
Build it from the tag, never from a branch. 2.3.1 was built from a branch
that already held a commit made after v2.3.1, and the gem shipped three
warning messages that differ from the npm package of the same version. A
version cannot be pushed twice, so a difference like that stays until the next
release. Build in a worktree of the tag, outside the repository so no .gem is
left at the root, and check what is in it before pushing:
out="$(mktemp -d)"
git worktree add "$out/src" vX.Y.Z
(cd "$out/src" && gem build gerillass.gemspec --output "$out/gerillass-X.Y.Z.gem")
git worktree remove "$out/src"
ruby -e 'require "rubygems/package"; s = Gem::Package.new(ARGV[0]).spec
puts s.version, s.runtime_dependencies.inspect, s.files.size,
s.files.reject { |f| f.start_with?("scss/") }' "$out/gerillass-X.Y.Z.gem"
Expect X.Y.Z, [], one file per scss/ partial plus seven, and those seven
being gerillass.json, SKILL.md, LICENSE.md, README.md and the three
files under lib/.
Then compare it with what npm published for the same version. Every command must print nothing:
gem unpack "$out/gerillass-X.Y.Z.gem" --target "$out/gem"
mkdir "$out/npm" && (cd "$out/npm" && tar -xzf "$(npm pack gerillass@X.Y.Z --silent)")
diff -r "$out/gem/gerillass-X.Y.Z/scss" "$out/npm/package/scss"
diff "$out/gem/gerillass-X.Y.Z/gerillass.json" "$out/npm/package/gerillass.json"
diff "$out/gem/gerillass-X.Y.Z/SKILL.md" "$out/npm/package/SKILL.md"
The macOS system Ruby is too old for the integrations; use Homebrew's
(/opt/homebrew/opt/ruby/bin). The gemspec is kept to ASCII on purpose: RubyGems
reads it in the locale's encoding and a literal non-ASCII character fails to
load. rubygems_mfa_required is set, so the push needs a one-time code, and
signing in (gem signin) is the maintainer's to do. Tell them to run:
gem push "$out/gerillass-X.Y.Z.gem" --otp=<code>
7. Verify what actually shipped
npm reports "may take a few minutes to become available", so the registry lags the publish. Poll rather than assuming:
until [ "$(npm view gerillass version)" = "X.Y.Z" ]; do sleep 10; done
npm view gerillass@X.Y.Z dependencies # MUST be empty
npm view gerillass@X.Y.Z dist.shasum # must match the shasum npm pack printed
Then confirm the consumer experience end to end:
cd "$(mktemp -d)" && npm init -y >/dev/null && npm install gerillass@X.Y.Z
ls node_modules | grep -v '^\.' # expect gerillass and nothing else
npm audit
Then seed the playground's FALLBACK with the new version, as in step 3c, and
build the site.
Report the published version, the dependency count a consumer now installs, and the audit result. If any Dependabot alerts or PRs were resolved by this release, close them and say so.