This commit is contained in:
Priya Gupta 2026-07-07 09:20:27 +05:30 committed by GitHub
commit f688984aea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 124836 additions and 112450 deletions

View File

@ -1,6 +0,0 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!src/

View File

@ -1,51 +0,0 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-plugin-jest/recommended',
'eslint-config-prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
rules: {
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
'yoda': 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}],
'node/no-extraneous-import': 'error'
},
overrides: [
{
files: ['**/*{test,spec}.ts'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off',
}
}
],
env: {
node: true,
es6: true,
'jest/globals': true
}
};

View File

@ -10,7 +10,10 @@ allowed:
- cc0-1.0 - cc0-1.0
- unlicense - unlicense
- 0bsd - 0bsd
- blueoak-1.0.0
reviewed: reviewed:
npm: npm:
- "@actions/http-client" - "@actions/http-client"
- "balanced-match"
- "brace-expansion"

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/@actions/glob-0.7.0.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/balanced-match-4.0.4.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/minimatch-10.2.5.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,11 +0,0 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
trailingComma: 'none',
bracketSpacing: false,
arrowParens: 'avoid'
};

10
.prettierrc.json Normal file
View File

@ -0,0 +1,10 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid"
}

View File

@ -9,6 +9,11 @@ This action sets up a Go environment for use in GitHub Actions by:
- Optionally caching Go modules and build outputs - Optionally caching Go modules and build outputs
- Registering problem matchers for error output - Registering problem matchers for error output
## Breaking changes in V7
The V7 edition of the action includes:
- **Migrated to ESM** to enable support for the latest `@actions/*` package versions.
## Breaking changes in V6 ## Breaking changes in V6
The V6 edition of the action includes: The V6 edition of the action includes:

View File

@ -1,18 +1,65 @@
import * as cache from '@actions/cache'; import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
import * as core from '@actions/core';
import * as glob from '@actions/glob';
import fs from 'fs'; import fs from 'fs';
import * as cacheRestore from '../src/cache-restore'; jest.unstable_mockModule('@actions/cache', () => ({
import * as cacheUtils from '../src/cache-utils'; saveCache: jest.fn(),
import {PackageManagerInfo} from '../src/package-managers'; restoreCache: jest.fn(),
isFeatureAvailable: jest.fn()
}));
jest.unstable_mockModule('@actions/glob', () => ({
create: jest.fn(),
hashFiles: jest.fn()
}));
jest.unstable_mockModule('@actions/core', () => ({
info: jest.fn(),
warning: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
notice: jest.fn(),
setFailed: jest.fn(),
setOutput: jest.fn(),
getInput: jest.fn(),
getBooleanInput: jest.fn(),
getMultilineInput: jest.fn(),
addPath: jest.fn(),
exportVariable: jest.fn(),
saveState: jest.fn(),
getState: jest.fn(),
setSecret: jest.fn(),
isDebug: jest.fn(() => false),
startGroup: jest.fn(),
endGroup: jest.fn(),
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
toPlatformPath: jest.fn((p: string) => p),
toWin32Path: jest.fn((p: string) => p),
toPosixPath: jest.fn((p: string) => p)
}));
// Import real cache-utils (with mocked @actions) before mocking it
const realCacheUtils = await import('../src/cache-utils.js');
jest.unstable_mockModule('../src/cache-utils.js', () => ({
...realCacheUtils,
getCacheDirectoryPath: jest.fn()
}));
const cache = await import('@actions/cache');
const core = await import('@actions/core');
const glob = await import('@actions/glob');
const cacheRestore = await import('../src/cache-restore.js');
const cacheUtils = await import('../src/cache-utils.js');
import type {PackageManagerInfo} from '../src/package-managers.js';
describe('restoreCache', () => { describe('restoreCache', () => {
let hashFilesSpy: jest.SpyInstance; let hashFilesSpy: jest.Mock<typeof glob.hashFiles>;
let getCacheDirectoryPathSpy: jest.SpyInstance; let getCacheDirectoryPathSpy: jest.Mock<
let restoreCacheSpy: jest.SpyInstance; typeof cacheUtils.getCacheDirectoryPath
let infoSpy: jest.SpyInstance; >;
let setOutputSpy: jest.SpyInstance; let restoreCacheSpy: jest.Mock<typeof cache.restoreCache>;
let infoSpy: jest.Mock<typeof core.info>;
let setOutputSpy: jest.Mock<typeof core.setOutput>;
const versionSpec = '1.13.1'; const versionSpec = '1.13.1';
const packageManager = 'default'; const packageManager = 'default';
@ -24,11 +71,15 @@ describe('restoreCache', () => {
originalWorkspace = process.env.GITHUB_WORKSPACE; originalWorkspace = process.env.GITHUB_WORKSPACE;
process.env.GITHUB_WORKSPACE = '/test/workspace'; process.env.GITHUB_WORKSPACE = '/test/workspace';
//Arrange //Arrange
hashFilesSpy = jest.spyOn(glob, 'hashFiles'); hashFilesSpy = glob.hashFiles as jest.Mock<typeof glob.hashFiles>;
getCacheDirectoryPathSpy = jest.spyOn(cacheUtils, 'getCacheDirectoryPath'); getCacheDirectoryPathSpy = cacheUtils.getCacheDirectoryPath as jest.Mock<
restoreCacheSpy = jest.spyOn(cache, 'restoreCache'); typeof cacheUtils.getCacheDirectoryPath
infoSpy = jest.spyOn(core, 'info'); >;
setOutputSpy = jest.spyOn(core, 'setOutput'); restoreCacheSpy = cache.restoreCache as jest.Mock<
typeof cache.restoreCache
>;
infoSpy = core.info as jest.Mock<typeof core.info>;
setOutputSpy = core.setOutput as jest.Mock<typeof core.setOutput>;
getCacheDirectoryPathSpy.mockImplementation( getCacheDirectoryPathSpy.mockImplementation(
(PackageManager: PackageManagerInfo) => { (PackageManager: PackageManagerInfo) => {

View File

@ -1,10 +1,41 @@
import * as cache from '@actions/cache'; import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
import * as core from '@actions/core';
import fs from 'fs';
import {run} from '../src/cache-save'; jest.unstable_mockModule('@actions/cache', () => ({
import * as cacheUtils from '../src/cache-utils'; saveCache: jest.fn(),
import {State} from '../src/constants'; restoreCache: jest.fn(),
isFeatureAvailable: jest.fn()
}));
jest.unstable_mockModule('@actions/core', () => ({
info: jest.fn(),
warning: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
setFailed: jest.fn(),
getInput: jest.fn(),
getBooleanInput: jest.fn(),
getState: jest.fn(),
saveState: jest.fn()
}));
const realFs = (await import('fs')).default;
const fsExports = {...realFs, existsSync: jest.fn()};
jest.unstable_mockModule('fs', () => ({...fsExports, default: fsExports}));
// Import real cache-utils (with mocked @actions) before mocking it
const realCacheUtils = await import('../src/cache-utils.js');
jest.unstable_mockModule('../src/cache-utils.js', () => ({
...realCacheUtils,
getCacheDirectoryPath: jest.fn()
}));
const cache = await import('@actions/cache');
const core = await import('@actions/core');
const fs = (await import('fs')).default;
const cacheUtils = await import('../src/cache-utils.js');
const {run} = await import('../src/cache-save.js');
const {State} = await import('../src/constants.js');
describe('cache-save', () => { describe('cache-save', () => {
const primaryKey = 'primary-key'; const primaryKey = 'primary-key';
@ -12,24 +43,28 @@ describe('cache-save', () => {
let primaryKeyValue: string; let primaryKeyValue: string;
let matchedKeyValue: string; let matchedKeyValue: string;
let getBooleanInputSpy: jest.SpyInstance; let getBooleanInputSpy: jest.Mock<typeof core.getBooleanInput>;
let getStateSpy: jest.SpyInstance; let getStateSpy: jest.Mock<typeof core.getState>;
let infoSpy: jest.SpyInstance; let infoSpy: jest.Mock<typeof core.info>;
let warningSpy: jest.SpyInstance; let warningSpy: jest.Mock<typeof core.warning>;
let debugSpy: jest.SpyInstance; let debugSpy: jest.Mock<typeof core.debug>;
let setFailedSpy: jest.SpyInstance; let setFailedSpy: jest.Mock<typeof core.setFailed>;
let saveCacheSpy: jest.SpyInstance; let saveCacheSpy: jest.Mock<typeof cache.saveCache>;
let getCacheDirectoryPathSpy: jest.SpyInstance; let getCacheDirectoryPathSpy: jest.Mock<
let existsSpy: jest.SpyInstance; typeof cacheUtils.getCacheDirectoryPath
>;
let existsSpy: jest.Mock<typeof fs.existsSync>;
beforeEach(() => { beforeEach(() => {
primaryKeyValue = primaryKey; primaryKeyValue = primaryKey;
matchedKeyValue = 'matched-key'; matchedKeyValue = 'matched-key';
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput'); getBooleanInputSpy = core.getBooleanInput as jest.Mock<
typeof core.getBooleanInput
>;
getBooleanInputSpy.mockReturnValue(true); getBooleanInputSpy.mockReturnValue(true);
getStateSpy = jest.spyOn(core, 'getState'); getStateSpy = core.getState as jest.Mock<typeof core.getState>;
getStateSpy.mockImplementation((key: string) => { getStateSpy.mockImplementation((key: string) => {
if (key === State.CachePrimaryKey) { if (key === State.CachePrimaryKey) {
return primaryKeyValue; return primaryKeyValue;
@ -40,32 +75,34 @@ describe('cache-save', () => {
return ''; return '';
}); });
infoSpy = jest.spyOn(core, 'info'); infoSpy = core.info as jest.Mock<typeof core.info>;
infoSpy.mockImplementation(() => undefined); infoSpy.mockImplementation(() => undefined);
warningSpy = jest.spyOn(core, 'warning'); warningSpy = core.warning as jest.Mock<typeof core.warning>;
warningSpy.mockImplementation(() => undefined); warningSpy.mockImplementation(() => undefined);
debugSpy = jest.spyOn(core, 'debug'); debugSpy = core.debug as jest.Mock<typeof core.debug>;
debugSpy.mockImplementation(() => undefined); debugSpy.mockImplementation(() => undefined);
setFailedSpy = jest.spyOn(core, 'setFailed'); setFailedSpy = core.setFailed as jest.Mock<typeof core.setFailed>;
setFailedSpy.mockImplementation(() => undefined); setFailedSpy.mockImplementation(() => undefined);
saveCacheSpy = jest.spyOn(cache, 'saveCache'); saveCacheSpy = cache.saveCache as jest.Mock<typeof cache.saveCache>;
saveCacheSpy.mockImplementation(() => Promise.resolve(0)); saveCacheSpy.mockImplementation(() => Promise.resolve(0));
getCacheDirectoryPathSpy = jest.spyOn(cacheUtils, 'getCacheDirectoryPath'); getCacheDirectoryPathSpy = cacheUtils.getCacheDirectoryPath as jest.Mock<
typeof cacheUtils.getCacheDirectoryPath
>;
getCacheDirectoryPathSpy.mockImplementation(() => getCacheDirectoryPathSpy.mockImplementation(() =>
Promise.resolve(['cache_directory_path', 'cache_directory_path']) Promise.resolve(['cache_directory_path', 'cache_directory_path'])
); );
existsSpy = jest.spyOn(fs, 'existsSync'); existsSpy = fs.existsSync as jest.Mock<typeof fs.existsSync>;
existsSpy.mockImplementation(() => true); existsSpy.mockImplementation(() => true);
}); });
afterEach(() => { afterEach(() => {
jest.restoreAllMocks(); jest.clearAllMocks();
}); });
it('does not save cache when the cache input is false', async () => { it('does not save cache when the cache input is false', async () => {

View File

@ -1,22 +1,60 @@
import * as exec from '@actions/exec'; import {jest, describe, it, expect, beforeEach, afterAll} from '@jest/globals';
import * as cache from '@actions/cache';
import * as core from '@actions/core'; jest.unstable_mockModule('@actions/exec', () => ({
import * as cacheUtils from '../src/cache-utils'; exec: jest.fn(),
import {PackageManagerInfo} from '../src/package-managers'; getExecOutput: jest.fn()
}));
jest.unstable_mockModule('@actions/cache', () => ({
saveCache: jest.fn(),
restoreCache: jest.fn(),
isFeatureAvailable: jest.fn()
}));
jest.unstable_mockModule('@actions/core', () => ({
info: jest.fn(),
warning: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
notice: jest.fn(),
setFailed: jest.fn(),
setOutput: jest.fn(),
getInput: jest.fn(),
getBooleanInput: jest.fn(),
getMultilineInput: jest.fn(),
addPath: jest.fn(),
exportVariable: jest.fn(),
saveState: jest.fn(),
getState: jest.fn(),
setSecret: jest.fn(),
isDebug: jest.fn(() => false),
startGroup: jest.fn(),
endGroup: jest.fn(),
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
toPlatformPath: jest.fn((p: string) => p),
toWin32Path: jest.fn((p: string) => p),
toPosixPath: jest.fn((p: string) => p)
}));
const exec = await import('@actions/exec');
const cache = await import('@actions/cache');
const core = await import('@actions/core');
const cacheUtils = await import('../src/cache-utils.js');
import type {PackageManagerInfo} from '../src/package-managers.js';
describe('getCommandOutput', () => { describe('getCommandOutput', () => {
//Arrange //Arrange
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); const getExecOutputSpy = exec.getExecOutput as jest.Mock<
typeof exec.getExecOutput
>;
it('should return trimmed stdout in case of successful exit code', async () => { it('should return trimmed stdout in case of successful exit code', async () => {
//Arrange //Arrange
const stdoutResult = ' stdout '; const stdoutResult = ' stdout ';
const trimmedStdout = stdoutResult.trim(); const trimmedStdout = stdoutResult.trim();
getExecOutputSpy.mockImplementation((commandLine: string) => { getExecOutputSpy.mockImplementation(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 0, stdout: stdoutResult, stderr: ''};
resolve({exitCode: 0, stdout: stdoutResult, stderr: ''});
});
}); });
//Act + Assert //Act + Assert
@ -29,10 +67,8 @@ describe('getCommandOutput', () => {
//Arrange //Arrange
const stderrResult = 'error message'; const stderrResult = 'error message';
getExecOutputSpy.mockImplementation((commandLine: string) => { getExecOutputSpy.mockImplementation(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 10, stdout: '', stderr: stderrResult};
resolve({exitCode: 10, stdout: '', stderr: stderrResult});
});
}); });
//Act + Assert //Act + Assert
@ -70,7 +106,9 @@ describe('getPackageManagerInfo', () => {
describe('getCacheDirectoryPath', () => { describe('getCacheDirectoryPath', () => {
//Arrange //Arrange
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); const getExecOutputSpy = exec.getExecOutput as jest.Mock<
typeof exec.getExecOutput
>;
const validPackageManager: PackageManagerInfo = { const validPackageManager: PackageManagerInfo = {
dependencyFilePattern: 'go.mod', dependencyFilePattern: 'go.mod',
@ -79,10 +117,8 @@ describe('getCacheDirectoryPath', () => {
it('should return path to the cache folders which specified package manager uses', async () => { it('should return path to the cache folders which specified package manager uses', async () => {
//Arrange //Arrange
getExecOutputSpy.mockImplementation((commandLine: string) => { getExecOutputSpy.mockImplementation(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''};
resolve({exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''});
});
}); });
const expectedResult = ['path/to/cache/folder', 'path/to/cache/folder']; const expectedResult = ['path/to/cache/folder', 'path/to/cache/folder'];
@ -95,16 +131,12 @@ describe('getCacheDirectoryPath', () => {
it('should return path to the cache folder if one command return empty str', async () => { it('should return path to the cache folder if one command return empty str', async () => {
//Arrange //Arrange
getExecOutputSpy.mockImplementationOnce((commandLine: string) => { getExecOutputSpy.mockImplementationOnce(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''};
resolve({exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''});
});
}); });
getExecOutputSpy.mockImplementationOnce((commandLine: string) => { getExecOutputSpy.mockImplementationOnce(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 0, stdout: '', stderr: ''};
resolve({exitCode: 0, stdout: '', stderr: ''});
});
}); });
const expectedResult = ['path/to/cache/folder']; const expectedResult = ['path/to/cache/folder'];
@ -116,10 +148,8 @@ describe('getCacheDirectoryPath', () => {
}); });
it('should throw if the both commands return empty str', async () => { it('should throw if the both commands return empty str', async () => {
getExecOutputSpy.mockImplementation((commandLine: string) => { getExecOutputSpy.mockImplementation(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 10, stdout: '', stderr: ''};
resolve({exitCode: 10, stdout: '', stderr: ''});
});
}); });
//Act + Assert //Act + Assert
@ -129,10 +159,8 @@ describe('getCacheDirectoryPath', () => {
}); });
it('should throw if the specified package name is invalid', async () => { it('should throw if the specified package name is invalid', async () => {
getExecOutputSpy.mockImplementation((commandLine: string) => { getExecOutputSpy.mockImplementation(async (commandLine: string) => {
return new Promise<exec.ExecOutput>(resolve => { return {exitCode: 10, stdout: '', stderr: 'Error message'};
resolve({exitCode: 10, stdout: '', stderr: 'Error message'});
});
}); });
//Act + Assert //Act + Assert
@ -144,8 +172,10 @@ describe('getCacheDirectoryPath', () => {
describe('isCacheFeatureAvailable', () => { describe('isCacheFeatureAvailable', () => {
//Arrange //Arrange
const isFeatureAvailableSpy = jest.spyOn(cache, 'isFeatureAvailable'); const isFeatureAvailableSpy = cache.isFeatureAvailable as jest.Mock<
const warningSpy = jest.spyOn(core, 'warning'); typeof cache.isFeatureAvailable
>;
const warningSpy = core.warning as jest.Mock<typeof core.warning>;
it('should return true when cache feature is available', () => { it('should return true when cache feature is available', () => {
//Arrange //Arrange
@ -211,10 +241,9 @@ describe('isCacheFeatureAvailable', () => {
}); });
describe('isGhes', () => { describe('isGhes', () => {
const pristineEnv = process.env; const pristineEnv = {...process.env};
beforeEach(() => { beforeEach(() => {
jest.resetModules();
process.env = {...pristineEnv}; process.env = {...pristineEnv};
}); });

View File

@ -1,17 +1,98 @@
import * as core from '@actions/core'; import {
import * as io from '@actions/io'; jest,
import * as tc from '@actions/tool-cache'; describe,
it,
expect,
beforeAll,
beforeEach,
afterEach,
afterAll
} from '@jest/globals';
import fs from 'fs'; import fs from 'fs';
import cp from 'child_process'; import cp from 'child_process';
import osm, {type} from 'os';
import path from 'path';
import * as main from '../src/main';
import * as im from '../src/installer';
import * as httpm from '@actions/http-client';
import goJsonData from './data/golang-dl.json'; import goJsonData from './data/golang-dl.json' with {type: 'json'};
import matchers from '../matchers.json'; import matchers from '../matchers.json' with {type: 'json'};
import goTestManifest from './data/versions-manifest.json'; import goTestManifest from './data/versions-manifest.json' with {type: 'json'};
import type {IGoVersion} from '../src/installer.js';
import type {IToolRelease} from '@actions/tool-cache';
const httpClientGetJson = jest.fn();
const realCore = await import('@actions/core');
jest.unstable_mockModule('@actions/core', () => ({
...realCore,
getInput: jest.fn(),
getBooleanInput: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
exportVariable: jest.fn()
}));
const realOs = (await import('os')).default;
const osPlatformMock = jest.fn();
const osArchMock = jest.fn();
const osExports = {
...realOs,
platform: osPlatformMock,
arch: osArchMock
};
jest.unstable_mockModule('os', () => ({...osExports, default: osExports}));
const realPath = (await import('path')).default;
const pathJoinMock = jest.fn();
const pathExports = {
...realPath,
join: pathJoinMock
};
jest.unstable_mockModule('path', () => ({
...pathExports,
default: pathExports
}));
jest.unstable_mockModule('@actions/io', () => ({
which: jest.fn(),
mkdirP: jest.fn(),
rmRF: jest.fn(),
mv: jest.fn(),
cp: jest.fn()
}));
const realTc = await import('@actions/tool-cache');
jest.unstable_mockModule('@actions/tool-cache', () => ({
...realTc,
find: jest.fn(),
downloadTool: jest.fn(),
extractTar: jest.fn(),
extractZip: jest.fn(),
cacheDir: jest.fn(),
getManifestFromRepo: jest.fn()
}));
const realHttp = await import('@actions/http-client');
jest.unstable_mockModule('@actions/http-client', () => ({
...realHttp,
HttpClient: jest.fn().mockImplementation(() => ({
getJson: httpClientGetJson
}))
}));
jest.unstable_mockModule('../src/go-version-fetch.js', () => ({
getVersionsDist: jest.fn()
}));
const core = await import('@actions/core');
const io = await import('@actions/io');
const tc = await import('@actions/tool-cache');
const vf = await import('../src/go-version-fetch.js');
const main = await import('../src/main.js');
const im = await import('../src/installer.js');
const osm = (await import('os')).default;
const path = (await import('path')).default;
const matcherPattern = matchers.problemMatcher[0].pattern[0]; const matcherPattern = matchers.problemMatcher[0].pattern[0];
const matcherRegExp = new RegExp(matcherPattern.regexp); const matcherRegExp = new RegExp(matcherPattern.regexp);
const win32Join = path.win32.join; const win32Join = path.win32.join;
@ -23,32 +104,31 @@ describe('setup-go', () => {
let inputs = {} as any; let inputs = {} as any;
let os = {} as any; let os = {} as any;
let inSpy: jest.SpyInstance; let inSpy: jest.Mock<typeof core.getInput>;
let getBooleanInputSpy: jest.SpyInstance; let getBooleanInputSpy: jest.Mock<typeof core.getBooleanInput>;
let exportVarSpy: jest.SpyInstance; let exportVarSpy: jest.Mock<typeof core.exportVariable>;
let findSpy: jest.SpyInstance; let findSpy: jest.Mock;
let cnSpy: jest.SpyInstance; let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
let logSpy: jest.SpyInstance; let logSpy: jest.Mock;
let getSpy: jest.SpyInstance; let getSpy: jest.Mock;
let platSpy: jest.SpyInstance; let platSpy: jest.Mock;
let archSpy: jest.SpyInstance; let archSpy: jest.Mock;
let joinSpy: jest.SpyInstance; let joinSpy: jest.Mock<typeof path.join>;
let dlSpy: jest.SpyInstance; let dlSpy: jest.Mock;
let extractTarSpy: jest.SpyInstance; let extractTarSpy: jest.Mock;
let extractZipSpy: jest.SpyInstance; let extractZipSpy: jest.Mock;
let cacheSpy: jest.SpyInstance; let cacheSpy: jest.Mock;
let dbgSpy: jest.SpyInstance; let dbgSpy: jest.Mock;
let whichSpy: jest.SpyInstance; let whichSpy: jest.Mock;
let existsSpy: jest.SpyInstance; let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
let readFileSpy: jest.SpyInstance; let readFileSpy: jest.SpiedFunction<typeof fs.readFileSync>;
let mkdirpSpy: jest.SpyInstance; let mkdirpSpy: jest.Mock;
let mkdirSpy: jest.SpyInstance; let mkdirSpy: jest.SpiedFunction<typeof fs.mkdir>;
let symlinkSpy: jest.SpyInstance; let symlinkSpy: jest.SpiedFunction<typeof fs.symlinkSync>;
let execSpy: jest.SpyInstance; let execSpy: jest.SpiedFunction<typeof cp.execSync>;
let execFileSpy: jest.SpyInstance; let execFileSpy: jest.SpiedFunction<typeof cp.execFileSync>;
let getManifestSpy: jest.SpyInstance; let getManifestSpy: jest.Mock;
let getAllVersionsSpy: jest.SpyInstance; let httpmGetJsonSpy: jest.Mock;
let httpmGetJsonSpy: jest.SpyInstance;
beforeAll(async () => { beforeAll(async () => {
process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible) process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
@ -59,17 +139,19 @@ describe('setup-go', () => {
// @actions/core // @actions/core
inputs = {}; inputs = {};
inSpy = jest.spyOn(core, 'getInput'); inSpy = core.getInput as jest.Mock<typeof core.getInput>;
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput'); getBooleanInputSpy = core.getBooleanInput as jest.Mock<
typeof core.getBooleanInput
>;
getBooleanInputSpy.mockImplementation(name => inputs[name]); getBooleanInputSpy.mockImplementation(name => inputs[name]);
exportVarSpy = jest.spyOn(core, 'exportVariable'); exportVarSpy = core.exportVariable as jest.Mock<typeof core.exportVariable>;
// node // node
os = {}; os = {};
platSpy = jest.spyOn(osm, 'platform'); platSpy = osm.platform as jest.Mock;
platSpy.mockImplementation(() => os['platform']); platSpy.mockImplementation(() => os['platform']);
archSpy = jest.spyOn(osm, 'arch'); archSpy = osm.arch as jest.Mock;
archSpy.mockImplementation(() => os['arch']); archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync'); execSpy = jest.spyOn(cp, 'execSync');
execFileSpy = jest.spyOn(cp, 'execFileSync'); execFileSpy = jest.spyOn(cp, 'execFileSync');
@ -78,7 +160,7 @@ describe('setup-go', () => {
}); });
// switch path join behaviour based on set os.platform // switch path join behaviour based on set os.platform
joinSpy = jest.spyOn(path, 'join'); joinSpy = path.join as jest.Mock<typeof path.join>;
joinSpy.mockImplementation((...paths: string[]): string => { joinSpy.mockImplementation((...paths: string[]): string => {
if (os['platform'] == 'win32') { if (os['platform'] == 'win32') {
return win32Join(...paths); return win32Join(...paths);
@ -88,23 +170,22 @@ describe('setup-go', () => {
}); });
// @actions/tool-cache // @actions/tool-cache
findSpy = jest.spyOn(tc, 'find'); findSpy = tc.find as jest.Mock;
dlSpy = jest.spyOn(tc, 'downloadTool'); dlSpy = tc.downloadTool as jest.Mock;
extractTarSpy = jest.spyOn(tc, 'extractTar'); extractTarSpy = tc.extractTar as jest.Mock;
extractZipSpy = jest.spyOn(tc, 'extractZip'); extractZipSpy = tc.extractZip as jest.Mock;
cacheSpy = jest.spyOn(tc, 'cacheDir'); cacheSpy = tc.cacheDir as jest.Mock;
getSpy = jest.spyOn(im, 'getVersionsDist'); getSpy = vf.getVersionsDist as jest.Mock;
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo'); getManifestSpy = tc.getManifestFromRepo as jest.Mock;
getAllVersionsSpy = jest.spyOn(im, 'getManifest');
// httm // httm
httpmGetJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson'); httpmGetJsonSpy = httpClientGetJson;
// io // io
whichSpy = jest.spyOn(io, 'which'); whichSpy = io.which as jest.Mock;
existsSpy = jest.spyOn(fs, 'existsSync'); existsSpy = jest.spyOn(fs, 'existsSync');
readFileSpy = jest.spyOn(fs, 'readFileSync'); readFileSpy = jest.spyOn(fs, 'readFileSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP'); mkdirpSpy = io.mkdirP as jest.Mock;
// fs // fs
mkdirSpy = jest.spyOn(fs, 'mkdir'); mkdirSpy = jest.spyOn(fs, 'mkdir');
@ -112,25 +193,16 @@ describe('setup-go', () => {
symlinkSpy.mockImplementation(() => {}); symlinkSpy.mockImplementation(() => {});
// gets // gets
getManifestSpy.mockImplementation(() => <tc.IToolRelease[]>goTestManifest); getManifestSpy.mockImplementation(() => goTestManifest as IToolRelease[]);
// writes // writes
cnSpy = jest.spyOn(process.stdout, 'write'); cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(core, 'info'); logSpy = core.info as jest.Mock;
dbgSpy = jest.spyOn(core, 'debug'); dbgSpy = core.debug as jest.Mock;
getSpy.mockImplementation(() => <im.IGoVersion[] | null>goJsonData); getSpy.mockImplementation(() => goJsonData as IGoVersion[] | null);
cnSpy.mockImplementation(line => { cnSpy.mockImplementation(() => true);
// uncomment to debug logSpy.mockImplementation(() => {});
// process.stderr.write('write:' + line + '\n'); dbgSpy.mockImplementation(() => {});
});
logSpy.mockImplementation(line => {
// uncomment to debug
//process.stderr.write('log:' + line + '\n');
});
dbgSpy.mockImplementation(msg => {
// uncomment to see debug output
// process.stderr.write(msg + '\n');
});
}); });
afterEach(() => { afterEach(() => {
@ -138,6 +210,10 @@ describe('setup-go', () => {
delete process.env[im.GOTOOLCHAIN_ENV_VAR]; delete process.env[im.GOTOOLCHAIN_ENV_VAR];
delete process.env['GO_DOWNLOAD_BASE_URL']; delete process.env['GO_DOWNLOAD_BASE_URL'];
// reset the exit code that core.setFailed sets on the error-path tests so
// that a passing run does not leak a non-zero process exit code
process.exitCode = 0;
//jest.resetAllMocks(); //jest.resetAllMocks();
jest.clearAllMocks(); jest.clearAllMocks();
//jest.restoreAllMocks(); //jest.restoreAllMocks();
@ -171,8 +247,10 @@ describe('setup-go', () => {
}); });
it('should return manifest from raw URL if repo fetch fails', async () => { it('should return manifest from raw URL if repo fetch fails', async () => {
getManifestSpy.mockRejectedValue(new Error('Fetch failed')); (getManifestSpy as jest.Mock<any>).mockRejectedValue(
httpmGetJsonSpy.mockResolvedValue({ new Error('Fetch failed')
);
(httpmGetJsonSpy as jest.Mock<any>).mockResolvedValue({
result: goTestManifest result: goTestManifest
}); });
const manifest = await im.getManifest(undefined); const manifest = await im.getManifest(undefined);
@ -211,7 +289,7 @@ describe('setup-go', () => {
os.arch = 'x64'; os.arch = 'x64';
// spec: 1.13.0 => 1.13 // spec: 1.13.0 => 1.13
const match: im.IGoVersion | undefined = await im.findMatch('1.13.0'); const match: IGoVersion | undefined = await im.findMatch('1.13.0');
expect(match).toBeDefined(); expect(match).toBeDefined();
const version: string = match ? match.version : ''; const version: string = match ? match.version : '';
expect(version).toBe('go1.13'); expect(version).toBe('go1.13');
@ -224,7 +302,7 @@ describe('setup-go', () => {
os.arch = 'x64'; os.arch = 'x64';
// spec: 1.13 => 1.13.7 (latest) // spec: 1.13 => 1.13.7 (latest)
const match: im.IGoVersion | undefined = await im.findMatch('1.13'); const match: IGoVersion | undefined = await im.findMatch('1.13');
expect(match).toBeDefined(); expect(match).toBeDefined();
const version: string = match ? match.version : ''; const version: string = match ? match.version : '';
expect(version).toBe('go1.13.7'); expect(version).toBe('go1.13.7');
@ -237,7 +315,7 @@ describe('setup-go', () => {
os.arch = 'x64'; os.arch = 'x64';
// spec: ^1.13.6 => 1.13.7 // spec: ^1.13.6 => 1.13.7
const match: im.IGoVersion | undefined = await im.findMatch('^1.13.6'); const match: IGoVersion | undefined = await im.findMatch('^1.13.6');
expect(match).toBeDefined(); expect(match).toBeDefined();
const version: string = match ? match.version : ''; const version: string = match ? match.version : '';
expect(version).toBe('go1.13.7'); expect(version).toBe('go1.13.7');
@ -250,7 +328,7 @@ describe('setup-go', () => {
os.arch = 'x32'; os.arch = 'x32';
// spec: 1 => 1.13.7 (latest) // spec: 1 => 1.13.7 (latest)
const match: im.IGoVersion | undefined = await im.findMatch('1'); const match: IGoVersion | undefined = await im.findMatch('1');
expect(match).toBeDefined(); expect(match).toBeDefined();
const version: string = match ? match.version : ''; const version: string = match ? match.version : '';
expect(version).toBe('go1.13.7'); expect(version).toBe('go1.13.7');
@ -263,7 +341,7 @@ describe('setup-go', () => {
os.arch = 'x64'; os.arch = 'x64';
// spec: 1.14, stable=false => go1.14rc1 // spec: 1.14, stable=false => go1.14rc1
const match: im.IGoVersion | undefined = await im.findMatch('1.14.0-rc.1'); const match: IGoVersion | undefined = await im.findMatch('1.14.0-rc.1');
expect(match).toBeDefined(); expect(match).toBeDefined();
const version: string = match ? match.version : ''; const version: string = match ? match.version : '';
expect(version).toBe('go1.14rc1'); expect(version).toBe('go1.14rc1');
@ -817,10 +895,9 @@ describe('setup-go', () => {
getManifestSpy.mockImplementation(() => { getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest'); throw new Error('Unable to download manifest');
}); });
httpmGetJsonSpy.mockRejectedValue( (httpmGetJsonSpy as jest.Mock<any>).mockRejectedValue(
new Error('Unable to download manifest from raw URL') new Error('Unable to download manifest from raw URL')
); );
getAllVersionsSpy.mockImplementationOnce(() => undefined);
dlSpy.mockImplementation(async () => '/some/temp/path'); dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.7/x64'); const toolPath = path.normalize('/cache/go/1.13.7/x64');

View File

@ -1,4 +1,5 @@
import {isSelfHosted} from '../src/utils'; import {describe, it, expect, beforeEach, afterEach} from '@jest/globals';
import {isSelfHosted} from '../src/utils.js';
describe('utils', () => { describe('utils', () => {
describe('isSelfHosted', () => { describe('isSelfHosted', () => {

View File

@ -1,38 +1,57 @@
import fs from 'fs'; import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import path from 'path'; import path from 'path';
const statSyncMock = jest.fn(() => ({
isDirectory: () => true,
isFile: () => true
}));
const readdirSyncMock = jest.fn(() => [] as any);
const writeFileSyncMock = jest.fn();
const mkdirMock = jest.fn();
const symlinkSyncMock = jest.fn();
jest.unstable_mockModule('@actions/io', () => ({
which: jest.fn(),
mkdirP: jest.fn(() => Promise.resolve()),
rmRF: jest.fn(() => Promise.resolve()),
mv: jest.fn(() => Promise.resolve()),
cp: jest.fn(() => Promise.resolve())
}));
const realFs = (await import('fs')).default;
const fsExports = {
...realFs,
statSync: statSyncMock,
readdirSync: readdirSyncMock,
writeFileSync: writeFileSyncMock,
mkdir: mkdirMock,
symlinkSync: symlinkSyncMock
};
jest.unstable_mockModule('fs', () => ({...fsExports, default: fsExports}));
const io = await import('@actions/io');
const tc = await import('@actions/tool-cache');
describe('Windows performance workaround', () => { describe('Windows performance workaround', () => {
let mkdirSpy: jest.SpyInstance; let rmRFSpy: jest.Mock;
let symlinkSpy: jest.SpyInstance; let mkdirPSpy: jest.Mock;
let statSpy: jest.SpyInstance; let cpSpy: jest.Mock;
let readdirSpy: jest.SpyInstance;
let writeFileSpy: jest.SpyInstance;
let rmRFSpy: jest.SpyInstance;
let mkdirPSpy: jest.SpyInstance;
let cpSpy: jest.SpyInstance;
let runnerToolCache: string | undefined; let runnerToolCache: string | undefined;
beforeEach(() => { beforeEach(() => {
mkdirSpy = jest.spyOn(fs, 'mkdir'); rmRFSpy = io.rmRF as jest.Mock;
symlinkSpy = jest.spyOn(fs, 'symlinkSync'); mkdirPSpy = io.mkdirP as jest.Mock;
statSpy = jest.spyOn(fs, 'statSync'); cpSpy = io.cp as jest.Mock;
readdirSpy = jest.spyOn(fs, 'readdirSync');
writeFileSpy = jest.spyOn(fs, 'writeFileSync');
rmRFSpy = jest.spyOn(io, 'rmRF');
mkdirPSpy = jest.spyOn(io, 'mkdirP');
cpSpy = jest.spyOn(io, 'cp');
// default implementations // default implementations
// @ts-ignore - not implement unused methods statSyncMock.mockImplementation(() => ({
statSpy.mockImplementation(() => ({ isDirectory: () => true,
isDirectory: () => true isFile: () => true
})); }));
readdirSpy.mockImplementation(() => []); readdirSyncMock.mockImplementation(() => []);
writeFileSpy.mockImplementation(() => {}); writeFileSyncMock.mockImplementation(() => {});
mkdirSpy.mockImplementation(() => {}); mkdirMock.mockImplementation(() => {});
symlinkSpy.mockImplementation(() => {}); symlinkSyncMock.mockImplementation(() => {});
rmRFSpy.mockImplementation(() => Promise.resolve()); rmRFSpy.mockImplementation(() => Promise.resolve());
mkdirPSpy.mockImplementation(() => Promise.resolve()); mkdirPSpy.mockImplementation(() => Promise.resolve());
cpSpy.mockImplementation(() => Promise.resolve()); cpSpy.mockImplementation(() => Promise.resolve());

113066
dist/cache-save/index.js vendored

File diff suppressed because one or more lines are too long

3
dist/cache-save/package.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"type": "module"
}

118705
dist/setup/index.js vendored

File diff suppressed because one or more lines are too long

3
dist/setup/package.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"type": "module"
}

74
eslint.config.mjs Normal file
View File

@ -0,0 +1,74 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import jest from 'eslint-plugin-jest';
import n from 'eslint-plugin-n';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
export default [
{
ignores: ['**/*', '!src/**', '!__tests__/**']
},
js.configs.recommended,
{
files: ['**/*.ts'],
languageOptions: {
parser: tsParser,
ecmaVersion: 2022,
sourceType: 'module',
globals: {
...globals.node,
...globals.es2015
}
},
plugins: {
'@typescript-eslint': tsPlugin,
n
},
rules: {
...tsPlugin.configs.recommended.rules,
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
yoda: 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}],
'no-undef': 'off',
'no-useless-assignment': 'off',
'n/no-extraneous-import': 'error'
}
},
{
files: ['**/*{test,spec}.ts'],
plugins: {jest},
languageOptions: {
globals: {
...globals.jest
}
},
rules: {
...jest.configs['flat/recommended'].rules,
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off'
}
},
prettier
];

View File

@ -1,11 +0,0 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}

24
jest.config.ts Normal file
View File

@ -0,0 +1,24 @@
export default {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
roots: ['<rootDir>'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
useESM: true,
diagnostics: {
ignoreCodes: [151002]
}
}
]
},
extensionsToTreatAsEsm: ['.ts'],
transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1'
},
verbose: true
};

4620
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
{ {
"name": "setup-go", "name": "setup-go",
"version": "6.5.0", "version": "7.0.0",
"type": "module",
"private": true, "private": true,
"description": "setup go action", "description": "setup go action",
"main": "lib/setup-go.js", "main": "lib/setup-go.js",
@ -9,11 +10,11 @@
}, },
"scripts": { "scripts": {
"build": "tsc && ncc build -o dist/setup src/setup-go.ts && ncc build -o dist/cache-save src/cache-save.ts", "build": "tsc && ncc build -o dist/setup src/setup-go.ts && ncc build -o dist/cache-save src/cache-save.ts",
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"", "format": "prettier --no-error-on-unmatched-pattern --write \"**/*.{ts,yml,yaml}\"",
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"", "format-check": "prettier --no-error-on-unmatched-pattern --check \"**/*.{ts,yml,yaml}\"",
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"", "lint": "eslint \"**/*.ts\"",
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix", "lint:fix": "eslint \"**/*.ts\" --fix",
"test": "jest --coverage", "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --coverage",
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test" "pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
}, },
"repository": { "repository": {
@ -28,31 +29,32 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^5.1.0", "@actions/cache": "^6.1.0",
"@actions/core": "^2.0.3", "@actions/core": "^3.0.1",
"@actions/exec": "^2.0.0", "@actions/exec": "^3.0.0",
"@actions/glob": "^0.5.1", "@actions/glob": "^0.7.0",
"@actions/http-client": "^3.0.2", "@actions/http-client": "^4.0.1",
"@actions/io": "^2.0.0", "@actions/io": "^3.0.2",
"@actions/tool-cache": "^3.0.1", "@actions/tool-cache": "^4.0.0",
"semver": "^7.7.3" "semver": "^7.8.5"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.14", "@eslint/js": "^10.0.1",
"@types/node": "^25.9.3", "@jest/globals": "^30.4.1",
"@types/semver": "^7.7.1", "@types/node": "^26.0.0",
"@typescript-eslint/eslint-plugin": "^8.61.0", "@types/semver": "^7.7.0",
"@typescript-eslint/parser": "^8.61.0", "@typescript-eslint/eslint-plugin": "^8.62.0",
"@vercel/ncc": "^0.38.1", "@typescript-eslint/parser": "^8.62.0",
"eslint": "^8.57.0", "@vercel/ncc": "^0.44.0",
"eslint-config-prettier": "^10.1.8", "eslint": "^10.5.0",
"eslint-plugin-jest": "^29.0.1", "eslint-config-prettier": "^10.0.0",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-jest": "^29.15.2",
"jest": "^29.7.0", "eslint-plugin-n": "^18.1.0",
"jest-circus": "^29.7.0", "globals": "^17.7.0",
"nock": "^10.0.6", "jest": "^30.4.2",
"prettier": "^2.8.4", "nock": "^14.0.0",
"ts-jest": "^29.3.2", "prettier": "^3.8.4",
"typescript": "^5.8.3" "ts-jest": "^29.4.11",
"typescript": "^6.0.3"
} }
} }

View File

@ -4,9 +4,9 @@ import * as glob from '@actions/glob';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import {State, Outputs} from './constants'; import {State, Outputs} from './constants.js';
import {PackageManagerInfo} from './package-managers'; import {PackageManagerInfo} from './package-managers.js';
import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils'; import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils.js';
export const restoreCache = async ( export const restoreCache = async (
versionSpec: string, versionSpec: string,

View File

@ -1,8 +1,8 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import fs from 'fs'; import fs from 'fs';
import {State} from './constants'; import {State} from './constants.js';
import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils'; import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils.js';
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // 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 // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to

View File

@ -1,7 +1,10 @@
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import {supportedPackageManagers, PackageManagerInfo} from './package-managers'; import {
supportedPackageManagers,
PackageManagerInfo
} from './package-managers.js';
export const getCommandOutput = async (toolCommand: string) => { export const getCommandOutput = async (toolCommand: string) => {
let {stdout, stderr, exitCode} = await exec.getExecOutput( let {stdout, stderr, exitCode} = await exec.getExecOutput(

13
src/go-version-fetch.ts Normal file
View File

@ -0,0 +1,13 @@
import * as httpm from '@actions/http-client';
import type {IGoVersion} from './installer.js';
export async function getVersionsDist(
dlUrl: string
): Promise<IGoVersion[] | null> {
// this returns versions descending so latest is first
const http: httpm.HttpClient = new httpm.HttpClient('setup-go', [], {
allowRedirects: true,
maxRedirects: 3
});
return (await http.getJson<IGoVersion[]>(dlUrl)).result;
}

View File

@ -3,13 +3,14 @@ import * as core from '@actions/core';
import * as path from 'path'; import * as path from 'path';
import * as semver from 'semver'; import * as semver from 'semver';
import * as httpm from '@actions/http-client'; import * as httpm from '@actions/http-client';
import * as sys from './system'; import * as sys from './system.js';
import crypto from 'crypto'; import crypto from 'crypto';
import cp from 'child_process'; import cp from 'child_process';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import {StableReleaseAlias, isSelfHosted} from './utils'; import {StableReleaseAlias, isSelfHosted} from './utils.js';
import {Architecture} from './types'; import {Architecture} from './types.js';
import {getVersionsDist} from './go-version-fetch.js';
export const GOTOOLCHAIN_ENV_VAR = 'GOTOOLCHAIN'; export const GOTOOLCHAIN_ENV_VAR = 'GOTOOLCHAIN';
export const GOTOOLCHAIN_LOCAL_VAL = 'local'; export const GOTOOLCHAIN_LOCAL_VAL = 'local';
@ -167,12 +168,14 @@ export async function getGo(
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) { if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
throw new Error( throw new Error(
`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` + `The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
`Download URL returned HTTP 404: ${downloadUrl}` `Download URL returned HTTP 404: ${downloadUrl}`,
{cause: err}
); );
} }
throw new Error( throw new Error(
`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` + `Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
`from ${downloadUrl}: ${err}` `from ${downloadUrl}: ${err}`,
{cause: err}
); );
} }
} else { } else {
@ -218,7 +221,9 @@ export async function getGo(
core.info('Install from dist'); core.info('Install from dist');
downloadPath = await installGoVersion(info, undefined, arch); downloadPath = await installGoVersion(info, undefined, arch);
} catch (err) { } catch (err) {
throw new Error(`Failed to download version ${versionSpec}: ${err}`); throw new Error(`Failed to download version ${versionSpec}: ${err}`, {
cause: err
});
} }
} }
} }
@ -569,9 +574,7 @@ export async function findMatch(
let result: IGoVersion | undefined; let result: IGoVersion | undefined;
let match: IGoVersion | undefined; let match: IGoVersion | undefined;
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist( const candidates: IGoVersion[] | null = await getVersionsDist(dlUrl);
dlUrl
);
if (!candidates) { if (!candidates) {
throw new Error(`golang download url did not return results`); throw new Error(`golang download url did not return results`);
} }
@ -607,17 +610,6 @@ export async function findMatch(
return result; return result;
} }
export async function getVersionsDist(
dlUrl: string
): Promise<IGoVersion[] | null> {
// this returns versions descending so latest is first
const http: httpm.HttpClient = new httpm.HttpClient('setup-go', [], {
allowRedirects: true,
maxRedirects: 3
});
return (await http.getJson<IGoVersion[]>(dlUrl)).result;
}
// //
// Convert the go version syntax into semver for semver matching // Convert the go version syntax into semver for semver matching
// 1.13.1 => 1.13.1 // 1.13.1 => 1.13.1
@ -686,9 +678,8 @@ async function resolveStableVersionDist(
) { ) {
const archFilter = sys.getArch(arch); const archFilter = sys.getArch(arch);
const platFilter = sys.getPlatform(); const platFilter = sys.getPlatform();
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist( const candidates: IGoVersion[] | null =
GOLANG_DOWNLOAD_URL await getVersionsDist(GOLANG_DOWNLOAD_URL);
);
if (!candidates) { if (!candidates) {
throw new Error(`golang download url did not return results`); throw new Error(`golang download url did not return results`);
} }

View File

@ -1,14 +1,15 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as installer from './installer'; import * as installer from './installer.js';
import * as semver from 'semver'; import * as semver from 'semver';
import path from 'path'; import path from 'path';
import {restoreCache} from './cache-restore'; import {fileURLToPath} from 'url';
import {isCacheFeatureAvailable} from './cache-utils'; import {restoreCache} from './cache-restore.js';
import {isCacheFeatureAvailable} from './cache-utils.js';
import cp from 'child_process'; import cp from 'child_process';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import {Architecture} from './types'; import {Architecture} from './types.js';
export async function run() { export async function run() {
try { try {
@ -91,7 +92,11 @@ export async function run() {
} }
// add problem matchers // add problem matchers
const matchersPath = path.join(__dirname, '../..', 'matchers.json'); const matchersPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'../..',
'matchers.json'
);
core.info(`##[add-matcher]${matchersPath}`); core.info(`##[add-matcher]${matchersPath}`);
// output the version actually being used // output the version actually being used

View File

@ -1,3 +1,3 @@
import {run} from './main'; import {run} from './main.js';
run(); run();

View File

@ -1,5 +1,5 @@
import os from 'os'; import os from 'os';
import {Architecture} from './types'; import {Architecture} from './types.js';
export function getPlatform(): string { export function getPlatform(): string {
// darwin and linux match already // darwin and linux match already

View File

@ -2,8 +2,8 @@
"compilerOptions": { "compilerOptions": {
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "NodeNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
@ -38,7 +38,7 @@
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */ /* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "moduleResolution": "NodeNext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
@ -61,5 +61,5 @@
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": ["node_modules", "**/*.test.ts", "jest.config.ts"]
} }