Skip to content

Release: post-upload reconcile cuts no tag, so the next run re-plans the same version, tags it at a later commit, and pypi-publish fails #1237

Closed

Description

@thekevinbot

Observed 2026-10-02 when #1228 (12c0cec) merged. Original premise (two concurrent runs planning the same versions) was wrong; the comment below has the logs. Corrected description:

What happened

  1. Run 37033996914 (75179f3, perf(rust): one walk per path table per statement, cheaper rows #1212 merge, 16:26Z) published dirsql 0.4.61 to PyPI at 16:46Z. Its pypi-tag / tag job ran reconcile three seconds later and returned {"ok":true,"actions":[]}: no dirsql-py-v0.4.61 tag was cut, presumably because the registry had not yet reflected the upload.
  2. Run 37042741616 (12c0cec, docs: correct seven stale claims (output format, init table, root, no_ignore, docs host) #1228, 17:44Z) therefore derived 0.4.61 again from the newest tag (0.4.60), got verdict skip from PyPI, but still built the wheels, and release-github created the dirsql-py-v0.4.61 release and tag at 12c0cec. pypi-publish downloads every *-sdist/*-wheel-* artifact regardless of verdict and failed with 400 File already exists.
  3. Net state: the dirsql-py-v0.4.61 tag points at 12c0cec, whose run.rs help text is not in the 0.4.61 wheel on PyPI (uvx dirsql==0.4.61 query --help prints the old wording). Every later dirsql-py bump will diff from a tag that overstates what shipped.

Run 37042756273 (9a5dc6d, #1227) planned only the plugin and is unrelated, except that its reconcile also cut no plugin tag; the newest plugin tag on origin is still v0.1.9 while PyPI has 0.1.21.

Fix options (see the comment for detail): tag from the upload step rather than one post-upload reconcile read; make pypi-publish honor the skip verdict; do not create a GitHub release for a package whose verdict was skip; add a workflow concurrency group as a belt. Needs a decision on which.

Also outstanding: the #1228 help text must still reach PyPI, which happens on the next dirsql-py bump.

Activity

  1. thekevinbot commented on Oct 2, 2026

    @thekevinbot
    CollaboratorAuthor

    Version planning

    release.yml delegates to thekevinscott/putitoutthere/.github/workflows/release.yml@v0; packages come from putitoutthere.toml. The plan job checks out with the full +refs/tags/* refspec and runs putitoutthere@v0 command: plan.

    Two separate inputs:

    • the version comes from git tags ({name}-v{version}): the newest tag for the package sets the base commit, the package's globs are diffed from that commit to HEAD, and a hit bumps the patch.
    • the verdict (publish / skip) comes from the live registry.

    So a missing tag makes the next plan recompute the same version, and the registry check then marks it skip.

    The two runs

    37033996914 (75179f3, plan 16:36:58Z) planned rust 0.4.50 / py 0.4.61 / npm 0.4.63, all publish. pypi-publish uploaded 6 dirsql-0.4.61 files, 16:46:26–16:47:05Z. pypi-tag / tag ran command: reconcile 16:47:08–16:47:17Z and returned {"ok":true,"dryRun":false,"actions":[]} — no dirsql-py-v0.4.61 tag was cut. That run's release-github created only dirsql-npm-v0.4.63 and dirsql-rust-v0.4.50.

    37042741616 (12c0cec, plan 17:44:22Z) therefore still saw dirsql-py-v0.4.60 (469c8f6, 16:36Z) as newest, diffed from there, and computed 0.4.61 again. Verdicts: rust 0.4.51 publish, npm 0.4.64 publish, plugin 0.1.21 publish, dirsql-py 0.4.61 skip. release / publish logged publish: dirsql-py@0.4.61 already published; skipping, then release-github created dirsql-npm-v0.4.64, dirsql-py-v0.4.61, dirsql-rust-v0.4.51 at 17:54:31–36Z. git ls-remote now resolves dirsql-py-v0.4.61^{} to 12c0cec, while the files on PyPI are 75179f3's build. The matrix still built the py wheels despite the skip, pypi-publish downloads *-sdist + *-wheel-* unfiltered, so it re-uploaded them → 400 File already exists; pypi-tag skipped.

    37042756273 (9a5dc6d, plan 17:54:42Z) planned only dirsql-plugin-embeddings 0.1.21 — rust/npm/py had been tagged at 12c0cec three seconds earlier. Correction to the report above: it never planned py/npm/rust. Its upload ran 17:55:30–17:55:55Z and its reconcile also returned actions: []; the newest plugin tag on origin is still dirsql-plugin-embeddings-v0.1.9.

    The plan never chose a stale base because of a concurrent run. It chose the correct base for the tag state it was given; the 16:47Z reconcile is what left no tag.

    Is #1228 on PyPI

    Refuted — the change is absent. uvx --exclude-newer-package "dirsql=2026-10-03T00:00:00Z" dirsql==0.4.61 query --help prints print the result rows as JSON on stdout. On main, 12c0cec rewrote that to print the result rows on stdout (a table on a terminal, a JSON array when piped). 0.4.61 is 75179f3's build and no later dirsql version exists.

    Fix options

    No concurrency: block exists in .github/workflows/release.yml.

    1. concurrency: { group: release, cancel-in-progress: false } at workflow level. Removes the possibility of two plans racing a publish, at the cost of queueing every merge behind the previous ~12-minute release.
    2. Cut the PyPI tag from what the upload job just pushed (or poll the registry until the version is visible) instead of a single reconcile read three seconds after upload. Fixes the gap that actually happened here; needs a change upstream in putitoutthere.
    3. Filter pypi-publish's download to packages the plan marked publish. Stops the 400 and the wasted upload, but leaves the missing-tag drift that caused it.
    4. Skip release-github for a pypi package whose verdict is skip. Stops a tag from pointing at a commit whose code is not in the published artifact; does not by itself get docs: correct seven stale claims (output format, init table, root, no_ignore, docs host) #1228 onto PyPI.
  2. changed the title [-]Release: concurrent runs planned the same version; #1228's dirsql-py publish failed with 'file already exists'[/-] [+]Release: post-upload reconcile cuts no tag, so the next run re-plans the same version, tags it at a later commit, and pypi-publish fails[/+] on Oct 2, 2026
  3. thekevinbot commented on Oct 2, 2026

    @thekevinbot
    CollaboratorAuthor

    Follow-up from the next merge, #1206 (43e5987, run 37044943501, 18:13Z), which shows the same reconcile step misfiring in a second way.

    Net: the registry is the truth and is correct; the tags are derived twice (reconcile after upload, and release-github) and both derivations pick commits by rule rather than from the build. A fix that tags the exact commit the build job used, once, would remove both failure modes.

  4. self-assigned this
    on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions