mirror of
https://github.com/actions/setup-go.git
synced 2026-04-05 14:26:56 +00:00
feat: add go-download-base-url input for custom Go distributions
Add support for downloading Go from custom sources such as Microsoft Go
(aka.ms). Users can specify a custom download base URL via the
`go-download-base-url` input or the `GO_DOWNLOAD_BASE_URL` environment
variable (input takes precedence).
When a custom URL is provided, the action skips the GitHub-hosted
manifest and attempts to resolve versions from the custom URL's JSON
listing. If the listing is unavailable (as with aka.ms redirect links),
it falls back to constructing the download URL directly from the
version, platform, and architecture.
Usage:
- uses: actions/setup-go@v6
with:
go-version: '1.25'
go-download-base-url: 'https://aka.ms/golang/release/latest'
Changes:
- action.yml: add go-download-base-url optional input
- installer.ts: add getInfoFromDirectDownload() for URL construction
fallback, thread custom URL through getGo/getInfoFromDist/findMatch
- main.ts: read new input and GO_DOWNLOAD_BASE_URL env var
- setup-go.test.ts: add 12 unit tests for custom URL behavior
- microsoft-validation.yml: add E2E workflow testing Microsoft build of Go
across ubuntu/windows/macos with versions 1.24 and 1.25
- README.md: document new input with Microsoft build of Go examples
This commit is contained in:
parent
27fdb267c1
commit
78af96c188
9 changed files with 976 additions and 55471 deletions
146
.github/workflows/microsoft-validation.yml
vendored
Normal file
146
.github/workflows/microsoft-validation.yml
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
name: Validate Microsoft build of Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
env:
|
||||
MICROSOFT_GO_BASE_URL: 'https://aka.ms/golang/release/latest'
|
||||
|
||||
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: ${{ env.MICROSOFT_GO_BASE_URL }}
|
||||
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: ${{ env.MICROSOFT_GO_BASE_URL }}
|
||||
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: ${{ env.MICROSOFT_GO_BASE_URL }}
|
||||
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: 'x64'
|
||||
|
||||
# Custom base URL for Go downloads (e.g., for mirrors or Microsoft Go)
|
||||
go-download-base-url: ''
|
||||
```
|
||||
<!-- 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)
|
||||
- [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
|
||||
|
|
|
|||
|
|
@ -1105,4 +1105,296 @@ 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');
|
||||
expect(dlSpy).toHaveBeenCalled();
|
||||
expect(extractTarSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
});
|
||||
|
||||
it('falls back to direct download when version listing is unavailable', 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;
|
||||
|
||||
// Simulate JSON API not being available (like aka.ms)
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
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(
|
||||
'Version listing not available from custom 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(dlSpy).toHaveBeenCalled();
|
||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||
});
|
||||
|
||||
it('constructs correct direct download URL for windows', 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\\';
|
||||
|
||||
// Simulate JSON API not being available
|
||||
getSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Not a JSON endpoint');
|
||||
});
|
||||
|
||||
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(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');
|
||||
|
||||
delete process.env['GO_DOWNLOAD_BASE_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`
|
||||
);
|
||||
|
||||
delete process.env['GO_DOWNLOAD_BASE_URL'];
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ 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 (e.g., for Microsoft Go). 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.'
|
||||
|
|
|
|||
27786
dist/cache-save/index.js
vendored
27786
dist/cache-save/index.js
vendored
File diff suppressed because one or more lines are too long
27953
dist/setup/index.js
vendored
27953
dist/setup/index.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -12,6 +12,7 @@
|
|||
- [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
|
||||
|
|
@ -417,6 +418,40 @@ 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`) and aliases (`stable`, `oldstable`) are not supported with custom download URLs. Use specific versions (e.g., `1.25` or `1.25.0`) instead.
|
||||
|
||||
## Using `setup-go` on GHES
|
||||
|
||||
### Avoiding rate limit issues
|
||||
|
|
|
|||
217
src/installer.ts
217
src/installer.ts
|
|
@ -15,6 +15,8 @@ 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 DEFAULT_GO_VERSIONS_URL = 'https://golang.org/dl/?mode=json&include=all';
|
||||
|
||||
type InstallationType = 'dist' | 'manifest';
|
||||
|
||||
|
|
@ -44,15 +46,23 @@ export async function getGo(
|
|||
versionSpec: string,
|
||||
checkLatest: boolean,
|
||||
auth: string | undefined,
|
||||
arch: Architecture = os.arch() as Architecture
|
||||
arch: Architecture = os.arch() as Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
) {
|
||||
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,
|
||||
|
|
@ -76,24 +86,36 @@ export async function getGo(
|
|||
}
|
||||
|
||||
if (checkLatest) {
|
||||
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}'`);
|
||||
if (customBaseUrl) {
|
||||
core.info(
|
||||
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
|
||||
);
|
||||
} 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 ? 'go-custom' : 'go';
|
||||
|
||||
// check cache
|
||||
const toolPath = tc.find('go', versionSpec, arch);
|
||||
const toolPath = tc.find(toolCacheName, versionSpec, arch);
|
||||
// If not found in cache, download
|
||||
if (toolPath) {
|
||||
core.info(`Found in cache @ ${toolPath}`);
|
||||
|
|
@ -103,50 +125,85 @@ export async function getGo(
|
|||
let downloadPath = '';
|
||||
let info: IGoVersionInfo | null = null;
|
||||
|
||||
//
|
||||
// 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 {
|
||||
if (customBaseUrl) {
|
||||
//
|
||||
// Download from custom base URL
|
||||
//
|
||||
core.info(`Using custom download base URL: ${customBaseUrl}`);
|
||||
try {
|
||||
info = await getInfoFromDist(versionSpec, arch, customBaseUrl);
|
||||
} catch {
|
||||
core.info(
|
||||
'Not found in manifest. Falling back to download directly from Go'
|
||||
'Version listing not available from custom URL. Constructing download URL directly.'
|
||||
);
|
||||
}
|
||||
} 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}.`
|
||||
);
|
||||
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
|
||||
}
|
||||
|
||||
try {
|
||||
core.info('Install from dist');
|
||||
downloadPath = await installGoVersion(info, undefined, arch);
|
||||
core.info('Install from custom download URL');
|
||||
downloadPath = await installGoVersion(
|
||||
info,
|
||||
undefined,
|
||||
arch,
|
||||
toolCacheName
|
||||
);
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to download version ${versionSpec}: ${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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downloadPath;
|
||||
|
|
@ -229,20 +286,21 @@ async function cacheWindowsDir(
|
|||
async function addExecutablesToToolCache(
|
||||
extPath: string,
|
||||
info: IGoVersionInfo,
|
||||
arch: string
|
||||
arch: string,
|
||||
toolName: string = 'go'
|
||||
): Promise<string> {
|
||||
const tool = 'go';
|
||||
const version = makeSemver(info.resolvedVersion);
|
||||
return (
|
||||
(await cacheWindowsDir(extPath, tool, version, arch)) ||
|
||||
(await tc.cacheDir(extPath, tool, version, arch))
|
||||
(await cacheWindowsDir(extPath, toolName, version, arch)) ||
|
||||
(await tc.cacheDir(extPath, toolName, version, arch))
|
||||
);
|
||||
}
|
||||
|
||||
async function installGoVersion(
|
||||
info: IGoVersionInfo,
|
||||
auth: string | undefined,
|
||||
arch: string
|
||||
arch: string,
|
||||
toolName: string = 'go'
|
||||
): Promise<string> {
|
||||
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||
|
||||
|
|
@ -261,7 +319,12 @@ async function installGoVersion(
|
|||
}
|
||||
|
||||
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}`);
|
||||
|
||||
return toolCacheDir;
|
||||
|
|
@ -384,14 +447,20 @@ export async function getInfoFromManifest(
|
|||
|
||||
async function getInfoFromDist(
|
||||
versionSpec: string,
|
||||
arch: Architecture
|
||||
arch: Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
): Promise<IGoVersionInfo | null> {
|
||||
const version: IGoVersion | undefined = await findMatch(versionSpec, arch);
|
||||
const version: IGoVersion | undefined = await findMatch(
|
||||
versionSpec,
|
||||
arch,
|
||||
goDownloadBaseUrl
|
||||
);
|
||||
if (!version) {
|
||||
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>{
|
||||
type: 'dist',
|
||||
|
|
@ -401,9 +470,37 @@ async function getInfoFromDist(
|
|||
};
|
||||
}
|
||||
|
||||
export function getInfoFromDirectDownload(
|
||||
versionSpec: string,
|
||||
arch: Architecture,
|
||||
goDownloadBaseUrl: string
|
||||
): IGoVersionInfo {
|
||||
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
|
||||
arch: Architecture = os.arch() as Architecture,
|
||||
goDownloadBaseUrl?: string
|
||||
): Promise<IGoVersion | undefined> {
|
||||
const archFilter = sys.getArch(arch);
|
||||
const platFilter = sys.getPlatform();
|
||||
|
|
@ -411,6 +508,9 @@ export async function findMatch(
|
|||
let result: IGoVersion | undefined;
|
||||
let match: IGoVersion | undefined;
|
||||
|
||||
const dlUrl = goDownloadBaseUrl
|
||||
? `${goDownloadBaseUrl}/?mode=json&include=all`
|
||||
: DEFAULT_GO_VERSIONS_URL;
|
||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
||||
GOLANG_DOWNLOAD_URL
|
||||
);
|
||||
|
|
@ -528,6 +628,7 @@ async function resolveStableVersionDist(
|
|||
) {
|
||||
const archFilter = sys.getArch(arch);
|
||||
const platFilter = sys.getPlatform();
|
||||
const dlUrl = DEFAULT_GO_VERSIONS_URL;
|
||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
||||
GOLANG_DOWNLOAD_URL
|
||||
);
|
||||
|
|
|
|||
12
src/main.ts
12
src/main.ts
|
|
@ -34,11 +34,21 @@ 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
|
||||
arch,
|
||||
goDownloadBaseUrl
|
||||
);
|
||||
|
||||
const installDirVersion = path.basename(path.dirname(installDir));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue