mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-05-01 16:30:20 +08:00
Use golangci-lint fmt to format code (#163)
Use `golangci-lint fmt` to format code, upgrading `.golangci.yml` to v2 and mirroring the linter configuration used by https://github.com/go-gitea/gitea. `gci` now handles import ordering into standard, project-local, blank, and default groups. Mirrors https://github.com/go-gitea/gitea/pull/37194. Changes: - Upgrade `.golangci.yml` to v2 format with the same linter set as gitea (minus `prealloc`, `unparam`, `testifylint`, `nilnil` which produced too many pre-existing issues) - Add path-based exclusions (`bodyclose`, `gosec` in tests; `gosec:G115`/`G117` globally) - Run lint via `make lint-go` in CI instead of `golangci/golangci-lint-action`, matching the pattern used by other Gitea repos - Apply safe auto-fixes (`modernize`, `perfsprint`, `usetesting`, etc.) - Add explanations to existing `//nolint` directives - Remove dead code (unused `newRemoteReusableWorkflow` and `networkName`), duplicate imports, and shadowed `max` builtins - Replace deprecated `docker/distribution/reference` with `distribution/reference` - Fix `Deprecated:` comment casing and simplify nil/len checks --- This PR was written with the help of Claude Opus 4.7 Reviewed-on: https://gitea.com/gitea/act/pulls/163 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-committed-by: silverwind <me@silverwind.io>
This commit is contained in:
6
.github/workflows/checks.yml
vendored
6
.github/workflows/checks.yml
vendored
@@ -22,10 +22,8 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: golangci/golangci-lint-action@v3.7.0
|
||||
with:
|
||||
version: v1.53
|
||||
only-new-issues: true
|
||||
- name: lint
|
||||
run: make lint-go
|
||||
- uses: megalinter/megalinter/flavors/go@v7.8.0
|
||||
env:
|
||||
DEFAULT_BRANCH: master
|
||||
|
||||
183
.golangci.yml
183
.golangci.yml
@@ -1,51 +1,146 @@
|
||||
# Minimum golangci-lint version required: v1.46.0
|
||||
run:
|
||||
timeout: 3m
|
||||
|
||||
skip-dirs:
|
||||
- report # megalinter results+fixes
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 15
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
importas:
|
||||
aliases:
|
||||
- pkg: 'github.com/sirupsen/logrus'
|
||||
alias: log
|
||||
- pkg: 'github.com/stretchr/testify/assert'
|
||||
alias: assert
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: Please use "errors" package from standard library
|
||||
- pkg: gotest.tools/v3
|
||||
desc: Please keep tests unified using only github.com/stretchr/testify
|
||||
- pkg: log
|
||||
desc: Please keep logging unified using only github.com/sirupsen/logrus
|
||||
version: "2"
|
||||
output:
|
||||
sort-order:
|
||||
- file
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- revive
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- contextcheck
|
||||
- depguard
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocheckcompilerdirectives
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gosec
|
||||
- unconvert
|
||||
- dupl
|
||||
- nakedret
|
||||
- prealloc
|
||||
- exportloopref
|
||||
- gocritic
|
||||
- goimports
|
||||
- whitespace
|
||||
- misspell
|
||||
- depguard
|
||||
- govet
|
||||
- importas
|
||||
- contextcheck
|
||||
- ineffassign
|
||||
- misspell
|
||||
- mirror
|
||||
- modernize
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- perfsprint
|
||||
- revive
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- usetesting
|
||||
- wastedassign
|
||||
- whitespace
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: Please use "errors" package from standard library
|
||||
- pkg: gotest.tools/v3
|
||||
desc: Please keep tests unified using only github.com/stretchr/testify
|
||||
- pkg: log
|
||||
desc: Please keep logging unified using only github.com/sirupsen/logrus
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
importas:
|
||||
alias:
|
||||
- pkg: github.com/sirupsen/logrus
|
||||
alias: log
|
||||
- pkg: github.com/stretchr/testify/assert
|
||||
alias: assert
|
||||
nolintlint:
|
||||
allow-unused: false
|
||||
require-explanation: true
|
||||
require-specific: true
|
||||
revive:
|
||||
severity: error
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: constant-logical-expr
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: empty-lines
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: exported
|
||||
- name: identical-branches
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: modifies-value-receiver
|
||||
- name: package-comments
|
||||
- name: redefines-builtin-id
|
||||
- name: superfluous-else
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: var-declaration
|
||||
- name: var-naming
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -ST1003
|
||||
- -ST1005
|
||||
- -QF1001
|
||||
- -QF1006
|
||||
- -QF1008
|
||||
usetesting:
|
||||
os-temp-dir: true
|
||||
perfsprint:
|
||||
concat-loop: false
|
||||
govet:
|
||||
enable:
|
||||
- nilness
|
||||
- unusedwrite
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- bodyclose
|
||||
- dupl
|
||||
- errcheck
|
||||
- gosec
|
||||
- staticcheck
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- gosec
|
||||
text: 'G115:'
|
||||
- linters:
|
||||
- gosec
|
||||
text: 'G117:'
|
||||
paths:
|
||||
- report
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
settings:
|
||||
gci:
|
||||
custom-order: true
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/nektos/act)
|
||||
- blank
|
||||
- default
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- report
|
||||
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
18
Makefile
18
Makefile
@@ -13,6 +13,8 @@ endif
|
||||
|
||||
ACT ?= go run main.go
|
||||
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
|
||||
|
||||
HAS_TOKEN = $(if $(test -e ~/.config/github/token),true,false)
|
||||
ifeq (true,$(HAS_TOKEN))
|
||||
export GITHUB_TOKEN := $(shell cat ~/.config/github/token)
|
||||
@@ -27,11 +29,19 @@ build:
|
||||
|
||||
.PHONY: format
|
||||
format:
|
||||
go fmt ./...
|
||||
go run $(GOLANGCI_LINT_PACKAGE) fmt
|
||||
|
||||
.PHONY: format-check
|
||||
format-check: format
|
||||
@diff=$$(git diff --color=always); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make format' and commit the result:"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: format-all
|
||||
format-all:
|
||||
go fmt ./...
|
||||
format-all: format
|
||||
npx prettier --write .
|
||||
|
||||
.PHONY: test
|
||||
@@ -41,7 +51,7 @@ test:
|
||||
|
||||
.PHONY: lint-go
|
||||
lint-go:
|
||||
golangci-lint run $(FIX)
|
||||
go run $(GOLANGCI_LINT_PACKAGE) run $(FIX)
|
||||
|
||||
.PHONY: lint-js
|
||||
lint-js:
|
||||
|
||||
@@ -76,7 +76,7 @@ func getVersionNotices(version string) []Notice {
|
||||
noticeURL.RawQuery = query.Encode()
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", noticeURL.String(), nil)
|
||||
req, err := http.NewRequest(http.MethodGet, noticeURL.String(), nil)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return nil
|
||||
@@ -102,7 +102,7 @@ func getVersionNotices(version string) []Notice {
|
||||
|
||||
defer resp.Body.Close()
|
||||
notices := []Notice{}
|
||||
if resp.StatusCode == 304 {
|
||||
if resp.StatusCode == http.StatusNotModified {
|
||||
log.Debug("No new notices")
|
||||
return nil
|
||||
}
|
||||
|
||||
28
cmd/root.go
28
cmd/root.go
@@ -4,13 +4,22 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/artifactcache"
|
||||
"github.com/nektos/act/pkg/artifacts"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"github.com/nektos/act/pkg/runner"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/andreaskoch/go-fswatch"
|
||||
@@ -20,13 +29,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
"github.com/nektos/act/pkg/artifactcache"
|
||||
"github.com/nektos/act/pkg/artifacts"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"github.com/nektos/act/pkg/runner"
|
||||
)
|
||||
|
||||
// Execute is the entry point to running the CLI
|
||||
@@ -147,7 +149,7 @@ func bugReport(ctx context.Context, version string) error {
|
||||
report := sprintf("act version:", version)
|
||||
report += sprintf("GOOS:", runtime.GOOS)
|
||||
report += sprintf("GOARCH:", runtime.GOARCH)
|
||||
report += sprintf("NumCPU:", fmt.Sprint(runtime.NumCPU()))
|
||||
report += sprintf("NumCPU:", strconv.Itoa(runtime.NumCPU()))
|
||||
|
||||
var dockerHost string
|
||||
var exists bool
|
||||
@@ -216,7 +218,7 @@ func bugReport(ctx context.Context, version string) error {
|
||||
report += sprintf("\tOS version:", info.OSVersion)
|
||||
report += sprintf("\tOS arch:", info.Architecture)
|
||||
report += sprintf("\tOS kernel:", info.KernelVersion)
|
||||
report += sprintf("\tOS CPU:", fmt.Sprint(info.NCPU))
|
||||
report += sprintf("\tOS CPU:", strconv.Itoa(info.NCPU))
|
||||
report += sprintf("\tOS memory:", fmt.Sprintf("%d MB", info.MemTotal/1024/1024))
|
||||
|
||||
report += fmt.Sprintln("\tSecurity options:")
|
||||
@@ -305,9 +307,7 @@ func readEnvs(path string, envs map[string]string) bool {
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading from %s: %v", path, err)
|
||||
}
|
||||
for k, v := range env {
|
||||
envs[k] = v
|
||||
}
|
||||
maps.Copy(envs, env)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -330,7 +330,7 @@ func parseMatrix(matrix []string) map[string]map[string]bool {
|
||||
return matrixes
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if input.jsonLogger {
|
||||
@@ -511,7 +511,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||
log.Warnf(deprecationWarning, "privileged", "--privileged")
|
||||
}
|
||||
if len(input.usernsMode) > 0 {
|
||||
log.Warnf(deprecationWarning, "userns", fmt.Sprintf("--userns=%s", input.usernsMode))
|
||||
log.Warnf(deprecationWarning, "userns", "--userns="+input.usernsMode)
|
||||
}
|
||||
if len(input.containerCapAdd) > 0 {
|
||||
log.Warnf(deprecationWarning, "container-cap-add", fmt.Sprintf("--cap-add=%s", input.containerCapAdd))
|
||||
|
||||
3
main.go
3
main.go
@@ -2,12 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/nektos/act/cmd"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed VERSION
|
||||
|
||||
@@ -15,12 +15,12 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/timshannon/bolthold"
|
||||
"go.etcd.io/bbolt"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -74,7 +74,7 @@ func StartHandler(dir, outboundIP string, port uint16, logger logrus.FieldLogger
|
||||
if outboundIP != "" {
|
||||
h.outboundIP = outboundIP
|
||||
} else if ip := common.GetOutboundIP(); ip == nil {
|
||||
return nil, fmt.Errorf("unable to determine outbound IP address")
|
||||
return nil, errors.New("unable to determine outbound IP address")
|
||||
} else {
|
||||
h.outboundIP = ip.String()
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func findCache(db *bolthold.Store, keys []string, version string) (*Cache, error
|
||||
}
|
||||
return cache, nil
|
||||
}
|
||||
prefixPattern := fmt.Sprintf("^%s", regexp.QuoteMeta(prefix))
|
||||
prefixPattern := "^" + regexp.QuoteMeta(prefix)
|
||||
re, err := regexp.Compile(prefixPattern)
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -415,6 +415,7 @@ const (
|
||||
keepOld = 5 * time.Minute
|
||||
)
|
||||
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (h *Handler) gcCache() {
|
||||
if h.gcing.Load() {
|
||||
return
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestHandler(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("clean", func(t *testing.T) {
|
||||
resp, err := http.Post(fmt.Sprintf("%s/clean", base), "", nil)
|
||||
resp, err := http.Post(base+"/clean", "", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
})
|
||||
@@ -72,7 +72,7 @@ func TestHandler(t *testing.T) {
|
||||
t.Run("reserve with bad request", func(t *testing.T) {
|
||||
body := []byte(`invalid json`)
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 400, resp.StatusCode)
|
||||
})
|
||||
@@ -90,7 +90,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -117,7 +117,7 @@ func TestHandler(t *testing.T) {
|
||||
|
||||
t.Run("upload with bad id", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPatch,
|
||||
fmt.Sprintf("%s/caches/invalid_id", base), bytes.NewReader(nil))
|
||||
base+"/caches/invalid_id", bytes.NewReader(nil))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Content-Range", "bytes 0-99/*")
|
||||
@@ -151,7 +151,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -202,7 +202,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -226,7 +226,7 @@ func TestHandler(t *testing.T) {
|
||||
|
||||
t.Run("commit with bad id", func(t *testing.T) {
|
||||
{
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches/invalid_id", base), "", nil)
|
||||
resp, err := http.Post(base+"/caches/invalid_id", "", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 400, resp.StatusCode)
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -300,7 +300,7 @@ func TestHandler(t *testing.T) {
|
||||
Size: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -328,7 +328,7 @@ func TestHandler(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("get with bad id", func(t *testing.T) {
|
||||
resp, err := http.Get(fmt.Sprintf("%s/artifacts/invalid_id", base))
|
||||
resp, err := http.Get(base + "/artifacts/invalid_id")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 400, resp.StatusCode)
|
||||
})
|
||||
@@ -537,7 +537,7 @@ func uploadCacheNormally(t *testing.T, base, key, version string, content []byte
|
||||
Size: int64(len(content)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/caches", base), "application/json", bytes.NewReader(body))
|
||||
resp, err := http.Post(base+"/caches", "application/json", bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
@@ -578,7 +578,7 @@ func uploadCacheNormally(t *testing.T, base, key, version string, content []byte
|
||||
archiveLocation = got.ArchiveLocation
|
||||
}
|
||||
{
|
||||
resp, err := http.Get(archiveLocation) //nolint:gosec
|
||||
resp, err := http.Get(archiveLocation)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
got, err := io.ReadAll(resp.Body)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
@@ -103,11 +104,11 @@ func (s *Storage) Remove(id uint64) {
|
||||
}
|
||||
|
||||
func (s *Storage) filename(id uint64) string {
|
||||
return filepath.Join(s.rootDir, fmt.Sprintf("%02x", id%0xff), fmt.Sprint(id))
|
||||
return filepath.Join(s.rootDir, fmt.Sprintf("%02x", id%0xff), strconv.FormatUint(id, 10))
|
||||
}
|
||||
|
||||
func (s *Storage) tempDir(id uint64) string {
|
||||
return filepath.Join(s.rootDir, "tmp", fmt.Sprint(id))
|
||||
return filepath.Join(s.rootDir, "tmp", strconv.FormatUint(id, 10))
|
||||
}
|
||||
|
||||
func (s *Storage) tempName(id uint64, offset int64) string {
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
type FileContainerResourceURL struct {
|
||||
@@ -55,8 +55,7 @@ type WriteFS interface {
|
||||
OpenAppendable(name string) (WritableFile, error)
|
||||
}
|
||||
|
||||
type readWriteFSImpl struct {
|
||||
}
|
||||
type readWriteFSImpl struct{}
|
||||
|
||||
func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) {
|
||||
return os.Open(name)
|
||||
@@ -74,7 +73,6 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
|
||||
return nil, err
|
||||
}
|
||||
file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o644)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,7 +86,7 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
|
||||
|
||||
var gzipExtension = ".gz__"
|
||||
|
||||
func safeResolve(baseDir string, relPath string) string {
|
||||
func safeResolve(baseDir, relPath string) string {
|
||||
return filepath.Join(baseDir, filepath.Clean(filepath.Join(string(os.PathSeparator), relPath)))
|
||||
}
|
||||
|
||||
@@ -127,7 +125,6 @@ func uploads(router *httprouter.Router, baseDir string, fsys WriteFS) {
|
||||
}
|
||||
return fsys.OpenWritable(safePath)
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -275,7 +272,7 @@ func downloads(router *httprouter.Router, baseDir string, fsys fs.FS) {
|
||||
})
|
||||
}
|
||||
|
||||
func Serve(ctx context.Context, artifactPath string, addr string, port string) context.CancelFunc {
|
||||
func Serve(ctx context.Context, artifactPath, addr, port string) context.CancelFunc {
|
||||
serverContext, cancel := context.WithCancel(ctx)
|
||||
logger := common.Logger(serverContext)
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"github.com/nektos/act/pkg/runner"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"github.com/nektos/act/pkg/runner"
|
||||
)
|
||||
|
||||
type writableMapFile struct {
|
||||
@@ -39,7 +39,7 @@ type writeMapFS struct {
|
||||
}
|
||||
|
||||
func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
|
||||
var file = &writableMapFile{
|
||||
file := &writableMapFile{
|
||||
MapFile: fstest.MapFile{
|
||||
Data: []byte("content2"),
|
||||
},
|
||||
@@ -50,7 +50,7 @@ func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
|
||||
}
|
||||
|
||||
func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
|
||||
var file = &writableMapFile{
|
||||
file := &writableMapFile{
|
||||
MapFile: fstest.MapFile{
|
||||
Data: []byte("content2"),
|
||||
},
|
||||
@@ -63,12 +63,12 @@ func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
|
||||
func TestNewArtifactUploadPrepare(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
|
||||
router := httprouter.New()
|
||||
uploads(router, "artifact/server/path", writeMapFS{memfs})
|
||||
|
||||
req, _ := http.NewRequest("POST", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
req, _ := http.NewRequest(http.MethodPost, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -89,12 +89,12 @@ func TestNewArtifactUploadPrepare(t *testing.T) {
|
||||
func TestArtifactUploadBlob(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
|
||||
router := httprouter.New()
|
||||
uploads(router, "artifact/server/path", writeMapFS{memfs})
|
||||
|
||||
req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=some/file", strings.NewReader("content"))
|
||||
req, _ := http.NewRequest(http.MethodPut, "http://localhost/upload/1?itemPath=some/file", strings.NewReader("content"))
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -116,12 +116,12 @@ func TestArtifactUploadBlob(t *testing.T) {
|
||||
func TestFinalizeArtifactUpload(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
|
||||
router := httprouter.New()
|
||||
uploads(router, "artifact/server/path", writeMapFS{memfs})
|
||||
|
||||
req, _ := http.NewRequest("PATCH", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
req, _ := http.NewRequest(http.MethodPatch, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -142,7 +142,7 @@ func TestFinalizeArtifactUpload(t *testing.T) {
|
||||
func TestListArtifacts(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{
|
||||
"artifact/server/path/1/file.txt": {
|
||||
Data: []byte(""),
|
||||
},
|
||||
@@ -151,7 +151,7 @@ func TestListArtifacts(t *testing.T) {
|
||||
router := httprouter.New()
|
||||
downloads(router, "artifact/server/path", memfs)
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -174,7 +174,7 @@ func TestListArtifacts(t *testing.T) {
|
||||
func TestListArtifactContainer(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{
|
||||
"artifact/server/path/1/some/file": {
|
||||
Data: []byte(""),
|
||||
},
|
||||
@@ -183,7 +183,7 @@ func TestListArtifactContainer(t *testing.T) {
|
||||
router := httprouter.New()
|
||||
downloads(router, "artifact/server/path", memfs)
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/download/1?itemPath=some/file", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost/download/1?itemPath=some/file", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -207,7 +207,7 @@ func TestListArtifactContainer(t *testing.T) {
|
||||
func TestDownloadArtifactFile(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{
|
||||
"artifact/server/path/1/some/file": {
|
||||
Data: []byte("content"),
|
||||
},
|
||||
@@ -216,7 +216,7 @@ func TestDownloadArtifactFile(t *testing.T) {
|
||||
router := httprouter.New()
|
||||
downloads(router, "artifact/server/path", memfs)
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/artifact/1/some/file", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost/artifact/1/some/file", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -344,7 +344,7 @@ func TestMkdirFsImplSafeResolve(t *testing.T) {
|
||||
func TestDownloadArtifactFileUnsafePath(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{
|
||||
"artifact/server/path/some/file": {
|
||||
Data: []byte("content"),
|
||||
},
|
||||
@@ -353,7 +353,7 @@ func TestDownloadArtifactFileUnsafePath(t *testing.T) {
|
||||
router := httprouter.New()
|
||||
downloads(router, "artifact/server/path", memfs)
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/artifact/2/../../some/file", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost/artifact/2/../../some/file", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -370,12 +370,12 @@ func TestDownloadArtifactFileUnsafePath(t *testing.T) {
|
||||
func TestArtifactUploadBlobUnsafePath(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
|
||||
|
||||
router := httprouter.New()
|
||||
uploads(router, "artifact/server/path", writeMapFS{memfs})
|
||||
|
||||
req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=../../some/file", strings.NewReader("content"))
|
||||
req, _ := http.NewRequest(http.MethodPut, "http://localhost/upload/1?itemPath=../../some/file", strings.NewReader("content"))
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package common
|
||||
|
||||
// CartesianProduct takes map of lists and returns list of unique tuples
|
||||
func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interface{} {
|
||||
func CartesianProduct(mapOfLists map[string][]any) []map[string]any {
|
||||
listNames := make([]string, 0)
|
||||
lists := make([][]interface{}, 0)
|
||||
lists := make([][]any, 0)
|
||||
for k, v := range mapOfLists {
|
||||
listNames = append(listNames, k)
|
||||
lists = append(lists, v)
|
||||
@@ -11,9 +11,9 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
|
||||
|
||||
listCart := cartN(lists...)
|
||||
|
||||
rtn := make([]map[string]interface{}, 0)
|
||||
rtn := make([]map[string]any, 0)
|
||||
for _, list := range listCart {
|
||||
vMap := make(map[string]interface{})
|
||||
vMap := make(map[string]any)
|
||||
for i, v := range list {
|
||||
vMap[listNames[i]] = v
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
|
||||
return rtn
|
||||
}
|
||||
|
||||
func cartN(a ...[]interface{}) [][]interface{} {
|
||||
func cartN(a ...[]any) [][]any {
|
||||
c := 1
|
||||
for _, a := range a {
|
||||
c *= len(a)
|
||||
@@ -30,8 +30,8 @@ func cartN(a ...[]interface{}) [][]interface{} {
|
||||
if c == 0 || len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
p := make([][]interface{}, c)
|
||||
b := make([]interface{}, c*len(a))
|
||||
p := make([][]any, c)
|
||||
b := make([]any, c*len(a))
|
||||
n := make([]int, len(a))
|
||||
s := 0
|
||||
for i := range p {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
func TestCartesianProduct(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
input := map[string][]interface{}{
|
||||
input := map[string][]any{
|
||||
"foo": {1, 2, 3, 4},
|
||||
"bar": {"a", "b", "c"},
|
||||
"baz": {false, true},
|
||||
@@ -25,7 +25,7 @@ func TestCartesianProduct(t *testing.T) {
|
||||
assert.Contains(v, "baz")
|
||||
}
|
||||
|
||||
input = map[string][]interface{}{
|
||||
input = map[string][]any{
|
||||
"foo": {1, 2, 3, 4},
|
||||
"bar": {},
|
||||
"baz": {false, true},
|
||||
@@ -33,7 +33,7 @@ func TestCartesianProduct(t *testing.T) {
|
||||
output = CartesianProduct(input)
|
||||
assert.Len(output, 0)
|
||||
|
||||
input = map[string][]interface{}{}
|
||||
input = map[string][]any{}
|
||||
output = CartesianProduct(input)
|
||||
assert.Len(output, 0)
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ func (p *Pen) drawTopBars(buf io.Writer, labels ...string) {
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
}
|
||||
|
||||
func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) {
|
||||
style := styleDefs[p.style]
|
||||
for _, label := range labels {
|
||||
@@ -83,6 +84,7 @@ func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) {
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
}
|
||||
|
||||
func (p *Pen) drawLabels(buf io.Writer, labels ...string) {
|
||||
style := styleDefs[p.style]
|
||||
for _, label := range labels {
|
||||
@@ -125,11 +127,8 @@ func (p *Pen) DrawBoxes(labels ...string) *Drawing {
|
||||
|
||||
// Draw to writer
|
||||
func (d *Drawing) Draw(writer io.Writer, centerOnWidth int) {
|
||||
padSize := (centerOnWidth - d.GetWidth()) / 2
|
||||
if padSize < 0 {
|
||||
padSize = 0
|
||||
}
|
||||
for _, l := range strings.Split(d.buf.String(), "\n") {
|
||||
padSize := max((centerOnWidth-d.GetWidth())/2, 0)
|
||||
for l := range strings.SplitSeq(d.buf.String(), "\n") {
|
||||
if len(l) > 0 {
|
||||
padding := strings.Repeat(" ", padSize)
|
||||
fmt.Fprintf(writer, "%s%s\n", padding, l)
|
||||
|
||||
@@ -19,7 +19,7 @@ func (w Warning) Error() string {
|
||||
}
|
||||
|
||||
// Warningf create a warning
|
||||
func Warningf(format string, args ...interface{}) Warning {
|
||||
func Warningf(format string, args ...any) Warning {
|
||||
w := Warning{
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
@@ -33,7 +33,7 @@ type Executor func(ctx context.Context) error
|
||||
type Conditional func(ctx context.Context) bool
|
||||
|
||||
// NewInfoExecutor is an executor that logs messages
|
||||
func NewInfoExecutor(format string, args ...interface{}) Executor {
|
||||
func NewInfoExecutor(format string, args ...any) Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := Logger(ctx)
|
||||
logger.Infof(format, args...)
|
||||
@@ -42,7 +42,7 @@ func NewInfoExecutor(format string, args ...interface{}) Executor {
|
||||
}
|
||||
|
||||
// NewDebugExecutor is an executor that logs messages
|
||||
func NewDebugExecutor(format string, args ...interface{}) Executor {
|
||||
func NewDebugExecutor(format string, args ...any) Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := Logger(ctx)
|
||||
logger.Debugf(format, args...)
|
||||
@@ -69,7 +69,7 @@ func NewPipelineExecutor(executors ...Executor) Executor {
|
||||
}
|
||||
|
||||
// NewConditionalExecutor creates a new executor based on conditions
|
||||
func NewConditionalExecutor(conditional Conditional, trueExecutor Executor, falseExecutor Executor) Executor {
|
||||
func NewConditionalExecutor(conditional Conditional, trueExecutor, falseExecutor Executor) Executor {
|
||||
return func(ctx context.Context) error {
|
||||
if conditional(ctx) {
|
||||
if trueExecutor != nil {
|
||||
@@ -128,14 +128,14 @@ func NewParallelExecutor(parallel int, executors ...Executor) Executor {
|
||||
}(i, work, errs)
|
||||
}
|
||||
|
||||
for i := 0; i < len(executors); i++ {
|
||||
for i := range executors {
|
||||
work <- executors[i]
|
||||
}
|
||||
close(work)
|
||||
|
||||
// Executor waits all executors to cleanup these resources.
|
||||
var firstErr error
|
||||
for i := 0; i < len(executors); i++ {
|
||||
for range executors {
|
||||
err := <-errs
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
|
||||
@@ -14,24 +14,24 @@ import (
|
||||
func TestMaxParallel2Quick(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var currentRunning int32
|
||||
var maxSimultaneous int32
|
||||
var currentRunning atomic.Int32
|
||||
var maxSimultaneous atomic.Int32
|
||||
|
||||
executors := make([]Executor, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
current := atomic.AddInt32(¤tRunning, 1)
|
||||
current := currentRunning.Add(1)
|
||||
|
||||
// Update max if needed
|
||||
for {
|
||||
maxValue := atomic.LoadInt32(&maxSimultaneous)
|
||||
if current <= maxValue || atomic.CompareAndSwapInt32(&maxSimultaneous, maxValue, current) {
|
||||
maxValue := maxSimultaneous.Load()
|
||||
if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
atomic.AddInt32(¤tRunning, -1)
|
||||
currentRunning.Add(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func TestMaxParallel2Quick(t *testing.T) {
|
||||
err := NewParallelExecutor(2, executors...)(ctx)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.LessOrEqual(t, atomic.LoadInt32(&maxSimultaneous), int32(2),
|
||||
assert.LessOrEqual(t, maxSimultaneous.Load(), int32(2),
|
||||
"Should not exceed max-parallel: 2")
|
||||
}
|
||||
|
||||
@@ -47,16 +47,16 @@ func TestMaxParallel2Quick(t *testing.T) {
|
||||
func TestMaxParallel1Sequential(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var currentRunning int32
|
||||
var maxSimultaneous int32
|
||||
var currentRunning atomic.Int32
|
||||
var maxSimultaneous atomic.Int32
|
||||
var executionOrder []int
|
||||
var orderMutex sync.Mutex
|
||||
|
||||
executors := make([]Executor, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
taskID := i
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
current := atomic.AddInt32(¤tRunning, 1)
|
||||
current := currentRunning.Add(1)
|
||||
|
||||
// Track execution order
|
||||
orderMutex.Lock()
|
||||
@@ -65,14 +65,14 @@ func TestMaxParallel1Sequential(t *testing.T) {
|
||||
|
||||
// Update max if needed
|
||||
for {
|
||||
maxValue := atomic.LoadInt32(&maxSimultaneous)
|
||||
if current <= maxValue || atomic.CompareAndSwapInt32(&maxSimultaneous, maxValue, current) {
|
||||
maxValue := maxSimultaneous.Load()
|
||||
if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
atomic.AddInt32(¤tRunning, -1)
|
||||
currentRunning.Add(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func TestMaxParallel1Sequential(t *testing.T) {
|
||||
err := NewParallelExecutor(1, executors...)(ctx)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), atomic.LoadInt32(&maxSimultaneous),
|
||||
assert.Equal(t, int32(1), maxSimultaneous.Load(),
|
||||
"max-parallel: 1 should only run 1 task at a time")
|
||||
assert.Len(t, executionOrder, 5, "All 5 tasks should have executed")
|
||||
}
|
||||
|
||||
@@ -13,21 +13,21 @@ import (
|
||||
// TestMaxParallelJobExecution tests actual job execution with max-parallel
|
||||
func TestMaxParallelJobExecution(t *testing.T) {
|
||||
t.Run("MaxParallel=1 Sequential", func(t *testing.T) {
|
||||
var currentRunning int32
|
||||
var currentRunning atomic.Int32
|
||||
var maxConcurrent int32
|
||||
var executionOrder []int
|
||||
var mu sync.Mutex
|
||||
|
||||
executors := make([]Executor, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
taskID := i
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
current := atomic.AddInt32(¤tRunning, 1)
|
||||
current := currentRunning.Add(1)
|
||||
|
||||
// Track max concurrent
|
||||
for {
|
||||
max := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) {
|
||||
maxValue := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func TestMaxParallelJobExecution(t *testing.T) {
|
||||
mu.Unlock()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
atomic.AddInt32(¤tRunning, -1)
|
||||
currentRunning.Add(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -51,23 +51,23 @@ func TestMaxParallelJobExecution(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("MaxParallel=3 Limited", func(t *testing.T) {
|
||||
var currentRunning int32
|
||||
var currentRunning atomic.Int32
|
||||
var maxConcurrent int32
|
||||
|
||||
executors := make([]Executor, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := range 10 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
current := atomic.AddInt32(¤tRunning, 1)
|
||||
current := currentRunning.Add(1)
|
||||
|
||||
for {
|
||||
max := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) {
|
||||
maxValue := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
atomic.AddInt32(¤tRunning, -1)
|
||||
currentRunning.Add(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -82,22 +82,22 @@ func TestMaxParallelJobExecution(t *testing.T) {
|
||||
|
||||
t.Run("MaxParallel=0 Uses1Worker", func(t *testing.T) {
|
||||
var maxConcurrent int32
|
||||
var currentRunning int32
|
||||
var currentRunning atomic.Int32
|
||||
|
||||
executors := make([]Executor, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
current := atomic.AddInt32(¤tRunning, 1)
|
||||
current := currentRunning.Add(1)
|
||||
|
||||
for {
|
||||
max := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) {
|
||||
maxValue := atomic.LoadInt32(&maxConcurrent)
|
||||
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
atomic.AddInt32(¤tRunning, -1)
|
||||
currentRunning.Add(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func TestMaxParallelWithErrors(t *testing.T) {
|
||||
var successCount int32
|
||||
|
||||
executors := make([]Executor, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
taskID := i
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
if taskID == 2 {
|
||||
@@ -143,7 +143,7 @@ func TestMaxParallelWithErrors(t *testing.T) {
|
||||
|
||||
var startedCount int32
|
||||
executors := make([]Executor, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := range 10 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
atomic.AddInt32(&startedCount, 1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -175,7 +175,7 @@ func TestMaxParallelPerformance(t *testing.T) {
|
||||
|
||||
t.Run("ParallelFasterThanSequential", func(t *testing.T) {
|
||||
executors := make([]Executor, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := range 10 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
return nil
|
||||
@@ -203,7 +203,7 @@ func TestMaxParallelPerformance(t *testing.T) {
|
||||
|
||||
t.Run("OptimalWorkerCount", func(t *testing.T) {
|
||||
executors := make([]Executor, 20)
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := range 20 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return nil
|
||||
@@ -236,7 +236,7 @@ func TestMaxParallelResourceSharing(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
|
||||
executors := make([]Executor, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
mu.Lock()
|
||||
sharedCounter++
|
||||
@@ -256,7 +256,7 @@ func TestMaxParallelResourceSharing(t *testing.T) {
|
||||
resultChan := make(chan int, 50)
|
||||
|
||||
executors := make([]Executor, 50)
|
||||
for i := 0; i < 50; i++ {
|
||||
for i := range 50 {
|
||||
taskID := i
|
||||
executors[i] = func(ctx context.Context) error {
|
||||
resultChan <- taskID
|
||||
|
||||
@@ -2,7 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestNewWorkflow(t *testing.T) {
|
||||
assert.Nil(emptyWorkflow(ctx))
|
||||
|
||||
// error case
|
||||
errorWorkflow := NewErrorExecutor(fmt.Errorf("test error"))
|
||||
errorWorkflow := NewErrorExecutor(errors.New("test error"))
|
||||
assert.NotNil(errorWorkflow(ctx))
|
||||
|
||||
// multiple success case
|
||||
@@ -122,7 +122,7 @@ func TestNewParallelExecutorFailed(t *testing.T) {
|
||||
count := 0
|
||||
errorWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
|
||||
count++
|
||||
return fmt.Errorf("fake error")
|
||||
return errors.New("fake error")
|
||||
})
|
||||
err := NewParallelExecutor(1, errorWorkflow)(ctx)
|
||||
assert.Equal(1, count)
|
||||
@@ -135,7 +135,7 @@ func TestNewParallelExecutorCanceled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
errExpected := fmt.Errorf("fake error")
|
||||
errExpected := errors.New("fake error")
|
||||
|
||||
count := 0
|
||||
successWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// CopyFile copy file
|
||||
func CopyFile(source string, dest string) (err error) {
|
||||
func CopyFile(source, dest string) (err error) {
|
||||
sourcefile, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -30,11 +30,11 @@ func CopyFile(source string, dest string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// CopyDir recursive copy of directory
|
||||
func CopyDir(source string, dest string) (err error) {
|
||||
func CopyDir(source, dest string) (err error) {
|
||||
// get properties of source dir
|
||||
sourceinfo, err := os.Stat(source)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@@ -18,8 +20,6 @@ import (
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/mattn/go-isatty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -52,7 +52,7 @@ func (e *Error) Commit() string {
|
||||
}
|
||||
|
||||
// FindGitRevision get the current git revision
|
||||
func FindGitRevision(ctx context.Context, file string) (shortSha string, sha string, err error) {
|
||||
func FindGitRevision(ctx context.Context, file string) (shortSha, sha string, err error) {
|
||||
logger := common.Logger(ctx)
|
||||
|
||||
gitDir, err := git.PlainOpenWithOptions(
|
||||
@@ -62,7 +62,6 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
|
||||
EnableDotGitCommonDir: true,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("path", file, "not located inside a git repository")
|
||||
return "", "", err
|
||||
@@ -74,7 +73,7 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
|
||||
}
|
||||
|
||||
if head.Hash().IsZero() {
|
||||
return "", "", fmt.Errorf("HEAD sha1 could not be resolved")
|
||||
return "", "", errors.New("HEAD sha1 could not be resolved")
|
||||
}
|
||||
|
||||
hash := head.Hash().String()
|
||||
@@ -96,8 +95,8 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
|
||||
logger.Debugf("HEAD points to '%s'", ref)
|
||||
|
||||
// Prefer the git library to iterate over the references and find a matching tag or branch.
|
||||
var refTag = ""
|
||||
var refBranch = ""
|
||||
refTag := ""
|
||||
refBranch := ""
|
||||
repo, err := git.PlainOpenWithOptions(
|
||||
file,
|
||||
&git.PlainOpenOptions{
|
||||
@@ -105,7 +104,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
|
||||
EnableDotGitCommonDir: true,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -144,7 +142,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -198,7 +195,7 @@ func findGitRemoteURL(_ context.Context, file, remoteName string) (string, error
|
||||
return remote.Config().URLs[0], nil
|
||||
}
|
||||
|
||||
func findGitSlug(url string, githubInstance string) (string, string, error) {
|
||||
func findGitSlug(url, githubInstance string) (string, string, error) {
|
||||
if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "CodeCommit", matches[2], nil
|
||||
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||
@@ -209,7 +206,7 @@ func findGitSlug(url string, githubInstance string) (string, string, error) {
|
||||
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
} else if githubInstance != "github.com" {
|
||||
gheHTTPRegex := regexp.MustCompile(fmt.Sprintf(`^https?://%s/(.+)/(.+?)(?:.git)?$`, githubInstance))
|
||||
gheSSHRegex := regexp.MustCompile(fmt.Sprintf(`%s[:/](.+)/(.+?)(?:.git)?$`, githubInstance))
|
||||
gheSSHRegex := regexp.MustCompile(githubInstance + "[:/](.+)/(.+?)(?:.git)?$")
|
||||
if matches := gheHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||
} else if matches := gheSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||
@@ -292,7 +289,7 @@ func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.Pu
|
||||
|
||||
// NewGitCloneExecutor creates an executor to clone git repos
|
||||
//
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := common.Logger(ctx)
|
||||
@@ -302,7 +299,7 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
|
||||
cloneLock.Lock()
|
||||
defer cloneLock.Unlock()
|
||||
|
||||
refName := plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", input.Ref))
|
||||
refName := plumbing.ReferenceName("refs/heads/" + input.Ref)
|
||||
r, err := CloneIfRequired(ctx, refName, input, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
func TestFindGitSlug(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var slugTests = []struct {
|
||||
slugTests := []struct {
|
||||
url string // input
|
||||
provider string // expected result
|
||||
slug string // expected result
|
||||
@@ -44,10 +44,7 @@ func TestFindGitSlug(t *testing.T) {
|
||||
}
|
||||
|
||||
func testDir(t *testing.T) string {
|
||||
basedir, err := os.MkdirTemp("", "act-test")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = os.RemoveAll(basedir) })
|
||||
return basedir
|
||||
return t.TempDir()
|
||||
}
|
||||
|
||||
func cleanGitHooks(dir string) error {
|
||||
@@ -163,8 +160,6 @@ func TestGitFindRef(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
name := name
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dir := filepath.Join(basedir, name)
|
||||
require.NoError(t, os.MkdirAll(dir, 0o755))
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
// NewContainerInput the input for the New function
|
||||
@@ -45,11 +46,11 @@ type FileEntry struct {
|
||||
|
||||
// Container for managing docker run containers
|
||||
type Container interface {
|
||||
Create(capAdd []string, capDrop []string) common.Executor
|
||||
Create(capAdd, capDrop []string) common.Executor
|
||||
ConnectToNetwork(name string) common.Executor
|
||||
Copy(destPath string, files ...*FileEntry) common.Executor
|
||||
CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error
|
||||
CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor
|
||||
CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor
|
||||
GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error)
|
||||
Pull(forcePull bool) common.Executor
|
||||
Start(attach bool) common.Executor
|
||||
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
func LoadDockerAuthConfig(ctx context.Context, image string) (registry.AuthConfig, error) {
|
||||
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
|
||||
// github.com/docker/docker/builder/dockerignore is deprecated
|
||||
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
|
||||
"github.com/moby/patternmatcher"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
// NewDockerBuildExecutor function to create a run executor for the container
|
||||
@@ -69,7 +68,8 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func createBuildContext(ctx context.Context, contextDir string, relDockerfile string) (io.ReadCloser, error) {
|
||||
|
||||
func createBuildContext(ctx context.Context, contextDir, relDockerfile string) (io.ReadCloser, error) {
|
||||
common.Logger(ctx).Debugf("Creating archive for build context dir '%s' with relative dockerfile '%s'", contextDir, relDockerfile)
|
||||
|
||||
// And canonicalize dockerfile name to a platform-independent one
|
||||
@@ -96,7 +96,7 @@ func createBuildContext(ctx context.Context, contextDir string, relDockerfile st
|
||||
// removed. The daemon will remove them for us, if needed, after it
|
||||
// parses the Dockerfile. Ignore errors here, as they will have been
|
||||
// caught by validateContextDirectory above.
|
||||
var includes = []string{"."}
|
||||
includes := []string{"."}
|
||||
keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes)
|
||||
keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes)
|
||||
if keepThem1 || keepThem2 {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// See DOCKER_LICENSE for the full license text.
|
||||
//
|
||||
|
||||
//nolint:unparam,errcheck,depguard,deadcode,unused
|
||||
//nolint:unparam,errcheck,depguard,unused // verbatim copy from docker/cli with minimal changes
|
||||
package container
|
||||
|
||||
import (
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -37,9 +38,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
|
||||
)
|
||||
var deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
|
||||
|
||||
// containerOptions is a data object with all the options for creating a container
|
||||
type containerOptions struct {
|
||||
@@ -322,7 +321,7 @@ type containerConfig struct {
|
||||
// a HostConfig and returns them with the specified command.
|
||||
// If the specified args are not valid, it will return an error.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) {
|
||||
var (
|
||||
attachStdin = copts.attach.Get("stdin")
|
||||
@@ -559,7 +558,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
return nil, errors.Errorf("--health-retries cannot be negative")
|
||||
}
|
||||
if copts.healthStartPeriod < 0 {
|
||||
return nil, fmt.Errorf("--health-start-period cannot be negative")
|
||||
return nil, errors.New("--health-start-period cannot be negative")
|
||||
}
|
||||
|
||||
healthConfig = &container.HealthConfig{
|
||||
@@ -722,7 +721,6 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
|
||||
)
|
||||
|
||||
for i, n := range copts.netMode.Value() {
|
||||
n := n
|
||||
if container.NetworkMode(n.Target).IsUserDefined() {
|
||||
hasUserDefined = true
|
||||
} else {
|
||||
@@ -837,7 +835,7 @@ func convertToStandardNotation(ports []string) ([]string, error) {
|
||||
for _, publish := range ports {
|
||||
if strings.Contains(publish, "=") {
|
||||
params := map[string]string{"protocol": "tcp"}
|
||||
for _, param := range strings.Split(publish, ",") {
|
||||
for param := range strings.SplitSeq(publish, ",") {
|
||||
opt := strings.Split(param, "=")
|
||||
if len(opt) < 2 {
|
||||
return optsList, errors.Errorf("invalid publish opts format (should be name=value but got '%s')", param)
|
||||
@@ -988,7 +986,7 @@ func validateDeviceCgroupRule(val string) (string, error) {
|
||||
// validDeviceMode checks if the mode for device is valid or not.
|
||||
// Valid mode is a composition of r (read), w (write), and m (mknod).
|
||||
func validDeviceMode(mode string) bool {
|
||||
var legalDeviceMode = map[rune]bool{
|
||||
legalDeviceMode := map[rune]bool{
|
||||
'r': true,
|
||||
'w': true,
|
||||
'm': true,
|
||||
@@ -1006,7 +1004,7 @@ func validDeviceMode(mode string) bool {
|
||||
}
|
||||
|
||||
// validateDevice validates a path for devices
|
||||
func validateDevice(val string, serverOS string) (string, error) {
|
||||
func validateDevice(val, serverOS string) (string, error) {
|
||||
switch serverOS {
|
||||
case "linux":
|
||||
return validateLinuxPath(val, validDeviceMode)
|
||||
@@ -1067,10 +1065,8 @@ func validateLinuxPath(val string, validator func(string) bool) (string, error)
|
||||
// validateAttach validates that the specified string is a valid attach option.
|
||||
func validateAttach(val string) (string, error) {
|
||||
s := strings.ToLower(val)
|
||||
for _, str := range []string{"stdin", "stdout", "stderr"} {
|
||||
if s == str {
|
||||
return s, nil
|
||||
}
|
||||
if slices.Contains([]string{"stdin", "stdout", "stderr"}, s) {
|
||||
return s, nil
|
||||
}
|
||||
return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// See DOCKER_LICENSE for the full license text.
|
||||
//
|
||||
|
||||
//nolint:unparam,whitespace,depguard,dupl,gocritic
|
||||
//nolint:unparam,depguard,gocritic // verbatim copy from docker/cli tests
|
||||
package container
|
||||
|
||||
import (
|
||||
@@ -135,7 +135,6 @@ func TestParseRunAttach(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.input, func(t *testing.T) {
|
||||
config, _ := mustParse(t, tc.input)
|
||||
assert.Equal(t, config.AttachStdin, tc.expected.AttachStdin)
|
||||
@@ -191,9 +190,8 @@ func TestParseRunWithInvalidArgs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func TestParseWithVolumes(t *testing.T) {
|
||||
|
||||
// A single volume
|
||||
arr, tryit := setupPlatformVolume([]string{`/tmp`}, []string{`c:\tmp`})
|
||||
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil {
|
||||
@@ -261,14 +259,13 @@ func TestParseWithVolumes(t *testing.T) {
|
||||
t.Fatalf("Error parsing %s. Should have a single bind mount and no volumes", arr[0])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// setupPlatformVolume takes two arrays of volume specs - a Unix style
|
||||
// spec and a Windows style spec. Depending on the platform being unit tested,
|
||||
// it returns one of them, along with a volume string that would be passed
|
||||
// on the docker CLI (e.g. -v /bar -v /foo).
|
||||
func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
||||
func setupPlatformVolume(u, w []string) ([]string, string) {
|
||||
var a []string
|
||||
if runtime.GOOS == "windows" {
|
||||
a = w
|
||||
@@ -340,7 +337,7 @@ func TestParseHostname(t *testing.T) {
|
||||
hostnameWithDomain := "--hostname=hostname.domainname"
|
||||
hostnameWithDomainTld := "--hostname=hostname.domainname.tld"
|
||||
for hostname, expectedHostname := range validHostnames {
|
||||
if config, _ := mustParse(t, fmt.Sprintf("--hostname=%s", hostname)); config.Hostname != expectedHostname {
|
||||
if config, _ := mustParse(t, "--hostname="+hostname); config.Hostname != expectedHostname {
|
||||
t.Fatalf("Expected the config to have 'hostname' as %q, got %q", expectedHostname, config.Hostname)
|
||||
}
|
||||
}
|
||||
@@ -462,7 +459,6 @@ func TestParseDevice(t *testing.T) {
|
||||
t.Fatalf("Expected %v, got %v", deviceMapping, hostconfig.Devices)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestParseNetworkConfig(t *testing.T) {
|
||||
@@ -634,7 +630,7 @@ func TestParseModes(t *testing.T) {
|
||||
}
|
||||
|
||||
// uts ko
|
||||
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled
|
||||
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled // ignoring multiple returns in test helpers
|
||||
assert.ErrorContains(t, err, "--uts: invalid UTS mode")
|
||||
|
||||
// uts ok
|
||||
@@ -678,7 +674,7 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for restart, expectedError := range invalids {
|
||||
if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||
if _, _, _, err := parseRun([]string{"--restart=" + restart, "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
||||
}
|
||||
}
|
||||
@@ -695,7 +691,7 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||
|
||||
func TestParseRestartPolicyAutoRemove(t *testing.T) {
|
||||
expected := "Conflicting options: --restart and --rm"
|
||||
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled
|
||||
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled // ignoring multiple returns in test helpers
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Fatalf("Expected error %v, but got none", expected)
|
||||
}
|
||||
@@ -967,7 +963,6 @@ func TestConvertToStandardNotation(t *testing.T) {
|
||||
|
||||
for key, ports := range valid {
|
||||
convertedPorts, err := convertToStandardNotation(ports)
|
||||
|
||||
if err != nil {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// ImageExistsLocally returns a boolean indicating if an image with the
|
||||
// requested name, tag and architecture exists in the local docker image store
|
||||
func ImageExistsLocally(ctx context.Context, imageName string, platform string) (bool, error) {
|
||||
func ImageExistsLocally(ctx context.Context, imageName, platform string) (bool, error) {
|
||||
cli, err := GetDockerClient(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -35,7 +35,7 @@ func ImageExistsLocally(ctx context.Context, imageName string, platform string)
|
||||
|
||||
// RemoveImage removes image from local store, the function is used to run different
|
||||
// container image architectures
|
||||
func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) {
|
||||
func RemoveImage(ctx context.Context, imageName string, force, pruneChildren bool) (bool, error) {
|
||||
cli, err := GetDockerClient(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -74,7 +74,7 @@ func logDockerResponse(logger logrus.FieldLogger, dockerResponse io.ReadCloser,
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeLog(logger logrus.FieldLogger, isError bool, format string, args ...interface{}) {
|
||||
func writeLog(logger logrus.FieldLogger, isError bool, format string, args ...any) {
|
||||
if isError {
|
||||
logger.Errorf(format, args...)
|
||||
} else {
|
||||
|
||||
@@ -5,9 +5,9 @@ package container
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
func NewDockerNetworkCreateExecutor(name string) common.Executor {
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
// NewDockerPullExecutor function to create a run executor for the container
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -16,6 +16,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/filecollector"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/docker/cli/cli/compose/loader"
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
@@ -23,7 +26,6 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/go-git/go-billy/v5/helper/polyfill"
|
||||
@@ -36,9 +38,6 @@ import (
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/filecollector"
|
||||
)
|
||||
|
||||
// NewContainer creates a reference to a container
|
||||
@@ -61,7 +60,7 @@ func (cr *containerReference) ConnectToNetwork(name string) common.Executor {
|
||||
|
||||
func (cr *containerReference) connectToNetwork(name string, aliases []string) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
return cr.cli.NetworkConnect(ctx, name, cr.input.Name, &networktypes.EndpointSettings{
|
||||
return cr.cli.NetworkConnect(ctx, name, cr.input.Name, &network.EndpointSettings{
|
||||
Aliases: aliases,
|
||||
})
|
||||
}
|
||||
@@ -85,7 +84,7 @@ func supportsContainerImagePlatform(ctx context.Context, cli client.APIClient) b
|
||||
return constraint.Check(sv)
|
||||
}
|
||||
|
||||
func (cr *containerReference) Create(capAdd []string, capDrop []string) common.Executor {
|
||||
func (cr *containerReference) Create(capAdd, capDrop []string) common.Executor {
|
||||
return common.
|
||||
NewInfoExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q network=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd, cr.input.NetworkMode).
|
||||
Then(
|
||||
@@ -142,7 +141,7 @@ func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.
|
||||
).IfNot(common.Dryrun)
|
||||
}
|
||||
|
||||
func (cr *containerReference) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
|
||||
func (cr *containerReference) CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor {
|
||||
return common.NewPipelineExecutor(
|
||||
common.NewInfoExecutor("%sdocker cp src=%s dst=%s", logPrefix, srcPath, destPath),
|
||||
cr.copyDir(destPath, srcPath, useGitIgnore),
|
||||
@@ -158,7 +157,7 @@ func (cr *containerReference) CopyDir(destPath string, srcPath string, useGitIgn
|
||||
|
||||
func (cr *containerReference) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) {
|
||||
if common.Dryrun(ctx) {
|
||||
return nil, fmt.Errorf("DRYRUN is not supported in GetContainerArchive")
|
||||
return nil, errors.New("DRYRUN is not supported in GetContainerArchive")
|
||||
}
|
||||
a, _, err := cr.cli.CopyFromContainer(ctx, cr.id, srcPath)
|
||||
return a, err
|
||||
@@ -190,7 +189,7 @@ func (cr *containerReference) Remove() common.Executor {
|
||||
).IfNot(common.Dryrun)
|
||||
}
|
||||
|
||||
func (cr *containerReference) ReplaceLogWriter(stdout io.Writer, stderr io.Writer) (io.Writer, io.Writer) {
|
||||
func (cr *containerReference) ReplaceLogWriter(stdout, stderr io.Writer) (io.Writer, io.Writer) {
|
||||
out := cr.input.Stdout
|
||||
err := cr.input.Stderr
|
||||
|
||||
@@ -429,7 +428,7 @@ func (cr *containerReference) mergeContainerConfigs(ctx context.Context, config
|
||||
return config, hostConfig, nil
|
||||
}
|
||||
|
||||
func (cr *containerReference) create(capAdd []string, capDrop []string) common.Executor {
|
||||
func (cr *containerReference) create(capAdd, capDrop []string) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
if cr.id != "" {
|
||||
return nil
|
||||
@@ -682,7 +681,7 @@ func (cr *containerReference) tryReadGID() common.Executor {
|
||||
return cr.tryReadID("-g", func(id int) { cr.GID = id })
|
||||
}
|
||||
|
||||
func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal bool, resp types.HijackedResponse, _ types.IDResponse, _ string, _ string) error {
|
||||
func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal bool, resp types.HijackedResponse, _ types.IDResponse, _, _ string) error {
|
||||
logger := common.Logger(ctx)
|
||||
|
||||
cmdResponse := make(chan error)
|
||||
@@ -753,7 +752,7 @@ func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgnore bool) common.Executor {
|
||||
func (cr *containerReference) copyDir(dstPath, srcPath string, useGitIgnore bool) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := common.Logger(ctx)
|
||||
tarFile, err := os.CreateTemp("", "act")
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -81,7 +81,7 @@ func (m *mockDockerClient) ContainerExecInspect(ctx context.Context, execID stri
|
||||
return args.Get(0).(types.ContainerExecInspect), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockDockerClient) CopyToContainer(ctx context.Context, id string, path string, content io.Reader, options types.CopyToContainerOptions) error {
|
||||
func (m *mockDockerClient) CopyToContainer(ctx context.Context, id, path string, content io.Reader, options types.CopyToContainerOptions) error {
|
||||
args := m.Called(ctx, id, path, content, options)
|
||||
return args.Error(0)
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func TestDockerCopyTarStreamErrorInCopyFiles(t *testing.T) {
|
||||
|
||||
conn := &mockConn{}
|
||||
|
||||
merr := fmt.Errorf("Failure")
|
||||
merr := errors.New("Failure")
|
||||
|
||||
client := &mockDockerClient{}
|
||||
client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr)
|
||||
@@ -228,7 +228,7 @@ func TestDockerCopyTarStreamErrorInMkdir(t *testing.T) {
|
||||
|
||||
conn := &mockConn{}
|
||||
|
||||
merr := fmt.Errorf("Failure")
|
||||
merr := errors.New("Failure")
|
||||
|
||||
client := &mockDockerClient{}
|
||||
client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil)
|
||||
|
||||
@@ -43,8 +43,8 @@ func socketLocation() (string, bool) {
|
||||
// indicating that the `daemonPath` is a Docker host URI. If it doesn't, or if the "://" delimiter
|
||||
// is not found in the `daemonPath`, the function returns false.
|
||||
func isDockerHostURI(daemonPath string) bool {
|
||||
if protoIndex := strings.Index(daemonPath, "://"); protoIndex != -1 {
|
||||
scheme := daemonPath[:protoIndex]
|
||||
if before, _, ok := strings.Cut(daemonPath, "://"); ok {
|
||||
scheme := before
|
||||
if strings.IndexFunc(scheme, func(r rune) bool {
|
||||
return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z')
|
||||
}) == -1 {
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestGetSocketAndHostWithSocket(t *testing.T) {
|
||||
CommonSocketLocations = originalCommonSocketLocations
|
||||
dockerHost := "unix:///my/docker/host.sock"
|
||||
socketURI := "/path/to/my.socket"
|
||||
os.Setenv("DOCKER_HOST", dockerHost)
|
||||
t.Setenv("DOCKER_HOST", dockerHost)
|
||||
|
||||
// Act
|
||||
ret, err := GetSocketAndHost(socketURI)
|
||||
@@ -32,7 +32,7 @@ func TestGetSocketAndHostWithSocket(t *testing.T) {
|
||||
func TestGetSocketAndHostNoSocket(t *testing.T) {
|
||||
// Arrange
|
||||
dockerHost := "unix:///my/docker/host.sock"
|
||||
os.Setenv("DOCKER_HOST", dockerHost)
|
||||
t.Setenv("DOCKER_HOST", dockerHost)
|
||||
|
||||
// Act
|
||||
ret, err := GetSocketAndHost("")
|
||||
@@ -63,7 +63,7 @@ func TestGetSocketAndHostDontMount(t *testing.T) {
|
||||
// Arrange
|
||||
CommonSocketLocations = originalCommonSocketLocations
|
||||
dockerHost := "unix:///my/docker/host.sock"
|
||||
os.Setenv("DOCKER_HOST", dockerHost)
|
||||
t.Setenv("DOCKER_HOST", dockerHost)
|
||||
|
||||
// Act
|
||||
ret, err := GetSocketAndHost("-")
|
||||
@@ -93,7 +93,7 @@ func TestGetSocketAndHostNoHostNoSocket(t *testing.T) {
|
||||
// > This happens if neither DOCKER_HOST nor --container-daemon-socket has a value, but socketLocation() returns a URI
|
||||
func TestGetSocketAndHostNoHostNoSocketDefaultLocation(t *testing.T) {
|
||||
// Arrange
|
||||
mySocketFile, tmpErr := os.CreateTemp("", "act-*.sock")
|
||||
mySocketFile, tmpErr := os.CreateTemp(t.TempDir(), "act-*.sock")
|
||||
mySocket := mySocketFile.Name()
|
||||
unixSocket := "unix://" + mySocket
|
||||
defer os.RemoveAll(mySocket)
|
||||
|
||||
@@ -6,20 +6,21 @@ import (
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ImageExistsLocally returns a boolean indicating if an image with the
|
||||
// requested name, tag and architecture exists in the local docker image store
|
||||
func ImageExistsLocally(ctx context.Context, imageName string, platform string) (bool, error) {
|
||||
func ImageExistsLocally(ctx context.Context, imageName, platform string) (bool, error) {
|
||||
return false, errors.New("Unsupported Operation")
|
||||
}
|
||||
|
||||
// RemoveImage removes image from local store, the function is used to run different
|
||||
// container image architectures
|
||||
func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) {
|
||||
func RemoveImage(ctx context.Context, imageName string, force, pruneChildren bool) (bool, error) {
|
||||
return false, errors.New("Unsupported Operation")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ package container
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executor {
|
||||
|
||||
@@ -9,7 +9,7 @@ type ExecutionsEnvironment interface {
|
||||
GetPathVariableName() string
|
||||
DefaultPathVariable() string
|
||||
JoinPathVariable(...string) string
|
||||
GetRunnerContext(ctx context.Context) map[string]interface{}
|
||||
GetRunnerContext(ctx context.Context) map[string]any
|
||||
// On windows PATH and Path are the same key
|
||||
IsEnvironmentCaseInsensitive() bool
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/filecollector"
|
||||
"github.com/nektos/act/pkg/lookpath"
|
||||
|
||||
"github.com/go-git/go-billy/v5/helper/polyfill"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/filecollector"
|
||||
"github.com/nektos/act/pkg/lookpath"
|
||||
)
|
||||
|
||||
type HostEnvironment struct {
|
||||
@@ -35,7 +35,7 @@ type HostEnvironment struct {
|
||||
StdOut io.Writer
|
||||
}
|
||||
|
||||
func (e *HostEnvironment) Create(_ []string, _ []string) common.Executor {
|
||||
func (e *HostEnvironment) Create(_, _ []string) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
|
||||
continue
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
return fmt.Errorf("CopyTarStream has been cancelled")
|
||||
return errors.New("CopyTarStream has been cancelled")
|
||||
}
|
||||
if err := cp.WriteFile(ti.Name, ti.FileInfo(), ti.Linkname, tr); err != nil {
|
||||
return err
|
||||
@@ -94,7 +94,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
|
||||
}
|
||||
}
|
||||
|
||||
func (e *HostEnvironment) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
|
||||
func (e *HostEnvironment) CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := common.Logger(ctx)
|
||||
srcPrefix := filepath.Dir(srcPath)
|
||||
@@ -227,7 +227,7 @@ func (l *localEnv) Getenv(name string) string {
|
||||
func lookupPathHost(cmd string, env map[string]string, writer io.Writer) (string, error) {
|
||||
f, err := lookpath.LookPath2(cmd, &localEnv{env: env})
|
||||
if err != nil {
|
||||
err := "Cannot find: " + fmt.Sprint(cmd) + " in PATH"
|
||||
err := "Cannot find: " + cmd + " in PATH"
|
||||
if _, _err := writer.Write([]byte(err + "\n")); _err != nil {
|
||||
return "", fmt.Errorf("%v: %w", err, _err)
|
||||
}
|
||||
@@ -346,7 +346,7 @@ func (e *HostEnvironment) exec(ctx context.Context, command []string, cmdline st
|
||||
}
|
||||
if tty != nil {
|
||||
writer.AutoStop = true
|
||||
if _, err := tty.Write([]byte("\x04")); err != nil {
|
||||
if _, err := tty.WriteString("\x04"); err != nil {
|
||||
common.Logger(ctx).Debug("Failed to write EOT")
|
||||
}
|
||||
}
|
||||
@@ -408,9 +408,10 @@ func (e *HostEnvironment) GetActPath() string {
|
||||
}
|
||||
|
||||
func (*HostEnvironment) GetPathVariableName() string {
|
||||
if runtime.GOOS == "plan9" {
|
||||
switch runtime.GOOS {
|
||||
case "plan9":
|
||||
return "path"
|
||||
} else if runtime.GOOS == "windows" {
|
||||
case "windows":
|
||||
return "Path" // Actually we need a case insensitive map
|
||||
}
|
||||
return "PATH"
|
||||
@@ -449,8 +450,8 @@ func goOsToActionOs(os string) string {
|
||||
return os
|
||||
}
|
||||
|
||||
func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]any {
|
||||
return map[string]any{
|
||||
"os": goOsToActionOs(runtime.GOOS),
|
||||
"arch": goArchToActionArch(runtime.GOARCH),
|
||||
"temp": e.TmpDir,
|
||||
@@ -458,7 +459,7 @@ func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]interfa
|
||||
}
|
||||
}
|
||||
|
||||
func (e *HostEnvironment) ReplaceLogWriter(stdout io.Writer, _ io.Writer) (io.Writer, io.Writer) {
|
||||
func (e *HostEnvironment) ReplaceLogWriter(stdout, _ io.Writer) (io.Writer, io.Writer) {
|
||||
org := e.StdOut
|
||||
e.StdOut = stdout
|
||||
return org, org
|
||||
|
||||
@@ -16,9 +16,7 @@ import (
|
||||
var _ ExecutionsEnvironment = &HostEnvironment{}
|
||||
|
||||
func TestCopyDir(t *testing.T) {
|
||||
dir, err := os.MkdirTemp("", "test-host-env-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
dir := t.TempDir()
|
||||
ctx := context.Background()
|
||||
e := &HostEnvironment{
|
||||
Path: filepath.Join(dir, "path"),
|
||||
@@ -28,18 +26,16 @@ func TestCopyDir(t *testing.T) {
|
||||
StdOut: os.Stdout,
|
||||
Workdir: path.Join("testdata", "scratch"),
|
||||
}
|
||||
_ = os.MkdirAll(e.Path, 0700)
|
||||
_ = os.MkdirAll(e.TmpDir, 0700)
|
||||
_ = os.MkdirAll(e.ToolCache, 0700)
|
||||
_ = os.MkdirAll(e.ActPath, 0700)
|
||||
err = e.CopyDir(e.Workdir, e.Path, true)(ctx)
|
||||
_ = os.MkdirAll(e.Path, 0o700)
|
||||
_ = os.MkdirAll(e.TmpDir, 0o700)
|
||||
_ = os.MkdirAll(e.ToolCache, 0o700)
|
||||
_ = os.MkdirAll(e.ActPath, 0o700)
|
||||
err := e.CopyDir(e.Workdir, e.Path, true)(ctx)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetContainerArchive(t *testing.T) {
|
||||
dir, err := os.MkdirTemp("", "test-host-env-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
dir := t.TempDir()
|
||||
ctx := context.Background()
|
||||
e := &HostEnvironment{
|
||||
Path: filepath.Join(dir, "path"),
|
||||
@@ -49,12 +45,12 @@ func TestGetContainerArchive(t *testing.T) {
|
||||
StdOut: os.Stdout,
|
||||
Workdir: path.Join("testdata", "scratch"),
|
||||
}
|
||||
_ = os.MkdirAll(e.Path, 0700)
|
||||
_ = os.MkdirAll(e.TmpDir, 0700)
|
||||
_ = os.MkdirAll(e.ToolCache, 0700)
|
||||
_ = os.MkdirAll(e.ActPath, 0700)
|
||||
_ = os.MkdirAll(e.Path, 0o700)
|
||||
_ = os.MkdirAll(e.TmpDir, 0o700)
|
||||
_ = os.MkdirAll(e.ToolCache, 0o700)
|
||||
_ = os.MkdirAll(e.ActPath, 0o700)
|
||||
expectedContent := []byte("sdde/7sh")
|
||||
err = os.WriteFile(filepath.Join(e.Path, "action.yml"), expectedContent, 0600)
|
||||
err := os.WriteFile(filepath.Join(e.Path, "action.yml"), expectedContent, 0o600)
|
||||
assert.NoError(t, err)
|
||||
archive, err := e.GetContainerArchive(ctx, e.Path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -10,8 +10,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LinuxContainerEnvironmentExtensions struct {
|
||||
}
|
||||
type LinuxContainerEnvironmentExtensions struct{}
|
||||
|
||||
// Resolves the equivalent host path inside the container
|
||||
// This is required for windows and WSL 2 to translate things like C:\Users\Myproject to /mnt/users/Myproject
|
||||
@@ -63,8 +62,8 @@ func (*LinuxContainerEnvironmentExtensions) JoinPathVariable(paths ...string) st
|
||||
return strings.Join(paths, ":")
|
||||
}
|
||||
|
||||
func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]any {
|
||||
return map[string]any{
|
||||
"os": "Linux",
|
||||
"arch": RunnerArch(ctx),
|
||||
"temp": "/tmp",
|
||||
|
||||
@@ -31,22 +31,17 @@ func TestContainerPath(t *testing.T) {
|
||||
for _, v := range []containerPathJob{
|
||||
{"/mnt/c/Users/act/go/src/github.com/nektos/act", "C:\\Users\\act\\go\\src\\github.com\\nektos\\act\\", ""},
|
||||
{"/mnt/f/work/dir", `F:\work\dir`, ""},
|
||||
{"/mnt/c/windows/to/unix", "windows\\to\\unix", fmt.Sprintf("%s\\", rootDrive)},
|
||||
{fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", fmt.Sprintf("%s\\", rootDrive)},
|
||||
{"/mnt/c/windows/to/unix", "windows\\to\\unix", rootDrive + "\\"},
|
||||
{fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", rootDrive + "\\"},
|
||||
} {
|
||||
if v.workDir != "" {
|
||||
if err := os.Chdir(v.workDir); err != nil {
|
||||
log.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
t.Chdir(v.workDir)
|
||||
}
|
||||
|
||||
assert.Equal(t, v.destinationPath, linuxcontainerext.ToContainerPath(v.sourcePath))
|
||||
}
|
||||
|
||||
if err := os.Chdir(cwd); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
t.Chdir(cwd)
|
||||
} else {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -13,9 +14,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
|
||||
"github.com/rhysd/actionlint"
|
||||
)
|
||||
|
||||
@@ -66,7 +67,7 @@ const (
|
||||
|
||||
func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.Value) (string, error) {
|
||||
input := impl.coerceToString(str).String()
|
||||
output := ""
|
||||
var output strings.Builder
|
||||
replacementIndex := ""
|
||||
|
||||
state := passThrough
|
||||
@@ -81,13 +82,13 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
|
||||
state = bracketClose
|
||||
|
||||
default:
|
||||
output += string(character)
|
||||
output.WriteRune(character)
|
||||
}
|
||||
|
||||
case bracketOpen: // found {
|
||||
switch character {
|
||||
case '{':
|
||||
output += "{"
|
||||
output.WriteString("{")
|
||||
replacementIndex = ""
|
||||
state = passThrough
|
||||
|
||||
@@ -103,7 +104,7 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
|
||||
return "", fmt.Errorf("The following format string references more arguments than were supplied: '%s'", input)
|
||||
}
|
||||
|
||||
output += impl.coerceToString(replaceValue[index]).String()
|
||||
output.WriteString(impl.coerceToString(replaceValue[index]).String())
|
||||
|
||||
state = passThrough
|
||||
|
||||
@@ -114,7 +115,7 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
|
||||
case bracketClose: // found }
|
||||
switch character {
|
||||
case '}':
|
||||
output += "}"
|
||||
output.WriteString("}")
|
||||
replacementIndex = ""
|
||||
state = passThrough
|
||||
|
||||
@@ -134,10 +135,10 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
return output.String(), nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) join(array reflect.Value, sep reflect.Value) (string, error) {
|
||||
func (impl *interperterImpl) join(array, sep reflect.Value) (string, error) {
|
||||
separator := impl.coerceToString(sep).String()
|
||||
switch array.Kind() {
|
||||
case reflect.Slice:
|
||||
@@ -165,12 +166,12 @@ func (impl *interperterImpl) toJSON(value reflect.Value) (string, error) {
|
||||
return string(json), nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) fromJSON(value reflect.Value) (interface{}, error) {
|
||||
func (impl *interperterImpl) fromJSON(value reflect.Value) (any, error) {
|
||||
if value.Kind() != reflect.String {
|
||||
return nil, fmt.Errorf("Cannot parse non-string type %v as JSON", value.Kind())
|
||||
}
|
||||
|
||||
var data interface{}
|
||||
var data any
|
||||
|
||||
err := json.Unmarshal([]byte(value.String()), &data)
|
||||
if err != nil {
|
||||
@@ -195,7 +196,7 @@ func (impl *interperterImpl) hashFiles(paths ...reflect.Value) (string, error) {
|
||||
}
|
||||
ps = append(ps, gitignore.ParsePattern(cleanPath, nil))
|
||||
} else {
|
||||
return "", fmt.Errorf("Non-string path passed to hashFiles")
|
||||
return "", errors.New("Non-string path passed to hashFiles")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFunctionContains(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"contains('search', 'item') }}", false, "contains-str-str"},
|
||||
@@ -48,7 +49,7 @@ func TestFunctionContains(t *testing.T) {
|
||||
func TestFunctionStartsWith(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"startsWith('search', 'se') }}", true, "startswith-string"},
|
||||
@@ -77,7 +78,7 @@ func TestFunctionStartsWith(t *testing.T) {
|
||||
func TestFunctionEndsWith(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"endsWith('search', 'ch') }}", true, "endsWith-string"},
|
||||
@@ -106,7 +107,7 @@ func TestFunctionEndsWith(t *testing.T) {
|
||||
func TestFunctionJoin(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"join(fromJSON('[\"a\", \"b\"]'), ',')", "a,b", "join-arr"},
|
||||
@@ -133,7 +134,7 @@ func TestFunctionJoin(t *testing.T) {
|
||||
func TestFunctionToJSON(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"toJSON(env) }}", "{\n \"key\": \"value\"\n}", "toJSON"},
|
||||
@@ -159,10 +160,10 @@ func TestFunctionToJSON(t *testing.T) {
|
||||
func TestFunctionFromJSON(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"fromJSON('{\"foo\":\"bar\"}') }}", map[string]interface{}{
|
||||
{"fromJSON('{\"foo\":\"bar\"}') }}", map[string]any{
|
||||
"foo": "bar",
|
||||
}, "fromJSON"},
|
||||
}
|
||||
@@ -182,7 +183,7 @@ func TestFunctionFromJSON(t *testing.T) {
|
||||
func TestFunctionHashFiles(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"hashFiles('**/non-extant-files') }}", "", "hash-non-existing-file"},
|
||||
@@ -212,8 +213,8 @@ func TestFunctionHashFiles(t *testing.T) {
|
||||
func TestFunctionFormat(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
error interface{}
|
||||
expected any
|
||||
error any
|
||||
name string
|
||||
}{
|
||||
{"format('text')", "text", nil, "format-plain-string"},
|
||||
|
||||
@@ -2,12 +2,14 @@ package exprparser
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/rhysd/actionlint"
|
||||
)
|
||||
|
||||
@@ -17,14 +19,14 @@ type EvaluationEnvironment struct {
|
||||
Job *model.JobContext
|
||||
Jobs *map[string]*model.WorkflowCallResult
|
||||
Steps map[string]*model.StepResult
|
||||
Runner map[string]interface{}
|
||||
Runner map[string]any
|
||||
Secrets map[string]string
|
||||
Vars map[string]string
|
||||
Strategy map[string]interface{}
|
||||
Matrix map[string]interface{}
|
||||
Strategy map[string]any
|
||||
Matrix map[string]any
|
||||
Needs map[string]Needs
|
||||
Inputs map[string]interface{}
|
||||
HashFiles func([]reflect.Value) (interface{}, error)
|
||||
Inputs map[string]any
|
||||
HashFiles func([]reflect.Value) (any, error)
|
||||
}
|
||||
|
||||
type Needs struct {
|
||||
@@ -63,7 +65,7 @@ func (dsc DefaultStatusCheck) String() string {
|
||||
}
|
||||
|
||||
type Interpreter interface {
|
||||
Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (interface{}, error)
|
||||
Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (any, error)
|
||||
}
|
||||
|
||||
type interperterImpl struct {
|
||||
@@ -78,7 +80,7 @@ func NewInterpeter(env *EvaluationEnvironment, config Config) Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (interface{}, error) {
|
||||
func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (any, error) {
|
||||
input = strings.TrimPrefix(input, "${{")
|
||||
if defaultStatusCheck != DefaultStatusCheckNone && input == "" {
|
||||
input = "success()"
|
||||
@@ -117,7 +119,7 @@ func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultSt
|
||||
return result, err2
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateNode(exprNode actionlint.ExprNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateNode(exprNode actionlint.ExprNode) (any, error) {
|
||||
switch node := exprNode.(type) {
|
||||
case *actionlint.VariableNode:
|
||||
return impl.evaluateVariable(node)
|
||||
@@ -150,8 +152,8 @@ func (impl *interperterImpl) evaluateNode(exprNode actionlint.ExprNode) (interfa
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableNode) (interface{}, error) {
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableNode) (any, error) {
|
||||
switch strings.ToLower(variableNode.Name) {
|
||||
case "github":
|
||||
return impl.env.Github, nil
|
||||
@@ -163,7 +165,7 @@ func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableN
|
||||
return impl.env.Job, nil
|
||||
case "jobs":
|
||||
if impl.env.Jobs == nil {
|
||||
return nil, fmt.Errorf("Unavailable context: jobs")
|
||||
return nil, errors.New("Unavailable context: jobs")
|
||||
}
|
||||
return impl.env.Jobs, nil
|
||||
case "steps":
|
||||
@@ -191,7 +193,7 @@ func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableN
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateIndexAccess(indexAccessNode *actionlint.IndexAccessNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateIndexAccess(indexAccessNode *actionlint.IndexAccessNode) (any, error) {
|
||||
left, err := impl.evaluateNode(indexAccessNode.Operand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -226,7 +228,7 @@ func (impl *interperterImpl) evaluateIndexAccess(indexAccessNode *actionlint.Ind
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.ObjectDerefNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.ObjectDerefNode) (any, error) {
|
||||
left, err := impl.evaluateNode(objectDerefNode.Receiver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -235,7 +237,7 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj
|
||||
return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property)
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.ArrayDerefNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.ArrayDerefNode) (any, error) {
|
||||
left, err := impl.evaluateNode(arrayDerefNode.Receiver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -244,17 +246,17 @@ func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.Array
|
||||
return impl.getSafeValue(reflect.ValueOf(left)), nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) getPropertyValue(left reflect.Value, property string) (value interface{}, err error) {
|
||||
func (impl *interperterImpl) getPropertyValue(left reflect.Value, property string) (value any, err error) {
|
||||
switch left.Kind() {
|
||||
case reflect.Ptr:
|
||||
return impl.getPropertyValue(left.Elem(), property)
|
||||
|
||||
case reflect.Struct:
|
||||
leftType := left.Type()
|
||||
for i := 0; i < leftType.NumField(); i++ {
|
||||
jsonName := leftType.Field(i).Tag.Get("json")
|
||||
for field := range leftType.Fields() {
|
||||
jsonName := field.Tag.Get("json")
|
||||
if jsonName == property {
|
||||
property = leftType.Field(i).Name
|
||||
property = field.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -298,7 +300,7 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin
|
||||
return nil, nil
|
||||
|
||||
case reflect.Slice:
|
||||
var values []interface{}
|
||||
var values []any
|
||||
|
||||
for i := 0; i < left.Len(); i++ {
|
||||
value, err := impl.getPropertyValue(left.Index(i).Elem(), property)
|
||||
@@ -315,7 +317,7 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, error) {
|
||||
func (impl *interperterImpl) getMapValue(value reflect.Value) (any, error) {
|
||||
if value.Kind() == reflect.Ptr {
|
||||
return impl.getMapValue(value.Elem())
|
||||
}
|
||||
@@ -323,7 +325,7 @@ func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, erro
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateNot(notNode *actionlint.NotOpNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateNot(notNode *actionlint.NotOpNode) (any, error) {
|
||||
operand, err := impl.evaluateNode(notNode.Operand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -332,7 +334,7 @@ func (impl *interperterImpl) evaluateNot(notNode *actionlint.NotOpNode) (interfa
|
||||
return !IsTruthy(operand), nil
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateCompare(compareNode *actionlint.CompareOpNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateCompare(compareNode *actionlint.CompareOpNode) (any, error) {
|
||||
left, err := impl.evaluateNode(compareNode.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -349,7 +351,7 @@ func (impl *interperterImpl) evaluateCompare(compareNode *actionlint.CompareOpNo
|
||||
return impl.compareValues(leftValue, rightValue, compareNode.Kind)
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) compareValues(leftValue reflect.Value, rightValue reflect.Value, kind actionlint.CompareOpNodeKind) (interface{}, error) {
|
||||
func (impl *interperterImpl) compareValues(leftValue, rightValue reflect.Value, kind actionlint.CompareOpNodeKind) (any, error) {
|
||||
if leftValue.Kind() != rightValue.Kind() {
|
||||
if !impl.isNumber(leftValue) {
|
||||
leftValue = impl.coerceToNumber(leftValue)
|
||||
@@ -461,7 +463,7 @@ func (impl *interperterImpl) coerceToString(value reflect.Value) reflect.Value {
|
||||
return value
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) compareString(left string, right string, kind actionlint.CompareOpNodeKind) (bool, error) {
|
||||
func (impl *interperterImpl) compareString(left, right string, kind actionlint.CompareOpNodeKind) (bool, error) {
|
||||
switch kind {
|
||||
case actionlint.CompareOpNodeKindLess:
|
||||
return left < right, nil
|
||||
@@ -480,7 +482,7 @@ func (impl *interperterImpl) compareString(left string, right string, kind actio
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) compareNumber(left float64, right float64, kind actionlint.CompareOpNodeKind) (bool, error) {
|
||||
func (impl *interperterImpl) compareNumber(left, right float64, kind actionlint.CompareOpNodeKind) (bool, error) {
|
||||
switch kind {
|
||||
case actionlint.CompareOpNodeKindLess:
|
||||
return left < right, nil
|
||||
@@ -499,7 +501,7 @@ func (impl *interperterImpl) compareNumber(left float64, right float64, kind act
|
||||
}
|
||||
}
|
||||
|
||||
func IsTruthy(input interface{}) bool {
|
||||
func IsTruthy(input any) bool {
|
||||
value := reflect.ValueOf(input)
|
||||
switch value.Kind() {
|
||||
case reflect.Bool:
|
||||
@@ -535,7 +537,7 @@ func (impl *interperterImpl) isNumber(value reflect.Value) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) getSafeValue(value reflect.Value) interface{} {
|
||||
func (impl *interperterImpl) getSafeValue(value reflect.Value) any {
|
||||
switch value.Kind() {
|
||||
case reflect.Invalid:
|
||||
return nil
|
||||
@@ -549,7 +551,7 @@ func (impl *interperterImpl) getSafeValue(value reflect.Value) interface{} {
|
||||
return value.Interface()
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateLogicalCompare(compareNode *actionlint.LogicalOpNode) (interface{}, error) {
|
||||
func (impl *interperterImpl) evaluateLogicalCompare(compareNode *actionlint.LogicalOpNode) (any, error) {
|
||||
left, err := impl.evaluateNode(compareNode.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -578,8 +580,8 @@ func (impl *interperterImpl) evaluateLogicalCompare(compareNode *actionlint.Logi
|
||||
return nil, fmt.Errorf("Unable to compare incompatibles types '%s' and '%s'", leftValue.Kind(), rightValue.Kind())
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (impl *interperterImpl) evaluateFuncCall(funcCallNode *actionlint.FuncCallNode) (interface{}, error) {
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (impl *interperterImpl) evaluateFuncCall(funcCallNode *actionlint.FuncCallNode) (any, error) {
|
||||
args := make([]reflect.Value, 0)
|
||||
|
||||
for _, arg := range funcCallNode.Args {
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLiterals(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"true", true, "true"},
|
||||
@@ -40,7 +41,7 @@ func TestLiterals(t *testing.T) {
|
||||
func TestOperators(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
error string
|
||||
}{
|
||||
@@ -68,7 +69,7 @@ func TestOperators(t *testing.T) {
|
||||
{`true && false`, false, "and", ""},
|
||||
{`true || false`, true, "or", ""},
|
||||
{`fromJSON('{}') && true`, true, "and-boolean-object", ""},
|
||||
{`fromJSON('{}') || false`, make(map[string]interface{}), "or-boolean-object", ""},
|
||||
{`fromJSON('{}') || false`, make(map[string]any), "or-boolean-object", ""},
|
||||
{"github.event.commits[0].author.username != github.event.commits[1].author.username", true, "property-comparison1", ""},
|
||||
{"github.event.commits[0].author.username1 != github.event.commits[1].author.username", true, "property-comparison2", ""},
|
||||
{"github.event.commits[0].author.username != github.event.commits[1].author.username1", true, "property-comparison3", ""},
|
||||
@@ -79,15 +80,15 @@ func TestOperators(t *testing.T) {
|
||||
env := &EvaluationEnvironment{
|
||||
Github: &model.GithubContext{
|
||||
Action: "push",
|
||||
Event: map[string]interface{}{
|
||||
"commits": []interface{}{
|
||||
map[string]interface{}{
|
||||
"author": map[string]interface{}{
|
||||
Event: map[string]any{
|
||||
"commits": []any{
|
||||
map[string]any{
|
||||
"author": map[string]any{
|
||||
"username": "someone",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"author": map[string]interface{}{
|
||||
map[string]any{
|
||||
"author": map[string]any{
|
||||
"username": "someone-else",
|
||||
},
|
||||
},
|
||||
@@ -114,7 +115,7 @@ func TestOperators(t *testing.T) {
|
||||
func TestOperatorsCompare(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"!null", true, "not-null"},
|
||||
@@ -162,7 +163,7 @@ func TestOperatorsCompare(t *testing.T) {
|
||||
func TestOperatorsBooleanEvaluation(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
// true &&
|
||||
@@ -529,7 +530,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
|
||||
func TestContexts(t *testing.T) {
|
||||
table := []struct {
|
||||
input string
|
||||
expected interface{}
|
||||
expected any
|
||||
name string
|
||||
}{
|
||||
{"github.action", "push", "github-context"},
|
||||
@@ -586,7 +587,7 @@ func TestContexts(t *testing.T) {
|
||||
Conclusion: model.StepStatusSkipped,
|
||||
},
|
||||
},
|
||||
Runner: map[string]interface{}{
|
||||
Runner: map[string]any{
|
||||
"os": "Linux",
|
||||
"temp": "/tmp",
|
||||
"tool_cache": "/opt/hostedtoolcache",
|
||||
@@ -597,10 +598,10 @@ func TestContexts(t *testing.T) {
|
||||
Vars: map[string]string{
|
||||
"name": "value",
|
||||
},
|
||||
Strategy: map[string]interface{}{
|
||||
Strategy: map[string]any{
|
||||
"fail-fast": true,
|
||||
},
|
||||
Matrix: map[string]interface{}{
|
||||
Matrix: map[string]any{
|
||||
"os": "Linux",
|
||||
},
|
||||
Needs: map[string]Needs{
|
||||
@@ -611,7 +612,7 @@ func TestContexts(t *testing.T) {
|
||||
Result: "success",
|
||||
},
|
||||
},
|
||||
Inputs: map[string]interface{}{
|
||||
Inputs: map[string]any{
|
||||
"name": "value",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package filecollector
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -97,8 +98,7 @@ type Fs interface {
|
||||
Readlink(path string) (string, error)
|
||||
}
|
||||
|
||||
type DefaultFs struct {
|
||||
}
|
||||
type DefaultFs struct{}
|
||||
|
||||
func (*DefaultFs) Walk(root string, fn filepath.WalkFunc) error {
|
||||
return filepath.Walk(root, fn)
|
||||
@@ -124,7 +124,7 @@ func (*DefaultFs) Readlink(path string) (string, error) {
|
||||
return os.Readlink(path)
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc {
|
||||
i, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...)))
|
||||
return func(file string, fi os.FileInfo, err error) error {
|
||||
@@ -134,7 +134,7 @@ func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []strin
|
||||
if ctx != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("copy cancelled")
|
||||
return errors.New("copy cancelled")
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func (mfs *memoryFs) walk(root string, fn filepath.WalkFunc) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < len(dir); i++ {
|
||||
for i := range dir {
|
||||
filename := filepath.Join(root, dir[i].Name())
|
||||
err = fn(filename, dir[i], nil)
|
||||
if dir[i].IsDir() {
|
||||
|
||||
@@ -6,8 +6,7 @@ type Env interface {
|
||||
Getenv(name string) string
|
||||
}
|
||||
|
||||
type defaultEnv struct {
|
||||
}
|
||||
type defaultEnv struct{}
|
||||
|
||||
func (*defaultEnv) Getenv(name string) string {
|
||||
return os.Getenv(name)
|
||||
|
||||
@@ -20,7 +20,7 @@ func findExecutable(file string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
|
||||
if m := d.Mode(); !m.IsDir() && m&0o111 != 0 {
|
||||
return nil
|
||||
}
|
||||
return fs.ErrPermission
|
||||
|
||||
@@ -22,7 +22,7 @@ func findExecutable(file string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
|
||||
if m := d.Mode(); !m.IsDir() && m&0o111 != 0 {
|
||||
return nil
|
||||
}
|
||||
return fs.ErrPermission
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// ActionRunsUsing is the type of runner for the action
|
||||
type ActionRunsUsing string
|
||||
|
||||
func (a *ActionRunsUsing) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
func (a *ActionRunsUsing) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var using string
|
||||
if err := unmarshal(&using); err != nil {
|
||||
return err
|
||||
|
||||
@@ -10,41 +10,41 @@ import (
|
||||
)
|
||||
|
||||
type GithubContext struct {
|
||||
Event map[string]interface{} `json:"event"`
|
||||
EventPath string `json:"event_path"`
|
||||
Workflow string `json:"workflow"`
|
||||
RunID string `json:"run_id"`
|
||||
RunNumber string `json:"run_number"`
|
||||
Actor string `json:"actor"`
|
||||
Repository string `json:"repository"`
|
||||
EventName string `json:"event_name"`
|
||||
Sha string `json:"sha"`
|
||||
Ref string `json:"ref"`
|
||||
RefName string `json:"ref_name"`
|
||||
RefType string `json:"ref_type"`
|
||||
HeadRef string `json:"head_ref"`
|
||||
BaseRef string `json:"base_ref"`
|
||||
Token string `json:"token"`
|
||||
Workspace string `json:"workspace"`
|
||||
Action string `json:"action"`
|
||||
ActionPath string `json:"action_path"`
|
||||
ActionRef string `json:"action_ref"`
|
||||
ActionRepository string `json:"action_repository"`
|
||||
Job string `json:"job"`
|
||||
JobName string `json:"job_name"`
|
||||
RepositoryOwner string `json:"repository_owner"`
|
||||
RetentionDays string `json:"retention_days"`
|
||||
RunnerPerflog string `json:"runner_perflog"`
|
||||
RunnerTrackingID string `json:"runner_tracking_id"`
|
||||
ServerURL string `json:"server_url"`
|
||||
APIURL string `json:"api_url"`
|
||||
GraphQLURL string `json:"graphql_url"`
|
||||
Event map[string]any `json:"event"`
|
||||
EventPath string `json:"event_path"`
|
||||
Workflow string `json:"workflow"`
|
||||
RunID string `json:"run_id"`
|
||||
RunNumber string `json:"run_number"`
|
||||
Actor string `json:"actor"`
|
||||
Repository string `json:"repository"`
|
||||
EventName string `json:"event_name"`
|
||||
Sha string `json:"sha"`
|
||||
Ref string `json:"ref"`
|
||||
RefName string `json:"ref_name"`
|
||||
RefType string `json:"ref_type"`
|
||||
HeadRef string `json:"head_ref"`
|
||||
BaseRef string `json:"base_ref"`
|
||||
Token string `json:"token"`
|
||||
Workspace string `json:"workspace"`
|
||||
Action string `json:"action"`
|
||||
ActionPath string `json:"action_path"`
|
||||
ActionRef string `json:"action_ref"`
|
||||
ActionRepository string `json:"action_repository"`
|
||||
Job string `json:"job"`
|
||||
JobName string `json:"job_name"`
|
||||
RepositoryOwner string `json:"repository_owner"`
|
||||
RetentionDays string `json:"retention_days"`
|
||||
RunnerPerflog string `json:"runner_perflog"`
|
||||
RunnerTrackingID string `json:"runner_tracking_id"`
|
||||
ServerURL string `json:"server_url"`
|
||||
APIURL string `json:"api_url"`
|
||||
GraphQLURL string `json:"graphql_url"`
|
||||
|
||||
// For Gitea
|
||||
RunAttempt string `json:"run_attempt"`
|
||||
}
|
||||
|
||||
func asString(v interface{}) string {
|
||||
func asString(v any) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
} else if s, ok := v.(string); ok {
|
||||
@@ -53,7 +53,7 @@ func asString(v interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) {
|
||||
func nestedMapLookup(m map[string]any, ks ...string) (rval any) {
|
||||
var ok bool
|
||||
|
||||
if len(ks) == 0 { // degenerate input
|
||||
@@ -63,20 +63,20 @@ func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{})
|
||||
return nil
|
||||
} else if len(ks) == 1 { // we've reached the final key
|
||||
return rval
|
||||
} else if m, ok = rval.(map[string]interface{}); !ok {
|
||||
} else if m, ok = rval.(map[string]any); !ok {
|
||||
return nil
|
||||
} else { // 1+ more keys
|
||||
return nestedMapLookup(m, ks[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func withDefaultBranch(ctx context.Context, b string, event map[string]interface{}) map[string]interface{} {
|
||||
func withDefaultBranch(ctx context.Context, b string, event map[string]any) map[string]any {
|
||||
repoI, ok := event["repository"]
|
||||
if !ok {
|
||||
repoI = make(map[string]interface{})
|
||||
repoI = make(map[string]any)
|
||||
}
|
||||
|
||||
repo, ok := repoI.(map[string]interface{})
|
||||
repo, ok := repoI.(map[string]any)
|
||||
if !ok {
|
||||
common.Logger(ctx).Warnf("unable to set default branch to %v", b)
|
||||
return event
|
||||
@@ -93,29 +93,31 @@ func withDefaultBranch(ctx context.Context, b string, event map[string]interface
|
||||
return event
|
||||
}
|
||||
|
||||
var findGitRef = git.FindGitRef
|
||||
var findGitRevision = git.FindGitRevision
|
||||
var (
|
||||
findGitRef = git.FindGitRef
|
||||
findGitRevision = git.FindGitRevision
|
||||
)
|
||||
|
||||
func (ghc *GithubContext) SetRef(ctx context.Context, defaultBranch string, repoPath string) {
|
||||
func (ghc *GithubContext) SetRef(ctx context.Context, defaultBranch, repoPath string) {
|
||||
logger := common.Logger(ctx)
|
||||
|
||||
// https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows
|
||||
// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads
|
||||
switch ghc.EventName {
|
||||
case "pull_request_target":
|
||||
ghc.Ref = fmt.Sprintf("refs/heads/%s", ghc.BaseRef)
|
||||
ghc.Ref = "refs/heads/" + ghc.BaseRef
|
||||
case "pull_request", "pull_request_review", "pull_request_review_comment":
|
||||
ghc.Ref = fmt.Sprintf("refs/pull/%.0f/merge", ghc.Event["number"])
|
||||
case "deployment", "deployment_status":
|
||||
ghc.Ref = asString(nestedMapLookup(ghc.Event, "deployment", "ref"))
|
||||
case "release":
|
||||
ghc.Ref = fmt.Sprintf("refs/tags/%s", asString(nestedMapLookup(ghc.Event, "release", "tag_name")))
|
||||
ghc.Ref = "refs/tags/" + asString(nestedMapLookup(ghc.Event, "release", "tag_name"))
|
||||
case "push", "create", "workflow_dispatch":
|
||||
ghc.Ref = asString(ghc.Event["ref"])
|
||||
default:
|
||||
defaultBranch := asString(nestedMapLookup(ghc.Event, "repository", "default_branch"))
|
||||
if defaultBranch != "" {
|
||||
ghc.Ref = fmt.Sprintf("refs/heads/%s", defaultBranch)
|
||||
ghc.Ref = "refs/heads/" + defaultBranch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +138,7 @@ func (ghc *GithubContext) SetRef(ctx context.Context, defaultBranch string, repo
|
||||
}
|
||||
|
||||
if ghc.Ref == "" {
|
||||
ghc.Ref = fmt.Sprintf("refs/heads/%s", asString(nestedMapLookup(ghc.Event, "repository", "default_branch")))
|
||||
ghc.Ref = "refs/heads/" + asString(nestedMapLookup(ghc.Event, "repository", "default_branch"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +169,7 @@ func (ghc *GithubContext) SetSha(ctx context.Context, repoPath string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ghc *GithubContext) SetRepositoryAndOwner(ctx context.Context, githubInstance string, remoteName string, repoPath string) {
|
||||
func (ghc *GithubContext) SetRepositoryAndOwner(ctx context.Context, githubInstance, remoteName, repoPath string) {
|
||||
if ghc.Repository == "" {
|
||||
repo, err := git.FindGithubRepo(ctx, repoPath, githubInstance, remoteName)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -27,19 +27,19 @@ func TestSetRef(t *testing.T) {
|
||||
|
||||
tables := []struct {
|
||||
eventName string
|
||||
event map[string]interface{}
|
||||
event map[string]any
|
||||
ref string
|
||||
refName string
|
||||
}{
|
||||
{
|
||||
eventName: "pull_request_target",
|
||||
event: map[string]interface{}{},
|
||||
event: map[string]any{},
|
||||
ref: "refs/heads/master",
|
||||
refName: "master",
|
||||
},
|
||||
{
|
||||
eventName: "pull_request",
|
||||
event: map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"number": 1234.,
|
||||
},
|
||||
ref: "refs/pull/1234/merge",
|
||||
@@ -47,8 +47,8 @@ func TestSetRef(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "deployment",
|
||||
event: map[string]interface{}{
|
||||
"deployment": map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"deployment": map[string]any{
|
||||
"ref": "refs/heads/somebranch",
|
||||
},
|
||||
},
|
||||
@@ -57,8 +57,8 @@ func TestSetRef(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "release",
|
||||
event: map[string]interface{}{
|
||||
"release": map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"release": map[string]any{
|
||||
"tag_name": "v1.0.0",
|
||||
},
|
||||
},
|
||||
@@ -67,7 +67,7 @@ func TestSetRef(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "push",
|
||||
event: map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"ref": "refs/heads/somebranch",
|
||||
},
|
||||
ref: "refs/heads/somebranch",
|
||||
@@ -75,8 +75,8 @@ func TestSetRef(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "unknown",
|
||||
event: map[string]interface{}{
|
||||
"repository": map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"repository": map[string]any{
|
||||
"default_branch": "main",
|
||||
},
|
||||
},
|
||||
@@ -85,7 +85,7 @@ func TestSetRef(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "no-event",
|
||||
event: map[string]interface{}{},
|
||||
event: map[string]any{},
|
||||
ref: "refs/heads/master",
|
||||
refName: "master",
|
||||
},
|
||||
@@ -109,12 +109,12 @@ func TestSetRef(t *testing.T) {
|
||||
|
||||
t.Run("no-default-branch", func(t *testing.T) {
|
||||
findGitRef = func(ctx context.Context, file string) (string, error) {
|
||||
return "", fmt.Errorf("no default branch")
|
||||
return "", errors.New("no default branch")
|
||||
}
|
||||
|
||||
ghc := &GithubContext{
|
||||
EventName: "no-default-branch",
|
||||
Event: map[string]interface{}{},
|
||||
Event: map[string]any{},
|
||||
}
|
||||
|
||||
ghc.SetRef(context.Background(), "", "/some/dir")
|
||||
@@ -141,14 +141,14 @@ func TestSetSha(t *testing.T) {
|
||||
|
||||
tables := []struct {
|
||||
eventName string
|
||||
event map[string]interface{}
|
||||
event map[string]any
|
||||
sha string
|
||||
}{
|
||||
{
|
||||
eventName: "pull_request_target",
|
||||
event: map[string]interface{}{
|
||||
"pull_request": map[string]interface{}{
|
||||
"base": map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"pull_request": map[string]any{
|
||||
"base": map[string]any{
|
||||
"sha": "pr-base-sha",
|
||||
},
|
||||
},
|
||||
@@ -157,15 +157,15 @@ func TestSetSha(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "pull_request",
|
||||
event: map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"number": 1234.,
|
||||
},
|
||||
sha: "1234fakesha",
|
||||
},
|
||||
{
|
||||
eventName: "deployment",
|
||||
event: map[string]interface{}{
|
||||
"deployment": map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"deployment": map[string]any{
|
||||
"sha": "deployment-sha",
|
||||
},
|
||||
},
|
||||
@@ -173,12 +173,12 @@ func TestSetSha(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "release",
|
||||
event: map[string]interface{}{},
|
||||
event: map[string]any{},
|
||||
sha: "1234fakesha",
|
||||
},
|
||||
{
|
||||
eventName: "push",
|
||||
event: map[string]interface{}{
|
||||
event: map[string]any{
|
||||
"after": "push-sha",
|
||||
"deleted": false,
|
||||
},
|
||||
@@ -186,12 +186,12 @@ func TestSetSha(t *testing.T) {
|
||||
},
|
||||
{
|
||||
eventName: "unknown",
|
||||
event: map[string]interface{}{},
|
||||
event: map[string]any{},
|
||||
sha: "1234fakesha",
|
||||
},
|
||||
{
|
||||
eventName: "no-event",
|
||||
event: map[string]interface{}{},
|
||||
event: map[string]any{},
|
||||
sha: "1234fakesha",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"slices"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -57,7 +58,7 @@ type WorkflowFiles struct {
|
||||
|
||||
// NewWorkflowPlanner will load a specific workflow, all workflows from a directory or all workflows from a directory and its subdirectories
|
||||
//
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func NewWorkflowPlanner(path string, noWorkflowRecurse bool) (WorkflowPlanner, error) {
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
@@ -115,9 +116,6 @@ func NewWorkflowPlanner(path string, noWorkflowRecurse bool) (WorkflowPlanner, e
|
||||
workflowDirEntry: fs.FileInfoToDirEntry(fi),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wp := new(workflowPlanner)
|
||||
for _, wf := range workflows {
|
||||
@@ -288,11 +286,8 @@ func (wp *workflowPlanner) GetEvents() []string {
|
||||
for _, w := range wp.workflows {
|
||||
found := false
|
||||
for _, e := range events {
|
||||
for _, we := range w.On() {
|
||||
if e == we {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if slices.Contains(w.On(), e) {
|
||||
found = true
|
||||
}
|
||||
if found {
|
||||
break
|
||||
@@ -305,9 +300,7 @@ func (wp *workflowPlanner) GetEvents() []string {
|
||||
}
|
||||
|
||||
// sort the list based on depth of dependencies
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i] < events[j]
|
||||
})
|
||||
slices.Sort(events)
|
||||
|
||||
return events
|
||||
}
|
||||
@@ -338,7 +331,7 @@ func (s *Stage) GetJobIDs() []string {
|
||||
// Merge stages with existing stages in plan
|
||||
func (p *Plan) mergeStages(stages []*Stage) {
|
||||
newStages := make([]*Stage, int(math.Max(float64(len(p.Stages)), float64(len(stages)))))
|
||||
for i := 0; i < len(newStages); i++ {
|
||||
for i := range newStages {
|
||||
newStages[i] = new(Stage)
|
||||
if i >= len(p.Stages) {
|
||||
newStages[i].Runs = append(newStages[i].Runs, stages[i].Runs...)
|
||||
@@ -390,7 +383,7 @@ func createStages(w *Workflow, jobIDs ...string) ([]*Stage, error) {
|
||||
}
|
||||
|
||||
if len(stages) == 0 {
|
||||
return nil, fmt.Errorf("Could not find any stages to run. View the valid jobs with `act --list`. Use `act --help` to find how to filter by Job ID/Workflow/Event Name")
|
||||
return nil, errors.New("Could not find any stages to run. View the valid jobs with `act --list`. Use `act --help` to find how to filter by Job ID/Workflow/Event Name")
|
||||
}
|
||||
|
||||
return stages, nil
|
||||
|
||||
@@ -4,15 +4,17 @@ import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
)
|
||||
|
||||
// Workflow is the structure of the files in .github/workflows
|
||||
@@ -45,7 +47,7 @@ func (w *Workflow) On() []string {
|
||||
}
|
||||
return val
|
||||
case yaml.MappingNode:
|
||||
var val map[string]interface{}
|
||||
var val map[string]any
|
||||
err := w.RawOn.Decode(&val)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -59,9 +61,9 @@ func (w *Workflow) On() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Workflow) OnEvent(event string) interface{} {
|
||||
func (w *Workflow) OnEvent(event string) any {
|
||||
if w.RawOn.Kind == yaml.MappingNode {
|
||||
var val map[string]interface{}
|
||||
var val map[string]any
|
||||
if !decodeNode(w.RawOn, &val) {
|
||||
return nil
|
||||
}
|
||||
@@ -77,10 +79,10 @@ func (w *Workflow) OnSchedule() []string {
|
||||
}
|
||||
|
||||
switch val := schedules.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
allSchedules := []string{}
|
||||
for _, v := range val {
|
||||
for k, cron := range v.(map[string]interface{}) {
|
||||
for k, cron := range v.(map[string]any) {
|
||||
if k != "cron" {
|
||||
continue
|
||||
}
|
||||
@@ -121,10 +123,8 @@ func (w *Workflow) WorkflowDispatchConfig() *WorkflowDispatch {
|
||||
if !decodeNode(w.RawOn, &val) {
|
||||
return nil
|
||||
}
|
||||
for _, v := range val {
|
||||
if v == "workflow_dispatch" {
|
||||
return &WorkflowDispatch{}
|
||||
}
|
||||
if slices.Contains(val, "workflow_dispatch") {
|
||||
return &WorkflowDispatch{}
|
||||
}
|
||||
case yaml.MappingNode:
|
||||
var val map[string]yaml.Node
|
||||
@@ -199,7 +199,7 @@ type Job struct {
|
||||
Defaults Defaults `yaml:"defaults"`
|
||||
Outputs map[string]string `yaml:"outputs"`
|
||||
Uses string `yaml:"uses"`
|
||||
With map[string]interface{} `yaml:"with"`
|
||||
With map[string]any `yaml:"with"`
|
||||
RawSecrets yaml.Node `yaml:"secrets"`
|
||||
RawPermissions yaml.Node `yaml:"permissions"`
|
||||
Result string
|
||||
@@ -378,9 +378,9 @@ func (j *Job) Environment() map[string]string {
|
||||
}
|
||||
|
||||
// Matrix decodes RawMatrix YAML node
|
||||
func (j *Job) Matrix() map[string][]interface{} {
|
||||
func (j *Job) Matrix() map[string][]any {
|
||||
if j.Strategy.RawMatrix.Kind == yaml.MappingNode {
|
||||
var val map[string][]interface{}
|
||||
var val map[string][]any
|
||||
if !decodeNode(j.Strategy.RawMatrix, &val) {
|
||||
return nil
|
||||
}
|
||||
@@ -392,22 +392,22 @@ func (j *Job) Matrix() map[string][]interface{} {
|
||||
// GetMatrixes returns the matrix cross product
|
||||
// It skips includes and hard fails excludes for non-existing keys
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (j *Job) GetMatrixes() ([]map[string]interface{}, error) {
|
||||
matrixes := make([]map[string]interface{}, 0)
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (j *Job) GetMatrixes() ([]map[string]any, error) {
|
||||
matrixes := make([]map[string]any, 0)
|
||||
if j.Strategy != nil {
|
||||
// Always set these values, even if there's an error later
|
||||
j.Strategy.FailFast = j.Strategy.GetFailFast()
|
||||
j.Strategy.MaxParallel = j.Strategy.GetMaxParallel()
|
||||
|
||||
if m := j.Matrix(); m != nil {
|
||||
includes := make([]map[string]interface{}, 0)
|
||||
extraIncludes := make([]map[string]interface{}, 0)
|
||||
includes := make([]map[string]any, 0)
|
||||
extraIncludes := make([]map[string]any, 0)
|
||||
for _, v := range m["include"] {
|
||||
switch t := v.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, i := range t {
|
||||
i := i.(map[string]interface{})
|
||||
i := i.(map[string]any)
|
||||
extraInclude := true
|
||||
for k := range i {
|
||||
if _, ok := m[k]; ok {
|
||||
@@ -420,8 +420,8 @@ func (j *Job) GetMatrixes() ([]map[string]interface{}, error) {
|
||||
extraIncludes = append(extraIncludes, i)
|
||||
}
|
||||
}
|
||||
case interface{}:
|
||||
v := v.(map[string]interface{})
|
||||
case any:
|
||||
v := v.(map[string]any)
|
||||
extraInclude := true
|
||||
for k := range v {
|
||||
if _, ok := m[k]; ok {
|
||||
@@ -437,9 +437,9 @@ func (j *Job) GetMatrixes() ([]map[string]interface{}, error) {
|
||||
}
|
||||
delete(m, "include")
|
||||
|
||||
excludes := make([]map[string]interface{}, 0)
|
||||
excludes := make([]map[string]any, 0)
|
||||
for _, e := range m["exclude"] {
|
||||
e := e.(map[string]interface{})
|
||||
e := e.(map[string]any)
|
||||
for k := range e {
|
||||
if _, ok := m[k]; ok {
|
||||
excludes = append(excludes, e)
|
||||
@@ -468,9 +468,7 @@ func (j *Job) GetMatrixes() ([]map[string]interface{}, error) {
|
||||
if commonKeysMatch2(matrix, include, m) {
|
||||
matched = true
|
||||
log.Debugf("Adding include values '%v' to existing entry", include)
|
||||
for k, v := range include {
|
||||
matrix[k] = v
|
||||
}
|
||||
maps.Copy(matrix, include)
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
@@ -482,19 +480,19 @@ func (j *Job) GetMatrixes() ([]map[string]interface{}, error) {
|
||||
matrixes = append(matrixes, include)
|
||||
}
|
||||
if len(matrixes) == 0 {
|
||||
matrixes = append(matrixes, make(map[string]interface{}))
|
||||
matrixes = append(matrixes, make(map[string]any))
|
||||
}
|
||||
} else {
|
||||
matrixes = append(matrixes, make(map[string]interface{}))
|
||||
matrixes = append(matrixes, make(map[string]any))
|
||||
}
|
||||
} else {
|
||||
matrixes = append(matrixes, make(map[string]interface{}))
|
||||
matrixes = append(matrixes, make(map[string]any))
|
||||
log.Debugf("Empty Strategy, matrixes=%v", matrixes)
|
||||
}
|
||||
return matrixes, nil
|
||||
}
|
||||
|
||||
func commonKeysMatch(a map[string]interface{}, b map[string]interface{}) bool {
|
||||
func commonKeysMatch(a, b map[string]any) bool {
|
||||
for aKey, aVal := range a {
|
||||
if bVal, ok := b[aKey]; ok && !reflect.DeepEqual(aVal, bVal) {
|
||||
return false
|
||||
@@ -503,7 +501,7 @@ func commonKeysMatch(a map[string]interface{}, b map[string]interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func commonKeysMatch2(a map[string]interface{}, b map[string]interface{}, m map[string][]interface{}) bool {
|
||||
func commonKeysMatch2(a, b map[string]any, m map[string][]any) bool {
|
||||
for aKey, aVal := range a {
|
||||
_, useKey := m[aKey]
|
||||
if bVal, ok := b[aKey]; useKey && ok && !reflect.DeepEqual(aVal, bVal) {
|
||||
@@ -623,7 +621,7 @@ func (s *Step) GetEnv() map[string]string {
|
||||
|
||||
for k, v := range s.With {
|
||||
envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(k), "_")
|
||||
envKey = fmt.Sprintf("INPUT_%s", strings.ToUpper(envKey))
|
||||
envKey = "INPUT_" + strings.ToUpper(envKey)
|
||||
env[envKey] = v
|
||||
}
|
||||
return env
|
||||
@@ -631,7 +629,7 @@ func (s *Step) GetEnv() map[string]string {
|
||||
|
||||
// ShellCommand returns the command for the shell
|
||||
func (s *Step) ShellCommand() string {
|
||||
shellCommand := ""
|
||||
var shellCommand string
|
||||
|
||||
// Reference: https://github.com/actions/runner/blob/8109c962f09d9acc473d92c595ff43afceddb347/src/Runner.Worker/Handlers/ScriptHandlerHelpers.cs#L9-L17
|
||||
switch s.Shell {
|
||||
@@ -760,11 +758,11 @@ func (w *Workflow) GetJobIDs() []string {
|
||||
return ids
|
||||
}
|
||||
|
||||
var OnDecodeNodeError = func(node yaml.Node, out interface{}, err error) {
|
||||
var OnDecodeNodeError = func(node yaml.Node, out any, err error) {
|
||||
log.Fatalf("Failed to decode node %v into %T: %v", node, out, err)
|
||||
}
|
||||
|
||||
func decodeNode(node yaml.Node, out interface{}) bool {
|
||||
func decodeNode(node yaml.Node, out any) bool {
|
||||
if err := node.Decode(out); err != nil {
|
||||
if OnDecodeNodeError != nil {
|
||||
OnDecodeNodeError(node, out, err)
|
||||
@@ -791,7 +789,7 @@ func (r *RawConcurrency) UnmarshalYAML(n *yaml.Node) error {
|
||||
return n.Decode((*objectConcurrency)(r))
|
||||
}
|
||||
|
||||
func (r *RawConcurrency) MarshalYAML() (interface{}, error) {
|
||||
func (r *RawConcurrency) MarshalYAML() (any, error) {
|
||||
if r.RawExpression != "" {
|
||||
return r.RawExpression, nil
|
||||
}
|
||||
|
||||
@@ -430,24 +430,24 @@ func TestReadWorkflow_Strategy(t *testing.T) {
|
||||
job := wf.Jobs["strategy-only-max-parallel"]
|
||||
matrixes, err := job.GetMatrixes()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, matrixes, []map[string]interface{}{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]interface{}(nil))
|
||||
assert.Equal(t, matrixes, []map[string]any{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]any(nil))
|
||||
assert.Equal(t, job.Strategy.MaxParallel, 2)
|
||||
assert.Equal(t, job.Strategy.FailFast, true)
|
||||
|
||||
job = wf.Jobs["strategy-only-fail-fast"]
|
||||
matrixes, err = job.GetMatrixes()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, matrixes, []map[string]interface{}{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]interface{}(nil))
|
||||
assert.Equal(t, matrixes, []map[string]any{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]any(nil))
|
||||
assert.Equal(t, job.Strategy.MaxParallel, 4)
|
||||
assert.Equal(t, job.Strategy.FailFast, false)
|
||||
|
||||
job = wf.Jobs["strategy-no-matrix"]
|
||||
matrixes, err = job.GetMatrixes()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, matrixes, []map[string]interface{}{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]interface{}(nil))
|
||||
assert.Equal(t, matrixes, []map[string]any{{}})
|
||||
assert.Equal(t, job.Matrix(), map[string][]any(nil))
|
||||
assert.Equal(t, job.Strategy.MaxParallel, 2)
|
||||
assert.Equal(t, job.Strategy.FailFast, false)
|
||||
|
||||
@@ -455,7 +455,7 @@ func TestReadWorkflow_Strategy(t *testing.T) {
|
||||
matrixes, err = job.GetMatrixes()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, matrixes,
|
||||
[]map[string]interface{}{
|
||||
[]map[string]any{
|
||||
{"datacenter": "site-c", "node-version": "14.x", "site": "staging"},
|
||||
{"datacenter": "site-c", "node-version": "16.x", "site": "staging"},
|
||||
{"datacenter": "site-d", "node-version": "16.x", "site": "staging"},
|
||||
@@ -465,15 +465,15 @@ func TestReadWorkflow_Strategy(t *testing.T) {
|
||||
},
|
||||
)
|
||||
assert.Equal(t, job.Matrix(),
|
||||
map[string][]interface{}{
|
||||
map[string][]any{
|
||||
"datacenter": {"site-c", "site-d"},
|
||||
"exclude": {
|
||||
map[string]interface{}{"datacenter": "site-d", "node-version": "14.x", "site": "staging"},
|
||||
map[string]any{"datacenter": "site-d", "node-version": "14.x", "site": "staging"},
|
||||
},
|
||||
"include": {
|
||||
map[string]interface{}{"php-version": 5.4},
|
||||
map[string]interface{}{"datacenter": "site-a", "node-version": "10.x", "site": "prod"},
|
||||
map[string]interface{}{"datacenter": "site-b", "node-version": "12.x", "site": "dev"},
|
||||
map[string]any{"php-version": 5.4},
|
||||
map[string]any{"datacenter": "site-a", "node-version": "10.x", "site": "prod"},
|
||||
map[string]any{"datacenter": "site-b", "node-version": "12.x", "site": "dev"},
|
||||
},
|
||||
"node-version": {"14.x", "16.x"},
|
||||
"site": {"staging"},
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
type actionStep interface {
|
||||
@@ -29,7 +29,7 @@ type actionStep interface {
|
||||
getCompositeSteps() *compositeSteps
|
||||
}
|
||||
|
||||
type readAction func(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error)
|
||||
type readAction func(ctx context.Context, step *model.Step, actionDir, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error)
|
||||
|
||||
type actionYamlReader func(filename string) (io.Reader, io.Closer, error)
|
||||
|
||||
@@ -40,7 +40,7 @@ type runAction func(step actionStep, actionDir string, remoteAction *remoteActio
|
||||
//go:embed res/trampoline.js
|
||||
var trampoline embed.FS
|
||||
|
||||
func readActionImpl(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
func readActionImpl(ctx context.Context, step *model.Step, actionDir, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
logger := common.Logger(ctx)
|
||||
allErrors := []error{}
|
||||
addError := func(fileName string, err error) {
|
||||
@@ -117,7 +117,7 @@ func readActionImpl(ctx context.Context, step *model.Step, actionDir string, act
|
||||
return action, err
|
||||
}
|
||||
|
||||
func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string, actionPath string, containerActionDir string) error {
|
||||
func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir, actionPath, containerActionDir string) error {
|
||||
logger := common.Logger(ctx)
|
||||
rc := step.getRunContext()
|
||||
stepModel := step.getStepModel()
|
||||
@@ -207,7 +207,7 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
|
||||
|
||||
rc.ApplyExtraPath(ctx, step.getEnv())
|
||||
|
||||
execFileName := fmt.Sprintf("%s.out", action.Runs.Main)
|
||||
execFileName := action.Runs.Main + ".out"
|
||||
buildArgs := []string{"go", "build", "-o", execFileName, action.Runs.Main}
|
||||
execArgs := []string{filepath.Join(containerActionDir, execFileName)}
|
||||
|
||||
@@ -262,8 +262,8 @@ func removeGitIgnore(ctx context.Context, directory string) error {
|
||||
|
||||
// TODO: break out parts of function to reduce complexicity
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func execAsDocker(ctx context.Context, step actionStep, actionName string, basedir string, localAction bool) error {
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func execAsDocker(ctx context.Context, step actionStep, actionName, basedir string, localAction bool) error {
|
||||
logger := common.Logger(ctx)
|
||||
rc := step.getRunContext()
|
||||
action := step.getActionModel()
|
||||
@@ -271,14 +271,14 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based
|
||||
var prepImage common.Executor
|
||||
var image string
|
||||
forcePull := false
|
||||
if strings.HasPrefix(action.Runs.Image, "docker://") {
|
||||
image = strings.TrimPrefix(action.Runs.Image, "docker://")
|
||||
if after, ok := strings.CutPrefix(action.Runs.Image, "docker://"); ok {
|
||||
image = after
|
||||
// Apply forcePull only for prebuild docker images
|
||||
forcePull = rc.Config.ForcePull
|
||||
} else {
|
||||
// "-dockeraction" enshures that "./", "./test " won't get converted to "act-:latest", "act-test-:latest" which are invalid docker image names
|
||||
image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest")
|
||||
image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-"))
|
||||
image = "act-" + strings.TrimLeft(image, "-")
|
||||
image = strings.ToLower(image)
|
||||
contextDir, fileName := filepath.Split(filepath.Join(basedir, action.Runs.Image))
|
||||
|
||||
@@ -391,7 +391,7 @@ func evalDockerArgs(ctx context.Context, step step, action *model.Action, cmd *[
|
||||
}
|
||||
}
|
||||
|
||||
func newStepContainer(ctx context.Context, step step, image string, cmd []string, entrypoint []string) container.Container {
|
||||
func newStepContainer(ctx context.Context, step step, image string, cmd, entrypoint []string) container.Container {
|
||||
rc := step.getRunContext()
|
||||
stepModel := step.getStepModel()
|
||||
rawLogger := common.Logger(ctx).WithField("raw_output", true)
|
||||
@@ -414,7 +414,7 @@ func newStepContainer(ctx context.Context, step step, image string, cmd []string
|
||||
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp"))
|
||||
|
||||
binds, mounts := rc.GetBindsAndMounts()
|
||||
networkMode := fmt.Sprintf("container:%s", rc.jobContainerName())
|
||||
networkMode := "container:" + rc.jobContainerName()
|
||||
if rc.IsHostEnv(ctx) {
|
||||
networkMode = "default"
|
||||
}
|
||||
@@ -446,7 +446,7 @@ func populateEnvsFromSavedState(env *map[string]string, step actionStep, rc *Run
|
||||
state, ok := rc.IntraActionState[step.getStepModel().ID]
|
||||
if ok {
|
||||
for name, value := range state {
|
||||
envName := fmt.Sprintf("STATE_%s", name)
|
||||
envName := "STATE_" + name
|
||||
(*env)[envName] = value
|
||||
}
|
||||
}
|
||||
@@ -456,7 +456,7 @@ func populateEnvsFromInput(ctx context.Context, env *map[string]string, action *
|
||||
eval := rc.NewExpressionEvaluator(ctx)
|
||||
for inputID, input := range action.Inputs {
|
||||
envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(inputID), "_")
|
||||
envKey = fmt.Sprintf("INPUT_%s", envKey)
|
||||
envKey = "INPUT_" + envKey
|
||||
if _, ok := (*env)[envKey]; !ok {
|
||||
(*env)[envKey] = eval.Interpolate(ctx, input.Default)
|
||||
}
|
||||
@@ -543,7 +543,7 @@ func runPreStep(step actionStep) common.Executor {
|
||||
actionPath = ""
|
||||
}
|
||||
|
||||
actionLocation := ""
|
||||
var actionLocation string
|
||||
if actionPath != "" {
|
||||
actionLocation = path.Join(actionDir, actionPath)
|
||||
} else {
|
||||
@@ -571,7 +571,7 @@ func runPreStep(step actionStep) common.Executor {
|
||||
if steps := step.getCompositeSteps(); steps != nil && steps.pre != nil {
|
||||
return steps.pre(ctx)
|
||||
}
|
||||
return fmt.Errorf("missing steps in composite action")
|
||||
return errors.New("missing steps in composite action")
|
||||
|
||||
case x == model.ActionRunsUsingGo:
|
||||
// defaults in pre steps were missing, however provided inputs are available
|
||||
@@ -587,7 +587,7 @@ func runPreStep(step actionStep) common.Executor {
|
||||
actionPath = ""
|
||||
}
|
||||
|
||||
actionLocation := ""
|
||||
var actionLocation string
|
||||
if actionPath != "" {
|
||||
actionLocation = path.Join(actionDir, actionPath)
|
||||
} else {
|
||||
@@ -602,7 +602,7 @@ func runPreStep(step actionStep) common.Executor {
|
||||
|
||||
rc.ApplyExtraPath(ctx, step.getEnv())
|
||||
|
||||
execFileName := fmt.Sprintf("%s.out", action.Runs.Pre)
|
||||
execFileName := action.Runs.Pre + ".out"
|
||||
buildArgs := []string{"go", "build", "-o", execFileName, action.Runs.Pre}
|
||||
execArgs := []string{filepath.Join(containerActionDir, execFileName)}
|
||||
|
||||
@@ -672,7 +672,7 @@ func runPostStep(step actionStep) common.Executor {
|
||||
actionPath = ""
|
||||
}
|
||||
|
||||
actionLocation := ""
|
||||
var actionLocation string
|
||||
if actionPath != "" {
|
||||
actionLocation = path.Join(actionDir, actionPath)
|
||||
} else {
|
||||
@@ -702,13 +702,13 @@ func runPostStep(step actionStep) common.Executor {
|
||||
if steps := step.getCompositeSteps(); steps != nil && steps.post != nil {
|
||||
return steps.post(ctx)
|
||||
}
|
||||
return fmt.Errorf("missing steps in composite action")
|
||||
return errors.New("missing steps in composite action")
|
||||
|
||||
case x == model.ActionRunsUsingGo:
|
||||
populateEnvsFromSavedState(step.getEnv(), step, rc)
|
||||
rc.ApplyExtraPath(ctx, step.getEnv())
|
||||
|
||||
execFileName := fmt.Sprintf("%s.out", action.Runs.Post)
|
||||
execFileName := action.Runs.Post + ".out"
|
||||
buildArgs := []string{"go", "build", "-o", execFileName, action.Runs.Post}
|
||||
execArgs := []string{filepath.Join(containerActionDir, execFileName)}
|
||||
|
||||
|
||||
@@ -5,17 +5,15 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//nolint:gosec
|
||||
func TestActionCache(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
cache := &GoGitActionCache{
|
||||
Path: os.TempDir(),
|
||||
Path: t.TempDir(),
|
||||
}
|
||||
ctx := context.Background()
|
||||
cacheDir := "nektos/act-test-actions"
|
||||
|
||||
@@ -2,8 +2,9 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
@@ -25,7 +26,7 @@ func evaluateCompositeInputAndEnv(ctx context.Context, parent *RunContext, step
|
||||
|
||||
for inputID, input := range step.getActionModel().Inputs {
|
||||
envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(inputID), "_")
|
||||
envKey = fmt.Sprintf("INPUT_%s", strings.ToUpper(envKey))
|
||||
envKey = "INPUT_" + strings.ToUpper(envKey)
|
||||
|
||||
// lookup if key is defined in the step but the already
|
||||
// evaluated value from the environment
|
||||
@@ -90,7 +91,7 @@ func execAsComposite(step actionStep) common.Executor {
|
||||
steps := step.getCompositeSteps()
|
||||
|
||||
if steps == nil || steps.main == nil {
|
||||
return fmt.Errorf("missing steps in composite action")
|
||||
return errors.New("missing steps in composite action")
|
||||
}
|
||||
|
||||
ctx = WithCompositeLogger(ctx, &compositeRC.Masks)
|
||||
@@ -138,7 +139,7 @@ func (rc *RunContext) compositeExecutor(action *model.Action) *compositeSteps {
|
||||
|
||||
for i, step := range action.Runs.Steps {
|
||||
if step.ID == "" {
|
||||
step.ID = fmt.Sprintf("%d", i)
|
||||
step.ID = strconv.Itoa(i)
|
||||
}
|
||||
step.Number = i
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -122,7 +123,7 @@ runs:
|
||||
|
||||
writeFile := func(filename string, data []byte, perm fs.FileMode) error {
|
||||
assert.Equal(t, "actionDir/actionPath/trampoline.js", filename)
|
||||
assert.Equal(t, fs.FileMode(0400), perm)
|
||||
assert.Equal(t, fs.FileMode(0o400), perm)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ func tryParseRawActionCommand(line string) (command string, kvPairs map[string]s
|
||||
arg = m[4]
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
return command, kvPairs, arg, ok
|
||||
}
|
||||
|
||||
func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler {
|
||||
@@ -135,10 +135,10 @@ func (rc *RunContext) addPath(ctx context.Context, arg string) {
|
||||
rc.ExtraPath = extraPath
|
||||
}
|
||||
|
||||
func parseKeyValuePairs(kvPairs string, separator string) map[string]string {
|
||||
func parseKeyValuePairs(kvPairs, separator string) map[string]string {
|
||||
rtn := make(map[string]string)
|
||||
kvPairList := strings.Split(kvPairs, separator)
|
||||
for _, kvPair := range kvPairList {
|
||||
kvPairList := strings.SplitSeq(kvPairs, separator)
|
||||
for kvPair := range kvPairList {
|
||||
kv := strings.Split(kvPair, "=")
|
||||
if len(kv) == 2 {
|
||||
rtn[kv[0]] = kv[1]
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetEnv(t *testing.T) {
|
||||
@@ -164,7 +164,7 @@ func TestAddmaskUsemask(t *testing.T) {
|
||||
|
||||
re := captureOutput(t, func() {
|
||||
ctx := context.Background()
|
||||
ctx = WithJobLogger(ctx, "0", "testjob", config, &rc.Masks, map[string]interface{}{})
|
||||
ctx = WithJobLogger(ctx, "0", "testjob", config, &rc.Masks, map[string]any{})
|
||||
|
||||
handler := rc.commandHandler(ctx)
|
||||
handler("::add-mask::secret\n")
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ type containerMock struct {
|
||||
container.LinuxContainerEnvironmentExtensions
|
||||
}
|
||||
|
||||
func (cm *containerMock) Create(capAdd []string, capDrop []string) common.Executor {
|
||||
func (cm *containerMock) Create(capAdd, capDrop []string) common.Executor {
|
||||
args := cm.Called(capAdd, capDrop)
|
||||
return args.Get(0).(func(context.Context) error)
|
||||
}
|
||||
@@ -55,7 +56,7 @@ func (cm *containerMock) Copy(destPath string, files ...*container.FileEntry) co
|
||||
return args.Get(0).(func(context.Context) error)
|
||||
}
|
||||
|
||||
func (cm *containerMock) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
|
||||
func (cm *containerMock) CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor {
|
||||
args := cm.Called(destPath, srcPath, useGitIgnore)
|
||||
return args.Get(0).(func(context.Context) error)
|
||||
}
|
||||
|
||||
@@ -4,24 +4,26 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/exprparser"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
// ExpressionEvaluator is the interface for evaluating expressions
|
||||
type ExpressionEvaluator interface {
|
||||
evaluate(context.Context, string, exprparser.DefaultStatusCheck) (interface{}, error)
|
||||
evaluate(context.Context, string, exprparser.DefaultStatusCheck) (any, error)
|
||||
EvaluateYamlNode(context.Context, *yaml.Node) error
|
||||
Interpolate(context.Context, string) string
|
||||
}
|
||||
@@ -36,7 +38,7 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||
|
||||
// todo: cleanup EvaluationEnvironment creation
|
||||
using := make(map[string]exprparser.Needs)
|
||||
strategy := make(map[string]interface{})
|
||||
strategy := make(map[string]any)
|
||||
if rc.Run != nil {
|
||||
job := rc.Run.Job()
|
||||
if job != nil && job.Strategy != nil {
|
||||
@@ -64,9 +66,7 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||
result := model.WorkflowCallResult{
|
||||
Outputs: map[string]string{},
|
||||
}
|
||||
for k, v := range job.Outputs {
|
||||
result.Outputs[k] = v
|
||||
}
|
||||
maps.Copy(result.Outputs, job.Outputs)
|
||||
workflowCallResult[jobName] = &result
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ var hashfiles string
|
||||
func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step) ExpressionEvaluator {
|
||||
// todo: cleanup EvaluationEnvironment creation
|
||||
job := rc.Run.Job()
|
||||
strategy := make(map[string]interface{})
|
||||
strategy := make(map[string]any)
|
||||
if job.Strategy != nil {
|
||||
strategy["fail-fast"] = job.Strategy.FailFast
|
||||
strategy["max-parallel"] = job.Strategy.MaxParallel
|
||||
@@ -157,8 +157,8 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step)
|
||||
}
|
||||
}
|
||||
|
||||
func getHashFilesFunction(ctx context.Context, rc *RunContext) func(v []reflect.Value) (interface{}, error) {
|
||||
hashFiles := func(v []reflect.Value) (interface{}, error) {
|
||||
func getHashFilesFunction(ctx context.Context, rc *RunContext) func(v []reflect.Value) (any, error) {
|
||||
hashFiles := func(v []reflect.Value) (any, error) {
|
||||
if rc.JobContainer != nil {
|
||||
timeed, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
@@ -182,9 +182,7 @@ func getHashFilesFunction(ctx context.Context, rc *RunContext) func(v []reflect.
|
||||
patterns = append(patterns, s)
|
||||
}
|
||||
env := map[string]string{}
|
||||
for k, v := range rc.Env {
|
||||
env[k] = v
|
||||
}
|
||||
maps.Copy(env, rc.Env)
|
||||
env["patterns"] = strings.Join(patterns, "\n")
|
||||
if followSymlink {
|
||||
env["followSymbolicLinks"] = "true"
|
||||
@@ -222,7 +220,7 @@ type expressionEvaluator struct {
|
||||
interpreter exprparser.Interpreter
|
||||
}
|
||||
|
||||
func (ee expressionEvaluator) evaluate(ctx context.Context, in string, defaultStatusCheck exprparser.DefaultStatusCheck) (interface{}, error) {
|
||||
func (ee expressionEvaluator) evaluate(ctx context.Context, in string, defaultStatusCheck exprparser.DefaultStatusCheck) (any, error) {
|
||||
logger := common.Logger(ctx)
|
||||
logger.Debugf("evaluating expression '%s'", in)
|
||||
evaluated, err := ee.interpreter.Evaluate(in, defaultStatusCheck)
|
||||
@@ -403,7 +401,7 @@ func escapeFormatString(in string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(in, "{", "{{"), "}", "}}")
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (string, error) {
|
||||
if !strings.Contains(in, "${{") || !strings.Contains(in, "}}") {
|
||||
return in, nil
|
||||
@@ -414,7 +412,7 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
||||
exprStart := -1
|
||||
strStart := -1
|
||||
var results []string
|
||||
formatOut := ""
|
||||
var formatOut strings.Builder
|
||||
for pos < len(in) {
|
||||
if strStart > -1 {
|
||||
matches := strPattern.FindStringIndex(in[pos:])
|
||||
@@ -437,7 +435,7 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
||||
}
|
||||
|
||||
if exprEnd > -1 {
|
||||
formatOut += fmt.Sprintf("{%d}", len(results))
|
||||
fmt.Fprintf(&formatOut, "{%d}", len(results))
|
||||
results = append(results, strings.TrimSpace(in[exprStart:pos+exprEnd]))
|
||||
pos += exprEnd + 2
|
||||
exprStart = -1
|
||||
@@ -449,30 +447,30 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
||||
} else {
|
||||
exprStart = strings.Index(in[pos:], "${{")
|
||||
if exprStart != -1 {
|
||||
formatOut += escapeFormatString(in[pos : pos+exprStart])
|
||||
formatOut.WriteString(escapeFormatString(in[pos : pos+exprStart]))
|
||||
exprStart = pos + exprStart + 3
|
||||
pos = exprStart
|
||||
} else {
|
||||
formatOut += escapeFormatString(in[pos:])
|
||||
formatOut.WriteString(escapeFormatString(in[pos:]))
|
||||
pos = len(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(results) == 1 && formatOut == "{0}" && !forceFormat {
|
||||
if len(results) == 1 && formatOut.String() == "{0}" && !forceFormat {
|
||||
return in, nil
|
||||
}
|
||||
|
||||
out := fmt.Sprintf("format('%s', %s)", strings.ReplaceAll(formatOut, "'", "''"), strings.Join(results, ", "))
|
||||
out := fmt.Sprintf("format('%s', %s)", strings.ReplaceAll(formatOut.String(), "'", "''"), strings.Join(results, ", "))
|
||||
if in != out {
|
||||
common.Logger(ctx).Debugf("expression '%s' rewritten to '%s'", in, out)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]interface{} {
|
||||
inputs := map[string]interface{}{}
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]any {
|
||||
inputs := map[string]any{}
|
||||
|
||||
setupWorkflowInputs(ctx, &inputs, rc)
|
||||
|
||||
@@ -484,8 +482,8 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod
|
||||
}
|
||||
|
||||
for k, v := range env {
|
||||
if strings.HasPrefix(k, "INPUT_") {
|
||||
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
|
||||
if after, ok := strings.CutPrefix(k, "INPUT_"); ok {
|
||||
inputs[strings.ToLower(after)] = v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +523,7 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod
|
||||
return inputs
|
||||
}
|
||||
|
||||
func setupWorkflowInputs(ctx context.Context, inputs *map[string]interface{}, rc *RunContext) {
|
||||
func setupWorkflowInputs(ctx context.Context, inputs *map[string]any, rc *RunContext) {
|
||||
if rc.caller != nil {
|
||||
config := rc.Run.Workflow.WorkflowCallConfig()
|
||||
|
||||
|
||||
@@ -6,17 +6,19 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/exprparser"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
yaml "go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
func createRunContext(t *testing.T) *RunContext {
|
||||
var yml yaml.Node
|
||||
err := yml.Encode(map[string][]interface{}{
|
||||
err := yml.Encode(map[string][]any{
|
||||
"os": {"Linux", "Windows"},
|
||||
"foo": {"bar", "baz"},
|
||||
})
|
||||
@@ -48,7 +50,7 @@ func createRunContext(t *testing.T) *RunContext {
|
||||
},
|
||||
},
|
||||
},
|
||||
Matrix: map[string]interface{}{
|
||||
Matrix: map[string]any{
|
||||
"os": "Linux",
|
||||
"foo": "bar",
|
||||
},
|
||||
@@ -84,7 +86,7 @@ func TestEvaluateRunContext(t *testing.T) {
|
||||
|
||||
tables := []struct {
|
||||
in string
|
||||
out interface{}
|
||||
out any
|
||||
errMesg string
|
||||
}{
|
||||
{" 1 ", 1, ""},
|
||||
@@ -138,7 +140,6 @@ func TestEvaluateRunContext(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
table := table
|
||||
t.Run(table.in, func(t *testing.T) {
|
||||
assertObject := assert.New(t)
|
||||
out, err := ee.evaluate(context.Background(), table.in, exprparser.DefaultStatusCheckNone)
|
||||
@@ -163,7 +164,7 @@ func TestEvaluateStep(t *testing.T) {
|
||||
|
||||
tables := []struct {
|
||||
in string
|
||||
out interface{}
|
||||
out any
|
||||
errMesg string
|
||||
}{
|
||||
{"steps.idwithnothing.conclusion", model.StepStatusSuccess.String(), ""},
|
||||
@@ -178,7 +179,6 @@ func TestEvaluateStep(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
table := table
|
||||
t.Run(table.in, func(t *testing.T) {
|
||||
assertObject := assert.New(t)
|
||||
out, err := ee.evaluate(context.Background(), table.in, exprparser.DefaultStatusCheckNone)
|
||||
@@ -262,7 +262,6 @@ func TestInterpolate(t *testing.T) {
|
||||
|
||||
updateTestExpressionWorkflow(t, tables, rc)
|
||||
for _, table := range tables {
|
||||
table := table
|
||||
t.Run("interpolate", func(t *testing.T) {
|
||||
assertObject := assert.New(t)
|
||||
out := ee.Interpolate(context.Background(), table.in)
|
||||
@@ -274,19 +273,21 @@ func TestInterpolate(t *testing.T) {
|
||||
func updateTestExpressionWorkflow(t *testing.T, tables []struct {
|
||||
in string
|
||||
out string
|
||||
}, rc *RunContext) {
|
||||
var envs string
|
||||
}, rc *RunContext,
|
||||
) {
|
||||
var envs strings.Builder
|
||||
keys := make([]string, 0, len(rc.Env))
|
||||
for k := range rc.Env {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k])
|
||||
envs.WriteString(fmt.Sprintf(" %s: %s\n", k, rc.Env[k]))
|
||||
}
|
||||
|
||||
// editorconfig-checker-disable
|
||||
workflow := fmt.Sprintf(`
|
||||
var workflow strings.Builder
|
||||
workflow.WriteString(fmt.Sprintf(`
|
||||
name: "Test how expressions are handled on GitHub"
|
||||
on: push
|
||||
|
||||
@@ -297,7 +298,7 @@ jobs:
|
||||
test-espressions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
`, envs)
|
||||
`, envs.String()))
|
||||
// editorconfig-checker-enable
|
||||
for _, table := range tables {
|
||||
expressionPattern := regexp.MustCompile(`\${{\s*(.+?)\s*}}`)
|
||||
@@ -307,7 +308,7 @@ jobs:
|
||||
})
|
||||
name := fmt.Sprintf(`%s -> %s should be equal to %s`, expr, table.in, table.out)
|
||||
echo := `run: echo "Done "`
|
||||
workflow += fmt.Sprintf("\n - name: %s\n %s\n", name, echo)
|
||||
workflow.WriteString(fmt.Sprintf("\n - name: %s\n %s\n", name, echo))
|
||||
}
|
||||
|
||||
file, err := os.Create("../../.github/workflows/test-expressions.yml")
|
||||
@@ -315,7 +316,7 @@ jobs:
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = file.WriteString(workflow)
|
||||
_, err = file.WriteString(workflow.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package runner
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type jobInfo interface {
|
||||
matrix() map[string]interface{}
|
||||
matrix() map[string]any
|
||||
steps() []*model.Step
|
||||
startContainer() common.Executor
|
||||
stopContainer() common.Executor
|
||||
@@ -19,7 +20,7 @@ type jobInfo interface {
|
||||
result(result string)
|
||||
}
|
||||
|
||||
//nolint:contextcheck,gocyclo
|
||||
//nolint:contextcheck,gocyclo // composes many step executors
|
||||
func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executor {
|
||||
steps := make([]common.Executor, 0)
|
||||
preSteps := make([]common.Executor, 0)
|
||||
@@ -54,19 +55,17 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
|
||||
})
|
||||
|
||||
for i, stepModel := range infoSteps {
|
||||
stepModel := stepModel
|
||||
if stepModel == nil {
|
||||
return func(ctx context.Context) error {
|
||||
return fmt.Errorf("invalid Step %v: missing run or uses key", i)
|
||||
}
|
||||
}
|
||||
if stepModel.ID == "" {
|
||||
stepModel.ID = fmt.Sprintf("%d", i)
|
||||
stepModel.ID = strconv.Itoa(i)
|
||||
}
|
||||
stepModel.Number = i
|
||||
|
||||
step, err := sf.newStep(stepModel, rc)
|
||||
|
||||
if err != nil {
|
||||
return common.NewErrorExecutor(err)
|
||||
}
|
||||
@@ -154,7 +153,7 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
|
||||
pipeline = append(pipeline, steps...)
|
||||
|
||||
return common.NewPipelineExecutor(info.startContainer(), common.NewPipelineExecutor(pipeline...).
|
||||
Finally(func(ctx context.Context) error { //nolint:contextcheck
|
||||
Finally(func(ctx context.Context) error { //nolint:contextcheck // intentionally detaches from canceled parent
|
||||
var cancel context.CancelFunc
|
||||
if ctx.Err() == context.Canceled {
|
||||
// in case of an aborted run, we still should execute the
|
||||
|
||||
@@ -2,13 +2,16 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -38,9 +41,9 @@ type jobInfoMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (jim *jobInfoMock) matrix() map[string]interface{} {
|
||||
func (jim *jobInfoMock) matrix() map[string]any {
|
||||
args := jim.Called()
|
||||
return args.Get(0).(map[string]interface{})
|
||||
return args.Get(0).(map[string]any)
|
||||
}
|
||||
|
||||
func (jim *jobInfoMock) steps() []*model.Step {
|
||||
@@ -232,12 +235,7 @@ func TestNewJobExecutor(t *testing.T) {
|
||||
}
|
||||
|
||||
contains := func(needle string, haystack []string) bool {
|
||||
for _, item := range haystack {
|
||||
if item == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(haystack, needle)
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
@@ -272,9 +270,6 @@ func TestNewJobExecutor(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, stepModel := range tt.steps {
|
||||
i := i
|
||||
stepModel := stepModel
|
||||
|
||||
sm := &stepMock{}
|
||||
|
||||
sfm.On("newStep", stepModel, rc).Return(sm, nil)
|
||||
@@ -289,7 +284,7 @@ func TestNewJobExecutor(t *testing.T) {
|
||||
sm.On("main").Return(func(ctx context.Context) error {
|
||||
executorOrder = append(executorOrder, "step"+stepModel.ID)
|
||||
if tt.hasError {
|
||||
return fmt.Errorf("error")
|
||||
return errors.New("error")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -305,7 +300,7 @@ func TestNewJobExecutor(t *testing.T) {
|
||||
}
|
||||
|
||||
if len(tt.steps) > 0 {
|
||||
jim.On("matrix").Return(map[string]interface{}{})
|
||||
jim.On("matrix").Return(map[string]any{})
|
||||
|
||||
jim.On("interpolateOutputs").Return(func(ctx context.Context) error {
|
||||
executorOrder = append(executorOrder, "interpolateOutputs")
|
||||
|
||||
@@ -26,9 +26,11 @@ const (
|
||||
gray = 37
|
||||
)
|
||||
|
||||
var colors []int
|
||||
var nextColor int
|
||||
var mux sync.Mutex
|
||||
var (
|
||||
colors []int
|
||||
nextColor int
|
||||
mux sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
nextColor = 0
|
||||
@@ -70,7 +72,7 @@ func WithJobLoggerFactory(ctx context.Context, factory JobLoggerFactory) context
|
||||
}
|
||||
|
||||
// WithJobLogger attaches a new logger to context that is aware of steps
|
||||
func WithJobLogger(ctx context.Context, jobID string, jobName string, config *Config, masks *[]string, matrix map[string]interface{}) context.Context {
|
||||
func WithJobLogger(ctx context.Context, jobID, jobName string, config *Config, masks *[]string, matrix map[string]any) context.Context {
|
||||
ctx = WithMasks(ctx, masks)
|
||||
|
||||
var logger *logrus.Logger
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
@@ -38,7 +39,7 @@ func TestMaxParallelStrategy(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
matrix := map[string][]interface{}{
|
||||
matrix := map[string][]any{
|
||||
"version": {1, 2, 3, 4, 5},
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archive, err := rc.Config.ActionCache.GetTarArchive(ctx, filename, sha, fmt.Sprintf(".github/workflows/%s", remoteReusableWorkflow.Filename))
|
||||
archive, err := rc.Config.ActionCache.GetTarArchive(ctx, filename, sha, ".github/workflows/"+remoteReusableWorkflow.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -121,9 +121,7 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
executorLock sync.Mutex
|
||||
)
|
||||
var executorLock sync.Mutex
|
||||
|
||||
func newMutexExecutor(executor common.Executor) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
@@ -160,7 +158,7 @@ func cloneIfRequired(rc *RunContext, remoteReusableWorkflow remoteReusableWorkfl
|
||||
)
|
||||
}
|
||||
|
||||
func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string) common.Executor {
|
||||
func newReusableWorkflowExecutor(rc *RunContext, directory, workflow string) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), true)
|
||||
if err != nil {
|
||||
@@ -260,24 +258,6 @@ func newRemoteReusableWorkflowFromAbsoluteURL(uses string) *remoteReusableWorkfl
|
||||
}
|
||||
}
|
||||
|
||||
// deprecated: use newRemoteReusableWorkflowWithPlat
|
||||
func newRemoteReusableWorkflow(uses string) *remoteReusableWorkflow {
|
||||
// GitHub docs:
|
||||
// https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_iduses
|
||||
r := regexp.MustCompile(`^([^/]+)/([^/]+)/.github/workflows/([^@]+)@(.*)$`)
|
||||
matches := r.FindStringSubmatch(uses)
|
||||
if len(matches) != 5 {
|
||||
return nil
|
||||
}
|
||||
return &remoteReusableWorkflow{
|
||||
Org: matches[1],
|
||||
Repo: matches[2],
|
||||
Filename: matches[3],
|
||||
Ref: matches[4],
|
||||
URL: "https://github.com",
|
||||
}
|
||||
}
|
||||
|
||||
// For Gitea
|
||||
func setReusedWorkflowCallerResult(rc *RunContext, runner Runner) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
maps0 "maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -18,20 +19,20 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/exprparser"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
)
|
||||
|
||||
// RunContext contains info about current job
|
||||
type RunContext struct {
|
||||
Name string
|
||||
Config *Config
|
||||
Matrix map[string]interface{}
|
||||
Matrix map[string]any
|
||||
Run *model.Run
|
||||
EventJSON string
|
||||
Env map[string]string
|
||||
@@ -100,19 +101,6 @@ func (rc *RunContext) jobContainerName() string {
|
||||
return createSimpleContainerName(nameParts...) // For Gitea
|
||||
}
|
||||
|
||||
// Deprecated: use `networkNameForGitea`
|
||||
// networkName return the name of the network which will be created by `act` automatically for job,
|
||||
// only create network if using a service container
|
||||
func (rc *RunContext) networkName() (string, bool) {
|
||||
if len(rc.Run.Job().Services) > 0 {
|
||||
return fmt.Sprintf("%s-%s-network", rc.jobContainerName(), rc.Run.JobID), true
|
||||
}
|
||||
if rc.Config.ContainerNetworkMode == "" {
|
||||
return "host", false
|
||||
}
|
||||
return string(rc.Config.ContainerNetworkMode), false
|
||||
}
|
||||
|
||||
// networkNameForGitea return the name of the network
|
||||
func (rc *RunContext) networkNameForGitea() (string, bool) {
|
||||
if rc.Config.ContainerNetworkMode != "" {
|
||||
@@ -122,13 +110,13 @@ func (rc *RunContext) networkNameForGitea() (string, bool) {
|
||||
}
|
||||
|
||||
func getDockerDaemonSocketMountPath(daemonPath string) string {
|
||||
if protoIndex := strings.Index(daemonPath, "://"); protoIndex != -1 {
|
||||
scheme := daemonPath[:protoIndex]
|
||||
if before, after, ok := strings.Cut(daemonPath, "://"); ok {
|
||||
scheme := before
|
||||
if strings.EqualFold(scheme, "npipe") {
|
||||
// linux container mount on windows, use the default socket path of the VM / wsl2
|
||||
return "/var/run/docker.sock"
|
||||
} else if strings.EqualFold(scheme, "unix") {
|
||||
return daemonPath[protoIndex+3:]
|
||||
return after
|
||||
} else if strings.IndexFunc(scheme, func(r rune) bool {
|
||||
return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z')
|
||||
}) == -1 {
|
||||
@@ -242,7 +230,7 @@ func (rc *RunContext) startHostEnvironment() common.Executor {
|
||||
rc.cleanUpJobContainer = rc.JobContainer.Remove()
|
||||
for k, v := range rc.JobContainer.GetRunnerContext(ctx) {
|
||||
if v, ok := v.(string); ok {
|
||||
rc.Env[fmt.Sprintf("RUNNER_%s", strings.ToUpper(k))] = v
|
||||
rc.Env["RUNNER_"+strings.ToUpper(k)] = v
|
||||
}
|
||||
}
|
||||
for _, env := range os.Environ() {
|
||||
@@ -268,7 +256,7 @@ func (rc *RunContext) startHostEnvironment() common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (rc *RunContext) startJobContainer() common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
logger := common.Logger(ctx)
|
||||
@@ -404,15 +392,8 @@ func (rc *RunContext) startJobContainer() common.Executor {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobContainerNetwork := rc.Config.ContainerNetworkMode.NetworkName()
|
||||
if rc.containerImage(ctx) != "" {
|
||||
jobContainerNetwork = networkName
|
||||
} else if jobContainerNetwork == "" {
|
||||
jobContainerNetwork = "host"
|
||||
}
|
||||
|
||||
// For Gitea, `jobContainerNetwork` should be the same as `networkName`
|
||||
jobContainerNetwork = networkName
|
||||
jobContainerNetwork := networkName
|
||||
|
||||
rc.JobContainer = container.NewContainer(&container.NewContainerInput{
|
||||
Cmd: nil,
|
||||
@@ -468,7 +449,7 @@ func (rc *RunContext) execJobContainer(cmd []string, env map[string]string, user
|
||||
}
|
||||
|
||||
func (rc *RunContext) ApplyExtraPath(ctx context.Context, env *map[string]string) {
|
||||
if rc.ExtraPath != nil && len(rc.ExtraPath) > 0 {
|
||||
if len(rc.ExtraPath) > 0 {
|
||||
path := rc.JobContainer.GetPathVariableName()
|
||||
if rc.JobContainer.IsEnvironmentCaseInsensitive() {
|
||||
// On windows system Path and PATH could also be in the map
|
||||
@@ -627,7 +608,7 @@ func (rc *RunContext) closeContainer() common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RunContext) matrix() map[string]interface{} {
|
||||
func (rc *RunContext) matrix() map[string]any {
|
||||
return rc.Matrix
|
||||
}
|
||||
|
||||
@@ -642,7 +623,7 @@ func (rc *RunContext) steps() []*model.Step {
|
||||
// Executor returns a pipeline executor for all the steps in the job
|
||||
func (rc *RunContext) Executor() (common.Executor, error) {
|
||||
var executor common.Executor
|
||||
var jobType, err = rc.Run.Job().Type()
|
||||
jobType, err := rc.Run.Job().Type()
|
||||
|
||||
switch jobType {
|
||||
case model.JobTypeDefault:
|
||||
@@ -779,14 +760,12 @@ func (rc *RunContext) isEnabled(ctx context.Context) (bool, error) {
|
||||
func mergeMaps(maps ...map[string]string) map[string]string {
|
||||
rtnMap := make(map[string]string)
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
rtnMap[k] = v
|
||||
}
|
||||
maps0.Copy(rtnMap, m)
|
||||
}
|
||||
return rtnMap
|
||||
}
|
||||
|
||||
// deprecated: use createSimpleContainerName
|
||||
// Deprecated: use createSimpleContainerName
|
||||
func createContainerName(parts ...string) string {
|
||||
name := strings.Join(parts, "-")
|
||||
pattern := regexp.MustCompile("[^a-zA-Z0-9]")
|
||||
@@ -843,10 +822,11 @@ func (rc *RunContext) getStepsContext() map[string]*model.StepResult {
|
||||
return rc.StepResults
|
||||
}
|
||||
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext {
|
||||
logger := common.Logger(ctx)
|
||||
ghc := &model.GithubContext{
|
||||
Event: make(map[string]interface{}),
|
||||
Event: make(map[string]any),
|
||||
Workflow: rc.Run.Workflow.Name,
|
||||
RunID: rc.Config.Env["GITHUB_RUN_ID"],
|
||||
RunNumber: rc.Config.Env["GITHUB_RUN_NUMBER"],
|
||||
@@ -954,7 +934,7 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
|
||||
ghc.GraphQLURL = "https://api.github.com/graphql"
|
||||
// per GHES
|
||||
if rc.Config.GitHubInstance != "github.com" {
|
||||
ghc.ServerURL = fmt.Sprintf("https://%s", rc.Config.GitHubInstance)
|
||||
ghc.ServerURL = "https://" + rc.Config.GitHubInstance
|
||||
ghc.APIURL = fmt.Sprintf("https://%s/api/v3", rc.Config.GitHubInstance)
|
||||
ghc.GraphQLURL = fmt.Sprintf("https://%s/api/graphql", rc.Config.GitHubInstance)
|
||||
}
|
||||
@@ -1010,7 +990,7 @@ func isLocalCheckout(ghc *model.GithubContext, step *model.Step) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) {
|
||||
func nestedMapLookup(m map[string]any, ks ...string) (rval any) {
|
||||
var ok bool
|
||||
|
||||
if len(ks) == 0 { // degenerate input
|
||||
@@ -1020,7 +1000,7 @@ func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{})
|
||||
return nil
|
||||
} else if len(ks) == 1 { // we've reached the final key
|
||||
return rval
|
||||
} else if m, ok = rval.(map[string]interface{}); !ok {
|
||||
} else if m, ok = rval.(map[string]any); !ok {
|
||||
return nil
|
||||
} else { // 1+ more keys
|
||||
return nestedMapLookup(m, ks[1:]...)
|
||||
@@ -1113,22 +1093,22 @@ func (rc *RunContext) handleCredentials(ctx context.Context) (string, string, er
|
||||
}
|
||||
|
||||
if container.Credentials != nil && len(container.Credentials) != 2 {
|
||||
err := fmt.Errorf("invalid property count for key 'credentials:'")
|
||||
err := errors.New("invalid property count for key 'credentials:'")
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
ee := rc.NewExpressionEvaluator(ctx)
|
||||
if username = ee.Interpolate(ctx, container.Credentials["username"]); username == "" {
|
||||
err := fmt.Errorf("failed to interpolate container.credentials.username")
|
||||
err := errors.New("failed to interpolate container.credentials.username")
|
||||
return "", "", err
|
||||
}
|
||||
if password = ee.Interpolate(ctx, container.Credentials["password"]); password == "" {
|
||||
err := fmt.Errorf("failed to interpolate container.credentials.password")
|
||||
err := errors.New("failed to interpolate container.credentials.password")
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if container.Credentials["username"] == "" || container.Credentials["password"] == "" {
|
||||
err := fmt.Errorf("container.credentials cannot be empty")
|
||||
err := errors.New("container.credentials cannot be empty")
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
@@ -1137,25 +1117,25 @@ func (rc *RunContext) handleCredentials(ctx context.Context) (string, string, er
|
||||
|
||||
func (rc *RunContext) handleServiceCredentials(ctx context.Context, creds map[string]string) (username, password string, err error) {
|
||||
if creds == nil {
|
||||
return
|
||||
return username, password, err
|
||||
}
|
||||
if len(creds) != 2 {
|
||||
err = fmt.Errorf("invalid property count for key 'credentials:'")
|
||||
return
|
||||
err = errors.New("invalid property count for key 'credentials:'")
|
||||
return username, password, err
|
||||
}
|
||||
|
||||
ee := rc.NewExpressionEvaluator(ctx)
|
||||
if username = ee.Interpolate(ctx, creds["username"]); username == "" {
|
||||
err = fmt.Errorf("failed to interpolate credentials.username")
|
||||
return
|
||||
err = errors.New("failed to interpolate credentials.username")
|
||||
return username, password, err
|
||||
}
|
||||
|
||||
if password = ee.Interpolate(ctx, creds["password"]); password == "" {
|
||||
err = fmt.Errorf("failed to interpolate credentials.password")
|
||||
return
|
||||
err = errors.New("failed to interpolate credentials.password")
|
||||
return username, password, err
|
||||
}
|
||||
|
||||
return
|
||||
return username, password, err
|
||||
}
|
||||
|
||||
// GetServiceBindsAndMounts returns the binds and mounts for the service container, resolving paths as appopriate
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
func TestRunContext_EvalBool(t *testing.T) {
|
||||
var yml yaml.Node
|
||||
err := yml.Encode(map[string][]interface{}{
|
||||
err := yml.Encode(map[string][]any{
|
||||
"os": {"Linux", "Windows"},
|
||||
"foo": {"bar", "baz"},
|
||||
})
|
||||
@@ -48,7 +48,7 @@ func TestRunContext_EvalBool(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Matrix: map[string]interface{}{
|
||||
Matrix: map[string]any{
|
||||
"os": "Linux",
|
||||
"foo": "bar",
|
||||
},
|
||||
@@ -154,7 +154,6 @@ func TestRunContext_EvalBool(t *testing.T) {
|
||||
|
||||
updateTestIfWorkflow(t, tables, rc)
|
||||
for _, table := range tables {
|
||||
table := table
|
||||
t.Run(table.in, func(t *testing.T) {
|
||||
assertObject := assert.New(t)
|
||||
b, err := EvalBool(context.Background(), rc.ExprEval, table.in, exprparser.DefaultStatusCheckSuccess)
|
||||
@@ -171,18 +170,20 @@ func updateTestIfWorkflow(t *testing.T, tables []struct {
|
||||
in string
|
||||
out bool
|
||||
wantErr bool
|
||||
}, rc *RunContext) {
|
||||
var envs string
|
||||
}, rc *RunContext,
|
||||
) {
|
||||
var envs strings.Builder
|
||||
keys := make([]string, 0, len(rc.Env))
|
||||
for k := range rc.Env {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k])
|
||||
envs.WriteString(fmt.Sprintf(" %s: %s\n", k, rc.Env[k]))
|
||||
}
|
||||
// editorconfig-checker-disable
|
||||
workflow := fmt.Sprintf(`
|
||||
var workflow strings.Builder
|
||||
workflow.WriteString(fmt.Sprintf(`
|
||||
name: "Test what expressions result in true and false on GitHub"
|
||||
on: push
|
||||
|
||||
@@ -193,7 +194,7 @@ jobs:
|
||||
test-ifs-and-buts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
`, envs)
|
||||
`, envs.String()))
|
||||
// editorconfig-checker-enable
|
||||
|
||||
for i, table := range tables {
|
||||
@@ -211,9 +212,9 @@ jobs:
|
||||
echo = `run: echo OK`
|
||||
name = fmt.Sprintf(`"✅ I should run, expr: %s"`, expr)
|
||||
}
|
||||
workflow += fmt.Sprintf("\n - name: %s\n id: step%d\n if: %s\n %s\n", name, i, table.in, echo)
|
||||
workflow.WriteString(fmt.Sprintf("\n - name: %s\n id: step%d\n if: %s\n %s\n", name, i, table.in, echo))
|
||||
if table.out {
|
||||
workflow += fmt.Sprintf("\n - name: \"Double checking expr: %s\"\n if: steps.step%d.conclusion == 'skipped'\n run: echo \"%s should have been true, but wasn't\"\n", expr, i, table.in)
|
||||
workflow.WriteString(fmt.Sprintf("\n - name: \"Double checking expr: %s\"\n if: steps.step%d.conclusion == 'skipped'\n run: echo \"%s should have been true, but wasn't\"\n", expr, i, table.in))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ jobs:
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = file.WriteString(workflow)
|
||||
_, err = file.WriteString(workflow.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -259,10 +260,8 @@ func TestRunContext_GetBindsAndMounts(t *testing.T) {
|
||||
|
||||
for _, testcase := range tests {
|
||||
// pin for scopelint
|
||||
testcase := testcase
|
||||
for _, bindWorkDir := range []bool{true, false} {
|
||||
// pin for scopelint
|
||||
bindWorkDir := bindWorkDir
|
||||
testBindSuffix := ""
|
||||
if bindWorkDir {
|
||||
testBindSuffix = "Bind"
|
||||
@@ -359,7 +358,7 @@ func TestGetGitHubContext(t *testing.T) {
|
||||
},
|
||||
Name: "GitHubContextTest",
|
||||
CurrentStep: "step",
|
||||
Matrix: map[string]interface{}{},
|
||||
Matrix: map[string]any{},
|
||||
Env: map[string]string{},
|
||||
ExtraPath: []string{},
|
||||
StepResults: map[string]*model.StepResult{},
|
||||
@@ -417,7 +416,6 @@ func TestGetGithubContextRef(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, data := range table {
|
||||
data := data
|
||||
t.Run(data.event, func(t *testing.T) {
|
||||
rc := &RunContext{
|
||||
EventJSON: data.json,
|
||||
@@ -461,7 +459,7 @@ func createIfTestRunContext(jobs map[string]*model.Job) *RunContext {
|
||||
return rc
|
||||
}
|
||||
|
||||
func createJob(t *testing.T, input string, result string) *model.Job {
|
||||
func createJob(t *testing.T, input, result string) *model.Job {
|
||||
var job *model.Job
|
||||
err := yaml.Unmarshal([]byte(input), &job)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
docker_container "github.com/docker/docker/api/types/container"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
docker_container "github.com/docker/docker/api/types/container"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Runner provides capabilities to run GitHub actions
|
||||
@@ -133,6 +133,8 @@ func (runner *runnerImpl) configure() (Runner, error) {
|
||||
}
|
||||
|
||||
// NewPlanExecutor ...
|
||||
//
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||
maxJobNameLen := 0
|
||||
|
||||
@@ -182,7 +184,7 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
var matrixes []map[string]interface{}
|
||||
var matrixes []map[string]any
|
||||
if m, err := job.GetMatrixes(); err != nil {
|
||||
log.Errorf("Error while get job's matrix: %v", err)
|
||||
} else {
|
||||
@@ -210,7 +212,6 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||
log.Infof("Running job with maxParallel=%d for %d matrix combinations", maxParallel, len(matrixes))
|
||||
|
||||
for i, matrix := range matrixes {
|
||||
matrix := matrix
|
||||
rc := runner.newRunContext(ctx, run, matrix)
|
||||
rc.JobName = rc.Name
|
||||
if len(matrixes) > 1 {
|
||||
@@ -225,7 +226,6 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||
stageExecutor = append(stageExecutor, func(ctx context.Context) error {
|
||||
jobName := fmt.Sprintf("%-*s", maxJobNameLen, rc.String())
|
||||
executor, err := rc.Executor()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -287,8 +287,8 @@ func handleFailure(plan *model.Plan) common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
func selectMatrixes(originalMatrixes []map[string]interface{}, targetMatrixValues map[string]map[string]bool) []map[string]interface{} {
|
||||
matrixes := make([]map[string]interface{}, 0)
|
||||
func selectMatrixes(originalMatrixes []map[string]any, targetMatrixValues map[string]map[string]bool) []map[string]any {
|
||||
matrixes := make([]map[string]any, 0)
|
||||
for _, original := range originalMatrixes {
|
||||
flag := true
|
||||
for key, val := range original {
|
||||
@@ -306,7 +306,7 @@ func selectMatrixes(originalMatrixes []map[string]interface{}, targetMatrixValue
|
||||
return matrixes
|
||||
}
|
||||
|
||||
func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, matrix map[string]interface{}) *RunContext {
|
||||
func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, matrix map[string]any) *RunContext {
|
||||
rc := &RunContext{
|
||||
Config: runner.config,
|
||||
Run: run,
|
||||
@@ -322,7 +322,7 @@ func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, mat
|
||||
}
|
||||
|
||||
// For Gitea
|
||||
func (c *caller) setReusedWorkflowJobResult(jobName string, result string) {
|
||||
func (c *caller) setReusedWorkflowJobResult(jobName, result string) {
|
||||
c.updateResultLock.Lock()
|
||||
defer c.updateResultLock.Unlock()
|
||||
c.reusedWorkflowJobResults[jobName] = result
|
||||
|
||||
@@ -90,14 +90,12 @@ func TestMaxParallelConcurrencyTracking(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
semaphore := make(chan struct{}, 2) // Limit to 2 concurrent
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range 6 {
|
||||
wg.Go(func() {
|
||||
semaphore <- struct{}{} // Acquire
|
||||
defer func() { <-semaphore }() // Release
|
||||
trackingFunc()
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -528,9 +528,9 @@ func (f *maskJobLoggerFactory) WithJobLogger() *log.Logger {
|
||||
}
|
||||
|
||||
func TestMaskValues(t *testing.T) {
|
||||
assertNoSecret := func(text string, secret string) {
|
||||
index := strings.Index(text, "composite secret")
|
||||
if index > -1 {
|
||||
assertNoSecret := func(text, secret string) {
|
||||
found := strings.Contains(text, "composite secret")
|
||||
if found {
|
||||
fmt.Printf("\nFound Secret in the given text:\n%s\n", text)
|
||||
}
|
||||
assert.False(t, strings.Contains(text, "composite secret"))
|
||||
@@ -607,7 +607,7 @@ func TestRunWithService(t *testing.T) {
|
||||
runner, err := New(runnerConfig)
|
||||
assert.NoError(t, err, workflowPath)
|
||||
|
||||
planner, err := model.NewWorkflowPlanner(fmt.Sprintf("testdata/%s", workflowPath), true)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/"+workflowPath, true)
|
||||
assert.NoError(t, err, workflowPath)
|
||||
|
||||
plan, err := planner.PlanEvent(eventName)
|
||||
|
||||
@@ -3,6 +3,7 @@ package runner
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
maps0 "maps"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -142,7 +143,7 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo
|
||||
Mode: 0o666,
|
||||
}, &container.FileEntry{
|
||||
Name: envFileCommand,
|
||||
Mode: 0666,
|
||||
Mode: 0o666,
|
||||
}, &container.FileEntry{
|
||||
Name: summaryFileCommand,
|
||||
Mode: 0o666,
|
||||
@@ -295,9 +296,7 @@ func mergeIntoMap(step step, target *map[string]string, maps ...map[string]strin
|
||||
|
||||
func mergeIntoMapCaseSensitive(target map[string]string, maps ...map[string]string) {
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
target[k] = v
|
||||
}
|
||||
maps0.Copy(target, m)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func (sal *stepActionLocal) main() common.Executor {
|
||||
_, cpath := getContainerActionPaths(sal.Step, path.Join(actionDir, ""), sal.RunContext)
|
||||
return func(filename string) (io.Reader, io.Closer, error) {
|
||||
spath := path.Join(cpath, filename)
|
||||
for i := 0; i < maxSymlinkDepth; i++ {
|
||||
for range maxSymlinkDepth {
|
||||
tars, err := sal.RunContext.JobContainer.GetContainerArchive(ctx, spath)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.yaml.in/yaml/v4"
|
||||
@@ -24,7 +25,7 @@ func (salm *stepActionLocalMocks) runAction(step actionStep, actionDir string, r
|
||||
return args.Get(0).(func(context.Context) error)
|
||||
}
|
||||
|
||||
func (salm *stepActionLocalMocks) readAction(_ context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
func (salm *stepActionLocalMocks) readAction(_ context.Context, step *model.Step, actionDir, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
args := salm.Called(step, actionDir, actionPath, readFile, writeFile)
|
||||
return args.Get(0).(*model.Action), args.Error(1)
|
||||
}
|
||||
@@ -257,7 +258,7 @@ func TestStepActionLocalPost(t *testing.T) {
|
||||
sal.RunContext.ExprEval = sal.RunContext.NewExpressionEvaluator(ctx)
|
||||
|
||||
if tt.mocks.exec {
|
||||
suffixMatcher := func(suffix string) interface{} {
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(array []string) bool {
|
||||
return strings.HasSuffix(array[1], suffix)
|
||||
})
|
||||
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/common/git"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
)
|
||||
|
||||
type stepActionRemote struct {
|
||||
@@ -35,6 +35,7 @@ type stepActionRemote struct {
|
||||
|
||||
var stepActionRemoteNewCloneExecutor = git.NewGitCloneExecutor
|
||||
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
if sar.remoteAction != nil && sar.action != nil {
|
||||
@@ -80,7 +81,7 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
||||
remoteReader := func(ctx context.Context) actionYamlReader {
|
||||
return func(filename string) (io.Reader, io.Closer, error) {
|
||||
spath := path.Join(sar.remoteAction.Path, filename)
|
||||
for i := 0; i < maxSymlinkDepth; i++ {
|
||||
for range maxSymlinkDepth {
|
||||
tars, err := cache.GetTarArchive(ctx, sar.cacheDir, sar.resolvedSha, spath)
|
||||
if err != nil {
|
||||
return nil, nil, os.ErrNotExist
|
||||
@@ -301,8 +302,8 @@ func (ra *remoteAction) IsCheckout() bool {
|
||||
func newRemoteAction(action string) *remoteAction {
|
||||
// support http(s)://host/owner/repo@v3
|
||||
for _, schema := range []string{"https://", "http://"} {
|
||||
if strings.HasPrefix(action, schema) {
|
||||
splits := strings.SplitN(strings.TrimPrefix(action, schema), "/", 2)
|
||||
if after, ok := strings.CutPrefix(action, schema); ok {
|
||||
splits := strings.SplitN(after, "/", 2)
|
||||
if len(splits) != 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,20 +10,20 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/common/git"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
type stepActionRemoteMocks struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (sarm *stepActionRemoteMocks) readAction(_ context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
func (sarm *stepActionRemoteMocks) readAction(_ context.Context, step *model.Step, actionDir, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
args := sarm.Called(step, actionDir, actionPath, readFile, writeFile)
|
||||
return args.Get(0).(*model.Action), args.Error(1)
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func TestStepActionRemote(t *testing.T) {
|
||||
}
|
||||
sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx)
|
||||
|
||||
suffixMatcher := func(suffix string) interface{} {
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(actionDir string) bool {
|
||||
return strings.HasSuffix(actionDir, suffix)
|
||||
})
|
||||
@@ -258,7 +258,7 @@ func TestStepActionRemotePre(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
suffixMatcher := func(suffix string) interface{} {
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(actionDir string) bool {
|
||||
return strings.HasSuffix(actionDir, suffix)
|
||||
})
|
||||
@@ -329,7 +329,7 @@ func TestStepActionRemotePreThroughAction(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
suffixMatcher := func(suffix string) interface{} {
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(actionDir string) bool {
|
||||
return strings.HasSuffix(actionDir, suffix)
|
||||
})
|
||||
@@ -405,7 +405,7 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
suffixMatcher := func(suffix string) interface{} {
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(actionDir string) bool {
|
||||
return strings.HasSuffix(actionDir, suffix)
|
||||
})
|
||||
|
||||
@@ -5,10 +5,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
type stepDocker struct {
|
||||
@@ -85,11 +86,9 @@ func (sd *stepDocker) runUsesContainer() common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ContainerNewContainer = container.NewContainer
|
||||
)
|
||||
var ContainerNewContainer = container.NewContainer
|
||||
|
||||
func (sd *stepDocker) newStepContainer(ctx context.Context, image string, cmd []string, entrypoint []string) container.Container {
|
||||
func (sd *stepDocker) newStepContainer(ctx context.Context, image string, cmd, entrypoint []string) container.Container {
|
||||
rc := sd.RunContext
|
||||
step := sd.Step
|
||||
|
||||
@@ -123,7 +122,7 @@ func (sd *stepDocker) newStepContainer(ctx context.Context, image string, cmd []
|
||||
Name: createSimpleContainerName(rc.jobContainerName(), "STEP-"+step.ID),
|
||||
Env: envList,
|
||||
Mounts: mounts,
|
||||
NetworkMode: fmt.Sprintf("container:%s", rc.jobContainerName()),
|
||||
NetworkMode: "container:" + rc.jobContainerName(),
|
||||
Binds: binds,
|
||||
Stdout: logWriter,
|
||||
Stderr: logWriter,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,15 +3,16 @@ package runner
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/lookpath"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
type stepRun struct {
|
||||
@@ -90,7 +91,7 @@ func getScriptName(rc *RunContext, step *model.Step) string {
|
||||
for rcs := rc; rcs.Parent != nil; rcs = rcs.Parent {
|
||||
scriptName = fmt.Sprintf("%s-composite-%s", rcs.Parent.CurrentStep, scriptName)
|
||||
}
|
||||
return fmt.Sprintf("workflow/%s", scriptName)
|
||||
return "workflow/" + scriptName
|
||||
}
|
||||
|
||||
// TODO: Currently we just ignore top level keys, BUT we should return proper error on them
|
||||
@@ -184,9 +185,7 @@ func (sr *stepRun) setupShell(ctx context.Context) {
|
||||
}
|
||||
step.Shell = shellWithFallback[0]
|
||||
lenv := &localEnv{env: map[string]string{}}
|
||||
for k, v := range sr.env {
|
||||
lenv.env[k] = v
|
||||
}
|
||||
maps.Copy(lenv.env, sr.env)
|
||||
sr.getRunContext().ApplyExtraPath(ctx, &lenv.env)
|
||||
_, err := lookpath.LookPath2(shellWithFallback[0], lenv)
|
||||
if err != nil {
|
||||
@@ -202,7 +201,7 @@ func (sr *stepRun) setupShell(ctx context.Context) {
|
||||
func (sr *stepRun) setupWorkingDirectory(ctx context.Context) {
|
||||
rc := sr.RunContext
|
||||
step := sr.Step
|
||||
workingdirectory := ""
|
||||
var workingdirectory string
|
||||
|
||||
if step.WorkingDirectory == "" {
|
||||
workingdirectory = rc.Run.Job().Defaults.Run.WorkingDirectory
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestStepRun(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
@@ -3,16 +3,16 @@ package workflowpattern
|
||||
import "fmt"
|
||||
|
||||
type TraceWriter interface {
|
||||
Info(string, ...interface{})
|
||||
Info(string, ...any)
|
||||
}
|
||||
|
||||
type EmptyTraceWriter struct{}
|
||||
|
||||
func (*EmptyTraceWriter) Info(string, ...interface{}) {
|
||||
func (*EmptyTraceWriter) Info(string, ...any) {
|
||||
}
|
||||
|
||||
type StdOutTraceWriter struct{}
|
||||
|
||||
func (*StdOutTraceWriter) Info(format string, args ...interface{}) {
|
||||
func (*StdOutTraceWriter) Info(format string, args ...any) {
|
||||
fmt.Printf(format+"\n", args...)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func CompilePattern(rawpattern string) (*WorkflowPattern, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo // function handles many cases
|
||||
func PatternToRegex(pattern string) (string, error) {
|
||||
var rpattern strings.Builder
|
||||
rpattern.WriteString("^")
|
||||
@@ -127,7 +127,7 @@ func PatternToRegex(pattern string) (string, error) {
|
||||
if errorMessage.Len() > 0 {
|
||||
errorMessage.WriteString(", ")
|
||||
}
|
||||
errorMessage.WriteString(fmt.Sprintf("Position: %d Error: %s", position, err))
|
||||
fmt.Fprintf(&errorMessage, "Position: %d Error: %s", position, err)
|
||||
}
|
||||
return "", fmt.Errorf("invalid Pattern '%s': %s", pattern, errorMessage.String())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user