mirror of
https://github.com/actions/setup-dotnet.git
synced 2026-05-10 08:58:17 +01:00
Add dotnet-version: latest support with dotnet-channel input (#730)
* feat: add dotnet-version: latest keyword with dotnet-channel support (#497) * restore test-proxy container image * update e2e-tests.yml and documentation * fix(tests): correct release-type and support-phase values in latest-version test mocks
This commit is contained in:
41
.github/workflows/e2e-tests.yml
vendored
41
.github/workflows/e2e-tests.yml
vendored
@@ -649,3 +649,44 @@ jobs:
|
||||
- name: Verify dotnet
|
||||
shell: pwsh
|
||||
run: __tests__/verify-dotnet.ps1 -Patterns "^8.0.416$", "^9.0.308$", "^10.0.101$", "^8.0"
|
||||
|
||||
test-setup-latest-version:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Clear toolcache
|
||||
shell: pwsh
|
||||
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
|
||||
- name: Setup dotnet latest
|
||||
uses: ./
|
||||
with:
|
||||
dotnet-version: latest
|
||||
- name: Verify dotnet
|
||||
shell: pwsh
|
||||
run: __tests__/verify-dotnet.ps1 -Patterns "^\d+\.\d+\.\d+"
|
||||
|
||||
test-setup-latest-with-channel-abcxx:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system: [macos-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Clear toolcache
|
||||
shell: pwsh
|
||||
run: __tests__/clear-toolcache.ps1 ${{ runner.os }}
|
||||
- name: Setup dotnet latest with A.B.Cxx channel
|
||||
uses: ./
|
||||
with:
|
||||
dotnet-version: latest
|
||||
dotnet-channel: '9.0.1xx'
|
||||
- name: Verify dotnet
|
||||
shell: pwsh
|
||||
run: __tests__/verify-dotnet.ps1 -Patterns "^9\.0\.1\d{2}"
|
||||
|
||||
40
README.md
40
README.md
@@ -57,7 +57,30 @@ The `dotnet-version` input supports following syntax:
|
||||
- **A.B** or **A.B.x** (e.g. 8.0, 8.0.x) - installs the latest patch version of .NET SDK on the channel `8.0`, including prerelease versions (preview, rc)
|
||||
- **A** or **A.x** (e.g. 8, 8.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc)
|
||||
- **A.B.Cxx** (e.g. 8.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc).
|
||||
- **latest** - dynamically resolves to the highest active .NET SDK version. By default, it installs the latest **stable (GA)** version (excluding previews and end-of-life releases). Can be combined with `dotnet-channel` and `dotnet-quality`.
|
||||
|
||||
## Using with `dotnet-channel` input
|
||||
|
||||
The optional `dotnet-channel` input specifies the source channel for the installation. Supported values:
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `STS` | The most recent Standard Term Support release |
|
||||
| `LTS` | The most recent Long Term Support release |
|
||||
| `A.B` (e.g. `8.0`) | A specific release channel |
|
||||
| `A.B.Cxx` (e.g. `8.0.1xx`) | A specific SDK release (available since 5.0) |
|
||||
|
||||
> **Note**: The `dotnet-channel` input is only applied when `dotnet-version` is set to `latest`. If used with a specific version, a warning will be logged and the channel input will be ignored.
|
||||
|
||||
**Install latest LTS version:**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: latest
|
||||
dotnet-channel: LTS
|
||||
```
|
||||
|
||||
## Using the `architecture` input
|
||||
Using the architecture input, it is possible to specify the required .NET SDK architecture. Possible values: `x64`, `x86`, `arm64`, `amd64`, `arm`, `s390x`, `ppc64le`, `riscv64`. If the input is not specified, the architecture defaults to the host OS architecture (not all of the architectures are available on all platforms).
|
||||
@@ -77,9 +100,10 @@ steps:
|
||||
```
|
||||
|
||||
## Using the `dotnet-quality` input
|
||||
This input sets up the action to install the latest build of the specified quality in the channel. The possible values of `dotnet-quality` are: **daily**, **signed**, **validated**, **preview**, **ga**.
|
||||
|
||||
> **Note**: `dotnet-quality` input can be used only with .NET SDK version in 'A.B', 'A.B.x', 'A', 'A.x' and 'A.B.Cxx' formats where the major version is higher than 5. In other cases, `dotnet-quality` input will be ignored.
|
||||
The `dotnet-quality` input installs the latest build of the specified quality in the channel. Supported values: `daily`, `preview`, `ga`.
|
||||
|
||||
> **Note**: When used with a specific SDK version, `dotnet-quality` supports only `A.B`, `A.B.x`, `A`, `A.x`, and `A.B.Cxx` formats where the major version is higher than 5. For all other formats, `dotnet-quality` will be ignored.
|
||||
|
||||
```yml
|
||||
steps:
|
||||
@@ -91,6 +115,18 @@ steps:
|
||||
- run: dotnet build <my project>
|
||||
```
|
||||
|
||||
`dotnet-quality` can also be combined with `dotnet-version: latest` and `dotnet-channel` to target specific builds such as the latest `daily` build from the `LTS` channel.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: latest
|
||||
dotnet-channel: LTS
|
||||
dotnet-quality: daily
|
||||
```
|
||||
|
||||
## Using the `global-json-file` input
|
||||
`setup-dotnet` action can read .NET SDK version from a `global.json` file. Input `global-json-file` is used for specifying the path to the `global.json`. If the file that was supplied to `global-json-file` input doesn't exist, the action will fail with error.
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import * as io from '@actions/io';
|
||||
import * as installer from '../src/installer';
|
||||
|
||||
import {IS_WINDOWS} from '../src/utils';
|
||||
import {QualityOptions} from '../src/setup-dotnet';
|
||||
|
||||
describe('installer tests', () => {
|
||||
const env = process.env;
|
||||
@@ -40,7 +39,7 @@ describe('installer tests', () => {
|
||||
|
||||
it('should throw the error in case of non-zero exit code of the installation script. The error message should contain logs.', async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const errorMessage = 'fictitious error message!';
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -62,7 +61,7 @@ describe('installer tests', () => {
|
||||
|
||||
it('should return version of .NET SDK after installation complete', async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
@@ -84,7 +83,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should supply 'version' argument to the installation script if supplied version is in A.B.C syntax`, async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -122,7 +121,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = 'ga' as QualityOptions;
|
||||
const inputQuality = 'ga';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
@@ -147,7 +146,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should warn if the 'quality' input is set and version isn't in A.B.C syntax but major tag is lower then 6`, async () => {
|
||||
const inputVersion = '3.1';
|
||||
const inputQuality = 'ga' as QualityOptions;
|
||||
const inputQuality = 'ga';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -174,7 +173,7 @@ describe('installer tests', () => {
|
||||
each(['10', '10.0', '10.0.x', '10.0.*', '10.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`,
|
||||
async inputVersion => {
|
||||
const inputQuality = 'ga' as QualityOptions;
|
||||
const inputQuality = 'ga';
|
||||
const exitCode = 0;
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -214,7 +213,7 @@ describe('installer tests', () => {
|
||||
each(['10', '10.0', '10.0.x', '10.0.*', '10.0.X']).test(
|
||||
`should supply 'channel' argument to the installation script if version (%s) isn't in A.B.C syntax`,
|
||||
async inputVersion => {
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const exitCode = 0;
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -255,7 +254,7 @@ describe('installer tests', () => {
|
||||
it(`should supply '-ProxyAddress' argument to the installation script if env.variable 'https_proxy' is set`, async () => {
|
||||
process.env['https_proxy'] = 'https://proxy.com';
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -293,7 +292,7 @@ describe('installer tests', () => {
|
||||
it(`should supply '-ProxyBypassList' argument to the installation script if env.variable 'no_proxy' is set`, async () => {
|
||||
process.env['no_proxy'] = 'first.url,second.url';
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -331,7 +330,7 @@ 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 inputQuality = '';
|
||||
const inputArchitecture = 'x64';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
@@ -365,7 +364,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should NOT supply 'architecture' argument when architecture is not provided`, async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
getExecOutputSpy.mockImplementation(() => {
|
||||
@@ -395,7 +394,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should supply 'install-dir' with arch subdirectory for cross-arch install`, async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const inputArchitecture = 'x64';
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
@@ -436,7 +435,7 @@ describe('installer tests', () => {
|
||||
|
||||
it(`should NOT supply 'install-dir' when architecture matches runner's native arch`, async () => {
|
||||
const inputVersion = '10.0.101';
|
||||
const inputQuality = '' as QualityOptions;
|
||||
const inputQuality = '';
|
||||
const nativeArch = os.arch().toLowerCase();
|
||||
const stdout = `Fictitious dotnet version ${inputVersion} is installed`;
|
||||
|
||||
|
||||
223
__tests__/latest-version.test.ts
Normal file
223
__tests__/latest-version.test.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import {DotnetVersionResolver} from '../src/installer';
|
||||
import * as hc from '@actions/http-client';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
// Mock http-client
|
||||
jest.mock('@actions/http-client');
|
||||
|
||||
describe('DotnetVersionResolver with latest', () => {
|
||||
let getJsonMock: jest.Mock;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
getJsonMock = jest.fn();
|
||||
(hc.HttpClient as any).mockImplementation(() => {
|
||||
return {
|
||||
getJson: getJsonMock
|
||||
};
|
||||
});
|
||||
warningSpy = jest.spyOn(core, 'warning').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const mockReleases = {
|
||||
'releases-index': [
|
||||
{
|
||||
'channel-version': '10.0',
|
||||
'support-phase': 'preview',
|
||||
'release-type': 'lts'
|
||||
},
|
||||
{
|
||||
'channel-version': '9.0',
|
||||
'support-phase': 'active',
|
||||
'release-type': 'sts'
|
||||
},
|
||||
{
|
||||
'channel-version': '8.0',
|
||||
'support-phase': 'active',
|
||||
'release-type': 'lts'
|
||||
},
|
||||
{
|
||||
'channel-version': '7.0',
|
||||
'support-phase': 'eol',
|
||||
'release-type': 'sts'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
it('should resolve "latest" to highest stable version by default', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('9.0');
|
||||
expect(version.type.toLowerCase()).toContain('channel');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
});
|
||||
|
||||
it('should resolve "LATEST" (uppercase) to highest stable version', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('LATEST');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('9.0');
|
||||
expect(version.type.toLowerCase()).toContain('channel');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
});
|
||||
|
||||
it('should resolve "latest" to highest preview version if quality is preview', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', 'preview');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('10.0');
|
||||
});
|
||||
|
||||
it('should resolve "latest" with channel filter LTS', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', '', 'LTS');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('8.0');
|
||||
});
|
||||
|
||||
it('should resolve "latest" with channel filter STS', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', '', 'STS');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('9.0');
|
||||
});
|
||||
|
||||
it('should resolve "latest" with channel filter STS and preview quality', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', 'preview', 'STS');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
// preview quality includes all support-phases; STS filter → 9.0 (active, sts)
|
||||
expect(version.value).toBe('9.0');
|
||||
});
|
||||
|
||||
it('should warn if channel is provided but version is not latest', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('8.0', '', 'LTS');
|
||||
await resolver.createDotnetVersion();
|
||||
|
||||
expect(warningSpy).toHaveBeenCalledWith(
|
||||
`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when releases-index API returns empty active releases', async () => {
|
||||
const emptyReleases = {
|
||||
'releases-index': [
|
||||
{
|
||||
'channel-version': '7.0',
|
||||
'support-phase': 'eol',
|
||||
'release-type': 'sts'
|
||||
}
|
||||
]
|
||||
};
|
||||
getJsonMock.mockResolvedValue({result: emptyReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest');
|
||||
|
||||
await expect(resolver.createDotnetVersion()).rejects.toThrow(
|
||||
/Could not find any active releases/
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when releases-index response has unexpected format', async () => {
|
||||
getJsonMock.mockResolvedValue({result: {}});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest');
|
||||
|
||||
await expect(resolver.createDotnetVersion()).rejects.toThrow(
|
||||
/Unexpected response format/
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when releases-index response is null', async () => {
|
||||
getJsonMock.mockResolvedValue({result: null});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest');
|
||||
|
||||
await expect(resolver.createDotnetVersion()).rejects.toThrow(
|
||||
/Unexpected response format/
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve "latest" with ga quality same as default (no previews)', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', 'ga');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
// ga should behave like no quality — skip preview (10.0), pick 9.0
|
||||
expect(version.value).toBe('9.0');
|
||||
});
|
||||
|
||||
it('should resolve "latest" with LTS channel and daily quality', async () => {
|
||||
getJsonMock.mockResolvedValue({result: mockReleases});
|
||||
|
||||
const resolver = new DotnetVersionResolver('latest', 'daily', 'LTS');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
// daily allows previews, LTS filter applies — 10.0 (preview, lts) is the highest LTS
|
||||
expect(version.value).toBe('10.0');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
});
|
||||
|
||||
it('should resolve "latest" with A.B channel directly without API call', async () => {
|
||||
const resolver = new DotnetVersionResolver('latest', '', '8.0');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('8.0');
|
||||
expect(version.type.toLowerCase()).toContain('channel');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
// Should NOT call the API
|
||||
expect(getJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should resolve "latest" with A.B.Cxx channel directly without API call', async () => {
|
||||
const resolver = new DotnetVersionResolver('latest', '', '8.0.1xx');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('8.0.1xx');
|
||||
expect(version.type.toLowerCase()).toContain('channel');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
// Should NOT call the API
|
||||
expect(getJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should resolve "latest" with A.B channel for older version with qualityFlag false', async () => {
|
||||
const resolver = new DotnetVersionResolver('latest', '', '3.1');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('3.1');
|
||||
expect(version.type.toLowerCase()).toContain('channel');
|
||||
// major 3 < 6 → qualityFlag false
|
||||
expect(version.qualityFlag).toBe(false);
|
||||
expect(getJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should resolve "latest" with A.B.Cxx channel and quality', async () => {
|
||||
const resolver = new DotnetVersionResolver('latest', 'ga', '8.0.2xx');
|
||||
const version = await resolver.createDotnetVersion();
|
||||
|
||||
expect(version.value).toBe('8.0.2xx');
|
||||
expect(version.qualityFlag).toBe(true);
|
||||
expect(getJsonMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -84,7 +84,7 @@ describe('setup-dotnet tests', () => {
|
||||
inputs['dotnet-version'] = ['10.0'];
|
||||
inputs['dotnet-quality'] = 'fictitiousQuality';
|
||||
|
||||
const expectedErrorMessage = `Value '${inputs['dotnet-quality']}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`;
|
||||
const expectedErrorMessage = `Value '${inputs['dotnet-quality']}' is not supported for the 'dotnet-quality' option. Supported values are: daily, preview, ga.`;
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
|
||||
@@ -256,5 +256,95 @@ describe('setup-dotnet tests', () => {
|
||||
await setup.run();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('should fail the action if unsupported dotnet-channel value is provided with latest', async () => {
|
||||
inputs['dotnet-version'] = ['latest'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = 'invalid';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
const expectedErrorMessage = `Value 'invalid' is not supported for the 'dotnet-channel' option. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`;
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('should warn but not fail if unsupported dotnet-channel value is provided with a specific version', async () => {
|
||||
inputs['dotnet-version'] = ['8.0.x'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = 'invalid';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(warningSpy).toHaveBeenCalledWith(
|
||||
`Value 'invalid' is not supported for the 'dotnet-channel' option and will be ignored because 'dotnet-version' is not set to 'latest'. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass valid dotnet-channel value through without error', async () => {
|
||||
inputs['dotnet-version'] = ['latest'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = 'LTS';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass A.B channel value through without error when used with latest', async () => {
|
||||
inputs['dotnet-version'] = ['latest'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = '8.0';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass A.B.Cxx channel value through without error when used with latest', async () => {
|
||||
inputs['dotnet-version'] = ['latest'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = '8.0.1xx';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail with A.B.Cxx channel if major version is below 5', async () => {
|
||||
inputs['dotnet-version'] = ['latest'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = '3.1.1xx';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
const expectedErrorMessage = `Value '3.1.1xx' is not supported for the 'dotnet-channel' option. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`;
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('should warn and not fail if valid dotnet-channel is provided with a non-latest version', async () => {
|
||||
inputs['dotnet-version'] = ['8.0.x'];
|
||||
inputs['dotnet-quality'] = '';
|
||||
inputs['dotnet-channel'] = 'LTS';
|
||||
inputs['architecture'] = '';
|
||||
|
||||
installDotnetSpy.mockImplementation(() => Promise.resolve(''));
|
||||
|
||||
await setup.run();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(warningSpy).toHaveBeenCalledWith(
|
||||
`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,9 +6,11 @@ branding:
|
||||
color: green
|
||||
inputs:
|
||||
dotnet-version:
|
||||
description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x, 6.0.2xx'
|
||||
description: 'Optional SDK version(s) to use. If not provided, will install global.json version when available. Examples: 2.2.104, 3.1, 3.1.x, 3.x, 6.0.2xx, latest'
|
||||
dotnet-quality:
|
||||
description: 'Optional quality of the build. The possible values are: daily, signed, validated, preview, ga.'
|
||||
description: 'Optional quality of the build. The possible values are: daily, preview, ga.'
|
||||
dotnet-channel:
|
||||
description: 'Optional channel for the installation. The possible values are: STS, LTS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx, available since 5.0). To be used with "dotnet-version: latest".'
|
||||
global-json-file:
|
||||
description: 'Optional global.json location, if your global.json isn''t located in the root of the repo.'
|
||||
source-url:
|
||||
|
||||
128
dist/setup/index.js
vendored
128
dist/setup/index.js
vendored
@@ -78690,15 +78690,51 @@ const utils_1 = __nccwpck_require__(71314);
|
||||
const QUALITY_INPUT_MINIMAL_MAJOR_TAG = 6;
|
||||
const LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG = 5;
|
||||
class DotnetVersionResolver {
|
||||
quality;
|
||||
dotnetChannel;
|
||||
inputVersion;
|
||||
resolvedArgument;
|
||||
constructor(version) {
|
||||
constructor(version, quality = '', dotnetChannel) {
|
||||
this.quality = quality;
|
||||
this.dotnetChannel = dotnetChannel;
|
||||
this.inputVersion = version.trim();
|
||||
this.resolvedArgument = { type: '', value: '', qualityFlag: false };
|
||||
}
|
||||
isVersionChannel(channel) {
|
||||
// A.B format (e.g., 3.1, 8.0)
|
||||
if (/^\d+\.\d+$/.test(channel))
|
||||
return true;
|
||||
// A.B.Cxx format (e.g., 8.0.1xx) is supported only for .NET 5.0+
|
||||
const latestPatchMatch = channel.match(/^(\d+)\.\d+\.\d{1}xx$/);
|
||||
if (latestPatchMatch) {
|
||||
const major = Number(latestPatchMatch[1]);
|
||||
return (!Number.isNaN(major) && major >= LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async resolveVersionInput() {
|
||||
if (this.inputVersion.toLowerCase() === 'latest') {
|
||||
const channel = this.dotnetChannel || '';
|
||||
if (this.isVersionChannel(channel)) {
|
||||
// A.B or A.B.Cxx channels are passed directly to the install script
|
||||
this.resolvedArgument.value = channel;
|
||||
}
|
||||
else {
|
||||
// LTS, STS, or empty — resolve via releases index API
|
||||
this.resolvedArgument.value = await this.getLatestVersion(channel);
|
||||
}
|
||||
this.resolvedArgument.type = 'channel';
|
||||
const latestChannelMajorTag = Number(this.resolvedArgument.value.split('.')[0]);
|
||||
this.resolvedArgument.qualityFlag =
|
||||
!Number.isNaN(latestChannelMajorTag) &&
|
||||
latestChannelMajorTag >= QUALITY_INPUT_MINIMAL_MAJOR_TAG;
|
||||
return;
|
||||
}
|
||||
if (this.dotnetChannel) {
|
||||
core.warning(`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`);
|
||||
}
|
||||
if (!semver_1.default.validRange(this.inputVersion) && !this.isLatestPatchSyntax()) {
|
||||
throw new Error(`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx`);
|
||||
throw new Error(`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx, latest`);
|
||||
}
|
||||
if (semver_1.default.valid(this.inputVersion)) {
|
||||
this.createVersionArgument();
|
||||
@@ -78756,6 +78792,46 @@ class DotnetVersionResolver {
|
||||
}
|
||||
return this.resolvedArgument;
|
||||
}
|
||||
async getLatestVersion(channelFilter) {
|
||||
const httpClient = new hc.HttpClient('actions/setup-dotnet', [], {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
const response = await httpClient.getJson(DotnetVersionResolver.DotnetCoreIndexUrl);
|
||||
const result = response.result;
|
||||
const rawReleasesInfo = result?.['releases-index'];
|
||||
if (!Array.isArray(rawReleasesInfo)) {
|
||||
throw new Error('Unexpected response format from .NET releases index.');
|
||||
}
|
||||
let releasesInfo = rawReleasesInfo;
|
||||
// Filter out EOL versions
|
||||
releasesInfo = releasesInfo.filter(info => info['support-phase'] !== 'eol');
|
||||
// Filter out preview versions if quality is not 'preview' or 'daily'
|
||||
// If quality is not specified, we assume strict stability (GA only)
|
||||
const normalizedQuality = (this.quality || '').toLowerCase();
|
||||
if (!['preview', 'daily'].includes(normalizedQuality)) {
|
||||
releasesInfo = releasesInfo.filter(info => info['support-phase'] !== 'preview');
|
||||
}
|
||||
// Apply channel filter (LTS/STS)
|
||||
if (channelFilter) {
|
||||
const type = channelFilter.toLowerCase();
|
||||
releasesInfo = releasesInfo.filter(info => info['release-type'] === type);
|
||||
}
|
||||
releasesInfo.sort((a, b) => {
|
||||
const partsA = a['channel-version'].split('.').map(Number);
|
||||
const partsB = b['channel-version'].split('.').map(Number);
|
||||
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
||||
const diff = (partsB[i] || 0) - (partsA[i] || 0);
|
||||
if (diff !== 0)
|
||||
return diff;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
if (releasesInfo.length === 0) {
|
||||
throw new Error(`Could not find any active releases matching channel '${channelFilter || 'any'}'`);
|
||||
}
|
||||
return releasesInfo[0]['channel-version'];
|
||||
}
|
||||
async getLatestByMajorTag(majorTag) {
|
||||
const httpClient = new hc.HttpClient('actions/setup-dotnet', [], {
|
||||
allowRetries: true,
|
||||
@@ -78891,16 +78967,18 @@ class DotnetCoreInstaller {
|
||||
version;
|
||||
quality;
|
||||
architecture;
|
||||
dotnetChannel;
|
||||
static {
|
||||
DotnetInstallDir.setEnvironmentVariable();
|
||||
}
|
||||
constructor(version, quality, architecture) {
|
||||
constructor(version, quality, architecture, dotnetChannel) {
|
||||
this.version = version;
|
||||
this.quality = quality;
|
||||
this.architecture = architecture;
|
||||
this.dotnetChannel = dotnetChannel;
|
||||
}
|
||||
async installDotnet() {
|
||||
const versionResolver = new DotnetVersionResolver(this.version);
|
||||
const versionResolver = new DotnetVersionResolver(this.version, this.quality, this.dotnetChannel);
|
||||
const dotnetVersion = await versionResolver.createDotnetVersion();
|
||||
const architectureArguments = this.architecture &&
|
||||
normalizeArch(this.architecture) !== normalizeArch(os_1.default.arch())
|
||||
@@ -79019,13 +79097,7 @@ const cache_utils_1 = __nccwpck_require__(41678);
|
||||
const cache_restore_1 = __nccwpck_require__(19517);
|
||||
const constants_1 = __nccwpck_require__(69042);
|
||||
const json5_1 = __importDefault(__nccwpck_require__(86904));
|
||||
const qualityOptions = [
|
||||
'daily',
|
||||
'signed',
|
||||
'validated',
|
||||
'preview',
|
||||
'ga'
|
||||
];
|
||||
const qualityOptions = ['daily', 'preview', 'ga'];
|
||||
const supportedArchitectures = [
|
||||
'x64',
|
||||
'x86',
|
||||
@@ -79036,6 +79108,19 @@ const supportedArchitectures = [
|
||||
'ppc64le',
|
||||
'riscv64'
|
||||
];
|
||||
function isValidChannel(channel) {
|
||||
const upper = channel.toUpperCase();
|
||||
if (upper === 'LTS' || upper === 'STS')
|
||||
return true;
|
||||
// A.B format (e.g., 3.1, 8.0)
|
||||
if (/^\d+\.\d+$/.test(channel))
|
||||
return true;
|
||||
// A.B.Cxx format (e.g., 8.0.1xx) - available since 5.0
|
||||
const match = channel.match(/^(?<major>\d+)\.\d+\.\d{1}xx$/);
|
||||
if (match && parseInt(match.groups.major) >= 5)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
async function run() {
|
||||
try {
|
||||
//
|
||||
@@ -79050,6 +79135,21 @@ async function run() {
|
||||
const versions = core.getMultilineInput('dotnet-version');
|
||||
const installedDotnetVersions = [];
|
||||
const architecture = getArchitectureInput();
|
||||
let dotnetChannel = core.getInput('dotnet-channel');
|
||||
const isLatestRequested = versions.some(version => version && version.toLowerCase() === 'latest');
|
||||
if (dotnetChannel && !isValidChannel(dotnetChannel)) {
|
||||
if (isLatestRequested) {
|
||||
throw new Error(`Value '${dotnetChannel}' is not supported for the 'dotnet-channel' option. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`);
|
||||
}
|
||||
else {
|
||||
core.warning(`Value '${dotnetChannel}' is not supported for the 'dotnet-channel' option and will be ignored because 'dotnet-version' is not set to 'latest'. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`);
|
||||
dotnetChannel = '';
|
||||
}
|
||||
}
|
||||
else if (dotnetChannel && !isLatestRequested) {
|
||||
core.warning(`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`);
|
||||
dotnetChannel = '';
|
||||
}
|
||||
const globalJsonFileInput = core.getInput('global-json-file');
|
||||
if (globalJsonFileInput) {
|
||||
const globalJsonPath = path_1.default.resolve(process.cwd(), globalJsonFileInput);
|
||||
@@ -79072,12 +79172,12 @@ async function run() {
|
||||
if (versions.length) {
|
||||
const quality = core.getInput('dotnet-quality');
|
||||
if (quality && !qualityOptions.includes(quality)) {
|
||||
throw new Error(`Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`);
|
||||
throw new Error(`Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, preview, ga.`);
|
||||
}
|
||||
let dotnetInstaller;
|
||||
const uniqueVersions = new Set(versions);
|
||||
const uniqueVersions = new Set(versions.map(v => (v.toLowerCase() === 'latest' ? 'latest' : v)));
|
||||
for (const version of uniqueVersions) {
|
||||
dotnetInstaller = new installer_1.DotnetCoreInstaller(version, quality, architecture);
|
||||
dotnetInstaller = new installer_1.DotnetCoreInstaller(version, quality, architecture, version.toLowerCase() === 'latest' ? dotnetChannel : undefined);
|
||||
const installedVersion = await dotnetInstaller.installDotnet();
|
||||
installedDotnetVersions.push(installedVersion);
|
||||
}
|
||||
|
||||
124
src/installer.ts
124
src/installer.ts
@@ -16,21 +16,74 @@ export interface DotnetVersion {
|
||||
qualityFlag: boolean;
|
||||
}
|
||||
|
||||
interface ReleaseIndexEntry {
|
||||
'channel-version': string;
|
||||
'support-phase': string;
|
||||
'release-type': string;
|
||||
}
|
||||
|
||||
interface ReleaseIndexResponse {
|
||||
'releases-index': ReleaseIndexEntry[];
|
||||
}
|
||||
|
||||
const QUALITY_INPUT_MINIMAL_MAJOR_TAG = 6;
|
||||
const LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG = 5;
|
||||
export class DotnetVersionResolver {
|
||||
private inputVersion: string;
|
||||
private resolvedArgument: DotnetVersion;
|
||||
|
||||
constructor(version: string) {
|
||||
constructor(
|
||||
version: string,
|
||||
private quality: QualityOptions = '',
|
||||
private dotnetChannel?: string
|
||||
) {
|
||||
this.inputVersion = version.trim();
|
||||
this.resolvedArgument = {type: '', value: '', qualityFlag: false};
|
||||
}
|
||||
|
||||
private isVersionChannel(channel: string): boolean {
|
||||
// A.B format (e.g., 3.1, 8.0)
|
||||
if (/^\d+\.\d+$/.test(channel)) return true;
|
||||
// A.B.Cxx format (e.g., 8.0.1xx) is supported only for .NET 5.0+
|
||||
const latestPatchMatch = channel.match(/^(\d+)\.\d+\.\d{1}xx$/);
|
||||
if (latestPatchMatch) {
|
||||
const major = Number(latestPatchMatch[1]);
|
||||
return (
|
||||
!Number.isNaN(major) && major >= LATEST_PATCH_SYNTAX_MINIMAL_MAJOR_TAG
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async resolveVersionInput(): Promise<void> {
|
||||
if (this.inputVersion.toLowerCase() === 'latest') {
|
||||
const channel = this.dotnetChannel || '';
|
||||
if (this.isVersionChannel(channel)) {
|
||||
// A.B or A.B.Cxx channels are passed directly to the install script
|
||||
this.resolvedArgument.value = channel;
|
||||
} else {
|
||||
// LTS, STS, or empty — resolve via releases index API
|
||||
this.resolvedArgument.value = await this.getLatestVersion(channel);
|
||||
}
|
||||
this.resolvedArgument.type = 'channel';
|
||||
const latestChannelMajorTag = Number(
|
||||
this.resolvedArgument.value.split('.')[0]
|
||||
);
|
||||
this.resolvedArgument.qualityFlag =
|
||||
!Number.isNaN(latestChannelMajorTag) &&
|
||||
latestChannelMajorTag >= QUALITY_INPUT_MINIMAL_MAJOR_TAG;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dotnetChannel) {
|
||||
core.warning(
|
||||
`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`
|
||||
);
|
||||
}
|
||||
|
||||
if (!semver.validRange(this.inputVersion) && !this.isLatestPatchSyntax()) {
|
||||
throw new Error(
|
||||
`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx`
|
||||
`The 'dotnet-version' was supplied in invalid format: ${this.inputVersion}! Supported syntax: A.B.C, A.B, A.B.x, A, A.x, A.B.Cxx, latest`
|
||||
);
|
||||
}
|
||||
if (semver.valid(this.inputVersion)) {
|
||||
@@ -96,6 +149,64 @@ export class DotnetVersionResolver {
|
||||
return this.resolvedArgument;
|
||||
}
|
||||
|
||||
private async getLatestVersion(channelFilter: string): Promise<string> {
|
||||
const httpClient = new hc.HttpClient('actions/setup-dotnet', [], {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
|
||||
const response = await httpClient.getJson<ReleaseIndexResponse>(
|
||||
DotnetVersionResolver.DotnetCoreIndexUrl
|
||||
);
|
||||
|
||||
const result = response.result;
|
||||
const rawReleasesInfo = result?.['releases-index'];
|
||||
|
||||
if (!Array.isArray(rawReleasesInfo)) {
|
||||
throw new Error('Unexpected response format from .NET releases index.');
|
||||
}
|
||||
|
||||
let releasesInfo = rawReleasesInfo;
|
||||
|
||||
// Filter out EOL versions
|
||||
releasesInfo = releasesInfo.filter(info => info['support-phase'] !== 'eol');
|
||||
|
||||
// Filter out preview versions if quality is not 'preview' or 'daily'
|
||||
// If quality is not specified, we assume strict stability (GA only)
|
||||
const normalizedQuality = (this.quality || '').toLowerCase();
|
||||
if (!['preview', 'daily'].includes(normalizedQuality)) {
|
||||
releasesInfo = releasesInfo.filter(
|
||||
info => info['support-phase'] !== 'preview'
|
||||
);
|
||||
}
|
||||
|
||||
// Apply channel filter (LTS/STS)
|
||||
if (channelFilter) {
|
||||
const type = channelFilter.toLowerCase();
|
||||
releasesInfo = releasesInfo.filter(info => info['release-type'] === type);
|
||||
}
|
||||
|
||||
releasesInfo.sort((a, b) => {
|
||||
const partsA = a['channel-version'].split('.').map(Number);
|
||||
const partsB = b['channel-version'].split('.').map(Number);
|
||||
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
||||
const diff = (partsB[i] || 0) - (partsA[i] || 0);
|
||||
if (diff !== 0) return diff;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (releasesInfo.length === 0) {
|
||||
throw new Error(
|
||||
`Could not find any active releases matching channel '${
|
||||
channelFilter || 'any'
|
||||
}'`
|
||||
);
|
||||
}
|
||||
|
||||
return releasesInfo[0]['channel-version'];
|
||||
}
|
||||
|
||||
private async getLatestByMajorTag(majorTag: string): Promise<string> {
|
||||
const httpClient = new hc.HttpClient('actions/setup-dotnet', [], {
|
||||
allowRetries: true,
|
||||
@@ -279,11 +390,16 @@ export class DotnetCoreInstaller {
|
||||
constructor(
|
||||
private version: string,
|
||||
private quality: QualityOptions,
|
||||
private architecture?: string
|
||||
private architecture?: string,
|
||||
private dotnetChannel?: string
|
||||
) {}
|
||||
|
||||
public async installDotnet(): Promise<string | null> {
|
||||
const versionResolver = new DotnetVersionResolver(this.version);
|
||||
const versionResolver = new DotnetVersionResolver(
|
||||
this.version,
|
||||
this.quality,
|
||||
this.dotnetChannel
|
||||
);
|
||||
const dotnetVersion = await versionResolver.createDotnetVersion();
|
||||
|
||||
const architectureArguments =
|
||||
|
||||
@@ -15,13 +15,7 @@ import {restoreCache} from './cache-restore';
|
||||
import {Outputs} from './constants';
|
||||
import JSON5 from 'json5';
|
||||
|
||||
const qualityOptions = [
|
||||
'daily',
|
||||
'signed',
|
||||
'validated',
|
||||
'preview',
|
||||
'ga'
|
||||
] as const;
|
||||
const qualityOptions = ['daily', 'preview', 'ga'] as const;
|
||||
const supportedArchitectures = [
|
||||
'x64',
|
||||
'x86',
|
||||
@@ -34,7 +28,18 @@ const supportedArchitectures = [
|
||||
] as const;
|
||||
type SupportedArchitecture = (typeof supportedArchitectures)[number];
|
||||
|
||||
export type QualityOptions = (typeof qualityOptions)[number];
|
||||
export type QualityOptions = (typeof qualityOptions)[number] | '';
|
||||
|
||||
function isValidChannel(channel: string): boolean {
|
||||
const upper = channel.toUpperCase();
|
||||
if (upper === 'LTS' || upper === 'STS') return true;
|
||||
// A.B format (e.g., 3.1, 8.0)
|
||||
if (/^\d+\.\d+$/.test(channel)) return true;
|
||||
// A.B.Cxx format (e.g., 8.0.1xx) - available since 5.0
|
||||
const match = channel.match(/^(?<major>\d+)\.\d+\.\d{1}xx$/);
|
||||
if (match && parseInt(match.groups!.major) >= 5) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function run() {
|
||||
try {
|
||||
@@ -50,6 +55,28 @@ export async function run() {
|
||||
const versions = core.getMultilineInput('dotnet-version');
|
||||
const installedDotnetVersions: (string | null)[] = [];
|
||||
const architecture = getArchitectureInput();
|
||||
let dotnetChannel = core.getInput('dotnet-channel');
|
||||
|
||||
const isLatestRequested = versions.some(
|
||||
version => version && version.toLowerCase() === 'latest'
|
||||
);
|
||||
if (dotnetChannel && !isValidChannel(dotnetChannel)) {
|
||||
if (isLatestRequested) {
|
||||
throw new Error(
|
||||
`Value '${dotnetChannel}' is not supported for the 'dotnet-channel' option. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`
|
||||
);
|
||||
} else {
|
||||
core.warning(
|
||||
`Value '${dotnetChannel}' is not supported for the 'dotnet-channel' option and will be ignored because 'dotnet-version' is not set to 'latest'. Supported values are: LTS, STS, A.B (e.g. 8.0), A.B.Cxx (e.g. 8.0.1xx).`
|
||||
);
|
||||
dotnetChannel = '';
|
||||
}
|
||||
} else if (dotnetChannel && !isLatestRequested) {
|
||||
core.warning(
|
||||
`The 'dotnet-channel' input is only supported when 'dotnet-version' is set to 'latest'.`
|
||||
);
|
||||
dotnetChannel = '';
|
||||
}
|
||||
|
||||
const globalJsonFileInput = core.getInput('global-json-file');
|
||||
if (globalJsonFileInput) {
|
||||
@@ -80,17 +107,20 @@ export async function run() {
|
||||
|
||||
if (quality && !qualityOptions.includes(quality)) {
|
||||
throw new Error(
|
||||
`Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, signed, validated, preview, ga.`
|
||||
`Value '${quality}' is not supported for the 'dotnet-quality' option. Supported values are: daily, preview, ga.`
|
||||
);
|
||||
}
|
||||
|
||||
let dotnetInstaller: DotnetCoreInstaller;
|
||||
const uniqueVersions = new Set<string>(versions);
|
||||
const uniqueVersions = new Set<string>(
|
||||
versions.map(v => (v.toLowerCase() === 'latest' ? 'latest' : v))
|
||||
);
|
||||
for (const version of uniqueVersions) {
|
||||
dotnetInstaller = new DotnetCoreInstaller(
|
||||
version,
|
||||
quality,
|
||||
architecture
|
||||
architecture,
|
||||
version.toLowerCase() === 'latest' ? dotnetChannel : undefined
|
||||
);
|
||||
const installedVersion = await dotnetInstaller.installDotnet();
|
||||
installedDotnetVersions.push(installedVersion);
|
||||
|
||||
Reference in New Issue
Block a user