Skip to content

feat: add Red Hat media type profile for oc-mirror#6

Merged
pavelanni merged 4 commits intomainfrom
feature/redhat-media-types
Apr 21, 2026
Merged

feat: add Red Hat media type profile for oc-mirror#6
pavelanni merged 4 commits intomainfrom
feature/redhat-media-types

Conversation

@pavelanni
Copy link
Copy Markdown
Collaborator

@pavelanni pavelanni commented Apr 21, 2026

Summary

  • Add --media-type flag to skillctl pack with two profiles: standard (default) and redhat
  • The redhat profile uses application/vnd.redhat.agentskill.layer.v1+tar and application/vnd.redhat.agentskill.config.v1+json media types, enabling oc-mirror to identify skill artifacts for disconnected/air-gapped mirroring
  • Standard OCI types remain the default — no behavior change for existing users
  • Manifest media type stays application/vnd.oci.image.manifest.v1+json in both profiles so kubelet image volumes and registry tooling work regardless

Ref: OCPSTRAT-3122 (Agent skill packaging for OpenShift)

Test plan

  • TestResolveMediaTypes — covers standard, redhat, empty, and invalid profiles
  • TestPackRedHatMediaType — full pack with redhat profile, verifies manifest type stays standard
  • TestPackInvalidMediaType — rejects unknown profile names
  • All existing tests pass (no behavior change for default profile)
  • make lint — 0 issues
  • Manual: skillctl pack --media-type redhat examples/skills/resume-screener and inspect the OCI layout to verify layer/config media types

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added --media-type CLI flag to select media type profiles (standard or Red Hat) when packing skills.
  • Documentation

    • New presentation deck introducing OCI-Based Skill Distribution, covering the skill model, distribution workflows, deployment strategies, and consumption paths.
  • Tests

    • Added comprehensive test coverage for media type profile parsing and resolution.

…d content

Add Markdown (Marp-compatible) version of the OCI skill distribution deck.
Update HTML deck with new slides on disconnected/air-gapped environments
and multiple consumption paths based on OCPSTRAT-3122 Jira discussion.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Pavel Anni <panni@redhat.com>
Add --media-type flag to skillctl pack that selects between standard
OCI media types (default) and Red Hat-specific types needed for
oc-mirror to identify skill artifacts in disconnected environments.

Standard OCI types remain the default to preserve compatibility with
all registries and tooling. The "redhat" profile sets layer and config
media types to application/vnd.redhat.agentskill.* while keeping the
manifest type standard so kubelet image volumes continue to work.

Ref: OCPSTRAT-3122

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Pavel Anni <panni@redhat.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Warning

Rate limit exceeded

@pavelanni has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 52 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 46 minutes and 52 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e8e6294-9761-4fd0-a3eb-7738324ca0bb

📥 Commits

Reviewing files that changed from the base of the PR and between 8a43617 and 5955051.

📒 Files selected for processing (5)
  • pkg/oci/client.go
  • pkg/oci/inspect.go
  • pkg/oci/mediatype.go
  • pkg/oci/mediatype_test.go
  • pkg/oci/oci_test.go
📝 Walkthrough

Walkthrough

Introduces OCI media type profile selection for skill image packing. Users can now specify --media-type flag (standard or Red Hat) at the CLI, which determines the OCI media type constants used when packing skill images. New validation, parsing, and media type resolution logic supports both standard OCI types and Red Hat-specific types for oc-mirror compatibility.

Changes

Cohort / File(s) Summary
Version Control Configuration
.gitignore
Added scratch/ directory to ignore rules.
Presentation Documentation
docs/slides/oci-skill-distribution-deck.md
Added comprehensive Marp slide deck covering OCI-based skill distribution workflow, including packing, pushing, inspection, deployment patterns, and disconnected environment handling.
CLI Flag Support
internal/cli/pack.go
Added --media-type flag to pack command; threaded parsed profile into client.Pack via PackOptions.MediaType.
OCI Client API & Pack Logic
pkg/oci/client.go, pkg/oci/pack.go
Added MediaType field to PackOptions struct; modified Pack to resolve and apply media types from the profile instead of hard-coded OCI constants.
Media Type Infrastructure
pkg/oci/mediatype.go
Introduced MediaTypeProfile type with standard and redhat constants; added ParseMediaTypeProfile for validation/normalization and internal resolveMediaTypes for mapping profiles to OCI media type strings.
Unit Tests
pkg/oci/mediatype_test.go, pkg/oci/oci_test.go
Added table-driven tests for ParseMediaTypeProfile (covering empty, case variants, whitespace, invalid input) and resolveMediaTypes; added integration test verifying pack with MediaTypeRedHat option and error handling for invalid profiles.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Red Hat media type profile for oc-mirror' accurately and concisely describes the main change: adding a new Red Hat media type profile capability to the tool.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/redhat-media-types

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Address code review findings:
- Add ParseMediaTypeProfile() with whitespace trimming and
  case-insensitive matching, following the ParseState() pattern
