Skip to content
Moku
@moku-labs/ci pages

moku-release

The CLI: setup once, doctor any time, wait after a merge, fleet for the whole organization.

On this page

Commands

The release* scripts are plain aliases of the moku-release bin.

CommandWhenChanges
bun run release:setuponce per projectyes, each step confirmed
bun run release:doctorany timenothing in the project
bun run release waitafter each merge to mainnothing
bun run release fleetany timenothing
bun run release <patch|minor|major|prerelease>a release a person asks fordispatches the workflow
FlagWorks onMeaning
--dry-runsetup, a releasePrints every action, changes nothing, asks nothing
--yes, -ysetupAnswers every confirmation with yes, for agents and CI
--jsondoctor, fleetMachine output
--markdownfleetThe public rows as a Markdown table
--depsfleetAdds the toolchain and runtime-dependency tables

An unknown flag or command prints the usage and exits 1. It is never guessed.

setup

An idempotent wizard. Each step first asks whether it is already done, and prints a check instead of redoing it. Run it again any time.

#StepWhat it does
1Prerequisitesgh and npm are logged in. If not, it prints the command and stops.
2WorkflowsWrites ci.yml and publish.yml. A thin caller with its own inputs stays. A legacy file is replaced after a question, and a copy is kept: in git when committed, as .bak when not.
3DependabotWrites dependabot.yml and dependabot-automerge.yml. A file that differs is yours and stays.
4package.jsonFills what is missing, after a confirm. It never rewrites a value you chose.
5First publishbun run build && npm publish --access public, only if the package is not on npm.
6TagPushes the version tag only. Never one for a publish that did not happen.
7Trusted publishernpm trust github <name> --file publish.yml --repo <owner/repo> --allow-publish --yes
8Branch rulesetCreates the ruleset, or moves a legacy one to the ci / … checks.
9Auto-mergeAllows auto-merge on the repository.
10DoctorThe wizard's verdict is the same report doctor prints.

setup fills this package.json contract, and doctor reports it:

FieldRequirement
scriptslint, typecheck, test, build, validate, release:setup, release:doctor, release
publishConfig.access"public"
repository.urlMatches git remote get-url origin. git@…, git+https://… and .git compare equal.
filesPresent and not empty
engines.node>=24, the npm Trusted Publishing floor

doctor

Twelve checks, one line each, and the exact fix: command for every line that is not green. It runs git fetch --tags first and changes nothing else. It exits 1 when a check fails. A warning does not fail it.

CheckLevelLooks at
gh-authfailgh --version, then gh auth status
npm-authfailnpm whoami
npm-versionfailnpm is new enough for Trusted Publishing
package-contractfailEvery missing script and field, by name
preview-depsfailNo dependency range is a pkg.pr.new URL
repository-urlfailrepository.url against the origin remote
workflowsfail, warnBoth files exist (fail), are pinned to @v1 and release on a merge (warn)
npm-packagewarnnpm view <name> version
trusted-publisherfail, warnnpm trust list <name>. An npm with no trust command warns.
tag-syncwarnThe newest v* tag against npm latest, with the direction of the drift
branch-rulesetwarnAn active ruleset exists and requires no legacy check names
working-treewarnA clean tree and HEAD on origin/main. A release treats this one as a failure.
doctor
bun run release:doctor
 * moku release  doctor
 -----------------------------------------------------------------
  ✓ gh installed and authenticated — authenticated
  ✗ npm is logged in — npm is not logged in
      fix: npm login
  ✓ no pkg.pr.new preview dependencies — every dependency comes from the registry
  – repository.url matches origin — no `origin` remote configured
  ⚠ clean tree on origin/main — working tree has uncommitted changes
      fix: commit or stash them

✓ passed, ✗ failed, ⚠ warned, – skipped because a check could not run here.

wait

A merge to main ships a patch by itself, so wait dispatches nothing. It answers one question: is my commit in a version that npm serves?

  • origin/main holds HEAD
  • a release tag holds HEAD
  • npm serves that version
Stops whenAfter
origin/main does not hold HEADat once. Merge first.
No publish.yml run startstwo minutes
The run failsat once, with the gh run view <id> --log-failed line
No tag holds the committwenty minutes
npm does not serve the versionten more minutes

It asks the tags, not one run. A queued run can be replaced by the run of a later merge, and then that later version carries your commit. Use the version it prints in a consuming project, not latest.

release

a release a person asks for
bun run release minor --dry-run
bun run release minor
  1. Preflight. doctor in strict mode. A dirty tree or a HEAD that is not origin/main stops the run, and nothing is dispatched.
  2. Dispatch. gh workflow run publish.yml -f release_type=minor --ref main.
  3. Watch. It follows the run and fails with it.
  4. Verify. It asks npm for up to ten minutes, until the dist-tag moves, then prints the version, tag, npm URL and release URL.
ArgumentResultnpm dist-tag
patchOnly to repeat a failed auto-release. A merge already shipped the patch.latest
minorThe next minorlatest
majorThe next majorlatest
prereleaseAn rc versionnext

fleet

One table for every repository of the organization, read from the owner of this checkout's remote. One GraphQL call answers the table, and one npm view per package adds the served version.

ColumnShows
repoThe repository
ciThe state of CI on main
releaseThe state of the release of main
tag, npmThe latest tag and the version npm serves
aheadCommits on main nobody released
prsOpen pull requests, with the red and the bot ones counted
old pins@moku-labs/* dependencies behind the latest version

The list after the table, Waits for a person, holds red CI, a failed release, unreleased commits, npm not on the tag's version, a red PR, a green PR nobody merged. A draft is not listed.

fleet
bun run release fleet
bun run release fleet --deps
bun run release fleet --markdown

--deps adds the shared toolchain with the version most repositories are on, and the runtime dependencies that are behind npm. --markdown leaves private repositories out, because its output lands on a public page. examples/org/fleet.yml does exactly that: in the organization's .github repository it refreshes the table in profile/README.md every 15 minutes, with no token added.