Skip to content

ci(release): reconcile lockfile before gated changeset commands#1104

Merged
ascorbic merged 5 commits into
mainfrom
fix/release-verify-deps-before-run
May 19, 2026
Merged

ci(release): reconcile lockfile before gated changeset commands#1104
ascorbic merged 5 commits into
mainfrom
fix/release-verify-deps-before-run

Conversation

@ascorbic
Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic commented May 19, 2026

What does this PR do?

Fixes the Release workflow silently failing to publish.

pnpm-workspace.yaml sets verifyDepsBeforeRun: error, which gates every pnpm run / pnpm <bin> call. changesets/action runs git checkout changeset-release/main + git reset --hard <main> and then invokes its version/publish command. After that git reset, the lockfile state can be inconsistent enough that the gated pnpm changeset version / pnpm changeset publish call aborts with ERR_PNPM_VERIFY_DEPS_BEFORE_RUN: Setting overrides of lockfile ... is outdated — before anything is versioned or published.

This is what broke the last release: the post-merge runs for #1100/#1101/#1102 all failed at the changesets step, the release PR merged without those changesets folded in, 0.13.0 was bumped on main but never published, and the bot opened a fresh 0.13.1 release PR (#1103) instead.

A reconcile cannot be a prior workflow step: the action's git reset --hard runs after all workflow steps and discards a working-tree lockfile fix. It has to happen after the action's git work and immediately before the gated pnpm call.

This PR routes the action's version/publish (and the manual-publish step) through node .github/scripts/release.mjs — a single non-gated entrypoint that runs pnpm install --no-frozen-lockfile (not gated by verifyDepsBeforeRun) and then pnpm changeset version|publish (gated, now passing). The now-unreferenced changeset:version package script is removed so there's a single release path. verifyDepsBeforeRun: error is unchanged for local dev and normal CI.

Workflow/tooling only; no application code touched.

Closes #

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes (N/A — CI/tooling only, no TS changed)
  • pnpm lint passes (N/A — CI/tooling only)
  • pnpm test passes (N/A — CI/tooling only)
  • pnpm format has been run (release.mjs uses tab indentation per repo style)
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (N/A)
  • I have added a changeset (N/A — CI-only change, no published package affected; root package.json is private)
  • New features link to an approved Discussion (N/A — not a feature)

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.7

Screenshots / test output

Failing release run that motivated this — the Create Release Pull Request or Publish step:

[command]/usr/bin/git checkout changeset-release/main
[command]/usr/bin/git reset --hard d16a3d527f...
[command]/.../pnpm run changeset:version
 ERR_PNPM_VERIFY_DEPS_BEFORE_RUN  Setting overrides of lockfile in /home/runner/work/emdash/emdash is outdated
##[error]The process '.../pnpm' failed with exit code 1

verifyDepsBeforeRun: error gates pnpm run/exec. The frozen install in the release job does not refresh the lockfile's overrides hash, so a lockfile whose overrides drifted from pnpm-workspace.yaml passes the frozen install but trips the gate on `pnpm run changeset:version` / `pnpm changeset publish`, killing every release before it versions or publishes. A non-frozen install is not gated and reconciles the hash; changesets/action commits the result, self-healing the repo.
Copilot AI review requested due to automatic review settings May 19, 2026 17:42
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

⚠️ No Changeset found

Latest commit: 8b49a64

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-perf-coordinator 8b49a64 May 19 2026, 06:49 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-i18n 8b49a64 May 19 2026, 06:49 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 8b49a64 May 19 2026, 06:51 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 8b49a64 May 19 2026, 06:50 PM

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 19, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1104

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1104

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1104

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1104

emdash

npm i https://pkg.pr.new/emdash@1104

create-emdash

npm i https://pkg.pr.new/create-emdash@1104

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1104

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1104

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1104

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1104

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1104

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1104

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1104

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1104

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1104

commit: 8b49a64

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 8b49a64 May 19 2026, 06:53 PM

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the GitHub Actions release workflow to prevent verifyDepsBeforeRun: error from aborting the release job before versioning/publishing when the lockfile’s recorded overrides metadata has drifted from pnpm-workspace.yaml.

Changes:

  • Adds a “Reconcile lockfile” step that runs a non-frozen pnpm install before the Changesets step to refresh lockfile metadata so gated pnpm commands can run.
  • Keeps verifyDepsBeforeRun: error unchanged for local dev and normal CI; only the release workflow is adjusted.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/release.yml Outdated
Comment on lines +69 to +73
# Not redundant with the frozen install: frozen install does not
# refresh the lockfile's overrides hash, so a drifted lockfile trips
# verifyDepsBeforeRun on the gated changeset commands below.
- name: Reconcile lockfile
run: pnpm install --no-frozen-lockfile
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — and digging into it showed the problem is deeper than placement. changesets/action runs git checkout changeset-release/main + git reset --hard <main> immediately before it invokes the version/publish command, inside the action. So a reconcile placed in any earlier workflow step (whether before or after pnpm build) gets its working-tree lockfile discarded by that git reset --hard, and the gated pnpm changeset call still trips verifyDepsBeforeRun. Moving the step earlier wouldn't have fixed it.

I confirmed the committed lockfile overrides actually matched pnpm-workspace.yaml at the failing SHA, yet the version command still failed — the only place a reconcile provably holds is right before the gated pnpm call, after all of the action's git work.

Revised: the action's version/publish now invoke node .github/scripts/release.mjs, a non-gated entrypoint that runs pnpm install --no-frozen-lockfile (not gated) and then pnpm changeset version|publish (gated, now passes). The earlier workflow step is removed.

On --lockfile-only: I kept a full non-frozen install rather than --lockfile-only. verifyDepsBeforeRun also checks node_modules consistency, not just the lockfile's overrides field, and changeset version/publish run afterward against node_modules; a lockfile-only refresh would leave node_modules unreconciled. The release job isn't latency-sensitive and already did a non-frozen install in the old changeset:version script, so this isn't a regression.

…a prior step

changesets/action does git checkout changeset-release/main + git reset --hard right before running the version/publish command, so any reconcile placed in an earlier workflow step is discarded by that reset and the gated 'pnpm changeset' call still trips verifyDepsBeforeRun. Move the non-frozen install into a single non-gated node entrypoint the action invokes, so it runs after the action's git work and immediately before the gated pnpm call. Drop the now-unreferenced changeset:version script.
@github-actions github-actions Bot added size/S and removed size/XS labels May 19, 2026
changeset publish rebuilds packages via prepublishOnly at pack time. A non-frozen reconcile could re-resolve in-range transitive deps so shipped bits diverge from tested bits. prefer-frozen reconciles the deps state to satisfy verifyDepsBeforeRun without re-resolving when the lockfile is satisfiable, and falls back to a full install (e.g. after changeset version bumps workspace versions) when it isn't.
@ascorbic ascorbic requested a review from Copilot May 19, 2026 18:22
@ascorbic
Copy link
Copy Markdown
Collaborator Author

/review

