Compare commits

...

3 Commits

Author SHA1 Message Date
Priya Gupta
def8c394e3 Rearrange README.md, add advanced-usage.md (#724)
* Rearrange README.md, add advanced-usage.md

* Fix YAML indentation in examples

* Fix YAML indentation in Advanced Usage workflow examples
2026-02-26 12:35:02 -06:00
Masahiro Furudate
4b73464bb3 Fix golang download url to go.dev (#469)
* Fix golang download url to go.dev

* Fix const name

* Run `npm run build`
2026-02-25 13:26:30 -06:00
Priya Gupta
a5f9b05d2d Update default Go module caching to use go.mod (#705)
* Update module cache to use go.mod as key

* Fix typo

* Revise breaking changes in README for V6

Updated breaking changes section with enhanced formatting and clarified toolchain management details.
2026-01-26 10:59:03 -06:00
10 changed files with 603 additions and 498 deletions

507
README.md
View File

@@ -5,421 +5,29 @@
This action sets up a Go environment for use in GitHub Actions by: This action sets up a Go environment for use in GitHub Actions by:
- Optionally downloading and caching a version of Go by version and adding to PATH - Optionally downloading and caching a version of Go by version and adding it to the PATH
- Optionally caching Go modules and build outputs
- Registering problem matchers for error output - Registering problem matchers for error output
- Providing intelligent caching for Go modules and build outputs
## Quick Start ## Breaking changes in V6
```yaml The V6 edition of the action includes:
steps: - **Upgraded Node.js runtime from node20 to node24**
- uses: actions/checkout@v6 > Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. See [Release Notes](https://github.com/actions/runner/releases/tag/v2.327.1)
- uses: actions/setup-go@v6
with:
go-version: '1.23'
- run: go version
```
## Breaking Changes - **Go toolchain**
- Supports both `go` and `toolchain` directives in `go.mod`. If the `toolchain` directive is present, its version is used; otherwise, the action falls back to the `go` directive.
### V6 Changes - **Cache key update**
- By default, the cache key for Go modules is based on `go.mod`. To use `go.sum`, configure the `cache-dependency-path` input.
#### Node Runtime Upgrade See full release notes on the [releases page](https://github.com/actions/setup-go/releases).
- **Upgraded from Node 20 to Node 24**
- ⚠️ **Action Required**: Ensure your runner is on version v2.327.1 or later for compatibility
- See [Release Notes](https://github.com/actions/runner/releases/tag/v2.327.1) for more details
#### Enhanced Go Toolchain Management
V6 introduces significant improvements for reliable and consistent Go version selection:
**Toolchain Directive Support**
Now correctly interprets both `go` and `toolchain` directives from `go.mod`:
```go
go 1.23.0 // Minimum required version
toolchain go1.23.2 // V6 uses this exact version
```
**Intelligent Caching**
Cache keys now incorporate the `toolchain` directive version from `go.mod`, eliminating cache conflicts when switching between different toolchain versions within the same Go minor release.
For more details, see the [full release notes](https://github.com/actions/setup-go/releases/tag/v6.0.0).
### V5 Changes
- **Upgraded Node.js runtime from node16 to node20**
- See [full release notes](https://github.com/actions/setup-go/releases) for complete details
## Version Resolution Behavior
The action follows this resolution order:
1. **Local cache** - Checks for a cached version match
2. **go-versions repository** - Pulls from the main branch of the [go-versions repository](https://github.com/actions/go-versions/blob/main/versions-manifest.json)
3. **Direct download** - Falls back to downloading directly from [go.dev](https://go.dev/dl)
To change the default behavior, use the `check-latest` input.
> **Note**: The setup-go action uses executable binaries built by the Golang team. The action does not build golang from source code.
## Usage ## Usage
### Basic Setup See [action.yml](action.yml).
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
- run: go run hello.go
```
### Version Specifications
#### Semantic Versioning
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '^1.23.1' # The Go version to download (if necessary) and use.
- run: go version
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '>=1.22.0'
- run: go version
```
> **Important**: Due to YAML parsing behavior, always wrap version numbers in single quotes:
> ```yaml
> go-version: '1.22' # Correct
> go-version: 1.22 # Incorrect - YAML parser interprets as 1.2
> ```
#### Pre-release Versions
```yaml
# RC version
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.24.0-rc.1' # The Go version to download (if necessary) and use
- run: go version
```
```yaml
# Beta version
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23.0-beta.1' # The Go version to download (if necessary) and use
- run: go version
```
#### Version Aliases
**Stable Release**
If `stable` is provided, action will get the latest stable version from the [go-versions](https://github.com/actions/go-versions/blob/main/versions-manifest.json) repository manifest.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'stable' # Latest stable version
- run: go version
```
**Previous Stable Release**
If `oldstable` is provided, when the current release is 1.23.x, the action will resolve version as 1.22.x, where x is the latest patch release.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'oldstable' # Previous stable version
- run: go version
```
> **Note**: Using aliases is equivalent to using the corresponding minor release with `check-latest: true`
### go-version-file
The action can automatically detect the Go version from various project files using the `go-version-file` input. This parameter supports `go.mod`, `go.work`, `.go-version`, and `.tool-versions` files.
> **Note**: If both `go-version` and `go-version-file` are provided, `go-version` takes precedence.
#### go.mod File
Automatically detect the Go version from your project's `go.mod` file:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
- run: go version
```
**Version Resolution from go.mod:**
1. Uses the `toolchain` directive version if present
2. Falls back to the `go` directive version
3. If no patch version is specified, uses the latest available patch
#### go.work File
Use the Go version specified in your `go.work` file:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: 'go.work'
- run: go version
```
#### .go-version File
Read the Go version from a `.go-version` file:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: '.go-version'
- run: go version
```
#### .tool-versions File
Use the Go version from an [`.tool-versions`](https://asdf-vm.com/manage/configuration.html#tool-versions) file:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: '.tool-versions'
- run: go version
```
#### Custom File Paths
The action searches for version files relative to the repository root by default. You can specify a custom path:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: 'path/to/.go-version'
- run: go version
```
**Supported Version Formats:**
- Major and minor only: `1.25` (action will use the latest patch version, e.g., `1.25.4`)
- Major, minor, and patch: `1.25.4` (exact version)
### Check Latest Version
The check-latest flag defaults to false for stability. This ensures your workflow uses a specific, predictable Go version.
When `check-latest: true`, the action verifies if your cached Go version is the latest available. If not, it downloads and uses the newest version.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
check-latest: true # Always check for the latest patch release
- run: go version
```
**Performance Considerations:**
- `check-latest: false` (default) - Uses cached versions for faster builds
- `check-latest: true` - Downloads the latest version, slower but ensures up-to-date releases
### Caching
The action features integrated caching for Go modules and build outputs. Built on [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache), it simplifies the caching process by requiring fewer configuration options. The cache input is optional, and caching is turned on by default.
#### Automatic Caching
Default behavior: Searches for `go.sum` in the repository root and uses its hash for the cache key.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
# cache: true (default)
- run: go run hello.go
```
#### Advanced Caching Scenarios
For advanced scenarios, use `cache-dependency-path` to specify:
- **Multiple dependency files**: When your project has dependencies in different directories
- **Custom locations**: When your `go.sum` files are not in the repository root
- **Monorepos**: When managing multiple Go modules in a single repository
- **Glob patterns**: For flexible file matching
```yaml
# Example: Monorepo with multiple go.sum files
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
check-latest: true
cache-dependency-path: |
subdir/go.sum
tools/go.sum
- run: go run hello.go
```
```yaml
# Example: Using glob patterns to match all go.sum files
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
cache-dependency-path: "**/*.sum"
- run: go run hello.go
```
#### Disable Caching
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.23'
cache: false
- run: go run hello.go
```
> **Note**: If caching fails, the action logs a warning but continues execution without interrupting your workflow.
**Restore-Only Cache**
```yaml
# In some workflows, you may want to restore a cache without saving it. This can help reduce cache writes and storage usage in workflows that only need to read from cache
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Setup Go
id: setup-go
uses: actions/setup-go@v6
with:
go-version: '1.24.10'
cache: false
# Capture Go cache locations
- name: Set Go cache variables (Linux/macOS)
if: runner.os != 'Windows'
run: |
echo "GO_MOD_CACHE=$(go env GOMODCACHE)" >> $GITHUB_ENV
echo "GO_BUILD_CACHE=$(go env GOCACHE)" >> $GITHUB_ENV
- name: Set Go cache variables (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
echo "GO_MOD_CACHE=$(go env GOMODCACHE)" | Out-File $env:GITHUB_ENV -Append
echo "GO_BUILD_CACHE=$(go env GOCACHE)" | Out-File $env:GITHUB_ENV -Append
# Normalize runner.arch to lowercase to ensure consistent cache keys
- name: Normalize runner architecture (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: echo "ARCH=$(echo '${{ runner.arch }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Normalize runner architecture (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$arch = "${{ runner.arch }}".ToLower()
echo "ARCH=$arch" | Out-File $env:GITHUB_ENV -Append
- name: Set cache OS suffix for Linux
if: runner.os == 'Linux'
shell: bash
run: echo "CACHE_OS_SUFFIX=$ImageOS-" >> $GITHUB_ENV
- name: Restore Go cache
id: go-cache
uses: actions/cache/restore@v5
with:
path: |
${{ env.GO_MOD_CACHE }}
${{ env.GO_BUILD_CACHE }}
key: setup-go-${{ runner.os }}-${{ env.ARCH }}-${{ env.CACHE_OS_SUFFIX }}go-${{ steps.setup-go.outputs.go-version }}-${{ hashFiles('**/go.sum') }}
- name: Download modules
run: go mod download
- name: Build
run: go build ./...
```
> If there are several builds on the same repo it might make sense to create a cache in one build and use it in the
others. The action [actions/cache/restore](https://github.com/actions/cache/tree/main/restore#only-restore-cache)
should be used in this case.
### Matrix Testing
Test across multiple Go versions:
```yaml
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.21', '1.22', '1.23']
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}
- run: go test ./...
```
## Advanced Configuration
### Supported Version Syntax
The `go-version` input supports the following syntax:
| Syntax Type | Example | Description |
|-------------|---------|-------------|
| Specific version | `1.23.2` | Installs this exact version |
| Semantic range (caret) | `^1.23.0` | Allows newer minor/patch versions (1.24.x, 1.25.x, etc.) |
| Semantic range (tilde) | `~1.23.0` | Allows newer patch versions only (1.23.1, 1.23.2, etc.) |
| Comparison (gte) | `>=1.22.0` | Any version equal to or newer than specified |
| Comparison (lt) | `<1.24.0` | Any version older than specified |
| Comparison range | `>=1.22.0 <1.24.0` | Any version within the specified range |
| Wildcard (patch) | `1.23.x` | Latest patch version of the specified minor release |
| Wildcard (minor) | `1.*` | Latest available version in the major version |
| Pre-release | `1.24.0-rc.1` | Beta/RC versions for testing upcoming releases |
| Aliases | `stable`, `oldstable` | Latest stable or previous stable release |
For more information about semantic versioning, see the [semver documentation](https://semver.org/).
### Complete Input Reference
<!-- start usage -->
```yaml ```yaml
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
@@ -427,15 +35,18 @@ For more information about semantic versioning, see the [semver documentation](h
go-version: '1.23' go-version: '1.23'
# Path to go.mod, go.work, .go-version, or .tool-versions file # Path to go.mod, go.work, .go-version, or .tool-versions file
# Note: if both go-version and go-version-file are provided, go-version takes precedence.
go-version-file: 'go.mod' go-version-file: 'go.mod'
# Check for latest version # Set this option if you want the action to check for the latest available version
# Default: false
check-latest: false check-latest: false
# GitHub token for authentication # GitHub token for authentication
token: ${{ github.token }} token: ${{ github.token }}
# Enable/disable caching # Used to specify whether caching is needed.
# Default: true
cache: true cache: true
# Path to dependency files for caching # Path to dependency files for caching
@@ -444,43 +55,91 @@ For more information about semantic versioning, see the [semver documentation](h
# Architecture to install (auto-detected if not specified) # Architecture to install (auto-detected if not specified)
architecture: 'x64' architecture: 'x64'
``` ```
<!-- end usage -->
## Using setup-go on GHES **Basic:**
setup-go comes pre-installed on GHES when Actions is enabled. For dynamic Go version downloads, the action fetches distributions from the [go-versions repository](https://github.com/actions/go-versions/) on github.com (external to your appliance).
These calls to `actions/go-versions` are made via unauthenticated requests, which are limited to 60 requests per hour per IP. If more requests are made within the time frame, then the action leverages the raw API to retrieve the version-manifest. This approach does not impose a rate limit and hence facilitates unrestricted consumption. This is particularly beneficial for GHES runners, which often share the same IP, to avoid the quick exhaustion of the unauthenticated rate limit. If that fails as well the action will try to download versions directly from [go.dev](https://go.dev/dl).
If that fails as well you can get a higher rate limit with generating a personal access token on github.com and passing it as the token input to the action:
```yaml ```yaml
uses: actions/setup-go@v6 steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: with:
token: ${{ secrets.GH_DOTCOM_TOKEN }} go-version: '1.25' # The Go version to download (if necessary) and use.
go-version: '1.23' - run: go run hello.go
``` ```
### Offline Runners **Version resolution behavior:**
For runners without github.com access, Go versions must be pre-cached in the runner's tool cache. See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)". The action resolves the requested version in the following order:
1. **Local cache** - Checks the local tool cache for a matching [semver](https://github.com/npm/node-semver#versions) version.
2. **go-versions repository** - If the requested version isnt available in the tool cache, it pulls the version manifest from the `main` branch of the [go-versions repository](https://github.com/actions/go-versions/blob/main/versions-manifest.json).
3. **Direct download** - If that lookup misses or fails, it will fall back to downloading directly from the [official Go distribution site](https://go.dev/dl).
## Recommended Permissions To change the default behavior, please use
the [check-latest input](docs/advanced-usage.md#check-latest-version).
When using the setup-go action in your GitHub Actions workflow, it is recommended to set the following permissions to ensure proper functionality: > **Note**: The `setup-go` action uses executable binaries built by the Go team and does not build Go binaries from source code.
## Supported version syntax
The `go-version` input supports the following syntax:
- Specific versions: `1.25`, `1.24.11`, `1.24.0-rc.1`, `1.23.0-beta.1`
- SemVer version range syntax: `^1.25.1`, `~1.24.1`, `>=1.25.0-rc.1`, `<1.25.0`, `>=1.22.0 <1.24.0`
- Aliases: `stable`, `oldstable`
- Wildcards: `1.25.x`, `1.x`
For details on Semantic Versioning, see [the semver package documentation](https://github.com/npm/node-semver).
> **Note**: Due to the peculiarities of YAML parsing, it is recommended to wrap the version in single quotation marks:
>
> ```yaml
> go-version: '1.20'
> ```
>
> The recommendation is based on the YAML parser's behavior, which interprets non-wrapped values as numbers and, in the case of version `1.20`, trims it down to `1.2`, which may not be very obvious.
For more usage examples, please refer to the section: [Using go-version input](docs/advanced-usage.md#using-the-go-version-input) of the [Advanced usage](docs/advanced-usage.md) guide.
## Recommended permissions
When using the `setup-go` action in your GitHub Actions workflow, it is recommended to set the following permissions to ensure proper functionality:
```yaml ```yaml
permissions: permissions:
contents: read # Required to checkout code and install dependencies contents: read # access to check out code and install dependencies
``` ```
## Caching dependency files and build outputs
The action includes built-in caching and restoration for Go modules and build outputs. It uses
[toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under the hood, but requires less configuration.
The `cache` input is optional, and caching is enabled by default. To disable caching, set `cache: false`.
By default, the action looks for `go.mod` in the repository root and uses its hash as part of the cache key. Use the `cache-dependency-path` input when you have multiple dependency files, or when theyre located in different subdirectories. This input supports glob patterns.
If caching cannot be performed for any reason, the action logs a warning and continues workflow execution.
For examples of using `cache-dependency-path`, see the [Caching](docs/advanced-usage.md#caching) section of the [Advanced usage](docs/advanced-usage.md) guide.
## Advanced usage
- [Using the go-version input](docs/advanced-usage.md#using-the-go-version-input)
- [Using the go-version-file input](docs/advanced-usage.md#using-the-go-version-file-input)
- [Check latest version](docs/advanced-usage.md#check-latest-version)
- [Caching](docs/advanced-usage.md#caching)
- [Outputs](docs/advanced-usage.md#outputs)
- [Using `setup-go` on GHES](docs/advanced-usage.md#using-setup-go-on-ghes)
## License ## License
The scripts and documentation in this project are released under the [MIT License](LICENSE). The scripts and documentation in this project are released under the [MIT License](LICENSE).
## Contributions ## Contributions
Contributions are welcome! See our [Contributor's Guide](docs/contributors.md) for details. Contributions are welcome! See our [Contributor's Guide](docs/contributors.md).
## Code of Conduct ## Code of Conduct
👋 Be nice. See our [Code of Conduct](CODE_OF_CONDUCT.md). :wave: Be nice. See [our code of conduct](CODE_OF_CONDUCT.md)

View File

@@ -1,93 +1,82 @@
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as glob from '@actions/glob'; import * as glob from '@actions/glob';
import fs from 'fs';
import * as cacheRestore from '../src/cache-restore'; import * as cacheRestore from '../src/cache-restore';
import * as cacheUtils from '../src/cache-utils'; import * as cacheUtils from '../src/cache-utils';
import {PackageManagerInfo} from '../src/package-managers'; import {PackageManagerInfo} from '../src/package-managers';
describe('restoreCache', () => { describe('restoreCache', () => {
//Arrange let hashFilesSpy: jest.SpyInstance;
const hashFilesSpy = jest.spyOn(glob, 'hashFiles'); let getCacheDirectoryPathSpy: jest.SpyInstance;
const getCacheDirectoryPathSpy = jest.spyOn( let restoreCacheSpy: jest.SpyInstance;
cacheUtils, let infoSpy: jest.SpyInstance;
'getCacheDirectoryPath' let setOutputSpy: jest.SpyInstance;
);
const restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
const infoSpy = jest.spyOn(core, 'info');
const setOutputSpy = jest.spyOn(core, 'setOutput');
const versionSpec = '1.13.1'; const versionSpec = '1.13.1';
const packageManager = 'default'; const packageManager = 'default';
const cacheDependencyPath = 'path'; const cacheDependencyPath = 'path';
let originalWorkspace: string | undefined;
beforeEach(() => { beforeEach(() => {
originalWorkspace = process.env.GITHUB_WORKSPACE;
process.env.GITHUB_WORKSPACE = '/test/workspace';
//Arrange
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
getCacheDirectoryPathSpy = jest.spyOn(cacheUtils, 'getCacheDirectoryPath');
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
infoSpy = jest.spyOn(core, 'info');
setOutputSpy = jest.spyOn(core, 'setOutput');
getCacheDirectoryPathSpy.mockImplementation( getCacheDirectoryPathSpy.mockImplementation(
(PackageManager: PackageManagerInfo) => { (PackageManager: PackageManagerInfo) => {
return new Promise<string[]>(resolve => { return Promise.resolve([
resolve(['cache_directory_path', 'cache_directory_path']); 'cache_directory_path',
}); 'cache_directory_path'
]);
} }
); );
}); });
it('should throw if dependency file path is not valid', async () => { afterEach(() => {
//Arrange process.env.GITHUB_WORKSPACE = originalWorkspace;
hashFilesSpy.mockImplementation((somePath: string) => { jest.restoreAllMocks();
return new Promise<string>(resolve => {
resolve('');
});
}); });
it('should throw if dependency file path is not valid', async () => {
// Arrange
hashFilesSpy.mockImplementation(() => Promise.resolve(''));
// Act + Assert // Act + Assert
await expect(async () => { await expect(
await cacheRestore.restoreCache( cacheRestore.restoreCache(
versionSpec, versionSpec,
packageManager, packageManager,
cacheDependencyPath cacheDependencyPath
); )
}).rejects.toThrow( ).rejects.toThrow(
'Some specified paths were not resolved, unable to cache dependencies.' 'Some specified paths were not resolved, unable to cache dependencies.'
); );
}); });
it('should inform if cache hit is not occured', async () => { it('should inform if cache hit is not occurred', async () => {
// Arrange // Arrange
hashFilesSpy.mockImplementation((somePath: string) => { hashFilesSpy.mockImplementation(() => Promise.resolve('file_hash'));
return new Promise<string>(resolve => { restoreCacheSpy.mockImplementation(() => Promise.resolve(''));
resolve('file_hash');
});
});
restoreCacheSpy.mockImplementation(() => {
return new Promise<string>(resolve => {
resolve('');
});
});
// Act + Assert // Act + Assert
await cacheRestore.restoreCache( await cacheRestore.restoreCache(
versionSpec, versionSpec,
packageManager, packageManager,
cacheDependencyPath cacheDependencyPath
); );
expect(infoSpy).toHaveBeenCalledWith(`Cache is not found`); expect(infoSpy).toHaveBeenCalledWith('Cache is not found');
}); });
it('should set output if cache hit is occured', async () => { it('should set output if cache hit is occurred', async () => {
// Arrange // Arrange
hashFilesSpy.mockImplementation((somePath: string) => { hashFilesSpy.mockImplementation(() => Promise.resolve('file_hash'));
return new Promise<string>(resolve => { restoreCacheSpy.mockImplementation(() => Promise.resolve('cache_key'));
resolve('file_hash');
});
});
restoreCacheSpy.mockImplementation(() => {
return new Promise<string>(resolve => {
resolve('cache_key');
});
});
// Act + Assert // Act + Assert
await cacheRestore.restoreCache( await cacheRestore.restoreCache(
versionSpec, versionSpec,
@@ -96,4 +85,18 @@ describe('restoreCache', () => {
); );
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true); expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
}); });
it('should throw if dependency file is not found in workspace', async () => {
jest.spyOn(fs, 'readdirSync').mockReturnValue(['main.go'] as any);
await expect(
cacheRestore.restoreCache(
versionSpec,
packageManager
// No cacheDependencyPath
)
).rejects.toThrow(
'Dependencies file is not found in /test/workspace. Supported file pattern: go.mod'
);
});
}); });

View File

@@ -47,7 +47,7 @@ describe('getPackageManagerInfo', () => {
//Arrange //Arrange
const packageManagerName = 'default'; const packageManagerName = 'default';
const expectedResult = { const expectedResult = {
dependencyFilePattern: 'go.sum', dependencyFilePattern: 'go.mod',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE'] cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
}; };
@@ -73,7 +73,7 @@ describe('getCacheDirectoryPath', () => {
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
const validPackageManager: PackageManagerInfo = { const validPackageManager: PackageManagerInfo = {
dependencyFilePattern: 'go.sum', dependencyFilePattern: 'go.mod',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE'] cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
}; };

View File

@@ -16,7 +16,7 @@ inputs:
description: Used to specify whether caching is needed. Set to true, if you'd like to enable caching. description: Used to specify whether caching is needed. Set to true, if you'd like to enable caching.
default: true default: true
cache-dependency-path: cache-dependency-path:
description: 'Used to specify the path to a dependency file - 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.'
outputs: outputs:

View File

@@ -44277,7 +44277,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.supportedPackageManagers = void 0; exports.supportedPackageManagers = void 0;
exports.supportedPackageManagers = { exports.supportedPackageManagers = {
default: { default: {
dependencyFilePattern: 'go.sum', dependencyFilePattern: 'go.mod',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE'] cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
} }
}; };

13
dist/setup/index.js vendored
View File

@@ -49364,8 +49364,8 @@ const findDependencyFile = (packageManager) => {
const dependencyFile = packageManager.dependencyFilePattern; const dependencyFile = packageManager.dependencyFilePattern;
const workspace = process.env.GITHUB_WORKSPACE; const workspace = process.env.GITHUB_WORKSPACE;
const rootContent = fs_1.default.readdirSync(workspace); const rootContent = fs_1.default.readdirSync(workspace);
const goSumFileExists = rootContent.includes(dependencyFile); const goModFileExists = rootContent.includes(dependencyFile);
if (!goSumFileExists) { if (!goModFileExists) {
throw new Error(`Dependencies file is not found in ${workspace}. Supported file pattern: ${dependencyFile}`); throw new Error(`Dependencies file is not found in ${workspace}. Supported file pattern: ${dependencyFile}`);
} }
return path_1.default.join(workspace, dependencyFile); return path_1.default.join(workspace, dependencyFile);
@@ -49585,6 +49585,7 @@ 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 GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
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()) {
var _a; var _a;
@@ -49846,8 +49847,7 @@ function findMatch(versionSpec_1) {
const platFilter = sys.getPlatform(); const platFilter = sys.getPlatform();
let result; let result;
let match; let match;
const dlUrl = 'https://golang.org/dl/?mode=json&include=all'; 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`);
} }
@@ -49937,8 +49937,7 @@ function resolveStableVersionDist(versionSpec, arch) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const archFilter = sys.getArch(arch); const archFilter = sys.getArch(arch);
const platFilter = sys.getPlatform(); const platFilter = sys.getPlatform();
const dlUrl = 'https://golang.org/dl/?mode=json&include=all'; 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`);
} }
@@ -50182,7 +50181,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.supportedPackageManagers = void 0; exports.supportedPackageManagers = void 0;
exports.supportedPackageManagers = { exports.supportedPackageManagers = {
default: { default: {
dependencyFilePattern: 'go.sum', dependencyFilePattern: 'go.mod',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE'] cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
} }
}; };

444
docs/advanced-usage.md Normal file
View File

@@ -0,0 +1,444 @@
# Advanced Usage
- [Using the go-version input](advanced-usage.md#using-the-go-version-input)
- [Specifying a go version](advanced-usage.md#specifying-a-go-version)
- [Matrix testing](advanced-usage.md#matrix-testing)
- [Using the go-version-file input](advanced-usage.md#using-the-go-version-file-input)
- [Check latest version](advanced-usage.md#check-latest-version)
- [Caching](advanced-usage.md#caching)
- [Caching in monorepos](advanced-usage.md#caching-in-monorepos)
- [Caching in multi-module repositories](advanced-usage.md#caching-in-multi-module-repositories)
- [Multi-target builds](advanced-usage.md#multi-target-builds)
- [Cache invalidation on source changes](advanced-usage.md#cache-invalidation-on-source-changes)
- [Restore-only caches](advanced-usage.md#restore-only-caches)
- [Parallel builds](advanced-usage.md#parallel-builds)
- [Outputs](advanced-usage.md#outputs)
- [Using `setup-go` on GHES](advanced-usage.md#using-setup-go-on-ghes)
## Using the `go-version` input
### Specifying a Go version
For repeatable builds, specify the **exact major, minor, and patch version** (such as `1.25.5`):
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.5'
- run: go run hello.go
```
- The only downside is that setup may take a little longer. If the exact version is not already installed on the runner due to more recent versions, the exact version will have to be downloaded.
You can specify **only a major and minor version** if you are okay with the most recent patch version being used:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
- run: go run hello.go
```
- There will be a single patch version already installed on each runner for every minor version of Go that is supported.
- The preinstalled patch version is generally the latest available. When a new patch is released, it replaces the previously preinstalled version on the runner.
- Using the most recent patch version speeds up setup because the required Go version is already installed on the runner and no download is needed.
To try a **pre-release**:
Download and use beta or RC Go versions as needed.
```yaml
# RC version
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25.0-rc.2'
- run: go version
```
```yaml
# Beta version
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.19.0-beta.1'
- run: go version
```
Using **stable/oldstable aliases**:
If `stable` is provided, the action will get the latest stable version from the [`go-versions`](https://github.com/actions/go-versions/blob/main/versions-manifest.json) repository manifest.
If `oldstable` is provided, the action resolves it to the latest patch release of the previous stable Go minor version (for example, if the latest stable is `1.25.x`, `oldstable` resolves to `1.24.x`, where `x` is the latest patch release).
**Note:** Using these aliases will result in the same version as when using the corresponding minor release with the `check-latest` input set to `true`.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- run: go run hello.go
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'oldstable'
- run: go run hello.go
```
You can also use **SemVer's version range syntax**, for instance:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '^1.25.1'
- run: go version
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '>=1.24.0-rc.1'
- run: go version
```
### Matrix testing
Using `setup-go` it's possible to use the [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of Go:
```yaml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.24', '1.25' ]
name: Go ${{ matrix.go }} sample
steps:
- uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- run: go run hello.go
```
Exclude a specific Go version:
```yaml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: [ '1.22', '1.24', '1.25']
exclude:
- os: macos-latest
go: '1.22'
- os: windows-latest
go: '1.25'
steps:
- uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- run: go run hello.go
```
## Using the `go-version-file` input
`setup-go` action can read the Go version from a version file. `go-version-file` input is used for specifying the path to the version file. If the file supplied to the `go-version-file` input doesn't exist, the action will fail with an error. This input supports go.mod, go.work, .go-version, and .tool-versions files.
If both the `go-version` and the `go-version-file` inputs are provided then the `go-version` input is used. The `.tool-versions` file supports version specifications in accordance with asdf standards, adhering to Semantic Versioning ([semver](https://semver.org)).
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: 'path/to/go.mod' # Read Go version from go.mod
- run: go version
```
- Uses the `toolchain` directive if present, otherwise the action falls back to the `go` directive.
- The `go` directive in `go.mod` can specify a patch version or omit it altogether (e.g., `go 1.25.0` or `go 1.25`). If a patch version is specified, that specific patch version will be used. If no patch version is specified, it will search for the latest available patch version in the cache,
[versions-manifest.json](https://github.com/actions/go-versions/blob/main/versions-manifest.json), and the
[official Go language website](https://go.dev/dl/?mode=json&include=all), in that order.
> The action will search for the `go.mod` file relative to the repository root.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: '.go-version' # Read Go version from .go-version
- run: go version
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: '.tool-versions' # Read Go version from .tool-versions
- run: go version
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: 'go.work' # Read Go version from go.work
- run: go version
```
## Check latest version
The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability
and if you want to ensure a specific Go version is always used.
If `check-latest` is set to `true`, the action first checks if the cached version is the latest one. If the locally
cached version is not the most up-to-date, a Go version will then be downloaded. Set `check-latest` to `true` if you
want the most up-to-date Go version to always be used. It supports major (e.g., "1") and major.minor (e.g., "1.25") version selectors, always resolving to the latest matching patch release.
> Setting `check-latest` to `true` has performance implications as downloading Go versions is slower than using cached
> versions.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
check-latest: true
- run: go run hello.go
```
## Caching
### Caching in monorepos
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
cache-dependency-path: subdir/go.sum
- run: go run hello.go
```
### Caching in multi-module repositories
`cache-dependency-path` input accepts glob patterns and multi-line values:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
cache-dependency-path: '**/go.sum'
- run: go run hello.go
```
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
cache-dependency-path: |
subdir/go.sum
tools/go.sum
- run: go run hello.go
```
### Multi-target builds
`cache-dependency-path` isnt limited to dependency files (like `go.sum`). It can also include files that capture build settings (for example, `GOOS`/`GOARCH`). This allows separate caches per target platform (OS/architecture) and helps avoid reusing caches across incompatible builds.
```yaml
env:
GOOS: ...
GOARCH: ...
steps:
- run: echo "$GOOS $GOARCH" > env.txt
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
cache-dependency-path: |
go.sum
env.txt
- run: go run hello.go
```
### Cache invalidation on source changes
Besides dependencies, the action can also cache build outputs (the [`GOCACHE`](https://pkg.go.dev/cmd/go#hdr-Build_and_test_caching) directory). By default, this cache is not updated based on source changes to help avoid unpredictable and frequent cache invalidation. To invalidate the cache when source files change, include source files in the `cache-dependency-path` input.
> **Note:** Including patterns like `**/*.go` can create new caches on many commits, increasing cache storage and potentially slowing workflows due to more frequent uploads/downloads.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
cache-dependency-path: |
go.sum
**/*.go
- run: go run hello.go
```
### Restore-only caches
Restore caches without saving new entries. This can help reduce cache writes and storage usage in workflows that only need to read from the cache:
```yaml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Setup go
id: setup-go
uses: actions/setup-go@v6
with:
go-version: '1.25.5'
cache: false
# Capture Go cache locations
- name: Set Go cache variables (Linux/macOS)
if: runner.os != 'Windows'
run: |
echo "GO_MOD_CACHE=$(go env GOMODCACHE)" >> $GITHUB_ENV
echo "GO_BUILD_CACHE=$(go env GOCACHE)" >> $GITHUB_ENV
- name: Set Go cache variables (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
echo "GO_MOD_CACHE=$(go env GOMODCACHE)" | Out-File $env:GITHUB_ENV -Append
echo "GO_BUILD_CACHE=$(go env GOCACHE)" | Out-File $env:GITHUB_ENV -Append
# Normalize runner.arch to lowercase to ensure consistent cache keys
- name: Normalize runner architecture (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: echo "ARCH=$(echo '${{ runner.arch }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Normalize runner architecture (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$arch = "${{ runner.arch }}".ToLower()
echo "ARCH=$arch" | Out-File $env:GITHUB_ENV -Append
- name: Set cache OS suffix for Linux
if: runner.os == 'Linux'
shell: bash
run: echo "CACHE_OS_SUFFIX=$ImageOS-" >> $GITHUB_ENV
- name: Restore Go cache
id: go-cache
uses: actions/cache/restore@v5
with:
path: |
${{ env.GO_MOD_CACHE }}
${{ env.GO_BUILD_CACHE }}
key: setup-go-${{ runner.os }}-${{ env.ARCH }}-${{ env.CACHE_OS_SUFFIX }}go-${{ steps.setup-go.outputs.go-version }}-${{ hashFiles('**/go.mod') }}
- name: Download modules
run: go mod download
- name: Build
run: go build ./...
```
> If there are several builds on the same repo, it may make sense to create a cache in one build and use it in others. The action [actions/cache/restore](https://github.com/actions/cache/tree/main/restore#only-restore-cache)
should be used in this case.
### Parallel builds
To avoid race conditions during parallel builds, either use distinct cache keys with [actions/cache](https://github.com/actions/cache/blob/main/examples.md#go---modules), or create the cache in only one build and [restore](#restore-only-caches) it in the other builds.
## Outputs
### `go-version`
Using **go-version** output, it's possible to get the precise Go version installed by the action. This output is useful when the input `go-version` is given as a range, but down the line you need to operate (such as in an `if:` statement) with the exact installed version (e.g. 1.24.11).
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
id: go124
with:
go-version: '^1.24'
- run: echo "Installed Go version: ${{ steps.go124.outputs.go-version }}"
```
### `cache-hit`
**cache-hit** output is available with a boolean value that indicates whether a cache hit occurred on the primary key:
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
id: go124
with:
go-version: '1.24'
cache: true
- run: echo "Was the Go cache restored? ${{ steps.go124.outputs.cache-hit }}" # true if cache-hit occurred
```
## Using `setup-go` on GHES
### Avoiding rate limit issues
`setup-go` comes pre-installed on the appliance with GHES if Actions is enabled.
When dynamically downloading Go distributions, `setup-go` downloads distributions from [`actions/go-versions`](https://github.com/actions/go-versions) on github.com (outside of the appliance).
These calls to `actions/go-versions` are made via unauthenticated requests, which are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting).
If more requests are made within the time frame, then the action leverages the `raw API` to retrieve the version-manifest. This approach does not impose a rate limit and hence facilitates unrestricted consumption. This is particularly beneficial for GHES runners, which often share the same IP, to avoid the quick exhaustion of the unauthenticated rate limit.
If that fails as well the action will try to download versions directly from [go.dev](https://go.dev/dl).
If that fails as well you can get a higher rate limit with [generating a personal access token on github.com](https://github.com/settings/tokens/new) and passing it as the `token` input to the action:
```yaml
uses: actions/setup-go@v6
with:
token: ${{ secrets.GH_DOTCOM_TOKEN }}
go-version: '1.25'
```
### No access to github.com
If the runner is not able to access github.com, any Go versions requested during a workflow run must come from the runner's tool cache.
See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)"
for more information.

View File

@@ -55,8 +55,8 @@ const findDependencyFile = (packageManager: PackageManagerInfo) => {
const workspace = process.env.GITHUB_WORKSPACE!; const workspace = process.env.GITHUB_WORKSPACE!;
const rootContent = fs.readdirSync(workspace); const rootContent = fs.readdirSync(workspace);
const goSumFileExists = rootContent.includes(dependencyFile); const goModFileExists = rootContent.includes(dependencyFile);
if (!goSumFileExists) { if (!goModFileExists) {
throw new Error( throw new Error(
`Dependencies file is not found in ${workspace}. Supported file pattern: ${dependencyFile}` `Dependencies file is not found in ${workspace}. Supported file pattern: ${dependencyFile}`
); );

View File

@@ -18,6 +18,8 @@ const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/$
type InstallationType = 'dist' | 'manifest'; type InstallationType = 'dist' | 'manifest';
const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
export interface IGoVersionFile { export interface IGoVersionFile {
filename: string; filename: string;
// darwin, linux, windows // darwin, linux, windows
@@ -409,9 +411,8 @@ export async function findMatch(
let result: IGoVersion | undefined; let result: IGoVersion | undefined;
let match: IGoVersion | undefined; let match: IGoVersion | undefined;
const dlUrl = 'https://golang.org/dl/?mode=json&include=all';
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist( const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
dlUrl GOLANG_DOWNLOAD_URL
); );
if (!candidates) { if (!candidates) {
throw new Error(`golang download url did not return results`); throw new Error(`golang download url did not return results`);
@@ -527,9 +528,8 @@ async function resolveStableVersionDist(
) { ) {
const archFilter = sys.getArch(arch); const archFilter = sys.getArch(arch);
const platFilter = sys.getPlatform(); const platFilter = sys.getPlatform();
const dlUrl = 'https://golang.org/dl/?mode=json&include=all';
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist( const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
dlUrl GOLANG_DOWNLOAD_URL
); );
if (!candidates) { if (!candidates) {
throw new Error(`golang download url did not return results`); throw new Error(`golang download url did not return results`);

View File

@@ -9,7 +9,7 @@ export interface PackageManagerInfo {
export const supportedPackageManagers: SupportedPackageManagers = { export const supportedPackageManagers: SupportedPackageManagers = {
default: { default: {
dependencyFilePattern: 'go.sum', dependencyFilePattern: 'go.mod',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE'] cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
} }
}; };