From 38dbe75f81e1028c12d730d385e27f368485d7b1 Mon Sep 17 00:00:00 2001 From: Milos Pantic <101411245+panticmilos@users.noreply.github.com> Date: Mon, 12 Dec 2022 10:58:49 +0100 Subject: [PATCH] Add stable and oldstable aliases (#300) --- .github/workflows/versions.yml | 51 +++++++++++++ README.md | 28 +++++++ __tests__/setup-go.test.ts | 32 +++++++- dist/cache-save/index.js | 13 ++-- dist/setup/index.js | 119 +++++++++++++++++++++++++----- package-lock.json | 14 ++-- package.json | 2 +- src/installer.ts | 129 ++++++++++++++++++++++++++++++--- src/main.ts | 7 +- src/utils.ts | 4 + 10 files changed, 354 insertions(+), 45 deletions(-) create mode 100644 src/utils.ts diff --git a/.github/workflows/versions.yml b/.github/workflows/versions.yml index faaa439ea..95d1b6aa9 100644 --- a/.github/workflows/versions.yml +++ b/.github/workflows/versions.yml @@ -12,6 +12,57 @@ on: - cron: 0 0 * * * jobs: + stable: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - name: Setup Go Stable + uses: ./ + with: + go-version: stable + - name: Verify Go + run: go version + + oldstable: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - name: Setup Go oldStable + uses: ./ + with: + go-version: oldstable + - name: Verify Go + run: go version + + aliases-arch: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + version: [stable, oldstable] + architecture: [x64, x32] + exclude: + - os: macos-latest + architecture: x32 + steps: + - uses: actions/checkout@v3 + - name: Setup Go ${{ matrix.version }} ${{ matrix.architecture }} + uses: ./ + with: + go-version: ${{ matrix.version }} + architecture: ${{ matrix.architecture }} + - name: Verify Go + run: go version + local-cache: name: Setup local-cache version runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index 8ef09d476..ee24ecac2 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The V3 edition of the action offers: - Proxy support - Check latest version - Caching packages dependencies +- stable and oldstable aliases - Bug Fixes (including issues around version matching and semver) The action will first check the local cache for a version match. If a version is not found locally, it will pull it from the `main` branch of the [go-versions](https://github.com/actions/go-versions/blob/main/versions-manifest.json) repository. On miss or failure, it will fall back to downloading directly from [go dist](https://storage.googleapis.com/golang). To change the default behavior, please use the [check-latest input](#check-latest-version). @@ -95,6 +96,33 @@ steps: check-latest: true - run: go run hello.go ``` + +## Using stable/oldstable aliases + +If `stable` is provided, action will get the latest stable version from the [`go-versions`](https://github.com/actions/go-versions/blob/main/versions-manifest.json) repository manifest. + +If `oldstable` is provided, when current release is 1.19.x, action will resolve version as 1.18.x, where x is the latest patch release. + +**Note:** using these aliases will result in same version as using corresponding minor release with `check-latest` input set to `true` + +```yaml +steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 'stable' + - run: go run hello.go +``` + +```yaml +steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 'oldstable' + - run: go run hello.go +``` + ## Caching dependency files and build outputs: The action has a built-in functionality for caching and restoring go modules and build outputs. It uses [actions/cache](https://github.com/actions/cache) under the hood but requires less configuration settings. The `cache` input is optional, and caching is turned off by default. diff --git a/__tests__/setup-go.test.ts b/__tests__/setup-go.test.ts index c7b3a2259..2188fc9b2 100644 --- a/__tests__/setup-go.test.ts +++ b/__tests__/setup-go.test.ts @@ -41,6 +41,7 @@ describe('setup-go', () => { let mkdirpSpy: jest.SpyInstance; let execSpy: jest.SpyInstance; let getManifestSpy: jest.SpyInstance; + let getAllVersionsSpy: jest.SpyInstance; beforeAll(async () => { process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible) @@ -83,6 +84,7 @@ describe('setup-go', () => { cacheSpy = jest.spyOn(tc, 'cacheDir'); getSpy = jest.spyOn(im, 'getVersionsDist'); getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo'); + getAllVersionsSpy = jest.spyOn(im, 'getManifest'); // io whichSpy = jest.spyOn(io, 'which'); @@ -700,7 +702,7 @@ describe('setup-go', () => { findSpy.mockImplementation(() => ''); dlSpy.mockImplementation(async () => '/some/temp/path'); - const toolPath = path.normalize('/cache/go/1.17.5/x64'); + const toolPath = path.normalize('/cache/go/1.17.6/x64'); extractTarSpy.mockImplementation(async () => '/some/other/temp/path'); cacheSpy.mockImplementation(async () => toolPath); @@ -779,6 +781,7 @@ describe('setup-go', () => { getManifestSpy.mockImplementation(() => { throw new Error('Unable to download manifest'); }); + getAllVersionsSpy.mockImplementationOnce(() => undefined); dlSpy.mockImplementation(async () => '/some/temp/path'); let toolPath = path.normalize('/cache/go/1.13.7/x64'); @@ -926,5 +929,32 @@ use . ); } }, 100000); + + it.each(['stable', 'oldstable'])( + 'acquires latest go version with %s go-version input', + async (alias: string) => { + const arch = 'x64'; + os.platform = 'darwin'; + os.arch = arch; + + inputs['go-version'] = alias; + inputs['architecture'] = os.arch; + + // ... but not in the local cache + findSpy.mockImplementation(() => ''); + + dlSpy.mockImplementation(async () => '/some/temp/path'); + let toolPath = path.normalize(`/cache/go/${alias}/${arch}`); + cacheSpy.mockImplementation(async () => toolPath); + + await main.run(); + + const releaseIndex = alias === 'stable' ? 0 : 1; + + expect(logSpy).toHaveBeenCalledWith( + `${alias} version resolved as ${goTestManifest[releaseIndex].version}` + ); + } + ); }); }); diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 10c4ddceb..e6e029d98 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -60313,7 +60313,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -60418,7 +60418,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -60437,7 +60437,7 @@ const cache = __importStar(__nccwpck_require__(7799)); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const package_managers_1 = __nccwpck_require__(6663); -exports.getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, function* () { +const getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, function* () { let { stdout, stderr, exitCode } = yield exec.getExecOutput(toolCommand, undefined, { ignoreReturnCode: true }); if (exitCode) { stderr = !stderr.trim() @@ -60447,14 +60447,16 @@ exports.getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, fu } return stdout.trim(); }); -exports.getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { +exports.getCommandOutput = getCommandOutput; +const getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { if (!package_managers_1.supportedPackageManagers[packageManager]) { throw new Error(`It's not possible to use ${packageManager}, please, check correctness of the package manager name spelling.`); } const obtainedPackageManager = package_managers_1.supportedPackageManagers[packageManager]; return obtainedPackageManager; }); -exports.getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { +exports.getPackageManagerInfo = getPackageManagerInfo; +const getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { let pathList = yield Promise.all(packageManagerInfo.cacheFolderCommandList.map(command => exports.getCommandOutput(command))); const emptyPaths = pathList.filter(item => !item); if (emptyPaths.length) { @@ -60462,6 +60464,7 @@ exports.getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0 } return pathList; }); +exports.getCacheDirectoryPath = getCacheDirectoryPath; function isGhes() { const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; diff --git a/dist/setup/index.js b/dist/setup/index.js index 217a6ff1e..7b0f1c428 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -63007,7 +63007,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -63032,7 +63032,7 @@ const path_1 = __importDefault(__nccwpck_require__(1017)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const constants_1 = __nccwpck_require__(9042); const cache_utils_1 = __nccwpck_require__(1678); -exports.restoreCache = (versionSpec, packageManager, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { +const restoreCache = (versionSpec, packageManager, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { const packageManagerInfo = yield cache_utils_1.getPackageManagerInfo(packageManager); const platform = process.env.RUNNER_OS; const cachePaths = yield cache_utils_1.getCacheDirectoryPath(packageManagerInfo); @@ -63056,6 +63056,7 @@ exports.restoreCache = (versionSpec, packageManager, cacheDependencyPath) => __a core.saveState(constants_1.State.CacheMatchedKey, cacheKey); core.info(`Cache restored from key: ${cacheKey}`); }); +exports.restoreCache = restoreCache; const findDependencyFile = (packageManager) => { let dependencyFile = packageManager.dependencyFilePattern; const workspace = process.env.GITHUB_WORKSPACE; @@ -63090,7 +63091,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -63109,7 +63110,7 @@ const cache = __importStar(__nccwpck_require__(7799)); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const package_managers_1 = __nccwpck_require__(6663); -exports.getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, function* () { +const getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, function* () { let { stdout, stderr, exitCode } = yield exec.getExecOutput(toolCommand, undefined, { ignoreReturnCode: true }); if (exitCode) { stderr = !stderr.trim() @@ -63119,14 +63120,16 @@ exports.getCommandOutput = (toolCommand) => __awaiter(void 0, void 0, void 0, fu } return stdout.trim(); }); -exports.getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { +exports.getCommandOutput = getCommandOutput; +const getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { if (!package_managers_1.supportedPackageManagers[packageManager]) { throw new Error(`It's not possible to use ${packageManager}, please, check correctness of the package manager name spelling.`); } const obtainedPackageManager = package_managers_1.supportedPackageManagers[packageManager]; return obtainedPackageManager; }); -exports.getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { +exports.getPackageManagerInfo = getPackageManagerInfo; +const getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { let pathList = yield Promise.all(packageManagerInfo.cacheFolderCommandList.map(command => exports.getCommandOutput(command))); const emptyPaths = pathList.filter(item => !item); if (emptyPaths.length) { @@ -63134,6 +63137,7 @@ exports.getCacheDirectoryPath = (packageManagerInfo) => __awaiter(void 0, void 0 } return pathList; }); +exports.getCacheDirectoryPath = getCacheDirectoryPath; function isGhes() { const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; @@ -63196,7 +63200,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -63213,7 +63217,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.parseGoVersionFile = exports.makeSemver = exports.getVersionsDist = exports.findMatch = exports.getInfoFromManifest = exports.extractGoArchive = exports.getGo = void 0; +exports.resolveStableVersionInput = exports.parseGoVersionFile = exports.makeSemver = exports.getVersionsDist = exports.findMatch = exports.getInfoFromManifest = exports.getManifest = exports.extractGoArchive = exports.getGo = void 0; const tc = __importStar(__nccwpck_require__(7784)); const core = __importStar(__nccwpck_require__(2186)); const path = __importStar(__nccwpck_require__(1017)); @@ -63222,12 +63226,26 @@ const httpm = __importStar(__nccwpck_require__(6255)); const sys = __importStar(__nccwpck_require__(4300)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const os_1 = __importDefault(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(1314); function getGo(versionSpec, checkLatest, auth, arch = os_1.default.arch()) { return __awaiter(this, void 0, void 0, function* () { + let manifest; let osPlat = os_1.default.platform(); + if (versionSpec === utils_1.StableReleaseAlias.Stable || + versionSpec === utils_1.StableReleaseAlias.OldStable) { + manifest = yield getManifest(auth); + let stableVersion = yield resolveStableVersionInput(versionSpec, arch, osPlat, manifest); + if (!stableVersion) { + stableVersion = yield resolveStableVersionDist(versionSpec, arch); + if (!stableVersion) { + throw new Error(`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.`); + } + } + versionSpec = stableVersion; + } if (checkLatest) { core.info('Attempting to resolve the latest version from the manifest...'); - const resolvedVersion = yield resolveVersionFromManifest(versionSpec, true, auth, arch); + const resolvedVersion = yield resolveVersionFromManifest(versionSpec, true, auth, arch, manifest); if (resolvedVersion) { versionSpec = resolvedVersion; core.info(`Resolved as '${versionSpec}'`); @@ -63251,7 +63269,7 @@ function getGo(versionSpec, checkLatest, auth, arch = os_1.default.arch()) { // Try download from internal distribution (popular versions only) // try { - info = yield getInfoFromManifest(versionSpec, true, auth, arch); + info = yield getInfoFromManifest(versionSpec, true, auth, arch, manifest); if (info) { downloadPath = yield installGoVersion(info, auth, arch); } @@ -63290,10 +63308,10 @@ function getGo(versionSpec, checkLatest, auth, arch = os_1.default.arch()) { }); } exports.getGo = getGo; -function resolveVersionFromManifest(versionSpec, stable, auth, arch) { +function resolveVersionFromManifest(versionSpec, stable, auth, arch, manifest) { return __awaiter(this, void 0, void 0, function* () { try { - const info = yield getInfoFromManifest(versionSpec, stable, auth, arch); + const info = yield getInfoFromManifest(versionSpec, stable, auth, arch, manifest); return info === null || info === void 0 ? void 0 : info.resolvedVersion; } catch (err) { @@ -63336,12 +63354,21 @@ function extractGoArchive(archivePath) { }); } exports.extractGoArchive = extractGoArchive; -function getInfoFromManifest(versionSpec, stable, auth, arch = os_1.default.arch()) { +function getManifest(auth) { + return __awaiter(this, void 0, void 0, function* () { + return tc.getManifestFromRepo('actions', 'go-versions', auth, 'main'); + }); +} +exports.getManifest = getManifest; +function getInfoFromManifest(versionSpec, stable, auth, arch = os_1.default.arch(), manifest) { return __awaiter(this, void 0, void 0, function* () { let info = null; - const releases = yield tc.getManifestFromRepo('actions', 'go-versions', auth, 'main'); + if (!manifest) { + core.debug('No manifest cached'); + manifest = yield getManifest(auth); + } core.info(`matching ${versionSpec}...`); - const rel = yield tc.findFromManifest(versionSpec, stable, releases, arch); + const rel = yield tc.findFromManifest(versionSpec, stable, manifest, arch); if (rel && rel.files.length > 0) { info = {}; info.type = 'manifest'; @@ -63452,6 +63479,47 @@ function parseGoVersionFile(versionFilePath) { return contents.trim(); } exports.parseGoVersionFile = parseGoVersionFile; +function resolveStableVersionDist(versionSpec, arch) { + return __awaiter(this, void 0, void 0, function* () { + let archFilter = sys.getArch(arch); + let platFilter = sys.getPlatform(); + const dlUrl = 'https://golang.org/dl/?mode=json&include=all'; + let candidates = yield module.exports.getVersionsDist(dlUrl); + if (!candidates) { + throw new Error(`golang download url did not return results`); + } + const fixedCandidates = candidates.map(item => { + return Object.assign(Object.assign({}, item), { version: makeSemver(item.version) }); + }); + const stableVersion = yield resolveStableVersionInput(versionSpec, archFilter, platFilter, fixedCandidates); + return stableVersion; + }); +} +function resolveStableVersionInput(versionSpec, arch, platform, manifest) { + return __awaiter(this, void 0, void 0, function* () { + const releases = manifest + .map(item => { + const index = item.files.findIndex(item => item.arch === arch && item.filename.includes(platform)); + if (index === -1) { + return ''; + } + return item.version; + }) + .filter(item => !!item && !semver.prerelease(item)); + if (versionSpec === utils_1.StableReleaseAlias.Stable) { + core.info(`stable version resolved as ${releases[0]}`); + return releases[0]; + } + else { + const versions = releases.map(release => `${semver.major(release)}.${semver.minor(release)}`); + const uniqueVersions = Array.from(new Set(versions)); + const oldstableVersion = releases.find(item => item.startsWith(uniqueVersions[1])); + core.info(`oldstable version resolved as ${oldstableVersion}`); + return oldstableVersion; + } + }); +} +exports.resolveStableVersionInput = resolveStableVersionInput; /***/ }), @@ -63476,7 +63544,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -63523,9 +63591,10 @@ function run() { let auth = !token ? undefined : `token ${token}`; const checkLatest = core.getBooleanInput('check-latest'); const installDir = yield installer.getGo(versionSpec, checkLatest, auth, arch); + const installDirVersion = path_1.default.basename(path_1.default.dirname(installDir)); core.addPath(path_1.default.join(installDir, 'bin')); core.info('Added go to the path'); - const version = installer.makeSemver(versionSpec); + const version = installer.makeSemver(installDirVersion); // Go versions less than 1.9 require GOROOT to be set if (semver.lt(version, '1.9.0')) { core.info('Setting GOROOT for Go version < 1.9'); @@ -63678,6 +63747,22 @@ function getArch(arch) { exports.getArch = getArch; +/***/ }), + +/***/ 1314: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.StableReleaseAlias = void 0; +var StableReleaseAlias; +(function (StableReleaseAlias) { + StableReleaseAlias["Stable"] = "stable"; + StableReleaseAlias["OldStable"] = "oldstable"; +})(StableReleaseAlias = exports.StableReleaseAlias || (exports.StableReleaseAlias = {})); + + /***/ }), /***/ 2877: diff --git a/package-lock.json b/package-lock.json index 73e443dee..24d1487cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "nock": "^10.0.6", "prettier": "^1.17.1", "ts-jest": "^27.0.5", - "typescript": "^3.8.3" + "typescript": "^4.3.3" } }, "node_modules/@actions/cache": { @@ -4694,9 +4694,9 @@ } }, "node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.3.tgz", + "integrity": "sha512-rUvLW0WtF7PF2b9yenwWUi9Da9euvDRhmH7BLyBG4DCFfOJ850LGNknmRpp8Z8kXNUPObdZQEfKOiHtXuQHHKA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8565,9 +8565,9 @@ } }, "typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.3.tgz", + "integrity": "sha512-rUvLW0WtF7PF2b9yenwWUi9Da9euvDRhmH7BLyBG4DCFfOJ850LGNknmRpp8Z8kXNUPObdZQEfKOiHtXuQHHKA==", "dev": true }, "universalify": { diff --git a/package.json b/package.json index fbbedc0ff..a12677c09 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,6 @@ "nock": "^10.0.6", "prettier": "^1.17.1", "ts-jest": "^27.0.5", - "typescript": "^3.8.3" + "typescript": "^4.3.3" } } diff --git a/src/installer.ts b/src/installer.ts index 94c5411dd..3657447b3 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -6,6 +6,7 @@ import * as httpm from '@actions/http-client'; import * as sys from './system'; import fs from 'fs'; import os from 'os'; +import {StableReleaseAlias} from './utils'; type InstallationType = 'dist' | 'manifest'; @@ -35,15 +36,41 @@ export async function getGo( auth: string | undefined, arch = os.arch() ) { + let manifest: tc.IToolRelease[] | undefined; let osPlat: string = os.platform(); + if ( + versionSpec === StableReleaseAlias.Stable || + versionSpec === StableReleaseAlias.OldStable + ) { + manifest = await getManifest(auth); + let stableVersion = await resolveStableVersionInput( + versionSpec, + arch, + osPlat, + manifest + ); + + if (!stableVersion) { + stableVersion = await resolveStableVersionDist(versionSpec, arch); + if (!stableVersion) { + throw new Error( + `Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.` + ); + } + } + + versionSpec = stableVersion; + } + if (checkLatest) { core.info('Attempting to resolve the latest version from the manifest...'); const resolvedVersion = await resolveVersionFromManifest( versionSpec, true, auth, - arch + arch, + manifest ); if (resolvedVersion) { versionSpec = resolvedVersion; @@ -69,7 +96,7 @@ export async function getGo( // Try download from internal distribution (popular versions only) // try { - info = await getInfoFromManifest(versionSpec, true, auth, arch); + info = await getInfoFromManifest(versionSpec, true, auth, arch, manifest); if (info) { downloadPath = await installGoVersion(info, auth, arch); } else { @@ -118,10 +145,17 @@ async function resolveVersionFromManifest( versionSpec: string, stable: boolean, auth: string | undefined, - arch: string + arch: string, + manifest: tc.IToolRelease[] | undefined ): Promise { try { - const info = await getInfoFromManifest(versionSpec, stable, auth, arch); + const info = await getInfoFromManifest( + versionSpec, + stable, + auth, + arch, + manifest + ); return info?.resolvedVersion; } catch (err) { core.info('Unable to resolve a version from the manifest...'); @@ -174,21 +208,26 @@ export async function extractGoArchive(archivePath: string): Promise { return extPath; } +export async function getManifest(auth: string | undefined) { + return tc.getManifestFromRepo('actions', 'go-versions', auth, 'main'); +} + export async function getInfoFromManifest( versionSpec: string, stable: boolean, auth: string | undefined, - arch = os.arch() + arch = os.arch(), + manifest?: tc.IToolRelease[] | undefined ): Promise { let info: IGoVersionInfo | null = null; - const releases = await tc.getManifestFromRepo( - 'actions', - 'go-versions', - auth, - 'main' - ); + if (!manifest) { + core.debug('No manifest cached'); + manifest = await getManifest(auth); + } + core.info(`matching ${versionSpec}...`); - const rel = await tc.findFromManifest(versionSpec, stable, releases, arch); + + const rel = await tc.findFromManifest(versionSpec, stable, manifest, arch); if (rel && rel.files.length > 0) { info = {}; @@ -326,3 +365,69 @@ export function parseGoVersionFile(versionFilePath: string): string { return contents.trim(); } + +async function resolveStableVersionDist(versionSpec: string, arch: string) { + let archFilter = sys.getArch(arch); + let platFilter = sys.getPlatform(); + const dlUrl: string = 'https://golang.org/dl/?mode=json&include=all'; + let candidates: IGoVersion[] | null = await module.exports.getVersionsDist( + dlUrl + ); + if (!candidates) { + throw new Error(`golang download url did not return results`); + } + + const fixedCandidates = candidates.map(item => { + return { + ...item, + version: makeSemver(item.version) + }; + }); + + const stableVersion = await resolveStableVersionInput( + versionSpec, + archFilter, + platFilter, + fixedCandidates + ); + + return stableVersion; +} + +export async function resolveStableVersionInput( + versionSpec: string, + arch: string, + platform: string, + manifest: tc.IToolRelease[] | IGoVersion[] +) { + const releases = manifest + .map(item => { + const index = item.files.findIndex( + item => item.arch === arch && item.filename.includes(platform) + ); + if (index === -1) { + return ''; + } + return item.version; + }) + .filter(item => !!item && !semver.prerelease(item)); + + if (versionSpec === StableReleaseAlias.Stable) { + core.info(`stable version resolved as ${releases[0]}`); + + return releases[0]; + } else { + const versions = releases.map( + release => `${semver.major(release)}.${semver.minor(release)}` + ); + const uniqueVersions = Array.from(new Set(versions)); + + const oldstableVersion = releases.find(item => + item.startsWith(uniqueVersions[1]) + ); + + core.info(`oldstable version resolved as ${oldstableVersion}`); + + return oldstableVersion; + } +} diff --git a/src/main.ts b/src/main.ts index 1ddf51149..5833f16de 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import * as installer from './installer'; import * as semver from 'semver'; import path from 'path'; import {restoreCache} from './cache-restore'; -import {isGhes, isCacheFeatureAvailable} from './cache-utils'; +import {isCacheFeatureAvailable} from './cache-utils'; import cp from 'child_process'; import fs from 'fs'; import os from 'os'; @@ -31,6 +31,7 @@ export async function run() { let auth = !token ? undefined : `token ${token}`; const checkLatest = core.getBooleanInput('check-latest'); + const installDir = await installer.getGo( versionSpec, checkLatest, @@ -38,10 +39,12 @@ export async function run() { arch ); + const installDirVersion = path.basename(path.dirname(installDir)); + core.addPath(path.join(installDir, 'bin')); core.info('Added go to the path'); - const version = installer.makeSemver(versionSpec); + const version = installer.makeSemver(installDirVersion); // Go versions less than 1.9 require GOROOT to be set if (semver.lt(version, '1.9.0')) { core.info('Setting GOROOT for Go version < 1.9'); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 000000000..79d03bcad --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,4 @@ +export enum StableReleaseAlias { + Stable = 'stable', + OldStable = 'oldstable' +}