mirror of
https://github.com/actions/setup-go.git
synced 2026-03-16 18:15:19 +00:00
Compare commits
1 Commits
main
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0955f25542 |
143
.github/workflows/microsoft-validation.yml
vendored
143
.github/workflows/microsoft-validation.yml
vendored
@@ -1,143 +0,0 @@
|
||||
name: Validate Microsoft build of Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
microsoft-basic:
|
||||
name: 'Microsoft build of Go ${{ matrix.go-version }} on ${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
go-version: ['1.25', '1.24']
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Microsoft build of Go ${{ matrix.go-version }}
|
||||
uses: ./
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
||||
cache: false
|
||||
|
||||
- name: Verify Go installation
|
||||
run: go version
|
||||
|
||||
- name: Verify Go env
|
||||
run: go env
|
||||
|
||||
- name: Verify Go is functional
|
||||
shell: bash
|
||||
run: |
|
||||
# Create a simple Go program and run it
|
||||
mkdir -p /tmp/test-go && cd /tmp/test-go
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
fmt.Println("Hello from Microsoft build of Go!")
|
||||
}
|
||||
EOF
|
||||
go run main.go
|
||||
|
||||
microsoft-env-var:
|
||||
name: 'Microsoft build of Go via env var on ${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
env:
|
||||
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Microsoft build of Go via environment variable
|
||||
uses: ./
|
||||
with:
|
||||
go-version: '1.25'
|
||||
cache: false
|
||||
|
||||
- name: Verify Go installation
|
||||
run: go version
|
||||
|
||||
- name: Verify Go is functional
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/test-go && cd /tmp/test-go
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
fmt.Println("Hello from Microsoft build of Go via env var!")
|
||||
}
|
||||
EOF
|
||||
go run main.go
|
||||
|
||||
microsoft-architecture:
|
||||
name: 'Microsoft build of Go arch ${{ matrix.architecture }} on ${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
architecture: [x64]
|
||||
include:
|
||||
- os: macos-latest
|
||||
architecture: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Microsoft build of Go with architecture
|
||||
uses: ./
|
||||
with:
|
||||
go-version: '1.25'
|
||||
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
||||
architecture: ${{ matrix.architecture }}
|
||||
cache: false
|
||||
|
||||
- name: Verify Go installation
|
||||
run: go version
|
||||
|
||||
microsoft-with-cache:
|
||||
name: 'Microsoft build of Go with caching on ${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Microsoft build of Go with caching
|
||||
uses: ./
|
||||
with:
|
||||
go-version: '1.25'
|
||||
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
||||
cache: true
|
||||
|
||||
- name: Verify Go installation
|
||||
run: go version
|
||||
|
||||
- name: Verify Go is functional
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/test-go && cd /tmp/test-go
|
||||
go mod init test
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
fmt.Println("Hello from cached Microsoft build of Go!")
|
||||
}
|
||||
EOF
|
||||
go run main.go
|
||||
@@ -54,9 +54,6 @@ See [action.yml](action.yml).
|
||||
|
||||
# Architecture to install (auto-detected if not specified)
|
||||
architecture: 'x64'
|
||||
|
||||
# Custom base URL for Go downloads (e.g., for mirrors)
|
||||
go-download-base-url: ''
|
||||
```
|
||||
<!-- end usage -->
|
||||
|
||||
@@ -133,7 +130,6 @@ For examples of using `cache-dependency-path`, see the [Caching](docs/advanced-u
|
||||
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
||||
- [Caching](docs/advanced-usage.md#caching)
|
||||
- [Outputs](docs/advanced-usage.md#outputs)
|
||||
- [Custom download URL](docs/advanced-usage.md#custom-download-url)
|
||||
- [Using `setup-go` on GHES](docs/advanced-usage.md#using-setup-go-on-ghes)
|
||||
|
||||
## License
|
||||
|
||||
@@ -45,7 +45,6 @@ describe('setup-go', () => {
|
||||
let mkdirSpy: jest.SpyInstance;
|
||||
let symlinkSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let execFileSpy: jest.SpyInstance;
|
||||
let getManifestSpy: jest.SpyInstance;
|
||||
let getAllVersionsSpy: jest.SpyInstance;
|
||||
let httpmGetJsonSpy: jest.SpyInstance;
|
||||
@@ -72,10 +71,6 @@ describe('setup-go', () => {
|
||||
archSpy = jest.spyOn(osm, 'arch');
|
||||
archSpy.mockImplementation(() => os['arch']);
|
||||
execSpy = jest.spyOn(cp, 'execSync');
|
||||
execFileSpy = jest.spyOn(cp, 'execFileSync');
|
||||
execFileSpy.mockImplementation(() => {
|
||||
throw new Error('ENOENT');
|
||||
});
|
||||
|
||||
// switch path join behaviour based on set os.platform
|
||||
joinSpy = jest.spyOn(path, 'join');
|
||||
@@ -134,9 +129,8 @@ describe('setup-go', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// clear out env vars set during 'run'
|
||||
// clear out env var set during 'run'
|
||||
delete process.env[im.GOTOOLCHAIN_ENV_VAR];
|
||||
delete process.env['GO_DOWNLOAD_BASE_URL'];
|
||||
|
||||
//jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
@@ -1111,456 +1105,4 @@ use .
|
||||
expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'});
|
||||
expect(process.env).toHaveProperty('GOTOOLCHAIN', 'local');
|
||||
});
|
||||
|
||||
describe('go-download-base-url', () => {
|
||||
it('downloads a version from custom base URL using version listing', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.13.1';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.13.1/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Using custom Go download base URL: ${customBaseUrl}`
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
|
||||
// Version listing should use custom base URL, not go.dev
|
||||
expect(getSpy).toHaveBeenCalledWith(
|
||||
`${customBaseUrl}/?mode=json&include=all`
|
||||
);
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(extractTarSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
});
|
||||
|
||||
it('skips version listing for known direct-download URL (aka.ms)', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.25.0';
|
||||
const customBaseUrl = 'https://aka.ms/golang/release/latest';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.25.0/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
const expPath = path.join(toolPath, 'bin');
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Skipping version listing for known direct-download URL. Constructing download URL directly.'
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Constructed direct download URL: ${customBaseUrl}/go1.25.0.linux-amd64.tar.gz`
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
|
||||
expect(getSpy).not.toHaveBeenCalled();
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
});
|
||||
|
||||
it('constructs correct direct download URL for windows (aka.ms)', async () => {
|
||||
os.platform = 'win32';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.25.0';
|
||||
const customBaseUrl = 'https://aka.ms/golang/release/latest';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
process.env['RUNNER_TEMP'] = 'C:\\temp\\';
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => 'C:\\temp\\some\\path');
|
||||
extractZipSpy.mockImplementation(() => 'C:\\temp\\some\\other\\path');
|
||||
const toolPath = path.normalize('C:\\cache\\go\\1.25.0\\x64');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(getSpy).not.toHaveBeenCalled();
|
||||
expect(dlSpy).toHaveBeenCalledWith(
|
||||
`${customBaseUrl}/go1.25.0.windows-amd64.zip`,
|
||||
'C:\\temp\\go1.25.0.windows-amd64.zip',
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it('skips manifest and downloads directly from custom URL', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.12.16';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
inputs['token'] = 'faketoken';
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.12.16/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
// Should not try to use the manifest at all
|
||||
expect(logSpy).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Not found in manifest')
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
|
||||
});
|
||||
|
||||
it('strips trailing slashes from custom base URL', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.13.1';
|
||||
const customBaseUrl = 'https://example.com/golang/';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.13.1/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Acquiring go1.13.1 from https://example.com/golang/go1.13.1.linux-amd64.tar.gz`
|
||||
);
|
||||
});
|
||||
|
||||
it('reads custom base URL from environment variable', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.13.1';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
process.env['GO_DOWNLOAD_BASE_URL'] = customBaseUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.13.1/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Using custom Go download base URL: ${customBaseUrl}`
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
|
||||
});
|
||||
|
||||
it('input takes precedence over environment variable', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.13.1';
|
||||
const inputUrl = 'https://input.example.com/golang';
|
||||
const envUrl = 'https://env.example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = inputUrl;
|
||||
process.env['GO_DOWNLOAD_BASE_URL'] = envUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.13.1/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Using custom Go download base URL: ${inputUrl}`
|
||||
);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Acquiring go1.13.1 from ${inputUrl}/go1.13.1.linux-amd64.tar.gz`
|
||||
);
|
||||
});
|
||||
|
||||
it('errors when stable alias is used with custom URL', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
inputs['go-version'] = 'stable';
|
||||
inputs['go-download-base-url'] = 'https://example.com/golang';
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
`::error::Version aliases 'stable' are not supported with a custom download base URL. Please specify an exact Go version.${osm.EOL}`
|
||||
);
|
||||
});
|
||||
|
||||
it('logs info when check-latest is used with custom URL', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.13.1';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
inputs['check-latest'] = true;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
const toolPath = path.normalize('/cache/go/1.13.1/x64');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
|
||||
);
|
||||
});
|
||||
|
||||
it('constructs direct download info correctly', () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const info = im.getInfoFromDirectDownload(
|
||||
'1.25.0',
|
||||
'x64',
|
||||
'https://aka.ms/golang/release/latest'
|
||||
);
|
||||
|
||||
expect(info.type).toBe('dist');
|
||||
expect(info.downloadUrl).toBe(
|
||||
'https://aka.ms/golang/release/latest/go1.25.0.linux-amd64.tar.gz'
|
||||
);
|
||||
expect(info.fileName).toBe('go1.25.0.linux-amd64.tar.gz');
|
||||
expect(info.resolvedVersion).toBe('1.25.0');
|
||||
});
|
||||
|
||||
it('constructs direct download info for windows', () => {
|
||||
os.platform = 'win32';
|
||||
os.arch = 'x64';
|
||||
|
||||
const info = im.getInfoFromDirectDownload(
|
||||
'1.25.0',
|
||||
'x64',
|
||||
'https://aka.ms/golang/release/latest'
|
||||
);
|
||||
|
||||
expect(info.type).toBe('dist');
|
||||
expect(info.downloadUrl).toBe(
|
||||
'https://aka.ms/golang/release/latest/go1.25.0.windows-amd64.zip'
|
||||
);
|
||||
expect(info.fileName).toBe('go1.25.0.windows-amd64.zip');
|
||||
});
|
||||
|
||||
it('constructs direct download info for arm64', () => {
|
||||
os.platform = 'darwin';
|
||||
os.arch = 'arm64';
|
||||
|
||||
const info = im.getInfoFromDirectDownload(
|
||||
'1.25.0',
|
||||
'arm64',
|
||||
'https://aka.ms/golang/release/latest'
|
||||
);
|
||||
|
||||
expect(info.type).toBe('dist');
|
||||
expect(info.downloadUrl).toBe(
|
||||
'https://aka.ms/golang/release/latest/go1.25.0.darwin-arm64.tar.gz'
|
||||
);
|
||||
expect(info.fileName).toBe('go1.25.0.darwin-arm64.tar.gz');
|
||||
});
|
||||
|
||||
it('caches under actual installed version when it differs from input spec', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.20';
|
||||
const customBaseUrl = 'https://aka.ms/golang/release/latest';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
|
||||
// Mock the installed Go binary reporting a different patch version
|
||||
execFileSpy.mockImplementation(() => 'go version go1.20.14 linux/amd64');
|
||||
|
||||
const expectedToolName = im.customToolCacheName(customBaseUrl);
|
||||
const toolPath = path.normalize(`/cache/${expectedToolName}/1.20.14/x64`);
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
"Requested version '1.20' resolved to installed version '1.20.14'"
|
||||
);
|
||||
// Cache key should use actual version, not the input spec
|
||||
expect(cacheSpy).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expectedToolName,
|
||||
'1.20.14',
|
||||
'x64'
|
||||
);
|
||||
});
|
||||
|
||||
it('shows clear error with platform/arch and URL on 404', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'arm64';
|
||||
|
||||
const versionSpec = '1.25.0';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
const httpError = new tc.HTTPError(404);
|
||||
dlSpy.mockImplementation(() => {
|
||||
throw httpError;
|
||||
});
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'The requested Go version 1.25.0 is not available for platform linux/arm64'
|
||||
)
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(expect.stringContaining('HTTP 404'));
|
||||
});
|
||||
|
||||
it('shows clear error with platform/arch and URL on download failure', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.25.0';
|
||||
const customBaseUrl = 'https://example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(() => {
|
||||
throw new Error('connection refused');
|
||||
});
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'Failed to download Go 1.25.0 for platform linux/x64'
|
||||
)
|
||||
);
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(customBaseUrl)
|
||||
);
|
||||
});
|
||||
|
||||
it.each(['^1.25.0', '~1.25', '>=1.25.0', '<1.26.0', '1.25.x', '1.x'])(
|
||||
'errors on version range "%s" when version listing is unavailable',
|
||||
async versionSpec => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = 'https://example.com/golang';
|
||||
|
||||
// Simulate version listing not available
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(cnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
`Version range '${versionSpec}' is not supported with a custom download base URL`
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('rejects version range in getInfoFromDirectDownload', () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
expect(() =>
|
||||
im.getInfoFromDirectDownload(
|
||||
'^1.25.0',
|
||||
'x64',
|
||||
'https://example.com/golang'
|
||||
)
|
||||
).toThrow(
|
||||
"Version range '^1.25.0' is not supported with a custom download base URL"
|
||||
);
|
||||
});
|
||||
|
||||
it('passes token as auth header for custom URL downloads', async () => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
|
||||
const versionSpec = '1.25.0';
|
||||
const customBaseUrl = 'https://private-mirror.example.com/golang';
|
||||
|
||||
inputs['go-version'] = versionSpec;
|
||||
inputs['go-download-base-url'] = customBaseUrl;
|
||||
inputs['token'] = 'ghp_testtoken123';
|
||||
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
findSpy.mockImplementation(() => '');
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
const expectedToolName = im.customToolCacheName(customBaseUrl);
|
||||
const toolPath = path.normalize(`/cache/${expectedToolName}/1.25.0/x64`);
|
||||
cacheSpy.mockImplementation(async () => toolPath);
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(dlSpy).toHaveBeenCalledWith(
|
||||
`${customBaseUrl}/go1.25.0.linux-amd64.tar.gz`,
|
||||
undefined,
|
||||
'token ghp_testtoken123'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,8 +19,6 @@ inputs:
|
||||
description: 'Used to specify the path to a dependency file (e.g., go.mod, go.sum)'
|
||||
architecture:
|
||||
description: 'Target architecture for Go to use. Examples: x86, x64. Will use system architecture by default.'
|
||||
go-download-base-url:
|
||||
description: 'Custom base URL for downloading Go distributions. Use this to download Go from a mirror or custom source. Defaults to "https://go.dev/dl". Can also be set via the GO_DOWNLOAD_BASE_URL environment variable. The input takes precedence over the environment variable.'
|
||||
outputs:
|
||||
go-version:
|
||||
description: 'The installed Go version. Useful when given a version range as input.'
|
||||
|
||||
148
dist/setup/index.js
vendored
148
dist/setup/index.js
vendored
@@ -77034,11 +77034,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.GOTOOLCHAIN_LOCAL_VAL = exports.GOTOOLCHAIN_ENV_VAR = void 0;
|
||||
exports.getGo = getGo;
|
||||
exports.customToolCacheName = customToolCacheName;
|
||||
exports.extractGoArchive = extractGoArchive;
|
||||
exports.getManifest = getManifest;
|
||||
exports.getInfoFromManifest = getInfoFromManifest;
|
||||
exports.getInfoFromDirectDownload = getInfoFromDirectDownload;
|
||||
exports.findMatch = findMatch;
|
||||
exports.getVersionsDist = getVersionsDist;
|
||||
exports.makeSemver = makeSemver;
|
||||
@@ -77050,8 +77048,6 @@ const path = __importStar(__nccwpck_require__(16928));
|
||||
const semver = __importStar(__nccwpck_require__(62088));
|
||||
const httpm = __importStar(__nccwpck_require__(54844));
|
||||
const sys = __importStar(__nccwpck_require__(57666));
|
||||
const crypto_1 = __importDefault(__nccwpck_require__(76982));
|
||||
const child_process_1 = __importDefault(__nccwpck_require__(35317));
|
||||
const fs_1 = __importDefault(__nccwpck_require__(79896));
|
||||
const os_1 = __importDefault(__nccwpck_require__(70857));
|
||||
const utils_1 = __nccwpck_require__(71798);
|
||||
@@ -77061,23 +77057,14 @@ const MANIFEST_REPO_OWNER = 'actions';
|
||||
const MANIFEST_REPO_NAME = 'go-versions';
|
||||
const MANIFEST_REPO_BRANCH = 'main';
|
||||
const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
||||
const DEFAULT_GO_DOWNLOAD_BASE_URL = 'https://go.dev/dl';
|
||||
const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
|
||||
// Base URLs known to not serve a version listing JSON endpoint.
|
||||
// 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'];
|
||||
function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||
return __awaiter(this, arguments, void 0, function* (versionSpec, checkLatest, auth, arch = os_1.default.arch(), goDownloadBaseUrl) {
|
||||
return __awaiter(this, arguments, void 0, function* (versionSpec, checkLatest, auth, arch = os_1.default.arch()) {
|
||||
var _a;
|
||||
let manifest;
|
||||
const osPlat = os_1.default.platform();
|
||||
const customBaseUrl = goDownloadBaseUrl === null || goDownloadBaseUrl === void 0 ? void 0 : goDownloadBaseUrl.replace(/\/+$/, '');
|
||||
if (versionSpec === utils_1.StableReleaseAlias.Stable ||
|
||||
versionSpec === utils_1.StableReleaseAlias.OldStable) {
|
||||
if (customBaseUrl) {
|
||||
throw new Error(`Version aliases '${versionSpec}' are not supported with a custom download base URL. Please specify an exact Go version.`);
|
||||
}
|
||||
manifest = yield getManifest(auth);
|
||||
let stableVersion = yield resolveStableVersionInput(versionSpec, arch, osPlat, manifest);
|
||||
if (!stableVersion) {
|
||||
@@ -77090,10 +77077,6 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||
versionSpec = stableVersion;
|
||||
}
|
||||
if (checkLatest) {
|
||||
if (customBaseUrl) {
|
||||
core.info('check-latest is not supported with a custom download base URL. Using the provided version spec directly.');
|
||||
}
|
||||
else {
|
||||
core.info('Attempting to resolve the latest version from the manifest...');
|
||||
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, true, auth, arch, manifest);
|
||||
if (resolvedVersion) {
|
||||
@@ -77104,14 +77087,8 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||
core.info(`Failed to resolve version ${versionSpec} from manifest`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use a distinct tool cache name for custom downloads to avoid
|
||||
// colliding with the runner's pre-installed Go
|
||||
const toolCacheName = customBaseUrl
|
||||
? customToolCacheName(customBaseUrl)
|
||||
: 'go';
|
||||
// check cache
|
||||
const toolPath = tc.find(toolCacheName, versionSpec, arch);
|
||||
const toolPath = tc.find('go', versionSpec, arch);
|
||||
// If not found in cache, download
|
||||
if (toolPath) {
|
||||
core.info(`Found in cache @ ${toolPath}`);
|
||||
@@ -77120,41 +77097,6 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||
core.info(`Attempting to download ${versionSpec}...`);
|
||||
let downloadPath = '';
|
||||
let info = null;
|
||||
if (customBaseUrl) {
|
||||
//
|
||||
// Download from custom base URL
|
||||
//
|
||||
const skipVersionListing = NO_VERSION_LISTING_BASE_URLS.some(url => customBaseUrl.toLowerCase() === url.toLowerCase());
|
||||
if (skipVersionListing) {
|
||||
core.info('Skipping version listing for known direct-download URL. Constructing download URL directly.');
|
||||
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
info = yield getInfoFromDist(versionSpec, arch, customBaseUrl);
|
||||
}
|
||||
catch (_b) {
|
||||
core.info('Version listing not available from custom URL. Constructing download URL directly.');
|
||||
}
|
||||
if (!info) {
|
||||
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||
}
|
||||
}
|
||||
try {
|
||||
core.info('Install from custom download URL');
|
||||
downloadPath = yield installGoVersion(info, auth, arch, toolCacheName);
|
||||
}
|
||||
catch (err) {
|
||||
const downloadUrl = (info === null || info === void 0 ? void 0 : info.downloadUrl) || customBaseUrl;
|
||||
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
|
||||
throw new Error(`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
|
||||
`Download URL returned HTTP 404: ${downloadUrl}`);
|
||||
}
|
||||
throw new Error(`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
|
||||
`from ${downloadUrl}: ${err}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Try download from internal distribution (popular versions only)
|
||||
//
|
||||
@@ -77194,7 +77136,6 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return downloadPath;
|
||||
});
|
||||
}
|
||||
@@ -77245,19 +77186,16 @@ function cacheWindowsDir(extPath, tool, version, arch) {
|
||||
return defaultToolCacheDir;
|
||||
});
|
||||
}
|
||||
function addExecutablesToToolCache(extPath_1, info_1, arch_1) {
|
||||
return __awaiter(this, arguments, void 0, function* (extPath, info, arch, toolName = 'go') {
|
||||
function addExecutablesToToolCache(extPath, info, arch) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const tool = 'go';
|
||||
const version = makeSemver(info.resolvedVersion);
|
||||
return ((yield cacheWindowsDir(extPath, toolName, version, arch)) ||
|
||||
(yield tc.cacheDir(extPath, toolName, version, arch)));
|
||||
return ((yield cacheWindowsDir(extPath, tool, version, arch)) ||
|
||||
(yield tc.cacheDir(extPath, tool, version, arch)));
|
||||
});
|
||||
}
|
||||
function customToolCacheName(baseUrl) {
|
||||
const hash = crypto_1.default.createHash('sha256').update(baseUrl).digest('hex');
|
||||
return `go-${hash.substring(0, 8)}`;
|
||||
}
|
||||
function installGoVersion(info_1, auth_1, arch_1) {
|
||||
return __awaiter(this, arguments, void 0, function* (info, auth, arch, toolName = 'go') {
|
||||
function installGoVersion(info, auth, arch) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||
// Windows requires that we keep the extension (.zip) for extraction
|
||||
const isWindows = os_1.default.platform() === 'win32';
|
||||
@@ -77270,33 +77208,12 @@ function installGoVersion(info_1, auth_1, arch_1) {
|
||||
if (info.type === 'dist') {
|
||||
extPath = path.join(extPath, 'go');
|
||||
}
|
||||
// For custom downloads, detect the actual installed version so the cache
|
||||
// key reflects the real patch level (e.g. input "1.20" may install 1.20.14).
|
||||
if (toolName !== 'go') {
|
||||
const actualVersion = detectInstalledGoVersion(extPath);
|
||||
if (actualVersion && actualVersion !== info.resolvedVersion) {
|
||||
core.info(`Requested version '${info.resolvedVersion}' resolved to installed version '${actualVersion}'`);
|
||||
info.resolvedVersion = actualVersion;
|
||||
}
|
||||
}
|
||||
core.info('Adding to the cache ...');
|
||||
const toolCacheDir = yield addExecutablesToToolCache(extPath, info, arch, toolName);
|
||||
const toolCacheDir = yield addExecutablesToToolCache(extPath, info, arch);
|
||||
core.info(`Successfully cached go to ${toolCacheDir}`);
|
||||
return toolCacheDir;
|
||||
});
|
||||
}
|
||||
function detectInstalledGoVersion(goDir) {
|
||||
try {
|
||||
const goBin = path.join(goDir, 'bin', os_1.default.platform() === 'win32' ? 'go.exe' : 'go');
|
||||
const output = child_process_1.default.execFileSync(goBin, ['version'], { encoding: 'utf8' });
|
||||
const match = output.match(/go version go(\S+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
catch (err) {
|
||||
core.debug(`Failed to detect installed Go version: ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function extractGoArchive(archivePath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const platform = os_1.default.platform();
|
||||
@@ -77381,17 +77298,13 @@ function getInfoFromManifest(versionSpec_1, stable_1, auth_1) {
|
||||
return info;
|
||||
});
|
||||
}
|
||||
function getInfoFromDist(versionSpec, arch, goDownloadBaseUrl) {
|
||||
function getInfoFromDist(versionSpec, arch) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const dlUrl = goDownloadBaseUrl
|
||||
? `${goDownloadBaseUrl}/?mode=json&include=all`
|
||||
: GOLANG_DOWNLOAD_URL;
|
||||
const version = yield findMatch(versionSpec, arch, dlUrl);
|
||||
const version = yield findMatch(versionSpec, arch);
|
||||
if (!version) {
|
||||
return null;
|
||||
}
|
||||
const baseUrl = goDownloadBaseUrl || DEFAULT_GO_DOWNLOAD_BASE_URL;
|
||||
const downloadUrl = `${baseUrl}/${version.files[0].filename}`;
|
||||
const downloadUrl = `https://go.dev/dl/${version.files[0].filename}`;
|
||||
return {
|
||||
type: 'dist',
|
||||
downloadUrl: downloadUrl,
|
||||
@@ -77400,36 +77313,13 @@ function getInfoFromDist(versionSpec, arch, goDownloadBaseUrl) {
|
||||
};
|
||||
});
|
||||
}
|
||||
function getInfoFromDirectDownload(versionSpec, arch, goDownloadBaseUrl) {
|
||||
// Reject version specs that can't map to an artifact filename
|
||||
if (/[~^>=<|*x]/.test(versionSpec)) {
|
||||
throw new Error(`Version range '${versionSpec}' is not supported with a custom download base URL ` +
|
||||
`when version listing is unavailable. Please specify an exact version (e.g., '1.25.0').`);
|
||||
}
|
||||
const archStr = sys.getArch(arch);
|
||||
const platStr = sys.getPlatform();
|
||||
const extension = platStr === 'windows' ? 'zip' : 'tar.gz';
|
||||
// Ensure version has the 'go' prefix for the filename
|
||||
const goVersion = versionSpec.startsWith('go')
|
||||
? versionSpec
|
||||
: `go${versionSpec}`;
|
||||
const fileName = `${goVersion}.${platStr}-${archStr}.${extension}`;
|
||||
const downloadUrl = `${goDownloadBaseUrl}/${fileName}`;
|
||||
core.info(`Constructed direct download URL: ${downloadUrl}`);
|
||||
return {
|
||||
type: 'dist',
|
||||
downloadUrl: downloadUrl,
|
||||
resolvedVersion: versionSpec.replace(/^go/, ''),
|
||||
fileName: fileName
|
||||
};
|
||||
}
|
||||
function findMatch(versionSpec_1) {
|
||||
return __awaiter(this, arguments, void 0, function* (versionSpec, arch = os_1.default.arch(), dlUrl = GOLANG_DOWNLOAD_URL) {
|
||||
return __awaiter(this, arguments, void 0, function* (versionSpec, arch = os_1.default.arch()) {
|
||||
const archFilter = sys.getArch(arch);
|
||||
const platFilter = sys.getPlatform();
|
||||
let result;
|
||||
let match;
|
||||
const candidates = yield module.exports.getVersionsDist(dlUrl);
|
||||
const candidates = yield module.exports.getVersionsDist(GOLANG_DOWNLOAD_URL);
|
||||
if (!candidates) {
|
||||
throw new Error(`golang download url did not return results`);
|
||||
}
|
||||
@@ -77639,13 +77529,7 @@ function run() {
|
||||
const token = core.getInput('token');
|
||||
const auth = !token ? undefined : `token ${token}`;
|
||||
const checkLatest = core.getBooleanInput('check-latest');
|
||||
const goDownloadBaseUrl = core.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 = yield installer.getGo(versionSpec, checkLatest, auth, arch, goDownloadBaseUrl);
|
||||
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');
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
- [Restore-only caches](advanced-usage.md#restore-only-caches)
|
||||
- [Parallel builds](advanced-usage.md#parallel-builds)
|
||||
- [Outputs](advanced-usage.md#outputs)
|
||||
- [Custom download URL](advanced-usage.md#custom-download-url)
|
||||
- [Using `setup-go` on GHES](advanced-usage.md#using-setup-go-on-ghes)
|
||||
|
||||
## Using the `go-version` input
|
||||
@@ -223,8 +222,6 @@ want the most up-to-date Go version to always be used. It supports major (e.g.,
|
||||
|
||||
> Setting `check-latest` to `true` has performance implications as downloading Go versions is slower than using cached
|
||||
> versions.
|
||||
>
|
||||
> `check-latest` is ignored when `go-download-base-url` is set. See [Custom download URL](#custom-download-url) for details.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
@@ -420,57 +417,6 @@ jobs:
|
||||
- run: echo "Was the Go cache restored? ${{ steps.go124.outputs.cache-hit }}" # true if cache-hit occurred
|
||||
```
|
||||
|
||||
## Custom download URL
|
||||
|
||||
The `go-download-base-url` input lets you download Go from a mirror or alternative source instead of the default `https://go.dev/dl`. This can also be set via the `GO_DOWNLOAD_BASE_URL` environment variable; the input takes precedence over the environment variable.
|
||||
|
||||
When a custom base URL is provided, the action skips the `actions/go-versions` manifest lookup and downloads directly from the specified URL.
|
||||
|
||||
**Using the [Microsoft build of Go](https://github.com/nicholasgasior/microsoft-go):**
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25'
|
||||
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
||||
- run: go version
|
||||
```
|
||||
|
||||
**Using an environment variable:**
|
||||
|
||||
```yaml
|
||||
env:
|
||||
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25'
|
||||
- run: go version
|
||||
```
|
||||
|
||||
> **Note:** Version range syntax (`^1.25`, `~1.24`, `>=1.25.0`) and aliases (`stable`, `oldstable`) are not supported with custom download URLs. Use exact versions such as `1.25`, `1.25.0`, or `1.25.0-1` (for sources that use revision numbers). If the custom server provides a version listing endpoint (`/?mode=json&include=all`), semver ranges will work; otherwise only exact versions are accepted.
|
||||
|
||||
> **Note:** The `check-latest` option is ignored when a custom download base URL is set. The action cannot query the custom server for the latest version, so it uses the version you specify directly. If you provide a partial version like `1.25`, the server determines which patch release to serve.
|
||||
|
||||
**Authenticated downloads:**
|
||||
|
||||
If your custom download source requires authentication, the `token` input is forwarded as an `Authorization` header. For example, to download from a private mirror:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25'
|
||||
go-download-base-url: 'https://private-mirror.example.com/golang'
|
||||
token: ${{ secrets.MIRROR_TOKEN }}
|
||||
- run: go version
|
||||
```
|
||||
|
||||
## Using `setup-go` on GHES
|
||||
|
||||
### Avoiding rate limit issues
|
||||
|
||||
74
package-lock.json
generated
74
package-lock.json
generated
@@ -13,7 +13,7 @@
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/glob": "^0.5.0",
|
||||
"@actions/http-client": "^2.2.1",
|
||||
"@actions/http-client": "^4.0.0",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@actions/tool-cache": "^2.0.2",
|
||||
"semver": "^7.7.3"
|
||||
@@ -101,15 +101,6 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/cache/node_modules/undici": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
|
||||
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
|
||||
@@ -120,6 +111,28 @@
|
||||
"@actions/http-client": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core/node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^5.25.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core/node_modules/undici": {
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/exec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
|
||||
@@ -140,13 +153,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
|
||||
"integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^5.25.4"
|
||||
"undici": "^6.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/io": {
|
||||
@@ -168,6 +181,16 @@
|
||||
"semver": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/tool-cache/node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^5.25.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/tool-cache/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@@ -177,6 +200,18 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/tool-cache/node_modules/undici": {
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
|
||||
@@ -6294,15 +6329,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
|
||||
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/glob": "^0.5.0",
|
||||
"@actions/http-client": "^2.2.1",
|
||||
"@actions/http-client": "^4.0.0",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@actions/tool-cache": "^2.0.2",
|
||||
"semver": "^7.7.3"
|
||||
|
||||
186
src/installer.ts
186
src/installer.ts
@@ -4,8 +4,6 @@ import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as sys from './system';
|
||||
import crypto from 'crypto';
|
||||
import cp from 'child_process';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import {StableReleaseAlias, isSelfHosted} from './utils';
|
||||
@@ -17,17 +15,11 @@ const MANIFEST_REPO_OWNER = 'actions';
|
||||
const MANIFEST_REPO_NAME = 'go-versions';
|
||||
const MANIFEST_REPO_BRANCH = 'main';
|
||||
const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
||||
const DEFAULT_GO_DOWNLOAD_BASE_URL = 'https://go.dev/dl';
|
||||
|
||||
type InstallationType = 'dist' | 'manifest';
|
||||
|
||||
const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
|
||||
|
||||
// Base URLs known to not serve a version listing JSON endpoint.
|
||||
// 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'];
|
||||
|
||||
export interface IGoVersionFile {
|
||||
filename: string;
|
||||
// darwin, linux, windows
|
||||
@@ -52,23 +44,15 @@ export async function getGo(
|
||||
versionSpec: string,
|
||||
checkLatest: boolean,
|
||||
auth: string | undefined,
|
||||
arch: Architecture = os.arch() as Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
arch: Architecture = os.arch() as Architecture
|
||||
) {
|
||||
let manifest: tc.IToolRelease[] | undefined;
|
||||
const osPlat: string = os.platform();
|
||||
const customBaseUrl = goDownloadBaseUrl?.replace(/\/+$/, '');
|
||||
|
||||
if (
|
||||
versionSpec === StableReleaseAlias.Stable ||
|
||||
versionSpec === StableReleaseAlias.OldStable
|
||||
) {
|
||||
if (customBaseUrl) {
|
||||
throw new Error(
|
||||
`Version aliases '${versionSpec}' are not supported with a custom download base URL. Please specify an exact Go version.`
|
||||
);
|
||||
}
|
||||
|
||||
manifest = await getManifest(auth);
|
||||
let stableVersion = await resolveStableVersionInput(
|
||||
versionSpec,
|
||||
@@ -92,14 +76,7 @@ export async function getGo(
|
||||
}
|
||||
|
||||
if (checkLatest) {
|
||||
if (customBaseUrl) {
|
||||
core.info(
|
||||
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
|
||||
);
|
||||
} else {
|
||||
core.info(
|
||||
'Attempting to resolve the latest version from the manifest...'
|
||||
);
|
||||
core.info('Attempting to resolve the latest version from the manifest...');
|
||||
const resolvedVersion = await resolveVersionFromManifest(
|
||||
versionSpec,
|
||||
true,
|
||||
@@ -114,16 +91,9 @@ export async function getGo(
|
||||
core.info(`Failed to resolve version ${versionSpec} from manifest`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use a distinct tool cache name for custom downloads to avoid
|
||||
// colliding with the runner's pre-installed Go
|
||||
const toolCacheName = customBaseUrl
|
||||
? customToolCacheName(customBaseUrl)
|
||||
: 'go';
|
||||
|
||||
// check cache
|
||||
const toolPath = tc.find(toolCacheName, versionSpec, arch);
|
||||
const toolPath = tc.find('go', versionSpec, arch);
|
||||
// If not found in cache, download
|
||||
if (toolPath) {
|
||||
core.info(`Found in cache @ ${toolPath}`);
|
||||
@@ -133,49 +103,6 @@ export async function getGo(
|
||||
let downloadPath = '';
|
||||
let info: IGoVersionInfo | null = null;
|
||||
|
||||
if (customBaseUrl) {
|
||||
//
|
||||
// Download from custom base URL
|
||||
//
|
||||
const skipVersionListing = NO_VERSION_LISTING_BASE_URLS.some(
|
||||
url => customBaseUrl.toLowerCase() === url.toLowerCase()
|
||||
);
|
||||
|
||||
if (skipVersionListing) {
|
||||
core.info(
|
||||
'Skipping version listing for known direct-download URL. Constructing download URL directly.'
|
||||
);
|
||||
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||
} else {
|
||||
try {
|
||||
info = await getInfoFromDist(versionSpec, arch, customBaseUrl);
|
||||
} catch {
|
||||
core.info(
|
||||
'Version listing not available from custom URL. Constructing download URL directly.'
|
||||
);
|
||||
}
|
||||
if (!info) {
|
||||
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
core.info('Install from custom download URL');
|
||||
downloadPath = await installGoVersion(info, auth, arch, toolCacheName);
|
||||
} catch (err) {
|
||||
const downloadUrl = info?.downloadUrl || customBaseUrl;
|
||||
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
|
||||
throw new Error(
|
||||
`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
|
||||
`Download URL returned HTTP 404: ${downloadUrl}`
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
|
||||
`from ${downloadUrl}: ${err}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Try download from internal distribution (popular versions only)
|
||||
//
|
||||
@@ -221,7 +148,6 @@ export async function getGo(
|
||||
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downloadPath;
|
||||
}
|
||||
@@ -303,26 +229,20 @@ async function cacheWindowsDir(
|
||||
async function addExecutablesToToolCache(
|
||||
extPath: string,
|
||||
info: IGoVersionInfo,
|
||||
arch: string,
|
||||
toolName: string = 'go'
|
||||
arch: string
|
||||
): Promise<string> {
|
||||
const tool = 'go';
|
||||
const version = makeSemver(info.resolvedVersion);
|
||||
return (
|
||||
(await cacheWindowsDir(extPath, toolName, version, arch)) ||
|
||||
(await tc.cacheDir(extPath, toolName, version, arch))
|
||||
(await cacheWindowsDir(extPath, tool, version, arch)) ||
|
||||
(await tc.cacheDir(extPath, tool, version, arch))
|
||||
);
|
||||
}
|
||||
|
||||
export function customToolCacheName(baseUrl: string): string {
|
||||
const hash = crypto.createHash('sha256').update(baseUrl).digest('hex');
|
||||
return `go-${hash.substring(0, 8)}`;
|
||||
}
|
||||
|
||||
async function installGoVersion(
|
||||
info: IGoVersionInfo,
|
||||
auth: string | undefined,
|
||||
arch: string,
|
||||
toolName: string = 'go'
|
||||
arch: string
|
||||
): Promise<string> {
|
||||
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||
|
||||
@@ -340,48 +260,13 @@ async function installGoVersion(
|
||||
extPath = path.join(extPath, 'go');
|
||||
}
|
||||
|
||||
// For custom downloads, detect the actual installed version so the cache
|
||||
// key reflects the real patch level (e.g. input "1.20" may install 1.20.14).
|
||||
if (toolName !== 'go') {
|
||||
const actualVersion = detectInstalledGoVersion(extPath);
|
||||
if (actualVersion && actualVersion !== info.resolvedVersion) {
|
||||
core.info(
|
||||
`Requested version '${info.resolvedVersion}' resolved to installed version '${actualVersion}'`
|
||||
);
|
||||
info.resolvedVersion = actualVersion;
|
||||
}
|
||||
}
|
||||
|
||||
core.info('Adding to the cache ...');
|
||||
const toolCacheDir = await addExecutablesToToolCache(
|
||||
extPath,
|
||||
info,
|
||||
arch,
|
||||
toolName
|
||||
);
|
||||
const toolCacheDir = await addExecutablesToToolCache(extPath, info, arch);
|
||||
core.info(`Successfully cached go to ${toolCacheDir}`);
|
||||
|
||||
return toolCacheDir;
|
||||
}
|
||||
|
||||
function detectInstalledGoVersion(goDir: string): string | null {
|
||||
try {
|
||||
const goBin = path.join(
|
||||
goDir,
|
||||
'bin',
|
||||
os.platform() === 'win32' ? 'go.exe' : 'go'
|
||||
);
|
||||
const output = cp.execFileSync(goBin, ['version'], {encoding: 'utf8'});
|
||||
const match = output.match(/go version go(\S+)/);
|
||||
return match ? match[1] : null;
|
||||
} catch (err) {
|
||||
core.debug(
|
||||
`Failed to detect installed Go version: ${(err as Error).message}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function extractGoArchive(archivePath: string): Promise<string> {
|
||||
const platform = os.platform();
|
||||
let extPath: string;
|
||||
@@ -499,23 +384,14 @@ export async function getInfoFromManifest(
|
||||
|
||||
async function getInfoFromDist(
|
||||
versionSpec: string,
|
||||
arch: Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
arch: Architecture
|
||||
): Promise<IGoVersionInfo | null> {
|
||||
const dlUrl = goDownloadBaseUrl
|
||||
? `${goDownloadBaseUrl}/?mode=json&include=all`
|
||||
: GOLANG_DOWNLOAD_URL;
|
||||
const version: IGoVersion | undefined = await findMatch(
|
||||
versionSpec,
|
||||
arch,
|
||||
dlUrl
|
||||
);
|
||||
const version: IGoVersion | undefined = await findMatch(versionSpec, arch);
|
||||
if (!version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const baseUrl = goDownloadBaseUrl || DEFAULT_GO_DOWNLOAD_BASE_URL;
|
||||
const downloadUrl = `${baseUrl}/${version.files[0].filename}`;
|
||||
const downloadUrl = `https://go.dev/dl/${version.files[0].filename}`;
|
||||
|
||||
return <IGoVersionInfo>{
|
||||
type: 'dist',
|
||||
@@ -525,43 +401,9 @@ async function getInfoFromDist(
|
||||
};
|
||||
}
|
||||
|
||||
export function getInfoFromDirectDownload(
|
||||
versionSpec: string,
|
||||
arch: Architecture,
|
||||
goDownloadBaseUrl: string
|
||||
): IGoVersionInfo {
|
||||
// Reject version specs that can't map to an artifact filename
|
||||
if (/[~^>=<|*x]/.test(versionSpec)) {
|
||||
throw new Error(
|
||||
`Version range '${versionSpec}' is not supported with a custom download base URL ` +
|
||||
`when version listing is unavailable. Please specify an exact version (e.g., '1.25.0').`
|
||||
);
|
||||
}
|
||||
|
||||
const archStr = sys.getArch(arch);
|
||||
const platStr = sys.getPlatform();
|
||||
const extension = platStr === 'windows' ? 'zip' : 'tar.gz';
|
||||
// Ensure version has the 'go' prefix for the filename
|
||||
const goVersion = versionSpec.startsWith('go')
|
||||
? versionSpec
|
||||
: `go${versionSpec}`;
|
||||
const fileName = `${goVersion}.${platStr}-${archStr}.${extension}`;
|
||||
const downloadUrl = `${goDownloadBaseUrl}/${fileName}`;
|
||||
|
||||
core.info(`Constructed direct download URL: ${downloadUrl}`);
|
||||
|
||||
return <IGoVersionInfo>{
|
||||
type: 'dist',
|
||||
downloadUrl: downloadUrl,
|
||||
resolvedVersion: versionSpec.replace(/^go/, ''),
|
||||
fileName: fileName
|
||||
};
|
||||
}
|
||||
|
||||
export async function findMatch(
|
||||
versionSpec: string,
|
||||
arch: Architecture = os.arch() as Architecture,
|
||||
dlUrl: string = GOLANG_DOWNLOAD_URL
|
||||
arch: Architecture = os.arch() as Architecture
|
||||
): Promise<IGoVersion | undefined> {
|
||||
const archFilter = sys.getArch(arch);
|
||||
const platFilter = sys.getPlatform();
|
||||
@@ -570,7 +412,7 @@ export async function findMatch(
|
||||
let match: IGoVersion | undefined;
|
||||
|
||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
||||
dlUrl
|
||||
GOLANG_DOWNLOAD_URL
|
||||
);
|
||||
if (!candidates) {
|
||||
throw new Error(`golang download url did not return results`);
|
||||
|
||||
12
src/main.ts
12
src/main.ts
@@ -34,21 +34,11 @@ export async function run() {
|
||||
|
||||
const checkLatest = core.getBooleanInput('check-latest');
|
||||
|
||||
const goDownloadBaseUrl =
|
||||
core.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 installer.getGo(
|
||||
versionSpec,
|
||||
checkLatest,
|
||||
auth,
|
||||
arch,
|
||||
goDownloadBaseUrl
|
||||
arch
|
||||
);
|
||||
|
||||
const installDirVersion = path.basename(path.dirname(installDir));
|
||||
|
||||
Reference in New Issue
Block a user