One version for everything: the six npm packages, the Flutter port, the Laravel port and
the Rails port.
Today it is 3.5.0. Every release bumps all of them, even a package with no change:
the wire Version header and the i18n-keyless-core pin must agree.
node scripts/set-version.mjs 3.5.0 # 1. the version, in 11 files
# 2. CHANGELOG.md: rename "## [Unreleased]"; ports/flutter/CHANGELOG.md: add "## 3.5.0"
node scripts/publish.mjs --dry-run # 3. preflight, build, every test suite, publish dry runs
node scripts/publish.mjs # 4. the release, one confirmation per stepscripts/publish.mjs runs the steps below in order and asks before each upload. It skips
what is already done (a package at this version on npm or pub.dev, an existing tag), so
after a failure you fix the cause and run it again. Flags: --yes (no questions),
--skip-tests, --skip-npm, --skip-flutter, --skip-laravel, --skip-rails, --skip-git.
The rest of this file is the same procedure by hand, and the reasons behind each step.
| Package | Registry | Command | Runs its own tests before publishing |
|---|---|---|---|
i18n-keyless-core |
npm | npm publish (root) |
yes, prepublishOnly |
i18n-keyless-react |
npm | same | yes |
i18n-keyless-node |
npm | same | yes |
i18n-keyless-vue |
npm | same | yes |
i18n-keyless-angular |
npm | same (builds with ngc) |
yes |
i18n-keyless-browser |
npm | same | yes |
i18n_keyless (Flutter) |
pub.dev | flutter pub publish in ports/flutter |
no: run flutter test first |
i18n-keyless/laravel |
Packagist | git tag on a mirror repo | no: run vendor/bin/phpunit first |
i18n-keyless-rails |
RubyGems | gem build + gem push in ports/rails |
no: run bundle exec rake test first |
.npmrc sets workspaces=true, so a bare npm publish at the root publishes every
workspace, in alphabetical order: angular, browser, core, node, react, vue. Each package's
prepublishOnly cleans dist, builds, runs its vitest suite and packs. A red suite stops
that package (and the ones after it).
-
npm:
npm whoamimust answer (elsenpm login). The three new names are free. -
pub.dev:
flutter pub publishopens a browser the first time to sign in with a Google account. The publisher is that account until you create a verified publisher on pub.dev. -
Packagist: Packagist reads
composer.jsonat the ROOT of a git repository, so the Laravel port needs a mirror repository. Create an empty GitHub repoarnaudambro/i18n-keyless-laravel, then in this repo:git remote add laravel git@github.com:arnaudambro/i18n-keyless-laravel.git
After the first push (step 5 below), submit
https://github.com/arnaudambro/i18n-keyless-laravelon https://packagist.org/packages/submit once, and enable the GitHub hook it proposes. Later tags are picked up on their own. -
RubyGems:
gem signinonce (the gemspec setsrubygems_mfa_required, so the account needs MFA). The namei18n-keyless-railsis free. -
Toolchains: Node, PHP 8.5 + Composer, Flutter, Ruby 3.4 + Bundler (all installed on this machine, 2026-09-02).
node scripts/set-version.mjs 3.4.0 # 10 files: package.json x7, pubspec, version.dart, ApiClient.php
node scripts/set-version.mjs 3.4.0 --dry-run # to see the list firstThen:
CHANGELOG.md: rename## [Unreleased]to## [3.4.0] - YYYY-MM-DD.ports/flutter/CHANGELOG.md: add a## 3.4.0entry (pub.dev shows it on the package page).
The publish order is alphabetical, so angular and browser build against
packages/core/dist BEFORE core's own prepublishOnly rebuilds it. A stale dist ships
the old Version constant. Rebuild it by hand first:
(cd packages/core && rm -rf dist && npx tsc --project tsconfig.json)for p in core react node vue angular browser; do (cd packages/$p && npx vitest run) || break; done
(cd ports/laravel && vendor/bin/phpunit)
(cd ports/rails && bundle exec rake test)
(cd ports/flutter && flutter analyze && flutter test)npm publishIf a package fails half-way, the packages before it are already on npm. Fix, then publish the rest one by one:
npm publish -w packages/react # one workspacenpm publish --dry-run --ignore-scripts lists the order without building anything.
cd ports/flutter
flutter pub publish --dry-run # must report 0 warnings
flutter pub publish # asks for a confirmation, then uploadspub.dev has no unpublish. A bad version can only be retracted from the package admin page within 7 days, then replaced by a new version.
Packagist versions are git tags on the mirror repo. Push the subtree, then the tag:
# from the repo root, after the release commit exists on main
git subtree push --prefix=ports/laravel laravel main
SPLIT=$(git subtree split --prefix=ports/laravel main)
test -n "$SPLIT" && git push laravel "$SPLIT":refs/tags/v3.4.0git subtree split prints a commit hash; it creates no branch. Packagist picks the tag up
through the GitHub hook (or press "Update" on the package page).
Two traps in the last line, both of which have already happened once:
- Keep
refs/tags/...OUTSIDE the quotes. In zsh,"$SPLIT:refs/tags/v3.4.0"applies the history modifier:rto the variable, so the refspec becomes<hash>efs/tags/v3.4.0and the push fails withsrc refspec ... does not match any. - Keep the
test -n "$SPLIT"guard, and run the three lines in the SAME shell. With an emptySPLIT, the refspec reads:refs/tags/v3.4.0, which deletes the tag on the mirror. Recover withgit push laravel <hash>:refs/tags/v3.4.0.
The gem builds from ports/rails (no mirror: RubyGems takes a .gem file):
cd ports/rails
gem build i18n-keyless-rails.gemspec # i18n-keyless-rails-3.5.0.gem
gem push i18n-keyless-rails-3.5.0.gem # asks for the MFA code
rm i18n-keyless-rails-3.5.0.gemlib/i18n_keyless/version.rb is the version RubyGems reads (written by set-version.mjs).
git add -A
git commit -m "chore: release 3.4.0"
git tag v3.4.0
git push && git push --tagsThe push to main triggers .github/workflows/context7-refresh.yml, which re-indexes the
docs for agents.
npm view i18n-keyless-vue version
npm view i18n-keyless-angular version
npm view i18n-keyless-browser version
open https://pub.dev/packages/i18n_keyless
open https://packagist.org/packages/i18n-keyless/laravel
gem search -r i18n-keyless-rails --exact- npm:
npm unpublish <name>@<version>within 72 hours, then publish a fixed version. After 72 hours,npm deprecate <name>@<version> "reason"and publish a fix. - pub.dev: retract the version on its page, publish a fix.
- Packagist: delete the tag on the mirror (
git push laravel :refs/tags/v3.4.0) and press "Update" on the package page. - RubyGems:
gem yank i18n-keyless-rails -v 3.4.0(the version number stays taken), then publish a fix.