Add support for optional architecture input for cross-architecture .NET installs (#700)

* fix basic validation with npm command

* Revert "fix basic validation with npm command"

This reverts commit 27a0803a2a.

* add architecture support

* updated installdir logic

* update architecture resolution

* update normalizeArch
This commit is contained in:
priya-kinthali
2026-02-27 00:47:54 +05:30
committed by GitHub
parent 16c7b3c2fa
commit 02574b18e2
8 changed files with 401 additions and 8 deletions

View File

@@ -2,6 +2,7 @@ import each from 'jest-each';
import semver from 'semver';
import fs from 'fs';
import fspromises from 'fs/promises';
import os from 'os';
import * as exec from '@actions/exec';
import * as core from '@actions/core';
import * as io from '@actions/io';
@@ -327,6 +328,143 @@ describe('installer tests', () => {
);
});
}
it(`should supply 'architecture' argument to the installation script when architecture is provided`, async () => {
const inputVersion = '10.0.101';
const inputQuality = '' as QualityOptions;
const inputArchitecture = 'x64';
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality,
inputArchitecture
);
await dotnetInstaller.installDotnet();
const callIndex = 1;
const scriptArguments = (
getExecOutputSpy.mock.calls[callIndex][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS
? `-Architecture ${inputArchitecture}`
: `--architecture ${inputArchitecture}`;
expect(scriptArguments).toContain(expectedArgument);
});
it(`should NOT supply 'architecture' argument when architecture is not provided`, async () => {
const inputVersion = '10.0.101';
const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await dotnetInstaller.installDotnet();
const callIndex = 1;
const scriptArguments = (
getExecOutputSpy.mock.calls[callIndex][1] as string[]
).join(' ');
expect(scriptArguments).not.toContain('--architecture');
expect(scriptArguments).not.toContain('-Architecture');
});
it(`should supply 'install-dir' with arch subdirectory for cross-arch install`, async () => {
const inputVersion = '10.0.101';
const inputQuality = '' as QualityOptions;
const inputArchitecture = 'x64';
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
// Mock os.arch() to return a different arch to simulate cross-arch
const archSpy = jest.spyOn(os, 'arch').mockReturnValue('arm64');
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality,
inputArchitecture
);
await dotnetInstaller.installDotnet();
const callIndex = 1;
const scriptArguments = (
getExecOutputSpy.mock.calls[callIndex][1] as string[]
).join(' ');
const expectedInstallDirFlag = IS_WINDOWS
? '-InstallDir'
: '--install-dir';
expect(scriptArguments).toContain(expectedInstallDirFlag);
expect(scriptArguments).toContain(inputArchitecture);
archSpy.mockRestore();
});
it(`should NOT supply 'install-dir' when architecture matches runner's native arch`, async () => {
const inputVersion = '10.0.101';
const inputQuality = '' as QualityOptions;
const nativeArch = os.arch().toLowerCase();
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
getExecOutputSpy.mockImplementation(() => {
return Promise.resolve({
exitCode: 0,
stdout: `${stdout}`,
stderr: ''
});
});
maxSatisfyingSpy.mockImplementation(() => inputVersion);
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality,
nativeArch
);
await dotnetInstaller.installDotnet();
const callIndex = 1;
const scriptArguments = (
getExecOutputSpy.mock.calls[callIndex][1] as string[]
).join(' ');
expect(scriptArguments).not.toContain('--install-dir');
expect(scriptArguments).not.toContain('-InstallDir');
});
});
describe('addToPath() tests', () => {
@@ -346,6 +484,32 @@ describe('installer tests', () => {
});
});
describe('normalizeArch() tests', () => {
it(`should normalize 'amd64' to 'x64'`, () => {
expect(installer.normalizeArch('amd64')).toBe('x64');
});
it(`should normalize 'AMD64' to 'x64' (case-insensitive)`, () => {
expect(installer.normalizeArch('AMD64')).toBe('x64');
});
it(`should pass through 'x64' unchanged`, () => {
expect(installer.normalizeArch('x64')).toBe('x64');
});
it(`should pass through 'arm64' unchanged`, () => {
expect(installer.normalizeArch('arm64')).toBe('arm64');
});
it(`should lowercase 'ARM64'`, () => {
expect(installer.normalizeArch('ARM64')).toBe('arm64');
});
it(`should pass through 'x86' unchanged`, () => {
expect(installer.normalizeArch('x86')).toBe('x86');
});
});
describe('DotnetVersionResolver tests', () => {
describe('createDotnetVersion() tests', () => {
each([

View File

@@ -2,7 +2,7 @@ import * as core from '@actions/core';
import fs from 'fs';
import semver from 'semver';
import * as auth from '../src/authutil';
import os from 'os';
import * as setup from '../src/setup-dotnet';
import {DotnetCoreInstaller, DotnetInstallDir} from '../src/installer';
import * as cacheUtils from '../src/cache-utils';
@@ -221,5 +221,40 @@ describe('setup-dotnet tests', () => {
await setup.run();
expect(restoreCacheSpy).not.toHaveBeenCalled();
});
it('should pass valid architecture input to DotnetCoreInstaller', async () => {
inputs['dotnet-version'] = ['10.0.101'];
inputs['dotnet-quality'] = '';
inputs['architecture'] = os.arch().toLowerCase();
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
await setup.run();
expect(installDotnetSpy).toHaveBeenCalledTimes(1);
expect(DotnetInstallDir.addToPath).toHaveBeenCalledTimes(1);
});
it('should work with empty architecture input for auto-detection', async () => {
inputs['dotnet-version'] = ['10.0.101'];
inputs['dotnet-quality'] = '';
inputs['architecture'] = '';
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
await setup.run();
expect(installDotnetSpy).toHaveBeenCalledTimes(1);
expect(DotnetInstallDir.addToPath).toHaveBeenCalledTimes(1);
});
it('should fail the action if unsupported architecture is provided', async () => {
inputs['dotnet-version'] = ['10.0.101'];
inputs['dotnet-quality'] = '';
inputs['architecture'] = 'x688';
const expectedErrorMessage = `Value 'x688' is not supported for the 'architecture' option. Supported values are: x64, x86, arm64, amd64, arm, s390x, ppc64le, riscv64.`;
await setup.run();
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
});
});
});