@bounded-systems/mint 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +20 -0
- package/.github/workflows/release-provenance.yml +81 -0
- package/.github/workflows/release.yml +125 -0
- package/.github/workflows/version.yml +48 -0
- package/.release/README.md +18 -0
- package/CHANGELOG.md +55 -0
- package/README.md +176 -0
- package/adoption.mjs +191 -0
- package/intents.mjs +52 -0
- package/jsr.json +21 -0
- package/mint.mjs +217 -0
- package/mint.test.mjs +179 -0
- package/package.json +26 -0
- package/plan.mjs +90 -0
- package/release.mjs +130 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
16
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
17
|
+
with:
|
|
18
|
+
node-version: "22"
|
|
19
|
+
- run: npm ci --no-audit --no-fund
|
|
20
|
+
- run: npm test
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Reusable release-provenance workflow (workflow_call). A consuming repo calls
|
|
2
|
+
# this on a `v*` tag push (the tag `mint release` cut) with a ~10-line caller;
|
|
3
|
+
# no install, no published package. mint is checked out here and run against the
|
|
4
|
+
# caller's manifest + CHANGELOG, so fixing the tool once + bumping the pin
|
|
5
|
+
# propagates mint's release flow to every repo.
|
|
6
|
+
#
|
|
7
|
+
# # .github/workflows/release.yml in the consumer:
|
|
8
|
+
# name: release
|
|
9
|
+
# on:
|
|
10
|
+
# push:
|
|
11
|
+
# tags: ["v*"]
|
|
12
|
+
# permissions:
|
|
13
|
+
# contents: write
|
|
14
|
+
# id-token: write
|
|
15
|
+
# jobs:
|
|
16
|
+
# release:
|
|
17
|
+
# uses: bounded-systems/mint/.github/workflows/release-provenance.yml@<sha>
|
|
18
|
+
# with:
|
|
19
|
+
# ref: <sha>
|
|
20
|
+
#
|
|
21
|
+
# It emits the deterministic in-toto release Statement (tag → version plan →
|
|
22
|
+
# commit) with `mint attest`, keyless-signs it with cosign (the workflow's OIDC
|
|
23
|
+
# identity — no key material), and attaches the Statement + its Sigstore bundle
|
|
24
|
+
# to the caller's GitHub release. Verify with `cosign verify-blob` (the same
|
|
25
|
+
# bundle @bounded-systems/verify consumes).
|
|
26
|
+
name: release-provenance (reusable)
|
|
27
|
+
|
|
28
|
+
on:
|
|
29
|
+
workflow_call:
|
|
30
|
+
inputs:
|
|
31
|
+
ref:
|
|
32
|
+
description: "mint ref to run from (tag/sha for reproducibility)."
|
|
33
|
+
type: string
|
|
34
|
+
default: main
|
|
35
|
+
node-version:
|
|
36
|
+
description: "Node version for the mint runtime."
|
|
37
|
+
type: string
|
|
38
|
+
default: "22"
|
|
39
|
+
|
|
40
|
+
permissions:
|
|
41
|
+
contents: write # create / upload to the caller's GitHub release
|
|
42
|
+
id-token: write # OIDC — cosign keyless signing
|
|
43
|
+
|
|
44
|
+
jobs:
|
|
45
|
+
release-provenance:
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- name: Checkout caller (at the tag)
|
|
49
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
50
|
+
- name: Checkout mint
|
|
51
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
52
|
+
with:
|
|
53
|
+
repository: bounded-systems/mint
|
|
54
|
+
ref: ${{ inputs.ref }}
|
|
55
|
+
path: .mint
|
|
56
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
57
|
+
with:
|
|
58
|
+
node-version: ${{ inputs.node-version }}
|
|
59
|
+
- name: Install mint deps
|
|
60
|
+
working-directory: .mint
|
|
61
|
+
run: npm ci --no-audit --no-fund
|
|
62
|
+
# Run mint against the CALLER's repo (cwd = caller workspace), so it reads
|
|
63
|
+
# the caller's package.json + CHANGELOG.md + git HEAD.
|
|
64
|
+
- name: Emit the in-toto release Statement
|
|
65
|
+
env:
|
|
66
|
+
MINT_REF: ${{ inputs.ref }}
|
|
67
|
+
run: node .mint/mint.mjs attest --out mint-release.intoto.json
|
|
68
|
+
- name: Install cosign
|
|
69
|
+
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.7.0
|
|
70
|
+
- name: Keyless-sign the release Statement (cosign, OIDC)
|
|
71
|
+
env:
|
|
72
|
+
COSIGN_EXPERIMENTAL: "1"
|
|
73
|
+
run: cosign sign-blob --yes --bundle mint-release.intoto.sigstore.json mint-release.intoto.json
|
|
74
|
+
- name: Attach provenance to the GitHub release
|
|
75
|
+
env:
|
|
76
|
+
GH_TOKEN: ${{ github.token }}
|
|
77
|
+
run: |
|
|
78
|
+
assets="mint-release.intoto.json mint-release.intoto.sigstore.json"
|
|
79
|
+
# Create the release from the tag annotation, or upload to it if it exists.
|
|
80
|
+
gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-from-tag $assets \
|
|
81
|
+
|| gh release upload "$GITHUB_REF_NAME" $assets --clobber
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# On a vX.Y.Z tag (pushed by `mint release`):
|
|
4
|
+
# release — test, pack, SLSA-attest the tarball, emit + keyless-sign the
|
|
5
|
+
# in-toto release Statement (tag → version plan → commit), publish
|
|
6
|
+
# the GitHub release (notes from the tag annotation mint wrote) with
|
|
7
|
+
# the statement + its Sigstore bundle attached.
|
|
8
|
+
# publish — stage on npm via OIDC trusted publishing (no tokens, ever) and
|
|
9
|
+
# publish to JSR. The npm package is staged, not live: a maintainer
|
|
10
|
+
# must approve it with 2FA before it appears on the registry.
|
|
11
|
+
# See job summary for the stage ID + approval instructions.
|
|
12
|
+
#
|
|
13
|
+
# This workflow covers: npm (staged) + JSR + GitHub release.
|
|
14
|
+
# Out of scope here — triggered separately on the same tag:
|
|
15
|
+
# Nix flakes — flake consumers pin a sha; update PRs are opened by the
|
|
16
|
+
# flake-update bot when the tag lands on the default branch.
|
|
17
|
+
# GHCR images — OCI builds run in their own repo/workflow and reference the
|
|
18
|
+
# published npm tarball or the GitHub release asset.
|
|
19
|
+
# Other deploys — bounded-systems infra workflows consume the GH release event.
|
|
20
|
+
on:
|
|
21
|
+
push:
|
|
22
|
+
tags: ["v*"]
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: write # create the GitHub release
|
|
26
|
+
id-token: write # OIDC — SLSA attestation, cosign keyless, npm/JSR publishing
|
|
27
|
+
attestations: write
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
release:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
34
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
35
|
+
with:
|
|
36
|
+
node-version: "22"
|
|
37
|
+
- run: npm ci --no-audit --no-fund
|
|
38
|
+
- run: npm test
|
|
39
|
+
- name: Pack the package
|
|
40
|
+
id: pack
|
|
41
|
+
run: echo "tgz=$(npm pack | tail -1)" >> "$GITHUB_OUTPUT"
|
|
42
|
+
- name: Attest build provenance (SLSA)
|
|
43
|
+
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
|
|
44
|
+
with:
|
|
45
|
+
subject-path: ${{ steps.pack.outputs.tgz }}
|
|
46
|
+
# Release provenance: the deterministic in-toto Statement binding this tag to
|
|
47
|
+
# its version plan + commit. `mint attest` reproduces byte-for-byte what
|
|
48
|
+
# `mint release` wrote locally, now with the CI builder identity filled in.
|
|
49
|
+
- name: Emit the in-toto release Statement
|
|
50
|
+
run: node mint.mjs attest --out mint-release.intoto.json
|
|
51
|
+
- name: Install cosign
|
|
52
|
+
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.7.0
|
|
53
|
+
# Keyless: no key material — the signature is bound to this workflow's OIDC
|
|
54
|
+
# identity via Fulcio + logged in Rekor. The self-contained --bundle is the
|
|
55
|
+
# same artifact @bounded-systems/verify consumes (cosign verify-blob).
|
|
56
|
+
- name: Keyless-sign the release Statement (cosign, OIDC)
|
|
57
|
+
env:
|
|
58
|
+
COSIGN_EXPERIMENTAL: "1"
|
|
59
|
+
run: cosign sign-blob --yes --bundle mint-release.intoto.sigstore.json mint-release.intoto.json
|
|
60
|
+
- name: Publish GitHub release (notes from the tag annotation)
|
|
61
|
+
env:
|
|
62
|
+
GH_TOKEN: ${{ github.token }}
|
|
63
|
+
run: |
|
|
64
|
+
gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-from-tag \
|
|
65
|
+
"${{ steps.pack.outputs.tgz }}" \
|
|
66
|
+
mint-release.intoto.json \
|
|
67
|
+
mint-release.intoto.sigstore.json
|
|
68
|
+
|
|
69
|
+
publish:
|
|
70
|
+
needs: release
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
74
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
75
|
+
with:
|
|
76
|
+
node-version: "24" # npm 11.15.0+ required for staged publishing
|
|
77
|
+
registry-url: "https://registry.npmjs.org"
|
|
78
|
+
- run: npm ci --no-audit --no-fund
|
|
79
|
+
- name: Publish or stage on npm (OIDC trusted publishing — no token)
|
|
80
|
+
id: stage
|
|
81
|
+
continue-on-error: true # JSR is the source of truth; npm failure must not block it
|
|
82
|
+
run: |
|
|
83
|
+
npm install -g npm@latest # ensure npm 11.15.0+ for staged publishing
|
|
84
|
+
echo "npm version: $(npm --version)"
|
|
85
|
+
PKG=$(node -p "require('./package.json').name")
|
|
86
|
+
# npm stage publish requires the package to already exist on the registry.
|
|
87
|
+
# On first publish, fall back to npm publish to seed it; all subsequent
|
|
88
|
+
# releases go through the staged flow (human 2FA approval required).
|
|
89
|
+
if npm view "$PKG" version 2>/dev/null; then
|
|
90
|
+
echo "package exists — using npm stage publish"
|
|
91
|
+
npm stage publish --access public --provenance 2>&1 | tee /tmp/stage-out.txt || true
|
|
92
|
+
else
|
|
93
|
+
echo "first publish — seeding with npm publish (staged publishing requires existing package)"
|
|
94
|
+
npm publish --access public --provenance 2>&1 | tee /tmp/stage-out.txt || true
|
|
95
|
+
fi
|
|
96
|
+
STAGE_OUT=$(cat /tmp/stage-out.txt)
|
|
97
|
+
STAGE_ID=$(echo "$STAGE_OUT" | grep -oE '[A-Za-z0-9]{8}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{12}' | head -1 || true)
|
|
98
|
+
echo "stage_id=${STAGE_ID}" >> "$GITHUB_OUTPUT"
|
|
99
|
+
- name: Surface approval instructions
|
|
100
|
+
env:
|
|
101
|
+
STAGE_ID: ${{ steps.stage.outputs.stage_id }}
|
|
102
|
+
PKG: ${{ github.repository }}
|
|
103
|
+
run: |
|
|
104
|
+
{
|
|
105
|
+
echo "## npm package staged — human approval required"
|
|
106
|
+
echo ""
|
|
107
|
+
echo "The package is in the staging area, **not yet live on the registry**."
|
|
108
|
+
echo "A maintainer must approve it with 2FA before it becomes publicly available."
|
|
109
|
+
echo ""
|
|
110
|
+
if [ -n "$STAGE_ID" ]; then
|
|
111
|
+
echo "**Stage ID:** \`${STAGE_ID}\`"
|
|
112
|
+
echo ""
|
|
113
|
+
echo "**Approve via CLI (2FA required):**"
|
|
114
|
+
echo '```'
|
|
115
|
+
echo "npm stage approve ${STAGE_ID}"
|
|
116
|
+
echo '```'
|
|
117
|
+
else
|
|
118
|
+
echo "> Could not parse stage ID from npm output."
|
|
119
|
+
echo "> Run \`npm stage list @bounded-systems/mint\` to find it."
|
|
120
|
+
fi
|
|
121
|
+
echo ""
|
|
122
|
+
echo "**Or approve on npmjs.com:** open the [Staged Packages](https://www.npmjs.com/settings/~/packages/staged) tab."
|
|
123
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
124
|
+
- name: Publish to JSR (OIDC — no token)
|
|
125
|
+
run: npx jsr publish --allow-slow-types
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Reusable versioning check (workflow_call). A consuming repo calls this with a
|
|
2
|
+
# ~6-line caller; no install, no published package. mint is checked out here and
|
|
3
|
+
# run against the caller's .release/ intents, so fixing the tool once + bumping
|
|
4
|
+
# the pin propagates to every repo.
|
|
5
|
+
#
|
|
6
|
+
# jobs:
|
|
7
|
+
# version:
|
|
8
|
+
# uses: bounded-systems/mint/.github/workflows/version.yml@<sha>
|
|
9
|
+
#
|
|
10
|
+
# `mint plan` validates every intent (fails closed on a malformed one) and
|
|
11
|
+
# previews the next version + changelog. It exits 0 when there are no intents.
|
|
12
|
+
name: version (reusable)
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
workflow_call:
|
|
16
|
+
inputs:
|
|
17
|
+
ref:
|
|
18
|
+
description: "mint ref to run from (tag/sha for reproducibility)."
|
|
19
|
+
type: string
|
|
20
|
+
default: main
|
|
21
|
+
dir:
|
|
22
|
+
description: "Caller's intent directory."
|
|
23
|
+
type: string
|
|
24
|
+
default: .release
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
version-check:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- name: Checkout caller
|
|
34
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
35
|
+
- name: Checkout mint
|
|
36
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
37
|
+
with:
|
|
38
|
+
repository: bounded-systems/mint
|
|
39
|
+
ref: ${{ inputs.ref }}
|
|
40
|
+
path: .mint
|
|
41
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
42
|
+
with:
|
|
43
|
+
node-version: "22"
|
|
44
|
+
- name: Install mint deps
|
|
45
|
+
working-directory: .mint
|
|
46
|
+
run: npm ci --no-audit --no-fund
|
|
47
|
+
- name: mint plan (validate intents + preview next version)
|
|
48
|
+
run: node .mint/mint.mjs plan --dir "${{ github.workspace }}/${{ inputs.dir }}"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Release intents
|
|
2
|
+
|
|
3
|
+
One file per user-facing change, added in the PR that makes the change. `mint`
|
|
4
|
+
collects every `*.md` here (except this README and `_`/`.`-prefixed files),
|
|
5
|
+
resolves the strongest `bump`, and cuts the release deterministically.
|
|
6
|
+
|
|
7
|
+
Format:
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
---
|
|
11
|
+
bump: minor # patch | minor | major
|
|
12
|
+
---
|
|
13
|
+
scan: promote to a verb (CLI + MCP) + shared Zod type contracts
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- `mint plan` — preview the next version + changelog entry (pure; pass `--date` to pin it).
|
|
17
|
+
- `mint version` — apply: bump `package.json` + lockfile, prepend `CHANGELOG.md`, consume these intents.
|
|
18
|
+
- `mint release` — cut the `v<version>` tag + emit the in-toto release provenance (keyless-signed in CI). `--dry-run` previews it.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.3 — 2026-06-29
|
|
4
|
+
|
|
5
|
+
### Patch
|
|
6
|
+
|
|
7
|
+
- seed npm first publish — detect new package and fall back to `npm publish` before staging is available
|
|
8
|
+
|
|
9
|
+
## 0.4.2 — 2026-06-29
|
|
10
|
+
|
|
11
|
+
### Patch
|
|
12
|
+
|
|
13
|
+
- retry staged npm publish — trusted publisher updated to allow staging
|
|
14
|
+
|
|
15
|
+
## 0.4.1 — 2026-06-29
|
|
16
|
+
|
|
17
|
+
### Patch
|
|
18
|
+
|
|
19
|
+
- fix publish logging — stream npm stage output so errors are visible in CI (was silently swallowed by set -e)
|
|
20
|
+
|
|
21
|
+
## 0.4.0 — 2026-06-29
|
|
22
|
+
|
|
23
|
+
### Minor
|
|
24
|
+
|
|
25
|
+
- staged npm publishing — `npm stage publish` replaces direct publish; human 2FA approval gate before the package goes live
|
|
26
|
+
|
|
27
|
+
## 0.3.1 — 2026-06-29
|
|
28
|
+
|
|
29
|
+
### Patch
|
|
30
|
+
|
|
31
|
+
- release: JSR publish must not be blocked by the brand-new-npm-package step (continue-on-error)
|
|
32
|
+
|
|
33
|
+
## 0.3.0 — 2026-06-29
|
|
34
|
+
|
|
35
|
+
### Minor
|
|
36
|
+
|
|
37
|
+
- mint release: cut the tag + emit a deterministic in-toto release Statement (tag → version plan → commit), keyless-signed in CI (cosign/Sigstore; anchored-chain-shaped); `mint attest` re-emits it; reusable release-provenance.yml workflow_call for consumers
|
|
38
|
+
|
|
39
|
+
### Patch
|
|
40
|
+
|
|
41
|
+
- publish-prep: make the package cleanly JSR-publishable — `jsr.json` gains the SPDX `license` + a `publish.include` allowlist (tarball = exports + mint.mjs + README + CHANGELOG); `@types/node` dev dependency so the JSR type-checker resolves the `node:` imports. `npx jsr publish --dry-run` and `deno publish --dry-run` both pass.
|
|
42
|
+
|
|
43
|
+
## 0.2.0 — 2026-06-24
|
|
44
|
+
|
|
45
|
+
### Minor
|
|
46
|
+
|
|
47
|
+
- mint release verb (signed tag) + SLSA provenance release workflow
|
|
48
|
+
|
|
49
|
+
## 0.1.0 — 2026-06-24
|
|
50
|
+
|
|
51
|
+
### Minor
|
|
52
|
+
|
|
53
|
+
- Deterministic plan core (pure intents+version→version+changelog) + Zod intent contract + CLI (plan/version)
|
|
54
|
+
- Reusable version.yml workflow + org adoption scanner (--write rollout)
|
|
55
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# @bounded-systems/mint
|
|
2
|
+
|
|
3
|
+
Deterministic versioning — **intent files in, signed release out**. A *seam over
|
|
4
|
+
[`semver`](https://github.com/npm/node-semver)*: mint owns the flow (intent
|
|
5
|
+
assembly, changelog, tagging, provenance) and delegates only the version
|
|
6
|
+
arithmetic to the proven core.
|
|
7
|
+
|
|
8
|
+
Built because tagging-by-hand drifts: a manifest left behind its release tags is
|
|
9
|
+
a recurring bug ([string-audit#42](https://github.com/bounded-systems/string-audit/issues/42)).
|
|
10
|
+
mint makes the version a per-PR declaration and the release a single atomic step.
|
|
11
|
+
|
|
12
|
+
## Principles
|
|
13
|
+
|
|
14
|
+
- **Opinionated** — one canonical release flow, no configurable branching.
|
|
15
|
+
- **Strongly deterministic** — `plan()` is a *pure function* of `(currentVersion, intents, date)`. No commit-history ordering, no `Date.now()`, no randomness. Same intents in → same version + changelog out (sha in → sha out).
|
|
16
|
+
- **Typed end-to-end** — intents and plan validated with Zod; a verbspec CLI/MCP surface mirrors the rest of the stack.
|
|
17
|
+
- **Owned** — delegate only `semver` arithmetic; own assembly, rendering, tagging, and provenance.
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
Each PR drops an intent in `.release/` (see [`.release/README.md`](.release/README.md)):
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
---
|
|
25
|
+
bump: minor
|
|
26
|
+
---
|
|
27
|
+
scan: promote to a verb (CLI + MCP) + shared Zod type contracts
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
mint plan # preview: 0.6.1 → 0.7.0 (minor) + the changelog entry
|
|
34
|
+
mint version # apply: bump package.json + lockfile, prepend CHANGELOG.md, consume intents
|
|
35
|
+
mint release # cut the v<version> tag + emit release provenance (CI keyless-signs it)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`mint plan --json` emits the machine-readable plan. `--date YYYY-MM-DD` pins the
|
|
39
|
+
changelog date (the pure core never reads the clock; the CLI injects it).
|
|
40
|
+
|
|
41
|
+
## Release provenance
|
|
42
|
+
|
|
43
|
+
`mint release` cuts the annotated tag `v<version>` (signed when a git signing key
|
|
44
|
+
is configured) **and** emits a release **provenance record** — an
|
|
45
|
+
[in-toto Statement v1](https://in-toto.io/Statement/v1) binding the three things a
|
|
46
|
+
release is:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
tag → version plan → commit
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The subject is the tag, anchored to its commit (in-toto `gitCommit` digest); the
|
|
53
|
+
predicate carries the byte-exact changelog entry (the deterministic `plan()`
|
|
54
|
+
output) and its `sha256`. Re-deriving the plan over the same intents reproduces
|
|
55
|
+
that digest — the tag-to-plan link is machine-checkable, offline.
|
|
56
|
+
|
|
57
|
+
The Statement is **deterministic** (`releaseStatement()` is pure, like `plan()`)
|
|
58
|
+
and **keyless-signed in CI**: `release.yml` runs `mint attest`, then
|
|
59
|
+
`cosign sign-blob` binds the signature to the workflow's OIDC identity (Fulcio +
|
|
60
|
+
Rekor — no key material). Locally the Statement is emitted **unsigned**
|
|
61
|
+
(`builder: null`) so the flow degrades gracefully off-CI. This is the same
|
|
62
|
+
Statement / DSSE / keyless-Sigstore shape the bounded-systems sites and
|
|
63
|
+
[`@bounded-systems/verify`](https://github.com/bounded-systems/verify) already
|
|
64
|
+
produce and verify, so a mint release record verifies with the same tooling:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
cosign verify-blob \
|
|
68
|
+
--bundle mint-release.intoto.sigstore.json \
|
|
69
|
+
--certificate-identity-regexp '^https://github.com/<org>/<repo>/' \
|
|
70
|
+
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
|
71
|
+
mint-release.intoto.json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **anchored-chain:** mint deliberately mirrors
|
|
75
|
+
> [`anchored-chain`](https://github.com/bounded-systems/anchored-chain)'s in-toto
|
|
76
|
+
> constants + Statement/DSSE shape rather than depending on it: anchored-chain's
|
|
77
|
+
> in-toto module is Phase-0 and not yet re-exported from its public surface, is
|
|
78
|
+
> bun/TypeScript with a build step, and its predicate models a *derivation
|
|
79
|
+
> chain*, not a release. mint stays a zero-build node ESM package and can adopt
|
|
80
|
+
> anchored-chain's `Signer`/`Verifier` once that surface lands — the bytes
|
|
81
|
+
> already match.
|
|
82
|
+
|
|
83
|
+
### Adopt mint's release in CI
|
|
84
|
+
|
|
85
|
+
A consumer repo calls the reusable
|
|
86
|
+
[`release-provenance.yml`](.github/workflows/release-provenance.yml) on its tag
|
|
87
|
+
push (a ~10-line caller — see the header of that workflow):
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
# .github/workflows/release.yml
|
|
91
|
+
name: release
|
|
92
|
+
on: { push: { tags: ["v*"] } }
|
|
93
|
+
permissions: { contents: write, id-token: write }
|
|
94
|
+
jobs:
|
|
95
|
+
release:
|
|
96
|
+
uses: bounded-systems/mint/.github/workflows/release-provenance.yml@<sha>
|
|
97
|
+
with: { ref: <sha> }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`adoption.mjs --write` drops both this caller and the `version.yml` caller into
|
|
101
|
+
every publishable repo — the path off hand-tagging.
|
|
102
|
+
|
|
103
|
+
## Publish (npm + JSR)
|
|
104
|
+
|
|
105
|
+
mint ships from `release.yml` on each `v*` tag via **OIDC trusted publishing** —
|
|
106
|
+
no `NPM_TOKEN`, no JSR token, ever. The package manifests are kept in lockstep by
|
|
107
|
+
`mint version` (it bumps `package.json`, `package-lock.json`, **and** `jsr.json`).
|
|
108
|
+
|
|
109
|
+
**npm uses staged publishing** — `npm stage publish` submits the package to a
|
|
110
|
+
staging area rather than making it live immediately. A maintainer must approve it
|
|
111
|
+
with 2FA before it appears on the registry:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
npm stage approve <stage-id> # CLI, 2FA required
|
|
115
|
+
# or: npmjs.com → Staged Packages tab → Approve
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The stage ID is surfaced in the GitHub Actions job summary after each release run.
|
|
119
|
+
|
|
120
|
+
**JSR publishes immediately** on the same tag (no staging concept on JSR).
|
|
121
|
+
|
|
122
|
+
JSR-readiness is verified the same way CI publishes:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
npx jsr publish --dry-run --allow-slow-types # the path release.yml runs
|
|
126
|
+
deno publish --dry-run --allow-slow-types # equivalent, Deno-native
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`jsr.json` carries the SPDX `license` and a `publish.include` allowlist, so the
|
|
130
|
+
published tarball is just the three exports + `mint.mjs`, `README`, `CHANGELOG`
|
|
131
|
+
(no tests, lockfiles, or workflows). `@types/node` is a dev dependency so the
|
|
132
|
+
JSR type-checker resolves mint's `node:` imports.
|
|
133
|
+
|
|
134
|
+
> **One-time JSR link (manual, once):** before the first JSR publish, link the
|
|
135
|
+
> package on [jsr.io](https://jsr.io) — create `@bounded-systems/mint` under the
|
|
136
|
+
> `@bounded-systems` scope and connect it to the `bounded-systems/mint` GitHub
|
|
137
|
+
> repo (Settings → "Link to a GitHub repository"). That GitHub link is what
|
|
138
|
+
> authorizes the keyless OIDC publish; after it, every tagged release publishes
|
|
139
|
+
> with no token.
|
|
140
|
+
|
|
141
|
+
## Library
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import { plan } from "@bounded-systems/mint";
|
|
145
|
+
import { releaseStatement } from "@bounded-systems/mint/release";
|
|
146
|
+
|
|
147
|
+
plan({
|
|
148
|
+
currentVersion: "0.6.1",
|
|
149
|
+
intents: [{ bump: "minor", summary: "scan: promote to a verb" }],
|
|
150
|
+
date: "2026-06-23",
|
|
151
|
+
}).nextVersion; // "0.7.0"
|
|
152
|
+
|
|
153
|
+
releaseStatement({
|
|
154
|
+
version: "0.7.0",
|
|
155
|
+
tag: "v0.7.0",
|
|
156
|
+
commit: "0123456789abcdef0123456789abcdef01234567",
|
|
157
|
+
date: "2026-06-23",
|
|
158
|
+
changelog: "## 0.7.0 — 2026-06-23\n\n### Minor\n\n- scan: promote to a verb\n",
|
|
159
|
+
producer: "@bounded-systems/mint",
|
|
160
|
+
})._type; // "https://in-toto.io/Statement/v1"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Roadmap
|
|
164
|
+
|
|
165
|
+
- [x] Deterministic `plan` core + Zod intent contract
|
|
166
|
+
- [x] `mint plan` / `mint version`
|
|
167
|
+
- [x] `mint release` — signed tag + in-toto release provenance, keyless-signed in CI (cosign/Sigstore; anchored-chain-shaped)
|
|
168
|
+
- [ ] verbspec-typed CLI + MCP surface
|
|
169
|
+
- [x] Reusable `workflow_call` Action (`version.yml` + `release-provenance.yml`)
|
|
170
|
+
- [x] Publish to npm (staged, human 2FA approval gate) + JSR
|
|
171
|
+
|
|
172
|
+
Tracking: [bounded-systems/string-audit#43](https://github.com/bounded-systems/string-audit/issues/43).
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
PolyForm-Noncommercial-1.0.0
|