Skip to content

fix(oci): parse feature refs with both a tag and a digest (name:tag@digest)#131

Merged
pofallon merged 1 commit into
mainfrom
fix/feature-ref-tag-and-digest
May 28, 2026
Merged

fix(oci): parse feature refs with both a tag and a digest (name:tag@digest)#131
pofallon merged 1 commit into
mainfrom
fix/feature-ref-tag-and-digest

Conversation

@pofallon
Copy link
Copy Markdown
Contributor

Summary

parse_name_and_tag handled name:tag and name@digest separately but not the combined name:tag@digest form. For a feature ref like ghcr.io/devcontainers/features/git:1@sha256:DIGEST, it split on @, kept the whole git:1 as the name, and produced repository devcontainers/features/git:1 — so the manifest URL became:

https://ghcr.io/v2/devcontainers/features/git:1/manifests/sha256:DIGEST   → HTTP 404

Per the OCI distribution spec, when both a tag and a digest are present the digest is authoritative and the tag is informational. The fix strips the tag from the name portion when a digest follows, so the repository path is devcontainers/features/git and the manifest is fetched by digest:

https://ghcr.io/v2/devcontainers/features/git/manifests/sha256:DIGEST    → 200

How it was found

The examples/features/oci-digest-pin canary (scenario 2: read-configuration --include-features-configuration over a digest-pinned feature) failed to resolve the manifest. After the fix, git:1@sha256:... resolves and installs correctly (git version 2.54.0).

Changes

  • crates/core/src/registry_parser.rs: strip the tag from the name when a digest is present; new unit tests (test_parse_name_and_tag_digest_forms, test_parse_registry_reference_tag_and_digest).
  • examples/features/oci-digest-pin/exec.sh: scenario 2 now passes --config explicitly (the config lives at the workspace root as devcontainer.json, not a spec discovery location), matching scenario 3.

Testing

  • New unit tests pass; cargo fmt/clippy clean.
  • Full oci-digest-pin canary passes end-to-end against ghcr.io.

🤖 Generated with Claude Code

…igest)

`parse_name_and_tag` handled `name:tag` and `name@digest` separately but not
the combined `name:tag@digest` form. For a feature ref like
`ghcr.io/devcontainers/features/git:1@sha256:DIGEST`, it split on `@`, kept the
whole `git:1` as the name, and produced repository `devcontainers/features/git:1`
— so the manifest URL became
`https://ghcr.io/v2/devcontainers/features/git:1/manifests/sha256:DIGEST`,
which 404s.

Per the OCI distribution spec, when both a tag and a digest are present the
digest is authoritative and the tag is informational. Strip the tag from the
name portion when a digest follows, so the repository path is
`devcontainers/features/git` and the manifest is fetched by digest.

Surfaced by the `examples/features/oci-digest-pin` canary (scenario 2:
`read-configuration --include-features-configuration` over a digest-pinned
feature), which failed to resolve the manifest. Also fixes that canary's
scenario 2 to pass `--config` explicitly (the config lives at the workspace
root as `devcontainer.json`, not a spec discovery location), matching scenario 3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the docs Documentation changes label May 28, 2026
@pofallon pofallon merged commit 17e9112 into main May 28, 2026
14 of 15 checks passed
@pofallon pofallon deleted the fix/feature-ref-tag-and-digest branch May 28, 2026 22:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant