Skip to main content

oras-pants-plugin

PyPI Version

A Pantsbuild plugin that wraps the outputs of package-able targets, such as pex_binary, as OCI artifacts and publishes them to OCI registries with oras. Any registry that stores OCI artifacts works: GitHub Container Registry, Docker Hub, Artifact Registry, ECR, ACR, Harbor, a local registry:2, and so on.

Installation

Add the plugin to pants.toml:

[GLOBAL]
plugins = ["oras-pants-plugin==<version>"]

[oras.registries.ghcr]
address = "ghcr.io/my-org"
default = true

Wrap the targets to publish in an oci_artifact target, like the builtin archive target does:

# src/app/BUILD
pex_binary(name="app", entry_point="main.py")

oci_artifact(
    name="app-artifact",
    packages=[":app"],
    oci_repository="app",
    oci_tags=["1.2.0", "latest"],
    oci_artifact_type="application/vnd.python.pex.v1",
)

Build it, or build and push it:

pants package src/app:app-artifact   # writes the OCI image layout to dist/src.app/app-artifact/
pants publish src/app:app-artifact   # copies it to ghcr.io/my-org/app:1.2.0 and :latest

Pull it back anywhere with oras pull ghcr.io/my-org/app:1.2.0, which restores src.app/app.pex.

How it works

oras is downloaded as a static binary; nothing is installed into a Pants resolve.

pants package builds every packages target as pants package would, then runs oras push --oci-layout to write one OCI image layout directory to output_path under --distdir. Every file or directory the packages targets produce becomes one layer, titled with its own --distdir-relative path (a packed or loose layout PEX is a directory, pushed as a gzipped tar layer that oras pull unpacks again). The artifact is tagged with every oci_tags entry and the manifest's artifactType is oci_artifact_type.

pants publish packages the target the same way, then runs one oras cp --from-oci-layout per registry, in the foreground so oras can show progress. The copy addresses the layout's manifest by digest, so the published artifact has exactly the digest pants package reported. A target without oci_repository, that resolves to no registry, with skip_oras=True, or run with [oras].skip = true or [oras].allow_publish = false is skipped before it is packaged. Each copy is reported as published, failed or skipped like every other publisher; --publish-output writes the same as JSON, with the registry, repository, tags and digest of each copy.

Tags from files

oci_tag_files lists targets whose files hold further tags, one per line, appended to oci_tags. Any file or resource target works, and so does any code generator producing them, which is how a version derived from git becomes a tag: the vcs-versioning-pants-plugin's scm_version_file target generates a one-line version file from the git tags at build time.

scm_version_file(name="version", tag_prefix="app-v", generate_to="src/app/VERSION", local_scheme="no-local-version")

oci_artifact(
    name="app-artifact",
    packages=[":app"],
    oci_repository="app",
    oci_tags=["latest"],
    oci_tag_files=[":version"],  # + `1.2.0` on a checkout of the `app-v1.2.0` tag, `1.2.1.dev3` after it
)

The files are hydrated, and generators run, every time the artifact is packaged or published, so the tag follows the git state. An OCI tag cannot contain +, hence local_scheme="no-local-version" for dev builds.

oci_artifact fields

Field Purpose
packages Addresses of targets pants package can build, e.g. [":app"]. Each output becomes a layer.
output_path Where pants package writes the layout directory, relative to --distdir. Default: <spec path with dots>/<target name>.
oci_tags Tags applied in the layout and in every registry. Default: ["latest"]. [] leaves the artifact untagged, addressable by digest only.
oci_tag_files Addresses of file, resource or code generator targets (e.g. scm_version_file, shell_command) whose files hold further tags, one per non-blank line, appended to oci_tags. Duplicates are dropped.
oci_artifact_type The artifactType of the manifest, passed as --artifact-type, e.g. application/vnd.python.pex.v1. Default: oras' application/vnd.unknown.artifact.v1.
oci_artifact_platform The artifact's platform as os[/arch][/variant][:os_version], e.g. linux/amd64, passed as --artifact-platform (experimental in oras): recorded in the manifest's config as {"os": ..., "architecture": ...}. Unset means platform-independent.
oci_registries Registry addresses (ghcr.io/my-org, localhost:5000) or @aliases of [oras].registries entries to publish to. Default: every [oras].registries entry with default = true.
oci_repository The repository within each registry, e.g. team/app; the published reference is <registry>/<oci_repository>:<tag>. Unset skips the target in pants publish.
skip_oras If true, the target is skipped by pants publish.

Configuration

Option Default Purpose
[oras].registries {} Registries by alias: {"alias": {"address": "host[:port][/prefix]", "default": bool}}. An entry aliased default is a default too.
[oras].skip false Skip oras during pants publish.
[oras].allow_publish true Whether pants publish may push oci_artifact targets at all. Set it to false in pants.toml and to true in a CI-only config (PANTS_CONFIG_FILES=pants.ci.toml), so only the release pipeline publishes.
[oras].args [] Extra arguments for every oras cp, e.g. --to-plain-http or --to-insecure. The registry is the copy's destination, so its flags are the --to-* ones. They follow the plugin's own flags, so a repeated flag overrides it.
[oras].config unset A registry authentication file in the ~/.docker/config.json format, passed as --to-registry-config. Relative paths are resolved from the build root.
[oras].extra_env_vars [] Extra environment variables for oras cp, as NAME=value or NAME to copy from Pants' environment, e.g. HTTPS_PROXY or SSL_CERT_FILE.
[oras].version bundled default oras release to download.
[oras].known_versions bundled default Checksums of downloadable releases; extend to use another version.

Credentials

oras reads the credentials that docker login or oras login store in $DOCKER_CONFIG/config.json or ~/.docker/config.json, and runs the credential helpers configured there. PATH, HOME and DOCKER_CONFIG are always passed to oras cp, so logging in once is enough:

oras login ghcr.io          # or: docker login ghcr.io
pants publish src/app:app-artifact

In CI, pass the token to a login step before pants publish, or point [oras].config at an auth file. Avoid --to-username/--to-password in [oras].args: pants.toml is committed.

Publishing into a directory

oras cp can write to an OCI image layout directory instead of a registry, which is handy for inspecting what would be pushed. With --to-oci-layout, the registry reference becomes a directory path in the sandbox, so combine it with --keep-sandboxes=always to look at the result:

pants --oras-args="['--to-oci-layout']" --keep-sandboxes=always publish src/app:app-artifact

The layout pants package writes to dist/ is the same artifact; oras discover, oras manifest fetch and oras pull all accept it with --oci-layout:

oras manifest fetch --oci-layout dist/src.app/app-artifact:1.2.0 | jq

Using a different oras version

Add the release's checksums and sizes to known_versions and set version:

[oras]
version = "1.3.5"
known_versions.add = [
  "1.3.5|linux_arm64 |<sha256>|<size>",
  "1.3.5|linux_x86_64|<sha256>|<size>",
  "1.3.5|macos_arm64 |<sha256>|<size>",
  "1.3.5|macos_x86_64|<sha256>|<size>",
]

Compute each line with curl -L $URL | tee >(wc -c) >(shasum -a 256) >/dev/null, where $URL is the oras_<version>_<os>_<arch>.tar.gz asset of the GitHub release (linux/darwin, amd64/arm64). The release's oras_<version>_checksums.txt lists the same checksums.

Supported versions

Plugin Pants Python oras
0.x 2.33 3.14 >=1.3,<2

Pants loads plugins into its own interpreter, so the plugin requires the Python version Pants runs on. The packages being wrapped may target any Python version.

Releases

Wheels are published to PyPI. Release notes live in the changelog and on the GitHub releases page.

Contributing, Bug Reports and Feature Requests

This plugin lives in the pants-plugin-depot repo. See its CONTRIBUTING.md, or open an issue.

Metadata

Release files for oras-pants-plugin 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for oras-pants-plugin 0.3.1
File Interpreter ABI Platform
oras_pants_plugin-0.3.1-py3-none-any.whl Python 3 none any Details

Release files / oras_pants_plugin-0.3.1-py3-none-any.whl

Download URL oras_pants_plugin-0.3.1-py3-none-any.whl
Size 26.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1823118903f991d003f5bccba8bb0ab820d2408c4e503335de1ca2602e84c946
BLAKE2b-256 checksum
How to use checksums
4ef1b7e9e40c3c4f71253723e97a1e5671e9915490733a5b991f1c2193ebe38f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Security for oras-pants-plugin 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 1 file in this release has been uploaded with provenance metadata.

Attestation for oras_pants_plugin-0.3.1-py3-none-any.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page