mirror of
https://code.forgejo.org/actions/setup-go.git
synced 2026-08-27 20:44:57 -04:00
Merge f75efb3f19 into fba3fb4ead
This commit is contained in:
commit
80d79fad81
7 changed files with 387 additions and 24 deletions
|
|
@ -44,6 +44,11 @@ See [action.yml](action.yml).
|
|||
# Note: if both go-version and go-version-file are provided, go-version takes precedence.
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
# How to interpret an exact version read from go-version-file.
|
||||
# Set to latest-patch to use the newest patch release of the same minor version.
|
||||
# Default: exact
|
||||
go-version-file-behavior: 'exact'
|
||||
|
||||
# Set this option if you want the action to check for the latest available version
|
||||
# Default: false
|
||||
check-latest: false
|
||||
|
|
|
|||
|
|
@ -1089,6 +1089,186 @@ use .
|
|||
);
|
||||
});
|
||||
|
||||
describe('go-version-file-behavior', () => {
|
||||
const buildGoMod = (goVersion: string) => `module example.com/mymodule
|
||||
|
||||
go ${goVersion}
|
||||
`;
|
||||
|
||||
it('resolves the latest patch of the minor with latest-patch', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
inputs['token'] = 'faketoken';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.12.16')));
|
||||
|
||||
const expectedUrl =
|
||||
'https://github.com/actions/go-versions/releases/download/1.12.17-20200616.21/go-1.12.17-linux-x64.tar.gz';
|
||||
|
||||
// ... but not in the local cache
|
||||
findSpy.mockImplementation(() => '');
|
||||
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.12.17/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Using latest patch release satisfying ~1.12.16 (version file specifies 1.12.16)'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Setup go version spec ~1.12.16');
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'go-version-file-behavior: latest-patch implies check-latest'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Attempting to resolve the latest version from the manifest...'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Acquiring 1.12.17 from ${expectedUrl}`
|
||||
);
|
||||
});
|
||||
|
||||
it('warns and falls back when the manifest cannot be resolved', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
inputs['token'] = 'faketoken';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.12.16')));
|
||||
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
throw new Error('Unable to download manifest');
|
||||
});
|
||||
(httpmGetJsonSpy as jest.Mock<any>).mockRejectedValue(
|
||||
new Error('Unable to download manifest from raw URL')
|
||||
);
|
||||
|
||||
// ... and not in the local cache, so the dist fallback downloads
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.12.17/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::warning::go-version-file-behavior: latest-patch could not be honored: unable to resolve ~1.12.16 from the versions manifest. Falling back to the version spec, which may resolve to an older patch release from the runner's tool cache.${osm.EOL}`
|
||||
);
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('leaves a bare minor version unchanged with latest-patch', async () => {
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.14')));
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Using version 1.14 as written (latest-patch only widens exact major.minor.patch versions)'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.14');
|
||||
});
|
||||
|
||||
it('does not widen an explicit toolchain directive pin', async () => {
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() =>
|
||||
Buffer.from(`module example.com/mymodule
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.3
|
||||
`)
|
||||
);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Using toolchain directive version 1.22.3 as written (latest-patch does not widen an explicit toolchain pin)'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.22.3');
|
||||
});
|
||||
|
||||
it('fails when combined with a custom download base URL', async () => {
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
inputs['go-download-base-url'] = 'https://internal.example.com/go';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.22.0')));
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::go-version-file-behavior: 'latest-patch' is not supported with a custom download base URL because version ranges cannot be resolved against it. Use the default 'exact' behavior.${osm.EOL}`
|
||||
);
|
||||
});
|
||||
|
||||
it('uses the exact version by default', async () => {
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.12.16')));
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.12.16');
|
||||
});
|
||||
|
||||
it('does not apply to the go-version input', async () => {
|
||||
inputs['go-version'] = '1.12.16';
|
||||
inputs['go-version-file-behavior'] = 'latest-patch';
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.12.16');
|
||||
});
|
||||
|
||||
it('fails on an unsupported value', async () => {
|
||||
inputs['go-version-file'] = 'go.mod';
|
||||
inputs['go-version-file-behavior'] = 'newest';
|
||||
existsSpy.mockImplementation(() => true);
|
||||
readFileSpy.mockImplementation(() => Buffer.from(buildGoMod('1.12.16')));
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Invalid go-version-file-behavior: 'newest'. Supported values: 'exact', 'latest-patch'${osm.EOL}`
|
||||
);
|
||||
});
|
||||
|
||||
it('fails on an unsupported value even when go-version is used', async () => {
|
||||
inputs['go-version'] = '1.12.16';
|
||||
inputs['go-version-file-behavior'] = 'newest';
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Invalid go-version-file-behavior: 'newest'. Supported values: 'exact', 'latest-patch'${osm.EOL}`
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['1.22.0', '~1.22.0'],
|
||||
['v1.22.0', '~1.22.0'],
|
||||
['1.22', '1.22'],
|
||||
['1.21rc2', '1.21rc2'],
|
||||
['1.22.x', '1.22.x'],
|
||||
['>=1.22.0', '>=1.22.0']
|
||||
])('latestPatchSpec(%s) == %s', (version, expected) => {
|
||||
expect(im.latestPatchSpec(version)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('go-version-file-toolchain', () => {
|
||||
const goVersions = ['1.22.0', '1.21rc2', '1.18'];
|
||||
const placeholderVersion = '1.19';
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ inputs:
|
|||
description: 'The Go version to download (if necessary) and use. Supports semver spec and ranges. Be sure to enclose this option in single quotation marks.'
|
||||
go-version-file:
|
||||
description: 'Path to the go.mod, go.work, .go-version, or .tool-versions file.'
|
||||
go-version-file-behavior:
|
||||
description: 'How to interpret an exact version read from go-version-file. Use "latest-patch" to resolve the newest available patch release of the same minor version (e.g. "1.22.0" in go.mod resolves to the newest 1.22.x); this implies check-latest and is not supported with go-download-base-url. Defaults to "exact", which uses the version as written.'
|
||||
default: exact
|
||||
check-latest:
|
||||
description: 'Set this option to true if you want the action to always check for the latest available version that satisfies the version spec'
|
||||
default: false
|
||||
|
|
|
|||
76
dist/setup/index.js
vendored
76
dist/setup/index.js
vendored
|
|
@ -43462,7 +43462,7 @@ const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
|
|||
// For these URLs we skip the getInfoFromDist() call entirely and construct
|
||||
// the download URL directly, avoiding a guaranteed-404 HTTP request.
|
||||
const NO_VERSION_LISTING_BASE_URLS = ['https://aka.ms/golang/release/latest'];
|
||||
async function getGo(versionSpec, checkLatest, auth, arch = external_os_default().arch(), goDownloadBaseUrl) {
|
||||
async function getGo(versionSpec, checkLatest, auth, arch = external_os_default().arch(), goDownloadBaseUrl, latestPatchApplied = false) {
|
||||
let manifest;
|
||||
const osPlat = external_os_default().platform();
|
||||
const customBaseUrl = goDownloadBaseUrl?.replace(/\/+$/, '');
|
||||
|
|
@ -43493,6 +43493,11 @@ async function getGo(versionSpec, checkLatest, auth, arch = external_os_default(
|
|||
versionSpec = resolvedVersion;
|
||||
core_info(`Resolved as '${versionSpec}'`);
|
||||
}
|
||||
else if (latestPatchApplied) {
|
||||
// latest-patch depends on the manifest to see patches newer than
|
||||
// the runner's tool cache, so a silent info line is not enough here
|
||||
warning(`go-version-file-behavior: latest-patch could not be honored: unable to resolve ${versionSpec} from the versions manifest. Falling back to the version spec, which may resolve to an older patch release from the runner's tool cache.`);
|
||||
}
|
||||
else {
|
||||
core_info(`Failed to resolve version ${versionSpec} from manifest`);
|
||||
}
|
||||
|
|
@ -43865,18 +43870,37 @@ function parseGoVersionFile(versionFilePath) {
|
|||
// toolchain directive: https://go.dev/ref/mod#go-mod-file-toolchain
|
||||
const matchToolchain = contents.match(/^toolchain go(1\.\d+(?:\.\d+|rc\d+)?)/m);
|
||||
if (matchToolchain) {
|
||||
return matchToolchain[1];
|
||||
return { version: matchToolchain[1], fromToolchainDirective: true };
|
||||
}
|
||||
}
|
||||
// go directive: https://go.dev/ref/mod#go-mod-file-go
|
||||
const matchGo = contents.match(/^go (\d+(\.\d+)*)/m);
|
||||
return matchGo ? matchGo[1] : '';
|
||||
return {
|
||||
version: matchGo ? matchGo[1] : '',
|
||||
fromToolchainDirective: false
|
||||
};
|
||||
}
|
||||
else if (external_path_.basename(versionFilePath) === '.tool-versions') {
|
||||
const match = contents.match(/^golang\s+([^\n#]+)/m);
|
||||
return match ? match[1].trim() : '';
|
||||
return {
|
||||
version: match ? match[1].trim() : '',
|
||||
fromToolchainDirective: false
|
||||
};
|
||||
}
|
||||
return contents.trim();
|
||||
return { version: contents.trim(), fromToolchainDirective: false };
|
||||
}
|
||||
// Widen an exact version from a version file into a semver range matching
|
||||
// the newest patch release of the same minor (go-version-file-behavior:
|
||||
// latest-patch). Only exact major.minor.patch versions are widened,
|
||||
// optionally with a leading 'v' as found in some .go-version files: bare
|
||||
// minors like '1.22' already resolve to the newest patch, and prereleases
|
||||
// like '1.21rc2' have no patch series to float within.
|
||||
function latestPatchSpec(version) {
|
||||
const match = version.match(/^v?(\d+\.\d+\.\d+)$/);
|
||||
if (!match) {
|
||||
return version;
|
||||
}
|
||||
return `~${match[1]}`;
|
||||
}
|
||||
async function resolveStableVersionDist(versionSpec, arch) {
|
||||
const archFilter = getArch(arch);
|
||||
|
|
@ -100573,7 +100597,7 @@ async function run() {
|
|||
// versionSpec is optional. If supplied, install / use from the tool cache
|
||||
// If not supplied then problem matchers will still be setup. Useful for self-hosted.
|
||||
//
|
||||
const versionSpec = resolveVersionInput();
|
||||
const { version: versionSpec, latestPatchApplied } = resolveVersionInput();
|
||||
setGoToolchain();
|
||||
const cache = getBooleanInput('cache');
|
||||
core_info(`Setup go version spec ${versionSpec}`);
|
||||
|
|
@ -100584,14 +100608,20 @@ async function run() {
|
|||
if (versionSpec) {
|
||||
const token = getInput('token');
|
||||
const auth = !token ? undefined : `token ${token}`;
|
||||
const checkLatest = getBooleanInput('check-latest');
|
||||
let checkLatest = getBooleanInput('check-latest');
|
||||
if (latestPatchApplied && !checkLatest) {
|
||||
// the runner's tool cache may only hold a stale patch release; the
|
||||
// newest one has to come from the versions manifest
|
||||
core_info('go-version-file-behavior: latest-patch implies check-latest');
|
||||
checkLatest = true;
|
||||
}
|
||||
const goDownloadBaseUrl = getInput('go-download-base-url') ||
|
||||
process.env['GO_DOWNLOAD_BASE_URL'] ||
|
||||
undefined;
|
||||
if (goDownloadBaseUrl) {
|
||||
core_info(`Using custom Go download base URL: ${goDownloadBaseUrl}`);
|
||||
}
|
||||
const installDir = await getGo(versionSpec, checkLatest, auth, arch, goDownloadBaseUrl);
|
||||
const installDir = await getGo(versionSpec, checkLatest, auth, arch, goDownloadBaseUrl, latestPatchApplied);
|
||||
const installDirVersion = external_path_default().basename(external_path_default().dirname(installDir));
|
||||
addPath(external_path_default().join(installDir, 'bin'));
|
||||
core_info('Added go to the path');
|
||||
|
|
@ -100672,19 +100702,43 @@ function parseGoVersion(versionString) {
|
|||
function resolveVersionInput() {
|
||||
let version = getInput('go-version');
|
||||
const versionFilePath = getInput('go-version-file');
|
||||
const behavior = getInput('go-version-file-behavior') || 'exact';
|
||||
if (behavior !== 'exact' && behavior !== 'latest-patch') {
|
||||
throw new Error(`Invalid go-version-file-behavior: '${behavior}'. Supported values: 'exact', 'latest-patch'`);
|
||||
}
|
||||
if (version && versionFilePath) {
|
||||
warning('Both go-version and go-version-file inputs are specified, only go-version will be used');
|
||||
}
|
||||
if (version) {
|
||||
return version;
|
||||
return { version, latestPatchApplied: false };
|
||||
}
|
||||
if (versionFilePath) {
|
||||
if (!external_fs_default().existsSync(versionFilePath)) {
|
||||
throw new Error(`The specified go version file at: ${versionFilePath} does not exist`);
|
||||
}
|
||||
version = parseGoVersionFile(versionFilePath);
|
||||
const versionFile = parseGoVersionFile(versionFilePath);
|
||||
version = versionFile.version;
|
||||
if (behavior === 'latest-patch' && version) {
|
||||
if (versionFile.fromToolchainDirective) {
|
||||
core_info(`Using toolchain directive version ${version} as written (latest-patch does not widen an explicit toolchain pin)`);
|
||||
}
|
||||
else {
|
||||
const spec = latestPatchSpec(version);
|
||||
if (spec === version) {
|
||||
core_info(`Using version ${version} as written (latest-patch only widens exact major.minor.patch versions)`);
|
||||
}
|
||||
else {
|
||||
if (getInput('go-download-base-url') ||
|
||||
process.env['GO_DOWNLOAD_BASE_URL']) {
|
||||
throw new Error(`go-version-file-behavior: 'latest-patch' is not supported with a custom download base URL because version ranges cannot be resolved against it. Use the default 'exact' behavior.`);
|
||||
}
|
||||
core_info(`Using latest patch release satisfying ${spec} (version file specifies ${version})`);
|
||||
return { version: spec, latestPatchApplied: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return version;
|
||||
return { version, latestPatchApplied: false };
|
||||
}
|
||||
function setGoToolchain() {
|
||||
// docs: https://go.dev/doc/toolchain
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
- [Specifying a go version](advanced-usage.md#specifying-a-go-version)
|
||||
- [Matrix testing](advanced-usage.md#matrix-testing)
|
||||
- [Using the go-version-file input](advanced-usage.md#using-the-go-version-file-input)
|
||||
- [Using the latest patch release](advanced-usage.md#using-the-latest-patch-release)
|
||||
- [Check latest version](advanced-usage.md#check-latest-version)
|
||||
- [Caching](advanced-usage.md#caching)
|
||||
- [Caching in monorepos](advanced-usage.md#caching-in-monorepos)
|
||||
|
|
@ -212,6 +213,37 @@ steps:
|
|||
- run: go version
|
||||
```
|
||||
|
||||
### Using the latest patch release
|
||||
|
||||
By default, an exact version read from the version file is used as written: a `go 1.22.0` directive installs exactly Go 1.22.0, even if newer 1.22.x patch releases with security fixes are available.
|
||||
|
||||
Set `go-version-file-behavior` to `latest-patch` to instead resolve the newest available patch release of the same minor version that is at least the version in the file (e.g., `go 1.22.0` resolves to the newest 1.22.x):
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
go-version-file-behavior: 'latest-patch'
|
||||
- run: go version
|
||||
```
|
||||
|
||||
Because the newest patch release is often not yet present in the runner's tool cache, `latest-patch` implies `check-latest`: the newest matching patch is resolved from the versions manifest rather than from whatever the cache happens to hold.
|
||||
|
||||
Two operational effects to be aware of:
|
||||
|
||||
- The dependency cache key includes the installed Go version, so with `cache: true` each new Go patch release changes the key: the first run after a patch release rebuilds the module and build caches from scratch.
|
||||
- If the versions manifest cannot be reached (for example on GitHub Enterprise Server or other runners without github.com access), the action emits a warning and falls back to resolving the version range locally, which may install an older patch release from the runner's tool cache.
|
||||
|
||||
Some versions are always used as written and are not affected by `latest-patch`:
|
||||
|
||||
- Versions without a patch component (e.g., `go 1.22`), which already resolve to the latest available patch release.
|
||||
- Prerelease versions (e.g., `go1.22rc1`), which have no patch series to float within.
|
||||
- An exact version pinned by a go.mod or go.work `toolchain` directive (e.g., `toolchain go1.22.3`): the pin is deliberate and is never widened.
|
||||
|
||||
`latest-patch` is not supported together with `go-download-base-url`, which requires an exact version.
|
||||
|
||||
## Check latest version
|
||||
|
||||
The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ export async function getGo(
|
|||
checkLatest: boolean,
|
||||
auth: string | undefined,
|
||||
arch: Architecture = os.arch() as Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
goDownloadBaseUrl?: string,
|
||||
latestPatchApplied = false
|
||||
) {
|
||||
let manifest: tc.IToolRelease[] | undefined;
|
||||
const osPlat: string = os.platform();
|
||||
|
|
@ -111,6 +112,12 @@ export async function getGo(
|
|||
if (resolvedVersion) {
|
||||
versionSpec = resolvedVersion;
|
||||
core.info(`Resolved as '${versionSpec}'`);
|
||||
} else if (latestPatchApplied) {
|
||||
// latest-patch depends on the manifest to see patches newer than
|
||||
// the runner's tool cache, so a silent info line is not enough here
|
||||
core.warning(
|
||||
`go-version-file-behavior: latest-patch could not be honored: unable to resolve ${versionSpec} from the versions manifest. Falling back to the version spec, which may resolve to an older patch release from the runner's tool cache.`
|
||||
);
|
||||
} else {
|
||||
core.info(`Failed to resolve version ${versionSpec} from manifest`);
|
||||
}
|
||||
|
|
@ -642,7 +649,16 @@ export function makeSemver(version: string): string {
|
|||
return fullVersion;
|
||||
}
|
||||
|
||||
export function parseGoVersionFile(versionFilePath: string): string {
|
||||
export interface GoVersionFileResult {
|
||||
version: string;
|
||||
// true when the version came from a go.mod/go.work toolchain directive,
|
||||
// an explicit pin that must be respected as written
|
||||
fromToolchainDirective: boolean;
|
||||
}
|
||||
|
||||
export function parseGoVersionFile(
|
||||
versionFilePath: string
|
||||
): GoVersionFileResult {
|
||||
const contents = fs.readFileSync(versionFilePath).toString();
|
||||
|
||||
if (
|
||||
|
|
@ -657,19 +673,39 @@ export function parseGoVersionFile(versionFilePath: string): string {
|
|||
/^toolchain go(1\.\d+(?:\.\d+|rc\d+)?)/m
|
||||
);
|
||||
if (matchToolchain) {
|
||||
return matchToolchain[1];
|
||||
return {version: matchToolchain[1], fromToolchainDirective: true};
|
||||
}
|
||||
}
|
||||
|
||||
// go directive: https://go.dev/ref/mod#go-mod-file-go
|
||||
const matchGo = contents.match(/^go (\d+(\.\d+)*)/m);
|
||||
return matchGo ? matchGo[1] : '';
|
||||
return {
|
||||
version: matchGo ? matchGo[1] : '',
|
||||
fromToolchainDirective: false
|
||||
};
|
||||
} else if (path.basename(versionFilePath) === '.tool-versions') {
|
||||
const match = contents.match(/^golang\s+([^\n#]+)/m);
|
||||
return match ? match[1].trim() : '';
|
||||
return {
|
||||
version: match ? match[1].trim() : '',
|
||||
fromToolchainDirective: false
|
||||
};
|
||||
}
|
||||
|
||||
return contents.trim();
|
||||
return {version: contents.trim(), fromToolchainDirective: false};
|
||||
}
|
||||
|
||||
// Widen an exact version from a version file into a semver range matching
|
||||
// the newest patch release of the same minor (go-version-file-behavior:
|
||||
// latest-patch). Only exact major.minor.patch versions are widened,
|
||||
// optionally with a leading 'v' as found in some .go-version files: bare
|
||||
// minors like '1.22' already resolve to the newest patch, and prereleases
|
||||
// like '1.21rc2' have no patch series to float within.
|
||||
export function latestPatchSpec(version: string): string {
|
||||
const match = version.match(/^v?(\d+\.\d+\.\d+)$/);
|
||||
if (!match) {
|
||||
return version;
|
||||
}
|
||||
return `~${match[1]}`;
|
||||
}
|
||||
|
||||
async function resolveStableVersionDist(
|
||||
|
|
|
|||
67
src/main.ts
67
src/main.ts
|
|
@ -17,7 +17,7 @@ export async function run() {
|
|||
// versionSpec is optional. If supplied, install / use from the tool cache
|
||||
// If not supplied then problem matchers will still be setup. Useful for self-hosted.
|
||||
//
|
||||
const versionSpec = resolveVersionInput();
|
||||
const {version: versionSpec, latestPatchApplied} = resolveVersionInput();
|
||||
setGoToolchain();
|
||||
|
||||
const cache = core.getBooleanInput('cache');
|
||||
|
|
@ -33,7 +33,15 @@ export async function run() {
|
|||
const token = core.getInput('token');
|
||||
const auth = !token ? undefined : `token ${token}`;
|
||||
|
||||
const checkLatest = core.getBooleanInput('check-latest');
|
||||
let checkLatest = core.getBooleanInput('check-latest');
|
||||
if (latestPatchApplied && !checkLatest) {
|
||||
// the runner's tool cache may only hold a stale patch release; the
|
||||
// newest one has to come from the versions manifest
|
||||
core.info(
|
||||
'go-version-file-behavior: latest-patch implies check-latest'
|
||||
);
|
||||
checkLatest = true;
|
||||
}
|
||||
|
||||
const goDownloadBaseUrl =
|
||||
core.getInput('go-download-base-url') ||
|
||||
|
|
@ -49,7 +57,8 @@ export async function run() {
|
|||
checkLatest,
|
||||
auth,
|
||||
arch,
|
||||
goDownloadBaseUrl
|
||||
goDownloadBaseUrl,
|
||||
latestPatchApplied
|
||||
);
|
||||
|
||||
const installDirVersion = path.basename(path.dirname(installDir));
|
||||
|
|
@ -152,10 +161,25 @@ export function parseGoVersion(versionString: string): string {
|
|||
return versionString.split(' ')[2].slice('go'.length);
|
||||
}
|
||||
|
||||
function resolveVersionInput(): string {
|
||||
interface ResolvedVersionInput {
|
||||
version: string;
|
||||
// true when latest-patch widened the version into a range; the newest
|
||||
// matching patch must then be resolved from the versions manifest, not
|
||||
// just the runner's local tool cache
|
||||
latestPatchApplied: boolean;
|
||||
}
|
||||
|
||||
function resolveVersionInput(): ResolvedVersionInput {
|
||||
let version = core.getInput('go-version');
|
||||
const versionFilePath = core.getInput('go-version-file');
|
||||
|
||||
const behavior = core.getInput('go-version-file-behavior') || 'exact';
|
||||
if (behavior !== 'exact' && behavior !== 'latest-patch') {
|
||||
throw new Error(
|
||||
`Invalid go-version-file-behavior: '${behavior}'. Supported values: 'exact', 'latest-patch'`
|
||||
);
|
||||
}
|
||||
|
||||
if (version && versionFilePath) {
|
||||
core.warning(
|
||||
'Both go-version and go-version-file inputs are specified, only go-version will be used'
|
||||
|
|
@ -163,7 +187,7 @@ function resolveVersionInput(): string {
|
|||
}
|
||||
|
||||
if (version) {
|
||||
return version;
|
||||
return {version, latestPatchApplied: false};
|
||||
}
|
||||
|
||||
if (versionFilePath) {
|
||||
|
|
@ -172,10 +196,39 @@ function resolveVersionInput(): string {
|
|||
`The specified go version file at: ${versionFilePath} does not exist`
|
||||
);
|
||||
}
|
||||
version = installer.parseGoVersionFile(versionFilePath);
|
||||
const versionFile = installer.parseGoVersionFile(versionFilePath);
|
||||
version = versionFile.version;
|
||||
|
||||
if (behavior === 'latest-patch' && version) {
|
||||
if (versionFile.fromToolchainDirective) {
|
||||
core.info(
|
||||
`Using toolchain directive version ${version} as written (latest-patch does not widen an explicit toolchain pin)`
|
||||
);
|
||||
} else {
|
||||
const spec = installer.latestPatchSpec(version);
|
||||
if (spec === version) {
|
||||
core.info(
|
||||
`Using version ${version} as written (latest-patch only widens exact major.minor.patch versions)`
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
core.getInput('go-download-base-url') ||
|
||||
process.env['GO_DOWNLOAD_BASE_URL']
|
||||
) {
|
||||
throw new Error(
|
||||
`go-version-file-behavior: 'latest-patch' is not supported with a custom download base URL because version ranges cannot be resolved against it. Use the default 'exact' behavior.`
|
||||
);
|
||||
}
|
||||
core.info(
|
||||
`Using latest patch release satisfying ${spec} (version file specifies ${version})`
|
||||
);
|
||||
return {version: spec, latestPatchApplied: true};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
return {version, latestPatchApplied: false};
|
||||
}
|
||||
|
||||
function setGoToolchain() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue