Skip to content

Conversation

@lbussell
Copy link
Member

@lbussell lbussell commented Jan 22, 2026

Fixes #1914.

Problem

The current PublishConfiguration tightly couples registry endpoints with authentication details. Each RegistryConfiguration embeds its own ServiceConnection, ResourceGroup, and Subscription, making it difficult to:

  • Share authentication credentials across multiple registries
  • Clearly separate "which registry to use" from "how to authenticate"
  • Support non-ACR registries cleanly

Changes

Refactored PublishConfiguration to separate concerns:

New types:

  • RegistryEndpoint - Holds only the registry server address
  • RegistryAuthentication - Holds Server + ServiceConnection + ACR metadata (ResourceGroup, Subscription)

New schema:

{
  "PublishConfiguration": {
    "BuildRegistry": { "Server": "build.azurecr.io" },
    "PublishRegistry": { "Server": "publish.azurecr.io" },
    "InternalMirrorRegistry": { "Server": "internal-mirror.azurecr.io" },
    "PublicMirrorRegistry": { "Server": "public-mirror.azurecr.io" },
    "RegistryAuthentication": [
      {
        "Server": "build.azurecr.io",
        "ServiceConnection": { "Name": "...", "Id": "...", "TenantId": "...", "ClientId": "..." },
        "ResourceGroup": "rg-build",
        "Subscription": "sub-build"
      },
      {
        "Server": "publish.azurecr.io",
        "ServiceConnection": { "Name": "...", "Id": "...", "TenantId": "...", "ClientId": "..." },
        "ResourceGroup": "rg-publish",
        "Subscription": "sub-publish"
      }
    ]
  }
}

Multiple registries can now share authentication by referencing the same server in RegistryAuthentication. The authentication is looked up by server address using FindRegistryAuthentication().

Registry Endpoints Added:

  • BuildRegistry - Images are built and pushed here before testing and publishing
  • PublishRegistry - Images are copied from BuildRegistry to here during publishing
  • InternalMirrorRegistry - External image dependencies are mirrored here
  • PublicMirrorRegistry - External images are mirrored here with anonymous pull access for public PR validation

Files changed:

  • Added RegistryEndpoint.cs - Registry endpoint record with only Server property
  • Added RegistryAuthentication.cs - Authentication record with Server, ServiceConnection, ResourceGroup, Subscription
  • Added RegistryExtensions.cs - Extension methods for registry operations
  • Updated PublishConfiguration.cs - Now uses RegistryEndpoint for registry references and List<RegistryAuthentication> for auth lookup
  • Updated ConfigurationExtensions.cs - Added FindRegistryAuthentication() and GetRegistryResource() methods
  • Added PublishConfigurationBindingTests.cs - Tests for config binding from JSON
  • Updated all consumers to use new lookup via FindRegistryAuthentication()

Breaking change: appsettings.json/PublishConfiguration schema has changed. RegistryAuthentication is now a list keyed by Server instead of a dictionary.

@lbussell lbussell self-assigned this Jan 26, 2026
CopyAcrImagesCommand was passing srcResourceId to the wrapper method,
but CopyImagesCommand.ImportImageAsync ignored it and passed null for
srcRegistryName to the service. This caused CopyImageService to set
ResourceId to null, resulting in Azure API 400 errors.

Fix by passing srcRegistryName instead, allowing CopyImageService to
look up the ResourceId from the registry name as designed.
Azure ACR import only accepts one source identifier. Set ResourceId for
ACR-to-ACR imports, or RegistryAddress for external registries, not both.
These CLI options (--acr-subscription, --acr-resource-group) were defined
but never used by BuildCommand. Registry subscription/resource group info
is now provided per-registry via PublishConfiguration.
@lbussell lbussell marked this pull request as ready for review February 2, 2026 17:59
@lbussell lbussell requested a review from a team as a code owner February 2, 2026 17:59
@mthalman mthalman requested a review from Copilot February 2, 2026 19:47
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 refactors PublishConfiguration to separate registry endpoint addresses from authentication details, making it easier to share credentials across registries and support non-ACR registries.

Changes:

  • Introduced RegistryEndpoint and RegistryAuthentication types to separate concerns
  • Changed PublishConfiguration schema to use RegistryEndpoint for registry references and a list of RegistryAuthentication for credential lookup
  • Removed subscription and resource group CLI arguments from commands, moving this data to the publish configuration
  • Updated all consumers to use new lookup methods (FindRegistryAuthentication, GetRegistryResource)

Reviewed changes

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

Show a summary per file
File Description
RegistryEndpoint.cs New record type holding registry server address only
RegistryAuthentication.cs New record type for authentication and Azure metadata
RegistryExtensions.cs Extension methods for registry operations
PublishConfiguration.cs Updated to use new types and lookup structure
ConfigurationExtensions.cs Added lookup methods for authentication and resource IDs
RegistryResolver.cs Updated to use RegistryAuthentication instead of RegistryConfiguration
RegistryManifestClientFactory.cs Updated to use new authentication lookup
RegistryCredentialsProvider.cs Updated method signature to accept RegistryAuthentication
CopyImageService.cs Moved resource ID creation from parameters to internal lookup
CopyImagesOptions.cs Removed subscription and resource group properties
CopyImagesCommand.cs Simplified with primary constructor, removed subscription/resource group parameters
BuildOptions.cs Removed subscription and resource group CLI options
BuildCommand.cs Updated to use simplified ImportImageAsync signature
AcrContentClientFactory.cs Updated to use new authentication lookup
AcrClientFactory.cs Updated to use new authentication lookup
PublishConfigurationBindingTests.cs New test file for configuration binding validation
Various test files Updated to remove subscription/resource group setup and verification

public string? Server { get; set; }

/// <summary>
/// The Azure DevOps service connection for authenticating to this registry.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The comment refers to 'Azure DevOps service connection' but the broader context suggests this could be used with other authentication mechanisms. Consider updating the comment to be more general or clarify that Azure DevOps is just one example.

Suggested change
/// The Azure DevOps service connection for authenticating to this registry.
/// The service connection used for authenticating to this registry (for example, an Azure DevOps service connection).

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +64
var resourceId =
ContainerRegistryResource.CreateResourceIdentifier(
subscriptionId: subscription,
resourceGroupName: resourceGroup,
registryName: Acr.Parse(registry).Name);
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The method calls Acr.Parse(registry) to extract the registry name, but Acr.Parse() was already called earlier in FindRegistryAuthentication (line 35). Consider passing the already-parsed Acr object to avoid redundant parsing.

Copilot uses AI. Check for mistakes.
Acr destAcr = Acr.Parse(destAcrName);

// Azure ACR import only supports one source identifier. Use ResourceId for ACR-to-ACR
// imports (same tenant), or RegistryAddress for external registries.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The comment states 'same tenant' but the code logic doesn't verify tenant matching. Consider clarifying whether tenant validation happens elsewhere or if this assumption should be documented as a requirement.

Suggested change
// imports (same tenant), or RegistryAddress for external registries.
// imports when the source and destination registries are expected to be in the same tenant
// (this method does not validate tenant matching), or RegistryAddress for external registries.

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.

ACR authentication can fail when using two different service connections for the same ACR

2 participants