Skip to content

A changelog-fragment-only merge publishes all three packages without ever running release-ci #944

Closed

Description

@thekevinbot

What

release-ci.yml's PR paths and putitoutthere's publish globs disagree about fragment directories, in the direction that skips verification but keeps publishing.

rust changelog fragment edit
release-ci.yml (PR-time precheck) excluded — !packages/rust/changelog.d/**, so no build precheck runs
putitoutthere.toml (push-time publish) packages/rust/** matches → publishes dirsql-rust, and depends_on cascades to dirsql-py and dirsql-npm

So a PR that adds only a changelog fragment merges having never run the release build matrix, and then republishes all three packages on main.

The globs carry no exclusions at all:

dirsql-rust: ["packages/rust/**", "Cargo.toml", "Cargo.lock"]
dirsql-py:   ["packages/python/**", "packages/rust/**", "Cargo.toml", "Cargo.lock"]
dirsql-npm:  ["packages/ts/**", "packages/rust/**", "Cargo.toml", "Cargo.lock"]

Scope

Pre-existing for python and ts — release-ci.yml has excluded their fragment dirs for some time. #834 extended the same exclusion to rust for consistency, which also extends this mismatch to rust. Not introduced by that epic, but now uniform across all three.

Two things worth separating

  1. The verification gap. A fragment-only change cannot plausibly break a build, so skipping the precheck is low-risk in practice. This half is close to harmless.
  2. The pointless release. This is the real cost. A merge that changes nothing but a markdown file cuts a release of three packages whose artifacts are byte-identical to the previous version. That burns the full cross-compile matrix, consumes version numbers, and puts no-op releases in front of users on crates.io, PyPI and npm.

Note that #939 (excluding fragment dirs from the crate archive) does not fix this. Excluding fragments from the .crate stops them shipping; it does not stop putitoutthere detecting a change under packages/rust/** and republishing.

Options

  • Exclude fragment dirs from the publish globs — mirror release-ci.yml's exclusions in putitoutthere.toml, so a fragment-only change is genuinely a no-op for release. Needs a check that putitoutthere's glob syntax supports negation; verify at the pinned @v0 sha rather than assuming.
  • Leave publishing as-is and re-add fragments to release-ci.yml — restores the precheck but keeps the no-op releases. Fixes the lesser half.
  • Accept and document — if a no-op release per changelog fragment is considered acceptable, say so where the globs are defined so this isn't rediscovered.

First option looks right, contingent on negation being supported.

Notes

Surfaced while auditing which globs fan into which workflow for #834. Not an observed incident — no bad release has been traced to it — so this is a correctness/cost question rather than a live bug.

Activity

  1. thekevinbot commented on Aug 20, 2026

    @thekevinbot
    CollaboratorAuthor

    This is no longer hypothetical, and the scope is wider than fragment dirs.

    Observed. #971 merged as 3ec31c6 at 18:02Z and fired Release run 371. Its only changes under packages/ were two new gate-config files — packages/python/testing-conventions.toml and packages/ts/testing-conventions.toml — plus comment edits in the root config; no SDK source, no fragment. packages/python/** and packages/ts/** matched, so dirsql-py and dirsql-npm published artifacts byte-identical to the previous version. So the "Not an observed incident" note at the bottom can come off.

    A second non-shipping class. The issue is framed around changelog.d/ / migrations.d/, but package-root testing-conventions.toml has the same property and is not covered by any exclusion anywhere:

    path ships in the artifact? release-ci.yml PR precheck publish globs
    packages/<pkg>/changelog.d/** no excluded matches → publishes
    packages/<pkg>/testing-conventions.toml no not excluded matches → publishes

    It differs from the fragment case in one way worth noting for option 2: this one does run the precheck, so only the "pointless release" half applies — but it is strictly worse in frequency terms, because gate config is edited whenever a lane's scope changes, and #933 just made these files a permanent fixture of both SDK packages rather than a one-off.

    Bearing on the options. Option 1 (negation in the publish globs) now needs to cover both classes, not just fragment dirs — !packages/*/testing-conventions.toml alongside !packages/*/changelog.d/**. Still contingent on the same unanswered question: whether putitoutthere's glob syntax supports negation, verified at the pinned @v0 sha. If it does not, the alternative for this class specifically is to move the two configs out of the package subtrees and point each lane's config: input at the new path — the workflows already pass it explicitly, so that is a two-line change, but it gives up the package-local placement that internals/checks and the embeddings plugin established (neither of which is published, which is why the cost never showed up there).

    I introduced this in #971 without checking the release globs against the new file paths, and should have flagged it on the PR.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions