Skip to content

feat: implement ExtendedCopy and ExtendedCopyOptions#318

Open
wangxiaoxuan273 wants to merge 34 commits intooras-project:mainfrom
wangxiaoxuan273:extendedcopy-2
Open

feat: implement ExtendedCopy and ExtendedCopyOptions#318
wangxiaoxuan273 wants to merge 34 commits intooras-project:mainfrom
wangxiaoxuan273:extendedcopy-2

Conversation

@wangxiaoxuan273
Copy link
Contributor

@wangxiaoxuan273 wangxiaoxuan273 commented Jan 12, 2026

What this PR does / why we need it

This PR implements ExtendedCopy and ExtendedCopyOptions for oras-dotnet.

Which issue(s) this PR resolves / fixes

Resolves / Fixes #155

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?

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
@codecov
Copy link

codecov bot commented Jan 12, 2026

Codecov Report

❌ Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 91.70%. Comparing base (bfd11c3) to head (05ea737).

Files with missing lines Patch % Lines
src/OrasProject.Oras/Registry/Remote/BlobStore.cs 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #318      +/-   ##
==========================================
+ Coverage   91.63%   91.70%   +0.06%     
==========================================
  Files          64       65       +1     
  Lines        2678     2700      +22     
  Branches      361      366       +5     
==========================================
+ Hits         2454     2476      +22     
  Misses        135      135              
  Partials       89       89              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
@wangxiaoxuan273 wangxiaoxuan273 changed the title feat: implement ExtendedCopyOptions feat: implement ExtendedCopy and ExtendedCopyOptions Jan 14, 2026
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
@wangxiaoxuan273 wangxiaoxuan273 marked this pull request as ready for review January 14, 2026 08:40
Copilot AI review requested due to automatic review settings January 14, 2026 08:40
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 implements the ExtendedCopyAsync method and supporting types to enable copying tagged OCI artifacts along with their directed acyclic graph (DAG) of referrers from a source to a destination target. This builds on the existing ExtendedCopyGraphAsync functionality by adding reference resolution and tagging capabilities.

Changes:

  • Introduces IReadOnlyGraphTarget interface combining graph storage and reference resolution
  • Adds ExtendedCopyOptions class for configuring extended copy operations
  • Implements ExtendedCopyAsync extension method for copying tagged artifacts with their DAG
  • Updates MemoryStore to implement the new IReadOnlyGraphTarget interface
  • Adds comprehensive test coverage for the new functionality

Reviewed changes

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

Show a summary per file
File Description
src/OrasProject.Oras/IReadOnlyGraphTarget.cs New interface combining IReadOnlyGraphStorage and IResolvable for read-only graph operations with reference resolution
src/OrasProject.Oras/ExtendedCopyOptions.cs New options class inheriting from ExtendedCopyGraphOptions for configuring ExtendedCopyAsync operations
src/OrasProject.Oras/ReadOnlyGraphTargetExtensions.cs New extension method ExtendedCopyAsync that resolves source reference, copies the DAG, and tags the destination
src/OrasProject.Oras/Content/MemoryStore.cs Updated to implement IReadOnlyGraphTarget instead of just IReadOnlyGraphStorage
tests/OrasProject.Oras.Tests/ExtendedCopyTest.cs Added three tests covering happy path, null parameter validation, and empty destination reference handling

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

/// <exception cref="ArgumentNullException">Thrown when src or dst is null</exception>
public static async Task<Descriptor> ExtendedCopyAsync(
this IReadOnlyGraphTarget src,
string srcRef,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also add a null check for srcRef and opts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added these null checks


await src.ExtendedCopyGraphAsync(dst, node, opts, cancellationToken).ConfigureAwait(false);

await dst.TagAsync(node, dstRef, cancellationToken).ConfigureAwait(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wondering what should be the expected behavior if the graph copy succeeds but tagging fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TagAsync will throw an exception and ExtendedCopy would fail.

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
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

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

Signed-off-by: Xiaoxuan Wang <wangxiaoxuan119@gmail.com>
namespace OrasProject.Oras.Content;

public class MemoryStore : ITarget, IReadOnlyGraphStorage
public class MemoryStore : ITarget, IReadOnlyGraphTarget
Copy link
Contributor

Choose a reason for hiding this comment

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

qq: What's the intended usage for ExtendedCopyAsync ? Currently only MemoryStore implements IReadOnlyGraphTarget. The Repository class implements IRepository: ITarget but not IReadOnlyGraphTarget, which means

var repo = new Repository(...);
await repo.ExtendedCopyAsync(...); 

would not work

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good question. By design, ExtendedCopy copies the artifact and any predecessor artifacts that reference it (common case is referrer artifact whose subject field is this artifact). Therefore ExtendedCopy can only work on a storage that supports finding the predecessors (implement the IPredecessorFindable interface). The Repository class currently does not implement the IPredecessorFindable interface and ExtendedCopy would not work on it. But it really should, we can open a feature request issue and complete it in the future. The implementation would be straightforward, just wrap FetchReferrersAsync which is already available.

This is the Go implementation:

// Predecessors returns the descriptors of image or artifact manifests directly
// referencing the given manifest descriptor.
// Predecessors internally leverages Referrers.
// Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.1/spec.md#listing-referrers
func (r *Repository) Predecessors(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
	var res []ocispec.Descriptor
	if err := r.Referrers(ctx, desc, "", func(referrers []ocispec.Descriptor) error {
		res = append(res, referrers...)
		return nil
	}); err != nil {
		return nil, err
	}
	return res, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created #334

Copilot AI review requested due to automatic review settings February 6, 2026 16:57
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

Copilot reviewed 5 out of 7 changed files in this pull request and generated 3 comments.

namespace OrasProject.Oras;

/// <summary>
/// ReadOnlyGraphTarget represents a read-only GraphTarget.
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The XML doc summary refers to "ReadOnlyGraphTarget" but the public interface name is IReadOnlyGraphTarget (consistent with IReadOnlyTarget). Please update the summary text (and any <see cref> references if added) to use the correct interface name to avoid confusion in generated docs.

Suggested change
/// ReadOnlyGraphTarget represents a read-only GraphTarget.
/// IReadOnlyGraphTarget represents a read-only graph target.

Copilot uses AI. Check for mistakes.
}

/// <summary>
/// ExtendedCopyAsync throws when source target, destination target, source target reference or ExtendedCopyOption is null.
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This test XML doc says "ExtendedCopyOption" but the type is ExtendedCopyOptions (plural). Please correct the wording to match the actual type name.

Suggested change
/// ExtendedCopyAsync throws when source target, destination target, source target reference or ExtendedCopyOption is null.
/// ExtendedCopyAsync throws when source target, destination target, source target reference or
/// ExtendedCopyOptions is null.

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +93
var node = await src.ResolveAsync(srcRef, cancellationToken).ConfigureAwait(false);

await src.ExtendedCopyGraphAsync(dst, node, opts, cancellationToken).ConfigureAwait(false);

await dst.TagAsync(node, dstRef, cancellationToken).ConfigureAwait(false);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

ExtendedCopyAsync resolves the source reference via ResolveAsync and then the copy path will fetch the same root content again during graph copy. For remote repositories, ResolveAsync is a HEAD request (see ManifestStore.ResolveAsync), so this introduces an avoidable extra network round-trip. Consider adding an optimization similar to ReadOnlyTargetExtensions.ResolveRootAsync (use IReferenceFetchable/ReferenceProxy) so the first fetch both resolves and populates the cache for the subsequent copy.

Copilot uses AI. Check for mistakes.
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 extended copy and options

3 participants