Preserve native Go environment names in action outputs (#793)

* preserve native Go environment names in action outputs

* implement review comment

* Add missing trailing newline to action.yml

---------

Co-authored-by: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com>
This commit is contained in:
v-mahabaleshwars
2026-09-25 21:05:28 +05:30
committed by GitHub
parent e626ada899
commit 3bfff43db6
8 changed files with 93 additions and 101 deletions

View File

@@ -58,15 +58,15 @@ jobs:
go-version: stable
- name: Show Go environment outputs
run: |
echo "go-path: ${{ steps.setup-go.outputs.go-path }}"
echo "go-bin: ${{ steps.setup-go.outputs.go-bin }}"
echo "GOPATH: ${{ steps.setup-go.outputs.GOPATH }}"
echo "GOBIN: ${{ steps.setup-go.outputs.GOBIN }}"
echo "go-bin-path: ${{ steps.setup-go.outputs.go-bin-path }}"
echo "go-root: ${{ steps.setup-go.outputs.go-root }}"
echo "go-cache: ${{ steps.setup-go.outputs.go-cache }}"
echo "go-mod-cache: ${{ steps.setup-go.outputs.go-mod-cache }}"
echo "go-os: ${{ steps.setup-go.outputs.go-os }}"
echo "go-arch: ${{ steps.setup-go.outputs.go-arch }}"
echo "go-tool-dir: ${{ steps.setup-go.outputs.go-tool-dir }}"
echo "GOROOT: ${{ steps.setup-go.outputs.GOROOT }}"
echo "GOCACHE: ${{ steps.setup-go.outputs.GOCACHE }}"
echo "GOMODCACHE: ${{ steps.setup-go.outputs.GOMODCACHE }}"
echo "GOOS: ${{ steps.setup-go.outputs.GOOS }}"
echo "GOARCH: ${{ steps.setup-go.outputs.GOARCH }}"
echo "GOTOOLDIR: ${{ steps.setup-go.outputs.GOTOOLDIR }}"
aliases-arch:
runs-on: ${{ matrix.os }}

View File

@@ -17,6 +17,7 @@ import goTestManifest from './data/versions-manifest.json' with {type: 'json'};
import type {IGoVersion} from '../src/installer.js';
import type {IToolRelease} from '@actions/tool-cache';
import {GO_ENV_OUTPUTS} from '../src/constants.js';
const httpClientGetJson = jest.fn();
@@ -697,17 +698,19 @@ describe('setup-go', () => {
it('sets an output for each exposed variable', () => {
main.setGoEnvOutputs(goEnv);
expect(setOutputSpy).toHaveBeenCalledWith('go-path', goEnv.GOPATH);
expect(setOutputSpy).toHaveBeenCalledWith('go-bin', '');
expect(setOutputSpy).toHaveBeenCalledWith('go-root', goEnv.GOROOT);
expect(setOutputSpy).toHaveBeenCalledWith('go-cache', goEnv.GOCACHE);
expect(setOutputSpy).toHaveBeenCalledWith(
'go-mod-cache',
goEnv.GOMODCACHE
);
expect(setOutputSpy).toHaveBeenCalledWith('go-os', goEnv.GOOS);
expect(setOutputSpy).toHaveBeenCalledWith('go-arch', goEnv.GOARCH);
expect(setOutputSpy).toHaveBeenCalledWith('go-tool-dir', goEnv.GOTOOLDIR);
expect(GO_ENV_OUTPUTS).toEqual([
'GOPATH',
'GOBIN',
'GOROOT',
'GOCACHE',
'GOMODCACHE',
'GOOS',
'GOARCH',
'GOTOOLDIR'
]);
for (const name of GO_ENV_OUTPUTS) {
expect(setOutputSpy).toHaveBeenCalledWith(name, goEnv[name] ?? '');
}
});
it('falls back to $GOPATH/bin when GOBIN is empty', () => {
@@ -735,7 +738,7 @@ describe('setup-go', () => {
main.setGoEnvOutputs({...goEnv, GOPATH});
expect(setOutputSpy).toHaveBeenCalledWith('go-path', GOPATH);
expect(setOutputSpy).toHaveBeenCalledWith('GOPATH', GOPATH);
expect(setOutputSpy).toHaveBeenCalledWith(
'go-bin-path',
'/Users/testuser/go/bin'
@@ -745,7 +748,7 @@ describe('setup-go', () => {
it('leaves variables missing from go env -json empty', () => {
main.setGoEnvOutputs({});
expect(setOutputSpy).toHaveBeenCalledWith('go-path', '');
expect(setOutputSpy).toHaveBeenCalledWith('GOPATH', '');
expect(setOutputSpy).toHaveBeenCalledWith('go-bin-path', '');
});
});

View File

@@ -26,23 +26,23 @@ outputs:
description: 'The installed Go version. Useful when given a version range as input.'
cache-hit:
description: 'A boolean value to indicate if a cache was hit'
go-path:
GOPATH:
description: 'The value of `go env GOPATH`.'
go-bin:
GOBIN:
description: 'The value of `go env GOBIN`.'
go-bin-path:
description: 'The base Go binary directory: `GOBIN`, otherwise `bin` under the first `GOPATH` entry.'
go-root:
GOROOT:
description: 'The value of `go env GOROOT`.'
go-cache:
GOCACHE:
description: 'The value of `go env GOCACHE`, the build cache directory.'
go-mod-cache:
GOMODCACHE:
description: 'The value of `go env GOMODCACHE`, the module cache directory.'
go-os:
GOOS:
description: 'The value of `go env GOOS`, the target OS in Go notation (linux, darwin, windows).'
go-arch:
GOARCH:
description: 'The value of `go env GOARCH`, the target architecture in Go notation (amd64, arm64).'
go-tool-dir:
GOTOOLDIR:
description: 'The value of `go env GOTOOLDIR`, the directory holding the toolchain binaries.'
runs:
using: 'node24'

View File

@@ -95641,16 +95641,18 @@ var State;
var Outputs;
(function (Outputs) {
Outputs["CacheHit"] = "cache-hit";
Outputs["GoPath"] = "go-path";
Outputs["GoBin"] = "go-bin";
Outputs["GoBinPath"] = "go-bin-path";
Outputs["GoRoot"] = "go-root";
Outputs["GoCache"] = "go-cache";
Outputs["GoModCache"] = "go-mod-cache";
Outputs["GoOs"] = "go-os";
Outputs["GoArch"] = "go-arch";
Outputs["GoToolDir"] = "go-tool-dir";
})(Outputs || (Outputs = {}));
const GO_ENV_OUTPUTS = (/* unused pure expression or super */ null && ([
'GOPATH',
'GOBIN',
'GOROOT',
'GOCACHE',
'GOMODCACHE',
'GOOS',
'GOARCH',
'GOTOOLDIR'
]));
;// CONCATENATED MODULE: ./src/package-managers.ts
const supportedPackageManagers = {

36
dist/setup/index.js vendored
View File

@@ -100441,16 +100441,18 @@ var State;
var Outputs;
(function (Outputs) {
Outputs["CacheHit"] = "cache-hit";
Outputs["GoPath"] = "go-path";
Outputs["GoBin"] = "go-bin";
Outputs["GoBinPath"] = "go-bin-path";
Outputs["GoRoot"] = "go-root";
Outputs["GoCache"] = "go-cache";
Outputs["GoModCache"] = "go-mod-cache";
Outputs["GoOs"] = "go-os";
Outputs["GoArch"] = "go-arch";
Outputs["GoToolDir"] = "go-tool-dir";
})(Outputs || (Outputs = {}));
const GO_ENV_OUTPUTS = [
'GOPATH',
'GOBIN',
'GOROOT',
'GOCACHE',
'GOMODCACHE',
'GOOS',
'GOARCH',
'GOTOOLDIR'
];
;// CONCATENATED MODULE: ./src/package-managers.ts
const supportedPackageManagers = {
@@ -100674,24 +100676,14 @@ function readGoEnv(goPath) {
return undefined;
}
}
const goEnvOutputs = [
[Outputs.GoPath, 'GOPATH'],
[Outputs.GoBin, 'GOBIN'],
[Outputs.GoRoot, 'GOROOT'],
[Outputs.GoCache, 'GOCACHE'],
[Outputs.GoModCache, 'GOMODCACHE'],
[Outputs.GoOs, 'GOOS'],
[Outputs.GoArch, 'GOARCH'],
[Outputs.GoToolDir, 'GOTOOLDIR']
];
function setGoEnvOutputs(goEnv) {
for (const [output, variable] of goEnvOutputs) {
setOutput(output, goEnv[variable] ?? '');
for (const name of GO_ENV_OUTPUTS) {
setOutput(name, goEnv[name] ?? '');
}
setOutput(Outputs.GoBinPath, goEnv['GOBIN'] || goPathBin(goEnv));
setOutput(Outputs.GoBinPath, goEnv.GOBIN || goPathBin(goEnv));
}
function goPathBin(goEnv) {
const goPath = (goEnv['GOPATH'] ?? '').split((external_path_default()).delimiter)[0];
const goPath = (goEnv.GOPATH ?? '').split((external_path_default()).delimiter)[0];
return goPath ? external_path_default().join(goPath, 'bin') : '';
}
async function addBinToPath() {

View File

@@ -427,17 +427,17 @@ jobs:
Once Go is on the `PATH`, the action exposes the most commonly needed `go env` variables as outputs, so workflows don't have to query them in duplicated `bash`/`pwsh` steps — one expression works on Linux, macOS, and Windows. The values are a snapshot from setup time: anything a later step changes, such as setting `GOOS`/`GOARCH` to cross-compile, is not reflected.
| Output | `go env` variable | Notes |
| --- | --- | --- |
| `go-path` | `GOPATH` | Go workspace root; `bin` and `pkg/mod` live under it |
| `go-bin` | `GOBIN` | Go 1.27+ reports an implicit `$GOPATH/bin` default; earlier releases are empty unless `GOBIN` is set |
| `go-bin-path` | `GOBIN` or `$GOPATH/bin` | The base Go binary directory, using the first `GOPATH` entry. Cross-compiled binaries go one level deeper, in `$GOPATH/bin/$GOOS_$GOARCH` |
| `go-root` | `GOROOT` | Installation directory of the Go toolchain in use |
| `go-cache` | `GOCACHE` | Build cache directory |
| `go-mod-cache` | `GOMODCACHE` | Module cache directory |
| `go-os` | `GOOS` | Go notation (`linux`, `darwin`, `windows`), unlike `runner.os` |
| `go-arch` | `GOARCH` | Go notation (`amd64`, `arm64`), unlike `runner.arch` or the `x64` in the action's cache key |
| `go-tool-dir` | `GOTOOLDIR` | Holds `compile`, `link`, `vet` and the other toolchain binaries |
| Output | Notes |
| --- | --- |
| `GOPATH` | Go workspace root; `bin` and `pkg/mod` live under it |
| `GOBIN` | Go 1.27+ reports an implicit `$GOPATH/bin` default; earlier releases are empty unless `GOBIN` is set |
| `go-bin-path` | The base Go binary directory (`GOBIN` or `$GOPATH/bin`), using the first `GOPATH` entry. Cross-compiled binaries go one level deeper, in `$GOPATH/bin/$GOOS_$GOARCH` |
| `GOROOT` | Installation directory of the Go toolchain in use |
| `GOCACHE` | Build cache directory |
| `GOMODCACHE` | Module cache directory |
| `GOOS` | Go notation (`linux`, `darwin`, `windows`), unlike `runner.os` |
| `GOARCH` | Go notation (`amd64`, `arm64`), unlike `runner.arch` or the `x64` in the action's cache key |
| `GOTOOLDIR` | Holds `compile`, `link`, `vet` and the other toolchain binaries |
```yaml
steps:
@@ -445,13 +445,13 @@ steps:
id: setup-go
with:
go-version: '1.25.5'
- run: echo "Modules are cached in ${{ steps.setup-go.outputs.go-mod-cache }}"
- run: echo "Modules are cached in ${{ steps.setup-go.outputs.GOMODCACHE }}"
```
Quote `go-bin-path` in shell commands, since it may contain spaces. Variables without a dedicated output are not exposed; run `go env <NAME>` in a step when you need one.
> [!NOTE]
> These outputs need Go 1.9 or newer (`go env -json`); on older releases the action logs a message, leaves them unset and does not fail. `go-cache` needs Go 1.10 and `go-mod-cache` needs Go 1.15.
> These outputs need Go 1.9 or newer (`go env -json`); on older releases the action logs a message, leaves them unset and does not fail. `GOCACHE` needs Go 1.10 and `GOMODCACHE` needs Go 1.15.
## Custom download URL

View File

@@ -5,13 +5,19 @@ export enum State {
export enum Outputs {
CacheHit = 'cache-hit',
GoPath = 'go-path',
GoBin = 'go-bin',
GoBinPath = 'go-bin-path',
GoRoot = 'go-root',
GoCache = 'go-cache',
GoModCache = 'go-mod-cache',
GoOs = 'go-os',
GoArch = 'go-arch',
GoToolDir = 'go-tool-dir'
GoBinPath = 'go-bin-path'
}
export const GO_ENV_OUTPUTS = [
'GOPATH',
'GOBIN',
'GOROOT',
'GOCACHE',
'GOMODCACHE',
'GOOS',
'GOARCH',
'GOTOOLDIR'
] as const;
export type GoEnvVar = (typeof GO_ENV_OUTPUTS)[number];
export type GoEnv = Partial<Record<GoEnvVar, string>>;

View File

@@ -10,7 +10,7 @@ import cp from 'child_process';
import fs from 'fs';
import os from 'os';
import {Architecture} from './types.js';
import {Outputs} from './constants.js';
import {GO_ENV_OUTPUTS, GoEnv, Outputs} from './constants.js';
export async function run() {
try {
@@ -125,7 +125,7 @@ export async function run() {
}
}
export function readGoEnv(goPath: string): Record<string, string> | undefined {
export function readGoEnv(goPath: string): GoEnv | undefined {
try {
const rawGoEnv = cp.execFileSync(goPath, ['env', '-json'], {
encoding: 'utf8',
@@ -141,7 +141,7 @@ export function readGoEnv(goPath: string): Record<string, string> | undefined {
throw new Error("'go env -json' did not return a JSON object");
}
return parsed as Record<string, string>;
return parsed as GoEnv;
} catch (error) {
core.info(
`Unable to read 'go env -json', the Go environment outputs will not be set: ${
@@ -152,27 +152,16 @@ export function readGoEnv(goPath: string): Record<string, string> | undefined {
}
}
const goEnvOutputs: ReadonlyArray<[Outputs, string]> = [
[Outputs.GoPath, 'GOPATH'],
[Outputs.GoBin, 'GOBIN'],
[Outputs.GoRoot, 'GOROOT'],
[Outputs.GoCache, 'GOCACHE'],
[Outputs.GoModCache, 'GOMODCACHE'],
[Outputs.GoOs, 'GOOS'],
[Outputs.GoArch, 'GOARCH'],
[Outputs.GoToolDir, 'GOTOOLDIR']
];
export function setGoEnvOutputs(goEnv: Record<string, string>): void {
for (const [output, variable] of goEnvOutputs) {
core.setOutput(output, goEnv[variable] ?? '');
export function setGoEnvOutputs(goEnv: GoEnv): void {
for (const name of GO_ENV_OUTPUTS) {
core.setOutput(name, goEnv[name] ?? '');
}
core.setOutput(Outputs.GoBinPath, goEnv['GOBIN'] || goPathBin(goEnv));
core.setOutput(Outputs.GoBinPath, goEnv.GOBIN || goPathBin(goEnv));
}
function goPathBin(goEnv: Record<string, string>): string {
const goPath = (goEnv['GOPATH'] ?? '').split(path.delimiter)[0];
function goPathBin(goEnv: GoEnv): string {
const goPath = (goEnv.GOPATH ?? '').split(path.delimiter)[0];
return goPath ? path.join(goPath, 'bin') : '';
}