Skip to content

feat: migrate ORAS CLI from oras-go v2 to v3 #2008

Description

@TerryHowe

Overview

This issue tracks the full migration of the ORAS CLI from oras.land/oras-go/v2 to github.com/oras-project/oras-go/v3. The migration is broken into three phases:

  1. Mechanical API migration — import path and breaking API renames (PR feat: migrate from oras-go v2 to v3 #2002)
  2. Deprecation cleanup — remove duplicated internal code now first-class in v3
  3. New feature adoption — unlock user-facing capabilities provided by v3

Motivation

oras-go v3 introduces a new module path, several breaking API renames, and new packages that provide capabilities the ORAS CLI currently either lacks entirely or implements through duplicated custom code. The primary user-facing motivation is support for the standard containers configuration file stack:

containers/image configuration files supported by oras-go v3

Config File Location Benefit to ORAS users
containers-auth.json ~/.config/containers/auth.json Podman/Buildah/Skopeo credentials are read automatically alongside Docker config.json — no --registry-config flag needed
containers-registries.conf /etc/containers/registries.conf Registry mirrors, blocked registries, and location remapping are applied automatically on every pull — critical for air-gapped and enterprise deployments
containers-certs.d /etc/containers/certs.d/<host>/ Per-registry CA certificates and client TLS certs are loaded without --ca-file flags

Beyond the config-file ecosystem, full v3 adoption also:

  • Fixes a process-safety bug in the ORAS_CACHE path (oci.New() is not safe for concurrent process writes)
  • Removes ~300 lines of duplicated internal code (internal/cache/, internal/trace/transport.go)
  • Adds structured error messages for oras cp that distinguish source from destination failures

Phases & Timeline

Phase 1 — Mechanical API Migration (PR #2002)

Status: open (draft)

Replace all oras.land/oras-go/v2 imports with github.com/oras-project/oras-go/v3 and update all breaking call sites.

Change Status
Import path oras.land/oras-go/v2github.com/oras-project/oras-go/v3 ✅ Done
auth.Credential{}credentials.Credential{} ✅ Done
auth.EmptyCredentialcredentials.EmptyCredential ✅ Done
client.Credential =client.CredentialFunc = ✅ Done
credentials.Credential(store)store.Get ✅ Done
credentials.Login/Logoutremote.Login/Logout ✅ Done
repo.Client/PlainHTTP/HandleWarningrepo.Registry.* ✅ Done
repo.Reference.Repositoryrepo.RepositoryName ✅ Done
ref.Reference (field) → ref.GetReference() (method) ✅ Done
SetReferrersCapability() — void, no longer returns error ✅ Done

Phase 2 — Deprecation Cleanup (follow-on PRs)

Remove internal code that is now first-class in oras-go v3.

# Task Files Net LOC Notes
2.1 oci.New()oci.NewStorage() in cache option/cache.go −2 Fixes concurrent-write safety bug for shared ORAS_CACHE
2.2 Delete internal/cache/; use oras-go/content/cache internal/cache/, option/cache.go −100 Library now ships CacheReadOnlyTarget with correct oci.NewStorage() backing
2.3 Replace custom handleWarning() with remote.NewWarningLogger() option/remote.go −25 Requires slog→logrus bridge (~15 lines) since v3 uses *slog.Logger
2.4 Replace internal/trace/transport.go with remote.NewLoggingTransport() via ClientBuilder.Logger internal/trace/, option/remote.go −160 Same header scrubbing + body cap; adds request IDs for concurrent correlation

Phase 3 — New Feature Adoption (follow-on PRs)

3.1 ClientBuilder + properties.Registry in NewRepository()/NewRegistry()

Replace the 40+ line manual assembly of tls.Config → http.Transport → retry.Transport → auth.Client with ClientBuilder, which handles TLS, retry, credentials, user-agent, and debug logging in one place.

3.2 config.LoadConfigs() — Full Ecosystem Config Stack

Use config.LoadConfigs() as the baseline for registry configuration, then override with CLI flags. This is the key change that unlocks the containers config-file ecosystem:

config.LoadConfigs()
  ├── Docker config.json          (credentials)
  ├── containers/auth.json        (Podman/Buildah/Skopeo credentials)
  ├── containers/registries.conf  (mirrors, blocked registries, location rewrites)
  └── containers/certs.d/         (per-registry TLS certificates)

CLI flags continue to take highest priority — no change in behavior for users who specify flags explicitly.

Capabilities unlocked at zero additional CLI surface:

  • Podman users: podman login-saved credentials work automatically
  • Enterprise/air-gapped: registries.conf mirrors tried on every pull, no config needed
  • Operators: per-registry CA certs from certs.d load without --ca-file

Future flags (separate PRs):

  • --registries-config — override path to registries.conf
  • --policy-file — override path to policy.json

3.3 oras.CopyError Structured Error Handling

Wrap oras.Copy() failures with *oras.CopyError to produce actionable messages distinguishing source from destination failures in oras cp, oras push, oras pull, and oras attach.


Phase 4 — Future Work (separate design required)

Capability oras-go Package Notes
Policy enforcement (--policy-file) registry/remote/policy Requires UX design for insecureAcceptAnything / reject / signedBy rules
OpenPGP signature verification registry/remote/signature Depends on registries.d config and lookaside store support
objects package for push/pack objects Higher-level alternative to PackManifest; consider for oras push simplification

Implementation Order

# Task Unblocked By
1 PR #2002 — mechanical API migration
2 oci.New()oci.NewStorage() PR #2002
3 Delete internal/cache/ step 2
4 slog→logrus bridge (internal/logutil/) PR #2002
5 remote.NewWarningLogger() step 4
6 ClientBuilder in NewRepository()/NewRegistry() steps 4, 5
7 config.LoadConfigs() full config stack step 6
8 remote.NewLoggingTransport() via builder.Logger step 6
9 oras.CopyError structured errors PR #2002

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions