Repository navigation
FE-1917: Upgrade Changesets to v3 to enable publishConfig - #9973
claude[bot] wants to merge 13 commits into
Conversation
Changesets v3 publishes Yarn workspaces with `yarn npm publish`, which applies `publishConfig` overrides and resolves `workspace:` ranges. - Bump `@changesets/cli` to 3.0.3 and port the publish-concurrency and `changeset status` hunks of its patch to the v3 files. - Drop the `@changesets/assemble-release-plan` patch: v3 bumps peer dependents by patch instead of major. - Bump `changesets/action` to v2.1.2, which v3 requires, and move to its renamed inputs. - Rename `prepublishOnly` to `prepublish`, the publish hook Yarn runs. - Let Yarn perform the trusted-publishing exchange, with provenance and the npm registry set explicitly, instead of installing npm 11.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9973 +/- ##
==========================================
+ Coverage 66.53% 66.58% +0.05%
==========================================
Files 1954 1965 +11
Lines 215400 216192 +792
Branches 8429 8469 +40
==========================================
+ Hits 143313 143951 +638
- Misses 70484 70625 +141
- Partials 1603 1616 +13
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Nothing in the repository calls `changeset status`, so its exit code no longer matters. The publish-concurrency hunk stays.
Merging this PR will not alter performance
|
`yarn npm publish` never runs `postpublish`, so the Block Protocol packages' `prepublish` edits to `package.json` and generated sources stayed in the working tree. Their entry fields already point at `dist`, so nothing needs rewriting: inline `Real` into the copied declarations during the type-system build, and stop stripping `devDependencies`, which Yarn now publishes with resolved ranges like the other packages. The shared rewrite and restore scripts in `@local/repo-chores` only served these packages and four private ones that never publish, so remove them with their helpers and the hooks that called them.
Yarn resolves `workspace:` ranges when it packs a package.
Yarn packs a `publishConfig` override in place of the top-level field.
It repeats the pending `stable-composer-controls` changeset, which writes the entry on the next release.
Publishing no longer runs npm, so it writes no npm debug logs.
Yarn reads its `.npmignore` allow-list differently from npm and packed the package's config files alongside `dist`.
|
I'm not super familiar with the publishing steps of all the projects this touches, so @CiaranMn should probably take a look at this - but one thing that's jumped out is that prepublish is deprecated, so not sure why this PR is switching everything from prepublishOnly back to prepublish |
|
@alex-e-leon
That page also says not to build in Generated by Claude Code |
Yes do that. And thanks for the flag on the prepublish, I thought only prepublishOnly was dead. |
Yarn recommends `prepack` for building a package and `prepublish` only for side-effect-free checks, so run each build ahead of the payload check there. `yarn pack` and `changeset pack` now build as well.
|
@indietyp Done in 0efa9be. Each package now builds in Generated by Claude Code |
Yes, although as claude flagged, it looks like this is not the case for yarn (whose lifecycle steps I'm less familiar with), and it looks like the changesets upgrade has switched the publish steps from npm back to yarn, so it could be fine to use prepublish now. If there's anyone more familiar with our publish workflows across all the packages would be good to get their eyes on this, and might also be a good idea to get claude to mock a publish to double check that there aren't any issues, as a small misconfiguration might cause issues here, and we've had problems with publishing in the past (ds-components wasn't publishing a valid package for over 6 months) |
Yarn matches an unanchored `files` entry at any depth, so `README.md` also packed every nested `README.md` under `src`, `docs` and `benchmarks`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4xQyzV5Mb61k3XbH9J8ZA
|
@alex-e-leon I ran a mock release locally. I bumped all six public packages with a throwaway changeset and ran For each tarball: every All 34 export subpaths type-check under The one thing only GitHub Actions can prove is the OIDC trusted-publishing exchange and provenance through Yarn. Generated by Claude Code |
Each published package's `prepack` is now `turbo run build:pack`, a task that depends on the package's build (or, for `@hashintel/ds-helpers`, on its codegen) and runs the payload check as its own script, uncached so it reads the files on disk at pack time. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4xQyzV5Mb61k3XbH9J8ZA
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1002 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1527 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 108 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 27 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | Flame Graph |
representative_read_entity
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |
Requested via Slack thread
🌟 Purpose
Changesets v2 publishes a Yarn workspace with
npm publish. npm ignores Yarn'spublishConfigfield overrides (main,types,exports,bin) and cannot readworkspace:ranges, so our packages cannot usepublishConfig. Changesets v3 publishes withyarn npm publishwhen the repository uses Yarn, and Yarn applies both at pack time.This PR moves to
@changesets/cli3.0.3, the currentlatestrelease (v3 has been stable since 3.0.0 on 2026-08-11;2.31.1is now onmaintenance-v2), and makes the release workflows work with Yarn as the publisher.🔍 Changes
Patches
@changesets/assemble-release-planpatch: removed. It stopped a minor bump from bumping peer dependents to a major. v3 depends onassemble-release-plan7.0.0, which no longer hasshouldBumpMajorand gives peer dependents a patch bump. Runningchangeset status --outputagainst the 163 changesets pending onmain: unpatched v2 plans@hashintel/petrinaut0.0.19 → 1.0.0 (it peer-depends on@hashintel/ds-components, which gets a minor), while patched v2 and unpatched v3 both plan 0.0.20. The whole release plan is identical between patched v2 and v3.@changesets/clipatch: regenerated for 3.0.3 as.yarn/patches/@changesets-cli-npm-3.0.3-37da92ed6c.patch(withyarn patch/yarn patch-commit), keeping one of its two hunks.createPromiseQueue(10)for publishing (dist/getPublishPlan.mjs) and has no option for it, and packages still build in theirprepackhook, so parallel publishes could again race on shared build output, the failure H-6772: Serialise npm publishing and verify package payloads at pack time #9244 fixed by serialising.changeset statusexiting 0 when packages changed without a changeset: removed. Nothing in the repository or.githubrunschangeset status, so v3's exit code 1 there affects nothing.Publishing through Yarn
changesets/actionmoves from v1.8.0 to v2.1.2 (pinned SHA). v2 is the release for CLI v3: v1.8.0 finds published packages by matchingNew tag:in the CLI's output, which v3 no longer prints, so it would create no GitHub releases. v2 reads theCHANGESETS_OUTPUTfile instead. Inputs are renamed (publish-script,version-script), the token moves togithub-token, andcommitMode: github-apiis dropped because API pushes are now the default and v2 refuses the old input.GITHUB_TOKENis no longer set on the step: v2 refuses aGITHUB_TOKENthat differs fromgithub-token, and sets it itself for the version and publish scripts, which the changelog formatter reads. The step that recreates the Version Packages PR stays: v2 still resetschangeset-release/maintomainand force-writes it through the API.prepublishOnly, whichyarn npm publishnever runs (norpostpublish), into abuild:packturbo task. Every published package'sprepackisturbo run build:pack; the task depends on the package'sbuild(oncodegenfor@hashintel/ds-helpers), runs the payload check as its own script, and is uncached so the check reads what is on disk at pack time. Yarn's lifecycle scripts page recommendsprepackfor building andprepublishonly for side-effect-free checks. As a result,yarn packandchangeset packbuild too.@hashintel/refractiveusedyarn builddirectly; it now builds through turbo like the rest, with byte-identical output.prepublishOnlyran ascripts/prepublish.tsthat deleteddevDependenciesfrompackage.json(and, for@blockprotocol/type-system, replaced the@local/hash-codecRealimport insrc/generatedwithtype Real = numberand rebuilt), and theirpostpublishrestored the package directory withgit restoreandgit clean -fdx. Yarn never runspostpublish, so those edits would have stayed behind. Theirmain,typesandexportsalready point atdist, so there was nothing forpublishConfigto override:@blockprotocol/type-systeminlinesRealwhile its Rollup build copies the generated declarations intodist, leavingsrcuntouched.devDependenciesare no longer stripped. Yarn publishes them with resolved ranges, as the other five packages already do; consumers never install them.@blockprotocol/graphlistsdistinfilesinstead of its.npmignoreallow-list, which Yarn reads differently and which packedtsconfig*.json,turbo.jsonandeslint.config.js.prepublish.tsscripts go, as do@local/repo-chores's sharedprepublish.ts/postpublish.tsand their helpers (errors,git,package-infos,update-json, thefix-esm-import-pathdependency). Besides the Block Protocol packages, only four private, changeset-ignored packages (block-design-system,design-system,query-editor,type-editor) hooked them, and private packages never publish, so their hooks are removed too.scripts/resolve-workspace-ranges.mjsand thechangeset:resolve-workspace-rangesscript are removed: Yarn resolvesworkspace:ranges when packing.changeset:publishis now justchangeset publish.scripts/check-package-payload.mjschecks apublishConfigoverride in place of the top-level field, since that is what Yarn packs.## Unreleasedsection in@hashintel/petrinaut's CHANGELOG (added in H-6763: Add a generic Petrinaut composer submission API #9355) is removed: it repeats the pendingstable-composer-controlschangeset, which writes the 0.0.20 entry.NPM_CONFIG_LOGLEVELare removed, and the release job's npm debug-log dump is replaced by Yarn's logs. Yarn writes each lifecycle script's output (prepack,postpack,prepublish) to<tmpdir>/xfs-*/<script>.logand keeps the file only when the script fails; changesets v3 prints the error, which names that path, under "Some packages failed to publish". On failure the job now prints/tmp/xfs-*/*.logand adds them to therelease-debug-logsartifact. Checked locally by publishing a fixture whoseprepublishwrites to stdout and stderr and exits 3 (aprepackfailure takes the same path): the CLI printedYN0036: Prepublish script failed (exit code 3, logs can be found here: …/xfs-a422bd7e/prepublish.log)and the file held both lines. Yarn 4.18.1 does the OIDC token exchange itself duringyarn npm publishin GitHub Actions when nonpmAuthTokenis set (none is). Two Yarn settings are set on the publish steps to keep current behaviour:YARN_NPM_PUBLISH_PROVENANCE(npm generated provenance automatically, Yarn does not; current releases carry provenance) andYARN_NPM_PUBLISH_REGISTRY(Yarn otherwise publishes toregistry.yarnpkg.com)..changeset/config.jsonpoints$schemaat@changesets/config@4.0.1. The existing options, including___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, are all in the v3 schema.Checked locally
publishConfigend to end: a two-package Yarn 4.18.1 fixture published by the patched 3.0.3 CLI to a local Verdaccio registry. Package A hasmain/types/exportspointing atsrcandpublishConfigoverrides pointing atdist; package B depends on A withworkspace:^. Afterchangeset versionandchangeset publish(exit 0, two git tags, twoCHANGESETS_OUTPUTevents), the registry manifest for A has"main": "./dist/index.js","types": "./dist/index.d.ts"and thedistexportsmap, the tarball'spackage.jsonmatches, and B's published dependency is"@fixture/a": "^0.1.0". Marker files showedprepublishran andprepublishOnly/postpublishdid not. For comparison, v2.30.0'sgetPublishToolreturnsnpmfor anything but pnpm.changeset versionon this branch with the GitHub GraphQL call stubbed: v3 loads@local/repo-chores/changesets-changelog, writes the CHANGELOGs, detectsoxfmtand formats them;oxfmt --checkon the results passes.changeset publish-planagainst npm:No projects to publish or tag., soyarn npm inforesolves all seven packages.yarn packwithprepackbuilding: in@hashintel/refractive(from an emptydist) theprepackrunsyarn build, then the payload check, and the tarball matches 0.0.4 file for file. In@blockprotocol/graphand@blockprotocol/type-system,prepackranturbo run buildand then the payload check; turbo was restricted to the package's own build with--only(the type-system crate's wasm and types were built beforehand, asmiseis not available here). Both tarballs match the ones verified before. Earlier,yarn npm publish --dry-runin refractive targetedhttps://registry.npmjs.orgwith the provenance setting picked up.yarn packafter building, compared withnpm pack <pkg>@<version>):@blockprotocol/type-systemand@blockprotocol/graph(built locally, including the type-system wasm) have identicalmain,types,exports,typesVersions,type,sideEffects,files,dependenciesandpeerDependencies, with@blockprotocol/type-system: workspace:^published as^0.2.2. The type-system tarball'sdist/*/generated/types.d.tshastype Real = number;and nothing indistmentionshash-codec; it also now shipsdist/*/generated/*.d.ts, which the 0.2.2 tarball lacked. Graph's file list matches 0.5.0 plusCHANGELOG.md, which Yarn always includes.@hashintel/refractivematches 0.0.4 file for file.@hashintel/petrinautand@hashintel/ds-components(manifest only, stub entry files) publish their@hashintel/*ranges exactly as 0.0.19 and 0.3.1 did. The working tree was clean after every pack.changeset versionwith GraphQL stubbed, after the CHANGELOG fix:## 0.0.20sits directly under the Petrinaut title and the Voice mode text appears once.yarn install --immutable(4.18.1),yarn constraints,yarn dedupe --strategy highest --check,yarn lint:format,yarn lint:license-in-workspaces, andlint:eslint/lint:tscfor@blockprotocol/graph,@blockprotocol/type-systemand@local/repo-chores.🤖 Generated with Claude Code
https://claude.ai/code/session_01L4xQyzV5Mb61k3XbH9J8ZA