From 66ae3ded49ab23b67df8ccb37d02a8b8d709b8f3 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Nov 2023 15:15:07 +0100 Subject: [PATCH] Added option that can be used to specify the architecture. --- README.md | 14 +- __tests__/installer.test.ts | 82 ++++++- __tests__/setup-dotnet.test.ts | 27 +-- action.yml | 2 + dist/cache-save/index.js | 426 ++++++++++++++++----------------- dist/setup/index.js | 28 ++- src/installer.ts | 23 +- src/setup-dotnet.ts | 35 ++- 8 files changed, 395 insertions(+), 242 deletions(-) diff --git a/README.md b/README.md index 37f02f22c..dcfe1197a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,17 @@ steps: ``` > **Warning**: Unless a concrete version is specified in the [`global.json`](https://learn.microsoft.com/en-us/dotnet/core/tools/global-json) file, **_the latest .NET version installed on the runner (including preinstalled versions) will be used [by default](https://learn.microsoft.com/en-us/dotnet/core/versions/selection#the-sdk-uses-the-latest-installed-version)_**. Please refer to the [documentation](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software) for the currently preinstalled .NET SDK versions. +**Specific architecture:** +```yml +steps: +- uses: actions/checkout@v3 +- uses: actions/setup-dotnet@v2 + with: + dotnet-version: '8.0.x' + architecture: 'x86' +- run: dotnet build +``` + **Multiple version installation**: ```yml steps: @@ -49,8 +60,7 @@ The `dotnet-version` input supports following syntax: - **A.B.C** (e.g 6.0.400, 7.0.100-preview.7.22377.5) - installs exact version of .NET SDK - **A.B** or **A.B.x** (e.g. 3.1, 3.1.x) - installs the latest patch version of .NET SDK on the channel `3.1`, including prerelease versions (preview, rc) - **A** or **A.x** (e.g. 3, 3.x) - installs the latest minor version of the specified major tag, including prerelease versions (preview, rc) -- **A.B.Cxx** (e.g. 6.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc). - +- **A.B.Cxx** (e.g. 6.0.4xx) - available since `.NET 5.0` release. Installs the latest version of the specific SDK release, including prerelease versions (preview, rc). ## 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**. diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 84aea3200..b922362de 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -8,7 +8,7 @@ import * as io from '@actions/io'; import * as installer from '../src/installer'; import {IS_WINDOWS} from '../src/utils'; -import {QualityOptions} from '../src/setup-dotnet'; +import {QualityOptions, ArchitectureOptions} from '../src/setup-dotnet'; describe('installer tests', () => { const env = process.env; @@ -119,6 +119,86 @@ describe('installer tests', () => { expect(scriptArguments).toContain(expectedArgument); }); + it(`should not supply 'architecture' argument to the installation script when architecture is an empty string`, async () => { + const inputVersion = '6.0.300'; + const inputQuality = '' as QualityOptions; + const inputArchitecture = '' as ArchitectureOptions; + 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(); + + /** + * First time script would be called to + * install runtime, here we checking only the + * second one that installs actual SDK. i.e. 1 + */ + const callIndex = 1; + + const scriptArguments = ( + getExecOutputSpy.mock.calls[callIndex][1] as string[] + ).join(' '); + const unexpectedArgument = IS_WINDOWS + ? `-Architecture` + : `--architecture`; + + expect(scriptArguments).not.toContain(unexpectedArgument); + }); + + it(`should supply 'architecture' argument to the installation script when arrchitecture is supplied`, async () => { + const inputVersion = '6.0.300'; + const inputQuality = '' as QualityOptions; + const inputArchitecture = 'x86'; + 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(); + + /** + * First time script would be called to + * install runtime, here we checking only the + * second one that installs actual SDK. i.e. 1 + */ + 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 warn if the 'quality' input is set and the supplied version is in A.B.C syntax`, async () => { const inputVersion = '6.0.300'; const inputQuality = 'ga' as QualityOptions; diff --git a/__tests__/setup-dotnet.test.ts b/__tests__/setup-dotnet.test.ts index 8e60cd1ab..04bcba623 100644 --- a/__tests__/setup-dotnet.test.ts +++ b/__tests__/setup-dotnet.test.ts @@ -9,7 +9,7 @@ import * as cacheUtils from '../src/cache-utils'; import * as cacheRestore from '../src/cache-restore'; describe('setup-dotnet tests', () => { - const inputs = {} as any; + let inputs = {} as any; const getInputSpy = jest.spyOn(core, 'getInput'); const getMultilineInputSpy = jest.spyOn(core, 'getMultilineInput'); @@ -49,6 +49,7 @@ describe('setup-dotnet tests', () => { DotnetInstallDir.addToPath = addToPathOriginal; jest.clearAllMocks(); jest.resetAllMocks(); + inputs = {}; }); it('should fail the action if global-json-file input is present, but the file does not exist in the file system', async () => { @@ -62,7 +63,6 @@ describe('setup-dotnet tests', () => { }); test(`if 'dotnet-version' and 'global-json-file' inputs aren't present, should log into debug output, try to find global.json in the repo root, fail and log message into info output`, async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = []; maxSatisfyingSpy.mockImplementation(() => null); @@ -80,7 +80,6 @@ describe('setup-dotnet tests', () => { }); it('should fail the action if quality is supplied but its value is not supported', async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = ['6.0']; inputs['dotnet-quality'] = 'fictitiousQuality'; @@ -90,10 +89,19 @@ describe('setup-dotnet tests', () => { expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage); }); + it('should fail the action if architecture is supplied but its value is not supported', async () => { + console.log(inputs); + inputs['dotnet-version'] = ['6.0']; + inputs['dotnet-architecture'] = 'fictitiousArchitecture'; + + const expectedErrorMessage = `Value '${inputs['dotnet-architecture']}' is not supported for the 'dotnet-architecture' option. Supported values are: amd64, x64, x86, arm64, arm, s390x, ppc64le, loongarch64.`; + + await setup.run(); + expect(setFailedSpy).toHaveBeenCalledWith(expectedErrorMessage); + }); + it('should call installDotnet() multiple times if dotnet-version multiline input is provided', async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = ['6.0', '7.0']; - inputs['dotnet-quality'] = ''; installDotnetSpy.mockImplementation(() => Promise.resolve('')); @@ -102,9 +110,7 @@ describe('setup-dotnet tests', () => { }); it('should call addToPath() after installation complete', async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = ['6.0', '7.0']; - inputs['dotnet-quality'] = ''; installDotnetSpy.mockImplementation(() => Promise.resolve('')); @@ -113,9 +119,7 @@ describe('setup-dotnet tests', () => { }); it('should call auth.configAuthentication() if source-url input is provided', async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = []; - inputs['dotnet-quality'] = ''; inputs['source-url'] = 'fictitious.source.url'; configAuthenticationSpy.mockImplementation(() => {}); @@ -128,9 +132,7 @@ describe('setup-dotnet tests', () => { }); it('should call auth.configAuthentication() with proper parameters if source-url and config-file inputs are provided', async () => { - inputs['global-json-file'] = ''; inputs['dotnet-version'] = []; - inputs['dotnet-quality'] = ''; inputs['source-url'] = 'fictitious.source.url'; inputs['config-file'] = 'fictitious.path'; @@ -178,7 +180,6 @@ describe('setup-dotnet tests', () => { it(`should get 'cache-dependency-path' and call restoreCache() if input cache is set to true and cache feature is available`, async () => { inputs['dotnet-version'] = ['6.0.300']; - inputs['dotnet-quality'] = ''; inputs['cache'] = true; inputs['cache-dependency-path'] = 'fictitious.package.lock.json'; @@ -196,7 +197,6 @@ describe('setup-dotnet tests', () => { it(`shouldn't call restoreCache() if input cache isn't set to true`, async () => { inputs['dotnet-version'] = ['6.0.300']; - inputs['dotnet-quality'] = ''; inputs['cache'] = false; installDotnetSpy.mockImplementation(() => Promise.resolve('')); @@ -210,7 +210,6 @@ describe('setup-dotnet tests', () => { it(`shouldn't call restoreCache() if cache feature isn't available`, async () => { inputs['dotnet-version'] = ['6.0.300']; - inputs['dotnet-quality'] = ''; inputs['cache'] = true; installDotnetSpy.mockImplementation(() => Promise.resolve('')); diff --git a/action.yml b/action.yml index bf24aca99..3c8a4743b 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,8 @@ inputs: 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' dotnet-quality: description: 'Optional quality of the build. The possible values are: daily, signed, validated, preview, ga.' + dotnet-architecture: + description: 'Optional architecture of the .NET binaries to install. Possible values: amd64, x64, x86, arm64, arm, s390x, ppc64le and loongarch64. If not provided, defaults to the OS architecture.' global-json-file: description: 'Optional global.json location, if your global.json isn''t located in the root of the repo.' source-url: diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index e105a4b39..60557b041 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -58523,91 +58523,91 @@ exports["default"] = _default; /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.run = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const cache = __importStar(__nccwpck_require__(7799)); -const node_fs_1 = __importDefault(__nccwpck_require__(7561)); -const cache_utils_1 = __nccwpck_require__(1678); -const constants_1 = __nccwpck_require__(9042); -// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in -// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to -// throw an uncaught exception. Instead of failing this action, just warn. -process.on('uncaughtException', e => { - const warningPrefix = '[warning]'; - core.info(`${warningPrefix}${e.message}`); -}); -function run() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (core.getBooleanInput('cache')) { - yield cachePackages(); - } - } - catch (error) { - core.setFailed(error.message); - } - }); -} -exports.run = run; -const cachePackages = () => __awaiter(void 0, void 0, void 0, function* () { - const state = core.getState(constants_1.State.CacheMatchedKey); - const primaryKey = core.getState(constants_1.State.CachePrimaryKey); - if (!primaryKey) { - core.info('Primary key was not generated, not saving cache.'); - return; - } - const { 'global-packages': cachePath } = yield (0, cache_utils_1.getNuGetFolderPath)(); - if (!node_fs_1.default.existsSync(cachePath)) { - throw new Error(`Cache folder path is retrieved for .NET CLI but doesn't exist on disk: ${cachePath}`); - } - if (primaryKey === state) { - core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`); - return; - } - const cacheId = yield cache.saveCache([cachePath], primaryKey); - if (cacheId == -1) { - return; - } - core.info(`Cache saved with the key: ${primaryKey}`); -}); -run(); + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.run = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const cache = __importStar(__nccwpck_require__(7799)); +const node_fs_1 = __importDefault(__nccwpck_require__(7561)); +const cache_utils_1 = __nccwpck_require__(1678); +const constants_1 = __nccwpck_require__(9042); +// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in +// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to +// throw an uncaught exception. Instead of failing this action, just warn. +process.on('uncaughtException', e => { + const warningPrefix = '[warning]'; + core.info(`${warningPrefix}${e.message}`); +}); +function run() { + return __awaiter(this, void 0, void 0, function* () { + try { + if (core.getBooleanInput('cache')) { + yield cachePackages(); + } + } + catch (error) { + core.setFailed(error.message); + } + }); +} +exports.run = run; +const cachePackages = () => __awaiter(void 0, void 0, void 0, function* () { + const state = core.getState(constants_1.State.CacheMatchedKey); + const primaryKey = core.getState(constants_1.State.CachePrimaryKey); + if (!primaryKey) { + core.info('Primary key was not generated, not saving cache.'); + return; + } + const { 'global-packages': cachePath } = yield (0, cache_utils_1.getNuGetFolderPath)(); + if (!node_fs_1.default.existsSync(cachePath)) { + throw new Error(`Cache folder path is retrieved for .NET CLI but doesn't exist on disk: ${cachePath}`); + } + if (primaryKey === state) { + core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`); + return; + } + const cacheId = yield cache.saveCache([cachePath], primaryKey); + if (cacheId == -1) { + return; + } + core.info(`Cache saved with the key: ${primaryKey}`); +}); +run(); /***/ }), @@ -58616,114 +58616,114 @@ run(); /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isCacheFeatureAvailable = exports.getNuGetFolderPath = void 0; -const cache = __importStar(__nccwpck_require__(7799)); -const core = __importStar(__nccwpck_require__(2186)); -const exec = __importStar(__nccwpck_require__(1514)); -const constants_1 = __nccwpck_require__(9042); -/** - * Get NuGet global packages, cache, and temp folders from .NET CLI. - * @returns (Folder Name)-(Path) mappings - * @see https://docs.microsoft.com/nuget/consume-packages/managing-the-global-packages-and-cache-folders - * @example - * Windows - * ```json - * { - * "http-cache": "C:\\Users\\user1\\AppData\\Local\\NuGet\\v3-cache", - * "global-packages": "C:\\Users\\user1\\.nuget\\packages\\", - * "temp": "C:\\Users\\user1\\AppData\\Local\\Temp\\NuGetScratch", - * "plugins-cache": "C:\\Users\\user1\\AppData\\Local\\NuGet\\plugins-cache" - * } - * ``` - * - * Mac/Linux - * ```json - * { - * "http-cache": "/home/user1/.local/share/NuGet/v3-cache", - * "global-packages": "/home/user1/.nuget/packages/", - * "temp": "/tmp/NuGetScratch", - * "plugins-cache": "/home/user1/.local/share/NuGet/plugins-cache" - * } - * ``` - */ -const getNuGetFolderPath = () => __awaiter(void 0, void 0, void 0, function* () { - const { stdout, stderr, exitCode } = yield exec.getExecOutput(constants_1.cliCommand, undefined, { ignoreReturnCode: true, silent: true }); - if (exitCode) { - throw new Error(!stderr.trim() - ? `The '${constants_1.cliCommand}' command failed with exit code: ${exitCode}` - : stderr); - } - const result = { - 'http-cache': '', - 'global-packages': '', - temp: '', - 'plugins-cache': '' - }; - const regex = /(?:^|\s)(?[a-z-]+): (?.+[/\\].+)$/gm; - let match; - while ((match = regex.exec(stdout)) !== null) { - const key = match.groups.key; - if (key in result) { - result[key] = match.groups.path; - } - } - return result; -}); -exports.getNuGetFolderPath = getNuGetFolderPath; -function isCacheFeatureAvailable() { - if (cache.isFeatureAvailable()) { - return true; - } - if (isGhes()) { - core.warning('Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'); - return false; - } - core.warning('The runner was not able to contact the cache service. Caching will be skipped'); - return false; -} -exports.isCacheFeatureAvailable = isCacheFeatureAvailable; -/** - * Returns this action runs on GitHub Enterprise Server or not. - * (port from https://github.com/actions/toolkit/blob/457303960f03375db6f033e214b9f90d79c3fe5c/packages/cache/src/internal/cacheUtils.ts#L134) - */ -function isGhes() { - const url = process.env['GITHUB_SERVER_URL'] || 'https://github.com'; - return new URL(url).hostname.toUpperCase() !== 'GITHUB.COM'; -} + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isCacheFeatureAvailable = exports.getNuGetFolderPath = void 0; +const cache = __importStar(__nccwpck_require__(7799)); +const core = __importStar(__nccwpck_require__(2186)); +const exec = __importStar(__nccwpck_require__(1514)); +const constants_1 = __nccwpck_require__(9042); +/** + * Get NuGet global packages, cache, and temp folders from .NET CLI. + * @returns (Folder Name)-(Path) mappings + * @see https://docs.microsoft.com/nuget/consume-packages/managing-the-global-packages-and-cache-folders + * @example + * Windows + * ```json + * { + * "http-cache": "C:\\Users\\user1\\AppData\\Local\\NuGet\\v3-cache", + * "global-packages": "C:\\Users\\user1\\.nuget\\packages\\", + * "temp": "C:\\Users\\user1\\AppData\\Local\\Temp\\NuGetScratch", + * "plugins-cache": "C:\\Users\\user1\\AppData\\Local\\NuGet\\plugins-cache" + * } + * ``` + * + * Mac/Linux + * ```json + * { + * "http-cache": "/home/user1/.local/share/NuGet/v3-cache", + * "global-packages": "/home/user1/.nuget/packages/", + * "temp": "/tmp/NuGetScratch", + * "plugins-cache": "/home/user1/.local/share/NuGet/plugins-cache" + * } + * ``` + */ +const getNuGetFolderPath = () => __awaiter(void 0, void 0, void 0, function* () { + const { stdout, stderr, exitCode } = yield exec.getExecOutput(constants_1.cliCommand, undefined, { ignoreReturnCode: true, silent: true }); + if (exitCode) { + throw new Error(!stderr.trim() + ? `The '${constants_1.cliCommand}' command failed with exit code: ${exitCode}` + : stderr); + } + const result = { + 'http-cache': '', + 'global-packages': '', + temp: '', + 'plugins-cache': '' + }; + const regex = /(?:^|\s)(?[a-z-]+): (?.+[/\\].+)$/gm; + let match; + while ((match = regex.exec(stdout)) !== null) { + const key = match.groups.key; + if (key in result) { + result[key] = match.groups.path; + } + } + return result; +}); +exports.getNuGetFolderPath = getNuGetFolderPath; +function isCacheFeatureAvailable() { + if (cache.isFeatureAvailable()) { + return true; + } + if (isGhes()) { + core.warning('Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'); + return false; + } + core.warning('The runner was not able to contact the cache service. Caching will be skipped'); + return false; +} +exports.isCacheFeatureAvailable = isCacheFeatureAvailable; +/** + * Returns this action runs on GitHub Enterprise Server or not. + * (port from https://github.com/actions/toolkit/blob/457303960f03375db6f033e214b9f90d79c3fe5c/packages/cache/src/internal/cacheUtils.ts#L134) + */ +function isGhes() { + const url = process.env['GITHUB_SERVER_URL'] || 'https://github.com'; + return new URL(url).hostname.toUpperCase() !== 'GITHUB.COM'; +} /***/ }), @@ -58732,26 +58732,26 @@ function isGhes() { /***/ ((__unused_webpack_module, exports) => { "use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.Outputs = exports.State = exports.cliCommand = exports.lockFilePatterns = void 0; -/** NuGet lock file patterns */ -exports.lockFilePatterns = ['packages.lock.json']; -/** - * .NET CLI command to list local NuGet resources. - * @see https://docs.microsoft.com/dotnet/core/tools/dotnet-nuget-locals - */ -exports.cliCommand = 'dotnet nuget locals all --list --force-english-output'; -var State; -(function (State) { - State["CachePrimaryKey"] = "CACHE_KEY"; - State["CacheMatchedKey"] = "CACHE_RESULT"; -})(State = exports.State || (exports.State = {})); -var Outputs; -(function (Outputs) { - Outputs["CacheHit"] = "cache-hit"; - Outputs["DotnetVersion"] = "dotnet-version"; -})(Outputs = exports.Outputs || (exports.Outputs = {})); + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Outputs = exports.State = exports.cliCommand = exports.lockFilePatterns = void 0; +/** NuGet lock file patterns */ +exports.lockFilePatterns = ['packages.lock.json']; +/** + * .NET CLI command to list local NuGet resources. + * @see https://docs.microsoft.com/dotnet/core/tools/dotnet-nuget-locals + */ +exports.cliCommand = 'dotnet nuget locals all --list --force-english-output'; +var State; +(function (State) { + State["CachePrimaryKey"] = "CACHE_KEY"; + State["CacheMatchedKey"] = "CACHE_RESULT"; +})(State = exports.State || (exports.State = {})); +var Outputs; +(function (Outputs) { + Outputs["CacheHit"] = "cache-hit"; + Outputs["DotnetVersion"] = "dotnet-version"; +})(Outputs = exports.Outputs || (exports.Outputs = {})); /***/ }), diff --git a/dist/setup/index.js b/dist/setup/index.js index 7567a1bdf..09872a995 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -72984,7 +72984,7 @@ class DotnetInstallScript { this.scriptArguments.push(...args); return this; } - useVersion(dotnetVersion, quality) { + useVersion(dotnetVersion, quality, architecture) { if (dotnetVersion.type) { this.useArguments(dotnetVersion.type, dotnetVersion.value); } @@ -72995,6 +72995,9 @@ class DotnetInstallScript { if (quality) { this.useArguments(utils_1.IS_WINDOWS ? '-Quality' : '--quality', quality); } + if (architecture) { + this.useArguments(utils_1.IS_WINDOWS ? '-Architecture' : '--architecture', architecture); + } return this; } execute() { @@ -73035,9 +73038,10 @@ DotnetInstallDir.dirPath = process.env['DOTNET_INSTALL_DIR'] ? DotnetInstallDir.convertInstallPathToAbsolute(process.env['DOTNET_INSTALL_DIR']) : DotnetInstallDir.default[utils_1.PLATFORM]; class DotnetCoreInstaller { - constructor(version, quality) { + constructor(version, quality, architecture) { this.version = version; this.quality = quality; + this.architecture = architecture; } installDotnet() { return __awaiter(this, void 0, void 0, function* () { @@ -73070,7 +73074,7 @@ class DotnetCoreInstaller { // Don't overwrite CLI because it should be already installed .useArguments(utils_1.IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files') // Use version provided by user - .useVersion(dotnetVersion, this.quality) + .useVersion(dotnetVersion, this.quality, this.architecture) .execute(); if (dotnetInstallOutput.exitCode) { throw new Error(`Failed to install dotnet, exit code: ${dotnetInstallOutput.exitCode}. ${dotnetInstallOutput.stderr}`); @@ -73155,6 +73159,16 @@ const qualityOptions = [ 'preview', 'ga' ]; +const architectureOptions = [ + 'amd64', + 'x64', + 'x86', + 'arm64', + 'arm', + 's390x', + 'ppc64le', + 'loongarch64' +]; function run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -73191,12 +73205,16 @@ 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: ${qualityOptions.join(', ')}.`); + } + const architecture = core.getInput('dotnet-architecture'); + if (architecture && !architectureOptions.includes(architecture)) { + throw new Error(`Value '${architecture}' is not supported for the 'dotnet-architecture' option. Supported values are: ${architectureOptions.join(', ')}.`); } let dotnetInstaller; const uniqueVersions = new Set(versions); for (const version of uniqueVersions) { - dotnetInstaller = new installer_1.DotnetCoreInstaller(version, quality); + dotnetInstaller = new installer_1.DotnetCoreInstaller(version, quality, architecture); const installedVersion = yield dotnetInstaller.installDotnet(); installedDotnetVersions.push(installedVersion); } diff --git a/src/installer.ts b/src/installer.ts index 4900afa3a..7506d36e3 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -8,7 +8,7 @@ import path from 'path'; import os from 'os'; import semver from 'semver'; import {IS_WINDOWS, PLATFORM} from './utils'; -import {QualityOptions} from './setup-dotnet'; +import {QualityOptions, ArchitectureOptions} from './setup-dotnet'; export interface DotnetVersion { type: string; @@ -182,7 +182,11 @@ export class DotnetInstallScript { return this; } - public useVersion(dotnetVersion: DotnetVersion, quality?: QualityOptions) { + public useVersion( + dotnetVersion: DotnetVersion, + quality?: QualityOptions, + architecture?: ArchitectureOptions + ) { if (dotnetVersion.type) { this.useArguments(dotnetVersion.type, dotnetVersion.value); } @@ -198,6 +202,13 @@ export class DotnetInstallScript { this.useArguments(IS_WINDOWS ? '-Quality' : '--quality', quality); } + if (architecture) { + this.useArguments( + IS_WINDOWS ? '-Architecture' : '--architecture', + architecture + ); + } + return this; } @@ -253,7 +264,11 @@ export class DotnetCoreInstaller { DotnetInstallDir.setEnvironmentVariable(); } - constructor(private version: string, private quality: QualityOptions) {} + constructor( + private version: string, + private quality: QualityOptions, + private architecture?: ArchitectureOptions + ) {} public async installDotnet(): Promise { const versionResolver = new DotnetVersionResolver(this.version); @@ -294,7 +309,7 @@ export class DotnetCoreInstaller { IS_WINDOWS ? '-SkipNonVersionedFiles' : '--skip-non-versioned-files' ) // Use version provided by user - .useVersion(dotnetVersion, this.quality) + .useVersion(dotnetVersion, this.quality, this.architecture) .execute(); if (dotnetInstallOutput.exitCode) { diff --git a/src/setup-dotnet.ts b/src/setup-dotnet.ts index 2a628a5ab..ec375d919 100644 --- a/src/setup-dotnet.ts +++ b/src/setup-dotnet.ts @@ -19,6 +19,19 @@ const qualityOptions = [ export type QualityOptions = (typeof qualityOptions)[number]; +const architectureOptions = [ + 'amd64', + 'x64', + 'x86', + 'arm64', + 'arm', + 's390x', + 'ppc64le', + 'loongarch64' +] as const; + +export type ArchitectureOptions = (typeof architectureOptions)[number]; + export async function run() { try { // @@ -59,17 +72,33 @@ export async function run() { if (versions.length) { const quality = core.getInput('dotnet-quality') as QualityOptions; - 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: ${qualityOptions.join( + ', ' + )}.` + ); + } + + const architecture = core.getInput( + 'dotnet-architecture' + ) as ArchitectureOptions; + if (architecture && !architectureOptions.includes(architecture)) { + throw new Error( + `Value '${architecture}' is not supported for the 'dotnet-architecture' option. Supported values are: ${architectureOptions.join( + ', ' + )}.` ); } let dotnetInstaller: DotnetCoreInstaller; const uniqueVersions = new Set(versions); for (const version of uniqueVersions) { - dotnetInstaller = new DotnetCoreInstaller(version, quality); + dotnetInstaller = new DotnetCoreInstaller( + version, + quality, + architecture + ); const installedVersion = await dotnetInstaller.installDotnet(); installedDotnetVersions.push(installedVersion); }