diff --git a/NuGet.config b/NuGet.config
index bb55c6d7b60a..b99b27aa1d76 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,39 +4,27 @@
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
-
+
@@ -63,24 +51,15 @@
-
+
-
-
-
-
-
-
-
-
-
-
+
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 548041aeb370..ddfd21bca8e8 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,71 +1,71 @@
-
+ https://github.com/dotnet/templating
- 93a589449f7f40bbef42d1e8df97737b1ba976ed
+ 5d99405601e2672cd496087efc51611f1fc0d860
-
+ https://github.com/dotnet/templating
- 93a589449f7f40bbef42d1e8df97737b1ba976ed
+ 5d99405601e2672cd496087efc51611f1fc0d860
-
+ https://github.com/dotnet/templating
- 93a589449f7f40bbef42d1e8df97737b1ba976ed
+ 5d99405601e2672cd496087efc51611f1fc0d860
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9https://dev.azure.com/dnceng/internal/_git/dotnet-runtime81cabf2857a01351e5ab578947c7403a5b128ad1
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
-
+ https://github.com/dotnet/emsdk
- c458a7647c04e323c238f381eb176af24254b3ff
+ ec84e775d21d3b7a6698ec30f7b2a5a9e3acd314
-
+ https://github.com/dotnet/msbuild
- 17752cebd2495744980da84649957e5b66144af0
+ 2b19be476c8b86077deb82788563d7f7cc53468b
-
+ https://github.com/dotnet/msbuild
- 17752cebd2495744980da84649957e5b66144af0
+ 2b19be476c8b86077deb82788563d7f7cc53468b
-
+ https://github.com/dotnet/msbuild
- 17752cebd2495744980da84649957e5b66144af0
+ 2b19be476c8b86077deb82788563d7f7cc53468b
@@ -77,9 +77,9 @@
e11d7079bebc6f101c5313fe0d1de9e3d38a7c02
-
- https://dev.azure.com/dnceng/internal/_git/dotnet-format
- 86f5186deeea364bd8541d51657e52a54b2a96b2
+
+ https://github.com/dotnet/format
+ 42ef1d04e544c2a0dca064f9fc982639ca516a84
@@ -111,13 +111,13 @@
https://github.com/dotnet/roslynbc1c3011064a493b0ca527df6fb7215e2e5cfa96
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59https://github.com/nuget/nuget.client
@@ -200,13 +200,13 @@
https://github.com/microsoft/vstest7855c9b221686104532ebf3380f2d45b3613b369
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -228,70 +228,70 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime5535e31a712343a63f5d7d796cd874e563e5ac14
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- e43b37de7d5916352702dda1b65d98dd66f5f9f4
+ 8506c0d82a3737ab3e7edce3976f11065bb57fd2
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- e43b37de7d5916352702dda1b65d98dd66f5f9f4
+ 8506c0d82a3737ab3e7edce3976f11065bb57fd2
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- e43b37de7d5916352702dda1b65d98dd66f5f9f4
+ 8506c0d82a3737ab3e7edce3976f11065bb57fd2
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- e43b37de7d5916352702dda1b65d98dd66f5f9f4
+ 8506c0d82a3737ab3e7edce3976f11065bb57fd2
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-wpf
- b7c5b2dd5e9b61459c7ba2e58f306029ec791c0d
+ af059f93ea0cd5b695d098162f0d9b1651adb6a3
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59https://github.com/dotnet/razor
@@ -306,21 +306,21 @@
https://github.com/dotnet/razorc937db618f8c8739c6fa66ab4ca541344a468fdc
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59https://github.com/dotnet/xdt
@@ -349,14 +349,14 @@
02fe27cd6a9b001c8feb7938e6ef4b3799745759
-
+ https://github.com/dotnet/source-build-externals
- c7cb4da26e74ef645e3e98fcb4534a7d66247a82
+ dc30cd1ec22f198d658e011c14525d4d65873991
-
+ https://github.com/dotnet/source-build-reference-packages
- f9542c50beaefc38dd9d7ec9ea38d54fd154f21a
+ d73fc552386797322e84fa9b2ef5eaa5369de83c
@@ -408,7 +408,7 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- eba546b0f0d448e0176a2222548fd7a2fbf464c0
+ 1584e493603cfc4e9b36b77d6d4afe97de6363f9
@@ -437,9 +437,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime5535e31a712343a63f5d7d796cd874e563e5ac14
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 009e1ccafde4086ea52999e878f6e7aa5a7c4ccf
+ 25ef4aa38b77974263cb10f6e9cbd10135f17b59https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -491,22 +491,22 @@
-
+ https://github.com/dotnet/arcade
- 4db725213dccb0d1102427bce1c39ba3117da7f7
+ a319ada170a54ee87c7a81e3309948e3d3ea7aca
-
+ https://github.com/dotnet/arcade
- 4db725213dccb0d1102427bce1c39ba3117da7f7
+ a319ada170a54ee87c7a81e3309948e3d3ea7aca
-
+ https://github.com/dotnet/arcade
- 4db725213dccb0d1102427bce1c39ba3117da7f7
+ a319ada170a54ee87c7a81e3309948e3d3ea7aca
-
+ https://github.com/dotnet/arcade
- 4db725213dccb0d1102427bce1c39ba3117da7f7
+ a319ada170a54ee87c7a81e3309948e3d3ea7acahttps://dev.azure.com/dnceng/internal/_git/dotnet-runtime
diff --git a/eng/Versions.props b/eng/Versions.props
index a63c47e9d8b8..2000178115a6 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -11,7 +11,7 @@
- 8.0.406
+ 8.0.4078.0.400true
@@ -36,7 +36,7 @@
8.0.04.0.08.0.0
- 8.0.0-beta.25066.6
+ 8.0.0-beta.25111.47.0.0-preview.22423.28.0.04.3.0
@@ -51,22 +51,22 @@
- 8.0.13
- 8.0.13-servicing.25066.9
- 8.0.13
+ 8.0.14
+ 8.0.14-servicing.25111.18
+ 8.0.14$(MicrosoftNETCoreAppRuntimewinx64PackageVersion)8.0.2
- 8.0.13
- 8.0.13-servicing.25066.9
+ 8.0.14
+ 8.0.14-servicing.25111.188.0.0$(MicrosoftExtensionsDependencyModelPackageVersion)8.0.18.0.38.0.1
- 8.0.13
+ 8.0.148.0.08.0.0
- 8.0.13
+ 8.0.148.0.08.0.08.0.0
@@ -115,7 +115,7 @@
- 8.3.546805
+ 8.3.607401
@@ -124,7 +124,7 @@
- 17.11.22
+ 17.11.26$(MicrosoftBuildPackageVersion)
- 8.0.406
+ 8.0.407$(MicrosoftTemplateEngineAbstractionsPackageVersion)$(MicrosoftTemplateEngineAbstractionsPackageVersion)$(MicrosoftTemplateEngineAbstractionsPackageVersion)$(MicrosoftTemplateEngineAbstractionsPackageVersion)
- 8.0.406-servicing.25066.2
+ 8.0.407-servicing.25105.9$(MicrosoftTemplateEngineMocksPackageVersion)$(MicrosoftTemplateEngineAbstractionsPackageVersion)$(MicrosoftTemplateEngineMocksPackageVersion)
@@ -169,13 +169,13 @@
- 8.0.13
- 8.0.13-servicing.25066.10
- 8.0.13-servicing.25066.10
- 8.0.13-servicing.25066.10
- 8.0.13-servicing.25066.10
- 8.0.13-servicing.25066.10
- 8.0.13
+ 8.0.14
+ 8.0.14-servicing.25112.21
+ 8.0.14-servicing.25112.21
+ 8.0.14-servicing.25112.21
+ 8.0.14-servicing.25112.21
+ 8.0.14-servicing.25112.21
+ 8.0.14
@@ -185,7 +185,7 @@
- 8.0.13-servicing.25066.5
+ 8.0.14-servicing.25113.2
@@ -213,7 +213,7 @@
6.12.06.1.0
- 8.0.0-beta.25066.6
+ 8.0.0-beta.25111.44.18.41.3.28.0.0-beta.23607.1
@@ -229,7 +229,7 @@
- 8.0.12
+ 8.0.14$(MicrosoftNETWorkloadEmscriptenCurrentManifest80100PackageVersion)8.0.100$([System.Text.RegularExpressions.Regex]::Match($(EmscriptenWorkloadManifestVersion), `-rtm|-[A-z]*\.*\d*`))
diff --git a/global.json b/global.json
index 8b18dee78510..ba40e5c7d5b5 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"tools": {
- "dotnet": "8.0.112",
+ "dotnet": "8.0.113",
"runtimes": {
"dotnet": [
"$(VSRedistCommonNetCoreSharedFrameworkx6480PackageVersion)"
@@ -14,7 +14,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.25066.6",
- "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.25066.6"
+ "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.25111.4",
+ "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.25111.4"
}
}
diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
index 8a0ae321c37f..9232b9a3790c 100644
--- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
+++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
@@ -254,7 +254,7 @@ private IEnumerable InstallWorkloadSet(ITransactionContex
PrintWorkloadSetTransition(workloadSetVersion);
var workloadSet = _workloadInstaller.InstallWorkloadSet(context, workloadSetVersion);
- return _workloadManifestUpdater.CalculateManifestUpdatesForWorkloadSet(workloadSet);
+ return workloadSet is null ? Enumerable.Empty() : _workloadManifestUpdater.CalculateManifestUpdatesForWorkloadSet(workloadSet);
}
private void PrintWorkloadSetTransition(string newVersion)
diff --git a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs
index 64d4e9a528ef..91f47193ce66 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/BuiltImage.cs
@@ -16,28 +16,43 @@ internal readonly struct BuiltImage
///
/// Gets image digest.
///
- internal required string ImageDigest { get; init; }
+ internal string? ImageDigest { get; init; }
///
/// Gets image SHA.
///
- internal required string ImageSha { get; init; }
+ internal string? ImageSha { get; init; }
///
- /// Gets image size.
+ /// Gets image manifest.
///
- internal required long ImageSize { get; init; }
+ internal required string Manifest { get; init; }
///
- /// Gets image manifest.
+ /// Gets manifest digest.
///
- internal required ManifestV2 Manifest { get; init; }
+ internal required string ManifestDigest { get; init; }
///
/// Gets manifest mediaType.
///
internal required string ManifestMediaType { get; init; }
+ ///
+ /// Gets image layers.
+ ///
+ internal List? Layers { get; init; }
+
+ ///
+ /// Gets image OS.
+ ///
+ internal string? OS { get; init; }
+
+ ///
+ /// Gets image architecture.
+ ///
+ internal string? Architecture { get; init; }
+
///
/// Gets layers descriptors.
///
@@ -45,7 +60,7 @@ internal IEnumerable LayerDescriptors
{
get
{
- List layersNode = Manifest.Layers ?? throw new NotImplementedException("Tried to get layer information but there is no layer node?");
+ List layersNode = Layers ?? throw new NotImplementedException("Tried to get layer information but there is no layer node?");
foreach (ManifestLayer layer in layersNode)
{
yield return new(layer.mediaType, layer.digest, layer.size);
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
index 48867a93de6b..665869e4a7a0 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
@@ -6,6 +6,12 @@
namespace Microsoft.NET.Build.Containers;
+internal enum KnownImageFormats
+{
+ OCI,
+ Docker
+}
+
internal static class ContainerBuilder
{
internal static async Task ContainerizeAsync(
@@ -33,6 +39,7 @@ internal static async Task ContainerizeAsync(
string? archiveOutputPath,
bool generateLabels,
bool generateDigestLabel,
+ KnownImageFormats? imageFormat,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken)
{
@@ -98,6 +105,15 @@ internal static async Task ContainerizeAsync(
logger.LogInformation(Strings.ContainerBuilder_StartBuildingImage, imageName, string.Join(",", imageName), sourceImageReference);
cancellationToken.ThrowIfCancellationRequested();
+ // forcibly change the media type if required
+ imageBuilder.ManifestMediaType = imageFormat switch
+ {
+ null => imageBuilder.ManifestMediaType,
+ KnownImageFormats.Docker => SchemaTypes.DockerManifestV2,
+ KnownImageFormats.OCI => SchemaTypes.OciManifestV1,
+ _ => imageBuilder.ManifestMediaType // should be impossible unless we add to the enum
+ };
+
Layer newLayer = Layer.FromDirectory(publishDirectory.FullName, workingDir, imageBuilder.IsWindows, imageBuilder.ManifestMediaType);
imageBuilder.AddLayer(newLayer);
imageBuilder.SetWorkingDirectory(workingDir);
diff --git a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs
index e549defd4635..a6f680436c39 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/DigestUtils.cs
@@ -17,6 +17,16 @@ internal sealed class DigestUtils
///
internal static string GetDigestFromSha(string sha) => $"sha256:{sha}";
+ internal static string GetShaFromDigest(string digest)
+ {
+ if (!digest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException($"Invalid digest '{digest}'. Digest must start with 'sha256:'.");
+ }
+
+ return digest.Substring("sha256:".Length);
+ }
+
///
/// Gets the SHA of .
///
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
index bf0876344f28..9a41e432c6a9 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
@@ -5,6 +5,7 @@
using Microsoft.NET.Build.Containers.Resources;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
+using System.Text.Json;
namespace Microsoft.NET.Build.Containers;
@@ -19,7 +20,6 @@ internal sealed class ImageBuilder
// the mutable internal manifest that we're building by modifying the base and applying customizations
private readonly ManifestV2 _manifest;
- private readonly string _manifestMediaType;
private readonly ImageConfig _baseImageConfig;
private readonly ILogger _logger;
@@ -32,15 +32,15 @@ internal sealed class ImageBuilder
public ImageConfig BaseImageConfig => _baseImageConfig;
///
- /// MediaType of the output manifest.
+ /// MediaType of the output manifest. By default, this will be the same as the base image manifest.
///
- public string ManifestMediaType => _manifestMediaType; // output the same media type as the base image manifest.
+ public string ManifestMediaType { get; set; }
internal ImageBuilder(ManifestV2 manifest, string manifestMediaType, ImageConfig baseImageConfig, ILogger logger)
{
_baseImageManifest = manifest;
_manifest = new ManifestV2() { SchemaVersion = manifest.SchemaVersion, Config = manifest.Config, Layers = new(manifest.Layers), MediaType = manifest.MediaType };
- _manifestMediaType = manifestMediaType;
+ ManifestMediaType = manifestMediaType;
_baseImageConfig = baseImageConfig;
_logger = logger;
}
@@ -70,14 +70,20 @@ internal BuiltImage Build()
ManifestConfig newManifestConfig = _manifest.Config with
{
digest = imageDigest,
- size = imageSize
+ size = imageSize,
+ mediaType = ManifestMediaType switch
+ {
+ SchemaTypes.OciManifestV1 => SchemaTypes.OciImageConfigV1,
+ SchemaTypes.DockerManifestV2 => SchemaTypes.DockerContainerV1,
+ _ => SchemaTypes.OciImageConfigV1 // opinion - defaulting to modern here, but really this should never happen
+ }
};
ManifestV2 newManifest = new ManifestV2()
{
Config = newManifestConfig,
SchemaVersion = _manifest.SchemaVersion,
- MediaType = _manifest.MediaType,
+ MediaType = ManifestMediaType,
Layers = _manifest.Layers
};
@@ -86,9 +92,10 @@ internal BuiltImage Build()
Config = imageJsonStr,
ImageDigest = imageDigest,
ImageSha = imageSha,
- ImageSize = imageSize,
- Manifest = newManifest,
- ManifestMediaType = ManifestMediaType
+ Manifest = JsonSerializer.SerializeToNode(newManifest)?.ToJsonString() ?? "",
+ ManifestDigest = newManifest.GetDigest(),
+ ManifestMediaType = ManifestMediaType,
+ Layers = _manifest.Layers
};
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs
index bc23073080ea..64a00b4236cb 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/ImageIndexGenerator.cs
@@ -1,53 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Text.Encodings.Web;
using System.Text.Json;
-using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using Microsoft.NET.Build.Containers.Resources;
-using Microsoft.NET.Build.Containers.Tasks;
namespace Microsoft.NET.Build.Containers;
-internal readonly struct ImageInfo
-{
- internal string Config { get; init; }
- internal string ManifestDigest { get; init; }
- internal string Manifest { get; init; }
- internal string ManifestMediaType { get; init; }
-
- public override string ToString() => ManifestDigest;
-}
-
internal static class ImageIndexGenerator
{
///
/// Generates an image index from the given images.
///
- ///
+ /// Images to generate image index from.
/// Returns json string of image index and image index mediaType.
///
///
- internal static (string, string) GenerateImageIndex(ImageInfo[] imageInfos)
+ internal static (string, string) GenerateImageIndex(BuiltImage[] images)
{
- if (imageInfos.Length == 0)
+ if (images.Length == 0)
{
- throw new ArgumentException(string.Format(Strings.ImagesEmpty));
+ throw new ArgumentException(Strings.ImagesEmpty);
}
- string manifestMediaType = imageInfos[0].ManifestMediaType;
+ string manifestMediaType = images[0].ManifestMediaType;
- if (!imageInfos.All(image => string.Equals(image.ManifestMediaType, manifestMediaType, StringComparison.OrdinalIgnoreCase)))
+ if (!images.All(image => string.Equals(image.ManifestMediaType, manifestMediaType, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(Strings.MixedMediaTypes);
}
if (manifestMediaType == SchemaTypes.DockerManifestV2)
{
- return GenerateImageIndex(imageInfos, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2);
+ return (GenerateImageIndex(images, SchemaTypes.DockerManifestV2, SchemaTypes.DockerManifestListV2), SchemaTypes.DockerManifestListV2);
}
else if (manifestMediaType == SchemaTypes.OciManifestV1)
{
- return GenerateImageIndex(imageInfos, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1);
+ return (GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1), SchemaTypes.OciImageIndexV1);
}
else
{
@@ -55,46 +45,94 @@ internal static (string, string) GenerateImageIndex(ImageInfo[] imageInfos)
}
}
- private static (string, string) GenerateImageIndex(ImageInfo[] images, string manifestMediaType, string imageIndexMediaType)
+ ///
+ /// Generates an image index from the given images.
+ ///
+ /// Images to generate image index from.
+ /// Media type of the manifest.
+ /// Media type of the produced image index.
+ /// Returns json string of image index and image index mediaType.
+ ///
+ ///
+ internal static string GenerateImageIndex(BuiltImage[] images, string manifestMediaType, string imageIndexMediaType)
{
+ if (images.Length == 0)
+ {
+ throw new ArgumentException(Strings.ImagesEmpty);
+ }
+
// Here we are using ManifestListV2 struct, but we could use ImageIndexV1 struct as well.
- // We are filling the same fiels, so we can use the same struct.
+ // We are filling the same fields, so we can use the same struct.
var manifests = new PlatformSpecificManifest[images.Length];
+
for (int i = 0; i < images.Length; i++)
{
- var image = images[i];
-
- var manifest = new PlatformSpecificManifest
+ manifests[i] = new PlatformSpecificManifest
{
mediaType = manifestMediaType,
- size = image.Manifest.Length,
- digest = image.ManifestDigest,
- platform = GetArchitectureAndOsFromConfig(image)
+ size = images[i].Manifest.Length,
+ digest = images[i].ManifestDigest,
+ platform = new PlatformInformation
+ {
+ architecture = images[i].Architecture!,
+ os = images[i].OS!
+ }
};
- manifests[i] = manifest;
}
- var dockerManifestList = new ManifestListV2
+ var imageIndex = new ManifestListV2
{
schemaVersion = 2,
mediaType = imageIndexMediaType,
manifests = manifests
};
- return (JsonSerializer.SerializeToNode(dockerManifestList)?.ToJsonString() ?? "", dockerManifestList.mediaType);
+ return GetJsonStringFromImageIndex(imageIndex);
}
- private static PlatformInformation GetArchitectureAndOsFromConfig(ImageInfo image)
+ internal static string GenerateImageIndexWithAnnotations(string manifestMediaType, string manifestDigest, long manifestSize, string repository, string[] tags)
{
- var configJson = JsonNode.Parse(image.Config) as JsonObject ??
- throw new ArgumentException($"{nameof(image.Config)} should be a JSON object.", nameof(image.Config));
+ string containerdImageNamePrefix = repository.Contains('/') ? "docker.io/" : "docker.io/library/";
+
+ var manifests = new PlatformSpecificOciManifest[tags.Length];
+ for (int i = 0; i < tags.Length; i++)
+ {
+ var tag = tags[i];
+ manifests[i] = new PlatformSpecificOciManifest
+ {
+ mediaType = manifestMediaType,
+ size = manifestSize,
+ digest = manifestDigest,
+ annotations = new Dictionary
+ {
+ { "io.containerd.image.name", $"{containerdImageNamePrefix}{repository}:{tag}" },
+ { "org.opencontainers.image.ref.name", tag }
+ }
+ };
+ }
- var architecture = configJson["architecture"]?.ToString() ??
- throw new ArgumentException($"{nameof(image.Config)} should contain 'architecture'.", nameof(image.Config));
+ var index = new ImageIndexV1
+ {
+ schemaVersion = 2,
+ mediaType = SchemaTypes.OciImageIndexV1,
+ manifests = manifests
+ };
- var os = configJson["os"]?.ToString() ??
- throw new ArgumentException($"{nameof(image.Config)} should contain 'os'.", nameof(image.Config));
+ return GetJsonStringFromImageIndex(index);
+ }
+
+ private static string GetJsonStringFromImageIndex(T imageIndex)
+ {
+ var nullIgnoreOptions = new JsonSerializerOptions
+ {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ };
+ // To avoid things like \u002B for '+' especially in media types ("application/vnd.oci.image.manifest.v1\u002Bjson"), we use UnsafeRelaxedJsonEscaping.
+ var escapeOptions = new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ };
- return new PlatformInformation { architecture = architecture, os = os };
+ return JsonSerializer.SerializeToNode(imageIndex, nullIgnoreOptions)?.ToJsonString(escapeOptions) ?? "";
}
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs
new file mode 100644
index 000000000000..1a16cbbad500
--- /dev/null
+++ b/src/Containers/Microsoft.NET.Build.Containers/ImagePublisher.cs
@@ -0,0 +1,162 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Build.Framework;
+using Microsoft.NET.Build.Containers.Resources;
+
+namespace Microsoft.NET.Build.Containers;
+
+internal static class ImagePublisher
+{
+ public static async Task PublishImageAsync(
+ BuiltImage singleArchImage,
+ SourceImageReference sourceImageReference,
+ DestinationImageReference destinationImageReference,
+ Microsoft.Build.Utilities.TaskLoggingHelper Log,
+ Telemetry telemetry,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ switch (destinationImageReference.Kind)
+ {
+ case DestinationImageReferenceKind.LocalRegistry:
+ await PushToLocalRegistryAsync(
+ singleArchImage,
+ sourceImageReference,
+ destinationImageReference,
+ Log,
+ telemetry,
+ cancellationToken,
+ destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false);
+ break;
+ case DestinationImageReferenceKind.RemoteRegistry:
+ await PushToRemoteRegistryAsync(
+ singleArchImage,
+ sourceImageReference,
+ destinationImageReference,
+ Log,
+ cancellationToken,
+ destinationImageReference.RemoteRegistry!.PushAsync,
+ Strings.ContainerBuilder_ImageUploadedToRegistry).ConfigureAwait(false);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ telemetry.LogPublishSuccess();
+ }
+
+ public static async Task PublishImageAsync(
+ MultiArchImage multiArchImage,
+ SourceImageReference sourceImageReference,
+ DestinationImageReference destinationImageReference,
+ Microsoft.Build.Utilities.TaskLoggingHelper Log,
+ Telemetry telemetry,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ switch (destinationImageReference.Kind)
+ {
+ case DestinationImageReferenceKind.LocalRegistry:
+ await PushToLocalRegistryAsync(
+ multiArchImage,
+ sourceImageReference,
+ destinationImageReference,
+ Log,
+ telemetry,
+ cancellationToken,
+ destinationImageReference.LocalRegistry!.LoadAsync).ConfigureAwait(false);
+ break;
+ case DestinationImageReferenceKind.RemoteRegistry:
+ await PushToRemoteRegistryAsync(
+ multiArchImage,
+ sourceImageReference,
+ destinationImageReference,
+ Log,
+ cancellationToken,
+ destinationImageReference.RemoteRegistry!.PushManifestListAsync,
+ Strings.ImageIndexUploadedToRegistry).ConfigureAwait(false);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ telemetry.LogPublishSuccess();
+ }
+
+ private static async Task PushToLocalRegistryAsync(
+ T image,
+ SourceImageReference sourceImageReference,
+ DestinationImageReference destinationImageReference,
+ Microsoft.Build.Utilities.TaskLoggingHelper Log,
+ Telemetry telemetry,
+ CancellationToken cancellationToken,
+ Func loadFunc)
+ {
+ ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!;
+ if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false)))
+ {
+ telemetry.LogMissingLocalBinary();
+ Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable));
+ return;
+ }
+ try
+ {
+ await loadFunc(image, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false);
+ Log.LogMessage(MessageImportance.High, Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry);
+ }
+ catch (ContainerHttpException e)
+ {
+ Log.LogErrorFromException(e, true);
+ }
+ catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle)
+ {
+ telemetry.LogLocalLoadError();
+ Log.LogErrorFromException(dle, showStackTrace: false);
+ }
+ catch (ArgumentException argEx)
+ {
+ Log.LogErrorFromException(argEx, showStackTrace: false);
+ }
+ catch (DockerLoadException dle)
+ {
+ telemetry.LogLocalLoadError();
+ Log.LogErrorFromException(dle, showStackTrace: false);
+ }
+ }
+
+ private static async Task PushToRemoteRegistryAsync(
+ T image,
+ SourceImageReference sourceImageReference,
+ DestinationImageReference destinationImageReference,
+ Microsoft.Build.Utilities.TaskLoggingHelper Log,
+ CancellationToken cancellationToken,
+ Func pushFunc,
+ string successMessage)
+ {
+ try
+ {
+ await pushFunc(
+ image,
+ sourceImageReference,
+ destinationImageReference,
+ cancellationToken).ConfigureAwait(false);
+ Log.LogMessage(MessageImportance.High, successMessage, destinationImageReference, destinationImageReference.RemoteRegistry!.RegistryName);
+ }
+ catch (UnableToAccessRepositoryException)
+ {
+ Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName);
+ }
+ catch (ContainerHttpException e)
+ {
+ Log.LogErrorFromException(e, true);
+ }
+ catch (Exception e)
+ {
+ Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message);
+ Log.LogMessage(MessageImportance.Low, "Details: {0}", e);
+ }
+ }
+}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs
index a12d15790d38..d127635ba584 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ArchiveFileRegistry.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.Extensions.Logging;
using Microsoft.NET.Build.Containers.Resources;
namespace Microsoft.NET.Build.Containers.LocalDaemons;
@@ -15,14 +14,22 @@ public ArchiveFileRegistry(string archiveOutputPath)
ArchiveOutputPath = archiveOutputPath;
}
- public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference,
- DestinationImageReference destinationReference,
- CancellationToken cancellationToken)
+ internal async Task LoadAsync(T image, SourceImageReference sourceReference,
+ DestinationImageReference destinationReference, CancellationToken cancellationToken,
+ Func writeStreamFunc)
{
var fullPath = Path.GetFullPath(ArchiveOutputPath);
+ var directorySeparatorChar = Path.DirectorySeparatorChar;
+
+ // if doesn't end with a file extension, assume it's a directory
+ if (!Path.HasExtension(fullPath))
+ {
+ fullPath += Path.DirectorySeparatorChar;
+ }
+
// pointing to a directory? -> append default name
- if (Directory.Exists(fullPath) || ArchiveOutputPath.EndsWith("/") || ArchiveOutputPath.EndsWith("\\"))
+ if (fullPath.EndsWith(directorySeparatorChar))
{
fullPath = Path.Combine(fullPath, destinationReference.Repository + ".tar.gz");
}
@@ -36,19 +43,27 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen
ArchiveOutputPath = fullPath;
await using var fileStream = File.Create(fullPath);
- await DockerCli.WriteImageToStreamAsync(
- image,
- sourceReference,
- destinationReference,
- fileStream,
- cancellationToken).ConfigureAwait(false);
+
+ // Call the delegate to write the image to the stream
+ await writeStreamFunc(image, sourceReference, destinationReference, fileStream, cancellationToken).ConfigureAwait(false);
}
+ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference,
+ DestinationImageReference destinationReference,
+ CancellationToken cancellationToken)
+ => await LoadAsync(image, sourceReference, destinationReference, cancellationToken,
+ DockerCli.WriteImageToStreamAsync);
+
+ public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference,
+ DestinationImageReference destinationReference,
+ CancellationToken cancellationToken)
+ => await LoadAsync(multiArchImage, sourceReference, destinationReference, cancellationToken,
+ DockerCli.WriteMultiArchOciImageToStreamAsync);
+
public Task IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true);
public bool IsAvailable() => true;
-
public override string ToString()
{
return string.Format(Strings.ArchiveRegistry_PushInfo, ArchiveOutputPath);
diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs
index 7e779e9ec3c0..5bdb7f40c8a4 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs
@@ -82,10 +82,21 @@ private async ValueTask FindFullCommandPath(CancellationToken cancellati
return _fullCommandPath;
}
- public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken)
+ private async Task LoadAsync(
+ T image,
+ SourceImageReference sourceReference,
+ DestinationImageReference destinationReference,
+ Func writeStreamFunc,
+ CancellationToken cancellationToken,
+ bool checkContainerdStore = false)
{
cancellationToken.ThrowIfCancellationRequested();
+ if (checkContainerdStore && !IsContainerdStoreEnabledForDocker())
+ {
+ throw new DockerLoadException(Strings.ImageLoadFailed_ContainerdStoreDisabled);
+ }
+
string commandPath = await FindFullCommandPath(cancellationToken);
// call `docker load` and get it ready to receive input
@@ -94,16 +105,12 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen
loadInfo.RedirectStandardOutput = true;
loadInfo.RedirectStandardError = true;
- using Process? loadProcess = Process.Start(loadInfo);
-
- if (loadProcess is null)
- {
+ using Process? loadProcess = Process.Start(loadInfo) ??
throw new NotImplementedException(Resource.FormatString(Strings.ContainerRuntimeProcessCreationFailed, commandPath));
- }
- // Create new stream tarball
- // We want to be able to export to docker, even oci images.
- await WriteDockerImageToStreamAsync(image, sourceReference, destinationReference, loadProcess.StandardInput.BaseStream, cancellationToken).ConfigureAwait(false);
+ // Call the delegate to write the image to the stream
+ await writeStreamFunc(image, sourceReference, destinationReference, loadProcess.StandardInput.BaseStream, cancellationToken)
+ .ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -119,6 +126,13 @@ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReferen
}
}
+ public async Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken)
+ // For loading to the local registry, we use the Docker format. Two reasons: one - compatibility with previous behavior before oci formatted publishing was available, two - Podman cannot load multi tag oci image tarball.
+ => await LoadAsync(image, sourceReference, destinationReference, WriteDockerImageToStreamAsync, cancellationToken);
+
+ public async Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken)
+ => await LoadAsync(multiArchImage, sourceReference, destinationReference, WriteMultiArchOciImageToStreamAsync, cancellationToken, checkContainerdStore: true);
+
public async Task IsAvailableAsync(CancellationToken cancellationToken)
{
bool commandPathWasUnknown = this._command is null; // avoid running the version command twice.
@@ -279,7 +293,7 @@ public static async Task WriteImageToStreamAsync(BuiltImage image, SourceImageRe
}
else
{
- throw new ArgumentException(Resource.FormatString(nameof(Strings.UnsupportedMediaTypeForTarball), image.Manifest.MediaType));
+ throw new ArgumentException(Resource.FormatString(nameof(Strings.UnsupportedMediaTypeForTarball), image.ManifestMediaType));
}
}
@@ -297,7 +311,7 @@ private static async Task WriteDockerImageToStreamAsync(
await WriteImageLayers(writer, image, sourceReference, d => $"{d.Substring("sha256:".Length)}/layer.tar", cancellationToken, layerTarballPaths)
.ConfigureAwait(false);
- string configTarballPath = $"{image.ImageSha}.json";
+ string configTarballPath = $"{image.ImageSha!}.json";
await WriteImageConfig(writer, image, configTarballPath, cancellationToken)
.ConfigureAwait(false);
@@ -396,24 +410,17 @@ private static async Task WriteOciImageToStreamAsync(
Stream imageStream,
CancellationToken cancellationToken)
{
- if (destinationReference.Tags.Length > 1)
- {
- throw new ArgumentException(Resource.FormatString(nameof(Strings.OciImageMultipleTagsNotSupported)));
- }
-
cancellationToken.ThrowIfCancellationRequested();
+
using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true);
- await WriteOciLayout(writer, cancellationToken)
+ await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken)
.ConfigureAwait(false);
- await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken)
+ await WriteIndexJsonForOciImage(writer, image, destinationReference, cancellationToken)
.ConfigureAwait(false);
- await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha}", cancellationToken)
- .ConfigureAwait(false);
-
- await WriteManifestForOciImage(writer, image, destinationReference, cancellationToken)
+ await WriteOciLayout(writer, cancellationToken)
.ConfigureAwait(false);
}
@@ -436,17 +443,12 @@ private static async Task WriteOciLayout(TarWriter writer, CancellationToken can
private static async Task WriteManifestForOciImage(
TarWriter writer,
BuiltImage image,
- DestinationImageReference destinationReference,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- string manifestContent = JsonSerializer.SerializeToNode(image.Manifest)!.ToJsonString();
- string manifestDigest = image.Manifest.GetDigest();
-
- // 1. add manifest to blobs
- string manifestPath = $"{_blobsPath}/{manifestDigest.Substring("sha256:".Length)}";
- using (MemoryStream manifestStream = new MemoryStream(Encoding.UTF8.GetBytes(manifestContent)))
+ string manifestPath = $"{_blobsPath}/{image.ManifestDigest.Substring("sha256:".Length)}";
+ using (MemoryStream manifestStream = new MemoryStream(Encoding.UTF8.GetBytes(image.Manifest)))
{
PaxTarEntry manifestEntry = new(TarEntryType.RegularFile, manifestPath)
{
@@ -454,26 +456,106 @@ private static async Task WriteManifestForOciImage(
};
await writer.WriteEntryAsync(manifestEntry, cancellationToken).ConfigureAwait(false);
}
+ }
+ private static async Task WriteIndexJsonForOciImage(
+ TarWriter writer,
+ BuiltImage image,
+ DestinationImageReference destinationReference,
+ CancellationToken cancellationToken)
+ {
cancellationToken.ThrowIfCancellationRequested();
- // 2. add index.json
- var index = new ImageIndexV1
+ string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations(
+ SchemaTypes.OciManifestV1,
+ image.ManifestDigest,
+ image.Manifest.Length,
+ destinationReference.Repository,
+ destinationReference.Tags);
+
+ using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson)))
{
- schemaVersion = 2,
- mediaType = SchemaTypes.OciImageIndexV1,
- manifests =
- [
- new PlatformSpecificOciManifest
- {
- mediaType = SchemaTypes.OciManifestV1,
- size = manifestContent.Length,
- digest = manifestDigest,
- annotations = new Dictionary { { "org.opencontainers.image.ref.name", $"{destinationReference.Repository}:{destinationReference.Tags[0]}" } }
- }
- ]
- };
- using (MemoryStream indexStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.SerializeToNode(index)!.ToJsonString())))
+ PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json")
+ {
+ DataStream = indexStream
+ };
+ await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private static async Task WriteOciImageToBlobs(
+ TarWriter writer,
+ BuiltImage image,
+ SourceImageReference sourceReference,
+ CancellationToken cancellationToken)
+ {
+ await WriteImageLayers(writer, image, sourceReference, d => $"{_blobsPath}/{d.Substring("sha256:".Length)}", cancellationToken)
+ .ConfigureAwait(false);
+
+ await WriteImageConfig(writer, image, $"{_blobsPath}/{image.ImageSha!}", cancellationToken)
+ .ConfigureAwait(false);
+
+ await WriteManifestForOciImage(writer, image, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ public static async Task WriteMultiArchOciImageToStreamAsync(
+ MultiArchImage multiArchImage,
+ SourceImageReference sourceReference,
+ DestinationImageReference destinationReference,
+ Stream imageStream,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using TarWriter writer = new(imageStream, TarEntryFormat.Pax, leaveOpen: true);
+
+ foreach (var image in multiArchImage.Images!)
+ {
+ await WriteOciImageToBlobs(writer, image, sourceReference, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ await WriteIndexJsonForMultiArchOciImage(writer, multiArchImage, destinationReference, cancellationToken)
+ .ConfigureAwait(false);
+
+ await WriteOciLayout(writer, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ private static async Task WriteIndexJsonForMultiArchOciImage(
+ TarWriter writer,
+ MultiArchImage multiArchImage,
+ DestinationImageReference destinationReference,
+ CancellationToken cancellationToken)
+ {
+ // 1. create manifest list for the blobs
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var manifestListDigest = DigestUtils.GetDigest(multiArchImage.ImageIndex);
+ var manifestListSha = DigestUtils.GetShaFromDigest(manifestListDigest);
+ var manifestListPath = $"{_blobsPath}/{manifestListSha}";
+
+ using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(multiArchImage.ImageIndex)))
+ {
+ PaxTarEntry indexEntry = new(TarEntryType.RegularFile, manifestListPath)
+ {
+ DataStream = indexStream
+ };
+ await writer.WriteEntryAsync(indexEntry, cancellationToken).ConfigureAwait(false);
+ }
+
+ // 2. create index.json that points to manifest list in the blobs
+ cancellationToken.ThrowIfCancellationRequested();
+
+ string indexJson = ImageIndexGenerator.GenerateImageIndexWithAnnotations(
+ multiArchImage.ImageIndexMediaType,
+ manifestListDigest,
+ multiArchImage.ImageIndex.Length,
+ destinationReference.Repository,
+ destinationReference.Tags);
+
+ using (MemoryStream indexStream = new(Encoding.UTF8.GetBytes(indexJson)))
{
PaxTarEntry indexEntry = new(TarEntryType.RegularFile, "index.json")
{
@@ -540,6 +622,37 @@ private static bool IsPodmanAlias()
}
}
+ internal static bool IsContainerdStoreEnabledForDocker()
+ {
+ try
+ {
+ // We don't need to check if this is docker, because there is no "DriverStatus" for podman
+ if (!GetDockerConfig().RootElement.TryGetProperty("DriverStatus", out var driverStatus) || driverStatus.ValueKind != JsonValueKind.Array)
+ {
+ return false;
+ }
+
+ foreach (var item in driverStatus.EnumerateArray())
+ {
+ if (item.ValueKind != JsonValueKind.Array || item.GetArrayLength() != 2) continue;
+
+ var array = item.EnumerateArray().ToArray();
+ // The usual output is [driver-type io.containerd.snapshotter.v1]
+ if (array[0].GetString() == "driver-type" && array[1].GetString()!.StartsWith("io.containerd.snapshotter"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+
#if NET
private async Task TryRunVersionCommandAsync(string command, CancellationToken cancellationToken)
{
diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs
index c06150fe2425..c4ee04a7f5d6 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/ILocalRegistry.cs
@@ -13,6 +13,11 @@ internal interface ILocalRegistry {
///
public Task LoadAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken);
+ ///
+ /// Loads a multi-arch image (presumably from a tarball) into the local registry.
+ ///
+ public Task LoadAsync(MultiArchImage multiArchImage, SourceImageReference sourceReference, DestinationImageReference destinationReference, CancellationToken cancellationToken);
+
///
/// Checks to see if the local registry is available. This is used to give nice errors to the user.
///
diff --git a/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs
new file mode 100644
index 000000000000..b24fbf65e87f
--- /dev/null
+++ b/src/Containers/Microsoft.NET.Build.Containers/MultiArchImage.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Containers;
+
+///
+/// Represents constructed image ready for further processing.
+///
+internal readonly struct MultiArchImage
+{
+ internal required string ImageIndex { get; init; }
+
+ internal required string ImageIndexMediaType { get; init; }
+
+ internal BuiltImage[]? Images { get; init; }
+}
\ No newline at end of file
diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
index 43526e9c3ab4..4da41db6b2dc 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
+++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
@@ -86,8 +86,12 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.get -> string?
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.set -> void
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ToolName.get -> string!
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateCommandLineCommands() -> string!
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateFullPathToTool() -> string!
diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
index b19e4fa81718..3370734680b1 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
+++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
@@ -19,6 +19,18 @@ Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UserBaseImage.
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseRegistry.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseRegistry.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageName.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.BaseImageTag.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.ArchiveOutputPath.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.LocalRegistry.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.LocalRegistry.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath.get -> string!
+Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.GeneratedArchiveOutputPath.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Cancel() -> void
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.CreateImageIndex() -> void
Microsoft.NET.Build.Containers.Tasks.CreateImageIndex.Dispose() -> void
@@ -239,8 +251,12 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateLabels.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.get -> string?
+Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.set -> void
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ContainerEnvironmentVariables.get -> Microsoft.Build.Framework.ITaskItem![]!
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ContainerEnvironmentVariables.set -> void
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
index f09cfd6fd4a3..1b01ebd5bdc4 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
@@ -3,11 +3,9 @@
using Microsoft.Extensions.Logging;
using Microsoft.NET.Build.Containers.Resources;
-using NuGet.Packaging;
using NuGet.RuntimeModel;
using System.Diagnostics;
using System.Net.Http.Json;
-using System.Text.Json;
using System.Text.Json.Nodes;
namespace Microsoft.NET.Build.Containers;
@@ -527,13 +525,17 @@ private async Task UploadBlobAsync(string repository, string digest, Stream cont
}
- public async Task PushManifestListAsync(string repositoryName, string[] tags, string manifestListJson, string mediaType, CancellationToken cancellationToken)
+ public async Task PushManifestListAsync(
+ MultiArchImage multiArchImage,
+ SourceImageReference sourceImageReference,
+ DestinationImageReference destinationImageReference,
+ CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- foreach (var tag in tags)
+ foreach (var tag in destinationImageReference.Tags)
{
_logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName);
- await _registryAPI.Manifest.PutAsync(repositoryName, tag, manifestListJson, mediaType, cancellationToken).ConfigureAwait(false);
+ await _registryAPI.Manifest.PutAsync(destinationImageReference.Repository, tag, multiArchImage.ImageIndex, multiArchImage.ImageIndexMediaType, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
}
}
@@ -593,7 +595,7 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source,
cancellationToken.ThrowIfCancellationRequested();
using (MemoryStream stringStream = new MemoryStream(Encoding.UTF8.GetBytes(builtImage.Config)))
{
- var configDigest = builtImage.ImageDigest;
+ var configDigest = builtImage.ImageDigest!;
_logger.LogInformation(Strings.Registry_ConfigUploadStarted, configDigest);
await UploadBlobAsync(destination.Repository, configDigest, stringStream, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_ConfigUploaded);
@@ -602,22 +604,20 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source,
// Tags can refer to an image manifest or an image manifest list.
// In the first case, we push tags to the registry.
// In the second case, we push the manifest digest so the manifest list can refer to it.
- string manifestJson = JsonSerializer.SerializeToNode(builtImage.Manifest)?.ToJsonString() ?? "";
if (pushTags)
{
Debug.Assert(destination.Tags.Length > 0);
foreach (string tag in destination.Tags)
{
_logger.LogInformation(Strings.Registry_TagUploadStarted, tag, RegistryName);
- await _registryAPI.Manifest.PutAsync(destination.Repository, tag, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
+ await _registryAPI.Manifest.PutAsync(destination.Repository, tag, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
}
}
else
{
- string manifestDigest = builtImage.Manifest.GetDigest();
- _logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, manifestDigest);
- await _registryAPI.Manifest.PutAsync(destination.Repository, manifestDigest, manifestJson, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, builtImage.ManifestDigest);
+ await _registryAPI.Manifest.PutAsync(destination.Repository, builtImage.ManifestDigest, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_ManifestUploaded, RegistryName);
}
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs
index 8021f652f72d..f7ae91a19e8b 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/SchemaTypes.cs
@@ -9,8 +9,11 @@ internal class SchemaTypes
internal const string DockerContainerV1 = "application/vnd.docker.container.image.v1+json";
internal const string DockerManifestListV2 = "application/vnd.docker.distribution.manifest.list.v2+json";
internal const string DockerManifestV2 = "application/vnd.docker.distribution.manifest.v2+json";
+
internal const string OciManifestV1 = "application/vnd.oci.image.manifest.v1+json"; // https://containers.gitbook.io/build-containers-the-hard-way/#registry-format-oci-image-manifest
internal const string OciImageIndexV1 = "application/vnd.oci.image.index.v1+json";
+ internal const string OciImageConfigV1 = "application/vnd.oci.image.config.v1+json";
+
internal const string DockerLayerGzip = "application/vnd.docker.image.rootfs.diff.tar.gzip";
internal const string OciLayerGzipV1 = "application/vnd.oci.image.layer.v1.tar+gzip";
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs
index aff92942f408..ec4966caaa27 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.Designer.cs
@@ -10,8 +10,8 @@
namespace Microsoft.NET.Build.Containers.Resources {
using System;
-
-
+
+
///
/// A strongly-typed resource class, for looking up localized strings, etc.
///
@@ -23,15 +23,15 @@ namespace Microsoft.NET.Build.Containers.Resources {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Strings {
-
+
private static global::System.Resources.ResourceManager resourceMan;
-
+
private static global::System.Globalization.CultureInfo resourceCulture;
-
+
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Strings() {
}
-
+
///
/// Returns the cached ResourceManager instance used by this class.
///
@@ -45,7 +45,7 @@ internal Strings() {
return resourceMan;
}
}
-
+
///
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
@@ -59,7 +59,7 @@ internal Strings() {
resourceCulture = value;
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER0000: Value for unit test {0}.
///
@@ -68,7 +68,7 @@ internal static string _Test {
return ResourceManager.GetString("_Test", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1002: Request to Amazon Elastic Container Registry failed prematurely. This is often caused when the target repository does not exist in the registry..
///
@@ -77,7 +77,7 @@ internal static string AmazonRegistryFailed {
return ResourceManager.GetString("AmazonRegistryFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2008: Both {0} and {1} were provided, but only one or the other is allowed..
///
@@ -86,7 +86,7 @@ internal static string AmbiguousTags {
return ResourceManager.GetString("AmbiguousTags", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2025: ContainerAppCommandArgs are provided without specifying a ContainerAppCommand..
///
@@ -95,7 +95,7 @@ internal static string AppCommandArgsSetNoAppCommand {
return ResourceManager.GetString("AppCommandArgsSetNoAppCommand", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2026: ContainerAppCommand and ContainerAppCommandArgs must be empty when ContainerAppCommandInstruction is '{0}'..
///
@@ -104,7 +104,7 @@ internal static string AppCommandSetNotUsed {
return ResourceManager.GetString("AppCommandSetNotUsed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to local archive at '{0}'.
///
@@ -113,7 +113,7 @@ internal static string ArchiveRegistry_PushInfo {
return ResourceManager.GetString("ArchiveRegistry_PushInfo", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2022: The base image has an entrypoint that will be overwritten to start the application. Set ContainerAppCommandInstruction to 'Entrypoint' if this is desired. To preserve the base image entrypoint, set ContainerAppCommandInstruction to 'DefaultArgs'..
///
@@ -122,7 +122,7 @@ internal static string BaseEntrypointOverwritten {
return ResourceManager.GetString("BaseEntrypointOverwritten", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2009: Could not parse {0}: {1}.
///
@@ -131,7 +131,7 @@ internal static string BaseImageNameParsingFailed {
return ResourceManager.GetString("BaseImageNameParsingFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2020: {0} does not specify a registry and will be pulled from Docker Hub. Please prefix the name with the image registry, for example: '{1}/<image>'..
///
@@ -140,7 +140,7 @@ internal static string BaseImageNameRegistryFallback {
return ResourceManager.GetString("BaseImageNameRegistryFallback", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2013: {0} had spaces in it, replacing with dashes..
///
@@ -149,7 +149,7 @@ internal static string BaseImageNameWithSpaces {
return ResourceManager.GetString("BaseImageNameWithSpaces", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1011: Couldn't find matching base image for {0} that matches RuntimeIdentifier {1}..
///
@@ -158,7 +158,7 @@ internal static string BaseImageNotFound {
return ResourceManager.GetString("BaseImageNotFound", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1001: Failed to upload blob using {0}; received status code '{1}'..
///
@@ -167,7 +167,7 @@ internal static string BlobUploadFailed {
return ResourceManager.GetString("BlobUploadFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Building image index '{0}' on top of manifests {1}..
///
@@ -176,7 +176,7 @@ internal static string BuildingImageIndex {
return ResourceManager.GetString("BuildingImageIndex", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Pushed image '{0}' to {1}..
///
@@ -185,7 +185,7 @@ internal static string ContainerBuilder_ImageUploadedToLocalDaemon {
return ResourceManager.GetString("ContainerBuilder_ImageUploadedToLocalDaemon", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Pushed image '{0}' to registry '{1}'..
///
@@ -194,7 +194,7 @@ internal static string ContainerBuilder_ImageUploadedToRegistry {
return ResourceManager.GetString("ContainerBuilder_ImageUploadedToRegistry", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Building image '{0}' with tags '{1}' on top of base image '{2}'..
///
@@ -203,7 +203,16 @@ internal static string ContainerBuilder_StartBuildingImage {
return ResourceManager.GetString("ContainerBuilder_StartBuildingImage", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'..
+ ///
+ internal static string ContainerBuilder_StartBuildingImageForRid {
+ get {
+ return ResourceManager.GetString("ContainerBuilder_StartBuildingImageForRid", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to CONTAINER3001: Failed creating {0} process..
///
@@ -212,7 +221,7 @@ internal static string ContainerRuntimeProcessCreationFailed {
return ResourceManager.GetString("ContainerRuntimeProcessCreationFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1007: Could not deserialize token from JSON..
///
@@ -221,7 +230,7 @@ internal static string CouldntDeserializeJsonToken {
return ResourceManager.GetString("CouldntDeserializeJsonToken", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2012: Could not recognize registry '{0}'..
///
@@ -230,7 +239,7 @@ internal static string CouldntRecognizeRegistry {
return ResourceManager.GetString("CouldntRecognizeRegistry", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to local registry via '{0}'.
///
@@ -239,7 +248,7 @@ internal static string DockerCli_PushInfo {
return ResourceManager.GetString("DockerCli_PushInfo", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER3002: Failed to get docker info({0})\n{1}\n{2}.
///
@@ -248,7 +257,7 @@ internal static string DockerInfoFailed {
return ResourceManager.GetString("DockerInfoFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER3002: Failed to get docker info: {0}.
///
@@ -257,7 +266,7 @@ internal static string DockerInfoFailed_Ex {
return ResourceManager.GetString("DockerInfoFailed_Ex", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4006: Property '{0}' is empty or contains whitespace and will be ignored..
///
@@ -266,7 +275,7 @@ internal static string EmptyOrWhitespacePropertyIgnored {
return ResourceManager.GetString("EmptyOrWhitespacePropertyIgnored", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4004: Items '{0}' contain empty item(s) which will be ignored..
///
@@ -275,7 +284,7 @@ internal static string EmptyValuesIgnored {
return ResourceManager.GetString("EmptyValuesIgnored", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2023: A ContainerEntrypoint and ContainerAppCommandArgs are provided. ContainerAppInstruction must be set to configure how the application is started. Valid instructions are {0}..
///
@@ -284,7 +293,7 @@ internal static string EntrypointAndAppCommandArgsSetNoAppCommandInstruction {
return ResourceManager.GetString("EntrypointAndAppCommandArgsSetNoAppCommandInstruction", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2024: ContainerEntrypointArgs are provided without specifying a ContainerEntrypoint..
///
@@ -293,7 +302,7 @@ internal static string EntrypointArgsSetNoEntrypoint {
return ResourceManager.GetString("EntrypointArgsSetNoEntrypoint", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2029: ContainerEntrypointArgsSet are provided. Change to use ContainerAppCommandArgs for arguments that must always be set, or ContainerDefaultArgs for arguments that can be overridden when the container is created..
///
@@ -302,7 +311,7 @@ internal static string EntrypointArgsSetPreferAppCommandArgs {
return ResourceManager.GetString("EntrypointArgsSetPreferAppCommandArgs", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2028: ContainerEntrypoint can not be combined with ContainerAppCommandInstruction '{0}'..
///
@@ -311,7 +320,7 @@ internal static string EntrypointConflictAppCommand {
return ResourceManager.GetString("EntrypointConflictAppCommand", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2027: A ContainerEntrypoint is provided. ContainerAppInstruction must be set to configure how the application is started. Valid instructions are {0}..
///
@@ -320,7 +329,7 @@ internal static string EntrypointSetNoAppCommandInstruction {
return ResourceManager.GetString("EntrypointSetNoAppCommandInstruction", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1008: Failed retrieving credentials for "{0}": {1}.
///
@@ -329,7 +338,7 @@ internal static string FailedRetrievingCredentials {
return ResourceManager.GetString("FailedRetrievingCredentials", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2030: GenerateLabels was disabled but GenerateDigestLabel was enabled - no digest label will be created..
///
@@ -338,7 +347,7 @@ internal static string GenerateDigestLabelWithoutGenerateLabels {
return ResourceManager.GetString("GenerateDigestLabelWithoutGenerateLabels", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to No host object detected..
///
@@ -347,7 +356,7 @@ internal static string HostObjectNotDetected {
return ResourceManager.GetString("HostObjectNotDetected", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Pushed image index '{0}' to registry '{1}'..
///
@@ -356,7 +365,7 @@ internal static string ImageIndexUploadedToRegistry {
return ResourceManager.GetString("ImageIndexUploadedToRegistry", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1009: Failed to load image from local registry. stdout: {0}.
///
@@ -365,7 +374,7 @@ internal static string ImageLoadFailed {
return ResourceManager.GetString("ImageLoadFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1010: Pulling images from local registry is not supported..
///
@@ -374,16 +383,25 @@ internal static string ImagePullNotSupported {
return ResourceManager.GetString("ImagePullNotSupported", resourceCulture);
}
}
-
+
///
- /// Looks up a localized string similar to Cannot create manifest list (image index) because no images were provided..
+ /// Looks up a localized string similar to Cannot create image index because no images were provided..
///
internal static string ImagesEmpty {
get {
return ResourceManager.GetString("ImagesEmpty", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'..
+ ///
+ internal static string InvalidContainerImageFormat {
+ get {
+ return ResourceManager.GetString("InvalidContainerImageFormat", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring..
///
@@ -392,16 +410,70 @@ internal static string InvalidEnvVar {
return ResourceManager.GetString("InvalidEnvVar", resourceCulture);
}
}
-
+
///
- /// Looks up a localized string similar to Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata..
+ /// Looks up a localized string similar to Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata..
///
internal static string InvalidImageMetadata {
get {
return ResourceManager.GetString("InvalidImageMetadata", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Cannot create image index because at least one of the provided images manifest is invalid..
+ ///
+ internal static string InvalidImageManifest {
+ get {
+ return ResourceManager.GetString("InvalidImageManifest", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create image index because at least one of the provided images config is invalid..
+ ///
+ internal static string InvalidImageConfig {
+ get {
+ return ResourceManager.GetString("InvalidImageConfig", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create image index because at least one of the provided images' config is missing 'architecture'..
+ ///
+ internal static string ImageConfigMissingArchitecture {
+ get {
+ return ResourceManager.GetString("ImageConfigMissingArchitecture", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create image index because at least one of the provided images' config is missing 'os'..
+ ///
+ internal static string ImageConfigMissingOs {
+ get {
+ return ResourceManager.GetString("ImageConfigMissingOs", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Image index creation for Podman is not supported..
+ ///
+ internal static string ImageIndex_PodmanNotSupported {
+ get {
+ return ResourceManager.GetString("ImageIndex_PodmanNotSupported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings..
+ ///
+ internal static string ImageLoadFailed_ContainerdStoreDisabled {
+ get {
+ return ResourceManager.GetString("ImageLoadFailed_ContainerdStoreDisabled", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to CONTAINER2005: The inferred image name '{0}' contains entirely invalid characters. The valid characters for an image name are alphanumeric characters, -, /, or _, and the image name must start with an alphanumeric character..
///
@@ -410,7 +482,7 @@ internal static string InvalidImageName_EntireNameIsInvalidCharacters {
return ResourceManager.GetString("InvalidImageName_EntireNameIsInvalidCharacters", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2005: The first character of the image name '{0}' must be a lowercase letter or a digit and all characters in the name must be an alphanumeric character, -, /, or _..
///
@@ -419,7 +491,7 @@ internal static string InvalidImageName_NonAlphanumericStartCharacter {
return ResourceManager.GetString("InvalidImageName_NonAlphanumericStartCharacter", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port number '{0}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'..
///
@@ -428,7 +500,7 @@ internal static string InvalidPort_Number {
return ResourceManager.GetString("InvalidPort_Number", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port number '{0}' and an invalid port type '{1}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'..
///
@@ -437,7 +509,7 @@ internal static string InvalidPort_NumberAndType {
return ResourceManager.GetString("InvalidPort_NumberAndType", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2017: A ContainerPort item was provided with an invalid port type '{0}'. ContainerPort items must have an Include value that is an integer, and a Type value that is either 'tcp' or 'udp'..
///
@@ -446,7 +518,7 @@ internal static string InvalidPort_Type {
return ResourceManager.GetString("InvalidPort_Type", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2018: Invalid SDK prerelease version '{0}' - only 'rc' and 'preview' are supported..
///
@@ -455,7 +527,7 @@ internal static string InvalidSdkPrereleaseVersion {
return ResourceManager.GetString("InvalidSdkPrereleaseVersion", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2019: Invalid SDK semantic version '{0}'..
///
@@ -464,7 +536,7 @@ internal static string InvalidSdkVersion {
return ResourceManager.GetString("InvalidSdkVersion", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2007: Invalid {0} provided: {1}. Image tags must be alphanumeric, underscore, hyphen, or period..
///
@@ -473,7 +545,7 @@ internal static string InvalidTag {
return ResourceManager.GetString("InvalidTag", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2010: Invalid {0} provided: {1}. {0} must be a semicolon-delimited list of valid image tags. Image tags must be alphanumeric, underscore, hyphen, or period..
///
@@ -482,7 +554,7 @@ internal static string InvalidTags {
return ResourceManager.GetString("InvalidTags", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Invalid string[] TargetRuntimeIdentifiers. Either all should be 'linux-musl' or none..
///
@@ -491,7 +563,7 @@ internal static string InvalidTargetRuntimeIdentifiers {
return ResourceManager.GetString("InvalidTargetRuntimeIdentifiers", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1003: Token response had neither token nor access_token..
///
@@ -500,7 +572,7 @@ internal static string InvalidTokenResponse {
return ResourceManager.GetString("InvalidTokenResponse", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4005: Item '{0}' contains items without metadata 'Value', and they will be ignored..
///
@@ -509,7 +581,7 @@ internal static string ItemsWithoutMetadata {
return ResourceManager.GetString("ItemsWithoutMetadata", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Error while reading daemon config: {0}.
///
@@ -518,7 +590,7 @@ internal static string LocalDocker_FailedToGetConfig {
return ResourceManager.GetString("LocalDocker_FailedToGetConfig", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to The daemon server reported errors: {0}.
///
@@ -527,7 +599,7 @@ internal static string LocalDocker_LocalDaemonErrors {
return ResourceManager.GetString("LocalDocker_LocalDaemonErrors", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1012: The local registry is not available, but pushing to a local registry was requested..
///
@@ -536,7 +608,7 @@ internal static string LocalRegistryNotAvailable {
return ResourceManager.GetString("LocalRegistryNotAvailable", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2004: Unable to download layer with descriptor '{0}' from registry '{1}' because it does not exist..
///
@@ -545,7 +617,7 @@ internal static string MissingLinkToRegistry {
return ResourceManager.GetString("MissingLinkToRegistry", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2016: ContainerPort item '{0}' does not specify the port number. Please ensure the item's Include is a port number, for example '<ContainerPort Include="80" />'.
///
@@ -554,16 +626,16 @@ internal static string MissingPortNumber {
return ResourceManager.GetString("MissingPortNumber", resourceCulture);
}
}
-
+
///
- /// Looks up a localized string similar to 'mediaType' of manifests should be the same in manifest list (image index)..
+ /// Looks up a localized string similar to 'mediaType' of manifests should be the same in image index..
///
internal static string MixedMediaTypes {
get {
return ResourceManager.GetString("MixedMediaTypes", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1004: No RequestUri specified..
///
@@ -572,7 +644,7 @@ internal static string NoRequestUriSpecified {
return ResourceManager.GetString("NoRequestUriSpecified", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to '{0}' was not a valid container image name, it was normalized to '{1}'.
///
@@ -581,7 +653,8 @@ internal static string NormalizedContainerName {
return ResourceManager.GetString("NormalizedContainerName", resourceCulture);
}
}
-
+
+
///
/// Looks up a localized string similar to Unable to create tarball for oci image with multiple tags..
///
@@ -590,7 +663,7 @@ internal static string OciImageMultipleTagsNotSupported {
return ResourceManager.GetString("OciImageMultipleTagsNotSupported", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2011: {0} '{1}' does not exist.
///
@@ -599,7 +672,7 @@ internal static string PublishDirectoryDoesntExist {
return ResourceManager.GetString("PublishDirectoryDoesntExist", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploaded config to registry..
///
@@ -608,7 +681,7 @@ internal static string Registry_ConfigUploaded {
return ResourceManager.GetString("Registry_ConfigUploaded", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploading config to registry at blob '{0}',.
///
@@ -617,7 +690,7 @@ internal static string Registry_ConfigUploadStarted {
return ResourceManager.GetString("Registry_ConfigUploadStarted", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Layer '{0}' already exists..
///
@@ -626,7 +699,7 @@ internal static string Registry_LayerExists {
return ResourceManager.GetString("Registry_LayerExists", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Finished uploading layer '{0}' to '{1}'..
///
@@ -635,7 +708,7 @@ internal static string Registry_LayerUploaded {
return ResourceManager.GetString("Registry_LayerUploaded", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploading layer '{0}' to '{1}'..
///
@@ -644,7 +717,7 @@ internal static string Registry_LayerUploadStarted {
return ResourceManager.GetString("Registry_LayerUploadStarted", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploaded manifest to '{0}'..
///
@@ -653,7 +726,7 @@ internal static string Registry_ManifestUploaded {
return ResourceManager.GetString("Registry_ManifestUploaded", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploading manifest to registry '{0}' as blob '{1}'..
///
@@ -662,7 +735,7 @@ internal static string Registry_ManifestUploadStarted {
return ResourceManager.GetString("Registry_ManifestUploadStarted", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploaded tag '{0}' to '{1}'..
///
@@ -671,7 +744,7 @@ internal static string Registry_TagUploaded {
return ResourceManager.GetString("Registry_TagUploaded", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Uploading tag '{0}' to '{1}'..
///
@@ -680,7 +753,7 @@ internal static string Registry_TagUploadStarted {
return ResourceManager.GetString("Registry_TagUploadStarted", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1017: Unable to communicate with the registry '{0}'..
///
@@ -689,7 +762,7 @@ internal static string RegistryOperationFailed {
return ResourceManager.GetString("RegistryOperationFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1013: Failed to push to the output registry: {0}.
///
@@ -698,7 +771,7 @@ internal static string RegistryOutputPushFailed {
return ResourceManager.GetString("RegistryOutputPushFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1014: Manifest pull failed..
///
@@ -707,7 +780,7 @@ internal static string RegistryPullFailed {
return ResourceManager.GetString("RegistryPullFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1005: Registry push failed; received status code '{0}'..
///
@@ -716,7 +789,7 @@ internal static string RegistryPushFailed {
return ResourceManager.GetString("RegistryPushFailed", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1015: Unable to access the repository '{0}' at tag '{1}' in the registry '{2}'. Please confirm that this name and tag are present in the registry..
///
@@ -725,7 +798,7 @@ internal static string RepositoryNotFound {
return ResourceManager.GetString("RepositoryNotFound", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4003: Required '{0}' items contain empty items..
///
@@ -734,7 +807,7 @@ internal static string RequiredItemsContainsEmptyItems {
return ResourceManager.GetString("RequiredItemsContainsEmptyItems", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4002: Required '{0}' items were not set..
///
@@ -743,7 +816,7 @@ internal static string RequiredItemsNotSet {
return ResourceManager.GetString("RequiredItemsNotSet", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER4001: Required property '{0}' was not set or empty..
///
@@ -752,7 +825,7 @@ internal static string RequiredPropertyNotSetOrEmpty {
return ResourceManager.GetString("RequiredPropertyNotSetOrEmpty", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1006: Too many retries, stopping..
///
@@ -761,7 +834,7 @@ internal static string TooManyRetries {
return ResourceManager.GetString("TooManyRetries", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER1016: Unable to access the repository '{0}' in the registry '{1}'. Please confirm your credentials are correct and that you have access to this repository and registry..
///
@@ -770,7 +843,7 @@ internal static string UnableToAccessRepository {
return ResourceManager.GetString("UnableToAccessRepository", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2021: Unknown AppCommandInstruction '{0}'. Valid instructions are {1}..
///
@@ -779,7 +852,7 @@ internal static string UnknownAppCommandInstruction {
return ResourceManager.GetString("UnknownAppCommandInstruction", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2002: Unknown local registry type '{0}'. Valid local container registry types are {1}..
///
@@ -788,7 +861,7 @@ internal static string UnknownLocalRegistryType {
return ResourceManager.GetString("UnknownLocalRegistryType", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2003: The manifest for {0}:{1} from registry {2} was an unknown type: {3}. Please raise an issue at https://github.com/dotnet/sdk-container-builds/issues with this message..
///
@@ -797,7 +870,7 @@ internal static string UnknownMediaType {
return ResourceManager.GetString("UnknownMediaType", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to CONTAINER2001: Unrecognized mediaType '{0}'..
///
@@ -806,16 +879,16 @@ internal static string UnrecognizedMediaType {
return ResourceManager.GetString("UnrecognizedMediaType", resourceCulture);
}
}
-
+
///
- /// Looks up a localized string similar to Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'..
+ /// Looks up a localized string similar to Cannot create image index for the provided 'mediaType' = '{0}'..
///
internal static string UnsupportedMediaType {
get {
return ResourceManager.GetString("UnsupportedMediaType", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Unable to create tarball for mediaType '{0}'..
///
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx
index 6337f4861b8e..b0e7350da710 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/Strings.resx
@@ -189,6 +189,10 @@
CONTAINER1009: Failed to load image from local registry. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+
CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.{StrBegin="CONTAINER2015: "}
@@ -244,9 +248,6 @@
CONTAINER2004: Unable to download layer with descriptor '{0}' from registry '{1}' because it does not exist.{StrBegin="CONTAINER2004: "}
-
- Unable to create tarball for oci image with multiple tags.
-
Unable to create tarball for mediaType '{0}'.
@@ -358,6 +359,10 @@
Building image '{0}' with tags '{1}' on top of base image '{2}'.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+
Building image index '{0}' on top of manifests {1}.
@@ -365,19 +370,39 @@
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+
+
+ Image index creation for Podman is not supported.
@@ -456,4 +481,8 @@
CONTAINER2030: GenerateLabels was disabled but GenerateDigestLabel was enabled - no digest label will be created.{StrBegin="CONTAINER2030: "}
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf
index f0d7173a9cea..383c7346ddca 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.cs.xlf
@@ -79,6 +79,11 @@
Sestavení image {0} se značkami {1} nad základní imagí {2}
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: Nepovedlo se deserializovat token z JSON.
@@ -159,34 +164,69 @@
Nebyl zjištěn žádný objekt hostitele.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: Nepodařilo se načíst bitovou kopii z místního registru. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: Načítání imagí z místního registru se nepodporuje.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' není platná proměnná prostředí. Ignorování.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' nebyl platný název image kontejneru, byl normalizován na '{1}'
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' neexistuje.
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf
index ea8fc611e0a0..f273669dca6c 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.de.xlf
@@ -79,6 +79,11 @@
Das Image „{0}“ mit den Tags „{1}“ wird auf dem Basisimage „{2}“ erstellt.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: Das Token konnte nicht aus JSON deserialisiert werden.
@@ -159,34 +164,69 @@
Es wurde kein Hostobjekt erkannt.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: Fehler beim Laden des Images aus der lokalen Registrierung. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: Das Pullen von Images aus der lokalen Registrierung wird nicht unterstützt.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: „{1}“ war keine gültige Umgebungsvariable. Sie wird ignoriert.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
„{0}“ war kein gültiger Containerimagename, er wurde zu „{1}“ normalisiert.
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} „{1}“ ist nicht vorhanden.
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf
index b4921dcbdf47..fb785d4e4f7d 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.es.xlf
@@ -79,6 +79,11 @@
Compilando imagen "{0}" con etiquetas "{1}" encima de la imagen base "{2}".
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: No se pudo deserializar el token de JSON.
@@ -159,34 +164,69 @@
No se detectó ningún objeto host.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: no se pudo cargar la imagen desde el registro local. Stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: No se admite la extracción de imágenes del registro local.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: "{1}" no era una variable de entorno válida. Ignorando.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
"{0}" no era un nombre de imagen de contenedor válido, se normalizó a "{1}"
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} "{1}" no existe
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf
index b945393a8398..ff593f5316cb 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.fr.xlf
@@ -79,6 +79,11 @@
Génération de l’image «{0}» avec des balises «{1}» au-dessus de l’image de base «{2}».
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: impossible de désérialiser le jeton à partir de JSON.
@@ -159,34 +164,69 @@
Aucun objet hôte détecté.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: Échec du chargement de l'image à partir du registre local. sortie standard : {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: L'extraction d'images à partir du registre local n'est pas prise en charge.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0} : '{1}' n’était pas une variable d’environnement valide. Ignorant.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' n’était pas un nom d’image conteneur valide, il a été normalisé pour '{1}'
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' n’existe pas
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf
index b42327bbfb94..0a8f8a977150 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.it.xlf
@@ -79,6 +79,11 @@
Compilazione dell'immagine '{0}' con i tag '{1}' sopra l'immagine di base '{2}'.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: non è stato possibile deserializzare il token da JSON.
@@ -159,34 +164,69 @@
Nessun oggetto host rilevato.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: non è stato possibile caricare l'immagine dal registro locale. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: il pull di immagini dal registro locale non è supportato.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' non è una variabile di ambiente valida. Il valore verrà ignorato.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' non è un nome di immagine contenitore valido, è stato normalizzato in '{1}'
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' non esiste
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf
index 5d2cf22f0f8c..6cf8d131368c 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ja.xlf
@@ -79,6 +79,11 @@
基本イメージ '{0}' の上にタグ '{1}' 付きのイメージ '{2}' をビルドしています。
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: JSON からトークンを逆シリアル化できませんでした。
@@ -159,34 +164,69 @@
ホスト オブジェクトが検出されませんでした。
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: ローカル レジストリからイメージを読み込めませんでした。stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: ローカル レジストリからのイメージのプルはサポートされていません。{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' は有効な環境変数ではありませんでした。無視しています。{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' は有効なコンテナー イメージ名ではありませんでした。'{1}' に正規化されました
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' が存在しません
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf
index 2ff624cec676..77de4d142dd3 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ko.xlf
@@ -79,6 +79,11 @@
기본 이미지 '{2}' 위에 '{1}' 태그가 있는 '{0}' 이미지를 빌드하는 중입니다.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: JSON에서 토큰을 역직렬화할 수 없습니다.
@@ -159,34 +164,69 @@
호스트 개체가 검색되지 않았습니다.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: 로컬 레지스트리에서 이미지를 로드하지 못했습니다. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: 로컬 레지스트리에서 이미지 끌어오기가 지원되지 않습니다.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}'은(는) 유효한 환경 변수가 아닙니다. 무시 중.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}'은(는) 유효한 컨테이너 이미지 이름이 아닙니다. '{1}'(으)로 정규화되었습니다.
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}'이(가) 존재하지 않습니다.
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf
index 86288597ee7c..6eeb96f72d6d 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pl.xlf
@@ -79,6 +79,11 @@
Tworzenie obrazu „{0}” z tagami „{1}” nad obrazem podstawowym „{2}”.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: nie można zdeserializować tokenu z pliku JSON.
@@ -159,34 +164,69 @@
Nie wykryto obiektu hosta.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: Nie można załadować obrazu z rejestru lokalnego. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: Ściąganie obrazów z rejestru lokalnego nie jest obsługiwane.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: „{1}” nie jest prawidłową zmienną środowiskową. Ignorowanie.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
Nazwa „{0}” nie była prawidłową nazwą obrazu kontenera, została znormalizowana do „{1}”
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} „{1}” nie istnieje
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf
index 69fcf3548d8d..9686e1444a08 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.pt-BR.xlf
@@ -79,6 +79,11 @@
Construindo imagem '{0}' com tags '{1}' em cima da imagem base '{2}'.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: não foi possível desserializar o token do JSON.
@@ -159,34 +164,69 @@
Nenhum objeto de host detectado.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: falha ao carregar a imagem do registro local. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: A extração de imagens do registro local não é suportada.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' não era uma variável de ambiente válida. Ignorando.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' não era um nome de imagem de contêiner válido, foi normalizado para '{1}'
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' não existe
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf
index 2989bc8e9687..8b942db5bfef 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.ru.xlf
@@ -79,6 +79,11 @@
Выполняется сборка образа "{0}" с тегами "{1}" поверх базового образа "{2}".
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: не удалось десериализовать токен из JSON.
@@ -159,34 +164,69 @@
Объект узла не обнаружен.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: не удалось загрузить образ из локального реестра. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: извлечение образов из локального реестра не поддерживается.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: "{1}" не является допустимой переменной среды. Пропуск.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
"{0}" не является допустимым именем образа контейнера, оно нормализовано до "{1}"
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} "{1}" не существует
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf
index 42fcb52485c7..146196fba1bc 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.tr.xlf
@@ -79,6 +79,11 @@
'{1}' etiketli '{0}' resmi, '{2}' temel görüntüsünün üzerinde oluşturuluyor.
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: Belirteç, JSON'dan seri durumdan çıkarılamadı.
@@ -159,34 +164,69 @@
Ana bilgisayar nesnesi algılanmadı.
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: Görüntü yerel kayıt defterinden yüklenemedi. stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: Yerel kayıt defterinden görüntü çekme desteklenmiyor.{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' geçerli bir Ortam Değişkeni değildi. Görmezden geliniyor.{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}', geçerli bir kapsayıcı görüntüsü adı değildi, '{1}' olarak normalleştirildi
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' yok
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf
index 8eb6429f7db6..ab3a5f1b5993 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hans.xlf
@@ -79,6 +79,11 @@
在基本映像“{2}”顶部构建标记为“{1}”的映像“{0}”。
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: 无法从 JSON 反序列化令牌。
@@ -159,34 +164,69 @@
未检测到主机对象。
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: 未能从本地注册表加载映像。stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: 不支持从本地注册表拉取映像。{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: "{1}" 不是有效的环境变量。忽略。{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
“{0}”不是有效的容器映像名称,已规范化为“{1}”
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} {1} 不存在
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf
index 892b174bbe97..9a144ff7f80b 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Containers/Microsoft.NET.Build.Containers/Resources/xlf/Strings.zh-Hant.xlf
@@ -79,6 +79,11 @@
在基礎映像 '{2}' 上建立具有標記 '{1}' 的映像 '{0}'。
+
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+ Building image '{0}' for runtime identifier '{1}' on top of base image '{2}'.
+
+ CONTAINER1007: Could not deserialize token from JSON.CONTAINER1007: 無法從 JSON 還原序列化權杖。
@@ -159,34 +164,69 @@
未偵測到主機物件。
+
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+ Cannot create image index because at least one of the provided images' config is missing 'architecture'.
+
+
+
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+ Cannot create image index because at least one of the provided images' config is missing 'os'.
+
+ Pushed image index '{0}' to registry '{1}'.Pushed image index '{0}' to registry '{1}'.
+
+ Image index creation for Podman is not supported.
+ Image index creation for Podman is not supported.
+
+ CONTAINER1009: Failed to load image from local registry. stdout: {0}CONTAINER1009: 無法從本機登錄載入映像。stdout: {0}{StrBegin="CONTAINER1009: "}
+
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ CONTAINER1020: Failed to load image because containerd image store is not enabled for Docker. Tip: You can enable it by checking 'Use containerd for pulling and storing images' in Docker Desktop settings.
+ {StrBegin="CONTAINER1020: "}
+ CONTAINER1010: Pulling images from local registry is not supported.CONTAINER1010: 不支援從本機登錄提取映像。{StrBegin="CONTAINER1010: "}
- Cannot create manifest list (image index) because no images were provided.
- Cannot create manifest list (image index) because no images were provided.
+ Cannot create image index because no images were provided.
+ Cannot create image index because no images were provided.
+
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ CONTAINER2031: The container image format '{0}' is not supported. Supported formats are '{1}'.
+ {StrBegins="CONTAINER2031: "}
+ CONTAINER2015: {0}: '{1}' was not a valid Environment Variable. Ignoring.CONTAINER2015: {0}: '{1}' 不是有效的環境變數。正在忽略。{StrBegin="CONTAINER2015: "}
+
+ Cannot create image index because at least one of the provided images' config is invalid.
+ Cannot create image index because at least one of the provided images' config is invalid.
+
+
+
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+ Cannot create image index because at least one of the provided images' manifest is invalid.
+
+
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
- Cannot create manifest list (image index) because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
+ Cannot create image index because provided images are invalid. Items must have 'Config', 'Manifest', 'ManifestMediaType' and 'ManifestDigest' metadata.
@@ -275,8 +315,8 @@
{StrBegin="CONTAINER2016: "}
- 'mediaType' of manifests should be the same in manifest list (image index).
- 'mediaType' of manifests should be the same in manifest list (image index).
+ 'mediaType' of manifests should be the same in image index.
+ 'mediaType' of manifests should be the same in image index.
@@ -289,11 +329,6 @@
'{0}' 不是有效的容器映像名稱,已標準化為 '{1}'
-
- Unable to create tarball for oci image with multiple tags.
- Unable to create tarball for oci image with multiple tags.
-
- CONTAINER2011: {0} '{1}' does not existCONTAINER2011: {0} '{1}' 不存在
@@ -415,8 +450,8 @@
{StrBegin="CONTAINER2001: "}
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
- Cannot create manifest list (image index) for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
+ Cannot create image index for the provided 'mediaType' = '{0}'.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs
new file mode 100644
index 000000000000..bb69651add0e
--- /dev/null
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.Interface.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Build.Framework;
+using Microsoft.NET.Build.Containers.Resources;
+
+namespace Microsoft.NET.Build.Containers.Tasks;
+
+partial class CreateImageIndex
+{
+ ///
+ /// The base registry to pull from.
+ /// Ex: mcr.microsoft.com
+ ///
+ public string BaseRegistry { get; set; }
+
+ ///
+ /// The base image to pull.
+ /// Ex: dotnet/runtime
+ ///
+ [Required]
+ public string BaseImageName { get; set; }
+
+ ///
+ /// The base image tag.
+ /// Ex: 6.0
+ ///
+ [Required]
+ public string BaseImageTag { get; set; }
+
+ ///
+ /// Manifests to include in the image index.
+ ///
+ [Required]
+ public ITaskItem[] GeneratedContainers { get; set; }
+
+ ///
+ /// The registry to push the image index to.
+ ///
+ public string OutputRegistry { get; set; }
+
+ ///
+ /// The file path to which to write a tar.gz archive of the container image.
+ ///
+ public string ArchiveOutputPath { get; set; }
+
+ ///
+ /// The kind of local registry to use, if any.
+ ///
+ public string LocalRegistry { get; set; }
+
+ ///
+ /// The name of the output image index (manifest list) that will be pushed to the registry.
+ ///
+ [Required]
+ public string Repository { get; set; }
+
+ ///
+ /// The tag to associate with the new image index (manifest list).
+ ///
+ [Required]
+ public string[] ImageTags { get; set; }
+
+ ///
+ /// The generated archive output path.
+ ///
+ [Output]
+ public string GeneratedArchiveOutputPath { get; set; }
+
+ ///
+ /// The generated image index (manifest list) in JSON format.
+ ///
+ [Output]
+ public string GeneratedImageIndex { get; set; }
+
+ public CreateImageIndex()
+ {
+ BaseRegistry = string.Empty;
+ BaseImageName = string.Empty;
+ BaseImageTag = string.Empty;
+ GeneratedContainers = Array.Empty();
+ OutputRegistry = string.Empty;
+ ArchiveOutputPath = string.Empty;
+ LocalRegistry = string.Empty;
+ Repository = string.Empty;
+ ImageTags = Array.Empty();
+ GeneratedArchiveOutputPath = string.Empty;
+ GeneratedImageIndex = string.Empty;
+
+ TaskResources = Resource.Manager;
+ }
+}
\ No newline at end of file
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs
index 0894686603d2..25658f612e06 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateImageIndex.cs
@@ -1,7 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Diagnostics;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using Microsoft.Build.Framework;
using Microsoft.Extensions.Logging;
using Microsoft.NET.Build.Containers.Logging;
@@ -11,49 +12,8 @@
namespace Microsoft.NET.Build.Containers.Tasks;
-public sealed class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelableTask, IDisposable
+public sealed partial class CreateImageIndex : Microsoft.Build.Utilities.Task, ICancelableTask, IDisposable
{
- #region Parameters
- ///
- /// Manifests to include in the image index.
- ///
- [Required]
- public ITaskItem[] GeneratedContainers { get; set; }
-
- ///
- /// The registry to push the image index to.
- ///
- [Required]
- public string OutputRegistry { get; set; }
-
- ///
- /// The name of the output image index (manifest list) that will be pushed to the registry.
- ///
- [Required]
- public string Repository { get; set; }
-
- ///
- /// The tag to associate with the new image index (manifest list).
- ///
- [Required]
- public string[] ImageTags { get; set; }
-
- ///
- /// The generated image index (manifest list) in JSON format.
- ///
- [Output]
- public string GeneratedImageIndex { get; set; }
-
- public CreateImageIndex()
- {
- GeneratedContainers = Array.Empty();
- OutputRegistry = string.Empty;
- Repository = string.Empty;
- ImageTags = Array.Empty();
- GeneratedImageIndex = string.Empty;
- }
- #endregion
-
private readonly CancellationTokenSource _cancellationTokenSource = new();
public void Cancel() => _cancellationTokenSource.Cancel();
@@ -63,6 +23,8 @@ public void Dispose()
_cancellationTokenSource.Dispose();
}
+ private bool IsLocalPull => string.IsNullOrWhiteSpace(BaseRegistry);
+
public override bool Execute()
{
try
@@ -84,9 +46,9 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- var images = ParseImages();
- if (Log.HasLoggedErrors)
+ if (LocalRegistry == "Podman")
{
+ Log.LogError(Strings.ImageIndex_PodmanNotSupported);
return false;
}
@@ -94,34 +56,42 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
ILoggerFactory msbuildLoggerFactory = new LoggerFactory(new[] { loggerProvider });
ILogger logger = msbuildLoggerFactory.CreateLogger();
- logger.LogInformation(Strings.BuildingImageIndex, GetRepositoryAndTagsString(), string.Join(", ", images.Select(i => i.ManifestDigest)));
+ RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull;
+ Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode);
+ SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag);
- try
- {
- (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
+ DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings(
+ Repository,
+ ImageTags,
+ msbuildLoggerFactory,
+ ArchiveOutputPath,
+ OutputRegistry,
+ LocalRegistry);
- GeneratedImageIndex = imageIndex;
-
- await PushToRemoteRegistry(GeneratedImageIndex, mediaType, logger, cancellationToken);
- }
- catch (ContainerHttpException e)
- {
- if (BuildEngine != null)
- {
- Log.LogErrorFromException(e, true);
- }
- }
- catch (ArgumentException ex)
+ var images = ParseImages(destinationImageReference.Kind);
+ if (Log.HasLoggedErrors)
{
- Log.LogErrorFromException(ex);
+ return false;
}
+ var multiArchImage = CreateMultiArchImage(images, destinationImageReference.Kind);
+
+ GeneratedImageIndex = multiArchImage.ImageIndex;
+ GeneratedArchiveOutputPath = ArchiveOutputPath;
+
+ logger.LogInformation(Strings.BuildingImageIndex, destinationImageReference, string.Join(", ", images.Select(i => i.ManifestDigest)));
+
+ var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log);
+
+ await ImagePublisher.PublishImageAsync(multiArchImage, sourceImageReference, destinationImageReference, Log, telemetry, cancellationToken)
+ .ConfigureAwait(false);
+
return !Log.HasLoggedErrors;
}
- private ImageInfo[] ParseImages()
+ private BuiltImage[] ParseImages(DestinationImageReferenceKind destinationKind)
{
- var images = new ImageInfo[GeneratedContainers.Length];
+ var images = new BuiltImage[GeneratedContainers.Length];
for (int i = 0; i < GeneratedContainers.Length; i++)
{
@@ -132,38 +102,95 @@ private ImageInfo[] ParseImages()
string manifest = unparsedImage.GetMetadata("Manifest");
string manifestMediaType = unparsedImage.GetMetadata("ManifestMediaType");
- if (string.IsNullOrEmpty(config) || string.IsNullOrEmpty(manifestDigest) || string.IsNullOrEmpty(manifest))
+ if (string.IsNullOrEmpty(config) || string.IsNullOrEmpty(manifestDigest) || string.IsNullOrEmpty(manifest) || string.IsNullOrEmpty(manifestMediaType))
{
- Log.LogError(Strings.InvalidImageMetadata, unparsedImage.ItemSpec);
+ Log.LogError(Strings.InvalidImageMetadata);
break;
}
- images[i] = new ImageInfo
+ (string architecture, string os) = GetArchitectureAndOsFromConfig(config);
+
+ // We don't need ImageDigest, ImageSha, Layers for remote registry, as the individual images should be pushed already
+ string? imageDigest = null;
+ string? imageSha = null;
+ List? layers = null;
+
+ if (destinationKind == DestinationImageReferenceKind.LocalRegistry)
+ {
+ var manifestV2 = JsonSerializer.Deserialize(manifest);
+ if (manifestV2 == null)
+ {
+ Log.LogError(Strings.InvalidImageManifest);
+ break;
+ }
+
+ imageDigest = manifestV2.Config.digest;
+ imageSha = DigestUtils.GetShaFromDigest(imageDigest);
+ layers = manifestV2.Layers;
+ }
+
+ images[i] = new BuiltImage()
{
Config = config,
- ManifestDigest = manifestDigest,
+ ImageDigest = imageDigest,
+ ImageSha = imageSha,
Manifest = manifest,
- ManifestMediaType = manifestMediaType
+ ManifestDigest = manifestDigest,
+ ManifestMediaType = manifestMediaType,
+ Layers = layers,
+ OS = os,
+ Architecture = architecture
};
}
return images;
}
- private async Task PushToRemoteRegistry(string manifestList, string mediaType, ILogger logger, CancellationToken cancellationToken)
+ private (string, string) GetArchitectureAndOsFromConfig(string config)
{
- cancellationToken.ThrowIfCancellationRequested();
- Debug.Assert(ImageTags.Length > 0);
- var registry = new Registry(OutputRegistry, logger, RegistryMode.Push);
- await registry.PushManifestListAsync(Repository, ImageTags, manifestList, mediaType, cancellationToken).ConfigureAwait(false);
- logger.LogInformation(Strings.ImageIndexUploadedToRegistry, GetRepositoryAndTagsString(), OutputRegistry);
+ var configJson = JsonNode.Parse(config) as JsonObject;
+ if (configJson is null)
+ {
+ Log.LogError(Strings.InvalidImageConfig);
+ return (string.Empty, string.Empty);
+ }
+ var architecture = configJson["architecture"]?.ToString();
+ if (architecture is null)
+ {
+ Log.LogError(Strings.ImageConfigMissingArchitecture);
+ return (string.Empty, string.Empty);
+ }
+ var os = configJson["os"]?.ToString();
+ if (os is null)
+ {
+ Log.LogError(Strings.ImageConfigMissingOs);
+ return (string.Empty, string.Empty);
+ }
+ return (architecture, os);
}
- private string? _repositoryAndTagsString = null;
-
- private string GetRepositoryAndTagsString()
+ private static MultiArchImage CreateMultiArchImage(BuiltImage[] images, DestinationImageReferenceKind destinationImageKind)
{
- _repositoryAndTagsString ??= $"{Repository}:{string.Join(", ", ImageTags)}";
- return _repositoryAndTagsString;
+ switch (destinationImageKind)
+ {
+ case DestinationImageReferenceKind.LocalRegistry:
+ return new MultiArchImage()
+ {
+ // For multi-arch we publish only oci-formatted image tarballs.
+ ImageIndex = ImageIndexGenerator.GenerateImageIndex(images, SchemaTypes.OciManifestV1, SchemaTypes.OciImageIndexV1),
+ ImageIndexMediaType = SchemaTypes.OciImageIndexV1,
+ Images = images
+ };
+ case DestinationImageReferenceKind.RemoteRegistry:
+ (string imageIndex, string mediaType) = ImageIndexGenerator.GenerateImageIndex(images);
+ return new MultiArchImage()
+ {
+ ImageIndex = imageIndex,
+ ImageIndexMediaType = mediaType,
+ // For remote registry we don't need individual images, as they should be pushed already
+ };
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
}
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs
index 3da5135b9b07..5ce5a7286fce 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs
@@ -20,7 +20,6 @@ partial class CreateNewImage
/// The base registry to pull from.
/// Ex: mcr.microsoft.com
///
- [Required]
public string BaseRegistry { get; set; }
///
@@ -161,6 +160,15 @@ partial class CreateNewImage
[Required]
public bool GenerateDigestLabel { get; set; }
+ ///
+ /// Set to either 'OCI', 'Docker', or null. If unset, the generated images' mediaType will be that of the base image. If set, the generated image will be given the specified media type.
+ ///
+ public string? ImageFormat { get; set; }
+
+ /// If true, the tooling will skip the publishing step.
+ ///
+ public bool SkipPublishing { get; set; }
+
[Output]
public string GeneratedContainerManifest { get; set; }
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
index 25ba26080af9..b0f4899f75e8 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
@@ -1,10 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Text.Json;
using Microsoft.Build.Framework;
using Microsoft.Extensions.Logging;
-using Microsoft.NET.Build.Containers.LocalDaemons;
using Microsoft.NET.Build.Containers.Logging;
using Microsoft.NET.Build.Containers.Resources;
using ILogger = Microsoft.Extensions.Logging.ILogger;
@@ -25,7 +23,7 @@ public sealed partial class CreateNewImage : Microsoft.Build.Utilities.Task, ICa
///
public string ToolPath { get; set; }
- private bool IsLocalPull => string.IsNullOrEmpty(BaseRegistry);
+ private bool IsLocalPull => string.IsNullOrWhiteSpace(BaseRegistry);
public void Cancel() => _cancellationTokenSource.Cancel();
@@ -72,7 +70,7 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
OutputRegistry,
LocalRegistry);
- var telemetry = CreateTelemetryContext(sourceImageReference, destinationImageReference);
+ var telemetry = new Telemetry(sourceImageReference, destinationImageReference, Log);
ImageBuilder? imageBuilder;
if (sourceRegistry is { } registry)
@@ -122,7 +120,28 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
return !Log.HasLoggedErrors;
}
- SafeLog(Strings.ContainerBuilder_StartBuildingImage, Repository, String.Join(",", ImageTags), sourceImageReference);
+ (string message, object[] parameters) = SkipPublishing ?
+ ( Strings.ContainerBuilder_StartBuildingImageForRid, new object[] { Repository, ContainerRuntimeIdentifier, sourceImageReference }) :
+ ( Strings.ContainerBuilder_StartBuildingImage, new object[] { Repository, String.Join(",", ImageTags), sourceImageReference });
+ Log.LogMessage(MessageImportance.High, message, parameters);
+
+ // forcibly change the media type if required
+ if (ImageFormat is not null)
+ {
+ if (Enum.TryParse(ImageFormat, out var imageFormat))
+ {
+ imageBuilder.ManifestMediaType = imageFormat switch
+ {
+ KnownImageFormats.Docker => SchemaTypes.DockerManifestV2,
+ KnownImageFormats.OCI => SchemaTypes.OciManifestV1,
+ _ => imageBuilder.ManifestMediaType // should be impossible unless we add to the enum
+ };
+ }
+ else
+ {
+ Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidContainerImageFormat), ImageFormat, string.Join(",", Enum.GetValues()));
+ }
+ }
Layer newLayer = Layer.FromDirectory(PublishDirectory, WorkingDirectory, imageBuilder.IsWindows, imageBuilder.ManifestMediaType);
imageBuilder.AddLayer(newLayer);
@@ -170,116 +189,22 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
cancellationToken.ThrowIfCancellationRequested();
// at this point we're done with modifications and are just pushing the data other places
- GeneratedContainerManifest = JsonSerializer.Serialize(builtImage.Manifest);
+ GeneratedContainerManifest = builtImage.Manifest;
GeneratedContainerConfiguration = builtImage.Config;
- GeneratedContainerDigest = builtImage.Manifest.GetDigest();
+ GeneratedContainerDigest = builtImage.ManifestDigest;
GeneratedArchiveOutputPath = ArchiveOutputPath;
GeneratedContainerMediaType = builtImage.ManifestMediaType;
GeneratedContainerNames = destinationImageReference.FullyQualifiedImageNames().Select(name => new Microsoft.Build.Utilities.TaskItem(name)).ToArray();
- switch (destinationImageReference.Kind)
+ if (!SkipPublishing)
{
- case DestinationImageReferenceKind.LocalRegistry:
- await PushToLocalRegistryAsync(builtImage,
- sourceImageReference,
- destinationImageReference,
- telemetry,
- cancellationToken).ConfigureAwait(false);
- break;
- case DestinationImageReferenceKind.RemoteRegistry:
- await PushToRemoteRegistryAsync(builtImage,
- sourceImageReference,
- destinationImageReference,
- telemetry,
- cancellationToken).ConfigureAwait(false);
- break;
- default:
- throw new ArgumentOutOfRangeException();
+ await ImagePublisher.PublishImageAsync(builtImage, sourceImageReference, destinationImageReference, Log, telemetry, cancellationToken)
+ .ConfigureAwait(false);
}
-
- telemetry.LogPublishSuccess();
-
+
return !Log.HasLoggedErrors;
}
- private async Task PushToLocalRegistryAsync(BuiltImage builtImage, SourceImageReference sourceImageReference,
- DestinationImageReference destinationImageReference,
- Telemetry telemetry,
- CancellationToken cancellationToken)
- {
- ILocalRegistry localRegistry = destinationImageReference.LocalRegistry!;
- if (!(await localRegistry.IsAvailableAsync(cancellationToken).ConfigureAwait(false)))
- {
- telemetry.LogMissingLocalBinary();
- Log.LogErrorWithCodeFromResources(nameof(Strings.LocalRegistryNotAvailable));
- return;
- }
- try
- {
- await localRegistry.LoadAsync(builtImage, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false);
- SafeLog(Strings.ContainerBuilder_ImageUploadedToLocalDaemon, destinationImageReference, localRegistry);
-
- if (localRegistry is ArchiveFileRegistry archive)
- {
- GeneratedArchiveOutputPath = archive.ArchiveOutputPath;
- }
- }
- catch (ContainerHttpException e)
- {
- if (BuildEngine != null)
- {
- Log.LogErrorFromException(e, true);
- }
- }
- catch (AggregateException ex) when (ex.InnerException is DockerLoadException dle)
- {
- telemetry.LogLocalLoadError();
- Log.LogErrorFromException(dle, showStackTrace: false);
- }
- catch (ArgumentException argEx)
- {
- Log.LogErrorFromException(argEx, showStackTrace: false);
- }
- }
-
- private async Task PushToRemoteRegistryAsync(BuiltImage builtImage, SourceImageReference sourceImageReference,
- DestinationImageReference destinationImageReference,
- Telemetry telemetry,
- CancellationToken cancellationToken)
- {
- try
- {
- await destinationImageReference.RemoteRegistry!.PushAsync(
- builtImage,
- sourceImageReference,
- destinationImageReference,
- cancellationToken).ConfigureAwait(false);
- SafeLog(Strings.ContainerBuilder_ImageUploadedToRegistry, destinationImageReference, OutputRegistry);
- }
- catch (UnableToAccessRepositoryException)
- {
- if (BuildEngine != null)
- {
- Log.LogErrorWithCodeFromResources(nameof(Strings.UnableToAccessRepository), destinationImageReference.Repository, destinationImageReference.RemoteRegistry!.RegistryName);
- }
- }
- catch (ContainerHttpException e)
- {
- if (BuildEngine != null)
- {
- Log.LogErrorFromException(e, true);
- }
- }
- catch (Exception e)
- {
- if (BuildEngine != null)
- {
- Log.LogErrorWithCodeFromResources(nameof(Strings.RegistryOutputPushFailed), e.Message);
- Log.LogMessage(MessageImportance.Low, "Details: {0}", e);
- }
- }
- }
-
private void SetPorts(ImageBuilder image, ITaskItem[] exposedPorts)
{
foreach (var port in exposedPorts)
@@ -326,11 +251,6 @@ private void SetEnvironmentVariables(ImageBuilder img, ITaskItem[] envVars)
}
}
- private void SafeLog(string message, params object[] formatParams)
- {
- if (BuildEngine != null) Log.LogMessage(MessageImportance.High, message, formatParams);
- }
-
public void Dispose()
{
_cancellationTokenSource.Dispose();
@@ -349,109 +269,4 @@ public void Dispose()
logWarning: s => Log.LogWarningWithCodeFromResources(s),
logError: (s, a) => { if (a is null) Log.LogErrorWithCodeFromResources(s); else Log.LogErrorWithCodeFromResources(s, a); });
}
-
- private Telemetry CreateTelemetryContext(SourceImageReference source, DestinationImageReference destination)
- {
- var context = new PublishTelemetryContext(
- source.Registry is not null ? GetRegistryType(source.Registry) : null,
- null, // we don't support local pull yet, but we may in the future
- destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null,
- destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null);
- return new Telemetry(Log, context);
- }
-
- private RegistryType GetRegistryType(Registry r)
- {
- if (r.IsMcr) return RegistryType.MCR;
- if (r.IsGithubPackageRegistry) return RegistryType.GitHub;
- if (r.IsAmazonECRRegistry) return RegistryType.AWS;
- if (r.IsAzureContainerRegistry) return RegistryType.Azure;
- if (r.IsGoogleArtifactRegistry) return RegistryType.Google;
- if (r.IsDockerHub) return RegistryType.DockerHub;
- return RegistryType.Other;
- }
-
- private LocalStorageType GetLocalStorageType(ILocalRegistry r)
- {
- if (r is ArchiveFileRegistry) return LocalStorageType.Tarball;
- var d = r as DockerCli;
- System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type");
- if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker;
- else return LocalStorageType.Podman;
- }
-
- ///
- /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process
- /// and to help diagnose issues with the container publish overall.
- ///
- /// If the base image came from a remote registry, what kind of registry was it?
- /// If the base image came from a local store of some kind, what kind of store was it?
- /// If the new image is being pushed to a remote registry, what kind of registry is it?
- /// If the new image is being stored in a local store of some kind, what kind of store is it?
- private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType);
- private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other }
- private enum LocalStorageType { Docker, Podman, Tarball }
-
- private class Telemetry(Microsoft.Build.Utilities.TaskLoggingHelper Log, PublishTelemetryContext context)
- {
- private IDictionary ContextProperties() => new Dictionary
- {
- { nameof(context.RemotePullType), context.RemotePullType?.ToString() },
- { nameof(context.LocalPullType), context.LocalPullType?.ToString() },
- { nameof(context.RemotePushType), context.RemotePushType?.ToString() },
- { nameof(context.LocalPushType), context.LocalPushType?.ToString() }
- };
-
- public void LogPublishSuccess()
- {
- Log.LogTelemetry("sdk/container/publish/success", ContextProperties());
- }
-
- public void LogUnknownRepository()
- {
- var props = ContextProperties();
- props.Add("error", "unknown_repository");
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- public void LogCredentialFailure(SourceImageReference _)
- {
- var props = ContextProperties();
- props.Add("error", "credential_failure");
- props.Add("direction", "pull");
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- public void LogCredentialFailure(DestinationImageReference d)
- {
- var props = ContextProperties();
- props.Add("error", "credential_failure");
- props.Add("direction", "push");
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- public void LogRidMismatch(string desiredRid, string[] availableRids)
- {
- var props = ContextProperties();
- props.Add("error", "rid_mismatch");
- props.Add("target_rid", desiredRid);
- props.Add("available_rids", string.Join(",", availableRids));
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- public void LogMissingLocalBinary()
- {
- var props = ContextProperties();
- props.Add("error", "missing_binary");
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- public void LogLocalLoadError()
- {
- var props = ContextProperties();
- props.Add("error", "local_load");
- Log.LogTelemetry("sdk/container/publish/error", props);
- }
-
- }
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
index 51869ccb6c54..d2ac3dbe0313 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
@@ -119,6 +119,10 @@ internal string GenerateCommandLineCommandsInt()
{
builder.AppendSwitchIfNotNull("--appcommandinstruction ", AppCommandInstruction);
}
+ if (!string.IsNullOrWhiteSpace(ImageFormat))
+ {
+ builder.AppendSwitchIfNotNull("--image-format ", ImageFormat);
+ }
AppendSwitchIfNotNullSanitized(builder, "--entrypoint ", nameof(Entrypoint), Entrypoint);
AppendSwitchIfNotNullSanitized(builder, "--entrypointargs ", nameof(EntrypointArgs), EntrypointArgs);
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs b/src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs
new file mode 100644
index 000000000000..009b93d7717c
--- /dev/null
+++ b/src/Containers/Microsoft.NET.Build.Containers/Telemetry.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.NET.Build.Containers.LocalDaemons;
+
+namespace Microsoft.NET.Build.Containers;
+
+internal class Telemetry
+{
+ ///
+ /// Interesting data about the container publish - used to track the usage rates of various sources/targets of the process
+ /// and to help diagnose issues with the container publish overall.
+ ///
+ /// If the base image came from a remote registry, what kind of registry was it?
+ /// If the base image came from a local store of some kind, what kind of store was it?
+ /// If the new image is being pushed to a remote registry, what kind of registry is it?
+ /// If the new image is being stored in a local store of some kind, what kind of store is it?
+ private record class PublishTelemetryContext(RegistryType? RemotePullType, LocalStorageType? LocalPullType, RegistryType? RemotePushType, LocalStorageType? LocalPushType);
+ private enum RegistryType { Azure, AWS, Google, GitHub, DockerHub, MCR, Other }
+ private enum LocalStorageType { Docker, Podman, Tarball }
+
+ private readonly Microsoft.Build.Utilities.TaskLoggingHelper Log;
+ private readonly PublishTelemetryContext Context;
+
+ internal Telemetry(
+ SourceImageReference source,
+ DestinationImageReference destination,
+ Microsoft.Build.Utilities.TaskLoggingHelper Log)
+ {
+ this.Log = Log;
+ Context = new PublishTelemetryContext(
+ source.Registry is not null ? GetRegistryType(source.Registry) : null,
+ null, // we don't support local pull yet, but we may in the future
+ destination.RemoteRegistry is not null ? GetRegistryType(destination.RemoteRegistry) : null,
+ destination.LocalRegistry is not null ? GetLocalStorageType(destination.LocalRegistry) : null);
+ }
+
+ private RegistryType GetRegistryType(Registry r)
+ {
+ if (r.IsMcr) return RegistryType.MCR;
+ if (r.IsGithubPackageRegistry) return RegistryType.GitHub;
+ if (r.IsAmazonECRRegistry) return RegistryType.AWS;
+ if (r.IsAzureContainerRegistry) return RegistryType.Azure;
+ if (r.IsGoogleArtifactRegistry) return RegistryType.Google;
+ if (r.IsDockerHub) return RegistryType.DockerHub;
+ return RegistryType.Other;
+ }
+
+ private LocalStorageType GetLocalStorageType(ILocalRegistry r)
+ {
+ if (r is ArchiveFileRegistry) return LocalStorageType.Tarball;
+ var d = r as DockerCli;
+ System.Diagnostics.Debug.Assert(d != null, "Unknown local registry type");
+ if (d.GetCommand() == DockerCli.DockerCommand) return LocalStorageType.Docker;
+ else return LocalStorageType.Podman;
+ }
+
+ private IDictionary ContextProperties() => new Dictionary
+ {
+ { nameof(Context.RemotePullType), Context.RemotePullType?.ToString() },
+ { nameof(Context.LocalPullType), Context.LocalPullType?.ToString() },
+ { nameof(Context.RemotePushType), Context.RemotePushType?.ToString() },
+ { nameof(Context.LocalPushType), Context.LocalPushType?.ToString() }
+ };
+
+ public void LogPublishSuccess()
+ {
+ Log.LogTelemetry("sdk/container/publish/success", ContextProperties());
+ }
+
+ public void LogUnknownRepository()
+ {
+ var props = ContextProperties();
+ props.Add("error", "unknown_repository");
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+
+ public void LogCredentialFailure(SourceImageReference _)
+ {
+ var props = ContextProperties();
+ props.Add("error", "credential_failure");
+ props.Add("direction", "pull");
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+
+ public void LogCredentialFailure(DestinationImageReference d)
+ {
+ var props = ContextProperties();
+ props.Add("error", "credential_failure");
+ props.Add("direction", "push");
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+
+ public void LogRidMismatch(string desiredRid, string[] availableRids)
+ {
+ var props = ContextProperties();
+ props.Add("error", "rid_mismatch");
+ props.Add("target_rid", desiredRid);
+ props.Add("available_rids", string.Join(",", availableRids));
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+
+ public void LogMissingLocalBinary()
+ {
+ var props = ContextProperties();
+ props.Add("error", "missing_binary");
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+
+ public void LogLocalLoadError()
+ {
+ var props = ContextProperties();
+ props.Add("error", "local_load");
+ Log.LogTelemetry("sdk/container/publish/error", props);
+ }
+}
\ No newline at end of file
diff --git a/src/Containers/containerize/ContainerizeCommand.cs b/src/Containers/containerize/ContainerizeCommand.cs
index 52b7e390b503..d611fb3e154d 100644
--- a/src/Containers/containerize/ContainerizeCommand.cs
+++ b/src/Containers/containerize/ContainerizeCommand.cs
@@ -197,6 +197,11 @@ internal class ContainerizeCommand : CliRootCommand
Arity = ArgumentArity.Zero
};
+ internal CliOption ImageFormatOption { get; } = new("--image-format")
+ {
+ Description = "If set to OCI or Docker will force the generated image to be that format. If unset, the base images format will be used."
+ };
+
internal ContainerizeCommand() : base("Containerize an application without Docker.")
{
PublishDirectoryArgument.AcceptLegalFilePathsOnly();
@@ -225,6 +230,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke
this.Options.Add(ContainerUserOption);
this.Options.Add(GenerateLabelsOption);
this.Options.Add(GenerateDigestLabelOption);
+ this.Options.Add(ImageFormatOption);
this.SetAction(async (parseResult, cancellationToken) =>
{
@@ -252,6 +258,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke
string? _containerUser = parseResult.GetValue(ContainerUserOption);
bool _generateLabels = parseResult.GetValue(GenerateLabelsOption);
bool _generateDigestLabel = parseResult.GetValue(GenerateDigestLabelOption);
+ KnownImageFormats? _imageFormat = parseResult.GetValue(ImageFormatOption);
//setup basic logging
bool traceEnabled = Env.GetEnvironmentVariableAsBool("CONTAINERIZE_TRACE_LOGGING_ENABLED");
@@ -283,6 +290,7 @@ await ContainerBuilder.ContainerizeAsync(
_archiveOutputPath,
_generateLabels,
_generateDigestLabel,
+ _imageFormat,
loggerFactory,
cancellationToken).ConfigureAwait(false);
});
diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
index 1b6ad491c8af..ff22ed811e83 100644
--- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
+++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
@@ -34,10 +34,10 @@
- $(RuntimeIdentifiers)
- $(RuntimeIdentifier)
+ $(RuntimeIdentifier)
+ $(RuntimeIdentifiers)linux-$(NETCoreSdkPortableRuntimeIdentifier.Split('-')[1])
<_ContainerIsUsingMicrosoftDefaultImages Condition="'$(ContainerBaseImage)' == ''">true
@@ -45,8 +45,8 @@
- <_TargetRuntimeIdentifiers Include="$(ContainerRuntimeIdentifiers)" Condition="'$(ContainerRuntimeIdentifiers)' != ''" />
- <_TargetRuntimeIdentifiers Include="$(ContainerRuntimeIdentifier)" Condition="'$(ContainerRuntimeIdentifiers)' == ''" />
+ <_TargetRuntimeIdentifiers Include="$(ContainerRuntimeIdentifier)" Condition="'$(ContainerRuntimeIdentifier)' != ''" />
+ <_TargetRuntimeIdentifiers Include="$(ContainerRuntimeIdentifiers)" Condition="@(_TargetRuntimeIdentifiers->Count()) == 0" />
@@ -296,11 +298,17 @@
-
+
+
+
+
+ <_SkipContainerPublishing>false
+ <_SkipContainerPublishing Condition="$(ContainerArchiveOutputPath) != '' or ( $(ContainerRegistry) == '' and ( $(LocalRegistry) == '' or $(LocalRegistry) == 'Docker' ) )">true
+
+
+ <_SkipCreateImageIndex>false
+ <_SkipCreateImageIndex Condition="$(ContainerArchiveOutputPath) == '' and $(ContainerRegistry) == '' and $(LocalRegistry) == 'Podman'">true
+
<_rids Include="$(ContainerRuntimeIdentifiers)" Condition="'$(ContainerRuntimeIdentifiers)' != ''" />
@@ -328,7 +336,8 @@
_ContainerEnvironmentVariables=@(ContainerEnvironmentVariable->'%(Identity):%(Value)');
ContainerUser=$(ContainerUser);
ContainerGenerateLabels=$(ContainerGenerateLabels);
- ContainerGenerateLabelsImageBaseDigest=$(ContainerGenerateLabelsImageBaseDigest)
+ ContainerGenerateLabelsImageBaseDigest=$(ContainerGenerateLabelsImageBaseDigest);
+ _SkipContainerPublishing=$(_SkipContainerPublishing)
"/>
<_rids Remove ="$(_rids)" />
@@ -340,17 +349,18 @@
-
- <_SkipCreateImageIndex>false
- <_SkipCreateImageIndex Condition="'$(ContainerRegistry)' == ''">true
-
-
+ ImageTags="@(ContainerImageTags)"
+ BaseRegistry="$(ContainerBaseRegistry)"
+ BaseImageName="$(ContainerBaseName)"
+ BaseImageTag="$(ContainerBaseTag)">
+
@@ -388,10 +398,6 @@
Include="$([System.String]::Copy('%(_ParsedContainerEnvironmentVariables.Identity)').Split(':')[0])"
Value="$([System.String]::Copy('%(_ParsedContainerEnvironmentVariables.Identity)').Split(':')[1])" />
-
-
- $([System.IO.Path]::Combine($(ContainerArchiveOutputPath), $(ContainerRepository)-$(ContainerRuntimeIdentifier).tar.gz))
-
<_IsMultiTFMBuild Condition="'$(TargetFrameworks)' != '' and '$(TargetFramework)' == ''">true
- <_IsMultiRIDBuild Condition="'$(BuildingInsideVisualStudio)' != 'true' and (('$(RuntimeIdentifiers)' != '' and '$(RuntimeIdentifier)' == '') or ('$(ContainerRuntimeIdentifiers)' != '' and '$(ContainerRuntimeIdentfier)' == ''))">true
+
+ <_HasCRIDsAndNoCRID Condition="'$(ContainerRuntimeIdentifiers)' != '' and '$(ContainerRuntimeIdentifier)' == ''">true
+ <_HasRIDs Condition="'$(RuntimeIdentifiers)' != ''">true
+ <_NoCRIDsOrCRIDorRID Condition="'$(ContainerRuntimeIdentifiers)' == '' and '$(ContainerRuntimeIdentifier)' == '' and '$(RuntimeIdentifier)' == ''">true
+ <_IsMultiRIDBuild Condition="'$(BuildingInsideVisualStudio)' != 'true' and ('$(_HasCRIDsAndNoCRID)' == true or ('$(_HasRIDs)' == 'true' and '$(_NoCRIDsOrCRIDorRID)' == 'true'))">true
<_IsSingleRIDBuild Condition="'$(_IsMultiRIDBuild)' == ''">true
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
index 2aca0c68dbd5..e24daeb6c321 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
@@ -539,7 +539,11 @@ static void AddWorkloadSetsForFeatureBand(Dictionary availa
{
var workloadSetVersion = Path.GetFileName(workloadSetDirectory);
var workloadSet = WorkloadSet.FromWorkloadSetFolder(workloadSetDirectory, workloadSetVersion, featureBand);
- availableWorkloadSets[workloadSet.Version!] = workloadSet;
+
+ if (workloadSet is not null)
+ {
+ availableWorkloadSets[workloadSet.Version!] = workloadSet;
+ }
}
}
}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
index eecb89c38f06..18adbfab7d13 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadSet.cs
@@ -81,7 +81,7 @@ public static WorkloadSet FromJson(string json, SdkFeatureBand defaultFeatureBan
#endif
}
- public static WorkloadSet FromWorkloadSetFolder(string path, string workloadSetVersion, SdkFeatureBand defaultFeatureBand)
+ public static WorkloadSet? FromWorkloadSetFolder(string path, string workloadSetVersion, SdkFeatureBand defaultFeatureBand)
{
WorkloadSet? workloadSet = null;
foreach (var jsonFile in Directory.GetFiles(path, "*.workloadset.json"))
@@ -107,7 +107,7 @@ public static WorkloadSet FromWorkloadSetFolder(string path, string workloadSetV
if (workloadSet == null)
{
- throw new InvalidOperationException("No workload set information found in: " + path);
+ return null;
}
if (File.Exists(Path.Combine(path, "baseline.workloadset.json")))
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs
new file mode 100644
index 000000000000..e5bfc54711d0
--- /dev/null
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ArchiveFileRegistryTests.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.NET.Build.Containers.LocalDaemons;
+
+namespace Microsoft.NET.Build.Containers.IntegrationTests;
+
+public class ArchiveFileRegistryTests
+{
+ [Fact]
+ public async Task ArchiveOutputPathIsExistingDirectory_CreatesFileWithRepositoryNameAndTarGz()
+ {
+ string archiveOutputPath = TestSettings.TestArtifactsDirectory;
+ string expectedCreatedFilePath = Path.Combine(TestSettings.TestArtifactsDirectory, "repository.tar.gz");
+
+ await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false);
+
+ Assert.True(File.Exists(expectedCreatedFilePath));
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task ArchiveOutputPathIsNonExistingDirectory_CreatesDirectoryAndFileWithRepositoryNameAndTarGz(bool includeDirectorySeperatorAtTheEnd)
+ {
+ string archiveOutputPath = Path.Combine(
+ TestSettings.TestArtifactsDirectory,
+ "nonexisting" + (includeDirectorySeperatorAtTheEnd ? Path.DirectorySeparatorChar : ""));
+ string expectedCreatedFilePath = Path.Combine(archiveOutputPath, "repository.tar.gz");
+
+ await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false);
+
+ Assert.True(File.Exists(expectedCreatedFilePath));
+ }
+
+ [Fact]
+ public async Task ArchiveOutputPathIsCustomFileNameInExistingDirectory_CreatesFileWithThatName()
+ {
+ string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, "custom-name.withextension");
+ string expectedCreatedFilePath = archiveOutputPath;
+
+ await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false);
+
+ Assert.True(File.Exists(expectedCreatedFilePath));
+ }
+
+ [Fact]
+ public async Task ArchiveOutputPathIsCustomFileNameInNonExistingDirectory_CreatesDirectoryAndFileWithThatName()
+ {
+ string archiveOutputPath = Path.Combine(TestSettings.TestArtifactsDirectory, $"nonexisting-directory{Path.AltDirectorySeparatorChar}custom-name.withextension");
+ string expectedCreatedFilePath = archiveOutputPath;
+
+ await CreateRegistryAndCallLoadAsync(archiveOutputPath).ConfigureAwait(false);
+
+ Assert.True(File.Exists(expectedCreatedFilePath));
+ }
+
+ private async Task CreateRegistryAndCallLoadAsync(string archiveOutputPath)
+ {
+ var registry = new ArchiveFileRegistry(archiveOutputPath);
+ var destinationImageReference = new DestinationImageReference(registry, "repository", ["tag"]);
+
+ await registry.LoadAsync(
+ "test image",
+ new SourceImageReference(),
+ destinationImageReference,
+ CancellationToken.None,
+ async (img, srcRef, destRef, stream, token) =>
+ {
+ await Task.CompletedTask;
+ }).ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs
index 82ccc3d4a2c9..690cb28fe1e3 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ContainerCli.cs
@@ -7,12 +7,13 @@ static class ContainerCli
{
public static bool IsPodman => _isPodman.Value;
+ public static bool IsAvailable => _isAvailable.Value;
+
+ public static bool IsContainerdStoreEnabledForDocker => DockerCli.IsContainerdStoreEnabledForDocker();
+
public static RunExeCommand PullCommand(ITestOutputHelper log, params string[] args)
=> CreateCommand(log, "pull", args);
- public static RunExeCommand TagCommand(ITestOutputHelper log, params string[] args)
- => CreateCommand(log, "tag", args);
-
public static RunExeCommand PushCommand(ITestOutputHelper log, params string[] args)
=> CreateCommand(log, "push", args);
@@ -60,4 +61,7 @@ private static RunExeCommand CreateCommand(ITestOutputHelper log, string command
private static readonly Lazy _isPodman =
new(() => new DockerCli(loggerFactory: new TestLoggerFactory()).GetCommand() == DockerCli.PodmanCommand);
+
+ private static readonly Lazy _isAvailable =
+ new(() => new DockerCli(loggerFactory: new TestLoggerFactory()).IsAvailable());
}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
index cf25db4b0309..f6a8e0823117 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
@@ -295,6 +295,72 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage()
Assert.Equal(RootlessUser, imageBuilder.BaseImageConfig.GetUser());
}
+
+ [DockerAvailableFact]
+ public void CanOverrideContainerImageFormat()
+ {
+ DirectoryInfo newProjectDir = new(GetTestDirectoryName());
+
+ if (newProjectDir.Exists)
+ {
+ newProjectDir.Delete(recursive: true);
+ }
+
+ newProjectDir.Create();
+
+ new DotnetNewCommand(_testOutput, "console", "-f", ToolsetInfo.CurrentTargetFramework)
+ .WithVirtualHive()
+ .WithWorkingDirectory(newProjectDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ new DotnetCommand(_testOutput, "build", "--configuration", "release")
+ .WithWorkingDirectory(newProjectDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ ParseContainerProperties pcp = new();
+ (IBuildEngine buildEngine, List errors) = SetupBuildEngine();
+ pcp.BuildEngine = buildEngine;
+
+ pcp.FullyQualifiedBaseImageName = "mcr.microsoft.com/dotnet/runtime:9.0";
+ pcp.ContainerRegistry = "localhost:5010";
+ pcp.ContainerRepository = "dotnet/testimage";
+ pcp.ContainerImageTags = new[] { "5.0", "latest" };
+
+ Assert.True(pcp.Execute(), FormatBuildMessages(errors));
+ Assert.Equal("mcr.microsoft.com", pcp.ParsedContainerRegistry);
+ Assert.Equal("dotnet/runtime", pcp.ParsedContainerImage);
+ Assert.Equal("9.0", pcp.ParsedContainerTag);
+
+ Assert.Equal("dotnet/testimage", pcp.NewContainerRepository);
+ pcp.NewContainerTags.Should().BeEquivalentTo(new[] { "5.0", "latest" });
+
+ CreateNewImage cni = new();
+ (buildEngine, errors) = SetupBuildEngine();
+ cni.BuildEngine = buildEngine;
+
+ cni.BaseRegistry = pcp.ParsedContainerRegistry;
+ cni.BaseImageName = pcp.ParsedContainerImage;
+ cni.BaseImageTag = pcp.ParsedContainerTag;
+ cni.Repository = pcp.NewContainerRepository;
+ cni.OutputRegistry = "localhost:5010";
+ cni.PublishDirectory = Path.Combine(newProjectDir.FullName, "bin", "release", ToolsetInfo.CurrentTargetFramework);
+ cni.WorkingDirectory = "app/";
+ cni.Entrypoint = new TaskItem[] { new(newProjectDir.Name) };
+ cni.ImageTags = pcp.NewContainerTags;
+ cni.ContainerRuntimeIdentifier = "linux-x64";
+ cni.RuntimeIdentifierGraphPath = ToolsetUtils.GetRuntimeGraphFilePath();
+
+ cni.ImageFormat = KnownImageFormats.OCI.ToString();
+
+ Assert.True(cni.Execute(), FormatBuildMessages(errors));
+
+ cni.GeneratedContainerMediaType.Should().Be(SchemaTypes.OciManifestV1);
+ newProjectDir.Delete(true);
+ }
+
+
private static (IBuildEngine buildEngine, List errors) SetupBuildEngine()
{
List errors = new();
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs
new file mode 100644
index 000000000000..ae0e76f3c153
--- /dev/null
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchFact.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Containers.IntegrationTests;
+
+public class DockerIsAvailableAndSupportsArchFactAttribute : FactAttribute
+{
+ public DockerIsAvailableAndSupportsArchFactAttribute(string arch, bool checkContainerdStoreAvailability = false)
+ {
+ if (!DockerSupportsArchHelper.DaemonIsAvailable)
+ {
+ base.Skip = "Skipping test because Docker is not available on this host.";
+ }
+ else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker)
+ {
+ base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver.";
+ }
+ else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch))
+ {
+ base.Skip = $"Skipping test because Docker daemon does not support {arch}.";
+ }
+ }
+}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs
new file mode 100644
index 000000000000..382e57604fab
--- /dev/null
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerIsAvailableAndSupportsArchTheory.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Build.Containers.IntegrationTests;
+
+public class DockerIsAvailableAndSupportsArchTheoryAttribute : TheoryAttribute
+{
+ public DockerIsAvailableAndSupportsArchTheoryAttribute(string arch, bool checkContainerdStoreAvailability = false)
+ {
+ if (!DockerSupportsArchHelper.DaemonIsAvailable)
+ {
+ base.Skip = "Skipping test because Docker is not available on this host.";
+ }
+ else if (checkContainerdStoreAvailability && !DockerSupportsArchHelper.IsContainerdStoreEnabledForDocker)
+ {
+ base.Skip = "Skipping test because Docker daemon is not using containerd as the storage driver.";
+ }
+ else if (!DockerSupportsArchHelper.DaemonSupportsArch(arch))
+ {
+ base.Skip = $"Skipping test because Docker daemon does not support {arch}.";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs
deleted file mode 100644
index 984bff2c0b37..000000000000
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchFact.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.NET.Build.Containers.IntegrationTests;
-
-public class DockerSupportsArchFactAttribute : FactAttribute
-{
- private readonly string _arch;
-
- public DockerSupportsArchFactAttribute(string arch)
- {
- _arch = arch;
-
- if (!DockerSupportsArchHelper.DaemonSupportsArch(_arch))
- {
- base.Skip = $"Skipping test because Docker daemon does not support {_arch}.";
- }
- }
-}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs
index 022a8e90c16a..7944436b39c9 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs
@@ -34,6 +34,10 @@ public override IEnumerable