Skip to content

Commit 397be11

Browse files
CodingIsBlissSarath chandra Bussakumaraksh1sarsharma
authored
ACR-based SDK distribution (#2837)
* feat: Oryx SDK regional distribution — pull SDKs from ACR * feat: Add ACR-based SDK distribution with feature flag Add two separate ACR SDK providers behind ORYX_ENABLE_ACR_SDK_PROVIDER: 1. ExternalAcrSdkProvider - communicates with LWASv2 via Unix socket to pull SDK images from WAWS Images ACR 2. AcrSdkProvider (direct) - downloads SDKs from Oryx ACR (oryxsdks.azurecr.io) using OCI Distribution API New files: - IExternalAcrSdkProvider.cs / ExternalAcrSdkProvider.cs - OciRegistryClient.cs - HTTP client for OCI Distribution API - AcrVersionProviderBase.cs - base class for ACR version discovery - Per-platform ACR version providers (Node, Python, PHP, DotNetCore) - publishSdkImageToAcr.sh / publishSdkToAcr.yml - ACR publish pipeline Modified files: - Platform install decisions (NodePlatform, PythonPlatform, PhpPlatform, DotNetCorePlatform) with ACR branch before existing DynamicInstall - Version provider orchestrators with ACR provider chain - PlatformInstallerBase with GetAcrInstallerScriptSnippet() - Go startup script generator with ACR download path - DI registrations for all new providers - Constants, options, settings keys for ACR configuration All changes are additive and behind feature flags. Existing blob storage code paths are completely untouched. * refactor: Simplify ExternalAcrSdkProvider - let LWASv2 handle image resolution Remove ACR repository/tag construction from ExternalAcrSdkProvider. Now sends only platform, version, and debianFlavor to LWASv2. LWASv2 resolves the SDK companion image from LinuxAssets and handles pinning, containerd pull, mount, and extraction. * refactor: Remove script-based ACR publish (moved to AAPT-Antares-Oryx OneBranch pipeline) Remove publishSdkImageToAcr.sh and publishSdkToAcr.yml since SDK images are now built and pushed using onebranch.pipeline.imagebuildinfo in the AAPT-Antares-Oryx pipeline, not via custom scripts. * fix: Default ACR SDK registry to oryxacr.azurecr.io Use the same ACR that AAPT-Antares-Oryx publishes SDK images to. Updated both C# constant and Go constant. * fix: Resolve StyleCop analyzer errors in ACR SDK provider files - SA1204: Move static GetFirstLayerDigest before instance members (OciRegistryClient.cs) - SA1124: Remove #region/#endregion around OCI JSON models (OciRegistryClient.cs) - SA1202: Move protected GetAcrInstallerScriptSnippet before private methods (PlatformInstallerBase.cs) - SA1116/SA1117: Place multi-line parameters each on own line (ExternalAcrSdkProvider.cs) - SA1515: Add blank line before single-line comment (DotNetCoreAcrVersionProvider.cs) * fix: Resolve StyleCop analyzer errors in ACR SDK provider files - SA1204: Move static GetFirstLayerDigest before instance members (OciRegistryClient.cs) - SA1124: Remove #region/#endregion around OCI JSON models (OciRegistryClient.cs) - SA1202: Move protected GetAcrInstallerScriptSnippet before private methods (PlatformInstallerBase.cs) - SA1116/SA1117: Place multi-line parameters each on own line (ExternalAcrSdkProvider.cs) - SA1515: Add blank line before single-line comment (DotNetCoreAcrVersionProvider.cs) * Feature/oryx sdk acr distribution sdks resolver (#2842) * fix: Resolve StyleCop analyzer errors in ACR SDK provider files - SA1204: Move static GetFirstLayerDigest before instance members (OciRegistryClient.cs) - SA1124: Remove #region/#endregion around OCI JSON models (OciRegistryClient.cs) - SA1202: Move protected GetAcrInstallerScriptSnippet before private methods (PlatformInstallerBase.cs) - SA1116/SA1117: Place multi-line parameters each on own line (ExternalAcrSdkProvider.cs) - SA1515: Add blank line before single-line comment (DotNetCoreAcrVersionProvider.cs) * fix sdk providers logic * refactor pythonPlatform * refctor for dotnet,php and node --------- Co-authored-by: Sarath chandra Bussa <sbussa@microsoft.com> * fix build and tests (#2843) * fix to fetch from acr (#2844) * Update version providers (#2845) * Refactor version providers (#2846) * Fix acr version info (#2848) * Changes for php composer (#2849) * Clean dead code (#2850) * Fix priroity in sdk resolver (#2851) * Fix Uts and version providers (#2852) * ACR Sdk provider refactor (#2854) * registryclient improvements * fix build issue * Add changes foe external ACR provider (#2855) * Change flag name for external acr case (#2856) * Handle multiplatform (#2857) * Revert "Handle multiplatform (#2857)" (#2858) This reverts commit 77d9f99. * Disable external ACR for multiplatform (#2859) * Fix test case (#2860) * Refactor external acr provider (#2862) * update externalacrsdkprovider contract * Align ExternalAcrSdkProvider and ExternalAcrVersionProviderBase with LWASv2 socket contract - Fix socket path to /var/sdk-image-sockets/oryx-pull-sdk-image.socket (ACR socket) - Add ExternalAcrSdksStorageDir constant (/var/OryxAcrSdks) for ACR SDK cache - Add top-level Action field (pull-sdk / get-version) to request DTOs - Remove BlobName and UrlParameters from ExternalAcrSdkProvider request - Accept server-returned filename as success response instead of expecting 'Success$' * update logic for dotnet and default versions * update logic for dotnet and default versions (#2864) * Refactor code for new acr way (#2863) * nit fix (#2865) * Refactor ACR SDK provider to return tarball path * ACR SDK distribution: Refactor direct acr sdk fetching logic (#2866) * update logic for dotnet and default versions * Refactor ACR SDK provider to return tarball path * Cleanup startupscriptgen and refactor ACR SDK distribution constants * ACR Sdk distribution: Cleanup startupscriptgenerator and fix image repo constants (#2867) * update logic for dotnet and default versions * Refactor ACR SDK provider to return tarball path * Cleanup startupscriptgen and refactor ACR SDK distribution constants * Refactor ACR SDK provider methods to return boolean status and update related logic across platforms * Update version provider logic to check for external SDK provider option * ACR Sdk distribution: Fix failure path for acr sdk fetching (#2868) * update logic for dotnet and default versions * Refactor ACR SDK provider to return tarball path * Cleanup startupscriptgen and refactor ACR SDK distribution constants * Refactor ACR SDK provider methods to return boolean status and update related logic across platforms * Update version provider logic to check for external SDK provider option * Update version provider fallback (#2872) * Update fallback logic for sdks (#2873) * version map fix * fix externalacrprovider for dotnet * ACR Sdk distribution: fix external ACR sdk provider for dotnet (#2874) * update logic for dotnet and default versions * Refactor ACR SDK provider to return tarball path * Cleanup startupscriptgen and refactor ACR SDK distribution constants * Refactor ACR SDK provider methods to return boolean status and update related logic across platforms * Update version provider logic to check for external SDK provider option * version map fix * fix externalacrprovider for dotnet * Feature/oryx sdk acr distribution fix phpand composer (#2875) * Update PHP and PHP-Composer Platform * update php and composer * Enhance OciRegistryClient to support anonymous token acquisition for public repositories * add logging * Acr SDK Distribution: Add token for listing sdk images (#2876) * update logic for dotnet and default versions * Refactor ACR SDK provider to return tarball path * Cleanup startupscriptgen and refactor ACR SDK distribution constants * Refactor ACR SDK provider methods to return boolean status and update related logic across platforms * Update version provider logic to check for external SDK provider option * version map fix * fix externalacrprovider for dotnet * Enhance OciRegistryClient to support anonymous token acquisition for public repositories * add logging * fix build * suppress auth header from logs * fix socket paths * Add a method to fetch all versions for a platform (#2877) * fix fallback flow * logging fixes * logging fixes (#2878) * fix tar extraction and add more logging * fix dotnet image pull * handle gzip stream * fix php installationscript duplication * ACR Sdk dist: Enhancements for cache management (#2889) * add fallback for no versions returned * improve image caching logic * handle mcr repo * Feature/oryx sdk acr distribution pr review 1 (#2888) * nit fixes in pr review * nit fixes 2.0 * refactor * Fix UTs * ACR SDK dist: Add tests, refactoring and some fixes (#2892) * Fix composer logic (#2893) * Fix composer logic * nit * fix build (#2894) * Add .NET SDK versions and composerVersion to constants.yml Add DOTNET_SDK_80, DOTNET_SDK_90, DOTNET_SDK_100 variables to pair with existing runtime version variables. Add composerVersion for PHP Composer. These are consumed by the Official and Buddy pipelines for SDK ACR publishing. * ACR SDK dist: Refactor version provider and bump up dependencies (#2895) * fix node logic and some other bugs * more fixes * socket helper refactor * more fixes * add tests * refactor version provider * fix build * bump version * ACR SDK dist: Additional test cases (#2896) * fix node logic and some other bugs * more fixes * socket helper refactor * more fixes * add tests * refactor version provider * fix build * bump version * add tests * follow existing pattern for php composer, add more tests * Revert "Add .NET SDK versions and composerVersion to constants.yml" This reverts commit 2f2b67d. * update comment * Add VERSIONS_TO_BUILD_OVERRIDE to force-build specific SDK versions (#2897) - Modify buildPlatform() in __common.sh: when VERSIONS_TO_BUILD_OVERRIDE is set, filter versionsToBuild.txt to only matching versions and set OVERWRITE_EXISTING_SDKS=true to bypass blob existence checks - Add ARG/ENV VERSIONS_TO_BUILD_OVERRIDE to nodejs, python, php, and php-composer Dockerfiles so the env var reaches build containers via --build-arg Co-authored-by: Sarath chandra Bussa <sbussa@microsoft.com> * resolve comments * nit * fix comment --------- Co-authored-by: Sarath chandra Bussa <sbussa@microsoft.com> Co-authored-by: Akshay Kumar <kumaraksh@microsoft.com> Co-authored-by: Akshay Kumar <112485097+kumaraksh1@users.noreply.github.com> Co-authored-by: sartsharma <sartsharma@microsoft.com>
1 parent e1e5445 commit 397be11

86 files changed

Lines changed: 6828 additions & 502 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

platforms/__common.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,50 @@ getSdkFromImage() {
148148
buildPlatform() {
149149
local versionFile="$1"
150150
local funcToCall="$2"
151+
152+
# When VERSIONS_TO_BUILD_OVERRIDE is set (comma-separated list of versions),
153+
# only build those specific versions and skip blob existence checks entirely.
154+
# This allows force-building specific SDK versions without rebuilding everything.
155+
if [ -n "$VERSIONS_TO_BUILD_OVERRIDE" ]; then
156+
echo "VERSIONS_TO_BUILD_OVERRIDE is set: $VERSIONS_TO_BUILD_OVERRIDE"
157+
echo "Building only specified versions, skipping storage account checks."
158+
export OVERWRITE_EXISTING_SDKS="true"
159+
160+
# Build a lookup set of requested versions
161+
IFS=',' read -ra _requested_versions <<< "$VERSIONS_TO_BUILD_OVERRIDE"
162+
declare -A _force_set
163+
for _v in "${_requested_versions[@]}"; do
164+
_v="$(echo "$_v" | xargs)"
165+
[ -n "$_v" ] && _force_set["$_v"]=1
166+
done
167+
168+
# Read the version file but only invoke the build function for matching versions.
169+
# This preserves extra args (e.g. GPG keys, SHAs) that some platforms need.
170+
while IFS= read -r VERSION_INFO || [[ -n $VERSION_INFO ]]; do
171+
VERSION_INFO="$(echo -e "${VERSION_INFO}" | sed -e 's/^[[:space:]]*//')"
172+
if [ -z "$VERSION_INFO" ] || [[ $VERSION_INFO = \#* ]]; then
173+
continue
174+
fi
175+
176+
IFS=',' read -ra VERSION_INFO_PARTS <<< "$VERSION_INFO"
177+
lineVersion="$(echo -e "${VERSION_INFO_PARTS[0]}" | sed -e 's/^[[:space:]]*//')"
178+
179+
if [ -z "${_force_set[$lineVersion]:-}" ]; then
180+
continue
181+
fi
182+
183+
echo "Force-building version: $lineVersion"
184+
versionArgs=()
185+
for arg in "${VERSION_INFO_PARTS[@]}"; do
186+
arg="$(echo -e "${arg}" | sed -e 's/^[[:space:]]*//')"
187+
versionArgs+=("$arg")
188+
done
189+
190+
$funcToCall "${versionArgs[@]}"
191+
done < "$versionFile"
192+
return
193+
fi
194+
151195
while IFS= read -r VERSION_INFO || [[ -n $VERSION_INFO ]]
152196
do
153197
# remove all whitespace before first character

platforms/nodejs/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ ARG OS_FLAVOR
22
FROM mcr.microsoft.com/mirror/docker/library/buildpack-deps:${OS_FLAVOR}
33
ARG OS_FLAVOR
44
ENV OS_FLAVOR=$OS_FLAVOR
5+
ARG VERSIONS_TO_BUILD_OVERRIDE=""
6+
ENV VERSIONS_TO_BUILD_OVERRIDE=$VERSIONS_TO_BUILD_OVERRIDE
57

68
RUN apt-get update \
79
&& apt-get install -y --no-install-recommends \

platforms/php/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ ARG OS_FLAVOR
22
FROM mcr.microsoft.com/mirror/docker/library/buildpack-deps:${OS_FLAVOR} AS php-buildpack-prereqs
33
ARG OS_FLAVOR
44
ENV OS_FLAVOR=$OS_FLAVOR
5+
ARG VERSIONS_TO_BUILD_OVERRIDE=""
6+
ENV VERSIONS_TO_BUILD_OVERRIDE=$VERSIONS_TO_BUILD_OVERRIDE
57
COPY platforms/php/prereqs /php
68
COPY platforms/php/prereqs/build.sh /tmp/
79
COPY images/receiveGpgKeys.sh /tmp/receiveGpgKeys.sh

platforms/php/composer/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ ARG OS_FLAVOR
22
FROM mcr.microsoft.com/mirror/docker/library/buildpack-deps:${OS_FLAVOR} AS php-buildpack-prereqs
33
ARG OS_FLAVOR
44
ENV OS_FLAVOR=$OS_FLAVOR
5+
ARG VERSIONS_TO_BUILD_OVERRIDE=""
6+
ENV VERSIONS_TO_BUILD_OVERRIDE=$VERSIONS_TO_BUILD_OVERRIDE
57
COPY platforms/php/prereqs /php
68
# COPY build/__phpVersions.sh /php/
79
COPY platforms/php/prereqs/build.sh /tmp/

platforms/python/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ ARG OS_FLAVOR
33
FROM mcr.microsoft.com/mirror/docker/library/buildpack-deps:${OS_FLAVOR}
44
ARG OS_FLAVOR
55
ENV OS_FLAVOR=$OS_FLAVOR
6+
ARG VERSIONS_TO_BUILD_OVERRIDE=""
7+
ENV VERSIONS_TO_BUILD_OVERRIDE=$VERSIONS_TO_BUILD_OVERRIDE
68

79
# COPY build/__pythonVersions.sh /tmp/
810

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// --------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT license.
4+
// --------------------------------------------------------------------------------------------
5+
6+
namespace Microsoft.Oryx.BuildScriptGenerator.Common
7+
{
8+
public static class SdkImageRepositoryHelper
9+
{
10+
/// <summary>
11+
/// Maps a platform name to its OCI SDK image repository path.
12+
/// e.g. "nodejs" → "oryx/nodejs-sdk", "php" → "oryx/php-sdk".
13+
/// Final image ref: mcr.microsoft.com/oryx/nodejs-sdk:bookworm-20.20.2
14+
/// </summary>
15+
public static string GetSdkImageRepository(string platformName, string prefix = null)
16+
{
17+
prefix = string.IsNullOrEmpty(prefix) ? SdkStorageConstants.DefaultAcrSdkRepositoryPrefix : prefix;
18+
return $"{prefix}/{platformName}-sdk";
19+
}
20+
}
21+
}

src/BuildScriptGenerator.Common/SdkStorageConstants.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,9 @@ public static class SdkStorageConstants
2121
public const string DotnetRuntimeVersionMetadataName = "Dotnet_runtime_version";
2222
public const string LegacyDotnetRuntimeVersionMetadataName = "Runtime_version";
2323
public const string OsTypeMetadataName = "Os_type";
24+
25+
// OCI image based SDK distribution constants
26+
public const string DefaultAcrSdkRegistryUrl = "https://mcr.microsoft.com";
27+
public const string DefaultAcrSdkRepositoryPrefix = "oryx";
2428
}
2529
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// --------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT license.
4+
// --------------------------------------------------------------------------------------------
5+
6+
using System;
7+
using System.Formats.Tar;
8+
using System.IO;
9+
using System.IO.Compression;
10+
using System.Threading.Tasks;
11+
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Options;
13+
using Microsoft.Oryx.BuildScriptGenerator.Common;
14+
15+
namespace Microsoft.Oryx.BuildScriptGenerator
16+
{
17+
/// <summary>
18+
/// Fetches SDK tarballs directly from an OCI container registry.
19+
/// SDK images are single-layer <c>FROM scratch</c> images containing a single
20+
/// <c>.tar.gz</c> SDK file. The OCI layer blob is a tar archive of the image
21+
/// filesystem, so this provider downloads the layer, extracts the inner SDK
22+
/// tarball from it, and caches it locally.
23+
/// </summary>
24+
/// <remarks>
25+
/// Makes direct HTTP calls to the registry (no Unix socket).
26+
/// See <see cref="ExternalAcrSdkProvider"/> for the socket-based variant.
27+
/// </remarks>
28+
public class AcrSdkProvider : IAcrSdkProvider
29+
{
30+
private readonly ILogger<AcrSdkProvider> logger;
31+
private readonly IStandardOutputWriter outputWriter;
32+
private readonly BuildScriptGeneratorOptions options;
33+
private readonly OciRegistryClient ociClient;
34+
35+
public AcrSdkProvider(
36+
IStandardOutputWriter outputWriter,
37+
ILogger<AcrSdkProvider> logger,
38+
IOptions<BuildScriptGeneratorOptions> options,
39+
OciRegistryClient ociClient)
40+
{
41+
this.logger = logger;
42+
this.outputWriter = outputWriter;
43+
this.options = options.Value;
44+
this.ociClient = ociClient;
45+
}
46+
47+
/// <inheritdoc/>
48+
public async Task<bool> RequestSdkFromAcrAsync(string platformName, string version, string debianFlavor, string runtimeVersion = null)
49+
{
50+
if (string.IsNullOrEmpty(platformName))
51+
{
52+
throw new ArgumentException("Platform name cannot be null or empty.", nameof(platformName));
53+
}
54+
55+
if (string.IsNullOrEmpty(version))
56+
{
57+
throw new ArgumentException("Version cannot be null or empty.", nameof(version));
58+
}
59+
60+
if (string.IsNullOrEmpty(debianFlavor))
61+
{
62+
debianFlavor = this.options.DebianFlavor ?? "bookworm";
63+
}
64+
65+
var repository = SdkImageRepositoryHelper.GetSdkImageRepository(platformName, this.options.OryxAcrSdkRepositoryPrefix);
66+
var tag = string.IsNullOrEmpty(runtimeVersion)
67+
? $"{debianFlavor}-{version}"
68+
: $"{debianFlavor}-{version}_{runtimeVersion}";
69+
var blobName = $"{platformName}-{debianFlavor}-{version}.tar.gz";
70+
71+
this.logger.LogInformation(
72+
"Requesting SDK from ACR: {Repository}:{Tag}",
73+
repository,
74+
tag);
75+
this.outputWriter.WriteLine(
76+
$"Requesting SDK from ACR: {repository}:{tag}");
77+
78+
// Download to the writable dynamic install directory, NOT /var/OryxSdks (read-only external mount).
79+
var downloadDir = Path.Combine(this.options.DynamicInstallRootDir, platformName);
80+
var tarballPath = Path.Combine(downloadDir, blobName);
81+
var digestPath = Path.Combine(downloadDir, $".{blobName}.digest");
82+
83+
try
84+
{
85+
// Get image manifest
86+
var remoteDigest = await this.ociClient.GetManifestDigestAsync(repository, tag);
87+
88+
// Check if cached tarball is still fresh
89+
if (File.Exists(tarballPath) && File.Exists(digestPath) && remoteDigest != null)
90+
{
91+
var localDigest = File.ReadAllText(digestPath).Trim();
92+
if (string.Equals(localDigest, remoteDigest, StringComparison.OrdinalIgnoreCase))
93+
{
94+
this.logger.LogInformation(
95+
"SDK cache is fresh (digest match): {FilePath}",
96+
tarballPath);
97+
this.outputWriter.WriteLine(
98+
$"SDK tarball already cached and fresh at {tarballPath}");
99+
return true;
100+
}
101+
102+
this.logger.LogInformation(
103+
"SDK cache is stale (digest mismatch). Re-downloading.");
104+
}
105+
106+
// Get manifest → extract single layer digest
107+
var manifest = await this.ociClient.GetManifestAsync(repository, tag);
108+
var layerDigest = OciRegistryClient.GetFirstLayerDigest(manifest);
109+
110+
if (string.IsNullOrEmpty(layerDigest))
111+
{
112+
this.logger.LogWarning(
113+
"No layer found in manifest for {Repository}:{Tag}",
114+
repository,
115+
tag);
116+
this.outputWriter.WriteLine($"No layer found in ACR manifest for {platformName} {version}.");
117+
return false;
118+
}
119+
120+
Directory.CreateDirectory(downloadDir);
121+
122+
// 2. Download the OCI layer blob to a temp file.
123+
// The layer is a tar archive of the image filesystem (not the SDK tarball itself).
124+
var layerTempPath = Path.Combine(downloadDir, $".layer-{Guid.NewGuid():N}.tmp");
125+
try
126+
{
127+
var downloadSuccess = await this.ociClient.DownloadLayerBlobAsync(
128+
repository,
129+
layerDigest,
130+
layerTempPath);
131+
132+
if (!downloadSuccess)
133+
{
134+
this.logger.LogWarning(
135+
"ACR SDK pull failed digest verification: {Repository}:{Tag}",
136+
repository,
137+
tag);
138+
this.outputWriter.WriteLine(
139+
$"Failed to pull SDK from ACR (digest mismatch): {platformName} {version}");
140+
return false;
141+
}
142+
143+
// 3. Extract the inner SDK .tar.gz from the layer tar.
144+
// The image is FROM scratch with a single COPY of the SDK tarball,
145+
// so the layer contains the .tar.gz as a top-level entry.
146+
this.ExtractFileFromTar(layerTempPath, tarballPath, blobName);
147+
}
148+
finally
149+
{
150+
// Always clean up the temporary layer file
151+
if (File.Exists(layerTempPath))
152+
{
153+
File.Delete(layerTempPath);
154+
}
155+
}
156+
157+
this.logger.LogInformation(
158+
"Successfully pulled SDK from ACR: {Repository}:{Tag} → {FilePath}",
159+
repository,
160+
tag,
161+
tarballPath);
162+
this.outputWriter.WriteLine(
163+
$"Successfully pulled SDK from ACR: {platformName} {version}");
164+
165+
// Write manifest digest sidecar for future freshness checks
166+
if (!string.IsNullOrEmpty(remoteDigest))
167+
{
168+
File.WriteAllText(digestPath, remoteDigest);
169+
}
170+
171+
return true;
172+
}
173+
catch (Exception ex)
174+
{
175+
this.logger.LogError(
176+
ex,
177+
"Error pulling SDK from ACR: {Repository}:{Tag}",
178+
repository,
179+
tag);
180+
this.outputWriter.WriteLine(
181+
$"Error pulling SDK from ACR: {platformName} {version}: {ex.Message}");
182+
return false;
183+
}
184+
}
185+
186+
/// <summary>
187+
/// Extracts the expected SDK .tar.gz file from an OCI layer tar archive.
188+
/// OCI layers use media type "application/vnd.docker.image.rootfs.diff.tar.gzip",
189+
/// so the blob must be decompressed before reading tar entries.
190+
/// </summary>
191+
private void ExtractFileFromTar(string layerPath, string outputPath, string expectedFileName)
192+
{
193+
using (var stream = File.OpenRead(layerPath))
194+
using (var gzipStream = new GZipStream(stream, CompressionMode.Decompress))
195+
using (var tarReader = new TarReader(gzipStream))
196+
{
197+
TarEntry entry;
198+
while ((entry = tarReader.GetNextEntry()) != null)
199+
{
200+
var name = entry.Name.TrimStart('.', '/');
201+
if (entry.DataStream != null && name.Equals(expectedFileName, StringComparison.OrdinalIgnoreCase))
202+
{
203+
entry.ExtractToFile(outputPath, overwrite: true);
204+
return;
205+
}
206+
}
207+
}
208+
209+
throw new InvalidOperationException($"Expected entry '{expectedFileName}' not found in OCI layer: {layerPath}");
210+
}
211+
}
212+
}

0 commit comments

Comments
 (0)