- Validate at CLI layer before reaching Pack()
- Simplify resolveMediaTypes() to return only values (no error)
  since invalid profiles are caught at parse time
- Add test cases for whitespace, mixed case, and error messages

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Pavel Anni <panni@redhat.com>
@pavelanni pavelanni marked this pull request as ready for review April 21, 2026 18:14
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/oci/mediatype.go (1)

39-46: Prefer ocispec constants over hardcoded strings for the standard profile.

pack.go imports ocispec which already exposes MediaTypeImageLayerGzip and MediaTypeImageConfig. Using them here keeps the standard-profile values in lockstep with the spec package and avoids drift.

Proposed refactor
-func resolveMediaTypes(profile MediaTypeProfile) (layer, config string) {
-	switch profile {
-	case MediaTypeRedHat:
-		return RedHatMediaTypeSkillLayer, RedHatMediaTypeSkillConfig
-	default:
-		return "application/vnd.oci.image.layer.v1.tar+gzip",
-			"application/vnd.oci.image.config.v1+json"
-	}
-}
+func resolveMediaTypes(profile MediaTypeProfile) (layer, config string) {
+	switch profile {
+	case MediaTypeRedHat:
+		return RedHatMediaTypeSkillLayer, RedHatMediaTypeSkillConfig
+	default:
+		return ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageConfig
+	}
+}

Note: mediatype_test.go already asserts the literal strings, so no test change required if the literals match.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/oci/mediatype.go` around lines 39 - 46, The standard-profile branch in
resolveMediaTypes uses hardcoded media-type strings; replace those literals with
the ocispec constants (MediaTypeImageLayerGzip and MediaTypeImageConfig) so the
default case returns ocispec.MediaTypeImageLayerGzip and
ocispec.MediaTypeImageConfig instead of the raw strings; update the import usage
if necessary to reference ocispec in the pkg/oci/mediatype.go file and keep the
MediaTypeRedHat branch as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/oci/mediatype.go`:
- Around line 20-22: The RedHat media-type constant RedHatMediaTypeSkillLayer
conflicts with createLayer() in pkg/oci/pack.go because createLayer() always
produces a gzip-compressed tar; update createLayer() to branch on the RedHat
profile and skip gzip compression (producing plain tar) so the produced blob,
descriptor size, and diff_id match the uncompressed tar media type
RedHatMediaTypeSkillLayer, or alternatively change RedHatMediaTypeSkillLayer to
the gzip variant (application/vnd.redhat.agentskill.layer.v1+tar+gzip) if the
RedHat spec allows compressed layers; locate the branching logic in
createLayer(), adjust the compression step and the code that computes diff_id
and descriptor.Size accordingly, and ensure consistency between the media type
constant RedHatMediaTypeSkillLayer and the actual blob produced.

In `@pkg/oci/oci_test.go`:
- Around line 480-508: TestPackRedHatMediaType currently only checks the
manifest media type via client.Inspect and never verifies that the packed
manifest's config and layer entries use the Red Hat media types; update the test
to fetch the manifest bytes for the tagged ref returned by client.Pack/Inspect
(or extend InspectResult) and unmarshal into ocispec.Manifest, then assert that
m.Config.MediaType == oci.RedHatMediaTypeSkillConfig and m.Layers[0].MediaType
== oci.RedHatMediaTypeSkillLayer so the Red Hat profile is actually exercised
end-to-end; locate this logic around TestPackRedHatMediaType and the client.Pack
/ client.Inspect usage and either call the existing manifest fetch helper or add
one in the test to perform the manifest fetch and checks.

---

Nitpick comments:
In `@pkg/oci/mediatype.go`:
- Around line 39-46: The standard-profile branch in resolveMediaTypes uses
hardcoded media-type strings; replace those literals with the ocispec constants
(MediaTypeImageLayerGzip and MediaTypeImageConfig) so the default case returns
ocispec.MediaTypeImageLayerGzip and ocispec.MediaTypeImageConfig instead of the
raw strings; update the import usage if necessary to reference ocispec in the
pkg/oci/mediatype.go file and keep the MediaTypeRedHat branch as-is.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d320bb7-2c9e-47d9-b25e-e4b6bbe16368

