Repository navigation
fix: stop a no-op merge from republishing every package - #979
Merged
Merged
Conversation
RED half of dirsql#944. `dirsql-checks release-globs` holds `putitoutthere.toml` and `release-ci.yml` to one invariant: everything the publish globs match, the PR-time build precheck also builds, and neither reaches a path that never lands in an artifact. It fails on the current config, which is the bug: six packages/roots glob `<root>/**` with no carve-out, so a changelog-fragment-only merge republishes all three packages, and release run 371 already shipped `dirsql-py` and `dirsql-npm` byte-identical off two new `testing-conventions.toml` files. The seventh finding is the same asymmetry in the other file -- `!plugins/dirsql-plugin-embeddings/tests/**` skips the precheck for a path publishing still cascades on. putitoutthere has no exclusion field and its leading-`!` negations do the opposite of what they look like; the check's module docstring records the verification at the pinned `@v0` sha (8f58767) and names the extglob form that does work. Both halves compare by name rather than re-matching paths, so the check carries no second copy of upstream's glob semantics to drift against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9pxEJux3YgFtKnWKXZAT6
GREEN half of dirsql#944. Each package subtree is now globbed as a pair of minimatch extglob patterns instead of a bare `<root>/**`, so a merge that only touches a changelog fragment, a migration fragment, an e2e attestation receipt or a package's `testing-conventions.toml` cascades nothing. Two patterns per root because one extglob cannot span both depths -- the first covers files directly in the package root, the second everything under its subdirectories. It stays a carve-out rather than an allowlist: a subdirectory or root file added later is still matched, only the named names drop out. Replaying putitoutthere @v0's `matchesAny` over the 559 tracked files, the change removes 59/92/92/15 paths per package and every one of them is a fragment, a receipt or a gate config; nothing new matches, and synthetic future paths (`packages/rust/brand-new-dir/deep/x.rs`, `packages/ts/new-root-file.json`) still cascade. `release-ci.yml` gains the matching `testing-conventions.toml` exclusions so the precheck stops building for a gate-config edit. It also LOSES `!plugins/dirsql-plugin-embeddings/tests/**`: hatchling excludes only `**/*_test.py`, so `tests/conftest.py` and the package inits do land in the plugin's sdist, and skipping the precheck there was the same publish-without- building asymmetry the rest of this change removes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9pxEJux3YgFtKnWKXZAT6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #944.
What
putitoutthere.tomlglobs each package's whole subtree (packages/rust/**),so putitoutthere cascades a release on any change under it — including files
that never reach the built artifact.
release-ci.ymlalready excludes severalof those paths from the PR-time build precheck, so the two files disagree in
the one direction that skips verification but keeps publishing.
packages/*/changelog.d/**,migrations.d/**,e2e-attestations/**packages/*/testing-conventions.tomlplugins/dirsql-plugin-embeddings/tests/**Row 2 is not hypothetical: #971 merged as
3ec31c6and fired Release run371,
publishing
dirsql-pyanddirsql-npmbyte-identical to the release beforethem off two new gate-config files. Row 3 is the same asymmetry read the other
way — the plugin's
tests/conftest.pyandtests/**/__init__.pydo land in itssdist (
[tool.hatch.build] excludedrops only**/*_test.py), so excludingthem from the precheck lets a merge release what the matrix never built.
Verifying putitoutthere's glob syntax at
@v0The issue's preferred option was contingent on negation being supported.
Checked against the pinned
v0tag, sha8f5876751f679ee3450617957c97495205002499:No exclusion field exists.
globsisz.array(z.string()).min(1)andevery package schema is
.strict(), so anexclude-shaped key is a configerror (
packages/engine/src/config.ts).Leading-
!negation is worse than a no-op. Both consumers —cascade.ts'sseed pass and
check.ts— callglob.ts'smatchesAny, a plain OR thatreturns on the first hit, under minimatch 10.2.6 with
{dot, matchBase:false, nocomment}. Reproduced with those exact options:So adding the negation neither subtracts the fragment (the earlier
packages/rust/**already returnedtrue) nor stays inert — it makes everypath in the repo match, and the package would cascade on every commit.
minimatch's extglob does work, because it excludes inside a single
positive pattern. That is what this PR uses.
The fix
Two patterns because one extglob cannot span both depths. It stays a
carve-out, not an allowlist: a subdirectory or root file added later is
matched automatically, and only the named names drop out.
release-ci.ymlgains the matching!<root>/testing-conventions.tomlexclusions and loses
!plugins/dirsql-plugin-embeddings/tests/**, so theprecheck now builds a superset of what publishing ships.
The gate
dirsql-checks release-globs(new subcommand, wired intoputitoutthere-config-check.yml) holds the two files to one invariant:everything the publish globs match, the precheck also builds, and neither
reaches a non-shipping path. It compares by name, not by re-matching paths,
so it carries no second copy of upstream's glob semantics to drift against —
the same reasoning
artifact-completenessuses for not re-deriving artifactnames. It catches a new
[[package]]added with naive globs, a carve-out"simplified" back to
<root>/**, and the leading-!footgun above.Red/green: the gate landed alone in
cc3add5and CI went red on it for exactlythe seven assertions it makes
(job);
bfa0d84is the fix that flips it.Manual run
And the globs themselves, replaying putitoutthere @v0's
matchesAnyverbatim(minimatch 10.2.6, same options) over the repo's 559 tracked files plus
synthetic future paths:
E2E Verification
N/A - CI/release-config only. No
packages/**orplugins/**source changed,so no SDK e2e suite or attestation is affected.
internals/checkschanged, soits own receipt is refreshed here.
packages/python/e2e-attestation.jsonrefreshed ifpackages/pythonchanged — N/A, unchangedpackages/ts/e2e-attestation.jsonrefreshed ifpackages/tschanged — N/A, unchangeduv run python -m pytest tests/e2e -qininternals/checks,attested via
uvx testing-conventions e2e attestinternals/checks/e2e-attestations/claude-tackle-944-iegeyj.jsonChangelog / Migrations
packages/<pkg>/orplugins/<pkg>/changed, sochangelog-gatedoesnot fire. The change alters when packages publish, not what they do.
PARITY.mdis untouched for the same reason — no SDK public API is involved.