Skip to content

feat(copy): Support copying artifacts across multiple platforms.#1954

Open
hellocn9 wants to merge 4 commits intooras-project:mainfrom
hellocn9:main
Open

feat(copy): Support copying artifacts across multiple platforms.#1954
hellocn9 wants to merge 4 commits intooras-project:mainfrom
hellocn9:main

Conversation

@hellocn9
Copy link

@hellocn9 hellocn9 commented Jan 8, 2026

What this PR does / why we need it:
In certain scenarios, only the images for the linux/amd64 and linux/arm64 platforms need to be synchronized. However, in the current version, if --platform is not specified, images for all platforms are synchronized; whereas when -- platform is specified, only a single platform architecture can be provided. To address this issue, this PR introduces support for specifying multiple platform architectures with --platform, separated by commas.

Which issue(s) this PR fixes (optional, in fixes #<issue number>(, fixes #<issue_number>, ...) format, will close the issue(s) when PR gets merged):
Fixes #1945

Please check the following list:

  • Does the affected code have corresponding tests, e.g. unit test, E2E test?
  • Does this change require a documentation update?
  • Does this introduce breaking changes that would require an announcement or bumping the major version?
  • Do all new files have an appropriate license header?

@wangxiaoxuan273
Copy link
Contributor

@shizhMSFT @FeynmanZhou Can you take a look?

@FeynmanZhou
Copy link
Member

Thanks @hellocn9 for the contribution! Are you looking to resolve this issue in this PR? #1945

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for copying artifacts across multiple platforms by allowing users to specify comma-separated platform architectures with the --platform flag (e.g., --platform linux/amd64,linux/arm64,linux/arm/v7). Previously, only a single platform could be specified at a time.

Key changes:

  • Extended platform parsing to support comma-separated lists of platforms
  • Added new copyMultiplePlatforms function to handle copying filtered manifests from indexes
  • Implemented platform matching logic to filter index manifests based on specified platforms

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 14 comments.

File Description
cmd/oras/internal/option/platform.go Added Platforms field to support multiple platforms, updated parsing logic to handle comma-separated platform strings, and refactored single platform parsing into a separate method
cmd/oras/root/cp.go Added import for metadata handler, updated documentation with multi-platform example, implemented copyMultiplePlatforms function with index filtering and platform matching logic
Comments suppressed due to low confidence (1)

cmd/oras/internal/option/platform.go:126

  • The parseSinglePlatform function doesn't populate the Platforms slice when a single platform is parsed. This creates inconsistency where Platform is set but Platforms remains nil/empty. This could cause issues in code that checks len(opts.Platform.Platforms) > 0 to determine if platforms were specified. For a single platform, the code at line 146 in cp.go would evaluate to false even when a platform is specified.
func (opts *Platform) parseSinglePlatform(platformStr string) error {
	// OS[/Arch[/Variant]][:OSVersion]
	// If Arch is not provided, will use GOARCH instead
	var platformPart string
	var p ocispec.Platform
	platformPart, p.OSVersion, _ = strings.Cut(platformStr, ":")
	parts := strings.Split(platformPart, "/")
	switch len(parts) {
	case 3:
		p.Variant = parts[2]
		fallthrough
	case 2:
		p.Architecture = parts[1]
	case 1:
		p.Architecture = runtime.GOARCH
	default:
		return fmt.Errorf("failed to parse platform %q: expected format os[/arch[/variant]]", platformStr)
	}
	p.OS = parts[0]
	if p.OS == "" {
		return fmt.Errorf("invalid platform: OS cannot be empty")
	}
	if p.Architecture == "" {
		return fmt.Errorf("invalid platform: Architecture cannot be empty")
	}
	opts.Platform = &p
	return nil
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hellocn9
Copy link
Author

Thanks @hellocn9 for the contribution! Are you looking to resolve this issue in this PR? #1945

Yes, this PR addresses #1945. Thanks for reviewing!

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: hellocn9 <zhangyancn9@126.com>
Copy link
Contributor

@shizhMSFT shizhMSFT left a comment

Choose a reason for hiding this comment

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

Besides above comments, please add related unit tests and E2E tests.

…form filtering and error handling

Signed-off-by: hellocn9 <zhangyancn9@126.com>
…opying

Signed-off-by: hellocn9 <zhangyancn9@126.com>
@hellocn9
Copy link
Author

Besides above comments, please add related unit tests and E2E tests.

Done! Added unit & E2E tests for all platform-related changes.

@hellocn9
Copy link
Author

@shizhMSFT I’ve addressed all your feedback — please take a look when you have time!

@hellocn9 hellocn9 requested a review from shizhMSFT January 18, 2026 09:03
@hellocn9 hellocn9 requested a review from shizhMSFT January 20, 2026 09:22
…form filtering

Signed-off-by: hellocn9 <zhangyancn9@126.com>
Comment on lines +368 to +371
// Variant: optional; if specified, must match exactly, otherwise it is ignored
//if targetPlatform.Variant == "" && manifestPlatform.Variant != "" {
// return false
//}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Variant: optional; if specified, must match exactly, otherwise it is ignored
//if targetPlatform.Variant == "" && manifestPlatform.Variant != "" {
// return false
//}

statusHandler, metadataHandler := display.NewCopyHandler(opts.Printer, opts.TTY, dst)

// Check if multiple platforms are specified
if len(opts.Platform.Platforms) > 1 && !opts.recursive {
Copy link
Member

Choose a reason for hiding this comment

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

This part is confusing me here, why is this checking for not recursive? If there are multiple platforms specified don't we want to copy the multiple platforms?

opts.FlagDescription = "request platform"
}
fs.StringVarP(&opts.platform, "platform", "", "", opts.FlagDescription+" in the form of `os[/arch][/variant][:os_version]`")
fs.StringSliceVarP(&opts.platform, "platform", "", nil, opts.FlagDescription+" in the form of `os[/arch][/variant][:os_version]` or comma-separated list for multiple platforms (supported in oras cp only)")
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated to this PR, but that bit about "supported in oras cp only" is a good argument for composition over inheritance

func (opts *ArtifactPlatform) ApplyFlags(fs *pflag.FlagSet) {
opts.FlagDescription = "set artifact platform"
fs.StringVarP(&opts.platform, "artifact-platform", "", "", "[Experimental] "+opts.FlagDescription+" in the form of `os[/arch][/variant][:os_version]`")
fs.StringSliceVarP(&opts.platform, "artifact-platform", "", nil, "[Experimental] "+opts.FlagDescription+" in the form of `os[/arch][/variant][:os_version]`")
Copy link
Member

Choose a reason for hiding this comment

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

Why was this changed?

}

// Push the new index to the destination
if err = dst.Push(ctx, newIndexDesc, strings.NewReader(string(newIndexContent))); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if err = dst.Push(ctx, newIndexDesc, strings.NewReader(string(newIndexContent))); err != nil {
if err = dst.Push(ctx, newIndexDesc, bytes.NewReader(newIndexContent)); err != nil {

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.

Support multiple --platform where appropriate

6 participants