This commit is contained in:
Justin Lampe 2025-11-11 16:05:10 +01:00
parent 1e4599d6b5
commit bbe37706ae
4 changed files with 130 additions and 29 deletions

View File

@ -54,6 +54,8 @@ steps:
The `dotnet-runtime` input allows you to install .NET runtimes separately from SDKs. This is useful for multi-targeting scenarios where you need one SDK version but multiple runtime versions for testing. The `dotnet-runtime` input allows you to install .NET runtimes separately from SDKs. This is useful for multi-targeting scenarios where you need one SDK version but multiple runtime versions for testing.
When `dotnet-runtime` is specified, both the .NET Runtime (Microsoft.NETCore.App) and the ASP.NET Core Runtime (Microsoft.AspNetCore.App) are installed for each specified version.
**Example: Install SDK 10 with runtimes 8 and 9**: **Example: Install SDK 10 with runtimes 8 and 9**:
```yml ```yml
steps: steps:
@ -69,8 +71,6 @@ steps:
- run: dotnet test <my project> - run: dotnet test <my project>
``` ```
> **Note**: The `dotnet-runtime` input supports the same version syntax as `dotnet-version`. The `dotnet-quality` input applies to both SDK and runtime installations.
## Supported version syntax ## Supported version syntax
The `dotnet-version` and `dotnet-runtime` inputs support following syntax: The `dotnet-version` and `dotnet-runtime` inputs support following syntax:

View File

@ -346,7 +346,17 @@ describe('installer tests', () => {
}); });
describe('installRuntime() tests', () => { describe('installRuntime() tests', () => {
it('should throw the error in case of non-zero exit code of the runtime installation script. The error message should contain logs.', async () => { beforeAll(() => {
whichSpy.mockImplementation(() => Promise.resolve('PathToShell'));
chmodSyncSpy.mockImplementation(() => {});
readdirSpy.mockImplementation(() => Promise.resolve([]));
});
afterAll(() => {
jest.resetAllMocks();
});
it('should throw the error in case of non-zero exit code of the .NET runtime installation script. The error message should contain logs.', async () => {
const inputVersion = '8.0.402'; const inputVersion = '8.0.402';
const inputQuality = '' as QualityOptions; const inputQuality = '' as QualityOptions;
const errorMessage = 'fictitious error message!'; const errorMessage = 'fictitious error message!';
@ -368,6 +378,36 @@ describe('installer tests', () => {
); );
}); });
it('should throw the error in case of non-zero exit code of the ASP.NET Core runtime installation script. The error message should contain logs.', async () => {
const inputVersion = '8.0.402';
const inputQuality = '' as QualityOptions;
const errorMessage = 'fictitious aspnetcore error message!';
getExecOutputSpy
.mockImplementationOnce(() => {
return Promise.resolve({
exitCode: 0,
stdout: `Fictitious dotnet runtime version ${inputVersion} is installed`,
stderr: ''
});
})
.mockImplementationOnce(() => {
return Promise.resolve({
exitCode: 1,
stdout: '',
stderr: errorMessage
});
});
const dotnetInstaller = new installer.DotnetCoreInstaller(
inputVersion,
inputQuality
);
await expect(dotnetInstaller.installRuntime()).rejects.toThrow(
`Failed to install aspnetcore runtime, exit code: 1. ${errorMessage}`
);
});
it('should return version of .NET runtime after installation complete', async () => { it('should return version of .NET runtime after installation complete', async () => {
const inputVersion = '8.0.402'; const inputVersion = '8.0.402';
const inputQuality = '' as QualityOptions; const inputQuality = '' as QualityOptions;
@ -390,7 +430,7 @@ describe('installer tests', () => {
expect(installedVersion).toBe(inputVersion); expect(installedVersion).toBe(inputVersion);
}); });
it(`should supply '--runtime dotnet' argument to the installation script`, async () => { it(`should supply '--runtime dotnet' and '--runtime aspnetcore' arguments to the installation script`, async () => {
const inputVersion = '8.0.402'; const inputVersion = '8.0.402';
const inputQuality = '' as QualityOptions; const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`; const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`;
@ -411,17 +451,28 @@ describe('installer tests', () => {
await dotnetInstaller.installRuntime(); await dotnetInstaller.installRuntime();
const scriptArguments = ( // Check first call installs .NET runtime
const dotnetScriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[] getExecOutputSpy.mock.calls[0][1] as string[]
).join(' '); ).join(' ');
const expectedArgument = IS_WINDOWS const expectedDotnetArgument = IS_WINDOWS
? `-Runtime dotnet` ? `-Runtime dotnet`
: `--runtime dotnet`; : `--runtime dotnet`;
expect(scriptArguments).toContain(expectedArgument); expect(dotnetScriptArguments).toContain(expectedDotnetArgument);
// Check second call installs ASP.NET Core runtime
const aspnetcoreScriptArguments = (
getExecOutputSpy.mock.calls[1][1] as string[]
).join(' ');
const expectedAspnetcoreArgument = IS_WINDOWS
? `-Runtime aspnetcore`
: `--runtime aspnetcore`;
expect(aspnetcoreScriptArguments).toContain(expectedAspnetcoreArgument);
}); });
it(`should supply 'version' argument to the installation script if supplied version is in A.B.C syntax`, async () => { it(`should supply 'version' argument to both runtime installation scripts if supplied version is in A.B.C syntax`, async () => {
const inputVersion = '8.0.402'; const inputVersion = '8.0.402';
const inputQuality = '' as QualityOptions; const inputQuality = '' as QualityOptions;
const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`; const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`;
@ -442,14 +493,20 @@ describe('installer tests', () => {
await dotnetInstaller.installRuntime(); await dotnetInstaller.installRuntime();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS const expectedArgument = IS_WINDOWS
? `-Version ${inputVersion}` ? `-Version ${inputVersion}`
: `--version ${inputVersion}`; : `--version ${inputVersion}`;
expect(scriptArguments).toContain(expectedArgument); // Check both calls contain version argument
const dotnetScriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(dotnetScriptArguments).toContain(expectedArgument);
const aspnetcoreScriptArguments = (
getExecOutputSpy.mock.calls[1][1] as string[]
).join(' ');
expect(aspnetcoreScriptArguments).toContain(expectedArgument);
}); });
it(`should warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => { it(`should warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => {
@ -478,7 +535,7 @@ describe('installer tests', () => {
}); });
each(['8', '8.0', '8.0.x', '8.0.*', '8.0.X']).test( each(['8', '8.0', '8.0.x', '8.0.*', '8.0.X']).test(
`should supply 'quality' argument to the installation script if quality input is set and version (%s) is not in A.B.C syntax`, `should supply 'quality' argument to both runtime installation scripts if quality input is set and version (%s) is not in A.B.C syntax`,
async inputVersion => { async inputVersion => {
const inputQuality = 'ga' as QualityOptions; const inputQuality = 'ga' as QualityOptions;
const exitCode = 0; const exitCode = 0;
@ -499,14 +556,20 @@ describe('installer tests', () => {
await dotnetInstaller.installRuntime(); await dotnetInstaller.installRuntime();
const scriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
const expectedArgument = IS_WINDOWS const expectedArgument = IS_WINDOWS
? `-Quality ${inputQuality}` ? `-Quality ${inputQuality}`
: `--quality ${inputQuality}`; : `--quality ${inputQuality}`;
expect(scriptArguments).toContain(expectedArgument); // Check both calls contain quality argument
const dotnetScriptArguments = (
getExecOutputSpy.mock.calls[0][1] as string[]
).join(' ');
expect(dotnetScriptArguments).toContain(expectedArgument);
const aspnetcoreScriptArguments = (
getExecOutputSpy.mock.calls[1][1] as string[]
).join(' ');
expect(aspnetcoreScriptArguments).toContain(expectedArgument);
} }
); );
}); });

28
dist/setup/index.js vendored
View File

@ -100720,21 +100720,37 @@ class DotnetCoreInstaller {
const versionResolver = new DotnetVersionResolver(this.version); const versionResolver = new DotnetVersionResolver(this.version);
const dotnetVersion = await versionResolver.createDotnetVersion(); const dotnetVersion = await versionResolver.createDotnetVersion();
/** /**
* Install dotnet runtime only (without SDK) * Install .NET runtime (Microsoft.NETCore.App)
* Skip non-versioned files to avoid overwriting CLI * Skip non-versioned files to avoid overwriting CLI
*/ */
const runtimeInstallOutput = await new DotnetInstallScript() const dotnetRuntimeOutput = await new DotnetInstallScript()
// If dotnet CLI is already installed - avoid overwriting it // If dotnet CLI is already installed - avoid overwriting it
.useArguments(utils_1.IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files') .useArguments(utils_1.IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files')
// Install only runtime (Microsoft.NETCore.App) // Install .NET runtime (Microsoft.NETCore.App)
.useArguments(utils_1.IS_WINDOWS ? '-Runtime' : '--runtime', 'dotnet') .useArguments(utils_1.IS_WINDOWS ? '-Runtime' : '--runtime', 'dotnet')
// Use version provided by user // Use version provided by user
.useVersion(dotnetVersion, this.quality) .useVersion(dotnetVersion, this.quality)
.execute(); .execute();
if (runtimeInstallOutput.exitCode) { if (dotnetRuntimeOutput.exitCode) {
throw new Error(`Failed to install dotnet runtime, exit code: ${runtimeInstallOutput.exitCode}. ${runtimeInstallOutput.stderr}`); throw new Error(`Failed to install dotnet runtime, exit code: ${dotnetRuntimeOutput.exitCode}. ${dotnetRuntimeOutput.stderr}`);
} }
return this.parseInstalledVersion(runtimeInstallOutput.stdout); /**
* Install ASP.NET Core runtime (Microsoft.AspNetCore.App)
* Skip non-versioned files to avoid overwriting CLI
*/
const aspnetcoreRuntimeOutput = await new DotnetInstallScript()
// If dotnet CLI is already installed - avoid overwriting it
.useArguments(utils_1.IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files')
// Install ASP.NET Core runtime (Microsoft.AspNetCore.App)
.useArguments(utils_1.IS_WINDOWS ? '-Runtime' : '--runtime', 'aspnetcore')
// Use version provided by user
.useVersion(dotnetVersion, this.quality)
.execute();
if (aspnetcoreRuntimeOutput.exitCode) {
throw new Error(`Failed to install aspnetcore runtime, exit code: ${aspnetcoreRuntimeOutput.exitCode}. ${aspnetcoreRuntimeOutput.stderr}`);
}
// Return the .NET runtime version (both should be the same version)
return this.parseInstalledVersion(dotnetRuntimeOutput.stdout);
} }
parseInstalledVersion(stdout) { parseInstalledVersion(stdout) {
const regex = /(?<version>\d+\.\d+\.\d+[a-z0-9._-]*)/gm; const regex = /(?<version>\d+\.\d+\.\d+[a-z0-9._-]*)/gm;

View File

@ -316,27 +316,49 @@ export class DotnetCoreInstaller {
const dotnetVersion = await versionResolver.createDotnetVersion(); const dotnetVersion = await versionResolver.createDotnetVersion();
/** /**
* Install dotnet runtime only (without SDK) * Install .NET runtime (Microsoft.NETCore.App)
* Skip non-versioned files to avoid overwriting CLI * Skip non-versioned files to avoid overwriting CLI
*/ */
const runtimeInstallOutput = await new DotnetInstallScript() const dotnetRuntimeOutput = await new DotnetInstallScript()
// If dotnet CLI is already installed - avoid overwriting it // If dotnet CLI is already installed - avoid overwriting it
.useArguments( .useArguments(
IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files' IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files'
) )
// Install only runtime (Microsoft.NETCore.App) // Install .NET runtime (Microsoft.NETCore.App)
.useArguments(IS_WINDOWS ? '-Runtime' : '--runtime', 'dotnet') .useArguments(IS_WINDOWS ? '-Runtime' : '--runtime', 'dotnet')
// Use version provided by user // Use version provided by user
.useVersion(dotnetVersion, this.quality) .useVersion(dotnetVersion, this.quality)
.execute(); .execute();
if (runtimeInstallOutput.exitCode) { if (dotnetRuntimeOutput.exitCode) {
throw new Error( throw new Error(
`Failed to install dotnet runtime, exit code: ${runtimeInstallOutput.exitCode}. ${runtimeInstallOutput.stderr}` `Failed to install dotnet runtime, exit code: ${dotnetRuntimeOutput.exitCode}. ${dotnetRuntimeOutput.stderr}`
); );
} }
return this.parseInstalledVersion(runtimeInstallOutput.stdout); /**
* Install ASP.NET Core runtime (Microsoft.AspNetCore.App)
* Skip non-versioned files to avoid overwriting CLI
*/
const aspnetcoreRuntimeOutput = await new DotnetInstallScript()
// If dotnet CLI is already installed - avoid overwriting it
.useArguments(
IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files'
)
// Install ASP.NET Core runtime (Microsoft.AspNetCore.App)
.useArguments(IS_WINDOWS ? '-Runtime' : '--runtime', 'aspnetcore')
// Use version provided by user
.useVersion(dotnetVersion, this.quality)
.execute();
if (aspnetcoreRuntimeOutput.exitCode) {
throw new Error(
`Failed to install aspnetcore runtime, exit code: ${aspnetcoreRuntimeOutput.exitCode}. ${aspnetcoreRuntimeOutput.stderr}`
);
}
// Return the .NET runtime version (both should be the same version)
return this.parseInstalledVersion(dotnetRuntimeOutput.stdout);
} }
private parseInstalledVersion(stdout: string): string | null { private parseInstalledVersion(stdout: string): string | null {