Skip to content

Publish all language packages reliably and surface registry status as badges#26

Merged
konard merged 7 commits intomainfrom
issue-25-76b0e5ea19d6
May 3, 2026
Merged

Publish all language packages reliably and surface registry status as badges#26
konard merged 7 commits intomainfrom
issue-25-76b0e5ea19d6

Conversation

@konard
Copy link
Copy Markdown
Member

@konard konard commented May 3, 2026

Fixes #25.

Summary

The repository advertises four language packages but only the npm one was actually
on its registry. Rust, C# and Python were silently failing to publish because their
release pipelines either swallowed missing-token errors or considered the local git
tag the source of truth for "is this version on the registry". The first publish
of every non-JS package therefore never went out, and the CI badge stayed green.

This PR makes the publish step honest, makes the "should we publish" decision look
at the registry instead of a local tag, and adds visible registry-version badges so
publication failures show up in the README without any custom logic.

The full investigation, evidence, and rationale are in
docs/case-studies/issue-25/README.md.

Changes

  • Workflows (.github/workflows/{python,rust,csharp}.yml)
    • Replace the local-git-tag gate with a live registry probe: PyPI JSON API for
      Python, crates.io API for Rust, NuGet flat-container .nuspec endpoint for
      C#. A 200 means "skip"; a 404 means "publish". Self-healing on retry.
    • Stop swallowing missing-token errors: Rust now hard-fails when
      CARGO_REGISTRY_TOKEN/CARGO_TOKEN is unset; C# now hard-fails when
      NUGET_API_KEY is unset (instead of echo + exit 0).
    • cargo publish is wrapped to treat "already uploaded" / "crate version is
      already uploaded" as success (race-tolerant).
    • pypa/gh-action-pypi-publish now runs with verbose: true and
      skip-existing: true for the same reason.
  • Python release script (python/scripts/create_github_release.py)
    • Added --tag-prefix argument (default v). The Python workflow already
      passed --tag-prefix python-v, but the script ignored unknown args, so the
      GitHub release would have been created with the wrong tag once the publish
      step started succeeding.
  • READMEs
    • Root README.md gets a "Package versions" block with shields.io badges that
      pull from npm / PyPI / crates.io / NuGet, each linking to the registry page.
    • Each language README gets the same badge for its own registry, plus a
      license badge for parity.
  • Case study (docs/case-studies/issue-25/)
    • Timeline (with run IDs), root-cause analysis, registry-state evidence, the
      fix design, and reproducibility script.

Tag/release convention

Language Tag prefix Source
JS v js/scripts/create-github-release.mjs (default)
Python python-v python.ymlcreate_github_release.py --tag-prefix
Rust rust-v rust.ymlcreate-github-release.mjs --tag-prefix
C# csharp-v csharp.ymlcreate-github-release.mjs --tag-prefix

Reproduction / verification

  1. Snapshot of registry presence as of the investigation date (still valid):
    bash docs/case-studies/issue-25/registry-presence.sh
    produces the four registry-* evidence files in the case-study folder.
  2. YAML lint of all four workflows:
    python3 -c "import yaml; [yaml.safe_load(open(f)) for f in
      ['.github/workflows/python.yml',
       '.github/workflows/rust.yml',
       '.github/workflows/csharp.yml',
       '.github/workflows/js.yml']]"
  3. Python release script accepts the new flag:
    python3 python/scripts/create_github_release.py --help

Test plan

  • All four workflow YAMLs parse successfully.
  • python/scripts/create_github_release.py --help lists --tag-prefix.
  • Branch is up to date with main.
  • CI on this PR is green (Python/Rust/C#/JS all passing on 240826f).
  • After merge, a main push should publish the missing 0.2.0 packages
    (or visibly fail if a registry secret is still missing — which is now
    the desired behaviour, not a silent skip).
  • The shields.io badges in the READMEs render correctly on github.com.

Out of scope / follow-ups

  • Configuring repository secrets (NUGET_API_KEY, CARGO_REGISTRY_TOKEN) and
    the PyPI Trusted Publisher binding is repo-admin work and is documented in
    the case study.
  • Filing equivalent issues against rust-ai-driven-development-pipeline-template
    and csharp-ai-driven-development-pipeline-template is documented in the
    case study; this PR does not file them.

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #25
@konard konard self-assigned this May 3, 2026
konard added 5 commits May 3, 2026 11:23
Captures the registry probe (npm 200, PyPI/crates.io/NuGet 404), the four
relevant CI runs as JSON evidence, and a root-cause write-up of the silent
publish-skip pattern that left Rust and C# auto-releases green while
publishing nothing, plus the git-tag gate that prevents retries.
Adds shields.io live registry badges so a reader can immediately see whether
a given package is published and at what version. The root README also
documents that a "not found" badge means the publish never happened.
Auto-release jobs for Python, Rust and C# previously checked the local Git
tag to decide whether to publish. Once the workflow itself created that
tag the gate flipped to "already released" forever, even though the
package never actually reached PyPI / crates.io / NuGet (silent skip when
the publish secret was missing).

* Replace the tag check with an HTTP probe of the registry (200 = already
  published; otherwise publish). The pipeline is now self-healing.
* Promote missing CARGO_REGISTRY_TOKEN / NUGET_API_KEY from a "::warning::
  exit 0" to a "::error:: exit 1" so secrets-not-configured is impossible
  to mistake for success.
* On retry, treat crates.io's "already uploaded/exists" as a non-fatal
  outcome to keep the pipeline idempotent.
* For PyPI publish, enable verbose + skip-existing so first-publish
  diagnostics surface and re-runs are idempotent.

See docs/case-studies/issue-25/README.md for the failure timeline and
root-cause analysis.
The python.yml workflow passes --tag-prefix python-v to this script
in both auto-release (line 255) and manual-release (line 336) jobs,
but the script's argparse did not declare the option, so any release
attempt would fail with "unrecognized arguments". This adds the flag
with default "v" (preserving prior behavior for direct callers) and
propagates it through create_release() to build the tag.

Mirrors the rust/scripts/create-github-release.mjs and
csharp/scripts/create-github-release.mjs interface, which both
expose --tag-prefix with language-specific defaults.

Refs: docs/case-studies/issue-25/README.md
@konard konard changed the title [WIP] All our packages should be published, and if that didn't happen, we need clear visible feedback as badges for each package Publish all language packages reliably and surface registry status as badges May 3, 2026
@konard konard marked this pull request as ready for review May 3, 2026 11:30
The JS workflow's any-code-changed detector flags non-JS workflow file
edits as code changes, which forces a changeset on this PR even though
no JS source moves. Marking as patch because js/README.md was touched
(badges) and that does ship to npm via the README field.
@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

Working session summary

Final state confirmed:

PR: #26

The PR fixes #25 by:

  1. Replacing the silent-skip + git-tag-gate with hard-fail + registry-probe in the Python/Rust/C# release pipelines (self-healing).
  2. Adding shields.io registry-version badges to the root and per-language READMEs (so a 404 is visible).
  3. Fixing python/scripts/create_github_release.py to honor the --tag-prefix python-v already passed by python.yml (otherwise the very first Python release would have crashed on argparse).
  4. Adding a case study at docs/case-studies/issue-25/README.md with timeline, root-cause analysis, evidence files, and reproducibility script.

This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $10.624658

📊 Context and tokens usage:

Claude Opus 4.7: (3 sub-sessions)

  1. 118.8K / 1M (12%) input tokens, 10.1K / 128K (8%) output tokens
  2. 116.8K / 1M (12%) input tokens, 26.4K / 128K (21%) output tokens
  3. 94.0K / 1M (9%) input tokens, 14.1K / 128K (11%) output tokens

Total: (4.0K new + 380.7K cache writes + 13.0M cache reads) input tokens, 69.8K output tokens, $10.624658 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (4732KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard konard merged commit 1acc6f6 into main May 3, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

All our packages should be published, and if that didn't happen, we need clear visible feedback as badges for each package

1 participant