hotfix.yml was deleted (folded into release.yml). Remove the now-broken release-coverage-scope and policy-release-no-npm-self-upgrade assertions that readFileSync'd hotfix.yml (release.yml equivalents retained), drop the dead install-smoke.yml path trigger, and update VERSIONING.md / docs/branching.md prose to describe hotfixes via the Release workflow with a patch version. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1
.github/workflows/install-smoke.yml
vendored
1
.github/workflows/install-smoke.yml
vendored
@@ -30,7 +30,6 @@ on:
|
||||
- 'tests/release-tarball-smoke.install.test.cjs'
|
||||
- '.github/workflows/install-smoke.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
- '.github/workflows/hotfix.yml'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
@@ -71,35 +71,24 @@ For fixes that need to ship without waiting for the next minor.
|
||||
|
||||
A hotfix `vX.YY.Z` cumulatively includes everything in `vX.YY.{Z-1}` plus every `fix:`/`chore:` commit landed on `main` since that base. The base tag is the anchor — `git cherry $BASE_TAG main` reveals exactly which commits are still unshipped, and the new `vX.YY.Z` tag becomes the next hotfix's base, so the cycle is self-documenting.
|
||||
|
||||
#### Two paths
|
||||
#### How to dispatch a hotfix
|
||||
|
||||
**Path A — `hotfix.yml` (canonical, two-step):**
|
||||
Hotfixes are dispatched via the **Release workflow (`release.yml`)** with a patch version (X.Y.Z). There is no separate hotfix workflow.
|
||||
|
||||
1. Trigger `hotfix.yml` with `action=create`, `version=1.27.1`, `auto_cherry_pick=true` (default).
|
||||
1. Trigger `release.yml` with `action=create`, `version=1.27.1`, `auto_cherry_pick=true` (default).
|
||||
- Workflow detects `BASE_TAG` = highest `v1.27.*` < `v1.27.1` (so `1.27.1` branches from `v1.27.0`; `1.27.2` would branch from `v1.27.1`).
|
||||
- Branches `hotfix/1.27.1` from `BASE_TAG`.
|
||||
- Auto-cherry-picks every `fix:`/`chore:` commit on `origin/main` not already in the base, oldest-first. Patch-equivalents are skipped via `git cherry`. `feat:`/`refactor:` are **never** auto-included.
|
||||
- On conflict the workflow halts with the offending SHA. Resolve manually on the branch, then re-run finalize with `auto_cherry_pick=false`.
|
||||
- Bumps `package.json` (and `sdk/package.json`), pushes the branch, and lists every included SHA in the run summary.
|
||||
2. (Optional) push additional manual commits to `hotfix/1.27.1`.
|
||||
3. Trigger `hotfix.yml` with `action=finalize`. The workflow:
|
||||
3. Trigger `release.yml` with `action=finalize`. The workflow:
|
||||
- Runs `install-smoke` cross-platform gate.
|
||||
- Runs full test suite + coverage.
|
||||
- Builds SDK, bundles `sdk-bundle/gsd-sdk.tgz` inside the CC tarball (parity with `release-sdk.yml`).
|
||||
- Builds SDK, bundles `sdk-bundle/gsd-sdk.tgz` inside the CC tarball.
|
||||
- Tags `v1.27.1`, publishes to `@latest`, re-points `@next → v1.27.1`.
|
||||
- Opens merge-back PR against `main`.
|
||||
|
||||
**Path B — `release-sdk.yml` (stopgap, one-shot):**
|
||||
|
||||
Active while the `@opengsd/gsd-sdk` npm token is unavailable; bundles the SDK inside the CC tarball.
|
||||
|
||||
1. Trigger `release-sdk.yml` with `action=hotfix`, `version=1.27.1`, `auto_cherry_pick=true`.
|
||||
- The `prepare` job creates the branch and cherry-picks (same logic as Path A).
|
||||
- `install-smoke` runs against the new branch.
|
||||
- The `release` job tags, publishes to `@latest`, re-points `@next`, opens merge-back PR.
|
||||
- Idempotent: if `hotfix/1.27.1` already exists (e.g. you ran `hotfix.yml create` first), the prepare job checks it out and re-runs cherry-pick as a no-op.
|
||||
2. `dry_run=true` exercises the full pipeline without pushing the branch or publishing.
|
||||
|
||||
### Minor Release (Standard Cycle)
|
||||
|
||||
For accumulated fixes and enhancements.
|
||||
|
||||
@@ -74,7 +74,7 @@ These are work branches. Open one, push commits, PR it, merge it, let it auto-de
|
||||
| `perf/` | Performance work, no behavior change | `next` | `perf/3300-skill-index` |
|
||||
| `ci/` | CI/workflow changes only | `next` | `ci/3801-add-node-26-matrix` |
|
||||
| `revert/` | Reverting a previously-merged change | `next` (or `main` if urgent) | `revert/3919-bad-merge` |
|
||||
| `hotfix/X.Y.Z` | Patch release branch (created by `hotfix.yml`) | `main` | `hotfix/1.27.1` |
|
||||
| `hotfix/X.Y.Z` | Patch release branch (created by `release.yml` with a patch version X.Y.Z) | `main` | `hotfix/1.27.1` |
|
||||
| `release/X.Y.0` | Minor/major release branch (created by `release.yml`) | `main` | `release/1.28.0` |
|
||||
|
||||
> **The branch name rule is enforced** by `.github/workflows/branch-naming.yml`.
|
||||
@@ -130,10 +130,10 @@ git pull --ff-only
|
||||
git checkout -b fix/3919-critical-crash
|
||||
# ... commit, push, PR to next, merge.
|
||||
|
||||
# 2. Trigger the hotfix workflow from the Actions tab:
|
||||
# workflow: Hotfix Release
|
||||
# 2. Trigger the Release workflow (release.yml) from the Actions tab:
|
||||
# workflow: Release
|
||||
# action: create
|
||||
# version: 1.27.1 (next patch number)
|
||||
# version: 1.27.1 (next patch number, X.Y.Z)
|
||||
# auto_cherry_pick: true (default)
|
||||
```
|
||||
|
||||
@@ -148,7 +148,7 @@ The workflow:
|
||||
6. `finalize` publishes to npm `@latest`, tags `v1.27.1`, opens
|
||||
merge-back PRs to **both** `main` and `next`.
|
||||
|
||||
> See `.github/workflows/hotfix.yml` and `VERSIONING.md` for the deep dive.
|
||||
> See `.github/workflows/release.yml` and `VERSIONING.md` for the deep dive.
|
||||
|
||||
### Flow 3 — Minor or major release
|
||||
|
||||
@@ -212,7 +212,7 @@ resolve anyway. The treadmill is gone.
|
||||
## Cheat sheet: "where does my PR go?"
|
||||
|
||||
```
|
||||
Is it a hotfix release branch? → main (cut by hotfix.yml)
|
||||
Is it a hotfix release branch? → main (cut by release.yml with a patch version X.Y.Z)
|
||||
Is it a stable release branch? → main (cut by release.yml)
|
||||
Is it an RC-blocker fix? → release/X.Y.0 (and also next, or rely on back-merge)
|
||||
Is it everything else? → next
|
||||
@@ -248,10 +248,10 @@ the base branch — no need to recreate the PR.
|
||||
```
|
||||
- **Phasing in:** see the migration notes in
|
||||
`docs/adr/230-introduce-next-integration-branch.md`.
|
||||
- **Updating release.yml / hotfix.yml:** these workflows currently branch
|
||||
from `main` and cherry-pick from `main`. After phase-2 of the migration
|
||||
they should branch from `next` (release) and cherry-pick from `next`
|
||||
(hotfix). The patches are inlined in the ADR.
|
||||
- **Updating release.yml:** this workflow currently branches from `main` and
|
||||
cherry-picks from `main`. After phase-2 of the migration it should branch
|
||||
from `next` (release) and cherry-pick from `next` (hotfix). The patches
|
||||
are inlined in the ADR.
|
||||
|
||||
---
|
||||
|
||||
@@ -262,7 +262,7 @@ A few exceptions where the rules above bend:
|
||||
- **True production-down emergency.** Push directly to a `fix/critical-*`
|
||||
branch, PR to `main`. The auto-back-merge workflow will replay it onto
|
||||
`next` within minutes. Use sparingly — most "urgent" things are fine to go
|
||||
through `next` and ship same day via the hotfix workflow.
|
||||
through `next` and ship same day via the Release workflow (patch version).
|
||||
- **Documentation-only typo on a published page.** If the only change is a
|
||||
doc fix that's visible right now and shouldn't wait for the next release,
|
||||
PR it to `main`. The auto-back-merge will sync `next`. Most doc changes
|
||||
|
||||
@@ -18,7 +18,6 @@ const NPM_SELF_UPGRADE_RE = /\bnpm\s+(install|i)\s+(-g|--global)\b[^\n]*\bnpm(@|
|
||||
|
||||
describe('policy: no runtime npm self-upgrade in release lanes (#318)', () => {
|
||||
const releaseFile = path.join(WORKFLOWS_DIR, 'release.yml');
|
||||
const hotfixFile = path.join(WORKFLOWS_DIR, 'hotfix.yml');
|
||||
|
||||
test('release.yml must not contain a runtime global npm self-upgrade step', () => {
|
||||
const content = fs.readFileSync(releaseFile, 'utf8');
|
||||
@@ -36,21 +35,6 @@ describe('policy: no runtime npm self-upgrade in release lanes (#318)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('hotfix.yml must not contain a runtime global npm self-upgrade step', () => {
|
||||
const content = fs.readFileSync(hotfixFile, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
const violations = lines
|
||||
.map((line, idx) => ({ line, lineNo: idx + 1 }))
|
||||
.filter(({ line }) => NPM_SELF_UPGRADE_RE.test(line));
|
||||
|
||||
assert.strictEqual(
|
||||
violations.length,
|
||||
0,
|
||||
`hotfix.yml contains ${violations.length} runtime npm self-upgrade line(s) — ` +
|
||||
`Violations: ${violations.map(({ lineNo, line }) => `line ${lineNo}: ${line.trim()}`).join('; ')}`
|
||||
);
|
||||
});
|
||||
|
||||
test('NPM_SELF_UPGRADE_RE correctly matches the antipattern', () => {
|
||||
// Positive cases — must match
|
||||
assert.ok(NPM_SELF_UPGRADE_RE.test('npm install -g npm@latest'), 'should match npm install -g npm@latest');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// allow-test-rule: source-text-is-the-product
|
||||
// .github/workflows/{release,hotfix}.yml are the deployed CI contract; asserting
|
||||
// .github/workflows/release.yml is the deployed CI contract; asserting
|
||||
// the release-gate test command is only expressible against the workflow text.
|
||||
|
||||
'use strict';
|
||||
@@ -10,7 +10,6 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const RELEASE_WORKFLOW = path.join(__dirname, '..', '.github', 'workflows', 'release.yml');
|
||||
const HOTFIX_WORKFLOW = path.join(__dirname, '..', '.github', 'workflows', 'hotfix.yml');
|
||||
|
||||
describe('release-coverage-scope', () => {
|
||||
test('release.yml uses test:coverage:unit (not full suite) in both rc and finalize gates', () => {
|
||||
@@ -23,13 +22,5 @@ describe('release-coverage-scope', () => {
|
||||
`release.yml has ${unitCount} 'npm run test:coverage:unit' line(s); expected 2`);
|
||||
});
|
||||
|
||||
test('hotfix.yml uses test:coverage:unit (not full suite) in the finalize gate', () => {
|
||||
const lines = fs.readFileSync(HOTFIX_WORKFLOW, 'utf8').split('\n').map(l => l.trim());
|
||||
const bareCount = lines.filter(l => l === 'npm run test:coverage').length;
|
||||
const unitCount = lines.filter(l => l === 'npm run test:coverage:unit').length;
|
||||
assert.strictEqual(bareCount, 0,
|
||||
`hotfix.yml still has ${bareCount} bare 'npm run test:coverage' line(s); expected 0`);
|
||||
assert.strictEqual(unitCount, 1,
|
||||
`hotfix.yml has ${unitCount} 'npm run test:coverage:unit' line(s); expected 1`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user