Skip to content

Commit 706e31b

Browse files
committed
chore: switch releases to tag-based publishing
1 parent 88f9742 commit 706e31b

5 files changed

Lines changed: 148 additions & 3716 deletions

File tree

.github/workflows/release.yml

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
name: Release
22

33
on:
4-
workflow_dispatch:
5-
inputs:
6-
increment:
7-
description: "Version bump (patch, minor, major)"
8-
required: true
9-
default: "patch"
10-
type: choice
11-
options:
12-
- patch
13-
- minor
14-
- major
4+
push:
5+
tags:
6+
- "v*.*.*"
157

168
concurrency:
179
group: release-${{ github.workflow }}-${{ github.ref }}
@@ -30,7 +22,7 @@ jobs:
3022
# "Unsupported GitHub Actions runner environment: self-hosted".
3123
runs-on: ubuntu-latest
3224
permissions:
33-
contents: write
25+
contents: read
3426
id-token: write
3527
steps:
3628
- uses: actions/checkout@v6
@@ -47,8 +39,6 @@ jobs:
4739
check-latest: true
4840
cache: pnpm
4941

50-
- run: git config user.name "github-actions[bot]"
51-
- run: git config user.email "github-actions[bot]@users.noreply.github.com"
5242
- run: pnpm install --frozen-lockfile
5343

5444
- name: Validate package metadata for trusted publishing
@@ -92,47 +82,73 @@ jobs:
9282
console.log("Package metadata validated.");
9383
NODE
9484
95-
- run: pnpm run lint
96-
- run: pnpm run typecheck
97-
- run: pnpm run build
98-
99-
# Sync the working tree to the currently published version, then let
100-
# release-it perform the requested bump from that baseline.
101-
- name: Bump version from npm registry
85+
- name: Validate release tag
86+
env:
87+
RELEASE_SHA: ${{ github.sha }}
88+
RELEASE_TAG: ${{ github.ref_name }}
10289
run: |
103-
LATEST=$(npm view acpx version 2>/dev/null || echo "0.0.0")
104-
echo "Latest on npm: $LATEST"
105-
TARGET=$(node - "$LATEST" "${{ inputs.increment }}" <<'NODE'
106-
const [version, increment] = process.argv.slice(2);
107-
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
108-
if (!match) {
109-
throw new Error(`Unsupported semver: ${version}`);
110-
}
111-
const [major, minor, patch] = match.slice(1).map(Number);
90+
set -euo pipefail
91+
git fetch --no-tags origin main --depth=1
11292
113-
if (increment === "patch") {
114-
console.log(`${major}.${minor}.${patch + 1}`);
115-
process.exit(0);
93+
node - <<'NODE'
94+
const { execFileSync } = require("node:child_process");
95+
const { readFileSync } = require("node:fs");
96+
97+
const releaseTag = process.env.RELEASE_TAG ?? "";
98+
const releaseSha = process.env.RELEASE_SHA ?? "";
99+
const semverTag = /^v\d+\.\d+\.\d+$/;
100+
101+
if (!semverTag.test(releaseTag)) {
102+
console.error(
103+
`Release tags must match vX.Y.Z; received ${releaseTag || "<missing>"}.`
104+
);
105+
process.exit(1);
116106
}
117-
if (increment === "minor") {
118-
console.log(`${major}.${minor + 1}.0`);
119-
process.exit(0);
107+
108+
const pkg = JSON.parse(readFileSync("package.json", "utf8"));
109+
const expectedTag = `v${pkg.version}`;
110+
111+
if (releaseTag !== expectedTag) {
112+
console.error(
113+
`Release tag ${releaseTag} does not match package.json version ${pkg.version}; expected ${expectedTag}.`
114+
);
115+
process.exit(1);
120116
}
121-
if (increment === "major") {
122-
console.log(`${major + 1}.0.0`);
123-
process.exit(0);
117+
118+
try {
119+
execFileSync(
120+
"git",
121+
["merge-base", "--is-ancestor", releaseSha, "origin/main"],
122+
{ stdio: "ignore" }
123+
);
124+
} catch {
125+
console.error(
126+
`Tagged commit ${releaseSha} is not contained in origin/main.`
127+
);
128+
process.exit(1);
124129
}
125130
126-
throw new Error(`Unsupported increment: ${increment}`);
131+
console.log(
132+
`Release tag ${releaseTag} matches package.json and points to a commit on origin/main.`
133+
);
127134
NODE
128-
)
129-
npm version --no-git-tag-version "$LATEST" --allow-same-version
130-
echo "VERSION=$TARGET" >> "$GITHUB_ENV"
131-
echo "Releasing: $TARGET"
132135
133-
- name: Release
136+
- name: Ensure version is not already published
134137
run: |
135-
pnpm exec release-it "$VERSION" --ci
136-
env:
137-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
138-
NPM_CONFIG_PROVENANCE: "true"
138+
set -euo pipefail
139+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
140+
PUBLISHED_VERSION=$(npm view acpx version 2>/dev/null || true)
141+
142+
if [ "$PUBLISHED_VERSION" = "$PACKAGE_VERSION" ]; then
143+
echo "acpx@$PACKAGE_VERSION is already published on npm."
144+
exit 1
145+
fi
146+
147+
echo "Publishing acpx@$PACKAGE_VERSION"
148+
149+
- run: pnpm run lint
150+
- run: pnpm run typecheck
151+
- run: pnpm run build
152+
153+
- name: Publish
154+
run: npm publish --access public --provenance

AGENTS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,12 @@ CI lives in [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
178178

179179
Release automation lives in [`.github/workflows/release.yml`](.github/workflows/release.yml).
180180

181-
- Releases are manual (`workflow_dispatch`)
181+
- Releases run when a `vX.Y.Z` tag is pushed
182182
- The workflow installs dependencies with `pnpm install --frozen-lockfile`
183183
- It validates `package.json` release metadata before publishing
184+
- It validates that the tag matches `package.json` version and that the tagged commit is on `main`
184185
- It runs `pnpm run lint`, `pnpm run typecheck`, and `pnpm run build`
185-
- It bumps from the latest npm version and publishes through `release-it`
186+
- It publishes directly to npm with trusted publishing and provenance
186187

187188
The release workflow currently requires these `package.json` values:
188189

0 commit comments

Comments
 (0)