mirror of
https://github.com/actions/setup-go.git
synced 2026-04-02 01:44:21 +03:00
Compare commits
2 Commits
dependabot
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a3601121d | ||
|
|
8f19afcc70 |
143
.github/workflows/microsoft-validation.yml
vendored
Normal file
143
.github/workflows/microsoft-validation.yml
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
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,6 +54,9 @@ See [action.yml](action.yml).
|
|||||||
|
|
||||||
# Architecture to install (auto-detected if not specified)
|
# Architecture to install (auto-detected if not specified)
|
||||||
architecture: 'x64'
|
architecture: 'x64'
|
||||||
|
|
||||||
|
# Custom base URL for Go downloads (e.g., for mirrors)
|
||||||
|
go-download-base-url: ''
|
||||||
```
|
```
|
||||||
<!-- end usage -->
|
<!-- end usage -->
|
||||||
|
|
||||||
@@ -130,6 +133,7 @@ For examples of using `cache-dependency-path`, see the [Caching](docs/advanced-u
|
|||||||
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
||||||
- [Caching](docs/advanced-usage.md#caching)
|
- [Caching](docs/advanced-usage.md#caching)
|
||||||
- [Outputs](docs/advanced-usage.md#outputs)
|
- [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)
|
- [Using `setup-go` on GHES](docs/advanced-usage.md#using-setup-go-on-ghes)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ describe('setup-go', () => {
|
|||||||
let mkdirSpy: jest.SpyInstance;
|
let mkdirSpy: jest.SpyInstance;
|
||||||
let symlinkSpy: jest.SpyInstance;
|
let symlinkSpy: jest.SpyInstance;
|
||||||
let execSpy: jest.SpyInstance;
|
let execSpy: jest.SpyInstance;
|
||||||
|
let execFileSpy: jest.SpyInstance;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let getManifestSpy: jest.SpyInstance;
|
||||||
let getAllVersionsSpy: jest.SpyInstance;
|
let getAllVersionsSpy: jest.SpyInstance;
|
||||||
let httpmGetJsonSpy: jest.SpyInstance;
|
let httpmGetJsonSpy: jest.SpyInstance;
|
||||||
@@ -71,6 +72,10 @@ describe('setup-go', () => {
|
|||||||
archSpy = jest.spyOn(osm, 'arch');
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
archSpy.mockImplementation(() => os['arch']);
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
execSpy = jest.spyOn(cp, 'execSync');
|
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
|
// switch path join behaviour based on set os.platform
|
||||||
joinSpy = jest.spyOn(path, 'join');
|
joinSpy = jest.spyOn(path, 'join');
|
||||||
@@ -129,8 +134,9 @@ describe('setup-go', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// clear out env var set during 'run'
|
// clear out env vars set during 'run'
|
||||||
delete process.env[im.GOTOOLCHAIN_ENV_VAR];
|
delete process.env[im.GOTOOLCHAIN_ENV_VAR];
|
||||||
|
delete process.env['GO_DOWNLOAD_BASE_URL'];
|
||||||
|
|
||||||
//jest.resetAllMocks();
|
//jest.resetAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -1105,4 +1111,456 @@ use .
|
|||||||
expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'});
|
expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'});
|
||||||
expect(process.env).toHaveProperty('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,6 +19,8 @@ inputs:
|
|||||||
description: 'Used to specify the path to a dependency file (e.g., go.mod, go.sum)'
|
description: 'Used to specify the path to a dependency file (e.g., go.mod, go.sum)'
|
||||||
architecture:
|
architecture:
|
||||||
description: 'Target architecture for Go to use. Examples: x86, x64. Will use system architecture by default.'
|
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:
|
outputs:
|
||||||
go-version:
|
go-version:
|
||||||
description: 'The installed Go version. Useful when given a version range as input.'
|
description: 'The installed Go version. Useful when given a version range as input.'
|
||||||
|
|||||||
222
dist/setup/index.js
vendored
222
dist/setup/index.js
vendored
@@ -77034,9 +77034,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.GOTOOLCHAIN_LOCAL_VAL = exports.GOTOOLCHAIN_ENV_VAR = void 0;
|
exports.GOTOOLCHAIN_LOCAL_VAL = exports.GOTOOLCHAIN_ENV_VAR = void 0;
|
||||||
exports.getGo = getGo;
|
exports.getGo = getGo;
|
||||||
|
exports.customToolCacheName = customToolCacheName;
|
||||||
exports.extractGoArchive = extractGoArchive;
|
exports.extractGoArchive = extractGoArchive;
|
||||||
exports.getManifest = getManifest;
|
exports.getManifest = getManifest;
|
||||||
exports.getInfoFromManifest = getInfoFromManifest;
|
exports.getInfoFromManifest = getInfoFromManifest;
|
||||||
|
exports.getInfoFromDirectDownload = getInfoFromDirectDownload;
|
||||||
exports.findMatch = findMatch;
|
exports.findMatch = findMatch;
|
||||||
exports.getVersionsDist = getVersionsDist;
|
exports.getVersionsDist = getVersionsDist;
|
||||||
exports.makeSemver = makeSemver;
|
exports.makeSemver = makeSemver;
|
||||||
@@ -77048,6 +77050,8 @@ const path = __importStar(__nccwpck_require__(16928));
|
|||||||
const semver = __importStar(__nccwpck_require__(62088));
|
const semver = __importStar(__nccwpck_require__(62088));
|
||||||
const httpm = __importStar(__nccwpck_require__(54844));
|
const httpm = __importStar(__nccwpck_require__(54844));
|
||||||
const sys = __importStar(__nccwpck_require__(57666));
|
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 fs_1 = __importDefault(__nccwpck_require__(79896));
|
||||||
const os_1 = __importDefault(__nccwpck_require__(70857));
|
const os_1 = __importDefault(__nccwpck_require__(70857));
|
||||||
const utils_1 = __nccwpck_require__(71798);
|
const utils_1 = __nccwpck_require__(71798);
|
||||||
@@ -77057,14 +77061,23 @@ const MANIFEST_REPO_OWNER = 'actions';
|
|||||||
const MANIFEST_REPO_NAME = 'go-versions';
|
const MANIFEST_REPO_NAME = 'go-versions';
|
||||||
const MANIFEST_REPO_BRANCH = 'main';
|
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 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';
|
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) {
|
function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
||||||
return __awaiter(this, arguments, void 0, function* (versionSpec, checkLatest, auth, arch = os_1.default.arch()) {
|
return __awaiter(this, arguments, void 0, function* (versionSpec, checkLatest, auth, arch = os_1.default.arch(), goDownloadBaseUrl) {
|
||||||
var _a;
|
var _a;
|
||||||
let manifest;
|
let manifest;
|
||||||
const osPlat = os_1.default.platform();
|
const osPlat = os_1.default.platform();
|
||||||
|
const customBaseUrl = goDownloadBaseUrl === null || goDownloadBaseUrl === void 0 ? void 0 : goDownloadBaseUrl.replace(/\/+$/, '');
|
||||||
if (versionSpec === utils_1.StableReleaseAlias.Stable ||
|
if (versionSpec === utils_1.StableReleaseAlias.Stable ||
|
||||||
versionSpec === utils_1.StableReleaseAlias.OldStable) {
|
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);
|
manifest = yield getManifest(auth);
|
||||||
let stableVersion = yield resolveStableVersionInput(versionSpec, arch, osPlat, manifest);
|
let stableVersion = yield resolveStableVersionInput(versionSpec, arch, osPlat, manifest);
|
||||||
if (!stableVersion) {
|
if (!stableVersion) {
|
||||||
@@ -77077,18 +77090,28 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
|||||||
versionSpec = stableVersion;
|
versionSpec = stableVersion;
|
||||||
}
|
}
|
||||||
if (checkLatest) {
|
if (checkLatest) {
|
||||||
core.info('Attempting to resolve the latest version from the manifest...');
|
if (customBaseUrl) {
|
||||||
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, true, auth, arch, manifest);
|
core.info('check-latest is not supported with a custom download base URL. Using the provided version spec directly.');
|
||||||
if (resolvedVersion) {
|
|
||||||
versionSpec = resolvedVersion;
|
|
||||||
core.info(`Resolved as '${versionSpec}'`);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.info(`Failed to resolve version ${versionSpec} from manifest`);
|
core.info('Attempting to resolve the latest version from the manifest...');
|
||||||
|
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, true, auth, arch, manifest);
|
||||||
|
if (resolvedVersion) {
|
||||||
|
versionSpec = resolvedVersion;
|
||||||
|
core.info(`Resolved as '${versionSpec}'`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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
|
// check cache
|
||||||
const toolPath = tc.find('go', versionSpec, arch);
|
const toolPath = tc.find(toolCacheName, versionSpec, arch);
|
||||||
// If not found in cache, download
|
// If not found in cache, download
|
||||||
if (toolPath) {
|
if (toolPath) {
|
||||||
core.info(`Found in cache @ ${toolPath}`);
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
@@ -77097,43 +77120,79 @@ function getGo(versionSpec_1, checkLatest_1, auth_1) {
|
|||||||
core.info(`Attempting to download ${versionSpec}...`);
|
core.info(`Attempting to download ${versionSpec}...`);
|
||||||
let downloadPath = '';
|
let downloadPath = '';
|
||||||
let info = null;
|
let info = null;
|
||||||
//
|
if (customBaseUrl) {
|
||||||
// Try download from internal distribution (popular versions only)
|
//
|
||||||
//
|
// Download from custom base URL
|
||||||
try {
|
//
|
||||||
info = yield getInfoFromManifest(versionSpec, true, auth, arch, manifest);
|
const skipVersionListing = NO_VERSION_LISTING_BASE_URLS.some(url => customBaseUrl.toLowerCase() === url.toLowerCase());
|
||||||
if (info) {
|
if (skipVersionListing) {
|
||||||
downloadPath = yield installGoVersion(info, auth, arch);
|
core.info('Skipping version listing for known direct-download URL. Constructing download URL directly.');
|
||||||
|
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.info('Not found in manifest. Falling back to download directly from Go');
|
try {
|
||||||
}
|
info = yield getInfoFromDist(versionSpec, arch, customBaseUrl);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (_b) {
|
||||||
if (err instanceof tc.HTTPError &&
|
core.info('Version listing not available from custom URL. Constructing download URL directly.');
|
||||||
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
|
}
|
||||||
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
|
if (!info) {
|
||||||
}
|
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||||
else {
|
}
|
||||||
core.info(err.message);
|
|
||||||
}
|
|
||||||
core.debug((_a = err.stack) !== null && _a !== void 0 ? _a : '');
|
|
||||||
core.info('Falling back to download directly from Go');
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Download from storage.googleapis.com
|
|
||||||
//
|
|
||||||
if (!downloadPath) {
|
|
||||||
info = yield getInfoFromDist(versionSpec, arch);
|
|
||||||
if (!info) {
|
|
||||||
throw new Error(`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.`);
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
core.info('Install from dist');
|
core.info('Install from custom download URL');
|
||||||
downloadPath = yield installGoVersion(info, undefined, arch);
|
downloadPath = yield installGoVersion(info, auth, arch, toolCacheName);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
throw new Error(`Failed to download version ${versionSpec}: ${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)
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
info = yield getInfoFromManifest(versionSpec, true, auth, arch, manifest);
|
||||||
|
if (info) {
|
||||||
|
downloadPath = yield installGoVersion(info, auth, arch);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info('Not found in manifest. Falling back to download directly from Go');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err instanceof tc.HTTPError &&
|
||||||
|
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
|
||||||
|
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(err.message);
|
||||||
|
}
|
||||||
|
core.debug((_a = err.stack) !== null && _a !== void 0 ? _a : '');
|
||||||
|
core.info('Falling back to download directly from Go');
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Download from storage.googleapis.com
|
||||||
|
//
|
||||||
|
if (!downloadPath) {
|
||||||
|
info = yield getInfoFromDist(versionSpec, arch);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
core.info('Install from dist');
|
||||||
|
downloadPath = yield installGoVersion(info, undefined, arch);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return downloadPath;
|
return downloadPath;
|
||||||
@@ -77186,16 +77245,19 @@ function cacheWindowsDir(extPath, tool, version, arch) {
|
|||||||
return defaultToolCacheDir;
|
return defaultToolCacheDir;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function addExecutablesToToolCache(extPath, info, arch) {
|
function addExecutablesToToolCache(extPath_1, info_1, arch_1) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, arguments, void 0, function* (extPath, info, arch, toolName = 'go') {
|
||||||
const tool = 'go';
|
|
||||||
const version = makeSemver(info.resolvedVersion);
|
const version = makeSemver(info.resolvedVersion);
|
||||||
return ((yield cacheWindowsDir(extPath, tool, version, arch)) ||
|
return ((yield cacheWindowsDir(extPath, toolName, version, arch)) ||
|
||||||
(yield tc.cacheDir(extPath, tool, version, arch)));
|
(yield tc.cacheDir(extPath, toolName, version, arch)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function installGoVersion(info, auth, arch) {
|
function customToolCacheName(baseUrl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
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') {
|
||||||
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||||
// Windows requires that we keep the extension (.zip) for extraction
|
// Windows requires that we keep the extension (.zip) for extraction
|
||||||
const isWindows = os_1.default.platform() === 'win32';
|
const isWindows = os_1.default.platform() === 'win32';
|
||||||
@@ -77208,12 +77270,33 @@ function installGoVersion(info, auth, arch) {
|
|||||||
if (info.type === 'dist') {
|
if (info.type === 'dist') {
|
||||||
extPath = path.join(extPath, 'go');
|
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 ...');
|
core.info('Adding to the cache ...');
|
||||||
const toolCacheDir = yield addExecutablesToToolCache(extPath, info, arch);
|
const toolCacheDir = yield addExecutablesToToolCache(extPath, info, arch, toolName);
|
||||||
core.info(`Successfully cached go to ${toolCacheDir}`);
|
core.info(`Successfully cached go to ${toolCacheDir}`);
|
||||||
return 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) {
|
function extractGoArchive(archivePath) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const platform = os_1.default.platform();
|
const platform = os_1.default.platform();
|
||||||
@@ -77298,13 +77381,17 @@ function getInfoFromManifest(versionSpec_1, stable_1, auth_1) {
|
|||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getInfoFromDist(versionSpec, arch) {
|
function getInfoFromDist(versionSpec, arch, goDownloadBaseUrl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const version = yield findMatch(versionSpec, arch);
|
const dlUrl = goDownloadBaseUrl
|
||||||
|
? `${goDownloadBaseUrl}/?mode=json&include=all`
|
||||||
|
: GOLANG_DOWNLOAD_URL;
|
||||||
|
const version = yield findMatch(versionSpec, arch, dlUrl);
|
||||||
if (!version) {
|
if (!version) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const downloadUrl = `https://go.dev/dl/${version.files[0].filename}`;
|
const baseUrl = goDownloadBaseUrl || DEFAULT_GO_DOWNLOAD_BASE_URL;
|
||||||
|
const downloadUrl = `${baseUrl}/${version.files[0].filename}`;
|
||||||
return {
|
return {
|
||||||
type: 'dist',
|
type: 'dist',
|
||||||
downloadUrl: downloadUrl,
|
downloadUrl: downloadUrl,
|
||||||
@@ -77313,13 +77400,36 @@ function getInfoFromDist(versionSpec, arch) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
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) {
|
function findMatch(versionSpec_1) {
|
||||||
return __awaiter(this, arguments, void 0, function* (versionSpec, arch = os_1.default.arch()) {
|
return __awaiter(this, arguments, void 0, function* (versionSpec, arch = os_1.default.arch(), dlUrl = GOLANG_DOWNLOAD_URL) {
|
||||||
const archFilter = sys.getArch(arch);
|
const archFilter = sys.getArch(arch);
|
||||||
const platFilter = sys.getPlatform();
|
const platFilter = sys.getPlatform();
|
||||||
let result;
|
let result;
|
||||||
let match;
|
let match;
|
||||||
const candidates = yield module.exports.getVersionsDist(GOLANG_DOWNLOAD_URL);
|
const candidates = yield module.exports.getVersionsDist(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`);
|
||||||
}
|
}
|
||||||
@@ -77529,7 +77639,13 @@ function run() {
|
|||||||
const token = core.getInput('token');
|
const token = core.getInput('token');
|
||||||
const auth = !token ? undefined : `token ${token}`;
|
const auth = !token ? undefined : `token ${token}`;
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
const checkLatest = core.getBooleanInput('check-latest');
|
||||||
const installDir = yield installer.getGo(versionSpec, checkLatest, auth, arch);
|
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 installDirVersion = path_1.default.basename(path_1.default.dirname(installDir));
|
const installDirVersion = path_1.default.basename(path_1.default.dirname(installDir));
|
||||||
core.addPath(path_1.default.join(installDir, 'bin'));
|
core.addPath(path_1.default.join(installDir, 'bin'));
|
||||||
core.info('Added go to the path');
|
core.info('Added go to the path');
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
- [Restore-only caches](advanced-usage.md#restore-only-caches)
|
- [Restore-only caches](advanced-usage.md#restore-only-caches)
|
||||||
- [Parallel builds](advanced-usage.md#parallel-builds)
|
- [Parallel builds](advanced-usage.md#parallel-builds)
|
||||||
- [Outputs](advanced-usage.md#outputs)
|
- [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 `setup-go` on GHES](advanced-usage.md#using-setup-go-on-ghes)
|
||||||
|
|
||||||
## Using the `go-version` input
|
## Using the `go-version` input
|
||||||
@@ -222,6 +223,8 @@ 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
|
> Setting `check-latest` to `true` has performance implications as downloading Go versions is slower than using cached
|
||||||
> versions.
|
> versions.
|
||||||
|
>
|
||||||
|
> `check-latest` is ignored when `go-download-base-url` is set. See [Custom download URL](#custom-download-url) for details.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
@@ -417,6 +420,57 @@ jobs:
|
|||||||
- run: echo "Was the Go cache restored? ${{ steps.go124.outputs.cache-hit }}" # true if cache-hit occurred
|
- 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/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
|
## Using `setup-go` on GHES
|
||||||
|
|
||||||
### Avoiding rate limit issues
|
### Avoiding rate limit issues
|
||||||
|
|||||||
282
src/installer.ts
282
src/installer.ts
@@ -4,6 +4,8 @@ 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';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
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';
|
||||||
@@ -15,11 +17,17 @@ const MANIFEST_REPO_OWNER = 'actions';
|
|||||||
const MANIFEST_REPO_NAME = 'go-versions';
|
const MANIFEST_REPO_NAME = 'go-versions';
|
||||||
const MANIFEST_REPO_BRANCH = 'main';
|
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 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';
|
type InstallationType = 'dist' | 'manifest';
|
||||||
|
|
||||||
const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
|
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 {
|
export interface IGoVersionFile {
|
||||||
filename: string;
|
filename: string;
|
||||||
// darwin, linux, windows
|
// darwin, linux, windows
|
||||||
@@ -44,15 +52,23 @@ export async function getGo(
|
|||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
checkLatest: boolean,
|
checkLatest: boolean,
|
||||||
auth: string | undefined,
|
auth: string | undefined,
|
||||||
arch: Architecture = os.arch() as Architecture
|
arch: Architecture = os.arch() as Architecture,
|
||||||
|
goDownloadBaseUrl?: string
|
||||||
) {
|
) {
|
||||||
let manifest: tc.IToolRelease[] | undefined;
|
let manifest: tc.IToolRelease[] | undefined;
|
||||||
const osPlat: string = os.platform();
|
const osPlat: string = os.platform();
|
||||||
|
const customBaseUrl = goDownloadBaseUrl?.replace(/\/+$/, '');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
versionSpec === StableReleaseAlias.Stable ||
|
versionSpec === StableReleaseAlias.Stable ||
|
||||||
versionSpec === StableReleaseAlias.OldStable
|
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);
|
manifest = await getManifest(auth);
|
||||||
let stableVersion = await resolveStableVersionInput(
|
let stableVersion = await resolveStableVersionInput(
|
||||||
versionSpec,
|
versionSpec,
|
||||||
@@ -76,24 +92,38 @@ export async function getGo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (checkLatest) {
|
if (checkLatest) {
|
||||||
core.info('Attempting to resolve the latest version from the manifest...');
|
if (customBaseUrl) {
|
||||||
const resolvedVersion = await resolveVersionFromManifest(
|
core.info(
|
||||||
versionSpec,
|
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
|
||||||
true,
|
);
|
||||||
auth,
|
|
||||||
arch,
|
|
||||||
manifest
|
|
||||||
);
|
|
||||||
if (resolvedVersion) {
|
|
||||||
versionSpec = resolvedVersion;
|
|
||||||
core.info(`Resolved as '${versionSpec}'`);
|
|
||||||
} else {
|
} else {
|
||||||
core.info(`Failed to resolve version ${versionSpec} from manifest`);
|
core.info(
|
||||||
|
'Attempting to resolve the latest version from the manifest...'
|
||||||
|
);
|
||||||
|
const resolvedVersion = await resolveVersionFromManifest(
|
||||||
|
versionSpec,
|
||||||
|
true,
|
||||||
|
auth,
|
||||||
|
arch,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
if (resolvedVersion) {
|
||||||
|
versionSpec = resolvedVersion;
|
||||||
|
core.info(`Resolved as '${versionSpec}'`);
|
||||||
|
} else {
|
||||||
|
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
|
// check cache
|
||||||
const toolPath = tc.find('go', versionSpec, arch);
|
const toolPath = tc.find(toolCacheName, versionSpec, arch);
|
||||||
// If not found in cache, download
|
// If not found in cache, download
|
||||||
if (toolPath) {
|
if (toolPath) {
|
||||||
core.info(`Found in cache @ ${toolPath}`);
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
@@ -103,49 +133,93 @@ export async function getGo(
|
|||||||
let downloadPath = '';
|
let downloadPath = '';
|
||||||
let info: IGoVersionInfo | null = null;
|
let info: IGoVersionInfo | null = null;
|
||||||
|
|
||||||
//
|
if (customBaseUrl) {
|
||||||
// Try download from internal distribution (popular versions only)
|
//
|
||||||
//
|
// Download from custom base URL
|
||||||
try {
|
//
|
||||||
info = await getInfoFromManifest(versionSpec, true, auth, arch, manifest);
|
const skipVersionListing = NO_VERSION_LISTING_BASE_URLS.some(
|
||||||
if (info) {
|
url => customBaseUrl.toLowerCase() === url.toLowerCase()
|
||||||
downloadPath = await installGoVersion(info, auth, arch);
|
);
|
||||||
} else {
|
|
||||||
core.info(
|
|
||||||
'Not found in manifest. Falling back to download directly from Go'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (
|
|
||||||
err instanceof tc.HTTPError &&
|
|
||||||
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
|
|
||||||
) {
|
|
||||||
core.info(
|
|
||||||
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
core.info((err as Error).message);
|
|
||||||
}
|
|
||||||
core.debug((err as Error).stack ?? '');
|
|
||||||
core.info('Falling back to download directly from Go');
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
if (skipVersionListing) {
|
||||||
// Download from storage.googleapis.com
|
core.info(
|
||||||
//
|
'Skipping version listing for known direct-download URL. Constructing download URL directly.'
|
||||||
if (!downloadPath) {
|
|
||||||
info = await getInfoFromDist(versionSpec, arch);
|
|
||||||
if (!info) {
|
|
||||||
throw new Error(
|
|
||||||
`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.`
|
|
||||||
);
|
);
|
||||||
|
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 {
|
try {
|
||||||
core.info('Install from dist');
|
core.info('Install from custom download URL');
|
||||||
downloadPath = await installGoVersion(info, undefined, arch);
|
downloadPath = await installGoVersion(info, auth, arch, toolCacheName);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Failed to download version ${versionSpec}: ${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)
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
info = await getInfoFromManifest(versionSpec, true, auth, arch, manifest);
|
||||||
|
if (info) {
|
||||||
|
downloadPath = await installGoVersion(info, auth, arch);
|
||||||
|
} else {
|
||||||
|
core.info(
|
||||||
|
'Not found in manifest. Falling back to download directly from Go'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err instanceof tc.HTTPError &&
|
||||||
|
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
|
||||||
|
) {
|
||||||
|
core.info(
|
||||||
|
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
core.info((err as Error).message);
|
||||||
|
}
|
||||||
|
core.debug((err as Error).stack ?? '');
|
||||||
|
core.info('Falling back to download directly from Go');
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Download from storage.googleapis.com
|
||||||
|
//
|
||||||
|
if (!downloadPath) {
|
||||||
|
info = await getInfoFromDist(versionSpec, arch);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to find Go version '${versionSpec}' for platform ${osPlat} and architecture ${arch}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
core.info('Install from dist');
|
||||||
|
downloadPath = await installGoVersion(info, undefined, arch);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,20 +303,26 @@ async function cacheWindowsDir(
|
|||||||
async function addExecutablesToToolCache(
|
async function addExecutablesToToolCache(
|
||||||
extPath: string,
|
extPath: string,
|
||||||
info: IGoVersionInfo,
|
info: IGoVersionInfo,
|
||||||
arch: string
|
arch: string,
|
||||||
|
toolName: string = 'go'
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const tool = 'go';
|
|
||||||
const version = makeSemver(info.resolvedVersion);
|
const version = makeSemver(info.resolvedVersion);
|
||||||
return (
|
return (
|
||||||
(await cacheWindowsDir(extPath, tool, version, arch)) ||
|
(await cacheWindowsDir(extPath, toolName, version, arch)) ||
|
||||||
(await tc.cacheDir(extPath, tool, version, arch))
|
(await tc.cacheDir(extPath, toolName, 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(
|
async function installGoVersion(
|
||||||
info: IGoVersionInfo,
|
info: IGoVersionInfo,
|
||||||
auth: string | undefined,
|
auth: string | undefined,
|
||||||
arch: string
|
arch: string,
|
||||||
|
toolName: string = 'go'
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||||
|
|
||||||
@@ -260,13 +340,48 @@ async function installGoVersion(
|
|||||||
extPath = path.join(extPath, 'go');
|
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 ...');
|
core.info('Adding to the cache ...');
|
||||||
const toolCacheDir = await addExecutablesToToolCache(extPath, info, arch);
|
const toolCacheDir = await addExecutablesToToolCache(
|
||||||
|
extPath,
|
||||||
|
info,
|
||||||
|
arch,
|
||||||
|
toolName
|
||||||
|
);
|
||||||
core.info(`Successfully cached go to ${toolCacheDir}`);
|
core.info(`Successfully cached go to ${toolCacheDir}`);
|
||||||
|
|
||||||
return 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> {
|
export async function extractGoArchive(archivePath: string): Promise<string> {
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
let extPath: string;
|
let extPath: string;
|
||||||
@@ -384,14 +499,23 @@ export async function getInfoFromManifest(
|
|||||||
|
|
||||||
async function getInfoFromDist(
|
async function getInfoFromDist(
|
||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
arch: Architecture
|
arch: Architecture,
|
||||||
|
goDownloadBaseUrl?: string
|
||||||
): Promise<IGoVersionInfo | null> {
|
): Promise<IGoVersionInfo | null> {
|
||||||
const version: IGoVersion | undefined = await findMatch(versionSpec, arch);
|
const dlUrl = goDownloadBaseUrl
|
||||||
|
? `${goDownloadBaseUrl}/?mode=json&include=all`
|
||||||
|
: GOLANG_DOWNLOAD_URL;
|
||||||
|
const version: IGoVersion | undefined = await findMatch(
|
||||||
|
versionSpec,
|
||||||
|
arch,
|
||||||
|
dlUrl
|
||||||
|
);
|
||||||
if (!version) {
|
if (!version) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadUrl = `https://go.dev/dl/${version.files[0].filename}`;
|
const baseUrl = goDownloadBaseUrl || DEFAULT_GO_DOWNLOAD_BASE_URL;
|
||||||
|
const downloadUrl = `${baseUrl}/${version.files[0].filename}`;
|
||||||
|
|
||||||
return <IGoVersionInfo>{
|
return <IGoVersionInfo>{
|
||||||
type: 'dist',
|
type: 'dist',
|
||||||
@@ -401,9 +525,43 @@ 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(
|
export async function findMatch(
|
||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
arch: Architecture = os.arch() as Architecture
|
arch: Architecture = os.arch() as Architecture,
|
||||||
|
dlUrl: string = GOLANG_DOWNLOAD_URL
|
||||||
): Promise<IGoVersion | undefined> {
|
): Promise<IGoVersion | undefined> {
|
||||||
const archFilter = sys.getArch(arch);
|
const archFilter = sys.getArch(arch);
|
||||||
const platFilter = sys.getPlatform();
|
const platFilter = sys.getPlatform();
|
||||||
@@ -412,7 +570,7 @@ export async function findMatch(
|
|||||||
let match: IGoVersion | undefined;
|
let match: IGoVersion | undefined;
|
||||||
|
|
||||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
||||||
GOLANG_DOWNLOAD_URL
|
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`);
|
||||||
|
|||||||
12
src/main.ts
12
src/main.ts
@@ -34,11 +34,21 @@ export async function run() {
|
|||||||
|
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
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(
|
const installDir = await installer.getGo(
|
||||||
versionSpec,
|
versionSpec,
|
||||||
checkLatest,
|
checkLatest,
|
||||||
auth,
|
auth,
|
||||||
arch
|
arch,
|
||||||
|
goDownloadBaseUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
const installDirVersion = path.basename(path.dirname(installDir));
|
const installDirVersion = path.basename(path.dirname(installDir));
|
||||||
|
|||||||
Reference in New Issue
Block a user