Releasing
Use this before cutting or publishing a release.
1. Tree and policy checks
- Ensure the worktree is clean or that every change is intentional.
- Confirm
.envis ignored and never committed. - Confirm
SECURITY.mdexists and still reflects the secret-handling policy. - Confirm the repository does not make IBM validation claims without evidence.
2. Verification
Confirm the runtime before running the checks. Node.js 24 or newer is required:
node --version # must be v24 or newer
Run the repository checks:
pnpm lint
pnpm format:check
pnpm fmt:check
pnpm typecheck
pnpm test
pnpm test:gnucobol
pnpm lint:conformance
pnpm lint:zos
pnpm sbom:check
pnpm bankc --help
pnpm bankc doctor
pnpm bankc check examples/account-transfer
pnpm bankc build examples/account-transfer --out /tmp/banklang-release-build
pnpm bankc layout examples/account-transfer
pnpm bankc verify examples/account-transfer
pnpm bankc test examples/account-transfer
pnpm bankc emit jcl examples/account-transfer
Every example must pass, not only the first one:
pnpm examples:verify
Not a loop over examples/*/. That is what this checklist used to say, and
it fails: end-of-day-settlement is three programs and a sort in one job, so it
has no src/main.bank.ts of its own and bankc test on the directory reports
that it is not a project. CI made the same mistake and
CONTRIBUTING.md records it. tools/verify-examples.ts
knows which entries are jobs.
pnpm test includes tests/cobol-compiles.test.ts, which compiles every
example with cobc when GnuCOBOL is installed. Treat a skipped compile lane as
an unverified release, not a passing one.
3. Documentation and evidence
- Update
CHANGELOG.md. - Update README command inventory and limitations if behavior changed.
- Update example READMEs and evidence bundle READMEs if generated output changed.
- Record what was validated in the commit body for compiler behaviour, generated output, or security changes.
- Keep generated outputs deterministic and avoid timestamps.
4. Git hygiene
- Run
git diff --check. - Commit with a message that explains why the change was made and what was validated.
- Push only after the release slice is verified.
5. Cutting the release
.github/workflows/release.yml does the rest, and refuses an unsigned tag:
a signature needs a key belonging to a person rather than to a runner, so the
workflow will not pretend it signed one. A lightweight tag has no object to
sign and is rejected the same way.
- Rename
## [Unreleased]inCHANGELOG.mdto## [X.Y.Z] - YYYY-MM-DD. - Set the version everywhere it is stated.
tests/release-version.test.tsenumerates those surfaces and fails if any disagrees withpackage.json: the extension manifest, the language server'sSERVER_VERSION,CITATION.cff, the extension's own changelog, and the samplebankc doctoroutput printed in toolchain.md. git tag -s vX.Y.Z -m "X.Y.Z"and push the tag.
The workflow then reruns the whole of ci.yml on the tagged commit and builds
the three artifacts a release publishes:
| Artifact | What it is |
|---|---|
banklang-vscode-X.Y.Z.vsix |
The editor extension, compiler bundled inside |
banklang-X.Y.Z.cdx.json |
CycloneDX bill of materials, every platform |
banklang-zos-conformance-X.Y.Z.tar.gz |
The z/OS bundle: COBOL, copybooks, JCL, expected results |
All three are attested with Sigstore through GitHub's OIDC identity and
attached to a GitHub release. There is no checksum file: the z/OS bundle
carries a SHA-256 per member in its own manifest.json, and
gh attestation verify covers the archives themselves.
No npm publish. Every workspace package is private: true, so the
repository is the distribution and nothing goes to a registry. The workflow
also deliberately does not run vsce publish: that needs a marketplace token
belonging to a person, and the attested .vsix on the release is the exact
file to upload by hand.
At the release cut, after the version section has been dated and
[Unreleased] is empty, take the snapshot the release page and README are held
to:
pnpm release:snapshot # writes evidence/release/X.Y.Z.json
pnpm release:snapshot --check
It refuses if the changelog has no dated section for the version, and in
ordinary development it leaves the already-released snapshot frozen. At the
release cut it compares the snapshot with the current evidence, and
tests/release-claims.test.ts fails if any figure quoted on the release page (
docs/releases/0.10.0.md is the current one) disagrees
with it.
Pre-1.0, so a release that adds language surface, a diagnostic or a CLI command
is a minor bump. 1.0 is not a function of the repository becoming public:
there has been no native IBM Enterprise COBOL validation and no production
ledger use, and a 1.0 would claim otherwise.
Read ## [Unreleased] to decide which: a new file organisation, a diagnostic
or a CLI command makes it a minor release rather than a patch.
The version stays where it is until the release is cut. The surfaces above
are held to each other, and the changelog is held to them: bumping the version
without renaming ## [Unreleased] to a dated section names a version the
changelog has never heard of, and the suite says so. The bump, the section
rename and the release snapshot are one commit, and the signed tag follows it.