mirror of
https://gitea.com/actions/setup-go.git
synced 2026-10-07 07:13:10 +01:00
Compare commits
16 Commits
v6.4.0
...
feat/split
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad9941188f | ||
|
|
90ad2b35f6 | ||
|
|
0464ec84ca | ||
|
|
3bfff43db6 | ||
|
|
e626ada899 | ||
|
|
468e940882 | ||
|
|
34829cb9ac | ||
|
|
addc0880c9 | ||
|
|
fba3fb4ead | ||
|
|
5064d95ba1 | ||
|
|
b7ad1dad31 | ||
|
|
0778a10ce4 | ||
|
|
924ae3a1cd | ||
|
|
e91cc3bfe0 | ||
|
|
4a2405e6ae | ||
|
|
78961f6f84 |
@@ -1,6 +0,0 @@
|
|||||||
# Ignore list
|
|
||||||
/*
|
|
||||||
|
|
||||||
# Do not ignore these folders:
|
|
||||||
!__tests__/
|
|
||||||
!src/
|
|
||||||
51
.eslintrc.js
51
.eslintrc.js
@@ -1,51 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:eslint-plugin-jest/recommended',
|
|
||||||
'eslint-config-prettier'
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-require-imports': 'error',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'ts-ignore': 'allow-with-description'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-console': 'error',
|
|
||||||
'yoda': 'error',
|
|
||||||
'prefer-const': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
destructuring: 'all'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-control-regex': 'off',
|
|
||||||
'no-constant-condition': ['error', {checkLoops: false}],
|
|
||||||
'node/no-extraneous-import': 'error'
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['**/*{test,spec}.ts'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'jest/no-standalone-expect': 'off',
|
|
||||||
'jest/no-conditional-expect': 'off',
|
|
||||||
'no-console': 'off',
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
'jest/globals': true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
8
.github/workflows/microsoft-validation.yml
vendored
8
.github/workflows/microsoft-validation.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
go-version: ['1.25', '1.24']
|
go-version: ['1.25', '1.24']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Setup Microsoft build of Go ${{ matrix.go-version }}
|
- name: Setup Microsoft build of Go ${{ matrix.go-version }}
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Setup Microsoft build of Go via environment variable
|
- name: Setup Microsoft build of Go via environment variable
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -95,7 +95,7 @@ jobs:
|
|||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
architecture: arm64
|
architecture: arm64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Setup Microsoft build of Go with architecture
|
- name: Setup Microsoft build of Go with architecture
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -116,7 +116,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Setup Microsoft build of Go with caching
|
- name: Setup Microsoft build of Go with caching
|
||||||
uses: ./
|
uses: ./
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checking out
|
- name: Checking out
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Publish
|
- name: Publish
|
||||||
id: publish
|
id: publish
|
||||||
uses: actions/publish-immutable-action@v0.0.4
|
uses: actions/publish-immutable-action@v0.0.4
|
||||||
|
|||||||
77
.github/workflows/versions.yml
vendored
77
.github/workflows/versions.yml
vendored
@@ -18,9 +18,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- 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-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go oldStable
|
- name: Setup Go oldStable
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -43,21 +43,46 @@ jobs:
|
|||||||
- name: Verify Go
|
- name: Verify Go
|
||||||
run: go version
|
run: go version
|
||||||
|
|
||||||
|
go-env-outputs:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- name: Setup Go
|
||||||
|
id: setup-go
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
go-version: stable
|
||||||
|
- name: Show Go environment outputs
|
||||||
|
run: |
|
||||||
|
echo "GOPATH: ${{ steps.setup-go.outputs.GOPATH }}"
|
||||||
|
echo "GOBIN: ${{ steps.setup-go.outputs.GOBIN }}"
|
||||||
|
echo "go-bin-path: ${{ steps.setup-go.outputs.go-bin-path }}"
|
||||||
|
echo "GOROOT: ${{ steps.setup-go.outputs.GOROOT }}"
|
||||||
|
echo "GOCACHE: ${{ steps.setup-go.outputs.GOCACHE }}"
|
||||||
|
echo "GOMODCACHE: ${{ steps.setup-go.outputs.GOMODCACHE }}"
|
||||||
|
echo "GOOS: ${{ steps.setup-go.outputs.GOOS }}"
|
||||||
|
echo "GOARCH: ${{ steps.setup-go.outputs.GOARCH }}"
|
||||||
|
echo "GOTOOLDIR: ${{ steps.setup-go.outputs.GOTOOLDIR }}"
|
||||||
|
|
||||||
aliases-arch:
|
aliases-arch:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
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-latest-large
|
- os: macos-15-intel
|
||||||
architecture: x32
|
architecture: x32
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go ${{ matrix.version }} ${{ matrix.architecture }}
|
- name: Setup Go ${{ matrix.version }} ${{ matrix.architecture }}
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -72,7 +97,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, windows-latest, ubuntu-latest, macos-latest-large]
|
os: [macos-latest, windows-latest, ubuntu-latest, macos-15-intel]
|
||||||
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 +107,7 @@ jobs:
|
|||||||
go: 1.23.2
|
go: 1.23.2
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: setup-go ${{ matrix.go }}
|
- name: setup-go ${{ matrix.go }}
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -98,10 +123,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
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@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go and check latest
|
- name: Setup Go and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -115,9 +140,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go and check latest
|
- name: Setup Go and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -131,9 +156,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go and check latest
|
- name: Setup Go and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -147,9 +172,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go and check latest
|
- name: Setup Go and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -163,9 +188,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go from .go-version file
|
- name: Setup Go from .go-version file
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -179,11 +204,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, windows-latest, ubuntu-latest, macos-latest-large]
|
os: [macos-latest, windows-latest, ubuntu-latest, macos-15-intel]
|
||||||
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@v6
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: setup-go ${{ matrix.go }}
|
- name: setup-go ${{ matrix.go }}
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -199,11 +224,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-latest, ubuntu-latest, macos-latest-large]
|
os: [windows-latest, ubuntu-latest, macos-15-intel]
|
||||||
go: [1.11.12]
|
go: [1.11.12]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: setup-go ${{ matrix.go }}
|
- name: setup-go ${{ matrix.go }}
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -219,7 +244,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
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
|
||||||
@@ -228,10 +253,10 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
architecture: x64
|
architecture: x64
|
||||||
- os: macos-latest-large
|
- os: macos-15-intel
|
||||||
architecture: x64
|
architecture: x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Setup Go and check latest
|
- name: Setup Go and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
|
|||||||
6
.github/workflows/windows-validation.yml
vendored
6
.github/workflows/windows-validation.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
cache: [false, true]
|
cache: [false, true]
|
||||||
go: [1.20.1]
|
go: [1.20.1]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- 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@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- 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@v6
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: 'Setup ${{ matrix.go }}, cache: ${{ matrix.cache }}'
|
- name: 'Setup ${{ matrix.go }}, cache: ${{ matrix.cache }}'
|
||||||
uses: ./
|
uses: ./
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ allowed:
|
|||||||
- cc0-1.0
|
- cc0-1.0
|
||||||
- unlicense
|
- unlicense
|
||||||
- 0bsd
|
- 0bsd
|
||||||
|
- blueoak-1.0.0
|
||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
npm:
|
npm:
|
||||||
- "@actions/http-client"
|
- "@actions/http-client"
|
||||||
|
- "balanced-match"
|
||||||
|
- "brace-expansion"
|
||||||
2
.licenses/npm/@actions/cache.dep.yml
generated
2
.licenses/npm/@actions/cache.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/cache"
|
name: "@actions/cache"
|
||||||
version: 5.0.1
|
version: 6.3.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions cache lib
|
summary: Actions cache lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/cache
|
homepage: https://github.com/actions/toolkit/tree/main/packages/cache
|
||||||
|
|||||||
20
.licenses/npm/@actions/core-1.11.1.dep.yml
generated
20
.licenses/npm/@actions/core-1.11.1.dep.yml
generated
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@actions/core"
|
|
||||||
version: 1.11.1
|
|
||||||
type: npm
|
|
||||||
summary: Actions core lib
|
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE.md
|
|
||||||
text: |-
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright 2019 GitHub
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/core"
|
name: "@actions/core"
|
||||||
version: 2.0.1
|
version: 3.0.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions core lib
|
summary: Actions core lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
||||||
20
.licenses/npm/@actions/exec-2.0.0.dep.yml
generated
20
.licenses/npm/@actions/exec-2.0.0.dep.yml
generated
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@actions/exec"
|
|
||||||
version: 2.0.0
|
|
||||||
type: npm
|
|
||||||
summary: Actions exec lib
|
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE.md
|
|
||||||
text: |-
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright 2019 GitHub
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/exec"
|
name: "@actions/exec"
|
||||||
version: 1.1.1
|
version: 3.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions exec lib
|
summary: Actions exec lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
||||||
2
.licenses/npm/@actions/glob.dep.yml
generated
2
.licenses/npm/@actions/glob.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/glob"
|
name: "@actions/glob"
|
||||||
version: 0.5.0
|
version: 0.7.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions glob lib
|
summary: Actions glob lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/glob
|
homepage: https://github.com/actions/toolkit/tree/main/packages/glob
|
||||||
|
|||||||
32
.licenses/npm/@actions/http-client-2.2.3.dep.yml
generated
32
.licenses/npm/@actions/http-client-2.2.3.dep.yml
generated
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@actions/http-client"
|
|
||||||
version: 2.2.3
|
|
||||||
type: npm
|
|
||||||
summary: Actions Http Client
|
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
Actions Http Client for Node.js
|
|
||||||
|
|
||||||
Copyright (c) GitHub, Inc.
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
||||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
||||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
||||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/http-client"
|
name: "@actions/http-client"
|
||||||
version: 3.0.2
|
version: 4.0.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions Http Client
|
summary: Actions Http Client
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
||||||
20
.licenses/npm/@actions/io-1.1.3.dep.yml
generated
20
.licenses/npm/@actions/io-1.1.3.dep.yml
generated
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@actions/io"
|
|
||||||
version: 1.1.3
|
|
||||||
type: npm
|
|
||||||
summary: Actions io lib
|
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE.md
|
|
||||||
text: |-
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright 2019 GitHub
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/io"
|
name: "@actions/io"
|
||||||
version: 2.0.0
|
version: 3.0.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions io lib
|
summary: Actions io lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
||||||
2
.licenses/npm/@actions/tool-cache.dep.yml
generated
2
.licenses/npm/@actions/tool-cache.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/tool-cache"
|
name: "@actions/tool-cache"
|
||||||
version: 2.0.2
|
version: 4.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions tool-cache lib
|
summary: Actions tool-cache lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache
|
homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache
|
||||||
|
|||||||
2
.licenses/npm/@azure/core-client.dep.yml
generated
2
.licenses/npm/@azure/core-client.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/core-client"
|
name: "@azure/core-client"
|
||||||
version: 1.10.1
|
version: 1.10.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: Core library for interfacing with AutoRest generated code
|
summary: Core library for interfacing with AutoRest generated code
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-client/
|
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-client/
|
||||||
|
|||||||
2
.licenses/npm/@azure/core-http-compat.dep.yml
generated
2
.licenses/npm/@azure/core-http-compat.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/core-http-compat"
|
name: "@azure/core-http-compat"
|
||||||
version: 2.3.1
|
version: 2.4.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Core HTTP Compatibility Library to bridge the gap between Core V1 & V2 packages.
|
summary: Core HTTP Compatibility Library to bridge the gap between Core V1 & V2 packages.
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-compat/
|
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-compat/
|
||||||
|
|||||||
4
.licenses/npm/@azure/core-rest-pipeline.dep.yml
generated
4
.licenses/npm/@azure/core-rest-pipeline.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/core-rest-pipeline"
|
name: "@azure/core-rest-pipeline"
|
||||||
version: 1.22.2
|
version: 1.23.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-rest-pipeline/
|
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/core-rest-pipeline/README.md
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
2
.licenses/npm/@azure/core-xml.dep.yml
generated
2
.licenses/npm/@azure/core-xml.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/core-xml"
|
name: "@azure/core-xml"
|
||||||
version: 1.5.0
|
version: 1.5.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Core library for interacting with XML payloads
|
summary: Core library for interacting with XML payloads
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-xml/
|
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-xml/
|
||||||
|
|||||||
4
.licenses/npm/@azure/storage-blob.dep.yml
generated
4
.licenses/npm/@azure/storage-blob.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/storage-blob"
|
name: "@azure/storage-blob"
|
||||||
version: 12.29.1
|
version: 12.31.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Microsoft Azure Storage SDK for JavaScript - Blob
|
summary: Microsoft Azure Storage SDK for JavaScript - Blob
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/
|
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-blob/README.md
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
4
.licenses/npm/@azure/storage-common.dep.yml
generated
4
.licenses/npm/@azure/storage-common.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/storage-common"
|
name: "@azure/storage-common"
|
||||||
version: 12.1.1
|
version: 12.4.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Azure Storage Common Client Library for JavaScript
|
summary: Azure Storage Common Client Library for JavaScript
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-internal-avro/
|
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-common/README.md
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
11
.licenses/npm/@nodable/entities.dep.yml
generated
Normal file
11
.licenses/npm/@nodable/entities.dep.yml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: "@nodable/entities"
|
||||||
|
version: 3.0.0
|
||||||
|
type: npm
|
||||||
|
summary: Entity parser for XML, HTML, External entites with security and NCR control
|
||||||
|
homepage:
|
||||||
|
license: mit
|
||||||
|
licenses:
|
||||||
|
- sources: README.md
|
||||||
|
text: MIT
|
||||||
|
notices: []
|
||||||
4
.licenses/npm/@typespec/ts-http-runtime.dep.yml
generated
4
.licenses/npm/@typespec/ts-http-runtime.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@typespec/ts-http-runtime"
|
name: "@typespec/ts-http-runtime"
|
||||||
version: 0.3.2
|
version: 0.3.6
|
||||||
type: npm
|
type: npm
|
||||||
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/ts-http-runtime/
|
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/ts-http-runtime/README.md
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
@@ -1,30 +1,35 @@
|
|||||||
---
|
---
|
||||||
name: "@fastify/busboy"
|
name: anynum
|
||||||
version: 2.1.1
|
version: 1.0.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: A streaming parser for HTML form data for node.js
|
summary: Normalize all Unicode decimal digits (Devanagari, Arabic, Thai, etc.) to
|
||||||
|
ASCII numerals. Zero dependencies, performance-first.
|
||||||
homepage:
|
homepage:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |-
|
text: |
|
||||||
Copyright Brian White. All rights reserved.
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Natural Intelligence
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
deal in the Software without restriction, including without limitation the
|
in the Software without restriction, including without limitation the rights
|
||||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
sell copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in all
|
||||||
all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
IN THE SOFTWARE.
|
SOFTWARE.
|
||||||
|
- sources: README.md
|
||||||
|
text: MIT
|
||||||
notices: []
|
notices: []
|
||||||
31
.licenses/npm/balanced-match.dep.yml
generated
31
.licenses/npm/balanced-match.dep.yml
generated
@@ -1,39 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: balanced-match
|
name: balanced-match
|
||||||
version: 1.0.2
|
version: 4.0.4
|
||||||
type: npm
|
type: npm
|
||||||
summary: Match balanced character pairs, like "{" and "}"
|
summary: Match balanced character pairs, like "{" and "}"
|
||||||
homepage: https://github.com/juliangruber/balanced-match
|
homepage:
|
||||||
license: mit
|
license: other
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE.md
|
- sources: LICENSE.md
|
||||||
text: |
|
text: |
|
||||||
(MIT)
|
(MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
Original code Copyright Julian Gruber <julian@juliangruber.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Port to TypeScript Copyright Isaac Z. Schlueter <i@izs.me>
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: |-
|
|
||||||
(MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
33
.licenses/npm/brace-expansion.dep.yml
generated
33
.licenses/npm/brace-expansion.dep.yml
generated
@@ -1,16 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: brace-expansion
|
name: brace-expansion
|
||||||
version: 1.1.12
|
version: 5.0.9
|
||||||
type: npm
|
type: npm
|
||||||
summary: Brace expansion as known from sh/bash
|
summary: Brace expansion as known from sh/bash
|
||||||
homepage: https://github.com/juliangruber/brace-expansion
|
homepage:
|
||||||
license: mit
|
license: other
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |
|
text: |
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
Copyright Julian Gruber <julian@juliangruber.com>
|
||||||
|
|
||||||
|
TypeScript port Copyright Isaac Z. Schlueter <i@izs.me>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,29 +24,6 @@ licenses:
|
|||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: |-
|
|
||||||
(MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
|||||||
31
.licenses/npm/concat-map.dep.yml
generated
31
.licenses/npm/concat-map.dep.yml
generated
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
name: concat-map
|
|
||||||
version: 0.0.1
|
|
||||||
type: npm
|
|
||||||
summary: concatenative mapdashery
|
|
||||||
homepage: https://github.com/substack/node-concat-map#readme
|
|
||||||
license: other
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
This software is released under the MIT license:
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
- sources: README.markdown
|
|
||||||
text: MIT
|
|
||||||
notices: []
|
|
||||||
2
.licenses/npm/fast-xml-builder.dep.yml
generated
2
.licenses/npm/fast-xml-builder.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: fast-xml-builder
|
name: fast-xml-builder
|
||||||
version: 1.0.0
|
version: 1.2.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Build XML from JSON without C/C++ based libraries
|
summary: Build XML from JSON without C/C++ based libraries
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
2
.licenses/npm/fast-xml-parser.dep.yml
generated
2
.licenses/npm/fast-xml-parser.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: fast-xml-parser
|
name: fast-xml-parser
|
||||||
version: 5.4.1
|
version: 5.10.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Validate XML, Parse XML, Build XML without C/C++ based libraries
|
summary: Validate XML, Parse XML, Build XML without C/C++ based libraries
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
35
.licenses/npm/is-unsafe.dep.yml
generated
Normal file
35
.licenses/npm/is-unsafe.dep.yml
generated
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: is-unsafe
|
||||||
|
version: 2.0.0
|
||||||
|
type: npm
|
||||||
|
summary: Zero-dependency, DOM-free, pure predicate for detecting unsafe strings across
|
||||||
|
HTML, XML, SVG, SQL, SHELL, and REGEX contexts
|
||||||
|
homepage:
|
||||||
|
license: mit
|
||||||
|
licenses:
|
||||||
|
- sources: LICENSE
|
||||||
|
text: |
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Natural Intelligence
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
- sources: README.md
|
||||||
|
text: MIT
|
||||||
|
notices: []
|
||||||
70
.licenses/npm/minimatch.dep.yml
generated
70
.licenses/npm/minimatch.dep.yml
generated
@@ -1,26 +1,66 @@
|
|||||||
---
|
---
|
||||||
name: minimatch
|
name: minimatch
|
||||||
version: 3.1.5
|
version: 10.2.5
|
||||||
type: npm
|
type: npm
|
||||||
summary: a glob matcher in javascript
|
summary: a glob matcher in javascript
|
||||||
homepage:
|
homepage:
|
||||||
license: isc
|
license: blueoak-1.0.0
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE.md
|
||||||
text: |
|
text: |
|
||||||
The ISC License
|
# Blue Oak Model License
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
Version 1.0.0
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
## Purpose
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
This license gives everyone as much permission to work with
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
this software as possible, while protecting contributors
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
from liability.
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
## Acceptance
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
In order to receive this license, you must agree to its
|
||||||
|
rules. The rules of this license are both obligations
|
||||||
|
under that agreement and conditions to your license.
|
||||||
|
You must not do anything with this software that triggers
|
||||||
|
a rule that you cannot or will not follow.
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
Each contributor licenses you to do everything with this
|
||||||
|
software that would otherwise infringe that contributor's
|
||||||
|
copyright in it.
|
||||||
|
|
||||||
|
## Notices
|
||||||
|
|
||||||
|
You must ensure that everyone who gets a copy of
|
||||||
|
any part of this software from you, with or without
|
||||||
|
changes, also gets the text of this license or a link to
|
||||||
|
<https://blueoakcouncil.org/license/1.0.0>.
|
||||||
|
|
||||||
|
## Excuse
|
||||||
|
|
||||||
|
If anyone notifies you in writing that you have not
|
||||||
|
complied with [Notices](#notices), you can keep your
|
||||||
|
license by taking all practical steps to comply within 30
|
||||||
|
days after the notice. If you do not do so, your license
|
||||||
|
ends immediately.
|
||||||
|
|
||||||
|
## Patent
|
||||||
|
|
||||||
|
Each contributor licenses you to do everything with this
|
||||||
|
software that would otherwise infringe any patent claims
|
||||||
|
they can license or become able to license.
|
||||||
|
|
||||||
|
## Reliability
|
||||||
|
|
||||||
|
No contributor can revoke this license.
|
||||||
|
|
||||||
|
## No Liability
|
||||||
|
|
||||||
|
**_As far as the law allows, this software comes as is,
|
||||||
|
without any warranty or condition, and no contributor
|
||||||
|
will be liable to anyone for any damages related to this
|
||||||
|
software or this license, under any kind of legal claim._**
|
||||||
notices: []
|
notices: []
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/abort-controller"
|
name: path-expression-matcher
|
||||||
version: 1.1.0
|
version: 1.6.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: Microsoft Azure SDK for JavaScript - Aborter
|
summary: Efficient path tracking and pattern matching for XML/JSON parsers
|
||||||
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/abort-controller/README.md
|
homepage: https://github.com/NaturalIntelligence/path-expression-matcher#readme
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |
|
text: |
|
||||||
The MIT License (MIT)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 Microsoft
|
Copyright (c) 2024
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
26
.licenses/npm/semver-6.3.1.dep.yml
generated
26
.licenses/npm/semver-6.3.1.dep.yml
generated
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
name: semver
|
|
||||||
version: 6.3.1
|
|
||||||
type: npm
|
|
||||||
summary: The semantic version parser used by npm.
|
|
||||||
homepage:
|
|
||||||
license: isc
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: semver
|
name: semver
|
||||||
version: 7.7.3
|
version: 7.8.5
|
||||||
type: npm
|
type: npm
|
||||||
summary: The semantic version parser used by npm.
|
summary: The semantic version parser used by npm.
|
||||||
homepage:
|
homepage:
|
||||||
2
.licenses/npm/strnum.dep.yml
generated
2
.licenses/npm/strnum.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: strnum
|
name: strnum
|
||||||
version: 2.1.2
|
version: 2.4.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Parse String to Number based on configuration
|
summary: Parse String to Number based on configuration
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: undici
|
name: undici
|
||||||
version: 6.23.0
|
version: 6.28.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: An HTTP/1.1 client, written from scratch for Node.js
|
summary: An HTTP/1.1 client, written from scratch for Node.js
|
||||||
homepage: https://undici.nodejs.org
|
homepage: https://undici.nodejs.org
|
||||||
12
.licenses/npm/xml-naming-0.1.0.dep.yml
generated
Normal file
12
.licenses/npm/xml-naming-0.1.0.dep.yml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: xml-naming
|
||||||
|
version: 0.1.0
|
||||||
|
type: npm
|
||||||
|
summary: Validates XML name productions — Name, NCName, QName, NMToken, NMTokens —
|
||||||
|
for XML 1.0 and 1.1
|
||||||
|
homepage:
|
||||||
|
license: mit
|
||||||
|
licenses:
|
||||||
|
- sources: README.md
|
||||||
|
text: MIT
|
||||||
|
notices: []
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
name: undici
|
name: xml-naming
|
||||||
version: 5.29.0
|
version: 0.3.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: An HTTP/1.1 client, written from scratch for Node.js
|
summary: Validates XML name productions — Name, NCName, QName, NMToken, NMTokens —
|
||||||
homepage: https://undici.nodejs.org
|
for XML 1.0 and 1.1
|
||||||
|
homepage:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |
|
text: |
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) Matteo Collina and Undici contributors
|
Copyright (c) 2026 Natural Intelligence
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
printWidth: 80,
|
|
||||||
tabWidth: 2,
|
|
||||||
useTabs: false,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'none',
|
|
||||||
bracketSpacing: false,
|
|
||||||
arrowParens: 'avoid'
|
|
||||||
};
|
|
||||||
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
14
README.md
14
README.md
@@ -9,6 +9,12 @@ This action sets up a Go environment for use in GitHub Actions by:
|
|||||||
- Optionally caching Go modules and build outputs
|
- Optionally caching Go modules and build outputs
|
||||||
- Registering problem matchers for error output
|
- Registering problem matchers for error output
|
||||||
|
|
||||||
|
|
||||||
|
## What's new in V7
|
||||||
|
|
||||||
|
The V7 edition of the action includes:
|
||||||
|
- Migrated action internals to ESM for compatibility with latest `@actions/*` packages. No changes to action inputs, outputs, or behavior.
|
||||||
|
|
||||||
## Breaking changes in V6
|
## Breaking changes in V6
|
||||||
|
|
||||||
The V6 edition of the action includes:
|
The V6 edition of the action includes:
|
||||||
@@ -29,7 +35,7 @@ See [action.yml](action.yml).
|
|||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
# Version or version range of Go to use
|
# Version or version range of Go to use
|
||||||
go-version: '1.23'
|
go-version: '1.23'
|
||||||
@@ -64,8 +70,8 @@ See [action.yml](action.yml).
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25' # The Go version to download (if necessary) and use.
|
go-version: '1.25' # The Go version to download (if necessary) and use.
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -121,6 +127,8 @@ The `cache` input is optional, and caching is enabled by default. To disable cac
|
|||||||
|
|
||||||
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 they’re located in different subdirectories. This input supports glob patterns.
|
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 they’re located in different subdirectories. This input supports glob patterns.
|
||||||
|
|
||||||
|
Module sources (`GOMODCACHE`) and build outputs (`GOCACHE`) are cached separately. On the same runner OS, module sources can be reused across Go versions and runner architectures, while build outputs remain partitioned by Go version, runner architecture, and Linux runner image. The `cache-hit` output is `true` only when both cache entries are restored on their primary keys.
|
||||||
|
|
||||||
If caching cannot be performed for any reason, the action logs a warning and continues workflow execution.
|
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.
|
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.
|
||||||
|
|||||||
@@ -1,89 +1,401 @@
|
|||||||
import * as cache from '@actions/cache';
|
/// <reference types="node" />
|
||||||
import * as core from '@actions/core';
|
|
||||||
import * as glob from '@actions/glob';
|
|
||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
import * as cacheRestore from '../src/cache-restore';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
import fs from 'fs';
|
||||||
import {PackageManagerInfo} from '../src/package-managers';
|
import path from 'path';
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
create: jest.fn(),
|
||||||
|
hashFiles: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Import real cache-utils (with mocked @actions) before mocking it
|
||||||
|
const realCacheUtils = await import('../src/cache-utils.js');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/cache-utils.js', () => ({
|
||||||
|
...realCacheUtils,
|
||||||
|
getCacheDirectoryPath: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const cacheRestore = await import('../src/cache-restore.js');
|
||||||
|
const cacheUtils = await import('../src/cache-utils.js');
|
||||||
|
const {run: saveCaches} = await import('../src/cache-save.js');
|
||||||
|
import type {CacheDirectory} from '../src/cache-utils.js';
|
||||||
|
|
||||||
|
function deferred() {
|
||||||
|
let resolve!: () => void;
|
||||||
|
const promise = new Promise<void>(done => {
|
||||||
|
resolve = done;
|
||||||
|
});
|
||||||
|
return {promise, resolve};
|
||||||
|
}
|
||||||
|
|
||||||
describe('restoreCache', () => {
|
describe('restoreCache', () => {
|
||||||
let hashFilesSpy: jest.SpyInstance;
|
const hashFilesSpy = glob.hashFiles as jest.Mock<typeof glob.hashFiles>;
|
||||||
let getCacheDirectoryPathSpy: jest.SpyInstance;
|
const getCacheDirectoryPathSpy =
|
||||||
let restoreCacheSpy: jest.SpyInstance;
|
cacheUtils.getCacheDirectoryPath as jest.Mock<
|
||||||
let infoSpy: jest.SpyInstance;
|
typeof cacheUtils.getCacheDirectoryPath
|
||||||
let setOutputSpy: jest.SpyInstance;
|
>;
|
||||||
|
const restoreCacheSpy = cache.restoreCache as jest.Mock<
|
||||||
|
typeof cache.restoreCache
|
||||||
|
>;
|
||||||
|
const saveCacheSpy = cache.saveCache as jest.Mock<typeof cache.saveCache>;
|
||||||
|
const infoSpy = core.info as jest.Mock<typeof core.info>;
|
||||||
|
const setOutputSpy = core.setOutput as jest.Mock<typeof core.setOutput>;
|
||||||
|
|
||||||
const versionSpec = '1.13.1';
|
const versionSpec = '1.13.1';
|
||||||
const packageManager = 'default';
|
const packageManager = 'default';
|
||||||
const cacheDependencyPath = 'path';
|
const cacheDependencyPath = 'path';
|
||||||
|
const modulePath = 'module_cache_directory';
|
||||||
let originalWorkspace: string | undefined;
|
const buildPath = 'build_cache_directory';
|
||||||
|
const directories: CacheDirectory[] = [
|
||||||
|
{kind: 'modules', path: modulePath},
|
||||||
|
{kind: 'build', path: buildPath}
|
||||||
|
];
|
||||||
|
const originalEnv = {...process.env};
|
||||||
|
const originalArch = Object.getOwnPropertyDescriptor(process, 'arch')!;
|
||||||
|
const originalPlatform = Object.getOwnPropertyDescriptor(
|
||||||
|
process,
|
||||||
|
'platform'
|
||||||
|
)!;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
originalWorkspace = process.env.GITHUB_WORKSPACE;
|
jest.resetAllMocks();
|
||||||
process.env.GITHUB_WORKSPACE = '/test/workspace';
|
process.env = {
|
||||||
//Arrange
|
...originalEnv,
|
||||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
GITHUB_WORKSPACE: '/test/workspace',
|
||||||
getCacheDirectoryPathSpy = jest.spyOn(cacheUtils, 'getCacheDirectoryPath');
|
RUNNER_OS: 'Linux',
|
||||||
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
|
ImageOS: 'ubuntu24'
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
};
|
||||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
Object.defineProperty(process, 'arch', {value: 'x64'});
|
||||||
|
Object.defineProperty(process, 'platform', {value: 'linux'});
|
||||||
getCacheDirectoryPathSpy.mockImplementation(
|
hashFilesSpy.mockResolvedValue('file_hash');
|
||||||
(PackageManager: PackageManagerInfo) => {
|
getCacheDirectoryPathSpy.mockResolvedValue(directories);
|
||||||
return Promise.resolve([
|
restoreCacheSpy.mockResolvedValue(undefined);
|
||||||
'cache_directory_path',
|
saveCacheSpy.mockResolvedValue(1);
|
||||||
'cache_directory_path'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
process.env.GITHUB_WORKSPACE = originalWorkspace;
|
process.env = {...originalEnv};
|
||||||
|
Object.defineProperty(process, 'arch', originalArch);
|
||||||
|
Object.defineProperty(process, 'platform', originalPlatform);
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const restore = (version = versionSpec) =>
|
||||||
|
cacheRestore.restoreCache(version, packageManager, cacheDependencyPath);
|
||||||
|
|
||||||
|
const requestedKeys = () => {
|
||||||
|
const moduleCall = restoreCacheSpy.mock.calls.find(
|
||||||
|
([paths]) => paths[0] === modulePath
|
||||||
|
);
|
||||||
|
const buildCall = restoreCacheSpy.mock.calls.find(
|
||||||
|
([paths]) => paths[0] === buildPath
|
||||||
|
);
|
||||||
|
expect(moduleCall).toBeDefined();
|
||||||
|
expect(buildCall).toBeDefined();
|
||||||
|
return {modules: moduleCall![1], build: buildCall![1]};
|
||||||
|
};
|
||||||
|
|
||||||
it('should throw if dependency file path is not valid', async () => {
|
it('should throw if dependency file path is not valid', async () => {
|
||||||
// Arrange
|
hashFilesSpy.mockResolvedValue('');
|
||||||
hashFilesSpy.mockImplementation(() => Promise.resolve(''));
|
|
||||||
// Act + Assert
|
await expect(restore()).rejects.toThrow(
|
||||||
await expect(
|
|
||||||
cacheRestore.restoreCache(
|
|
||||||
versionSpec,
|
|
||||||
packageManager,
|
|
||||||
cacheDependencyPath
|
|
||||||
)
|
|
||||||
).rejects.toThrow(
|
|
||||||
'Some specified paths were not resolved, unable to cache dependencies.'
|
'Some specified paths were not resolved, unable to cache dependencies.'
|
||||||
);
|
);
|
||||||
|
expect(restoreCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inform if cache hit is not occurred', async () => {
|
it('restores modules and build outputs as separate cache entries', async () => {
|
||||||
// Arrange
|
await restore();
|
||||||
hashFilesSpy.mockImplementation(() => Promise.resolve('file_hash'));
|
|
||||||
restoreCacheSpy.mockImplementation(() => Promise.resolve(''));
|
const keys = requestedKeys();
|
||||||
// Act + Assert
|
expect(keys.modules).not.toBe(keys.build);
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledWith([modulePath], keys.modules);
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledWith([buildPath], keys.build);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith('modules cache is not found');
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith('build cache is not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{platform: 'linux', firstFails: false},
|
||||||
|
{platform: 'linux', firstFails: true},
|
||||||
|
{platform: 'win32', firstFails: false},
|
||||||
|
{platform: 'win32', firstFails: true}
|
||||||
|
])(
|
||||||
|
'starts both restores concurrently on $platform and waits for both (first fails=$firstFails)',
|
||||||
|
async ({platform, firstFails}) => {
|
||||||
|
Object.defineProperty(process, 'platform', {value: platform});
|
||||||
|
const started = [deferred(), deferred()];
|
||||||
|
const release = [deferred(), deferred()];
|
||||||
|
let completed = false;
|
||||||
|
restoreCacheSpy.mockImplementation(async (paths, key) => {
|
||||||
|
const index = paths[0] === modulePath ? 0 : 1;
|
||||||
|
started[index].resolve();
|
||||||
|
await release[index].promise;
|
||||||
|
if (index === 0 && firstFails) {
|
||||||
|
throw new Error('Module cache unavailable');
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pending = restore().then(() => {
|
||||||
|
completed = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await Promise.all(started.map(signal => signal.promise));
|
||||||
|
release[0].resolve();
|
||||||
|
// Let the first completion propagate while the second stays blocked.
|
||||||
|
await new Promise<void>(resolve => setImmediate(resolve));
|
||||||
|
expect(completed).toBe(false);
|
||||||
|
expect(core.saveState).not.toHaveBeenCalled();
|
||||||
|
expect(setOutputSpy).not.toHaveBeenCalledWith('cache-hit', true);
|
||||||
|
} finally {
|
||||||
|
release.forEach(signal => signal.resolve());
|
||||||
|
await pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', !firstFails);
|
||||||
|
expect(core.saveState).toHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['linux', 'win32'])(
|
||||||
|
'restores overlapping directories sequentially on %s',
|
||||||
|
async platform => {
|
||||||
|
Object.defineProperty(process, 'platform', {value: platform});
|
||||||
|
getCacheDirectoryPathSpy.mockResolvedValue([
|
||||||
|
directories[0],
|
||||||
|
{kind: 'build', path: path.join(modulePath, 'build')}
|
||||||
|
]);
|
||||||
|
const firstStarted = deferred();
|
||||||
|
const releaseFirst = deferred();
|
||||||
|
restoreCacheSpy.mockImplementation(async (paths, key) => {
|
||||||
|
if (paths[0] === modulePath) {
|
||||||
|
firstStarted.resolve();
|
||||||
|
await releaseFirst.promise;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pending = restore();
|
||||||
|
try {
|
||||||
|
await firstStarted.promise;
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(1);
|
||||||
|
} finally {
|
||||||
|
releaseFirst.resolve();
|
||||||
|
await pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{modules: false, build: false},
|
||||||
|
{modules: true, build: false},
|
||||||
|
{modules: false, build: true},
|
||||||
|
{modules: true, build: true}
|
||||||
|
])(
|
||||||
|
'reports hits and saves misses independently (modules=$modules, build=$build)',
|
||||||
|
async hits => {
|
||||||
|
const state = new Map<string, string>();
|
||||||
|
(core.saveState as jest.Mock<typeof core.saveState>).mockImplementation(
|
||||||
|
(name, value) => {
|
||||||
|
state.set(name, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
(core.getState as jest.Mock<typeof core.getState>).mockImplementation(
|
||||||
|
name => state.get(name) || ''
|
||||||
|
);
|
||||||
|
(
|
||||||
|
core.getBooleanInput as jest.Mock<typeof core.getBooleanInput>
|
||||||
|
).mockReturnValue(true);
|
||||||
|
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
||||||
|
restoreCacheSpy.mockImplementation(async (paths, key) =>
|
||||||
|
hits[paths[0] === modulePath ? 'modules' : 'build'] ? key : undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
await restore();
|
||||||
|
const keys = requestedKeys();
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith(
|
||||||
|
'cache-hit',
|
||||||
|
hits.modules && hits.build
|
||||||
|
);
|
||||||
|
|
||||||
|
// Changing the Go environment later must not change the saved paths.
|
||||||
|
process.env.GOMODCACHE = 'different_module_cache';
|
||||||
|
process.env.GOCACHE = 'different_build_cache';
|
||||||
|
await saveCaches();
|
||||||
|
|
||||||
|
for (const {kind, path} of directories) {
|
||||||
|
if (hits[kind]) {
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalledWith([path], keys[kind]);
|
||||||
|
} else {
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledWith([path], keys[kind]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(
|
||||||
|
Number(!hits.modules) + Number(!hits.build)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('does not report a full hit for a non-primary cache match', async () => {
|
||||||
|
restoreCacheSpy.mockImplementation(async (_paths, key) => `${key}-other`);
|
||||||
|
|
||||||
|
await restore();
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(directories)(
|
||||||
|
'continues restoring when the $kind cache fails',
|
||||||
|
async ({kind, path}) => {
|
||||||
|
restoreCacheSpy.mockImplementation(async (paths, key) => {
|
||||||
|
if (paths[0] === path) {
|
||||||
|
throw new Error('Unable to reach the service');
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
|
||||||
|
await restore();
|
||||||
|
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
`Restore ${kind} cache failed: Unable to reach the service`
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(directories)(
|
||||||
|
'restores only $kind when the other directory cannot be resolved',
|
||||||
|
async directory => {
|
||||||
|
getCacheDirectoryPathSpy.mockResolvedValue([directory]);
|
||||||
|
restoreCacheSpy.mockImplementation(async (_paths, key) => key);
|
||||||
|
|
||||||
|
await restore();
|
||||||
|
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(restoreCacheSpy).toHaveBeenCalledWith(
|
||||||
|
[directory.path],
|
||||||
|
expect.any(String)
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenLastCalledWith('cache-hit', false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('reuses modules across Go versions without reusing build outputs', async () => {
|
||||||
|
await restore();
|
||||||
|
const before = requestedKeys();
|
||||||
|
restoreCacheSpy.mockClear();
|
||||||
|
|
||||||
|
await restore('1.26.1');
|
||||||
|
const after = requestedKeys();
|
||||||
|
|
||||||
|
expect(after.modules).toBe(before.modules);
|
||||||
|
expect(after.build).not.toBe(before.build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: 'runner architecture',
|
||||||
|
change: () => Object.defineProperty(process, 'arch', {value: 'arm64'})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'runner image',
|
||||||
|
change: () => {
|
||||||
|
process.env.ImageOS = 'ubuntu26';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])('reuses only modules when the $name changes', async ({change}) => {
|
||||||
|
await restore();
|
||||||
|
const before = requestedKeys();
|
||||||
|
restoreCacheSpy.mockClear();
|
||||||
|
|
||||||
|
change();
|
||||||
|
await restore();
|
||||||
|
const after = requestedKeys();
|
||||||
|
|
||||||
|
expect(after.modules).toBe(before.modules);
|
||||||
|
expect(after.build).not.toBe(before.build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps both cache entries isolated by runner OS', async () => {
|
||||||
|
await restore();
|
||||||
|
const before = requestedKeys();
|
||||||
|
restoreCacheSpy.mockClear();
|
||||||
|
|
||||||
|
process.env.RUNNER_OS = 'Windows';
|
||||||
|
await restore();
|
||||||
|
const after = requestedKeys();
|
||||||
|
|
||||||
|
expect(after.modules).not.toBe(before.modules);
|
||||||
|
expect(after.build).not.toBe(before.build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalidates both cache entries when the dependency hash changes', async () => {
|
||||||
|
await restore();
|
||||||
|
const before = requestedKeys();
|
||||||
|
restoreCacheSpy.mockClear();
|
||||||
|
|
||||||
|
hashFilesSpy.mockResolvedValue('different_file_hash');
|
||||||
|
await restore();
|
||||||
|
const after = requestedKeys();
|
||||||
|
|
||||||
|
expect(after.modules).not.toBe(before.modules);
|
||||||
|
expect(after.build).not.toBe(before.build);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hashes the configured dependency paths for both cache entries', async () => {
|
||||||
|
const dependencyPaths = 'go.sum\ntools/go.sum';
|
||||||
|
|
||||||
await cacheRestore.restoreCache(
|
await cacheRestore.restoreCache(
|
||||||
versionSpec,
|
versionSpec,
|
||||||
packageManager,
|
packageManager,
|
||||||
cacheDependencyPath
|
dependencyPaths
|
||||||
);
|
);
|
||||||
expect(infoSpy).toHaveBeenCalledWith('Cache is not found');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set output if cache hit is occurred', async () => {
|
expect(hashFilesSpy).toHaveBeenCalledWith(dependencyPaths);
|
||||||
// Arrange
|
expect(restoreCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
hashFilesSpy.mockImplementation(() => Promise.resolve('file_hash'));
|
|
||||||
restoreCacheSpy.mockImplementation(() => Promise.resolve('cache_key'));
|
|
||||||
// Act + Assert
|
|
||||||
await cacheRestore.restoreCache(
|
|
||||||
versionSpec,
|
|
||||||
packageManager,
|
|
||||||
cacheDependencyPath
|
|
||||||
);
|
|
||||||
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if dependency file is not found in workspace', async () => {
|
it('should throw if dependency file is not found in workspace', async () => {
|
||||||
|
|||||||
306
__tests__/cache-save.test.ts
Normal file
306
__tests__/cache-save.test.ts
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
|
||||||
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
saveState: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realFs = (await import('fs')).default;
|
||||||
|
const fsExports = {...realFs, existsSync: jest.fn()};
|
||||||
|
jest.unstable_mockModule('fs', () => ({...fsExports, default: fsExports}));
|
||||||
|
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const fs = (await import('fs')).default;
|
||||||
|
const {run} = await import('../src/cache-save.js');
|
||||||
|
import type {CacheEntry} from '../src/cache-utils.js';
|
||||||
|
|
||||||
|
function deferred() {
|
||||||
|
let resolve!: () => void;
|
||||||
|
const promise = new Promise<void>(done => {
|
||||||
|
resolve = done;
|
||||||
|
});
|
||||||
|
return {promise, resolve};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('cache-save', () => {
|
||||||
|
let entries: CacheEntry[];
|
||||||
|
|
||||||
|
let getBooleanInputSpy: jest.Mock<typeof core.getBooleanInput>;
|
||||||
|
let getStateSpy: jest.Mock<typeof core.getState>;
|
||||||
|
let infoSpy: jest.Mock<typeof core.info>;
|
||||||
|
let warningSpy: jest.Mock<typeof core.warning>;
|
||||||
|
let debugSpy: jest.Mock<typeof core.debug>;
|
||||||
|
let setFailedSpy: jest.Mock<typeof core.setFailed>;
|
||||||
|
let saveCacheSpy: jest.Mock<typeof cache.saveCache>;
|
||||||
|
let existsSpy: jest.Mock<typeof fs.existsSync>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
entries = [
|
||||||
|
{
|
||||||
|
kind: 'modules',
|
||||||
|
path: 'module_cache_directory',
|
||||||
|
primaryKey: 'modules-primary-key'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'build',
|
||||||
|
path: 'build_cache_directory',
|
||||||
|
primaryKey: 'build-primary-key'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
getBooleanInputSpy = core.getBooleanInput as jest.Mock<
|
||||||
|
typeof core.getBooleanInput
|
||||||
|
>;
|
||||||
|
getBooleanInputSpy.mockReturnValue(true);
|
||||||
|
|
||||||
|
getStateSpy = core.getState as jest.Mock<typeof core.getState>;
|
||||||
|
getStateSpy.mockImplementation(() => JSON.stringify(entries));
|
||||||
|
|
||||||
|
infoSpy = core.info as jest.Mock<typeof core.info>;
|
||||||
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
warningSpy = core.warning as jest.Mock<typeof core.warning>;
|
||||||
|
warningSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
debugSpy = core.debug as jest.Mock<typeof core.debug>;
|
||||||
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
setFailedSpy = core.setFailed as jest.Mock<typeof core.setFailed>;
|
||||||
|
setFailedSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
saveCacheSpy = cache.saveCache as jest.Mock<typeof cache.saveCache>;
|
||||||
|
saveCacheSpy.mockImplementation(() => Promise.resolve(0));
|
||||||
|
|
||||||
|
existsSpy = fs.existsSync as jest.Mock<typeof fs.existsSync>;
|
||||||
|
existsSpy.mockImplementation(() => true);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
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.each([false, true])(
|
||||||
|
'starts both saves concurrently and waits before exiting (first fails=%s)',
|
||||||
|
async firstFails => {
|
||||||
|
const started = [deferred(), deferred()];
|
||||||
|
const release = [deferred(), deferred()];
|
||||||
|
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
|
||||||
|
return undefined as never;
|
||||||
|
});
|
||||||
|
saveCacheSpy.mockImplementation(async paths => {
|
||||||
|
const index = paths[0] === entries[0].path ? 0 : 1;
|
||||||
|
started[index].resolve();
|
||||||
|
await release[index].promise;
|
||||||
|
if (index === 0 && firstFails) {
|
||||||
|
throw new Error('Module cache unavailable');
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pending = run(true);
|
||||||
|
try {
|
||||||
|
await Promise.all(started.map(signal => signal.promise));
|
||||||
|
release[0].resolve();
|
||||||
|
// Let the first completion propagate while the second stays blocked.
|
||||||
|
await new Promise<void>(resolve => setImmediate(resolve));
|
||||||
|
expect(exitSpy).not.toHaveBeenCalled();
|
||||||
|
} finally {
|
||||||
|
release.forEach(signal => signal.resolve());
|
||||||
|
await pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exitSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache saved with the key: ${entries[1].primaryKey}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('does not save cache when there are no cache folders on the disk', async () => {
|
||||||
|
existsSpy.mockImplementation(() => false);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
for (const {kind, path} of entries) {
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`[warning]${kind} cache folder doesn't exist on disk: ${path}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(['modules', 'build'])(
|
||||||
|
'saves the other cache when the %s directory is missing',
|
||||||
|
async kind => {
|
||||||
|
const missing = entries.find(entry => entry.kind === kind)!;
|
||||||
|
const existing = entries.find(entry => entry.kind !== kind)!;
|
||||||
|
existsSpy.mockImplementation(path => path !== missing.path);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledWith(
|
||||||
|
[existing.path],
|
||||||
|
existing.primaryKey
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('does not save cache when no cache entries were prepared', async () => {
|
||||||
|
getStateSpy.mockReturnValue('');
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
'Cache keys were not generated. Please check the log messages above for more errors or information'
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not fail the workflow if the saved state is invalid', async () => {
|
||||||
|
getStateSpy.mockReturnValue('{');
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(expect.any(String));
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not save either cache when both primary keys were hit', async () => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
entry.matchedKey = entry.primaryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(['modules', 'build'])(
|
||||||
|
'skips only the %s cache when its primary key was hit',
|
||||||
|
async kind => {
|
||||||
|
const hit = entries.find(entry => entry.kind === kind)!;
|
||||||
|
const miss = entries.find(entry => entry.kind !== kind)!;
|
||||||
|
hit.matchedKey = hit.primaryKey;
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledWith([miss.path], miss.primaryKey);
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('saves both cache directories under separate keys on a cold run', async () => {
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
for (const {path, primaryKey} of entries) {
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledWith([path], primaryKey);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache saved with the key: ${primaryKey}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves caches restored from non-primary matches', async () => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
entry.matchedKey = `${entry.primaryKey}-other`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(['modules', 'build'])(
|
||||||
|
'continues saving when the %s cache reservation fails',
|
||||||
|
async kind => {
|
||||||
|
const failed = entries.find(entry => entry.kind === kind)!;
|
||||||
|
const saved = entries.find(entry => entry.kind !== kind)!;
|
||||||
|
saveCacheSpy.mockImplementation(async paths =>
|
||||||
|
paths[0] === failed.path ? -1 : 1
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(debugSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache was not saved for the key: ${failed.primaryKey}`
|
||||||
|
);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache saved with the key: ${failed.primaryKey}`
|
||||||
|
);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache saved with the key: ${saved.primaryKey}`
|
||||||
|
);
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['modules', 'build'])(
|
||||||
|
'continues saving when the %s cache upload throws',
|
||||||
|
async kind => {
|
||||||
|
const failed = entries.find(entry => entry.kind === kind)!;
|
||||||
|
const saved = entries.find(entry => entry.kind !== kind)!;
|
||||||
|
saveCacheSpy.mockImplementation(async paths => {
|
||||||
|
if (paths[0] === failed.path) {
|
||||||
|
throw new Error('Unable to reach the service');
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
`Save ${kind} cache failed: Unable to reach the service`
|
||||||
|
);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache saved with the key: ${saved.primaryKey}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,22 +1,70 @@
|
|||||||
import * as exec from '@actions/exec';
|
import {
|
||||||
import * as cache from '@actions/cache';
|
jest,
|
||||||
import * as core from '@actions/core';
|
describe,
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
it,
|
||||||
import {PackageManagerInfo} from '../src/package-managers';
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cacheUtils = await import('../src/cache-utils.js');
|
||||||
|
import type {PackageManagerInfo} from '../src/package-managers.js';
|
||||||
|
|
||||||
describe('getCommandOutput', () => {
|
describe('getCommandOutput', () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
const getExecOutputSpy = exec.getExecOutput as jest.Mock<
|
||||||
|
typeof exec.getExecOutput
|
||||||
|
>;
|
||||||
|
|
||||||
it('should return trimmed stdout in case of successful exit code', async () => {
|
it('should return trimmed stdout in case of successful exit code', async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const stdoutResult = ' stdout ';
|
const stdoutResult = ' stdout ';
|
||||||
const trimmedStdout = stdoutResult.trim();
|
const trimmedStdout = stdoutResult.trim();
|
||||||
|
|
||||||
getExecOutputSpy.mockImplementation((commandLine: string) => {
|
getExecOutputSpy.mockImplementation(async (commandLine: string) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
return {exitCode: 0, stdout: stdoutResult, stderr: ''};
|
||||||
resolve({exitCode: 0, stdout: stdoutResult, stderr: ''});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
@@ -29,10 +77,8 @@ describe('getCommandOutput', () => {
|
|||||||
//Arrange
|
//Arrange
|
||||||
const stderrResult = 'error message';
|
const stderrResult = 'error message';
|
||||||
|
|
||||||
getExecOutputSpy.mockImplementation((commandLine: string) => {
|
getExecOutputSpy.mockImplementation(async (commandLine: string) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
return {exitCode: 10, stdout: '', stderr: stderrResult};
|
||||||
resolve({exitCode: 10, stdout: '', stderr: stderrResult});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
@@ -48,7 +94,10 @@ describe('getPackageManagerInfo', () => {
|
|||||||
const packageManagerName = 'default';
|
const packageManagerName = 'default';
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
dependencyFilePattern: 'go.mod',
|
dependencyFilePattern: 'go.mod',
|
||||||
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
|
cacheFolderCommandList: [
|
||||||
|
{kind: 'modules', command: 'go env GOMODCACHE'},
|
||||||
|
{kind: 'build', command: 'go env GOCACHE'}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
@@ -70,22 +119,35 @@ describe('getPackageManagerInfo', () => {
|
|||||||
|
|
||||||
describe('getCacheDirectoryPath', () => {
|
describe('getCacheDirectoryPath', () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
const getExecOutputSpy = exec.getExecOutput as jest.Mock<
|
||||||
|
typeof exec.getExecOutput
|
||||||
|
>;
|
||||||
|
|
||||||
const validPackageManager: PackageManagerInfo = {
|
const validPackageManager: PackageManagerInfo = {
|
||||||
dependencyFilePattern: 'go.mod',
|
dependencyFilePattern: 'go.mod',
|
||||||
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
|
cacheFolderCommandList: [
|
||||||
|
{kind: 'modules', command: 'go env GOMODCACHE'},
|
||||||
|
{kind: 'build', command: 'go env GOCACHE'}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should return path to the cache folders which specified package manager uses', async () => {
|
it('should return path to the cache folders which specified package manager uses', async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
getExecOutputSpy.mockImplementation((commandLine: string) => {
|
getExecOutputSpy.mockImplementation(async (commandLine: string) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
return {
|
||||||
resolve({exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''});
|
exitCode: 0,
|
||||||
});
|
stdout:
|
||||||
|
commandLine === 'go env GOMODCACHE'
|
||||||
|
? 'path/to/modules'
|
||||||
|
: 'path/to/build',
|
||||||
|
stderr: ''
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectedResult = ['path/to/cache/folder', 'path/to/cache/folder'];
|
const expectedResult = [
|
||||||
|
{kind: 'modules', path: 'path/to/modules'},
|
||||||
|
{kind: 'build', path: 'path/to/build'}
|
||||||
|
];
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
return cacheUtils
|
return cacheUtils
|
||||||
@@ -93,33 +155,43 @@ describe('getCacheDirectoryPath', () => {
|
|||||||
.then(data => expect(data).toEqual(expectedResult));
|
.then(data => expect(data).toEqual(expectedResult));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return path to the cache folder if one command return empty str', async () => {
|
it.each(validPackageManager.cacheFolderCommandList)(
|
||||||
//Arrange
|
'preserves the $kind identity when the other command returns an empty path',
|
||||||
getExecOutputSpy.mockImplementationOnce((commandLine: string) => {
|
async ({kind, command}) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
getExecOutputSpy.mockImplementation(async commandLine => ({
|
||||||
resolve({exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''});
|
exitCode: 0,
|
||||||
});
|
stdout: commandLine === command ? 'path/to/cache/folder' : '',
|
||||||
|
stderr: ''
|
||||||
|
}));
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
cacheUtils.getCacheDirectoryPath(validPackageManager)
|
||||||
|
).resolves.toEqual([{kind, path: 'path/to/cache/folder'}]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(validPackageManager.cacheFolderCommandList)(
|
||||||
|
'preserves the $kind identity when the other command fails',
|
||||||
|
async ({kind, command}) => {
|
||||||
|
getExecOutputSpy.mockImplementation(async commandLine => {
|
||||||
|
if (commandLine !== command) {
|
||||||
|
throw new Error('Could not run go env');
|
||||||
|
}
|
||||||
|
return {exitCode: 0, stdout: 'path/to/cache/folder', stderr: ''};
|
||||||
});
|
});
|
||||||
|
|
||||||
getExecOutputSpy.mockImplementationOnce((commandLine: string) => {
|
await expect(
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
cacheUtils.getCacheDirectoryPath(validPackageManager)
|
||||||
resolve({exitCode: 0, stdout: '', stderr: ''});
|
).resolves.toEqual([{kind, path: 'path/to/cache/folder'}]);
|
||||||
});
|
expect(core.info).toHaveBeenCalledWith(
|
||||||
});
|
expect.stringContaining('Could not run go env')
|
||||||
|
);
|
||||||
const expectedResult = ['path/to/cache/folder'];
|
}
|
||||||
|
);
|
||||||
//Act + Assert
|
|
||||||
return cacheUtils
|
|
||||||
.getCacheDirectoryPath(validPackageManager)
|
|
||||||
.then(data => expect(data).toEqual(expectedResult));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if the both commands return empty str', async () => {
|
it('should throw if the both commands return empty str', async () => {
|
||||||
getExecOutputSpy.mockImplementation((commandLine: string) => {
|
getExecOutputSpy.mockImplementation(async (commandLine: string) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
return {exitCode: 0, stdout: '', stderr: ''};
|
||||||
resolve({exitCode: 10, stdout: '', stderr: ''});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
@@ -128,11 +200,9 @@ describe('getCacheDirectoryPath', () => {
|
|||||||
}).rejects.toThrow();
|
}).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the specified package name is invalid', async () => {
|
it('should throw if both commands fail', async () => {
|
||||||
getExecOutputSpy.mockImplementation((commandLine: string) => {
|
getExecOutputSpy.mockImplementation(async (commandLine: string) => {
|
||||||
return new Promise<exec.ExecOutput>(resolve => {
|
return {exitCode: 10, stdout: '', stderr: 'Error message'};
|
||||||
resolve({exitCode: 10, stdout: '', stderr: 'Error message'});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Act + Assert
|
//Act + Assert
|
||||||
@@ -142,10 +212,145 @@ describe('getCacheDirectoryPath', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('canRestoreCachesInParallel', () => {
|
||||||
|
const originalPlatform = Object.getOwnPropertyDescriptor(
|
||||||
|
process,
|
||||||
|
'platform'
|
||||||
|
)!;
|
||||||
|
const directoryStat = fs.lstatSync(process.cwd());
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Object.defineProperty(process, 'platform', {value: 'linux'});
|
||||||
|
jest.spyOn(fs, 'lstatSync').mockReturnValue(directoryStat);
|
||||||
|
jest
|
||||||
|
.spyOn(fs, 'realpathSync')
|
||||||
|
.mockImplementation(directory => path.resolve(directory.toString()));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
Object.defineProperty(process, 'platform', originalPlatform);
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{modules: 'cache/modules', build: 'cache/build', parallel: true},
|
||||||
|
{modules: 'cache', build: 'cache-build', parallel: true},
|
||||||
|
{modules: 'cache', build: 'cache', parallel: false},
|
||||||
|
{modules: 'cache', build: 'cache/build', parallel: false},
|
||||||
|
{modules: 'cache/modules', build: 'cache', parallel: false},
|
||||||
|
{modules: 'cache', build: 'cache/../cache', parallel: false}
|
||||||
|
])(
|
||||||
|
'checks overlapping paths: $modules and $build',
|
||||||
|
({modules, build, parallel}) => {
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: modules},
|
||||||
|
{kind: 'build', path: build}
|
||||||
|
])
|
||||||
|
).toBe(parallel);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('allows concurrent Windows restores with disjoint paths', () => {
|
||||||
|
Object.defineProperty(process, 'platform', {value: 'win32'});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: 'cache/modules'},
|
||||||
|
{kind: 'build', path: 'cache/build'}
|
||||||
|
])
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(['darwin', 'win32'])(
|
||||||
|
'handles case-insensitive paths conservatively on %s',
|
||||||
|
platform => {
|
||||||
|
Object.defineProperty(process, 'platform', {value: platform});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: 'cache'},
|
||||||
|
{kind: 'build', path: 'CACHE/build'}
|
||||||
|
])
|
||||||
|
).toBe(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([true, false])(
|
||||||
|
'serializes symlink aliases with missing cache directories (overlap=%s)',
|
||||||
|
overlap => {
|
||||||
|
const alias = path.resolve('alias');
|
||||||
|
const target = path.resolve('target');
|
||||||
|
jest.spyOn(fs, 'lstatSync').mockImplementation(directory => {
|
||||||
|
if (directory !== alias && directory !== target) {
|
||||||
|
throw Object.assign(new Error('Missing path'), {code: 'ENOENT'});
|
||||||
|
}
|
||||||
|
return directoryStat;
|
||||||
|
});
|
||||||
|
jest.spyOn(fs, 'realpathSync').mockReturnValue(target);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: path.join(alias, 'modules')},
|
||||||
|
{
|
||||||
|
kind: 'build',
|
||||||
|
path: path.join(target, overlap ? 'modules/build' : 'build')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
).toBe(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['nested alias', 'alias through module cache'])(
|
||||||
|
'serializes restores when the %s resolves outside the module cache',
|
||||||
|
scenario => {
|
||||||
|
const modules = path.resolve('modules');
|
||||||
|
const target = path.resolve('external-build');
|
||||||
|
const alias = path.join(modules, 'build-link');
|
||||||
|
const build =
|
||||||
|
scenario === 'nested alias'
|
||||||
|
? path.join(alias, 'cache')
|
||||||
|
: path.resolve('build-alias', 'cache');
|
||||||
|
jest.spyOn(fs, 'realpathSync').mockImplementation(directory => {
|
||||||
|
const absolute = path.resolve(directory.toString());
|
||||||
|
if (absolute === build) {
|
||||||
|
return path.join(target, 'cache');
|
||||||
|
}
|
||||||
|
if (absolute === path.dirname(build)) {
|
||||||
|
return scenario === 'nested alias' ? target : alias;
|
||||||
|
}
|
||||||
|
return absolute;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: modules},
|
||||||
|
{kind: 'build', path: build}
|
||||||
|
])
|
||||||
|
).toBe(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('falls back to sequential restores when path resolution fails', () => {
|
||||||
|
jest.spyOn(fs, 'realpathSync').mockImplementation(() => {
|
||||||
|
throw new Error('Cannot resolve path');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
cacheUtils.canRestoreCachesInParallel([
|
||||||
|
{kind: 'modules', path: 'cache/modules'},
|
||||||
|
{kind: 'build', path: 'cache/build'}
|
||||||
|
])
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('isCacheFeatureAvailable', () => {
|
describe('isCacheFeatureAvailable', () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const isFeatureAvailableSpy = jest.spyOn(cache, 'isFeatureAvailable');
|
const isFeatureAvailableSpy = cache.isFeatureAvailable as jest.Mock<
|
||||||
const warningSpy = jest.spyOn(core, 'warning');
|
typeof cache.isFeatureAvailable
|
||||||
|
>;
|
||||||
|
const warningSpy = core.warning as jest.Mock<typeof core.warning>;
|
||||||
|
|
||||||
it('should return true when cache feature is available', () => {
|
it('should return true when cache feature is available', () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
@@ -211,10 +416,9 @@ describe('isCacheFeatureAvailable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('isGhes', () => {
|
describe('isGhes', () => {
|
||||||
const pristineEnv = process.env;
|
const pristineEnv = {...process.env};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules();
|
|
||||||
process.env = {...pristineEnv};
|
process.env = {...pristineEnv};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,101 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm, {type} from 'os';
|
|
||||||
import path from 'path';
|
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as im from '../src/installer';
|
|
||||||
import * as httpm from '@actions/http-client';
|
|
||||||
|
|
||||||
import goJsonData from './data/golang-dl.json';
|
import goJsonData from './data/golang-dl.json' with {type: 'json'};
|
||||||
import matchers from '../matchers.json';
|
import matchers from '../matchers.json' with {type: 'json'};
|
||||||
import goTestManifest from './data/versions-manifest.json';
|
import goTestManifest from './data/versions-manifest.json' with {type: 'json'};
|
||||||
|
|
||||||
|
import type {IGoVersion} from '../src/installer.js';
|
||||||
|
import type {IToolRelease} from '@actions/tool-cache';
|
||||||
|
import {GO_ENV_OUTPUTS} from '../src/constants.js';
|
||||||
|
|
||||||
|
const httpClientGetJson = jest.fn();
|
||||||
|
|
||||||
|
const realCore = await import('@actions/core');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
...realCore,
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
exportVariable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realOs = (await import('os')).default;
|
||||||
|
const osPlatformMock = jest.fn();
|
||||||
|
const osArchMock = jest.fn();
|
||||||
|
const osExports = {
|
||||||
|
...realOs,
|
||||||
|
platform: osPlatformMock,
|
||||||
|
arch: osArchMock
|
||||||
|
};
|
||||||
|
jest.unstable_mockModule('os', () => ({...osExports, default: osExports}));
|
||||||
|
|
||||||
|
const realPath = (await import('path')).default;
|
||||||
|
const pathJoinMock = jest.fn();
|
||||||
|
const pathExports = {
|
||||||
|
...realPath,
|
||||||
|
join: pathJoinMock
|
||||||
|
};
|
||||||
|
jest.unstable_mockModule('path', () => ({
|
||||||
|
...pathExports,
|
||||||
|
default: pathExports
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realTc = await import('@actions/tool-cache');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
...realTc,
|
||||||
|
find: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realHttp = await import('@actions/http-client');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
...realHttp,
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: httpClientGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/go-version-fetch.js', () => ({
|
||||||
|
getVersionsDist: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const vf = await import('../src/go-version-fetch.js');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const im = await import('../src/installer.js');
|
||||||
|
const osm = (await import('os')).default;
|
||||||
|
const path = (await import('path')).default;
|
||||||
const matcherPattern = matchers.problemMatcher[0].pattern[0];
|
const matcherPattern = matchers.problemMatcher[0].pattern[0];
|
||||||
const matcherRegExp = new RegExp(matcherPattern.regexp);
|
const matcherRegExp = new RegExp(matcherPattern.regexp);
|
||||||
const win32Join = path.win32.join;
|
const win32Join = path.win32.join;
|
||||||
@@ -23,32 +107,33 @@ describe('setup-go', () => {
|
|||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock<typeof core.getInput>;
|
||||||
let getBooleanInputSpy: jest.SpyInstance;
|
let getBooleanInputSpy: jest.Mock<typeof core.getBooleanInput>;
|
||||||
let exportVarSpy: jest.SpyInstance;
|
let exportVarSpy: jest.Mock<typeof core.exportVariable>;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let getSpy: jest.SpyInstance;
|
let setOutputSpy: jest.Mock<typeof core.setOutput>;
|
||||||
let platSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock<typeof core.warning>;
|
||||||
let archSpy: jest.SpyInstance;
|
let getSpy: jest.Mock;
|
||||||
let joinSpy: jest.SpyInstance;
|
let platSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let archSpy: jest.Mock;
|
||||||
let extractTarSpy: jest.SpyInstance;
|
let joinSpy: jest.Mock<typeof path.join>;
|
||||||
let extractZipSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let extractTarSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let extractZipSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let readFileSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let mkdirSpy: jest.SpyInstance;
|
let readFileSpy: jest.SpiedFunction<typeof fs.readFileSync>;
|
||||||
let symlinkSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let execSpy: jest.SpyInstance;
|
let mkdirSpy: jest.SpiedFunction<typeof fs.mkdir>;
|
||||||
let execFileSpy: jest.SpyInstance;
|
let symlinkSpy: jest.SpiedFunction<typeof fs.symlinkSync>;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let getAllVersionsSpy: jest.SpyInstance;
|
let execFileSpy: jest.SpiedFunction<typeof cp.execFileSync>;
|
||||||
let httpmGetJsonSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
|
let httpmGetJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
|
process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
|
||||||
@@ -59,17 +144,19 @@ describe('setup-go', () => {
|
|||||||
|
|
||||||
// @actions/core
|
// @actions/core
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock<typeof core.getInput>;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
|
getBooleanInputSpy = core.getBooleanInput as jest.Mock<
|
||||||
|
typeof core.getBooleanInput
|
||||||
|
>;
|
||||||
getBooleanInputSpy.mockImplementation(name => inputs[name]);
|
getBooleanInputSpy.mockImplementation(name => inputs[name]);
|
||||||
exportVarSpy = jest.spyOn(core, 'exportVariable');
|
exportVarSpy = core.exportVariable as jest.Mock<typeof core.exportVariable>;
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
platSpy = jest.spyOn(osm, 'platform');
|
platSpy = osm.platform as jest.Mock;
|
||||||
platSpy.mockImplementation(() => os['platform']);
|
platSpy.mockImplementation(() => os['platform']);
|
||||||
archSpy = jest.spyOn(osm, 'arch');
|
archSpy = osm.arch as jest.Mock;
|
||||||
archSpy.mockImplementation(() => os['arch']);
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
execFileSpy = jest.spyOn(cp, 'execFileSync');
|
execFileSpy = jest.spyOn(cp, 'execFileSync');
|
||||||
@@ -78,7 +165,7 @@ describe('setup-go', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// switch path join behaviour based on set os.platform
|
// switch path join behaviour based on set os.platform
|
||||||
joinSpy = jest.spyOn(path, 'join');
|
joinSpy = path.join as jest.Mock<typeof path.join>;
|
||||||
joinSpy.mockImplementation((...paths: string[]): string => {
|
joinSpy.mockImplementation((...paths: string[]): string => {
|
||||||
if (os['platform'] == 'win32') {
|
if (os['platform'] == 'win32') {
|
||||||
return win32Join(...paths);
|
return win32Join(...paths);
|
||||||
@@ -88,23 +175,22 @@ describe('setup-go', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
extractTarSpy = jest.spyOn(tc, 'extractTar');
|
extractTarSpy = tc.extractTar as jest.Mock;
|
||||||
extractZipSpy = jest.spyOn(tc, 'extractZip');
|
extractZipSpy = tc.extractZip as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getSpy = jest.spyOn(im, 'getVersionsDist');
|
getSpy = vf.getVersionsDist as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
getAllVersionsSpy = jest.spyOn(im, 'getManifest');
|
|
||||||
|
|
||||||
// httm
|
// httm
|
||||||
httpmGetJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
httpmGetJsonSpy = httpClientGetJson;
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
readFileSpy = jest.spyOn(fs, 'readFileSync');
|
readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// fs
|
// fs
|
||||||
mkdirSpy = jest.spyOn(fs, 'mkdir');
|
mkdirSpy = jest.spyOn(fs, 'mkdir');
|
||||||
@@ -112,25 +198,18 @@ describe('setup-go', () => {
|
|||||||
symlinkSpy.mockImplementation(() => {});
|
symlinkSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
// gets
|
// gets
|
||||||
getManifestSpy.mockImplementation(() => <tc.IToolRelease[]>goTestManifest);
|
getManifestSpy.mockImplementation(() => goTestManifest as IToolRelease[]);
|
||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
setOutputSpy = core.setOutput as jest.Mock<typeof core.setOutput>;
|
||||||
getSpy.mockImplementation(() => <im.IGoVersion[] | null>goJsonData);
|
warningSpy = core.warning as jest.Mock<typeof core.warning>;
|
||||||
cnSpy.mockImplementation(line => {
|
dbgSpy = core.debug as jest.Mock;
|
||||||
// uncomment to debug
|
getSpy.mockImplementation(() => goJsonData as IGoVersion[] | null);
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
|
||||||
//process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -138,6 +217,10 @@ describe('setup-go', () => {
|
|||||||
delete process.env[im.GOTOOLCHAIN_ENV_VAR];
|
delete process.env[im.GOTOOLCHAIN_ENV_VAR];
|
||||||
delete process.env['GO_DOWNLOAD_BASE_URL'];
|
delete process.env['GO_DOWNLOAD_BASE_URL'];
|
||||||
|
|
||||||
|
// reset the exit code that core.setFailed sets on the error-path tests so
|
||||||
|
// that a passing run does not leak a non-zero process exit code
|
||||||
|
process.exitCode = 0;
|
||||||
|
|
||||||
//jest.resetAllMocks();
|
//jest.resetAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
//jest.restoreAllMocks();
|
//jest.restoreAllMocks();
|
||||||
@@ -171,8 +254,10 @@ describe('setup-go', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return manifest from raw URL if repo fetch fails', async () => {
|
it('should return manifest from raw URL if repo fetch fails', async () => {
|
||||||
getManifestSpy.mockRejectedValue(new Error('Fetch failed'));
|
(getManifestSpy as jest.Mock<any>).mockRejectedValue(
|
||||||
httpmGetJsonSpy.mockResolvedValue({
|
new Error('Fetch failed')
|
||||||
|
);
|
||||||
|
(httpmGetJsonSpy as jest.Mock<any>).mockResolvedValue({
|
||||||
result: goTestManifest
|
result: goTestManifest
|
||||||
});
|
});
|
||||||
const manifest = await im.getManifest(undefined);
|
const manifest = await im.getManifest(undefined);
|
||||||
@@ -211,7 +296,7 @@ describe('setup-go', () => {
|
|||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
|
|
||||||
// spec: 1.13.0 => 1.13
|
// spec: 1.13.0 => 1.13
|
||||||
const match: im.IGoVersion | undefined = await im.findMatch('1.13.0');
|
const match: IGoVersion | undefined = await im.findMatch('1.13.0');
|
||||||
expect(match).toBeDefined();
|
expect(match).toBeDefined();
|
||||||
const version: string = match ? match.version : '';
|
const version: string = match ? match.version : '';
|
||||||
expect(version).toBe('go1.13');
|
expect(version).toBe('go1.13');
|
||||||
@@ -224,7 +309,7 @@ describe('setup-go', () => {
|
|||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
|
|
||||||
// spec: 1.13 => 1.13.7 (latest)
|
// spec: 1.13 => 1.13.7 (latest)
|
||||||
const match: im.IGoVersion | undefined = await im.findMatch('1.13');
|
const match: IGoVersion | undefined = await im.findMatch('1.13');
|
||||||
expect(match).toBeDefined();
|
expect(match).toBeDefined();
|
||||||
const version: string = match ? match.version : '';
|
const version: string = match ? match.version : '';
|
||||||
expect(version).toBe('go1.13.7');
|
expect(version).toBe('go1.13.7');
|
||||||
@@ -237,7 +322,7 @@ describe('setup-go', () => {
|
|||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
|
|
||||||
// spec: ^1.13.6 => 1.13.7
|
// spec: ^1.13.6 => 1.13.7
|
||||||
const match: im.IGoVersion | undefined = await im.findMatch('^1.13.6');
|
const match: IGoVersion | undefined = await im.findMatch('^1.13.6');
|
||||||
expect(match).toBeDefined();
|
expect(match).toBeDefined();
|
||||||
const version: string = match ? match.version : '';
|
const version: string = match ? match.version : '';
|
||||||
expect(version).toBe('go1.13.7');
|
expect(version).toBe('go1.13.7');
|
||||||
@@ -250,7 +335,7 @@ describe('setup-go', () => {
|
|||||||
os.arch = 'x32';
|
os.arch = 'x32';
|
||||||
|
|
||||||
// spec: 1 => 1.13.7 (latest)
|
// spec: 1 => 1.13.7 (latest)
|
||||||
const match: im.IGoVersion | undefined = await im.findMatch('1');
|
const match: IGoVersion | undefined = await im.findMatch('1');
|
||||||
expect(match).toBeDefined();
|
expect(match).toBeDefined();
|
||||||
const version: string = match ? match.version : '';
|
const version: string = match ? match.version : '';
|
||||||
expect(version).toBe('go1.13.7');
|
expect(version).toBe('go1.13.7');
|
||||||
@@ -263,7 +348,7 @@ describe('setup-go', () => {
|
|||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
|
|
||||||
// spec: 1.14, stable=false => go1.14rc1
|
// spec: 1.14, stable=false => go1.14rc1
|
||||||
const match: im.IGoVersion | undefined = await im.findMatch('1.14.0-rc.1');
|
const match: IGoVersion | undefined = await im.findMatch('1.14.0-rc.1');
|
||||||
expect(match).toBeDefined();
|
expect(match).toBeDefined();
|
||||||
const version: string = match ? match.version : '';
|
const version: string = match ? match.version : '';
|
||||||
expect(version).toBe('go1.14rc1');
|
expect(version).toBe('go1.14rc1');
|
||||||
@@ -568,6 +653,106 @@ describe('setup-go', () => {
|
|||||||
expect(added).toBeTruthy();
|
expect(added).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('go env outputs', () => {
|
||||||
|
const goEnv = {
|
||||||
|
GOPATH: '/Users/testuser/go',
|
||||||
|
GOBIN: '',
|
||||||
|
GOROOT: '/usr/local/go',
|
||||||
|
GOCACHE: '/Users/testuser/.cache/go-build',
|
||||||
|
GOMODCACHE: '/Users/testuser/go/pkg/mod',
|
||||||
|
GOOS: 'darwin',
|
||||||
|
GOARCH: 'arm64',
|
||||||
|
GOTOOLDIR: '/usr/local/go/pkg/tool/darwin_arm64'
|
||||||
|
};
|
||||||
|
|
||||||
|
it('parses the output of go env -json', () => {
|
||||||
|
execFileSpy.mockImplementation(() => JSON.stringify(goEnv));
|
||||||
|
|
||||||
|
expect(main.readGoEnv('/usr/local/go/bin/go')).toEqual(goEnv);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when go env -json is not supported', () => {
|
||||||
|
execFileSpy.mockImplementation(() => {
|
||||||
|
throw new Error('flag provided but not defined: -json');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(main.readGoEnv('/usr/local/go/bin/go')).toBeUndefined();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('flag provided but not defined: -json')
|
||||||
|
);
|
||||||
|
expect(warningSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when the output is not valid JSON', () => {
|
||||||
|
execFileSpy.mockImplementation(() => 'GOPATH="/Users/testuser/go"');
|
||||||
|
|
||||||
|
expect(main.readGoEnv('/usr/local/go/bin/go')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when the output is not a JSON object', () => {
|
||||||
|
execFileSpy.mockImplementation(() => '[]');
|
||||||
|
|
||||||
|
expect(main.readGoEnv('/usr/local/go/bin/go')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets an output for each exposed variable', () => {
|
||||||
|
main.setGoEnvOutputs(goEnv);
|
||||||
|
|
||||||
|
expect(GO_ENV_OUTPUTS).toEqual([
|
||||||
|
'GOPATH',
|
||||||
|
'GOBIN',
|
||||||
|
'GOROOT',
|
||||||
|
'GOCACHE',
|
||||||
|
'GOMODCACHE',
|
||||||
|
'GOOS',
|
||||||
|
'GOARCH',
|
||||||
|
'GOTOOLDIR'
|
||||||
|
]);
|
||||||
|
for (const name of GO_ENV_OUTPUTS) {
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(name, goEnv[name] ?? '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to $GOPATH/bin when GOBIN is empty', () => {
|
||||||
|
main.setGoEnvOutputs(goEnv);
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'go-bin-path',
|
||||||
|
'/Users/testuser/go/bin'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefers GOBIN over $GOPATH/bin when it is set', () => {
|
||||||
|
main.setGoEnvOutputs({...goEnv, GOBIN: '/Users/testuser/bin'});
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'go-bin-path',
|
||||||
|
'/Users/testuser/bin'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the first entry of a multi-entry GOPATH', () => {
|
||||||
|
const GOPATH = ['/Users/testuser/go', '/Users/testuser/other'].join(
|
||||||
|
path.delimiter
|
||||||
|
);
|
||||||
|
|
||||||
|
main.setGoEnvOutputs({...goEnv, GOPATH});
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('GOPATH', GOPATH);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'go-bin-path',
|
||||||
|
'/Users/testuser/go/bin'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves variables missing from go env -json empty', () => {
|
||||||
|
main.setGoEnvOutputs({});
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('GOPATH', '');
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('go-bin-path', '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
interface Annotation {
|
interface Annotation {
|
||||||
file: string;
|
file: string;
|
||||||
line: number;
|
line: number;
|
||||||
@@ -817,10 +1002,9 @@ describe('setup-go', () => {
|
|||||||
getManifestSpy.mockImplementation(() => {
|
getManifestSpy.mockImplementation(() => {
|
||||||
throw new Error('Unable to download manifest');
|
throw new Error('Unable to download manifest');
|
||||||
});
|
});
|
||||||
httpmGetJsonSpy.mockRejectedValue(
|
(httpmGetJsonSpy as jest.Mock<any>).mockRejectedValue(
|
||||||
new Error('Unable to download manifest from raw URL')
|
new Error('Unable to download manifest from raw URL')
|
||||||
);
|
);
|
||||||
getAllVersionsSpy.mockImplementationOnce(() => undefined);
|
|
||||||
|
|
||||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
const toolPath = path.normalize('/cache/go/1.13.7/x64');
|
const toolPath = path.normalize('/cache/go/1.13.7/x64');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {isSelfHosted} from '../src/utils';
|
import {describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
|
import {isSelfHosted} from '../src/utils.js';
|
||||||
|
|
||||||
describe('utils', () => {
|
describe('utils', () => {
|
||||||
describe('isSelfHosted', () => {
|
describe('isSelfHosted', () => {
|
||||||
|
|||||||
@@ -1,38 +1,57 @@
|
|||||||
import fs from 'fs';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as io from '@actions/io';
|
|
||||||
import * as tc from '@actions/tool-cache';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
const statSyncMock = jest.fn(() => ({
|
||||||
|
isDirectory: () => true,
|
||||||
|
isFile: () => true
|
||||||
|
}));
|
||||||
|
const readdirSyncMock = jest.fn(() => [] as any);
|
||||||
|
const writeFileSyncMock = jest.fn();
|
||||||
|
const mkdirMock = jest.fn();
|
||||||
|
const symlinkSyncMock = jest.fn();
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(() => Promise.resolve()),
|
||||||
|
rmRF: jest.fn(() => Promise.resolve()),
|
||||||
|
mv: jest.fn(() => Promise.resolve()),
|
||||||
|
cp: jest.fn(() => Promise.resolve())
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realFs = (await import('fs')).default;
|
||||||
|
const fsExports = {
|
||||||
|
...realFs,
|
||||||
|
statSync: statSyncMock,
|
||||||
|
readdirSync: readdirSyncMock,
|
||||||
|
writeFileSync: writeFileSyncMock,
|
||||||
|
mkdir: mkdirMock,
|
||||||
|
symlinkSync: symlinkSyncMock
|
||||||
|
};
|
||||||
|
jest.unstable_mockModule('fs', () => ({...fsExports, default: fsExports}));
|
||||||
|
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
|
||||||
describe('Windows performance workaround', () => {
|
describe('Windows performance workaround', () => {
|
||||||
let mkdirSpy: jest.SpyInstance;
|
let rmRFSpy: jest.Mock;
|
||||||
let symlinkSpy: jest.SpyInstance;
|
let mkdirPSpy: jest.Mock;
|
||||||
let statSpy: jest.SpyInstance;
|
let cpSpy: jest.Mock;
|
||||||
let readdirSpy: jest.SpyInstance;
|
|
||||||
let writeFileSpy: jest.SpyInstance;
|
|
||||||
let rmRFSpy: jest.SpyInstance;
|
|
||||||
let mkdirPSpy: jest.SpyInstance;
|
|
||||||
let cpSpy: jest.SpyInstance;
|
|
||||||
|
|
||||||
let runnerToolCache: string | undefined;
|
let runnerToolCache: string | undefined;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mkdirSpy = jest.spyOn(fs, 'mkdir');
|
rmRFSpy = io.rmRF as jest.Mock;
|
||||||
symlinkSpy = jest.spyOn(fs, 'symlinkSync');
|
mkdirPSpy = io.mkdirP as jest.Mock;
|
||||||
statSpy = jest.spyOn(fs, 'statSync');
|
cpSpy = io.cp as jest.Mock;
|
||||||
readdirSpy = jest.spyOn(fs, 'readdirSync');
|
|
||||||
writeFileSpy = jest.spyOn(fs, 'writeFileSync');
|
|
||||||
rmRFSpy = jest.spyOn(io, 'rmRF');
|
|
||||||
mkdirPSpy = jest.spyOn(io, 'mkdirP');
|
|
||||||
cpSpy = jest.spyOn(io, 'cp');
|
|
||||||
|
|
||||||
// default implementations
|
// default implementations
|
||||||
// @ts-ignore - not implement unused methods
|
statSyncMock.mockImplementation(() => ({
|
||||||
statSpy.mockImplementation(() => ({
|
isDirectory: () => true,
|
||||||
isDirectory: () => true
|
isFile: () => true
|
||||||
}));
|
}));
|
||||||
readdirSpy.mockImplementation(() => []);
|
readdirSyncMock.mockImplementation(() => []);
|
||||||
writeFileSpy.mockImplementation(() => {});
|
writeFileSyncMock.mockImplementation(() => {});
|
||||||
mkdirSpy.mockImplementation(() => {});
|
mkdirMock.mockImplementation(() => {});
|
||||||
symlinkSpy.mockImplementation(() => {});
|
symlinkSyncMock.mockImplementation(() => {});
|
||||||
rmRFSpy.mockImplementation(() => Promise.resolve());
|
rmRFSpy.mockImplementation(() => Promise.resolve());
|
||||||
mkdirPSpy.mockImplementation(() => Promise.resolve());
|
mkdirPSpy.mockImplementation(() => Promise.resolve());
|
||||||
cpSpy.mockImplementation(() => Promise.resolve());
|
cpSpy.mockImplementation(() => Promise.resolve());
|
||||||
|
|||||||
20
action.yml
20
action.yml
@@ -25,7 +25,25 @@ 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 indicating whether both module and build cache entries were restored on their primary keys'
|
||||||
|
GOPATH:
|
||||||
|
description: 'The value of `go env GOPATH`.'
|
||||||
|
GOBIN:
|
||||||
|
description: 'The value of `go env GOBIN`.'
|
||||||
|
go-bin-path:
|
||||||
|
description: 'The base Go binary directory: `GOBIN`, otherwise `bin` under the first `GOPATH` entry.'
|
||||||
|
GOROOT:
|
||||||
|
description: 'The value of `go env GOROOT`.'
|
||||||
|
GOCACHE:
|
||||||
|
description: 'The value of `go env GOCACHE`, the build cache directory.'
|
||||||
|
GOMODCACHE:
|
||||||
|
description: 'The value of `go env GOMODCACHE`, the module cache directory.'
|
||||||
|
GOOS:
|
||||||
|
description: 'The value of `go env GOOS`, the target OS in Go notation (linux, darwin, windows).'
|
||||||
|
GOARCH:
|
||||||
|
description: 'The value of `go env GOARCH`, the target architecture in Go notation (amd64, arm64).'
|
||||||
|
GOTOOLDIR:
|
||||||
|
description: 'The value of `go env GOTOOLDIR`, the directory holding the toolchain binaries.'
|
||||||
runs:
|
runs:
|
||||||
using: 'node24'
|
using: 'node24'
|
||||||
main: 'dist/setup/index.js'
|
main: 'dist/setup/index.js'
|
||||||
|
|||||||
141662
dist/cache-save/index.js
vendored
141662
dist/cache-save/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/cache-save/package.json
vendored
Normal file
3
dist/cache-save/package.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
148377
dist/setup/index.js
vendored
148377
dist/setup/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/setup/package.json
vendored
Normal file
3
dist/setup/package.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
- [Restore-only caches](advanced-usage.md#restore-only-caches)
|
- [Restore-only caches](advanced-usage.md#restore-only-caches)
|
||||||
- [Parallel builds](advanced-usage.md#parallel-builds)
|
- [Parallel builds](advanced-usage.md#parallel-builds)
|
||||||
- [Outputs](advanced-usage.md#outputs)
|
- [Outputs](advanced-usage.md#outputs)
|
||||||
|
- [go-version](advanced-usage.md#go-version)
|
||||||
|
- [cache-hit](advanced-usage.md#cache-hit)
|
||||||
|
- [Go environment outputs](advanced-usage.md#go-environment-outputs)
|
||||||
- [Custom download URL](advanced-usage.md#custom-download-url)
|
- [Custom download URL](advanced-usage.md#custom-download-url)
|
||||||
- [Using `setup-go` on GHES](advanced-usage.md#using-setup-go-on-ghes)
|
- [Using `setup-go` on GHES](advanced-usage.md#using-setup-go-on-ghes)
|
||||||
|
|
||||||
@@ -23,8 +26,8 @@ For repeatable builds, specify the **exact major, minor, and patch version** (su
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.5'
|
go-version: '1.25.5'
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -36,8 +39,8 @@ You can specify **only a major and minor version** if you are okay with the most
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -53,8 +56,8 @@ Download and use beta or RC Go versions as needed.
|
|||||||
```yaml
|
```yaml
|
||||||
# RC version
|
# RC version
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0-rc.2'
|
go-version: '1.25.0-rc.2'
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -63,8 +66,8 @@ steps:
|
|||||||
```yaml
|
```yaml
|
||||||
# Beta version
|
# Beta version
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.19.0-beta.1'
|
go-version: '1.19.0-beta.1'
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -80,8 +83,8 @@ If `oldstable` is provided, the action resolves it to the latest patch release o
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -89,8 +92,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: 'oldstable'
|
go-version: 'oldstable'
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -100,8 +103,8 @@ You can also use **SemVer's version range syntax**, for instance:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '^1.25.1'
|
go-version: '^1.25.1'
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -109,8 +112,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '>=1.24.0-rc.1'
|
go-version: '>=1.24.0-rc.1'
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -129,9 +132,9 @@ jobs:
|
|||||||
go: [ '1.24', '1.25' ]
|
go: [ '1.24', '1.25' ]
|
||||||
name: Go ${{ matrix.go }} sample
|
name: Go ${{ matrix.go }} sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -154,9 +157,9 @@ jobs:
|
|||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
go: '1.25'
|
go: '1.25'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- run: go run hello.go
|
- run: go run hello.go
|
||||||
@@ -170,8 +173,8 @@ If both the `go-version` and the `go-version-file` inputs are provided then the
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: 'path/to/go.mod' # Read Go version from go.mod
|
go-version-file: 'path/to/go.mod' # Read Go version from go.mod
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -187,8 +190,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: '.go-version' # Read Go version from .go-version
|
go-version-file: '.go-version' # Read Go version from .go-version
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -196,8 +199,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: '.tool-versions' # Read Go version from .tool-versions
|
go-version-file: '.tool-versions' # Read Go version from .tool-versions
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -205,8 +208,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.work' # Read Go version from go.work
|
go-version-file: 'go.work' # Read Go version from go.work
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -228,8 +231,8 @@ want the most up-to-date Go version to always be used. It supports major (e.g.,
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -238,12 +241,21 @@ steps:
|
|||||||
|
|
||||||
## Caching
|
## Caching
|
||||||
|
|
||||||
|
The action restores and saves two independent cache entries:
|
||||||
|
|
||||||
|
- **Module sources (`GOMODCACHE`)**: keyed by the runner OS and dependency-file hash. These sources can be reused across Go versions, runner architectures, and runner images on the same OS.
|
||||||
|
- **Build outputs (`GOCACHE`)**: keyed by the runner OS, runner architecture, Linux runner image (when applicable), Go version, and dependency-file hash.
|
||||||
|
|
||||||
|
Both entries use the hash of `cache-dependency-path`, or the root `go.mod` by default. Adding source files or build settings to that input invalidates both entries when those files change. Custom cache locations and the cache service's normal access restrictions still apply.
|
||||||
|
|
||||||
|
Each entry is restored and saved independently, with concurrent transfers where safe. An exact hit is not uploaded again, even when the other entry misses. A failure for one entry does not prevent caching the other. The `cache-hit` output is `true` only when both entries are restored on their primary keys.
|
||||||
|
|
||||||
### Caching in monorepos
|
### Caching in monorepos
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
cache-dependency-path: subdir/go.sum
|
cache-dependency-path: subdir/go.sum
|
||||||
@@ -256,8 +268,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
cache-dependency-path: '**/go.sum'
|
cache-dependency-path: '**/go.sum'
|
||||||
@@ -266,8 +278,8 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
@@ -278,7 +290,7 @@ steps:
|
|||||||
|
|
||||||
### Multi-target builds
|
### Multi-target builds
|
||||||
|
|
||||||
`cache-dependency-path` isn’t 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.
|
`GOOS` and `GOARCH` are not automatically included in the cache keys. `cache-dependency-path` isn’t limited to dependency files (like `go.sum`); it can also include files that capture these build settings. This prevents jobs for different targets from competing to populate the same immutable cache entry. Because both entries use the same dependency-file hash, this workaround separates the module cache as well as the build cache.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
env:
|
env:
|
||||||
@@ -286,16 +298,15 @@ env:
|
|||||||
GOARCH: ...
|
GOARCH: ...
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
- run: echo "$GOOS $GOARCH" > env.txt
|
- run: echo "$GOOS $GOARCH" > env.txt
|
||||||
|
- uses: actions/setup-go@v7
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- uses: actions/setup-go@v6
|
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
go.sum
|
go.sum
|
||||||
env.txt
|
env.txt
|
||||||
- run: go run hello.go
|
- run: go build ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cache invalidation on source changes
|
### Cache invalidation on source changes
|
||||||
@@ -306,8 +317,8 @@ Besides dependencies, the action can also cache build outputs (the [`GOCACHE`](h
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
@@ -328,10 +339,10 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
id: setup-go
|
id: setup-go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.5'
|
go-version: '1.25.5'
|
||||||
cache: false
|
cache: false
|
||||||
@@ -362,20 +373,26 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: echo "CACHE_OS_SUFFIX=$ImageOS-" >> $GITHUB_ENV
|
run: echo "CACHE_OS_SUFFIX=$ImageOS-" >> $GITHUB_ENV
|
||||||
- name: Restore Go cache
|
- name: Restore Go module cache
|
||||||
id: go-cache
|
id: go-module-cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: ${{ env.GO_MOD_CACHE }}
|
||||||
${{ env.GO_MOD_CACHE }}
|
key: setup-go-modules-${{ runner.os }}-${{ hashFiles('go.mod') }}
|
||||||
${{ env.GO_BUILD_CACHE }}
|
- name: Restore Go build cache
|
||||||
key: setup-go-${{ runner.os }}-${{ env.ARCH }}-${{ env.CACHE_OS_SUFFIX }}go-${{ steps.setup-go.outputs.go-version }}-${{ hashFiles('**/go.mod') }}
|
id: go-build-cache
|
||||||
|
uses: actions/cache/restore@v5
|
||||||
|
with:
|
||||||
|
path: ${{ env.GO_BUILD_CACHE }}
|
||||||
|
key: setup-go-build-${{ runner.os }}-${{ env.ARCH }}-${{ env.CACHE_OS_SUFFIX }}go-${{ steps.setup-go.outputs.go-version }}-${{ hashFiles('go.mod') }}
|
||||||
- name: Download modules
|
- name: Download modules
|
||||||
run: go mod download
|
run: go mod download
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build ./...
|
run: go build ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Use the same file patterns in `hashFiles` as the `cache-dependency-path` of the workflow that saves the caches. The example above matches the default root `go.mod` input.
|
||||||
|
|
||||||
> 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)
|
> 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.
|
should be used in this case.
|
||||||
|
|
||||||
@@ -394,8 +411,8 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
id: go124
|
id: go124
|
||||||
with:
|
with:
|
||||||
go-version: '^1.24'
|
go-version: '^1.24'
|
||||||
@@ -404,22 +421,52 @@ jobs:
|
|||||||
|
|
||||||
### `cache-hit`
|
### `cache-hit`
|
||||||
|
|
||||||
**cache-hit** output is available with a boolean value that indicates whether a cache hit occurred on the primary key:
|
**cache-hit** is `true` only when both the module cache and the build cache are restored on their primary keys. If only one entry hits its primary key, the output is `false`, but that entry is still used and is not saved again. An entry restored from a non-primary key can still be saved under its primary key:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
id: go124
|
id: go124
|
||||||
with:
|
with:
|
||||||
go-version: '1.24'
|
go-version: '1.24'
|
||||||
cache: true
|
cache: true
|
||||||
- run: echo "Was the Go cache restored? ${{ steps.go124.outputs.cache-hit }}" # true if cache-hit occurred
|
- run: echo "Were both Go caches restored? ${{ steps.go124.outputs.cache-hit }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Go environment outputs
|
||||||
|
|
||||||
|
Once Go is on the `PATH`, the action exposes the most commonly needed `go env` variables as outputs, so workflows don't have to query them in duplicated `bash`/`pwsh` steps — one expression works on Linux, macOS, and Windows. The values are a snapshot from setup time: anything a later step changes, such as setting `GOOS`/`GOARCH` to cross-compile, is not reflected.
|
||||||
|
|
||||||
|
| Output | Notes |
|
||||||
|
| --- | --- |
|
||||||
|
| `GOPATH` | Go workspace root; `bin` and `pkg/mod` live under it |
|
||||||
|
| `GOBIN` | Go 1.27+ reports an implicit `$GOPATH/bin` default; earlier releases are empty unless `GOBIN` is set |
|
||||||
|
| `go-bin-path` | The base Go binary directory (`GOBIN` or `$GOPATH/bin`), using the first `GOPATH` entry. Cross-compiled binaries go one level deeper, in `$GOPATH/bin/$GOOS_$GOARCH` |
|
||||||
|
| `GOROOT` | Installation directory of the Go toolchain in use |
|
||||||
|
| `GOCACHE` | Build cache directory |
|
||||||
|
| `GOMODCACHE` | Module cache directory |
|
||||||
|
| `GOOS` | Go notation (`linux`, `darwin`, `windows`), unlike `runner.os` |
|
||||||
|
| `GOARCH` | Go notation (`amd64`, `arm64`), unlike `runner.arch` or the `x64` in the action's cache key |
|
||||||
|
| `GOTOOLDIR` | Holds `compile`, `link`, `vet` and the other toolchain binaries |
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v7
|
||||||
|
id: setup-go
|
||||||
|
with:
|
||||||
|
go-version: '1.25.5'
|
||||||
|
- run: echo "Modules are cached in ${{ steps.setup-go.outputs.GOMODCACHE }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Quote `go-bin-path` in shell commands, since it may contain spaces. Variables without a dedicated output are not exposed; run `go env <NAME>` in a step when you need one.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> These outputs need Go 1.9 or newer (`go env -json`); on older releases the action logs a message, leaves them unset and does not fail. `GOCACHE` needs Go 1.10 and `GOMODCACHE` needs Go 1.15.
|
||||||
|
|
||||||
## Custom download URL
|
## 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.
|
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.
|
||||||
@@ -430,8 +477,8 @@ When a custom base URL is provided, the action skips the `actions/go-versions` m
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
go-download-base-url: 'https://aka.ms/golang/release/latest'
|
||||||
@@ -445,8 +492,8 @@ env:
|
|||||||
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
GO_DOWNLOAD_BASE_URL: 'https://aka.ms/golang/release/latest'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
- run: go version
|
- run: go version
|
||||||
@@ -462,8 +509,8 @@ If your custom download source requires authentication, the `token` input is for
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
go-download-base-url: 'https://private-mirror.example.com/golang'
|
go-download-base-url: 'https://private-mirror.example.com/golang'
|
||||||
@@ -485,7 +532,7 @@ If that fails as well the action will try to download versions directly from [go
|
|||||||
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:
|
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
|
```yaml
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
||||||
go-version: '1.25'
|
go-version: '1.25'
|
||||||
|
|||||||
74
eslint.config.mjs
Normal file
74
eslint.config.mjs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||||
|
import jest from 'eslint-plugin-jest';
|
||||||
|
import n from 'eslint-plugin-n';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/*', '!src/**', '!__tests__/**']
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2015
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsPlugin,
|
||||||
|
n
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...tsPlugin.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-require-imports': 'error',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'ts-ignore': 'allow-with-description'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-console': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'prefer-const': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
destructuring: 'all'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-control-regex': 'off',
|
||||||
|
'no-constant-condition': ['error', {checkLoops: false}],
|
||||||
|
'no-undef': 'off',
|
||||||
|
'no-useless-assignment': 'off',
|
||||||
|
'n/no-extraneous-import': 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*{test,spec}.ts'],
|
||||||
|
plugins: {jest},
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.jest
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...jest.configs['flat/recommended'].rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'jest/no-standalone-expect': 'off',
|
||||||
|
'jest/no-conditional-expect': 'off',
|
||||||
|
'no-console': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prettier
|
||||||
|
];
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
clearMocks: true,
|
|
||||||
moduleFileExtensions: ['js', 'ts'],
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testMatch: ['**/*.test.ts'],
|
|
||||||
testRunner: 'jest-circus/runner',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.ts$': 'ts-jest'
|
|
||||||
},
|
|
||||||
verbose: true
|
|
||||||
}
|
|
||||||
24
jest.config.ts
Normal file
24
jest.config.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
roots: ['<rootDir>'],
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
useESM: true,
|
||||||
|
diagnostics: {
|
||||||
|
ignoreCodes: [151002]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
};
|
||||||
4970
package-lock.json
generated
4970
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "setup-go",
|
"name": "setup-go",
|
||||||
"version": "6.2.0",
|
"version": "7.0.0",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "setup go action",
|
"description": "setup go action",
|
||||||
"main": "lib/setup-go.js",
|
"main": "lib/setup-go.js",
|
||||||
@@ -9,11 +10,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && ncc build -o dist/setup src/setup-go.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
"build": "tsc && ncc build -o dist/setup src/setup-go.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
||||||
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
|
"format": "prettier --no-error-on-unmatched-pattern --write \"**/*.{ts,yml,yaml}\"",
|
||||||
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"",
|
"format-check": "prettier --no-error-on-unmatched-pattern --check \"**/*.{ts,yml,yaml}\"",
|
||||||
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"",
|
"lint": "eslint \"**/*.ts\"",
|
||||||
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix",
|
"lint:fix": "eslint \"**/*.ts\" --fix",
|
||||||
"test": "jest --coverage",
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --coverage",
|
||||||
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -28,31 +29,32 @@
|
|||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/cache": "^5.0.1",
|
"@actions/cache": "^6.3.0",
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/glob": "^0.5.0",
|
"@actions/glob": "^0.7.0",
|
||||||
"@actions/http-client": "^2.2.1",
|
"@actions/http-client": "^4.0.1",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/io": "^3.0.2",
|
||||||
"@actions/tool-cache": "^2.0.2",
|
"@actions/tool-cache": "^4.0.0",
|
||||||
"semver": "^7.7.3"
|
"semver": "^7.8.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.14",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^24.1.0",
|
"@jest/globals": "^30.4.1",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/node": "^24.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
"@types/semver": "^7.7.0",
|
||||||
"@typescript-eslint/parser": "^8.35.1",
|
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
||||||
"@vercel/ncc": "^0.38.1",
|
"@typescript-eslint/parser": "^8.62.0",
|
||||||
"eslint": "^8.57.0",
|
"@vercel/ncc": "^0.44.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint": "^10.5.0",
|
||||||
"eslint-plugin-jest": "^29.0.1",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-jest": "^29.15.2",
|
||||||
"jest": "^29.7.0",
|
"eslint-plugin-n": "^18.1.0",
|
||||||
"jest-circus": "^29.7.0",
|
"globals": "^17.7.0",
|
||||||
"nock": "^10.0.6",
|
"jest": "^30.4.2",
|
||||||
"prettier": "^2.8.4",
|
"nock": "^14.0.0",
|
||||||
"ts-jest": "^29.3.2",
|
"prettier": "^3.8.4",
|
||||||
"typescript": "^5.8.3"
|
"ts-jest": "^29.4.11",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,21 @@ import * as glob from '@actions/glob';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import {State, Outputs} from './constants';
|
import {State, Outputs} from './constants.js';
|
||||||
import {PackageManagerInfo} from './package-managers';
|
import {PackageManagerInfo} from './package-managers.js';
|
||||||
import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils';
|
import {
|
||||||
|
CacheEntry,
|
||||||
|
canRestoreCachesInParallel,
|
||||||
|
getCacheDirectoryPath,
|
||||||
|
getPackageManagerInfo
|
||||||
|
} from './cache-utils.js';
|
||||||
|
|
||||||
export const restoreCache = async (
|
export const restoreCache = async (
|
||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
packageManager: string,
|
packageManager: string,
|
||||||
cacheDependencyPath?: string
|
cacheDependencyPath?: string
|
||||||
) => {
|
) => {
|
||||||
|
core.setOutput(Outputs.CacheHit, false);
|
||||||
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
||||||
const platform = process.env.RUNNER_OS;
|
const platform = process.env.RUNNER_OS;
|
||||||
const arch = process.arch;
|
const arch = process.arch;
|
||||||
@@ -32,22 +38,51 @@ export const restoreCache = async (
|
|||||||
|
|
||||||
const linuxVersion =
|
const linuxVersion =
|
||||||
process.env.RUNNER_OS === 'Linux' ? `${process.env.ImageOS}-` : '';
|
process.env.RUNNER_OS === 'Linux' ? `${process.env.ImageOS}-` : '';
|
||||||
const primaryKey = `setup-go-${platform}-${arch}-${linuxVersion}go-${versionSpec}-${fileHash}`;
|
const primaryKeys = {
|
||||||
core.debug(`primary key is ${primaryKey}`);
|
// Downloaded module sources can be reused across Go versions and runner
|
||||||
|
// architectures. Keep OS isolation for filesystem compatibility.
|
||||||
|
modules: `setup-go-modules-${platform}-${fileHash}`,
|
||||||
|
build: `setup-go-build-${platform}-${arch}-${linuxVersion}go-${versionSpec}-${fileHash}`
|
||||||
|
};
|
||||||
|
const entries: CacheEntry[] = cachePaths.map(directory => ({
|
||||||
|
...directory,
|
||||||
|
primaryKey: primaryKeys[directory.kind]
|
||||||
|
}));
|
||||||
|
|
||||||
core.saveState(State.CachePrimaryKey, primaryKey);
|
const restoreEntry = async (entry: CacheEntry) => {
|
||||||
|
core.debug(`${entry.kind} primary key is ${entry.primaryKey}`);
|
||||||
|
try {
|
||||||
|
entry.matchedKey = await cache.restoreCache(
|
||||||
|
[entry.path],
|
||||||
|
entry.primaryKey
|
||||||
|
);
|
||||||
|
if (entry.matchedKey) {
|
||||||
|
core.info(`${entry.kind} cache restored from key: ${entry.matchedKey}`);
|
||||||
|
} else {
|
||||||
|
core.info(`${entry.kind} cache is not found`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
core.warning(`Restore ${entry.kind} cache failed: ${message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const cacheKey = await cache.restoreCache(cachePaths, primaryKey);
|
if (canRestoreCachesInParallel(entries)) {
|
||||||
core.setOutput(Outputs.CacheHit, Boolean(cacheKey));
|
await Promise.all(entries.map(restoreEntry));
|
||||||
|
} else {
|
||||||
if (!cacheKey) {
|
for (const entry of entries) {
|
||||||
core.info(`Cache is not found`);
|
await restoreEntry(entry);
|
||||||
core.setOutput(Outputs.CacheHit, false);
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.saveState(State.CacheMatchedKey, cacheKey);
|
// Preserve the resolved paths so the post step uses the same cache versions,
|
||||||
core.info(`Cache restored from key: ${cacheKey}`);
|
// even if a later step changes the Go environment.
|
||||||
|
core.saveState(State.CacheEntries, entries);
|
||||||
|
core.setOutput(
|
||||||
|
Outputs.CacheHit,
|
||||||
|
entries.length === packageManagerInfo.cacheFolderCommandList.length &&
|
||||||
|
entries.every(entry => entry.matchedKey === entry.primaryKey)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const findDependencyFile = (packageManager: PackageManagerInfo) => {
|
const findDependencyFile = (packageManager: PackageManagerInfo) => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils';
|
import type {CacheEntry} from './cache-utils.js';
|
||||||
|
|
||||||
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
||||||
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
||||||
@@ -39,48 +39,48 @@ export async function run(earlyExit?: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cachePackages = async () => {
|
const cachePackages = async () => {
|
||||||
const packageManager = 'default';
|
const entries: CacheEntry[] = JSON.parse(
|
||||||
|
core.getState(State.CacheEntries) || '[]'
|
||||||
const state = core.getState(State.CacheMatchedKey);
|
|
||||||
const primaryKey = core.getState(State.CachePrimaryKey);
|
|
||||||
|
|
||||||
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
|
||||||
|
|
||||||
const cachePaths = await getCacheDirectoryPath(packageManagerInfo);
|
|
||||||
|
|
||||||
const nonExistingPaths = cachePaths.filter(
|
|
||||||
cachePath => !fs.existsSync(cachePath)
|
|
||||||
);
|
);
|
||||||
|
if (!entries.length) {
|
||||||
if (nonExistingPaths.length === cachePaths.length) {
|
|
||||||
core.warning('There are no cache folders on the disk');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonExistingPaths.length) {
|
|
||||||
logWarning(
|
|
||||||
`Cache folder path is retrieved but doesn't exist on disk: ${nonExistingPaths.join(
|
|
||||||
', '
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!primaryKey) {
|
|
||||||
core.info(
|
core.info(
|
||||||
'Primary key was not generated. Please check the log messages above for more errors or information'
|
'Cache keys were not generated. Please check the log messages above for more errors or information'
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primaryKey === state) {
|
await Promise.all(
|
||||||
|
entries.map(async entry => {
|
||||||
|
try {
|
||||||
|
await saveCacheEntry(entry);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
core.warning(`Save ${entry.kind} cache failed: ${message}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveCacheEntry = async (entry: CacheEntry) => {
|
||||||
|
const {kind, path, primaryKey, matchedKey} = entry;
|
||||||
|
if (primaryKey === matchedKey) {
|
||||||
core.info(
|
core.info(
|
||||||
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheId = await cache.saveCache(cachePaths, primaryKey);
|
if (!fs.existsSync(path)) {
|
||||||
|
logWarning(`${kind} cache folder doesn't exist on disk: ${path}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheId = await cache.saveCache([path], 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}`);
|
||||||
|
|||||||
@@ -1,7 +1,23 @@
|
|||||||
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 exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import {supportedPackageManagers, PackageManagerInfo} from './package-managers';
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import {
|
||||||
|
supportedPackageManagers,
|
||||||
|
PackageManagerInfo,
|
||||||
|
CacheKind
|
||||||
|
} from './package-managers.js';
|
||||||
|
|
||||||
|
export interface CacheDirectory {
|
||||||
|
kind: CacheKind;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CacheEntry extends CacheDirectory {
|
||||||
|
primaryKey: string;
|
||||||
|
matchedKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const getCommandOutput = async (toolCommand: string) => {
|
export const getCommandOutput = async (toolCommand: string) => {
|
||||||
let {stdout, stderr, exitCode} = await exec.getExecOutput(
|
let {stdout, stderr, exitCode} = await exec.getExecOutput(
|
||||||
@@ -33,24 +49,24 @@ export const getPackageManagerInfo = async (packageManager: string) => {
|
|||||||
|
|
||||||
export const getCacheDirectoryPath = async (
|
export const getCacheDirectoryPath = async (
|
||||||
packageManagerInfo: PackageManagerInfo
|
packageManagerInfo: PackageManagerInfo
|
||||||
) => {
|
): Promise<CacheDirectory[]> => {
|
||||||
const pathOutputs = await Promise.allSettled(
|
const pathOutputs = await Promise.allSettled(
|
||||||
packageManagerInfo.cacheFolderCommandList.map(async command =>
|
packageManagerInfo.cacheFolderCommandList.map(async ({kind, command}) => ({
|
||||||
getCommandOutput(command)
|
kind,
|
||||||
)
|
path: await getCommandOutput(command)
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
const results = pathOutputs.map(item => {
|
const cachePaths: CacheDirectory[] = [];
|
||||||
|
for (const item of pathOutputs) {
|
||||||
if (item.status === 'fulfilled') {
|
if (item.status === 'fulfilled') {
|
||||||
return item.value;
|
if (item.value.path) {
|
||||||
|
cachePaths.push(item.value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
core.info(`[warning]getting cache directory path failed: ${item.reason}`);
|
core.info(`[warning]getting cache directory path failed: ${item.reason}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
const cachePaths = results.filter(item => item);
|
|
||||||
|
|
||||||
if (!cachePaths.length) {
|
if (!cachePaths.length) {
|
||||||
throw new Error(`Could not get cache folder paths.`);
|
throw new Error(`Could not get cache folder paths.`);
|
||||||
@@ -59,6 +75,69 @@ export const getCacheDirectoryPath = async (
|
|||||||
return cachePaths;
|
return cachePaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function canRestoreCachesInParallel(
|
||||||
|
directories: CacheDirectory[]
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
const paths = directories.map(directory => {
|
||||||
|
const resolvedPath = resolveCachePath(directory.path);
|
||||||
|
// Conservatively handle case-insensitive Windows/macOS filesystems.
|
||||||
|
return process.platform === 'darwin' || process.platform === 'win32'
|
||||||
|
? resolvedPath.toLowerCase()
|
||||||
|
: resolvedPath;
|
||||||
|
});
|
||||||
|
// A cache archive can replace symlinks traversed by the other restore.
|
||||||
|
// Canonical destinations alone do not capture that extraction footprint.
|
||||||
|
if (
|
||||||
|
paths.some((resolvedPath, index) => {
|
||||||
|
const absolutePath = path.resolve(directories[index].path);
|
||||||
|
const normalizedPath =
|
||||||
|
process.platform === 'darwin' || process.platform === 'win32'
|
||||||
|
? absolutePath.toLowerCase()
|
||||||
|
: absolutePath;
|
||||||
|
return resolvedPath !== normalizedPath;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return paths.every((parent, index) =>
|
||||||
|
paths.every((child, otherIndex) => {
|
||||||
|
if (index === otherIndex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const relative = path.relative(parent, child);
|
||||||
|
return (
|
||||||
|
relative === '..' ||
|
||||||
|
relative.startsWith(`..${path.sep}`) ||
|
||||||
|
path.isAbsolute(relative)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// If the paths cannot be resolved safely, avoid concurrent extraction.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCachePath(directory: string): string {
|
||||||
|
const absolutePath = path.resolve(directory);
|
||||||
|
try {
|
||||||
|
fs.lstatSync(absolutePath);
|
||||||
|
} catch (error) {
|
||||||
|
const parent = path.dirname(absolutePath);
|
||||||
|
if (
|
||||||
|
(error as NodeJS.ErrnoException).code !== 'ENOENT' ||
|
||||||
|
parent === absolutePath
|
||||||
|
) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
// Cold cache directories may not exist yet. Resolve their existing
|
||||||
|
// ancestors so symlink aliases still participate in the overlap check.
|
||||||
|
return path.join(resolveCachePath(parent), path.basename(absolutePath));
|
||||||
|
}
|
||||||
|
return fs.realpathSync(absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
export function isGhes(): boolean {
|
export function isGhes(): boolean {
|
||||||
const ghUrl = new URL(
|
const ghUrl = new URL(
|
||||||
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
export enum State {
|
export enum State {
|
||||||
CachePrimaryKey = 'CACHE_KEY',
|
CacheEntries = 'CACHE_ENTRIES'
|
||||||
CacheMatchedKey = 'CACHE_RESULT'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Outputs {
|
export enum Outputs {
|
||||||
CacheHit = 'cache-hit'
|
CacheHit = 'cache-hit',
|
||||||
|
GoBinPath = 'go-bin-path'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const GO_ENV_OUTPUTS = [
|
||||||
|
'GOPATH',
|
||||||
|
'GOBIN',
|
||||||
|
'GOROOT',
|
||||||
|
'GOCACHE',
|
||||||
|
'GOMODCACHE',
|
||||||
|
'GOOS',
|
||||||
|
'GOARCH',
|
||||||
|
'GOTOOLDIR'
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type GoEnvVar = (typeof GO_ENV_OUTPUTS)[number];
|
||||||
|
export type GoEnv = Partial<Record<GoEnvVar, string>>;
|
||||||
|
|||||||
13
src/go-version-fetch.ts
Normal file
13
src/go-version-fetch.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import type {IGoVersion} from './installer.js';
|
||||||
|
|
||||||
|
export async function getVersionsDist(
|
||||||
|
dlUrl: string
|
||||||
|
): Promise<IGoVersion[] | null> {
|
||||||
|
// this returns versions descending so latest is first
|
||||||
|
const http: httpm.HttpClient = new httpm.HttpClient('setup-go', [], {
|
||||||
|
allowRedirects: true,
|
||||||
|
maxRedirects: 3
|
||||||
|
});
|
||||||
|
return (await http.getJson<IGoVersion[]>(dlUrl)).result;
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@ import * as core from '@actions/core';
|
|||||||
import * as path from 'path';
|
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.js';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import cp from 'child_process';
|
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.js';
|
||||||
import {Architecture} from './types';
|
import {Architecture} from './types.js';
|
||||||
|
import {getVersionsDist} from './go-version-fetch.js';
|
||||||
|
|
||||||
export const GOTOOLCHAIN_ENV_VAR = 'GOTOOLCHAIN';
|
export const GOTOOLCHAIN_ENV_VAR = 'GOTOOLCHAIN';
|
||||||
export const GOTOOLCHAIN_LOCAL_VAL = 'local';
|
export const GOTOOLCHAIN_LOCAL_VAL = 'local';
|
||||||
@@ -167,12 +168,14 @@ export async function getGo(
|
|||||||
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
|
if (err instanceof tc.HTTPError && err.httpStatusCode === 404) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
|
`The requested Go version ${versionSpec} is not available for platform ${osPlat}/${arch}. ` +
|
||||||
`Download URL returned HTTP 404: ${downloadUrl}`
|
`Download URL returned HTTP 404: ${downloadUrl}`,
|
||||||
|
{cause: err}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
|
`Failed to download Go ${versionSpec} for platform ${osPlat}/${arch} ` +
|
||||||
`from ${downloadUrl}: ${err}`
|
`from ${downloadUrl}: ${err}`,
|
||||||
|
{cause: err}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -218,7 +221,9 @@ export async function getGo(
|
|||||||
core.info('Install from dist');
|
core.info('Install from dist');
|
||||||
downloadPath = await installGoVersion(info, undefined, arch);
|
downloadPath = await installGoVersion(info, undefined, arch);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Failed to download version ${versionSpec}: ${err}`);
|
throw new Error(`Failed to download version ${versionSpec}: ${err}`, {
|
||||||
|
cause: err
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,9 +574,7 @@ export async function findMatch(
|
|||||||
let result: IGoVersion | undefined;
|
let result: IGoVersion | undefined;
|
||||||
let match: IGoVersion | undefined;
|
let match: IGoVersion | undefined;
|
||||||
|
|
||||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
const candidates: IGoVersion[] | null = await getVersionsDist(dlUrl);
|
||||||
dlUrl
|
|
||||||
);
|
|
||||||
if (!candidates) {
|
if (!candidates) {
|
||||||
throw new Error(`golang download url did not return results`);
|
throw new Error(`golang download url did not return results`);
|
||||||
}
|
}
|
||||||
@@ -607,17 +610,6 @@ export async function findMatch(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVersionsDist(
|
|
||||||
dlUrl: string
|
|
||||||
): Promise<IGoVersion[] | null> {
|
|
||||||
// this returns versions descending so latest is first
|
|
||||||
const http: httpm.HttpClient = new httpm.HttpClient('setup-go', [], {
|
|
||||||
allowRedirects: true,
|
|
||||||
maxRedirects: 3
|
|
||||||
});
|
|
||||||
return (await http.getJson<IGoVersion[]>(dlUrl)).result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Convert the go version syntax into semver for semver matching
|
// Convert the go version syntax into semver for semver matching
|
||||||
// 1.13.1 => 1.13.1
|
// 1.13.1 => 1.13.1
|
||||||
@@ -686,9 +678,8 @@ async function resolveStableVersionDist(
|
|||||||
) {
|
) {
|
||||||
const archFilter = sys.getArch(arch);
|
const archFilter = sys.getArch(arch);
|
||||||
const platFilter = sys.getPlatform();
|
const platFilter = sys.getPlatform();
|
||||||
const candidates: IGoVersion[] | null = await module.exports.getVersionsDist(
|
const candidates: IGoVersion[] | null =
|
||||||
GOLANG_DOWNLOAD_URL
|
await getVersionsDist(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`);
|
||||||
}
|
}
|
||||||
|
|||||||
69
src/main.ts
69
src/main.ts
@@ -1,14 +1,16 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as io from '@actions/io';
|
import * as io from '@actions/io';
|
||||||
import * as installer from './installer';
|
import * as installer from './installer.js';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {restoreCache} from './cache-restore';
|
import {fileURLToPath} from 'url';
|
||||||
import {isCacheFeatureAvailable} from './cache-utils';
|
import {restoreCache} from './cache-restore.js';
|
||||||
|
import {isCacheFeatureAvailable} from './cache-utils.js';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import {Architecture} from './types';
|
import {Architecture} from './types.js';
|
||||||
|
import {GO_ENV_OUTPUTS, GoEnv, Outputs} from './constants.js';
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
try {
|
try {
|
||||||
@@ -20,7 +22,6 @@ export async function run() {
|
|||||||
setGoToolchain();
|
setGoToolchain();
|
||||||
|
|
||||||
const cache = core.getBooleanInput('cache');
|
const cache = core.getBooleanInput('cache');
|
||||||
core.info(`Setup go version spec ${versionSpec}`);
|
|
||||||
|
|
||||||
let arch = core.getInput('architecture') as Architecture;
|
let arch = core.getInput('architecture') as Architecture;
|
||||||
|
|
||||||
@@ -29,6 +30,10 @@ export async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (versionSpec) {
|
if (versionSpec) {
|
||||||
|
core.startGroup('Installed version');
|
||||||
|
try {
|
||||||
|
core.info(`Setup go version spec ${versionSpec}`);
|
||||||
|
|
||||||
const token = core.getInput('token');
|
const token = core.getInput('token');
|
||||||
const auth = !token ? undefined : `token ${token}`;
|
const auth = !token ? undefined : `token ${token}`;
|
||||||
|
|
||||||
@@ -64,6 +69,9 @@ export async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Successfully set up Go version ${versionSpec}`);
|
core.info(`Successfully set up Go version ${versionSpec}`);
|
||||||
|
} finally {
|
||||||
|
core.endGroup();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
core.info(
|
core.info(
|
||||||
'[warning]go-version input was not specified. The action will try to use pre-installed version.'
|
'[warning]go-version input was not specified. The action will try to use pre-installed version.'
|
||||||
@@ -75,6 +83,11 @@ export async function run() {
|
|||||||
|
|
||||||
const goPath = await io.which('go');
|
const goPath = await io.which('go');
|
||||||
const goVersion = (cp.execSync(`${goPath} version`) || '').toString();
|
const goVersion = (cp.execSync(`${goPath} version`) || '').toString();
|
||||||
|
const goEnvJson = readGoEnv(goPath);
|
||||||
|
|
||||||
|
if (goEnvJson) {
|
||||||
|
setGoEnvOutputs(goEnvJson);
|
||||||
|
}
|
||||||
|
|
||||||
if (cache && isCacheFeatureAvailable()) {
|
if (cache && isCacheFeatureAvailable()) {
|
||||||
const packageManager = 'default';
|
const packageManager = 'default';
|
||||||
@@ -91,7 +104,11 @@ export async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add problem matchers
|
// add problem matchers
|
||||||
const matchersPath = path.join(__dirname, '../..', 'matchers.json');
|
const matchersPath = path.join(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'../..',
|
||||||
|
'matchers.json'
|
||||||
|
);
|
||||||
core.info(`##[add-matcher]${matchersPath}`);
|
core.info(`##[add-matcher]${matchersPath}`);
|
||||||
|
|
||||||
// output the version actually being used
|
// output the version actually being used
|
||||||
@@ -108,6 +125,46 @@ export async function run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readGoEnv(goPath: string): GoEnv | undefined {
|
||||||
|
try {
|
||||||
|
const rawGoEnv = cp.execFileSync(goPath, ['env', '-json'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
|
});
|
||||||
|
const parsed: unknown = JSON.parse(rawGoEnv);
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof parsed !== 'object' ||
|
||||||
|
parsed === null ||
|
||||||
|
Array.isArray(parsed)
|
||||||
|
) {
|
||||||
|
throw new Error("'go env -json' did not return a JSON object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed as GoEnv;
|
||||||
|
} catch (error) {
|
||||||
|
core.info(
|
||||||
|
`Unable to read 'go env -json', the Go environment outputs will not be set: ${
|
||||||
|
(error as Error).message
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setGoEnvOutputs(goEnv: GoEnv): void {
|
||||||
|
for (const name of GO_ENV_OUTPUTS) {
|
||||||
|
core.setOutput(name, goEnv[name] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
core.setOutput(Outputs.GoBinPath, goEnv.GOBIN || goPathBin(goEnv));
|
||||||
|
}
|
||||||
|
|
||||||
|
function goPathBin(goEnv: GoEnv): string {
|
||||||
|
const goPath = (goEnv.GOPATH ?? '').split(path.delimiter)[0];
|
||||||
|
return goPath ? path.join(goPath, 'bin') : '';
|
||||||
|
}
|
||||||
|
|
||||||
export async function addBinToPath(): Promise<boolean> {
|
export async function addBinToPath(): Promise<boolean> {
|
||||||
let added = false;
|
let added = false;
|
||||||
const g = await io.which('go');
|
const g = await io.which('go');
|
||||||
|
|||||||
@@ -2,14 +2,19 @@ type SupportedPackageManagers = {
|
|||||||
[prop: string]: PackageManagerInfo;
|
[prop: string]: PackageManagerInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CacheKind = 'modules' | 'build';
|
||||||
|
|
||||||
export interface PackageManagerInfo {
|
export interface PackageManagerInfo {
|
||||||
dependencyFilePattern: string;
|
dependencyFilePattern: string;
|
||||||
cacheFolderCommandList: string[];
|
cacheFolderCommandList: {kind: CacheKind; command: string}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const supportedPackageManagers: SupportedPackageManagers = {
|
export const supportedPackageManagers: SupportedPackageManagers = {
|
||||||
default: {
|
default: {
|
||||||
dependencyFilePattern: 'go.mod',
|
dependencyFilePattern: 'go.mod',
|
||||||
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
|
cacheFolderCommandList: [
|
||||||
|
{kind: 'modules', command: 'go env GOMODCACHE'},
|
||||||
|
{kind: 'build', command: 'go env GOCACHE'}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import {run} from './main';
|
import {run} from './main.js';
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import {Architecture} from './types';
|
import {Architecture} from './types.js';
|
||||||
|
|
||||||
export function getPlatform(): string {
|
export function getPlatform(): string {
|
||||||
// darwin and linux match already
|
// darwin and linux match already
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
"target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"module": "NodeNext", /* Specify module code generation. */
|
||||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
// "checkJs": true, /* Report errors in .js files. */
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|
||||||
/* Module Resolution Options */
|
/* Module Resolution Options */
|
||||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
// "moduleResolution": "NodeNext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "**/*.test.ts"]
|
"exclude": ["node_modules", "**/*.test.ts", "jest.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user