Comment thread .github/scripts/release.mjs Outdated
// prefer-frozen avoids re-resolving deps when the lockfile is already
// satisfiable (so published packages, which rebuild via prepublishOnly,
// match what was tested) but falls back to a full install when it isn't.
run(["install", "--prefer-frozen-lockfile"]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (low): The PR description and the response to copilot both state this script runs pnpm install --no-frozen-lockfile, but the final committed code (last commit ee2a06e) uses --prefer-frozen-lockfile. The flag change is intentional and arguably better — the existing comment at lines 14–16 already explains why prefer-frozen is preferred — but the PR body is now stale. Worth updating the PR description before merge so the release-notes / git-blame trail for this fix matches what actually shipped; otherwise the next person who hits a related release failure will read the PR body, see --no-frozen-lockfile, and be confused when git show disagrees.

Also worth a sanity check: the failure mode the PR is fixing is ERR_PNPM_VERIFY_DEPS_BEFORE_RUN: Setting overrides of lockfile ... is outdated. --prefer-frozen-lockfile short-circuits when the lockfile is satisfiable — which is exactly the state where the outdated-overrides-hash error can still fire (lockfile content fine, recorded metadata stale). If pnpm's frozen path skips rewriting the lockfile's overrides hash, the gated pnpm changeset version immediately after could still hit the same error. The non-frozen install in the previous commit was guaranteed to refresh that hash; prefer-frozen is not, depending on pnpm internals. If this hasn't been verified against an actual reproduction of the original failure (or a forced stale-hash repro), --no-frozen-lockfile is the safer choice for the reconcile call specifically — the second install after changeset version can stay prefer-frozen since versions definitely changed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good objection — and it outranks the finding that motivated the prefer-frozen switch, so I've reverted both installs to --no-frozen-lockfile (commit 8b49a64).

Reasoning: the entire purpose of this PR is to reliably clear ERR_PNPM_VERIFY_DEPS_BEFORE_RUN. --no-frozen-lockfile is pnpm's documented remediation and unconditionally refreshes the deps-state/overrides hash. --prefer-frozen-lockfile's fast path is gated by a satisfiability check whose inclusion of the settings hash I was reasoning about, not verifying — your third case (lockfile content satisfiable, recorded metadata stale → fast path skips the rewrite → gate still fires) is exactly the failure class this fixes. Wrong call = releases stay broken, so I'm not trading a guaranteed fix for an unverified internal assumption.

The shipped-vs-tested concern that prefer-frozen was meant to mitigate was rated low-probability / pre-existing / "not a blocker, worth a conversation" in the adversarial pass, and in practice is negligible: when the committed lockfile is satisfiable, --no-frozen-lockfile does not re-resolve — it only reconciles the genuinely-drifted bits, which is precisely what we want before the prepublishOnly rebuild. Captured as a known follow-up rather than a reason to weaken the fix.

PR body already says --no-frozen-lockfile, so it now matches the code again. The // Must be --no-frozen-lockfile, not --prefer-frozen-lockfile comment in release.mjs records this so it isn't "optimized" back.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 19, 2026

Review posted successfully. Summary of the review:

  • Overall: This is a sound fix. The diagnosis (changesets/action's git reset --hard runs after all workflow steps and clobbers any earlier reconcile, so the reconcile must happen inside the action's gated invocation) is correct. Routing through a non-gated Node entrypoint that does pnpm install before the gated pnpm changeset call is the right shape.

  • One low-severity comment posted on .github/scripts/release.mjs:17:

    1. The PR description (and the author's reply to copilot) say --no-frozen-lockfile, but the final committed code uses --prefer-frozen-lockfile. The body is stale and should be updated for git-blame/release-notes hygiene.
    2. A correctness concern with --prefer-frozen-lockfile for the first (reconcile) install: the original failure is the stale-overrides-hash error, which fires precisely when the lockfile is satisfiable. Prefer-frozen short-circuits in that state and may not rewrite the metadata that's failing the gate. The previous non-frozen install was guaranteed to refresh it. The second install (after changeset version) is fine as prefer-frozen because versions definitely changed.

github run

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread .github/scripts/release.mjs Outdated
Comment on lines +14 to +17
// prefer-frozen avoids re-resolving deps when the lockfile is already
// satisfiable (so published packages, which rebuild via prepublishOnly,
// match what was tested) but falls back to a full install when it isn't.
run(["install", "--prefer-frozen-lockfile"]);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted to --no-frozen-lockfile in 8b49a64 — code and PR description now agree. --no-frozen-lockfile unconditionally refreshes the deps-state hash that triggers ERR_PNPM_VERIFY_DEPS_BEFORE_RUN, which is the guarantee this PR needs; see the detailed reply on the ask-bonk thread.

The PR's purpose is to reliably clear ERR_PNPM_VERIFY_DEPS_BEFORE_RUN. --no-frozen-lockfile is pnpm's documented remediation and unconditionally refreshes the deps-state hash. --prefer-frozen-lockfile's fast path is gated by a satisfiability check that may not include the settings hash, so it could skip the rewrite in exactly the stale-metadata case this fixes. The shipped-vs-tested concern that motivated prefer-frozen is low-probability, pre-existing, and negligible when the lockfile is satisfiable (no re-resolution occurs).
@ascorbic ascorbic merged commit 74a9078 into main May 19, 2026
43 checks passed
@ascorbic ascorbic deleted the fix/release-verify-deps-before-run branch May 19, 2026 19:00
ascorbic added a commit that referenced this pull request May 19, 2026
main is at 0.13.0 (bumped by the #1096 release-PR merge) but 0.13.0 was never published: the verify-deps release bug (fixed in #1104) made the post-merge run open a 0.13.1 PR instead of publishing. These two changesets remained on main, keeping changesets/action on the version path. Removing them makes the next Release run take the publish path and ship the already-versioned 0.13.0 with its full changelog, tags, and GitHub releases. They are restored in the follow-up 0.13.1 PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants