From bbe37706aedb5aded08db6cf4468e36862507eef Mon Sep 17 00:00:00 2001 From: Justin Lampe Date: Tue, 11 Nov 2025 16:05:10 +0100 Subject: [PATCH] update --- README.md | 4 +- __tests__/installer.test.ts | 93 +++++++++++++++++++++++++++++++------ dist/setup/index.js | 28 ++++++++--- src/installer.ts | 34 +++++++++++--- 4 files changed, 130 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 55ac813..142cb53 100644 --- a/README.md +++ b/README.md @@ -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. +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**: ```yml steps: @@ -69,8 +71,6 @@ steps: - run: dotnet test ``` -> **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 The `dotnet-version` and `dotnet-runtime` inputs support following syntax: diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index af542de..7a83468 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -346,7 +346,17 @@ describe('installer 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 inputQuality = '' as QualityOptions; 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 () => { const inputVersion = '8.0.402'; const inputQuality = '' as QualityOptions; @@ -390,7 +430,7 @@ describe('installer tests', () => { 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 inputQuality = '' as QualityOptions; const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`; @@ -411,17 +451,28 @@ describe('installer tests', () => { await dotnetInstaller.installRuntime(); - const scriptArguments = ( + // Check first call installs .NET runtime + const dotnetScriptArguments = ( getExecOutputSpy.mock.calls[0][1] as string[] ).join(' '); - const expectedArgument = IS_WINDOWS + const expectedDotnetArgument = IS_WINDOWS ? `-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 inputQuality = '' as QualityOptions; const stdout = `Fictitious dotnet runtime version ${inputVersion} is installed`; @@ -442,14 +493,20 @@ describe('installer tests', () => { await dotnetInstaller.installRuntime(); - const scriptArguments = ( - getExecOutputSpy.mock.calls[0][1] as string[] - ).join(' '); const expectedArgument = IS_WINDOWS ? `-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 () => { @@ -478,7 +535,7 @@ describe('installer tests', () => { }); 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 => { const inputQuality = 'ga' as QualityOptions; const exitCode = 0; @@ -499,14 +556,20 @@ describe('installer tests', () => { await dotnetInstaller.installRuntime(); - const scriptArguments = ( - getExecOutputSpy.mock.calls[0][1] as string[] - ).join(' '); const expectedArgument = IS_WINDOWS ? `-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); } ); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 79e5fd5..e5fe526 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -100720,21 +100720,37 @@ class DotnetCoreInstaller { const versionResolver = new DotnetVersionResolver(this.version); 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 */ - const runtimeInstallOutput = await new DotnetInstallScript() + const dotnetRuntimeOutput = await new DotnetInstallScript() // If dotnet CLI is already installed - avoid overwriting it .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') // Use version provided by user .useVersion(dotnetVersion, this.quality) .execute(); - if (runtimeInstallOutput.exitCode) { - throw new Error(`Failed to install dotnet runtime, exit code: ${runtimeInstallOutput.exitCode}. ${runtimeInstallOutput.stderr}`); + if (dotnetRuntimeOutput.exitCode) { + 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) { const regex = /(?\d+\.\d+\.\d+[a-z0-9._-]*)/gm; diff --git a/src/installer.ts b/src/installer.ts index 30bfca2..371e9c6 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -316,27 +316,49 @@ export class DotnetCoreInstaller { 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 */ - const runtimeInstallOutput = await new DotnetInstallScript() + const dotnetRuntimeOutput = await new DotnetInstallScript() // If dotnet CLI is already installed - avoid overwriting it .useArguments( 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') // Use version provided by user .useVersion(dotnetVersion, this.quality) .execute(); - if (runtimeInstallOutput.exitCode) { + if (dotnetRuntimeOutput.exitCode) { 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 {