Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public static IServiceCollection AddBuildScriptGeneratorServices(this IServiceCo
services.AddSingleton<DefaultPlatformsInformationProvider>();
services.AddSingleton<PlatformsInstallationScriptProvider>();
services.AddSingleton<IExternalSdkProvider, ExternalSdkProvider>();
services.AddSingleton<IMcrSdkProvider, McrSdkProvider>();
services.AddSingleton<ISdkResolver, SdkResolver>();
services.AddHttpClient("general", httpClient =>
{
// NOTE: Setting user agent is required to avoid receiving 403 Forbidden response.
Expand Down
39 changes: 39 additions & 0 deletions src/BuildScriptGenerator/Contracts/IMcrSdkProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------

using System.Threading.Tasks;

namespace Microsoft.Oryx.BuildScriptGenerator
{
/// <summary>
/// Interface for MCR (Microsoft Container Registry) SDK provider that pulls
/// SDK tarballs from Docker images hosted in MCR.
/// </summary>
public interface IMcrSdkProvider
{
/// <summary>
/// The default base URL for MCR SDK images.
/// Images follow the convention: {BaseUrl}/{platformName}:{version}-{debianFlavor}.
/// </summary>
public const string DefaultMcrSdkImageBaseUrl = "mcr.microsoft.com/oryx/sdks";

/// <summary>
/// The directory inside the Docker image where the SDK tarball is stored.
/// </summary>
public const string SdkDirectoryInImage = "/sdks";

/// <summary>
/// Pulls an SDK tarball from a Docker image in MCR and stores it in the local cache.
/// The tarball is extracted from the image and placed at the same cache directory
/// used by <see cref="IExternalSdkProvider"/> so that existing installation scripts
/// can locate and extract it.
/// </summary>
/// <param name="platformName">The name of the platform (e.g., "nodejs", "python", "dotnet").</param>
/// <param name="version">The version of the SDK to pull.</param>
/// <param name="debianFlavor">The Debian flavor (e.g., "bookworm", "bullseye").</param>
/// <returns>True if the SDK was successfully pulled and cached; false otherwise.</returns>
Task<bool> PullSdkAsync(string platformName, string version, string debianFlavor);
}
}
29 changes: 29 additions & 0 deletions src/BuildScriptGenerator/Contracts/ISdkResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------

namespace Microsoft.Oryx.BuildScriptGenerator
{
/// <summary>
/// Resolves SDK binaries by trying multiple sources in priority order:
/// MCR Docker images → External SDK provider (blob storage) → CDN fallback.
/// </summary>
public interface ISdkResolver
{
/// <summary>
/// Attempts to fetch the SDK tarball for the given platform and version from available
/// SDK sources (MCR, External SDK provider). If a source succeeds, the tarball will be
/// available in the shared SDK cache directory and the caller can generate an installation
/// script that skips the binary download.
/// </summary>
/// <param name="platformName">The name of the platform (e.g., "nodejs", "python", "dotnet").</param>
/// <param name="version">The version of the SDK to fetch.</param>
/// <param name="debianFlavor">The Debian flavor (e.g., "bookworm", "bullseye").</param>
/// <returns>
/// True if the SDK was successfully fetched and cached from any source;
/// false if no source could provide the SDK (caller should fall back to CDN download).
/// </returns>
bool TryFetchSdk(string platformName, string version, string debianFlavor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public IEnumerable<PlatformInfo> GetPlatformsInfo(RepositoryContext context)
this.outputWriter.WriteLine("External SDK provider is enabled.");
}

if (this.commonOptions.EnableMcrSdkProvider)
{
this.outputWriter.WriteLine("MCR SDK provider is enabled.");
}

foreach (var platform in this.platforms)
{
// Check if a platform is enabled or not
Expand Down
46 changes: 13 additions & 33 deletions src/BuildScriptGenerator/DotNetCore/DotnetCorePlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class DotNetCorePlatform : IProgrammingPlatform
private readonly BuildScriptGeneratorOptions commonOptions;
private readonly DotNetCorePlatformInstaller platformInstaller;
private readonly GlobalJsonSdkResolver globalJsonSdkResolver;
private readonly IExternalSdkProvider externalSdkProvider;
private readonly ISdkResolver sdkResolver;
private readonly TelemetryClient telemetryClient;

/// <summary>
Expand All @@ -47,6 +47,7 @@ internal class DotNetCorePlatform : IProgrammingPlatform
/// <param name="dotNetCoreScriptGeneratorOptions">The options if .NET platform.</param>
/// <param name="platformInstaller">The <see cref="DotNetCorePlatformInstaller"/>.</param>
/// <param name="globalJsonSdkResolver">The <see cref="GlobalJsonSdkResolver"/>.</param>
/// <param name="sdkResolver">The <see cref="ISdkResolver"/>.</param>
public DotNetCorePlatform(
IDotNetCoreVersionProvider versionProvider,
ILogger<DotNetCorePlatform> logger,
Expand All @@ -55,7 +56,7 @@ public DotNetCorePlatform(
IOptions<DotNetCoreScriptGeneratorOptions> dotNetCoreScriptGeneratorOptions,
DotNetCorePlatformInstaller platformInstaller,
GlobalJsonSdkResolver globalJsonSdkResolver,
IExternalSdkProvider externalSdkProvider,
ISdkResolver sdkResolver,
TelemetryClient telemetryClient)
{
this.versionProvider = versionProvider;
Expand All @@ -65,7 +66,7 @@ public DotNetCorePlatform(
this.commonOptions = commonOptions.Value;
this.platformInstaller = platformInstaller;
this.globalJsonSdkResolver = globalJsonSdkResolver;
this.externalSdkProvider = externalSdkProvider;
this.sdkResolver = sdkResolver;
this.telemetryClient = telemetryClient;
}

Expand Down Expand Up @@ -244,36 +245,15 @@ public string GetInstallerScriptSnippet(
}
else
{
if (this.commonOptions.EnableExternalSdkProvider)
{
this.logger.LogDebug("DotNetCore SDK version {version} is not installed. External SDK provider is enabled so trying to fetch SDK using it.", dotNetCorePlatformDetectorResult.SdkVersion);

try
{
var blobName = BlobNameHelper.GetBlobNameForVersion(this.Name, dotNetCorePlatformDetectorResult.SdkVersion, this.commonOptions.DebianFlavor);
var isExternalFetchSuccess = this.externalSdkProvider.RequestBlobAsync(this.Name, blobName).Result;
if (isExternalFetchSuccess)
{
this.logger.LogDebug("DotNetCore SDK version {version} is fetched successfully using external SDK provider. So generating an installation script snippet which skips platform binary download.", dotNetCorePlatformDetectorResult.SdkVersion);
installationScriptSnippet = this.platformInstaller.GetInstallerScriptSnippet(dotNetCorePlatformDetectorResult.SdkVersion, skipSdkBinaryDownload: true);
}
else
{
this.logger.LogDebug("DotNetCore SDK version {version} is not fetched successfully using external SDK provider. So generating an installation script snippet for it.", dotNetCorePlatformDetectorResult.SdkVersion);
installationScriptSnippet = this.platformInstaller.GetInstallerScriptSnippet(dotNetCorePlatformDetectorResult.SdkVersion);
}
}
catch (Exception ex)
{
this.logger.LogError(ex, "Error while fetching DotNetCore SDK version version {version} using external SDK provider.", dotNetCorePlatformDetectorResult.SdkVersion);
installationScriptSnippet = this.platformInstaller.GetInstallerScriptSnippet(dotNetCorePlatformDetectorResult.SdkVersion);
}
}
else
{
this.logger.LogDebug("DotNetCore SDK version {globalJsonSdkVersion} is not installed. So generating an installation script snippet for it.", dotNetCorePlatformDetectorResult.SdkVersion);
installationScriptSnippet = this.platformInstaller.GetInstallerScriptSnippet(dotNetCorePlatformDetectorResult.SdkVersion);
}
this.logger.LogDebug(
"DotNetCore SDK version {version} is not installed. Trying to fetch SDK.",
dotNetCorePlatformDetectorResult.SdkVersion);

var sdkFetched = this.sdkResolver.TryFetchSdk(
this.Name, dotNetCorePlatformDetectorResult.SdkVersion, this.commonOptions.DebianFlavor);

installationScriptSnippet = this.platformInstaller.GetInstallerScriptSnippet(
dotNetCorePlatformDetectorResult.SdkVersion, skipSdkBinaryDownload: sdkFetched);
}
}
else
Expand Down
Loading