Skip to content

feat(manifest): add omit_media_type to leave mediaType out of the manifest - #805

Merged
malt3 merged 1 commit into
bazel-contrib:mainfrom
rdark:richard/pla-2603-omit-manifest-media-type
Oct 19, 2026
Merged

malt3 merged 1 commit into
bazel-contrib:mainfrom
rdark:richard/pla-2603-omit-manifest-media-type

Conversation

@rdark

@rdark rdark commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

image_manifest always writes a top-level "mediaType" into the manifest. helm push does not, so a Helm chart built with rules_img cannot have the digest Helm gives the same chart. periareon/rules_helm#305 adds helm_deploy, which builds a chart as an image_manifest so multi_deploy can push it. For its tests/helm_deploy:simple chart the config, layer and annotations match helm push, but the manifest is sha256:ab07a521… and helm push (3.16.3 and 4.2.0) gives sha256:b6137ff1…. Removing the field is the only difference.

This is a small, opt-in change in the shape of #742 (omit_platform), so I went straight to a PR.

Change

image_manifest (and image_from_binary, which inherits its attributes) gets omit_media_type, default False, which passes the new --omit-media-type flag to img manifest. Existing manifests are byte-identical. Only the manifest bytes change: the descriptor, index and layout entries, and the Content-Type sent to a registry keep application/vnd.oci.image.manifest.v1+json. The OCI spec says mediaType SHOULD be set, so the docstring says to keep the default unless a manifest must match one from another tool. Nothing rejects it on an ordinary image config; as with omit_platform, that is the caller's choice.

image_manifest(
    name = "chart",
    config_fragment = ":chart_config.json",
    config_media_type = "application/vnd.cncf.helm.config.v1+json",
    layers = [":chart_layer"],
    omit_media_type = True,
)

Readers of the field

Several tool paths took the manifest's media type from its bytes. With the field absent they failed or wrote an empty mediaType:

  • img deploy-metadata put "" in the root, manifest and referrer descriptors of the deploy request. A sink then wrote an index.json entry with no mediaType (seen in tests/omit_media_type with the fix reverted), and the deploy VFS's Taggable, LayersFromRoot and DigestsFromRoot return "unsupported media type" for an empty type.
  • pkg/ocilayout (clean, annotated and root-descriptor index styles, and the editor) wrote empty entries for the oci_layout, oci_tarball and sparse layout outputs and the oci: sink. The distribution sink's referrers listing did the same.
  • cmd/deploy's sink root took the type from the bytes for the oci-tar and docker-save index.json.
  • The BES syncer (img serve) sent an empty Content-Type on the manifest PUT, for a root manifest and for each child of an index.

Each now uses api.ImageManifestMediaType, which treats an empty value as the OCI image manifest type (Docker manifests must set the field). The syncer takes the type from the descriptor it fetched the manifest by. This also covers pulled OCI manifests that omit the field on these paths.

Already safe without changes: image_index and signing (they read descriptors), containerd load and the lazy, dedup and eager push strategies (they read the deploy request), and the CAS registry, pkg/signer and the cached-blob transport (they already infer the type).

Testing

  • Go unit tests for the flag (field absent with it, present without it, nothing else different, descriptor unchanged), deploy metadata (root, manifest, referrer), the layout writers and referrers, the oci-tar, oci, docker-save and distribution sinks, and a syncer push to an in-process registry that checks the PUT Content-Type. Each test fails with its fix reverted.
  • tests/omit_media_type builds a Helm-config manifest and a SOCI-enabled image with the attribute and checks the built manifest, descriptor, oci_tarball output group and a push run with --sink oci-tar:. It is excluded on Windows, because no other test starts a pusher from a test there.
  • bazel test //... @rules_img_tool//... @rules_img_internal_tools//... and //util:buildifier.check pass on macOS (Bazel 9.1.1).
  • By hand, with rules_helm#305 pointed at this branch and omit_media_type = True in helm_deploy: multi_deploy pushed simple, template and plus to crane registry serve as sha256:b6137ff1…, sha256:16fda365… and sha256:108e6fb7…, the same digests a real helm push (3.16.3) gives for the same .tgz files. The registry returns Content-Type: application/vnd.oci.image.manifest.v1+json, and helm pull accepts the chart.
  • Not run: Linux and Windows CI, //e2e:integration_tests, and registries other than crane registry serve.

Not changed

image_index also hardcodes its top-level mediaType. img optimize adds the field back when it rewrites a manifest. Index roots without mediaType still give an empty descriptor in deploy metadata and sinks, which rules_img's own indexes never hit. syncocirefgraph rejects remote manifests without the field, as it already does for charts pushed by helm push.

…ifest

image_manifest always writes a top-level mediaType. helm push does not,
so a Helm chart built with rules_img cannot have the digest Helm gives
the same chart. The new attribute, off by default, passes
--omit-media-type to img manifest. The descriptor, index and layout
entries and the registry Content-Type keep the image manifest type.

deploy-metadata, the OCI layout writers, the distribution referrers
listing, the deploy sink root and the BES syncer read the media type
from the manifest bytes and failed or wrote an empty type without the
field. They now use api.ImageManifestMediaType, which treats an empty
value as the OCI image manifest type.
@rdark
rdark marked this pull request as ready for review October 19, 2026 16:25

@malt3 malt3 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@malt3
malt3 added this pull request to the merge queue Oct 19, 2026
Merged via the queue into bazel-contrib:main with commit 2d567bb Oct 19, 2026
24 of 26 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.

2 participants