Add check-latest input to reuse a locally installed .NET SDK and skip network calls (#774)

* check-latest-input feature implemented

* e2e test update

* e2e-tests.yml updated

* update documentation

* fix: align local SDK matching with the online resolver

* implement review comments

* implement review comments

* implement review comments

* implement review comments

* implement review comments

* implement review comments

* implement review comments

---------

Co-authored-by: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com>
This commit is contained in:
v-mahabaleshwars
2026-09-28 20:16:01 +05:30
committed by GitHub
parent 4d6fc87ace
commit 074373ff88
8 changed files with 1790 additions and 61 deletions

View File

@@ -31,6 +31,8 @@ type SupportedArchitecture = (typeof supportedArchitectures)[number];
export type QualityOptions = (typeof qualityOptions)[number] | '';
const CHECK_LATEST_ENV_VAR = 'DOTNET_CHECK_LATEST';
function isValidChannel(channel: string): boolean {
const upper = channel.toUpperCase();
if (upper === 'LTS' || upper === 'STS') return true;
@@ -54,8 +56,24 @@ export async function run() {
// Proxy, auth, (etc) are still set up, even if no version is identified
//
const versions = core.getMultilineInput('dotnet-version');
const explicitVersions = new Set(versions);
const globalJsonConstraints = new Map<
string,
{minimumVersion: string; rollForward?: string}
>();
const addVersionFromGlobalJson = (globalJsonPath: string) => {
const {version, minimumVersion, rollForward} =
getVersionFromGlobalJson(globalJsonPath);
versions.push(version);
const isRedundantExplicitPin =
explicitVersions.has(version) && version === minimumVersion;
if (minimumVersion && !isRedundantExplicitPin) {
globalJsonConstraints.set(version, {minimumVersion, rollForward});
}
};
const installedDotnetVersions: (string | null)[] = [];
const architecture = getArchitectureInput();
const checkLatest = getCheckLatestInput();
let dotnetChannel = core.getInput('dotnet-channel');
const isLatestRequested = versions.some(
@@ -87,7 +105,7 @@ export async function run() {
`The specified global.json file '${globalJsonFileInput}' does not exist`
);
}
versions.push(getVersionFromGlobalJson(globalJsonPath));
addVersionFromGlobalJson(globalJsonPath);
}
if (!versions.length) {
@@ -95,7 +113,7 @@ export async function run() {
core.debug('No version found, trying to find version from global.json');
const globalJsonPath = path.join(process.cwd(), 'global.json');
if (fs.existsSync(globalJsonPath)) {
versions.push(getVersionFromGlobalJson(globalJsonPath));
addVersionFromGlobalJson(globalJsonPath);
} else {
core.info(
`The global.json wasn't found in the root directory. No .NET version will be installed.`
@@ -117,11 +135,15 @@ export async function run() {
versions.map(v => (v.toLowerCase() === 'latest' ? 'latest' : v))
);
for (const version of uniqueVersions) {
const constraint = globalJsonConstraints.get(version);
dotnetInstaller = new DotnetCoreInstaller(
version,
quality,
architecture,
version.toLowerCase() === 'latest' ? dotnetChannel : undefined
version.toLowerCase() === 'latest' ? dotnetChannel : undefined,
checkLatest,
constraint?.minimumVersion,
constraint?.rollForward
);
const installedVersion = await dotnetInstaller.installDotnet();
installedDotnetVersions.push(installedVersion);
@@ -200,8 +222,51 @@ function getArchitectureInput(): SupportedArchitecture | '' {
);
}
function getVersionFromGlobalJson(globalJsonPath: string): string {
function getCheckLatestInput(): boolean {
if ((core.getInput('check-latest') || '').trim()) {
return core.getBooleanInput('check-latest');
}
const rawEnvValue = (process.env[CHECK_LATEST_ENV_VAR] || '').trim();
if (rawEnvValue) {
const envValue = rawEnvValue.toLowerCase();
if (envValue === 'true' || envValue === 'false') {
core.debug(
`The 'check-latest' option is set to '${envValue}' by the ${CHECK_LATEST_ENV_VAR} environment variable.`
);
return envValue === 'true';
}
core.warning(
`Value '${rawEnvValue}' is not supported for the ${CHECK_LATEST_ENV_VAR} environment variable. Supported values are: true, false. The 'check-latest' option falls back to 'true'.`
);
}
return true;
}
interface GlobalJsonVersion {
version: string;
minimumVersion?: string;
rollForward?: string;
}
const ROLL_FORWARD_POLICIES = [
'patch',
'feature',
'minor',
'major',
'latestPatch',
'latestFeature',
'latestMinor',
'latestMajor'
];
const versionPattern = /^\d+\.\d+\.[1-9]\d{2,}$/;
function getVersionFromGlobalJson(globalJsonPath: string): GlobalJsonVersion {
let version = '';
let minimumVersion: string | undefined;
let rollForwardPolicy: string | undefined;
const globalJson = JSON5.parse(
// .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim(),
@@ -215,7 +280,6 @@ function getVersionFromGlobalJson(globalJsonPath: string): string {
version = globalJson.sdk.version;
const rollForward = globalJson.sdk.rollForward;
if (rollForward && !semver.prerelease(version)) {
const versionPattern = /^\d+\.\d+\.[1-9]\d{2,}$/;
if (!versionPattern.test(version)) {
throw new Error(
`Version '${version}' is not valid for the 'sdk.version' value in global.json. ` +
@@ -244,9 +308,21 @@ function getVersionFromGlobalJson(globalJsonPath: string): string {
version = `${major}.${minor}.${feature}xx`;
break;
}
if (ROLL_FORWARD_POLICIES.includes(rollForward)) {
minimumVersion = globalJson.sdk.version;
rollForwardPolicy = rollForward;
}
} else if (
!rollForward &&
!semver.prerelease(version) &&
versionPattern.test(version)
) {
minimumVersion = version;
rollForwardPolicy = 'patch';
}
}
return version;
return {version, minimumVersion, rollForward: rollForwardPolicy};
}
function outputInstalledVersion(