Explorar el Código

feat(release): auto-sync package-lock.json on version drift + CLAUDE.md: don't bump version unless asked

Paired with the maintainer-preference clarification that Claude
shouldn't proactively bump versions, and that version bumps are often
made via the GitHub web UI (single-file edit to package.json only).

**Workflow change** (`.github/workflows/release.yml`):

  Adds a 'Sync package-lock.json if version drifted' step BEFORE the
  existing `npm ci` step. It:
    1. Reads the version field from both package.json and package-lock.json.
    2. If they match, no-ops.
    3. Otherwise runs `npm install --package-lock-only --ignore-scripts`
       which rewrites just the lock file's version fields (top-level +
       packages."") without touching node_modules — ~100ms locally.
    4. Auto-commits + pushes the lock-file change back to main with
       `[skip ci]`, same pattern as the prepare-release auto-promote step.

  Effect: a maintainer can now edit ONLY package.json (e.g. via the
  GitHub web UI) and trigger the workflow. The previously-fatal
  `npm ci` mismatch is detected, fixed, and committed before the
  build proceeds. Editing both files locally still works — the sync
  step just no-ops in that case.

  Verified the `npm install --package-lock-only --ignore-scripts`
  mechanic against a synthetic drifted lock file locally: both the
  top-level `version` and `packages."".version` get rewritten to
  match package.json in one command.

**CLAUDE.md change** (§ Release flow):

  Adds an explicit 'Claude does NOT bump the version unless explicitly
  asked' rule. Documents that the maintainer typically bumps
  package.json via the GitHub web UI (single-file edit). Explains the
  new sync step and lists the workflow's 5-step internals (sync lock →
  promote CHANGELOG → bundles → release → npm) for future Claude
  sessions to understand.

940/942 existing tests still pass; no new tests needed (the sync step
is a thin wrapper around an npm CLI invocation; the verification was
the local synthetic-drift smoke test in the commit-message above).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Colby McHenry hace 4 semanas
padre
commit
9111c6d7cc
Se han modificado 2 ficheros con 59 adiciones y 17 borrados
  1. 37 0
      .github/workflows/release.yml
  2. 22 17
      CLAUDE.md

+ 37 - 0
.github/workflows/release.yml

@@ -33,6 +33,43 @@ jobs:
         with:
           node-version: 22
           registry-url: https://registry.npmjs.org
+
+      - name: Sync package-lock.json if version drifted
+        # When the maintainer bumps the version on package.json only — for
+        # example via a GitHub web-UI edit — `npm ci` would refuse to run
+        # with `EUSAGE: npm ci can only install packages when your
+        # package.json and package-lock.json … are in sync`. This step
+        # rewrites just the lock-file's version fields (top-level + the
+        # `packages.""` entry) to match package.json, then auto-commits
+        # and pushes the result so on-disk truth on `main` stays
+        # consistent. Idempotent: if the lock file already matches, no
+        # commit is made.
+        run: |
+          set -euo pipefail
+          PKG_V=$(node -p "require('./package.json').version")
+          LOCK_V=$(node -p "require('./package-lock.json').version")
+          if [ "$PKG_V" = "$LOCK_V" ]; then
+            echo "package-lock.json already at $PKG_V — nothing to sync."
+            exit 0
+          fi
+          echo "Lock-file version drift: lock=$LOCK_V, package=$PKG_V. Syncing."
+          # `--package-lock-only` rewrites only the lock file, doesn't
+          # touch node_modules or actually install anything. Cheap.
+          npm install --package-lock-only --ignore-scripts
+          # Sanity: lockfile should now report the package version.
+          NEW_LOCK_V=$(node -p "require('./package-lock.json').version")
+          if [ "$NEW_LOCK_V" != "$PKG_V" ]; then
+            echo "::error::lock-file still at $NEW_LOCK_V after sync attempt; expected $PKG_V"; exit 1
+          fi
+          if git diff --quiet -- package-lock.json; then
+            echo "lock file unchanged after sync? bailing"; exit 1
+          fi
+          git config user.name  "github-actions[bot]"
+          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+          git add package-lock.json
+          git commit -m "release: sync package-lock.json to ${PKG_V}" -m "[skip ci] Auto-generated by Release workflow."
+          git push origin "HEAD:${GITHUB_REF#refs/heads/}"
+
       - run: npm ci
       - name: Ensure zip/unzip
         run: sudo apt-get update -qq && sudo apt-get install -y -qq zip unzip

+ 22 - 17
CLAUDE.md

@@ -225,23 +225,28 @@ and publishes both the GitHub Release and the npm thin-installer
 Publishing manually is **wrong** now — a plain `npm publish` ships the root
 package (non-bundled), which breaks anyone on Node < 22.5.
 
-The release cut itself is:
-
-```bash
-# CHANGELOG already has entries under [Unreleased] — no manual moves needed.
-# Just bump the version:
-npm version --no-git-tag-version <X.Y.Z>      # or edit package.json manually
-git add package.json package-lock.json
-git commit -m "release: X.Y.Z"
-git push
-```
-
-Then trigger **Actions → Release → Run workflow** (on `main`). The workflow:
-
-1. Runs `prepare-release.mjs <X.Y.Z>` → promotes `[Unreleased]` → `[X.Y.Z] - <today>` in `CHANGELOG.md`, appends the link reference, commits + pushes the move with `[skip ci]`.
-2. Builds every platform bundle on one runner, generates `SHA256SUMS`.
-3. Creates the GitHub Release with notes from the freshly-promoted `[X.Y.Z]` block.
-4. Publishes the npm shim + per-platform packages. Requires the `NPM_TOKEN` repo secret.
+**Claude does NOT bump the version unless explicitly asked.** The maintainer
+typically does it themselves — often by editing `package.json` directly via
+the GitHub web UI. Don't proactively commit a version bump as part of
+unrelated work, and don't propose one when summarizing a PR.
+
+When the maintainer DOES bump the version, the only edit strictly required is
+to `package.json` — the workflow's "Sync package-lock.json" step detects a
+mismatch between `package.json` and `package-lock.json`, runs
+`npm install --package-lock-only --ignore-scripts` to rewrite the lock file's
+version fields (top-level + `packages.""`), and auto-commits + pushes the
+result back to `main` with `[skip ci]`. So a GitHub-web-UI single-file edit to
+`package.json` is enough to kick off a clean release. (If they edit both files
+locally, that's fine too — the sync step no-ops.)
+
+Once `package.json` is at the target version on `main`, trigger
+**Actions → Release → Run workflow** (on `main`). The workflow:
+
+1. Syncs `package-lock.json` to `package.json`'s version if they've drifted; commits + pushes that change.
+2. Runs `prepare-release.mjs <X.Y.Z>` → promotes `[Unreleased]` → `[X.Y.Z] - <today>` in `CHANGELOG.md`, appends the link reference, commits + pushes the move with `[skip ci]`.
+3. Builds every platform bundle on one runner, generates `SHA256SUMS`.
+4. Creates the GitHub Release with notes from the freshly-promoted `[X.Y.Z]` block.
+5. Publishes the npm shim + per-platform packages. Requires the `NPM_TOKEN` repo secret.
 
 **Do not run `npm publish`, `git push`, or `git tag` yourself** — these are
 publish actions on shared state. Write the files, hand the user the commands.