Skip to content

fix(oci): fail a publish at the first tag the registry rejects - #64

Merged
aledbf merged 1 commit into
mainfrom
oci-push-tag-failure
Nov 12, 2026
Merged

aledbf merged 1 commit into
mainfrom
oci-push-tag-failure

Conversation

@aledbf

@aledbf aledbf commented Nov 12, 2026

Copy link
Copy Markdown
Member

Problem. PushArtifact only warned when a tag's manifest PUT failed and went on with the remaining tags. So features publish / templates publish printed the tags that made it, pushed the collection metadata and exited 0. Only "every tag failed" was an error.

Reference (v0.90.0).

  • putManifestWithTags (containerCollectionsOCIPush.ts) stops at the first PUT that isn't 201 and logs Failed to PUT manifest for tag <tag>.
  • doPublishCommand logs (!) ERR: Failed to publish <type>: '<resource>'.
  • featuresCLI/publish.ts / templatesCLI/publish.ts log (!) ERR: Failed to publish '<resource>' and process.exit(1) inside the item loop, so there's no metadata push and no stdout.

Fix. PushArtifact returns an error at the first failed tag; tags pushed before it stay pushed, as in TS. publishOne already logged both (!) ERR lines and exited 1 on an error.

Tests (fail on main)

  • TestPushArtifact_BearerAuthAndStopsAtFirstFailedTag replaces …PartialTagFailure. With 1.0 rejected, the PUTs are exactly [1 1.0], only 1 lands, the error names the tag and the resource, and the log has Failed to PUT manifest for tag 1.0.
  • TestPublishCollection_FailedTagPushExits (feature, template): exit 1, empty stdout, both (!) ERR lines, no metadata PUT. It uses a small in-memory registry, pushRegistry, with the real OCI client.
  • TestPublishParity/tag_failure (feature, template): runs the TS reference and Go against that registry and requires the same stdout, exit code, error lines, tag PUTs and metadata PUTs. Against main, Go printed a result, exited 0, PUT [1 1.0 1.0.0 latest] and pushed metadata.

Still different from TS (unchanged): the line after Failed to PUT manifest is oras's error text rather than the registry's JSON body; the success line reads Published tag: X where TS writes Tagged: X -> <location>.

Validation (local)

  • task lint: pass.
  • task parity:publish: pass (tag_failure, features, templates).
  • task test:race with TMPDIR outside /tmp: pass.
  • task parity:contract: only the 2 known read-configuration.features-configuration-local-feature-* cases fail locally (/tmp/.git, same on main).

🤖 Generated with Claude Code

PushArtifact only warned when a tag's manifest PUT failed and went on
with the remaining tags, so `features publish` and `templates publish`
reported the subset that made it and exited 0, and pushed the
collection metadata as well.

The reference CLI's putManifestWithTags stops at the first rejected
tag, logs "Failed to PUT manifest for tag <tag>", and publish logs the
"(!) ERR: Failed to publish" lines and exits 1 before the collection
metadata and before printing a result. Go now does the same. Tags
pushed before the failure stay pushed, as in TS.

TestPublishParity gains a tag_failure case that runs both CLIs against
a registry that rejects one tag and compares stdout, the exit code, the
error lines and the PUTs each one made.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@aledbf
aledbf merged commit da817e9 into main Nov 12, 2026
13 checks passed
@aledbf
aledbf deleted the oci-push-tag-failure branch November 12, 2026 23:52
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