Compare commits

...

23 Commits
v6.0.0 ... main

Author SHA1 Message Date
Haritha
924ae3a1cd
chore: bump version to 6.5.0 in package.json and package-lock.json (#762)
* chore: bump version to 6.5.0 in package.json and package-lock.json

* chore: update README.md for improved clarity and structure

* License update
2026-06-23 12:03:17 -05:00
Jason Ginchereau
e91cc3bfe0
Bump @actions/cache to 5.1.0, log cache write denied (#758)
* Bump @actions/cache to 5.1.0, log cache write denied

* Add cache save tests

* Re-trigger CI
2026-06-22 14:14:01 -05:00
Haritha
4a2405e6ae
chore: update @types/node and @typescript-eslint dependencies to latest versions (#755) 2026-06-16 11:46:40 -05:00
Copilot
78961f6f84
chore: update @actions dependencies and refresh license cache (#744)
Agent-Logs-Url: https://github.com/actions/setup-go/sessions/0e15a7f6-9e3f-4b17-98fc-7c6501a56b31

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: priyagupta108 <147705955+priyagupta108@users.noreply.github.com>
2026-04-13 14:45:08 -05:00
George Adams
4a3601121d
docs: fix Microsoft build of Go link (#734) 2026-03-17 14:02:21 -05:00
George Adams
8f19afcc70
feat: add go-download-base-url input for custom Go distributions (#721)
* 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

* run prettier

* fixup PR review

* revert cache-save

* fixup

* handle distinct cache

* skip json for known URL

* fix bug in JSON with custom URL
2026-03-16 12:43:44 -05:00
dependabot[bot]
27fdb267c1
Bump minimatch from 3.1.2 to 3.1.5 (#727)
* Bump minimatch from 3.1.2 to 3.1.5

Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Check failure fix

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: gowridurgad <gowridurgad@gmail.com>
2026-03-02 15:43:24 -06:00
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
dependabot[bot]
7a3fe6cf4c
Bump qs from 6.14.0 to 6.14.1 (#703)
* Bump qs from 6.11.2 to 6.14.1

Bumps [qs](https://github.com/ljharb/qs) from 6.11.2 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.11.2...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* package.json version update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aparna Jyothi <aparnajyothi-y@github.com>
2026-01-12 20:41:32 -06:00
dependabot[bot]
b9adafd441
Bump actions/checkout from 5 to 6 (#686)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 20:37:59 -06:00
Andreas Deininger
d73f6bcfc2
README.md: correct to actions/checkout@v6 (#683) 2026-01-12 13:52:41 -06:00
Salman Chishti
ae252ee6fb
Bump @actions/cache to v5 (#695)
* Bump @actions/cache to version 5.0.0 in package.json and package-lock.json

* Bump @actions/cache to v5

* prepease release 6.0.1

* Bump @actions/cache to v5.0.1

- Resolves punycode deprecation warning
- Updates licensed cache metadata

* upgrade actions/cache to 5.0.1

---------

Co-authored-by: Aparna Jyothi <aparnajyothi-y@github.com>
2026-01-07 10:40:06 -06:00
dependabot[bot]
bf7446afaf
Bump js-yaml from 3.14.1 to 3.14.2 (#682)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 10:36:25 -06:00
ccoVeille
02aadfee7f
Fix Node.js version in action.yml (#691)
It looks like the Node.js version specified in action.yml was left behind
when we upgraded the project to Node.js 24. This commit updates action.yml
to reflect the correct Node.js version.
2026-01-07 09:26:13 -06:00
aparnajyothi-y
4aaadf4266
Example for restore-only cache in documentation (#696)
* cache-restore-only example

* resolve conflicts

* example update

* format update

* format update

* update
2025-12-17 21:18:55 -06:00
dependabot[bot]
4dc6199c7b
Bump semver and @types/semver (#652)
* Bump semver and @types/semver

Bumps [semver](https://github.com/npm/node-semver) and [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver). These dependencies needed to be updated together.

Updates `semver` from 7.7.1 to 7.7.2
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.7.1...v7.7.2)

Updates `@types/semver` from 7.5.8 to 7.7.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver)

---
updated-dependencies:
- dependency-name: semver
  dependency-version: 7.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/semver"
  dependency-version: 7.7.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: add license information for semver dependency

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: HarithaVattikuti <73516759+HarithaVattikuti@users.noreply.github.com>
2025-11-19 20:54:04 -06:00
mahabaleshwars
f3787be646
Add comprehensive breaking changes documentation for v6 (#674)
* v6 documentation update

* v6 documentation update

* Readme file update

* update documentation

* documentation update

* update documentation

* Updated documentation

* Updated Documentation

* documentation update

* update documentation. action.yml and versions.yml

* Update documentation to organize go-version-file

* update v6 features and improve version patterns table

* Update documentation to specify supported syntaxes for `go-version` input
2025-11-19 09:40:16 -06:00
dependabot[bot]
3a0c2c8245
Bump actions/publish-action from 0.3.0 to 0.4.0 (#641)
Bumps [actions/publish-action](https://github.com/actions/publish-action) from 0.3.0 to 0.4.0.
- [Commits](https://github.com/actions/publish-action/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: actions/publish-action
  dependency-version: 0.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:46:50 -06:00
priya-kinthali
faf52423ec
Add support for .tool-versions file in setup-go, update workflow (#673)
* setup go in local

* add .tool-versions file support
2025-10-28 10:26:52 -05:00
Nicholas Ngai
7bc60db215
Fall back to downloading from go.dev/dl instead of storage.googleapis.com/golang (#665)
* Fall back to downloading from dl.google.com/go instead of storage.googleapis.com/golang

* Use go.dev/dl instead
2025-10-27 15:43:21 -05:00
dependabot[bot]
c0137caad7
Bump eslint-config-prettier from 10.0.1 to 10.1.8 and document breaking changes in v6 (#617)
* Bump eslint-config-prettier from 10.0.1 to 10.1.8

---
updated-dependencies:
- dependency-name: eslint-config-prettier
  dependency-version: 10.1.8
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* doc update

* doc update

* update

* doc update

* doc update

* doc update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aparna Jyothi <aparnajyothi-y@github.com>
2025-09-15 15:23:43 -05:00
99 changed files with 161188 additions and 163583 deletions

View File

@ -0,0 +1,143 @@
name: Validate Microsoft build of Go
on:
push:
branches:
- main
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
jobs:
microsoft-basic:
name: 'Microsoft build of Go ${{ matrix.go-version }} on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: ['1.25', '1.24']
steps:
- uses: actions/checkout@v6
- name: Setup Microsoft build of Go ${{ matrix.go-version }}
uses: ./
with:
go-version: ${{ matrix.go-version }}
go-download-base-url: 'https://aka.ms/golang/release/latest'
cache: false
- name: Verify Go installation
run: go version
- name: Verify Go env
run: go env
- name: Verify Go is functional
shell: bash
run: |
# Create a simple Go program and run it
mkdir -p /tmp/test-go && cd /tmp/test-go
cat > main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("Hello from Microsoft build of Go!")
}
EOF
go run main.go
microsoft-env-var:
name: 'Microsoft build of Go via env var on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
env:
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
steps:
- uses: actions/checkout@v6
- name: Setup Microsoft build of Go via environment variable
uses: ./
with:
go-version: '1.25'
cache: false
- name: Verify Go installation
run: go version
- name: Verify Go is functional
shell: bash
run: |
mkdir -p /tmp/test-go && cd /tmp/test-go
cat > main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("Hello from Microsoft build of Go via env var!")
}
EOF
go run main.go
microsoft-architecture:
name: 'Microsoft build of Go arch ${{ matrix.architecture }} on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
architecture: [x64]
include:
- os: macos-latest
architecture: arm64
steps:
- uses: actions/checkout@v6
- name: Setup Microsoft build of Go with architecture
uses: ./
with:
go-version: '1.25'
go-download-base-url: 'https://aka.ms/golang/release/latest'
architecture: ${{ matrix.architecture }}
cache: false
- name: Verify Go installation
run: go version
microsoft-with-cache:
name: 'Microsoft build of Go with caching on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v6
- name: Setup Microsoft build of Go with caching
uses: ./
with:
go-version: '1.25'
go-download-base-url: 'https://aka.ms/golang/release/latest'
cache: true
- name: Verify Go installation
run: go version
- name: Verify Go is functional
shell: bash
run: |
mkdir -p /tmp/test-go && cd /tmp/test-go
go mod init test
cat > main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("Hello from cached Microsoft build of Go!")
}
EOF
go run main.go

View File

@ -14,7 +14,7 @@ jobs:
steps: steps:
- name: Checking out - name: Checking out
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Publish - name: Publish
id: publish id: publish
uses: actions/publish-immutable-action@v0.0.4 uses: actions/publish-immutable-action@v0.0.4

View File

@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Update the ${{ env.TAG_NAME }} tag - name: Update the ${{ env.TAG_NAME }} tag
uses: actions/publish-action@v0.3.0 uses: actions/publish-action@v0.4.0
with: with:
source-tag: ${{ env.TAG_NAME }} source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }} slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -18,9 +18,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go Stable - name: Setup Go Stable
uses: ./ uses: ./
with: with:
@ -33,9 +33,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go oldStable - name: Setup Go oldStable
uses: ./ uses: ./
with: with:
@ -48,16 +48,16 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
version: [stable, oldstable] version: [stable, oldstable]
architecture: [x64, x32] architecture: [x64, x32]
exclude: exclude:
- os: macos-latest - os: macos-latest
architecture: x32 architecture: x32
- os: macos-13 - os: macos-latest-large
architecture: x32 architecture: x32
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go ${{ matrix.version }} ${{ matrix.architecture }} - name: Setup Go ${{ matrix.version }} ${{ matrix.architecture }}
uses: ./ uses: ./
with: with:
@ -72,7 +72,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-latest, macos-13] os: [macos-latest, windows-latest, ubuntu-latest, macos-latest-large]
go: [1.21.13, 1.22.8, 1.23.2] go: [1.21.13, 1.22.8, 1.23.2]
include: include:
- os: windows-latest - os: windows-latest
@ -82,7 +82,7 @@ jobs:
go: 1.23.2 go: 1.23.2
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: setup-go ${{ matrix.go }} - name: setup-go ${{ matrix.go }}
uses: ./ uses: ./
@ -98,10 +98,10 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
go-version: ['1.20', '1.21', '1.22', '1.23'] go-version: ['1.20', '1.21', '1.22', '1.23']
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go and check latest - name: Setup Go and check latest
uses: ./ uses: ./
with: with:
@ -115,9 +115,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go and check latest - name: Setup Go and check latest
uses: ./ uses: ./
with: with:
@ -131,9 +131,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go and check latest - name: Setup Go and check latest
uses: ./ uses: ./
with: with:
@ -142,16 +142,48 @@ jobs:
run: __tests__/verify-go.sh 1.21 run: __tests__/verify-go.sh 1.21
shell: bash shell: bash
go-version-file-with-tool-versions:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps:
- uses: actions/checkout@v6
- name: Setup Go and check latest
uses: ./
with:
go-version-file: __tests__/data/.tool-versions
- name: verify go
run: __tests__/verify-go.sh 1.23.2
shell: bash
go-version-file-with-go-version:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
steps:
- uses: actions/checkout@v6
- name: Setup Go from .go-version file
uses: ./
with:
go-version-file: __tests__/data/.go-version
- name: verify go
run: __tests__/verify-go.sh 1.22.4
shell: bash
setup-versions-from-manifest: setup-versions-from-manifest:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-latest, macos-13] os: [macos-latest, windows-latest, ubuntu-latest, macos-latest-large]
go: [1.20.14, 1.21.10, 1.22.8, 1.23.2] go: [1.20.14, 1.21.10, 1.22.8, 1.23.2]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: setup-go ${{ matrix.go }} - name: setup-go ${{ matrix.go }}
uses: ./ uses: ./
@ -167,11 +199,11 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [windows-latest, ubuntu-latest, macos-13] os: [windows-latest, ubuntu-latest, macos-latest-large]
go: [1.11.12] go: [1.11.12]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: setup-go ${{ matrix.go }} - name: setup-go ${{ matrix.go }}
uses: ./ uses: ./
@ -187,7 +219,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, macos-13] os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
go-version: [1.20.14, 1.21, 1.22, 1.23] go-version: [1.20.14, 1.21, 1.22, 1.23]
include: include:
- os: macos-latest - os: macos-latest
@ -196,10 +228,10 @@ jobs:
architecture: x64 architecture: x64
- os: windows-latest - os: windows-latest
architecture: x64 architecture: x64
- os: macos-13 - os: macos-latest-large
architecture: x64 architecture: x64
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Go and check latest - name: Setup Go and check latest
uses: ./ uses: ./
with: with:

View File

@ -19,7 +19,7 @@ jobs:
cache: [false, true] cache: [false, true]
go: [1.20.1] go: [1.20.1]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: 'Setup ${{ matrix.cache }}, cache: ${{ matrix.go }}' - name: 'Setup ${{ matrix.cache }}, cache: ${{ matrix.go }}'
uses: ./ uses: ./
@ -88,7 +88,7 @@ jobs:
matrix: matrix:
cache: [false, true] cache: [false, true]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: 'Setup default go, cache: ${{ matrix.cache }}' - name: 'Setup default go, cache: ${{ matrix.cache }}'
uses: ./ uses: ./
@ -121,7 +121,7 @@ jobs:
cache: [false] cache: [false]
go: [1.20.1] go: [1.20.1]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: 'Setup ${{ matrix.go }}, cache: ${{ matrix.cache }}' - name: 'Setup ${{ matrix.go }}, cache: ${{ matrix.cache }}'
uses: ./ uses: ./

View File

@ -13,3 +13,4 @@ allowed:
reviewed: reviewed:
npm: npm:
- "@actions/http-client"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/@nodable/entities.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/debug.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/tslib.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/xml-naming.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

331
README.md
View File

@ -3,68 +3,96 @@
[![Basic validation](https://github.com/actions/setup-go/actions/workflows/basic-validation.yml/badge.svg)](https://github.com/actions/setup-go/actions/workflows/basic-validation.yml) [![Basic validation](https://github.com/actions/setup-go/actions/workflows/basic-validation.yml/badge.svg)](https://github.com/actions/setup-go/actions/workflows/basic-validation.yml)
[![Validate 'setup-go'](https://github.com/actions/setup-go/actions/workflows/versions.yml/badge.svg)](https://github.com/actions/setup-go/actions/workflows/versions.yml) [![Validate 'setup-go'](https://github.com/actions/setup-go/actions/workflows/versions.yml/badge.svg)](https://github.com/actions/setup-go/actions/workflows/versions.yml)
This action sets up a go environment for use in 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
- Registering problem matchers for error output. - Optionally caching Go modules and build outputs
- Registering problem matchers for error output
# V5 ## Breaking changes in V6
The V5 edition of the action offers: The V6 edition of the action includes:
- **Upgraded Node.js runtime from node20 to node24**
> 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)
- Upgraded Node.js runtime from node16 to node20 - **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.
- **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.
See full release notes on the [releases page](https://github.com/actions/setup-go/releases). See full release notes on the [releases page](https://github.com/actions/setup-go/releases).
# V4 ## Usage
The V4 edition of the action offers: See [action.yml](action.yml).
- Enabled caching by default <!-- start usage -->
```yaml
- uses: actions/setup-go@v6
with:
# Version or version range of Go to use
go-version: '1.23'
The action will try to enable caching unless the `cache` input is explicitly set to false. # 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'
Please see "[Caching dependency files and build outputs](https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs)" for more information. # Set this option if you want the action to check for the latest available version
# Default: false
check-latest: false
# V3 # GitHub token for authentication
token: ${{ github.token }}
The V3 edition of the action offers: # Used to specify whether caching is needed.
# Default: true
cache: true
- Adds `GOBIN` to the `PATH` # Path to dependency files for caching
- Proxy support cache-dependency-path: 'go.sum'
- Check latest version
- Caching packages dependencies
- stable and oldstable aliases
- Bug Fixes (including issues around version matching and semver)
The action will first check the local cache for a version match. If a version is not found locally, it will pull it from # Architecture to install (auto-detected if not specified)
the `main` branch of the [go-versions](https://github.com/actions/go-versions/blob/main/versions-manifest.json) architecture: 'x64'
repository. On miss or failure, it will fall back to downloading directly
from [go dist](https://storage.googleapis.com/golang). To change the default behavior, please use
the [check-latest input](#check-latest-version).
**Note:** The `setup-go` action uses executable binaries which are built by Golang side. The action does not build # Custom base URL for Go downloads (e.g., for mirrors)
golang from source code. go-download-base-url: ''
```
<!-- end usage -->
Matching by [semver spec](https://github.com/npm/node-semver): **Basic:**
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version: '^1.13.1' # The Go version to download (if necessary) and use. go-version: '1.25' # The Go version to download (if necessary) and use.
- run: go version - run: go run hello.go
``` ```
```yaml **Version resolution behavior:**
steps:
- uses: actions/checkout@v5 The action resolves the requested version in the following order:
- uses: actions/setup-go@v6 1. **Local cache** - Checks the local tool cache for a matching [semver](https://github.com/npm/node-semver#versions) version.
with: 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).
go-version: '>=1.17.0' 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).
- run: go version
``` To change the default behavior, please use
the [check-latest input](docs/advanced-usage.md#check-latest-version).
> **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: > **Note**: Due to the peculiarities of YAML parsing, it is recommended to wrap the version in single quotation marks:
> >
@ -72,201 +100,9 @@ steps:
> go-version: '1.20' > 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. > 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.
Matching an unstable pre-release: 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.
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: '1.18.0-rc.1' # The Go version to download (if necessary) and use.
- run: go version
```
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: '1.16.0-beta.1' # The Go version to download (if necessary) and use.
- run: go version
```
# Usage
See [action.yml](action.yml)
## Basic
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: '1.16.1' # The Go version to download (if necessary) and use.
- run: go run hello.go
```
## 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.
> Setting `check-latest` to `true` has performance implications as downloading Go versions is slower than using cached
> versions.
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: '1.14'
check-latest: true
- run: go run hello.go
```
## Using stable/oldstable aliases
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.
If `oldstable` is provided, when current release is 1.19.x, action will resolve version as 1.18.x, where x is the latest
patch release.
**Note:** using these aliases will result in same version as using corresponding minor release with `check-latest` input
set to `true`
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- run: go run hello.go
```
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: 'oldstable'
- run: go run hello.go
```
## Caching dependency files and build outputs:
The action has a built-in functionality for caching and restoring 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 settings.
The `cache` input is optional, and caching is turned on by default.
The action defaults to search for the dependency file - go.sum in the repository root, and uses its hash as a part of
the cache key. Use `cache-dependency-path` input for cases when multiple dependency files are used, or they are located
in different subdirectories. The input supports glob patterns.
If some problem that prevents success caching happens then the action issues the warning in the log and continues the execution of the pipeline.
**Caching in monorepos**
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: '1.17'
check-latest: true
cache-dependency-path: |
subdir/go.sum
tools/go.sum
# cache-dependency-path: "**/*.sum"
- run: go run hello.go
```
## Getting go version from the go.mod file
The `go-version-file` input accepts a path to a `go.mod` file or a `go.work`
file that contains the version of Go to be used by a project. The version taken
from thils file will be:
- The version from the `toolchain` directive, if there is one, otherwise
- The version from the `go` directive
The version can specify a patch version or omit it altogether (e.g., `go 1.22.0` or `go 1.22`).
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://golang.org/dl/?mode=json&include=all), in that order.
If both the `go-version` and the `go-version-file` inputs are provided then the `go-version` input is used.
> The action will search for the `go.mod` file relative to the repository root
```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version-file: 'path/to/go.mod'
- run: go version
```
## Matrix testing
```yaml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.14', '1.13' ]
name: Go ${{ matrix.go }} sample
steps:
- uses: actions/checkout@v5
- name: Setup go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- run: go run hello.go
```
### Supported version syntax
The `go-version` input supports the following syntax:
- Specific versions: `1.15`, `1.16.1`, `1.17.0-rc.2`, `1.16.0-beta.1`
- SemVer's version range syntax: `^1.13.1`, `>=1.18.0-rc.1`
For more information about semantic versioning, please refer to [semver](https://github.com/npm/node-semver)
documentation.
## Using `setup-go` on GHES
`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 https://storage.googleapis.com/golang.
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.18'
```
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.
## Recommended permissions ## Recommended permissions
@ -277,13 +113,36 @@ permissions:
contents: read # access to check out code and install dependencies contents: read # access to check out code and install dependencies
``` ```
# License ## Caching dependency files and build outputs
The scripts and documentation in this project are released under the [MIT License](LICENSE) 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`.
# Contributions 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.
Contributions are welcome! See [Contributor's Guide](docs/contributors.md) 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)
- [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
The scripts and documentation in this project are released under the [MIT License](LICENSE).
## Contributions
Contributions are welcome! See our [Contributor's Guide](docs/contributors.md).
## Code of Conduct ## Code of Conduct

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

@ -0,0 +1,155 @@
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import fs from 'fs';
import {run} from '../src/cache-save';
import * as cacheUtils from '../src/cache-utils';
import {State} from '../src/constants';
describe('cache-save', () => {
const primaryKey = 'primary-key';
let primaryKeyValue: string;
let matchedKeyValue: string;
let getBooleanInputSpy: jest.SpyInstance;
let getStateSpy: jest.SpyInstance;
let infoSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
let setFailedSpy: jest.SpyInstance;
let saveCacheSpy: jest.SpyInstance;
let getCacheDirectoryPathSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
beforeEach(() => {
primaryKeyValue = primaryKey;
matchedKeyValue = 'matched-key';
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
getBooleanInputSpy.mockReturnValue(true);
getStateSpy = jest.spyOn(core, 'getState');
getStateSpy.mockImplementation((key: string) => {
if (key === State.CachePrimaryKey) {
return primaryKeyValue;
}
if (key === State.CacheMatchedKey) {
return matchedKeyValue;
}
return '';
});
infoSpy = jest.spyOn(core, 'info');
infoSpy.mockImplementation(() => undefined);
warningSpy = jest.spyOn(core, 'warning');
warningSpy.mockImplementation(() => undefined);
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(() => undefined);
setFailedSpy = jest.spyOn(core, 'setFailed');
setFailedSpy.mockImplementation(() => undefined);
saveCacheSpy = jest.spyOn(cache, 'saveCache');
saveCacheSpy.mockImplementation(() => Promise.resolve(0));
getCacheDirectoryPathSpy = jest.spyOn(cacheUtils, 'getCacheDirectoryPath');
getCacheDirectoryPathSpy.mockImplementation(() =>
Promise.resolve(['cache_directory_path', 'cache_directory_path'])
);
existsSpy = jest.spyOn(fs, 'existsSync');
existsSpy.mockImplementation(() => true);
});
afterEach(() => {
jest.restoreAllMocks();
});
it('does not save cache when the cache input is false', async () => {
getBooleanInputSpy.mockReturnValue(false);
await run();
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(warningSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('does not save cache when there are no cache folders on the disk', async () => {
existsSpy.mockImplementation(() => false);
await run();
expect(warningSpy).toHaveBeenCalledWith(
'There are no cache folders on the disk'
);
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('does not save cache when the primary key was not generated', async () => {
primaryKeyValue = '';
await run();
expect(infoSpy).toHaveBeenCalledWith(
'Primary key was not generated. Please check the log messages above for more errors or information'
);
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('does not save cache when a cache hit occurred on the primary key', async () => {
matchedKeyValue = primaryKey;
await run();
expect(infoSpy).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('saves cache when the primary key differs from the matched key', async () => {
await run();
expect(saveCacheSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(
`Cache saved with the key: ${primaryKey}`
);
expect(warningSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('save with -1 cacheId , should not fail workflow', async () => {
saveCacheSpy.mockImplementation(() => Promise.resolve(-1));
await run();
expect(saveCacheSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith(
`Cache was not saved for the key: ${primaryKey}`
);
expect(infoSpy).not.toHaveBeenCalledWith(
`Cache saved with the key: ${primaryKey}`
);
expect(warningSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('saves with error from toolkit, should not fail workflow', async () => {
saveCacheSpy.mockImplementation(() =>
Promise.reject(new Error('Unable to reach the service'))
);
await run();
expect(saveCacheSpy).toHaveBeenCalled();
expect(warningSpy).toHaveBeenCalledWith('Unable to reach the service');
expect(setFailedSpy).not.toHaveBeenCalled();
});
});

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

@ -0,0 +1 @@
1.22.4

View File

@ -0,0 +1 @@
golang 1.23.2

View File

@ -45,6 +45,7 @@ describe('setup-go', () => {
let mkdirSpy: jest.SpyInstance; let mkdirSpy: jest.SpyInstance;
let symlinkSpy: jest.SpyInstance; let symlinkSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance; let execSpy: jest.SpyInstance;
let execFileSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance; let getManifestSpy: jest.SpyInstance;
let getAllVersionsSpy: jest.SpyInstance; let getAllVersionsSpy: jest.SpyInstance;
let httpmGetJsonSpy: jest.SpyInstance; let httpmGetJsonSpy: jest.SpyInstance;
@ -71,6 +72,10 @@ describe('setup-go', () => {
archSpy = jest.spyOn(osm, 'arch'); archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => os['arch']); archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync'); execSpy = jest.spyOn(cp, 'execSync');
execFileSpy = jest.spyOn(cp, 'execFileSync');
execFileSpy.mockImplementation(() => {
throw new Error('ENOENT');
});
// switch path join behaviour based on set os.platform // switch path join behaviour based on set os.platform
joinSpy = jest.spyOn(path, 'join'); joinSpy = jest.spyOn(path, 'join');
@ -129,8 +134,9 @@ describe('setup-go', () => {
}); });
afterEach(() => { afterEach(() => {
// clear out env var set during 'run' // clear out env vars set during 'run'
delete process.env[im.GOTOOLCHAIN_ENV_VAR]; delete process.env[im.GOTOOLCHAIN_ENV_VAR];
delete process.env['GO_DOWNLOAD_BASE_URL'];
//jest.resetAllMocks(); //jest.resetAllMocks();
jest.clearAllMocks(); jest.clearAllMocks();
@ -389,7 +395,7 @@ describe('setup-go', () => {
const expPath = path.win32.join(toolPath, 'bin'); const expPath = path.win32.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalledWith( expect(dlSpy).toHaveBeenCalledWith(
'https://storage.googleapis.com/golang/go1.13.1.windows-amd64.zip', 'https://go.dev/dl/go1.13.1.windows-amd64.zip',
'C:\\temp\\go1.13.1.windows-amd64.zip', 'C:\\temp\\go1.13.1.windows-amd64.zip',
undefined undefined
); );
@ -866,6 +872,9 @@ exclude example.com/thismodule v1.3.0
use . use .
`;
const toolVersionsContents = `golang 1.23
`; `;
it('reads version from go.mod', async () => { it('reads version from go.mod', async () => {
@ -892,6 +901,18 @@ use .
expect(logSpy).toHaveBeenCalledWith('matching 1.19...'); expect(logSpy).toHaveBeenCalledWith('matching 1.19...');
}); });
it('reads version from .tool-versions', async () => {
inputs['go-version-file'] = '.tool-versions';
existsSpy.mockImplementation(() => true);
readFileSpy.mockImplementation(() => Buffer.from(toolVersionsContents));
await main.run();
expect(logSpy).toHaveBeenCalledWith('Setup go version spec 1.23');
expect(logSpy).toHaveBeenCalledWith('Attempting to download 1.23...');
expect(logSpy).toHaveBeenCalledWith('matching 1.23...');
});
it('reads version from .go-version', async () => { it('reads version from .go-version', async () => {
inputs['go-version-file'] = '.go-version'; inputs['go-version-file'] = '.go-version';
existsSpy.mockImplementation(() => true); existsSpy.mockImplementation(() => true);
@ -946,7 +967,7 @@ use .
const expectedUrl = const expectedUrl =
platform === 'win32' platform === 'win32'
? `https://github.com/actions/go-versions/releases/download/${version}/go-${version}-${platform}-${arch}.${fileExtension}` ? `https://github.com/actions/go-versions/releases/download/${version}/go-${version}-${platform}-${arch}.${fileExtension}`
: `https://storage.googleapis.com/golang/go${version}.${osSpec}-${arch}.${fileExtension}`; : `https://go.dev/dl/go${version}.${osSpec}-${arch}.${fileExtension}`;
// ... but not in the local cache // ... but not in the local cache
findSpy.mockImplementation(() => ''); findSpy.mockImplementation(() => '');
@ -1090,4 +1111,456 @@ use .
expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'}); expect(vars).toStrictEqual({GOTOOLCHAIN: 'local'});
expect(process.env).toHaveProperty('GOTOOLCHAIN', 'local'); expect(process.env).toHaveProperty('GOTOOLCHAIN', 'local');
}); });
describe('go-download-base-url', () => {
it('downloads a version from custom base URL using version listing', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.13.1';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.1/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(logSpy).toHaveBeenCalledWith(
`Using custom Go download base URL: ${customBaseUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
// Version listing should use custom base URL, not go.dev
expect(getSpy).toHaveBeenCalledWith(
`${customBaseUrl}/?mode=json&include=all`
);
expect(dlSpy).toHaveBeenCalled();
expect(extractTarSpy).toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('skips version listing for known direct-download URL (aka.ms)', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.25.0';
const customBaseUrl = 'https://aka.ms/golang/release/latest';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.25.0/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(logSpy).toHaveBeenCalledWith(
'Skipping version listing for known direct-download URL. Constructing download URL directly.'
);
expect(logSpy).toHaveBeenCalledWith(
`Constructed direct download URL: ${customBaseUrl}/go1.25.0.linux-amd64.tar.gz`
);
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
expect(getSpy).not.toHaveBeenCalled();
expect(dlSpy).toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('constructs correct direct download URL for windows (aka.ms)', async () => {
os.platform = 'win32';
os.arch = 'x64';
const versionSpec = '1.25.0';
const customBaseUrl = 'https://aka.ms/golang/release/latest';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
process.env['RUNNER_TEMP'] = 'C:\\temp\\';
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => 'C:\\temp\\some\\path');
extractZipSpy.mockImplementation(() => 'C:\\temp\\some\\other\\path');
const toolPath = path.normalize('C:\\cache\\go\\1.25.0\\x64');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(getSpy).not.toHaveBeenCalled();
expect(dlSpy).toHaveBeenCalledWith(
`${customBaseUrl}/go1.25.0.windows-amd64.zip`,
'C:\\temp\\go1.25.0.windows-amd64.zip',
undefined
);
});
it('skips manifest and downloads directly from custom URL', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.12.16';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
inputs['token'] = 'faketoken';
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.12.16/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
// Should not try to use the manifest at all
expect(logSpy).not.toHaveBeenCalledWith(
expect.stringContaining('Not found in manifest')
);
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
});
it('strips trailing slashes from custom base URL', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.13.1';
const customBaseUrl = 'https://example.com/golang/';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.1/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring go1.13.1 from https://example.com/golang/go1.13.1.linux-amd64.tar.gz`
);
});
it('reads custom base URL from environment variable', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.13.1';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
process.env['GO_DOWNLOAD_BASE_URL'] = customBaseUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.1/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
`Using custom Go download base URL: ${customBaseUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Install from custom download URL');
});
it('input takes precedence over environment variable', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.13.1';
const inputUrl = 'https://input.example.com/golang';
const envUrl = 'https://env.example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = inputUrl;
process.env['GO_DOWNLOAD_BASE_URL'] = envUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.1/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
`Using custom Go download base URL: ${inputUrl}`
);
expect(logSpy).toHaveBeenCalledWith(
`Acquiring go1.13.1 from ${inputUrl}/go1.13.1.linux-amd64.tar.gz`
);
});
it('errors when stable alias is used with custom URL', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['go-version'] = 'stable';
inputs['go-download-base-url'] = 'https://example.com/golang';
findSpy.mockImplementation(() => '');
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
`::error::Version aliases 'stable' are not supported with a custom download base URL. Please specify an exact Go version.${osm.EOL}`
);
});
it('logs info when check-latest is used with custom URL', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.13.1';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
inputs['check-latest'] = true;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/go/1.13.1/x64');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
);
});
it('constructs direct download info correctly', () => {
os.platform = 'linux';
os.arch = 'x64';
const info = im.getInfoFromDirectDownload(
'1.25.0',
'x64',
'https://aka.ms/golang/release/latest'
);
expect(info.type).toBe('dist');
expect(info.downloadUrl).toBe(
'https://aka.ms/golang/release/latest/go1.25.0.linux-amd64.tar.gz'
);
expect(info.fileName).toBe('go1.25.0.linux-amd64.tar.gz');
expect(info.resolvedVersion).toBe('1.25.0');
});
it('constructs direct download info for windows', () => {
os.platform = 'win32';
os.arch = 'x64';
const info = im.getInfoFromDirectDownload(
'1.25.0',
'x64',
'https://aka.ms/golang/release/latest'
);
expect(info.type).toBe('dist');
expect(info.downloadUrl).toBe(
'https://aka.ms/golang/release/latest/go1.25.0.windows-amd64.zip'
);
expect(info.fileName).toBe('go1.25.0.windows-amd64.zip');
});
it('constructs direct download info for arm64', () => {
os.platform = 'darwin';
os.arch = 'arm64';
const info = im.getInfoFromDirectDownload(
'1.25.0',
'arm64',
'https://aka.ms/golang/release/latest'
);
expect(info.type).toBe('dist');
expect(info.downloadUrl).toBe(
'https://aka.ms/golang/release/latest/go1.25.0.darwin-arm64.tar.gz'
);
expect(info.fileName).toBe('go1.25.0.darwin-arm64.tar.gz');
});
it('caches under actual installed version when it differs from input spec', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.20';
const customBaseUrl = 'https://aka.ms/golang/release/latest';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
// Mock the installed Go binary reporting a different patch version
execFileSpy.mockImplementation(() => 'go version go1.20.14 linux/amd64');
const expectedToolName = im.customToolCacheName(customBaseUrl);
const toolPath = path.normalize(`/cache/${expectedToolName}/1.20.14/x64`);
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
"Requested version '1.20' resolved to installed version '1.20.14'"
);
// Cache key should use actual version, not the input spec
expect(cacheSpy).toHaveBeenCalledWith(
expect.any(String),
expectedToolName,
'1.20.14',
'x64'
);
});
it('shows clear error with platform/arch and URL on 404', async () => {
os.platform = 'linux';
os.arch = 'arm64';
const versionSpec = '1.25.0';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
getSpy.mockImplementationOnce(() => {
throw new Error('Not a JSON endpoint');
});
findSpy.mockImplementation(() => '');
const httpError = new tc.HTTPError(404);
dlSpy.mockImplementation(() => {
throw httpError;
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
expect.stringContaining(
'The requested Go version 1.25.0 is not available for platform linux/arm64'
)
);
expect(cnSpy).toHaveBeenCalledWith(expect.stringContaining('HTTP 404'));
});
it('shows clear error with platform/arch and URL on download failure', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.25.0';
const customBaseUrl = 'https://example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
getSpy.mockImplementationOnce(() => {
throw new Error('Not a JSON endpoint');
});
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(() => {
throw new Error('connection refused');
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
expect.stringContaining(
'Failed to download Go 1.25.0 for platform linux/x64'
)
);
expect(cnSpy).toHaveBeenCalledWith(
expect.stringContaining(customBaseUrl)
);
});
it.each(['^1.25.0', '~1.25', '>=1.25.0', '<1.26.0', '1.25.x', '1.x'])(
'errors on version range "%s" when version listing is unavailable',
async versionSpec => {
os.platform = 'linux';
os.arch = 'x64';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = 'https://example.com/golang';
// Simulate version listing not available
getSpy.mockImplementationOnce(() => {
throw new Error('Not a JSON endpoint');
});
findSpy.mockImplementation(() => '');
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
expect.stringContaining(
`Version range '${versionSpec}' is not supported with a custom download base URL`
)
);
}
);
it('rejects version range in getInfoFromDirectDownload', () => {
os.platform = 'linux';
os.arch = 'x64';
expect(() =>
im.getInfoFromDirectDownload(
'^1.25.0',
'x64',
'https://example.com/golang'
)
).toThrow(
"Version range '^1.25.0' is not supported with a custom download base URL"
);
});
it('passes token as auth header for custom URL downloads', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '1.25.0';
const customBaseUrl = 'https://private-mirror.example.com/golang';
inputs['go-version'] = versionSpec;
inputs['go-download-base-url'] = customBaseUrl;
inputs['token'] = 'ghp_testtoken123';
getSpy.mockImplementationOnce(() => {
throw new Error('Not a JSON endpoint');
});
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
extractTarSpy.mockImplementation(async () => '/some/other/temp/path');
const expectedToolName = im.customToolCacheName(customBaseUrl);
const toolPath = path.normalize(`/cache/${expectedToolName}/1.25.0/x64`);
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalledWith(
`${customBaseUrl}/go1.25.0.linux-amd64.tar.gz`,
undefined,
'token ghp_testtoken123'
);
});
});
}); });

View File

@ -5,7 +5,7 @@ inputs:
go-version: go-version:
description: 'The Go version to download (if necessary) and use. Supports semver spec and ranges. Be sure to enclose this option in single quotation marks.' description: 'The Go version to download (if necessary) and use. Supports semver spec and ranges. Be sure to enclose this option in single quotation marks.'
go-version-file: go-version-file:
description: 'Path to the go.mod or go.work file.' description: 'Path to the go.mod, go.work, .go-version, or .tool-versions file.'
check-latest: check-latest:
description: 'Set this option to true if you want the action to always check for the latest available version that satisfies the version spec' description: 'Set this option to true if you want the action to always check for the latest available version that satisfies the version spec'
default: false default: false
@ -16,16 +16,18 @@ 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.'
go-download-base-url:
description: 'Custom base URL for downloading Go distributions. Use this to download Go from a mirror or custom source. Defaults to "https://go.dev/dl". Can also be set via the GO_DOWNLOAD_BASE_URL environment variable. The input takes precedence over the environment variable.'
outputs: outputs:
go-version: go-version:
description: 'The installed Go version. Useful when given a version range as input.' description: 'The installed Go version. Useful when given a version range as input.'
cache-hit: cache-hit:
description: 'A boolean value to indicate if a cache was hit' description: 'A boolean value to indicate if a cache was hit'
runs: runs:
using: 'node20' using: 'node24'
main: 'dist/setup/index.js' main: 'dist/setup/index.js'
post: 'dist/cache-save/index.js' post: 'dist/cache-save/index.js'
post-if: success() post-if: success()

156250
dist/cache-save/index.js vendored

File diff suppressed because one or more lines are too long

162087
dist/setup/index.js vendored

File diff suppressed because one or more lines are too long

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

@ -0,0 +1,498 @@
# 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)
- [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
### 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.
>
> `check-latest` is ignored when `go-download-base-url` is set. See [Custom download URL](#custom-download-url) for details.
```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
```
## Custom download URL
The `go-download-base-url` input lets you download Go from a mirror or alternative source instead of the default `https://go.dev/dl`. This can also be set via the `GO_DOWNLOAD_BASE_URL` environment variable; the input takes precedence over the environment variable.
When a custom base URL is provided, the action skips the `actions/go-versions` manifest lookup and downloads directly from the specified URL.
**Using the [Microsoft build of Go](https://github.com/microsoft/go):**
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
go-download-base-url: 'https://aka.ms/golang/release/latest'
- run: go version
```
**Using an environment variable:**
```yaml
env:
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
- run: go version
```
> **Note:** Version range syntax (`^1.25`, `~1.24`, `>=1.25.0`) and aliases (`stable`, `oldstable`) are not supported with custom download URLs. Use exact versions such as `1.25`, `1.25.0`, or `1.25.0-1` (for sources that use revision numbers). If the custom server provides a version listing endpoint (`/?mode=json&include=all`), semver ranges will work; otherwise only exact versions are accepted.
> **Note:** The `check-latest` option is ignored when a custom download base URL is set. The action cannot query the custom server for the latest version, so it uses the version you specify directly. If you provide a partial version like `1.25`, the server determines which patch release to serve.
**Authenticated downloads:**
If your custom download source requires authentication, the `token` input is forwarded as an `Authorization` header. For example, to download from a private mirror:
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.25'
go-download-base-url: 'https://private-mirror.example.com/golang'
token: ${{ secrets.MIRROR_TOKEN }}
- run: go version
```
## Using `setup-go` on GHES
### 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.

3258
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "setup-go", "name": "setup-go",
"version": "6.0.0", "version": "6.5.0",
"private": true, "private": true,
"description": "setup go action", "description": "setup go action",
"main": "lib/setup-go.js", "main": "lib/setup-go.js",
@ -28,24 +28,24 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^4.0.3", "@actions/cache": "^5.1.0",
"@actions/core": "^1.11.1", "@actions/core": "^2.0.3",
"@actions/exec": "^1.1.1", "@actions/exec": "^2.0.0",
"@actions/glob": "^0.5.0", "@actions/glob": "^0.5.1",
"@actions/http-client": "^2.2.1", "@actions/http-client": "^3.0.2",
"@actions/io": "^1.0.2", "@actions/io": "^2.0.0",
"@actions/tool-cache": "^2.0.2", "@actions/tool-cache": "^3.0.1",
"semver": "^7.6.3" "semver": "^7.7.3"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/node": "^24.1.0", "@types/node": "^25.9.3",
"@types/semver": "^7.5.8", "@types/semver": "^7.7.1",
"@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/eslint-plugin": "^8.61.0",
"@typescript-eslint/parser": "^8.35.1", "@typescript-eslint/parser": "^8.61.0",
"@vercel/ncc": "^0.38.1", "@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^10.0.1", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-jest": "^29.0.1", "eslint-plugin-jest": "^29.0.1",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"jest": "^29.7.0", "jest": "^29.7.0",

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

@ -15,6 +15,7 @@ process.on('uncaughtException', e => {
// Added early exit to resolve issue with slow post action step: // Added early exit to resolve issue with slow post action step:
// - https://github.com/actions/setup-node/issues/878 // - https://github.com/actions/setup-node/issues/878
// https://github.com/actions/cache/pull/1217 // https://github.com/actions/cache/pull/1217
export async function run(earlyExit?: boolean) { export async function run(earlyExit?: boolean) {
try { try {
const cacheInput = core.getBooleanInput('cache'); const cacheInput = core.getBooleanInput('cache');
@ -80,6 +81,10 @@ const cachePackages = async () => {
const cacheId = await cache.saveCache(cachePaths, primaryKey); const cacheId = await cache.saveCache(cachePaths, primaryKey);
if (cacheId === -1) { if (cacheId === -1) {
// saveCache returns -1 without throwing when the cache was not saved, e.g.
// a reserve collision or a read-only token (fork PR). @actions/cache has
// already logged the reason at the appropriate severity, so just trace it.
core.debug(`Cache was not saved for the key: ${primaryKey}`);
return; return;
} }
core.info(`Cache saved with the key: ${primaryKey}`); core.info(`Cache saved with the key: ${primaryKey}`);

View File

@ -4,6 +4,8 @@ import * as path from 'path';
import * as semver from 'semver'; import * as semver from 'semver';
import * as httpm from '@actions/http-client'; import * as httpm from '@actions/http-client';
import * as sys from './system'; import * as sys from './system';
import crypto from 'crypto';
import cp from 'child_process';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import {StableReleaseAlias, isSelfHosted} from './utils'; import {StableReleaseAlias, isSelfHosted} from './utils';
@ -15,9 +17,17 @@ const MANIFEST_REPO_OWNER = 'actions';
const MANIFEST_REPO_NAME = 'go-versions'; const MANIFEST_REPO_NAME = 'go-versions';
const MANIFEST_REPO_BRANCH = 'main'; const MANIFEST_REPO_BRANCH = 'main';
const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
const DEFAULT_GO_DOWNLOAD_BASE_URL = 'https://go.dev/dl';
type InstallationType = 'dist' | 'manifest'; type InstallationType = 'dist' | 'manifest';
const GOLANG_DOWNLOAD_URL = 'https://go.dev/dl/?mode=json&include=all';
// Base URLs known to not serve a version listing JSON endpoint.
// For these URLs we skip the getInfoFromDist() call entirely and construct
// the download URL directly, avoiding a guaranteed-404 HTTP request.
const NO_VERSION_LISTING_BASE_URLS = ['https://aka.ms/golang/release/latest'];
export interface IGoVersionFile { export interface IGoVersionFile {
filename: string; filename: string;
// darwin, linux, windows // darwin, linux, windows
@ -42,15 +52,23 @@ export async function getGo(
versionSpec: string, versionSpec: string,
checkLatest: boolean, checkLatest: boolean,
auth: string | undefined, auth: string | undefined,
arch: Architecture = os.arch() as Architecture arch: Architecture = os.arch() as Architecture,
goDownloadBaseUrl?: string
) { ) {
let manifest: tc.IToolRelease[] | undefined; let manifest: tc.IToolRelease[] | undefined;
const osPlat: string = os.platform(); const osPlat: string = os.platform();
const customBaseUrl = goDownloadBaseUrl?.replace(/\/+$/, '');
if ( if (
versionSpec === StableReleaseAlias.Stable || versionSpec === StableReleaseAlias.Stable ||
versionSpec === StableReleaseAlias.OldStable versionSpec === StableReleaseAlias.OldStable
) { ) {
if (customBaseUrl) {
throw new Error(
`Version aliases '${versionSpec}' are not supported with a custom download base URL. Please specify an exact Go version.`
);
}
manifest = await getManifest(auth); manifest = await getManifest(auth);
let stableVersion = await resolveStableVersionInput( let stableVersion = await resolveStableVersionInput(
versionSpec, versionSpec,
@ -74,7 +92,14 @@ export async function getGo(
} }
if (checkLatest) { if (checkLatest) {
core.info('Attempting to resolve the latest version from the manifest...'); if (customBaseUrl) {
core.info(
'check-latest is not supported with a custom download base URL. Using the provided version spec directly.'
);
} else {
core.info(
'Attempting to resolve the latest version from the manifest...'
);
const resolvedVersion = await resolveVersionFromManifest( const resolvedVersion = await resolveVersionFromManifest(
versionSpec, versionSpec,
true, true,
@ -89,9 +114,16 @@ export async function getGo(
core.info(`Failed to resolve version ${versionSpec} from manifest`); core.info(`Failed to resolve version ${versionSpec} from manifest`);
} }
} }
}
// Use a distinct tool cache name for custom downloads to avoid
// colliding with the runner's pre-installed Go
const toolCacheName = customBaseUrl
? customToolCacheName(customBaseUrl)
: 'go';
// check cache // check cache
const toolPath = tc.find('go', versionSpec, arch); const toolPath = tc.find(toolCacheName, versionSpec, arch);
// If not found in cache, download // If not found in cache, download
if (toolPath) { if (toolPath) {
core.info(`Found in cache @ ${toolPath}`); core.info(`Found in cache @ ${toolPath}`);
@ -101,6 +133,49 @@ export async function getGo(
let downloadPath = ''; let downloadPath = '';
let info: IGoVersionInfo | null = null; let info: IGoVersionInfo | null = null;
if (customBaseUrl) {
//
// Download from custom base URL
//
const skipVersionListing = NO_VERSION_LISTING_BASE_URLS.some(
url => customBaseUrl.toLowerCase() === url.toLowerCase()
);
if (skipVersionListing) {
core.info(
'Skipping version listing for known direct-download URL. Constructing download URL directly.'
);
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
} else {
try {
info = await getInfoFromDist(versionSpec, arch, customBaseUrl);
} catch {
core.info(
'Version listing not available from custom URL. Constructing download URL directly.'
);
}
if (!info) {
info = getInfoFromDirectDownload(versionSpec, arch, customBaseUrl);
}
}
try {
core.info('Install from custom download URL');
downloadPath = await installGoVersion(info, auth, arch, toolCacheName);
} catch (err) {
const downloadUrl = info?.downloadUrl || customBaseUrl;
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
throw new Error(
`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
`Download URL returned HTTP 404: ${downloadUrl}`
);
}
throw new Error(
`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
`from ${downloadUrl}: ${err}`
);
}
} else {
// //
// Try download from internal distribution (popular versions only) // Try download from internal distribution (popular versions only)
// //
@ -146,6 +221,7 @@ export async function getGo(
throw new Error(`Failed to download version ${versionSpec}: ${err}`); throw new Error(`Failed to download version ${versionSpec}: ${err}`);
} }
} }
}
return downloadPath; return downloadPath;
} }
@ -227,20 +303,26 @@ async function cacheWindowsDir(
async function addExecutablesToToolCache( async function addExecutablesToToolCache(
extPath: string, extPath: string,
info: IGoVersionInfo, info: IGoVersionInfo,
arch: string arch: string,
toolName: string = 'go'
): Promise<string> { ): Promise<string> {
const tool = 'go';
const version = makeSemver(info.resolvedVersion); const version = makeSemver(info.resolvedVersion);
return ( return (
(await cacheWindowsDir(extPath, tool, version, arch)) || (await cacheWindowsDir(extPath, toolName, version, arch)) ||
(await tc.cacheDir(extPath, tool, version, arch)) (await tc.cacheDir(extPath, toolName, version, arch))
); );
} }
export function customToolCacheName(baseUrl: string): string {
const hash = crypto.createHash('sha256').update(baseUrl).digest('hex');
return `go-${hash.substring(0, 8)}`;
}
async function installGoVersion( async function installGoVersion(
info: IGoVersionInfo, info: IGoVersionInfo,
auth: string | undefined, auth: string | undefined,
arch: string arch: string,
toolName: string = 'go'
): Promise<string> { ): Promise<string> {
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`); core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
@ -258,13 +340,48 @@ async function installGoVersion(
extPath = path.join(extPath, 'go'); extPath = path.join(extPath, 'go');
} }
// For custom downloads, detect the actual installed version so the cache
// key reflects the real patch level (e.g. input "1.20" may install 1.20.14).
if (toolName !== 'go') {
const actualVersion = detectInstalledGoVersion(extPath);
if (actualVersion && actualVersion !== info.resolvedVersion) {
core.info(
`Requested version '${info.resolvedVersion}' resolved to installed version '${actualVersion}'`
);
info.resolvedVersion = actualVersion;
}
}
core.info('Adding to the cache ...'); core.info('Adding to the cache ...');
const toolCacheDir = await addExecutablesToToolCache(extPath, info, arch); const toolCacheDir = await addExecutablesToToolCache(
extPath,
info,
arch,
toolName
);
core.info(`Successfully cached go to ${toolCacheDir}`); core.info(`Successfully cached go to ${toolCacheDir}`);
return toolCacheDir; return toolCacheDir;
} }
function detectInstalledGoVersion(goDir: string): string | null {
try {
const goBin = path.join(
goDir,
'bin',
os.platform() === 'win32' ? 'go.exe' : 'go'
);
const output = cp.execFileSync(goBin, ['version'], {encoding: 'utf8'});
const match = output.match(/go version go(\S+)/);
return match ? match[1] : null;
} catch (err) {
core.debug(
`Failed to detect installed Go version: ${(err as Error).message}`
);
return null;
}
}
export async function extractGoArchive(archivePath: string): Promise<string> { export async function extractGoArchive(archivePath: string): Promise<string> {
const platform = os.platform(); const platform = os.platform();
let extPath: string; let extPath: string;
@ -382,14 +499,23 @@ export async function getInfoFromManifest(
async function getInfoFromDist( async function getInfoFromDist(
versionSpec: string, versionSpec: string,
arch: Architecture arch: Architecture,
goDownloadBaseUrl?: string
): Promise<IGoVersionInfo | null> { ): Promise<IGoVersionInfo | null> {
const version: IGoVersion | undefined = await findMatch(versionSpec, arch); const dlUrl = goDownloadBaseUrl
? `${goDownloadBaseUrl}/?mode=json&include=all`
: GOLANG_DOWNLOAD_URL;
const version: IGoVersion | undefined = await findMatch(
versionSpec,
arch,
dlUrl
);
if (!version) { if (!version) {
return null; return null;
} }
const downloadUrl = `https://storage.googleapis.com/golang/${version.files[0].filename}`; const baseUrl = goDownloadBaseUrl || DEFAULT_GO_DOWNLOAD_BASE_URL;
const downloadUrl = `${baseUrl}/${version.files[0].filename}`;
return <IGoVersionInfo>{ return <IGoVersionInfo>{
type: 'dist', type: 'dist',
@ -399,9 +525,43 @@ async function getInfoFromDist(
}; };
} }
export function getInfoFromDirectDownload(
versionSpec: string,
arch: Architecture,
goDownloadBaseUrl: string
): IGoVersionInfo {
// Reject version specs that can't map to an artifact filename
if (/[~^>=<|*x]/.test(versionSpec)) {
throw new Error(
`Version range '${versionSpec}' is not supported with a custom download base URL ` +
`when version listing is unavailable. Please specify an exact version (e.g., '1.25.0').`
);
}
const archStr = sys.getArch(arch);
const platStr = sys.getPlatform();
const extension = platStr === 'windows' ? 'zip' : 'tar.gz';
// Ensure version has the 'go' prefix for the filename
const goVersion = versionSpec.startsWith('go')
? versionSpec
: `go${versionSpec}`;
const fileName = `${goVersion}.${platStr}-${archStr}.${extension}`;
const downloadUrl = `${goDownloadBaseUrl}/${fileName}`;
core.info(`Constructed direct download URL: ${downloadUrl}`);
return <IGoVersionInfo>{
type: 'dist',
downloadUrl: downloadUrl,
resolvedVersion: versionSpec.replace(/^go/, ''),
fileName: fileName
};
}
export async function findMatch( export async function findMatch(
versionSpec: string, versionSpec: string,
arch: Architecture = os.arch() as Architecture arch: Architecture = os.arch() as Architecture,
dlUrl: string = GOLANG_DOWNLOAD_URL
): Promise<IGoVersion | undefined> { ): Promise<IGoVersion | undefined> {
const archFilter = sys.getArch(arch); const archFilter = sys.getArch(arch);
const platFilter = sys.getPlatform(); const platFilter = sys.getPlatform();
@ -409,7 +569,6 @@ 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 dlUrl
); );
@ -513,6 +672,9 @@ export function parseGoVersionFile(versionFilePath: string): string {
// go directive: https://go.dev/ref/mod#go-mod-file-go // go directive: https://go.dev/ref/mod#go-mod-file-go
const matchGo = contents.match(/^go (\d+(\.\d+)*)/m); const matchGo = contents.match(/^go (\d+(\.\d+)*)/m);
return matchGo ? matchGo[1] : ''; return matchGo ? matchGo[1] : '';
} else if (path.basename(versionFilePath) === '.tool-versions') {
const match = contents.match(/^golang\s+([^\n#]+)/m);
return match ? match[1].trim() : '';
} }
return contents.trim(); return contents.trim();
@ -524,9 +686,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

@ -34,11 +34,21 @@ export async function run() {
const checkLatest = core.getBooleanInput('check-latest'); const checkLatest = core.getBooleanInput('check-latest');
const goDownloadBaseUrl =
core.getInput('go-download-base-url') ||
process.env['GO_DOWNLOAD_BASE_URL'] ||
undefined;
if (goDownloadBaseUrl) {
core.info(`Using custom Go download base URL: ${goDownloadBaseUrl}`);
}
const installDir = await installer.getGo( const installDir = await installer.getGo(
versionSpec, versionSpec,
checkLatest, checkLatest,
auth, auth,
arch arch,
goDownloadBaseUrl
); );
const installDirVersion = path.basename(path.dirname(installDir)); const installDirVersion = path.basename(path.dirname(installDir));

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']
} }
}; };