📥 Commits

Reviewing files that changed from the base of the PR and between 08fc4e5 and 8a43617.

📒 Files selected for processing (9)
  • .gitignore
  • docs/slides/oci-skill-distribution-deck.html
  • docs/slides/oci-skill-distribution-deck.md
  • internal/cli/pack.go
  • pkg/oci/client.go
  • pkg/oci/mediatype.go
  • pkg/oci/mediatype_test.go
  • pkg/oci/oci_test.go
  • pkg/oci/pack.go

Comment thread pkg/oci/mediatype.go Outdated
Comment on lines +20 to +22
RedHatMediaTypeSkillLayer = "application/vnd.redhat.agentskill.layer.v1+tar"
RedHatMediaTypeSkillConfig = "application/vnd.redhat.agentskill.config.v1+json"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Red Hat agent skill OCI media type application/vnd.redhat.agentskill.layer.v1+tar oc-mirror specification

💡 Result:

The media type "application/vnd.redhat.agentskill.layer.v1+tar" appears to be a custom OCI layer media type defined by Red Hat for packaging "agent skills" as OCI artifacts in container registries like Red Hat Quay. Agent skills are reusable instruction sets or capabilities for AI agents, following emerging standards like agentskills.io, packaged similarly to Helm charts or OPA policies. To use this media type in Red Hat Quay: - Enable general OCI support: FEATURE_GENERAL_OCI_SUPPORT: true - Add to ALLOWED_OCI_ARTIFACT_TYPES in config.yaml, e.g.: application/vnd.redhat.agentskill.config.v1+json: - application/vnd.redhat.agentskill.layer.v1+tar - Reconfigure Quay. oc-mirror, used for mirroring OpenShift images and operators to disconnected environments, supports standard OCI images and catalogs but has no explicit mention of agent skill media types or special handling. Agent skills would be mirrored as regular OCI artifacts if included in an ImageSetConfiguration. No official Red Hat documentation explicitly defines this exact media type as of 2026-04-21, suggesting it is either emerging (related to Red Hat AI/OpenShift agentic workflows) or for internal/experimental use in agent-mode operators. Similar patterns exist in Quay docs for custom OCI types like application/vnd.sylabs.sif.layer.v1+tar.

Citations:


🏁 Script executed:

# First, find the createLayer function in pack.go
fd -t f "pack.go" | head -20

Repository: redhat-et/skillimage

Length of output: 100


🏁 Script executed:

# Search for createLayer implementation
rg -A 30 "func.*createLayer" --type go

Repository: redhat-et/skillimage

Length of output: 1277


🏁 Script executed:

# Check for any references to RedHatMediaTypeSkillLayer usage
rg "RedHatMediaTypeSkillLayer" --type go

Repository: redhat-et/skillimage

Length of output: 315


🏁 Script executed:

# Look for OCPSTRAT-3122 references or specification files
rg "OCPSTRAT-3122|agentskill" --type go -i

Repository: redhat-et/skillimage

Length of output: 265


🏁 Script executed:

# Check the mediatype.go file to see all media type definitions
cat -n pkg/oci/mediatype.go | head -50

Repository: redhat-et/skillimage

Length of output: 1915


🏁 Script executed:

# Get the full createLayer function
rg -A 100 "^func createLayer" pkg/oci/pack.go

Repository: redhat-et/skillimage

Length of output: 2529


🏁 Script executed:

# Also check what the function returns and how it's used
rg -B 5 -A 15 "createLayer(" pkg/oci/pack.go | head -80

Repository: redhat-et/skillimage

Length of output: 138


🏁 Script executed:

# Check for any spec files or documentation about media types
fd -t f "spec|SPEC|spec.md|agentskill" | head -20

Repository: redhat-et/skillimage

Length of output: 152


🏁 Script executed:

# Search for any comments or documentation about the +tar media type choice
rg "tar\+gzip|tar\+|media.*type" pkg/oci/pack.go -B 2 -A 2

Repository: redhat-et/skillimage

Length of output: 191


🏁 Script executed:

# Check git history to see if there are related commits or TODOs
rg "TODO|FIXME|BUG|XXX" pkg/oci/ | grep -i "media\|tar\|gzip\|compress"

Repository: redhat-et/skillimage

Length of output: 46


🏁 Script executed:

# Check if there's any special handling for the RedHat profile during layer creation
rg -B 10 -A 10 "resolveMediaTypes|MediaTypeProfile" pkg/oci/pack.go

Repository: redhat-et/skillimage

Length of output: 790


🏁 Script executed:

# Read the implementation spec
cat docs/design/2026-04-16-implementation-spec.md

Repository: redhat-et/skillimage

Length of output: 10890


🏁 Script executed:

# Look for any other design docs or specifications
fd -t f "design|spec" docs/

Repository: redhat-et/skillimage

Length of output: 234


🏁 Script executed:

# Search for OCPSTRAT or any references to Red Hat specifications
rg "OCPSTRAT|RHTAS|Red Hat|redhat" docs/ -i

Repository: redhat-et/skillimage

Length of output: 50376


🏁 Script executed:

# Check if there's any comment in the code explaining the +tar choice
rg -B 5 "RedHatMediaTypeSkillLayer" pkg/oci/

Repository: redhat-et/skillimage

Length of output: 1145


🏁 Script executed:

# Check if there's any TODO or FIXME related to gzip/compression in redhat profile
rg "TODO|FIXME|gzip|compress" pkg/oci/ -B 2 -A 2 | grep -A 3 -B 3 "redhat\|RedHat"

Repository: redhat-et/skillimage

Length of output: 441


🏁 Script executed:

# Look for any spec or documentation files that might define the redhat media type behavior
find docs/ -type f -name "*.md" | xargs rg "tar\+gzip|tar\+|RedHat.*media|agentskill.*layer" -l 2>/dev/null

Repository: redhat-et/skillimage

Length of output: 152


🏁 Script executed:

# Check the slides for more context about the redhat media type
grep -A 10 -B 10 "application/vnd.redhat.agentskill.layer.v1" docs/slides/oci-skill-distribution-deck.md

Repository: redhat-et/skillimage

Length of output: 1101


🏁 Script executed:

# Check implementation spec for any notes about media type choices
grep -A 20 -B 5 "RedHat.*media\|+tar\|redhat.*profile" docs/design/2026-04-16-implementation-spec.md | head -60

Repository: redhat-et/skillimage

Length of output: 46


Fix media type and content mismatch for RedHat profile.

pkg/oci/pack.go createLayer() produces a tar.gz layer regardless of profile, but RedHatMediaTypeSkillLayer declares application/vnd.redhat.agentskill.layer.v1+tar per OCPSTRAT-3122. Consumers (e.g., oc-mirror) expecting uncompressed tar will fail.

Branch createLayer() to skip gzip compression when the RedHat profile is selected, adjusting diff_id and descriptor sizes accordingly. Alternatively, if OCPSTRAT-3122 actually permits gzip, change the media type to application/vnd.redhat.agentskill.layer.v1+tar+gzip.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/oci/mediatype.go` around lines 20 - 22, The RedHat media-type constant
RedHatMediaTypeSkillLayer conflicts with createLayer() in pkg/oci/pack.go
because createLayer() always produces a gzip-compressed tar; update
createLayer() to branch on the RedHat profile and skip gzip compression
(producing plain tar) so the produced blob, descriptor size, and diff_id match
the uncompressed tar media type RedHatMediaTypeSkillLayer, or alternatively
change RedHatMediaTypeSkillLayer to the gzip variant
(application/vnd.redhat.agentskill.layer.v1+tar+gzip) if the RedHat spec allows
compressed layers; locate the branching logic in createLayer(), adjust the
compression step and the code that computes diff_id and descriptor.Size
accordingly, and ensure consistency between the media type constant
RedHatMediaTypeSkillLayer and the actual blob produced.

Comment thread pkg/oci/oci_test.go
- Fix layer MIME type to match gzip-compressed content: change
  RedHatMediaTypeSkillLayer from +tar to .tar+gzip since createLayer()
  always produces gzip output
- Add ConfigMediaType and LayerMediaType to InspectResult so callers
  can verify the inner descriptor types
- Strengthen TestPackRedHatMediaType to assert config and layer media
  types end-to-end, not just the manifest type
- Use ocispec constants instead of hardcoded strings in resolveMediaTypes

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Pavel Anni <panni@redhat.com>
@pavelanni pavelanni merged commit 443ec21 into main Apr 21, 2026
6 checks passed
@pavelanni pavelanni deleted the feature/redhat-media-types branch April 21, 2026 18:31
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