Skip to content

fix(publish): pass the tarball as a path, and make the label one-shot - #203

Merged
derrickmehaffy merged 1 commit into
mainfrom
fix/publish-tarball-path
Aug 14, 2026
Merged

derrickmehaffy merged 1 commit into
mainfrom
fix/publish-tarball-path

Conversation

@derrickmehaffy

Copy link
Copy Markdown
Contributor

The experimental publish failed — and not on auth

Run 31760869448. Build and pack succeeded; Publish to npm died:

npm error code 128
npm error An unknown git error occurred
npm error command git --no-replace-objects ls-remote \
  ssh://git@github.com/artifacts/strapi-community-plugin-rest-cache-0.0.0-experimental.ff1018e….tgz.git
npm error git@github.com: Permission denied (publickey).

That looks like an OIDC/trusted-publishing failure. It isn't — npm never
contacted the registry. It parses a bare relative path containing a slash as
a GitHub shorthand
(owner/repo), so artifacts/x.tgz sent it looking for a
git remote instead of reading the tarball.

This would have broken the 5.1.0 release in exactly the same way. Finding
it is precisely what the experimental dry run was for.

Reproduced and fixed locally, with --dry-run

$ npm publish --dry-run artifacts/…-memory-5.0.0.tgz
npm error command git ls-remote ssh://git@github.com/artifacts/…tgz.git   ← identical

$ npm publish --dry-run ./artifacts/…-memory-5.0.0.tgz
npm notice name: @strapi-community/provider-rest-cache-memory
npm notice filename: strapi-community-provider-rest-cache-memory-5.0.0.tgz
npm error You cannot publish over the previously published versions: 5.0.0.   ← reached the registry

The leading ./ is what makes it unambiguously a path.

Second change: the label is now one-shot

Per @derrickmehaffy — the run now removes publish-experimental when it
finishes
, so the label means "publish a build now" rather than "this PR is in
experimental mode". To cut another build after pushing, add it back.

synchronize is dropped from the trigger. It was the mechanism that made every
subsequent push republish silently for as long as the label happened to still
be attached; with the label removed after each run it would only ever fire in
the window before cleanup.

Three details worth reviewing:

  • unlabel is a separate job. It needs pull-requests: write, and the
    build job — the one that may execute unreviewed branch code — must keep
    contents: read and nothing else. unlabel checks out nothing and runs only
    this file, from the default branch.
  • always(), so a failed publish still clears the label. Otherwise the
    next person to add it sees nothing happen, because it is already there.
  • A 404 from removeLabel is swallowed — someone removing it by hand produces
    the desired end state, not a failure.

The PR comment and .claude/skills/cut-a-release/SKILL.md both promised
republish-on-push, so both are updated.

Note

Touches .github/workflows/publish.yml, so it needs a maintainer to merge.

And because pull_request_target runs the workflow from the default
branch
, this fix has no effect until it is on main — re-labelling #201
before then will fail the same way.

🤖 Generated with Claude Code

The first experimental publish failed, and not on authentication:

  npm error command git --no-replace-objects ls-remote \
    ssh://git@github.com/artifacts/strapi-community-plugin-rest-cache-0.0.0-experimental.ff1018e.tgz.git
  npm error git@github.com: Permission denied (publickey).

npm parses a bare relative path containing a slash as a GitHub shorthand -
owner/repo - so `artifacts/x.tgz` sent it looking for a git remote rather than
reading the tarball. It reads like an OIDC failure and is nothing of the sort;
trusted publishing was never reached.

Reproduced and fixed locally with --dry-run:

  npm publish artifacts/x.tgz    -> the same git ls-remote error
  npm publish ./artifacts/x.tgz  -> reads the tarball, resolves the name, and
                                    is rejected only by "cannot publish over
                                    the previously published versions: 5.0.0"

This would have broken the 5.1.0 release identically, which is what the
experimental dry run existed to find out.

Also: remove the trigger label once the run finishes. The label is a request to
publish one build, not a mode the PR sits in - it used to also fire on
`synchronize`, so every later push republished silently for as long as the
label was still attached. `synchronize` is dropped and a separate `unlabel` job
takes the label back off, `always()` so a failed publish cannot leave it behind
and make the next attempt look like a no-op.

That job is separate because it needs `pull-requests: write`, and the build job
- the one that may run unreviewed code - must keep `contents: read` and nothing
else.

Co-Authored-By: Claude <noreply@anthropic.com>
@derrickmehaffy
derrickmehaffy merged commit 466acb1 into main Aug 14, 2026
12 checks passed
This was referenced Aug 14, 2026
@derrickmehaffy
derrickmehaffy deleted the fix/publish-tarball-path branch August 14, 2026 05:45
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.

1 participant