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 <[email protected]>
Co-authored-by: silverwind <[email protected]>
Co-committed-by: silverwind <[email protected]>
This commit is contained in:
silverwind authored and silverwind committed 2026-04-18 09:10:09 +00:00
1 parent 3232358e71
commit f923badec7
89 files changed
+821 -791

No files matched your search

+2 -4
View File
@@ -22,10 +22,8 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- uses: golangci/[email protected] - name: lint
with: run: make lint-go
version: v1.53
only-new-issues: true
- uses: megalinter/megalinter/flavors/[email protected] - uses: megalinter/megalinter/flavors/[email protected]
env: env:
DEFAULT_BRANCH: master DEFAULT_BRANCH: master
+139 -44
View File
@@ -1,51 +1,146 @@
# Minimum golangci-lint version required: v1.46.0 version: "2"
run: output:
timeout: 3m sort-order:
- file
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
linters: linters:
default: none
enable: enable:
- megacheck - bidichk
- govet - bodyclose
- revive - contextcheck
- depguard
- dupl
- errcheck
- gocheckcompilerdirectives
- gocritic
- gocyclo - gocyclo
- gosec - gosec
- unconvert - govet
- dupl
- nakedret
- prealloc
- exportloopref
- gocritic
- goimports
- whitespace
- misspell
- depguard
- importas - importas
- contextcheck - ineffassign
- misspell
- mirror
- modernize
- nakedret
- nolintlint - nolintlint
- perfsprint
- revive - 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
+14 -4
View File
@@ -13,6 +13,8 @@ endif
ACT ?= go run main.go ACT ?= go run main.go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/[email protected]
HAS_TOKEN = $(if $(test -e ~/.config/github/token),true,false) HAS_TOKEN = $(if $(test -e ~/.config/github/token),true,false)
ifeq (true,$(HAS_TOKEN)) ifeq (true,$(HAS_TOKEN))
export GITHUB_TOKEN := $(shell cat ~/.config/github/token) export GITHUB_TOKEN := $(shell cat ~/.config/github/token)
@@ -27,11 +29,19 @@ build:
.PHONY: format .PHONY: format
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 .PHONY: format-all
format-all: format-all: format
go fmt ./...
npx prettier --write . npx prettier --write .
.PHONY: test .PHONY: test
@@ -41,7 +51,7 @@ test:
.PHONY: lint-go .PHONY: lint-go
lint-go: lint-go:
golangci-lint run $(FIX) go run $(GOLANGCI_LINT_PACKAGE) run $(FIX)
.PHONY: lint-js .PHONY: lint-js
lint-js: lint-js:
+2 -2
View File
@@ -76,7 +76,7 @@ func getVersionNotices(version string) []Notice {
noticeURL.RawQuery = query.Encode() noticeURL.RawQuery = query.Encode()
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest("GET", noticeURL.String(), nil) req, err := http.NewRequest(http.MethodGet, noticeURL.String(), nil)
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
return nil return nil
@@ -102,7 +102,7 @@ func getVersionNotices(version string) []Notice {
defer resp.Body.Close() defer resp.Body.Close()
notices := []Notice{} notices := []Notice{}
if resp.StatusCode == 304 { if resp.StatusCode == http.StatusNotModified {
log.Debug("No new notices") log.Debug("No new notices")
return nil return nil
} }
+14 -14
View File
@@ -4,13 +4,22 @@ import (
"bufio" "bufio"
"context" "context"
"fmt" "fmt"
"maps"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "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/AlecAivazis/survey/v2"
"github.com/adrg/xdg" "github.com/adrg/xdg"
"github.com/andreaskoch/go-fswatch" "github.com/andreaskoch/go-fswatch"
@@ -20,13 +29,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.yaml.in/yaml/v4" "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 // 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("act version:", version)
report += sprintf("GOOS:", runtime.GOOS) report += sprintf("GOOS:", runtime.GOOS)
report += sprintf("GOARCH:", runtime.GOARCH) report += sprintf("GOARCH:", runtime.GOARCH)
report += sprintf("NumCPU:", fmt.Sprint(runtime.NumCPU())) report += sprintf("NumCPU:", strconv.Itoa(runtime.NumCPU()))
var dockerHost string var dockerHost string
var exists bool var exists bool
@@ -216,7 +218,7 @@ func bugReport(ctx context.Context, version string) error {
report += sprintf("\tOS version:", info.OSVersion) report += sprintf("\tOS version:", info.OSVersion)
report += sprintf("\tOS arch:", info.Architecture) report += sprintf("\tOS arch:", info.Architecture)
report += sprintf("\tOS kernel:", info.KernelVersion) 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 += sprintf("\tOS memory:", fmt.Sprintf("%d MB", info.MemTotal/1024/1024))
report += fmt.Sprintln("\tSecurity options:") report += fmt.Sprintln("\tSecurity options:")
@@ -305,9 +307,7 @@ func readEnvs(path string, envs map[string]string) bool {
if err != nil { if err != nil {
log.Fatalf("Error loading from %s: %v", path, err) log.Fatalf("Error loading from %s: %v", path, err)
} }
for k, v := range env { maps.Copy(envs, env)
envs[k] = v
}
return true return true
} }
return false return false
@@ -330,7 +330,7 @@ func parseMatrix(matrix []string) map[string]map[string]bool {
return matrixes return matrixes
} }
//nolint:gocyclo //nolint:gocyclo // function handles many cases
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error { func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error {
if input.jsonLogger { if input.jsonLogger {
@@ -511,7 +511,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
log.Warnf(deprecationWarning, "privileged", "--privileged") log.Warnf(deprecationWarning, "privileged", "--privileged")
} }
if len(input.usernsMode) > 0 { 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 { if len(input.containerCapAdd) > 0 {
log.Warnf(deprecationWarning, "container-cap-add", fmt.Sprintf("--cap-add=%s", input.containerCapAdd)) log.Warnf(deprecationWarning, "container-cap-add", fmt.Sprintf("--cap-add=%s", input.containerCapAdd))
+2 -1
View File
@@ -2,12 +2,13 @@ package main
import ( import (
"context" "context"
_ "embed"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/nektos/act/cmd" "github.com/nektos/act/cmd"
_ "embed"
) )
//go:embed VERSION //go:embed VERSION
+5 -4
View File
@@ -15,12 +15,12 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/nektos/act/pkg/common"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/timshannon/bolthold" "github.com/timshannon/bolthold"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
"github.com/nektos/act/pkg/common"
) )
const ( const (
@@ -74,7 +74,7 @@ func StartHandler(dir, outboundIP string, port uint16, logger logrus.FieldLogger
if outboundIP != "" { if outboundIP != "" {
h.outboundIP = outboundIP h.outboundIP = outboundIP
} else if ip := common.GetOutboundIP(); ip == nil { } 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 { } else {
h.outboundIP = ip.String() h.outboundIP = ip.String()
} }
@@ -363,7 +363,7 @@ func findCache(db *bolthold.Store, keys []string, version string) (*Cache, error
} }
return cache, nil return cache, nil
} }
prefixPattern := fmt.Sprintf("^%s", regexp.QuoteMeta(prefix)) prefixPattern := "^" + regexp.QuoteMeta(prefix)
re, err := regexp.Compile(prefixPattern) re, err := regexp.Compile(prefixPattern)
if err != nil { if err != nil {
continue continue
@@ -415,6 +415,7 @@ const (
keepOld = 5 * time.Minute keepOld = 5 * time.Minute
) )
//nolint:gocyclo // function handles many cases
func (h *Handler) gcCache() { func (h *Handler) gcCache() {
if h.gcing.Load() { if h.gcing.Load() {
return return
+13 -13
View File
@@ -64,7 +64,7 @@ func TestHandler(t *testing.T) {
}) })
t.Run("clean", func(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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) 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) { t.Run("reserve with bad request", func(t *testing.T) {
body := []byte(`invalid json`) body := []byte(`invalid json`)
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 400, resp.StatusCode) assert.Equal(t, 400, resp.StatusCode)
}) })
@@ -90,7 +90,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, 200, resp.StatusCode)
@@ -104,7 +104,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) 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) { t.Run("upload with bad id", func(t *testing.T) {
req, err := http.NewRequest(http.MethodPatch, 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) require.NoError(t, err)
req.Header.Set("Content-Type", "application/octet-stream") req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Content-Range", "bytes 0-99/*") req.Header.Set("Content-Range", "bytes 0-99/*")
@@ -151,7 +151,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, 200, resp.StatusCode)
@@ -202,7 +202,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) 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) { 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) require.NoError(t, err)
assert.Equal(t, 400, resp.StatusCode) assert.Equal(t, 400, resp.StatusCode)
} }
@@ -254,7 +254,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, 200, resp.StatusCode)
@@ -300,7 +300,7 @@ func TestHandler(t *testing.T) {
Size: 100, Size: 100,
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) 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) { 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.NoError(t, err)
require.Equal(t, 400, resp.StatusCode) 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)), Size: int64(len(content)),
}) })
require.NoError(t, err) 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) require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, 200, resp.StatusCode)
@@ -578,7 +578,7 @@ func uploadCacheNormally(t *testing.T, base, key, version string, content []byte
archiveLocation = got.ArchiveLocation archiveLocation = got.ArchiveLocation
} }
{ {
resp, err := http.Get(archiveLocation) //nolint:gosec resp, err := http.Get(archiveLocation)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) require.Equal(t, 200, resp.StatusCode)
got, err := io.ReadAll(resp.Body) got, err := io.ReadAll(resp.Body)
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
) )
type Storage struct { type Storage struct {
@@ -103,11 +104,11 @@ func (s *Storage) Remove(id uint64) {
} }
func (s *Storage) filename(id uint64) string { 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 { 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 { func (s *Storage) tempName(id uint64, offset int64) string {
+5 -8
View File
@@ -13,9 +13,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/julienschmidt/httprouter"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/julienschmidt/httprouter"
) )
type FileContainerResourceURL struct { type FileContainerResourceURL struct {
@@ -55,8 +55,7 @@ type WriteFS interface {
OpenAppendable(name string) (WritableFile, error) OpenAppendable(name string) (WritableFile, error)
} }
type readWriteFSImpl struct { type readWriteFSImpl struct{}
}
func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) { func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) {
return os.Open(name) return os.Open(name)
@@ -74,7 +73,6 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
return nil, err return nil, err
} }
file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o644) file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o644)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -88,7 +86,7 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
var gzipExtension = ".gz__" 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))) 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) return fsys.OpenWritable(safePath)
}() }()
if err != nil { if err != nil {
panic(err) 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) serverContext, cancel := context.WithCancel(ctx)
logger := common.Logger(serverContext) logger := common.Logger(serverContext)
+21 -21
View File
@@ -13,12 +13,12 @@ import (
"testing" "testing"
"testing/fstest" "testing/fstest"
"github.com/nektos/act/pkg/model"
"github.com/nektos/act/pkg/runner"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/nektos/act/pkg/model"
"github.com/nektos/act/pkg/runner"
) )
type writableMapFile struct { type writableMapFile struct {
@@ -39,7 +39,7 @@ type writeMapFS struct {
} }
func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) { func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
var file = &writableMapFile{ file := &writableMapFile{
MapFile: fstest.MapFile{ MapFile: fstest.MapFile{
Data: []byte("content2"), Data: []byte("content2"),
}, },
@@ -50,7 +50,7 @@ func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
} }
func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) { func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
var file = &writableMapFile{ file := &writableMapFile{
MapFile: fstest.MapFile{ MapFile: fstest.MapFile{
Data: []byte("content2"), Data: []byte("content2"),
}, },
@@ -63,12 +63,12 @@ func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
func TestNewArtifactUploadPrepare(t *testing.T) { func TestNewArtifactUploadPrepare(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New() router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs}) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -89,12 +89,12 @@ func TestNewArtifactUploadPrepare(t *testing.T) {
func TestArtifactUploadBlob(t *testing.T) { func TestArtifactUploadBlob(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New() router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs}) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -116,12 +116,12 @@ func TestArtifactUploadBlob(t *testing.T) {
func TestFinalizeArtifactUpload(t *testing.T) { func TestFinalizeArtifactUpload(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New() router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs}) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -142,7 +142,7 @@ func TestFinalizeArtifactUpload(t *testing.T) {
func TestListArtifacts(t *testing.T) { func TestListArtifacts(t *testing.T) {
assert := assert.New(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": { "artifact/server/path/1/file.txt": {
Data: []byte(""), Data: []byte(""),
}, },
@@ -151,7 +151,7 @@ func TestListArtifacts(t *testing.T) {
router := httprouter.New() router := httprouter.New()
downloads(router, "artifact/server/path", memfs) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -174,7 +174,7 @@ func TestListArtifacts(t *testing.T) {
func TestListArtifactContainer(t *testing.T) { func TestListArtifactContainer(t *testing.T) {
assert := assert.New(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": { "artifact/server/path/1/some/file": {
Data: []byte(""), Data: []byte(""),
}, },
@@ -183,7 +183,7 @@ func TestListArtifactContainer(t *testing.T) {
router := httprouter.New() router := httprouter.New()
downloads(router, "artifact/server/path", memfs) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -207,7 +207,7 @@ func TestListArtifactContainer(t *testing.T) {
func TestDownloadArtifactFile(t *testing.T) { func TestDownloadArtifactFile(t *testing.T) {
assert := assert.New(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": { "artifact/server/path/1/some/file": {
Data: []byte("content"), Data: []byte("content"),
}, },
@@ -216,7 +216,7 @@ func TestDownloadArtifactFile(t *testing.T) {
router := httprouter.New() router := httprouter.New()
downloads(router, "artifact/server/path", memfs) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -344,7 +344,7 @@ func TestMkdirFsImplSafeResolve(t *testing.T) {
func TestDownloadArtifactFileUnsafePath(t *testing.T) { func TestDownloadArtifactFileUnsafePath(t *testing.T) {
assert := assert.New(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": { "artifact/server/path/some/file": {
Data: []byte("content"), Data: []byte("content"),
}, },
@@ -353,7 +353,7 @@ func TestDownloadArtifactFileUnsafePath(t *testing.T) {
router := httprouter.New() router := httprouter.New()
downloads(router, "artifact/server/path", memfs) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
@@ -370,12 +370,12 @@ func TestDownloadArtifactFileUnsafePath(t *testing.T) {
func TestArtifactUploadBlobUnsafePath(t *testing.T) { func TestArtifactUploadBlobUnsafePath(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New() router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs}) 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() rr := httptest.NewRecorder()
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)
+7 -7
View File
@@ -1,9 +1,9 @@
package common package common
// CartesianProduct takes map of lists and returns list of unique tuples // 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) listNames := make([]string, 0)
lists := make([][]interface{}, 0) lists := make([][]any, 0)
for k, v := range mapOfLists { for k, v := range mapOfLists {
listNames = append(listNames, k) listNames = append(listNames, k)
lists = append(lists, v) lists = append(lists, v)
@@ -11,9 +11,9 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
listCart := cartN(lists...) listCart := cartN(lists...)
rtn := make([]map[string]interface{}, 0) rtn := make([]map[string]any, 0)
for _, list := range listCart { for _, list := range listCart {
vMap := make(map[string]interface{}) vMap := make(map[string]any)
for i, v := range list { for i, v := range list {
vMap[listNames[i]] = v vMap[listNames[i]] = v
} }
@@ -22,7 +22,7 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
return rtn return rtn
} }
func cartN(a ...[]interface{}) [][]interface{} { func cartN(a ...[]any) [][]any {
c := 1 c := 1
for _, a := range a { for _, a := range a {
c *= len(a) c *= len(a)
@@ -30,8 +30,8 @@ func cartN(a ...[]interface{}) [][]interface{} {
if c == 0 || len(a) == 0 { if c == 0 || len(a) == 0 {
return nil return nil
} }
p := make([][]interface{}, c) p := make([][]any, c)
b := make([]interface{}, c*len(a)) b := make([]any, c*len(a))
n := make([]int, len(a)) n := make([]int, len(a))
s := 0 s := 0
for i := range p { for i := range p {
+3 -3
View File
@@ -8,7 +8,7 @@ import (
func TestCartesianProduct(t *testing.T) { func TestCartesianProduct(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
input := map[string][]interface{}{ input := map[string][]any{
"foo": {1, 2, 3, 4}, "foo": {1, 2, 3, 4},
"bar": {"a", "b", "c"}, "bar": {"a", "b", "c"},
"baz": {false, true}, "baz": {false, true},
@@ -25,7 +25,7 @@ func TestCartesianProduct(t *testing.T) {
assert.Contains(v, "baz") assert.Contains(v, "baz")
} }
input = map[string][]interface{}{ input = map[string][]any{
"foo": {1, 2, 3, 4}, "foo": {1, 2, 3, 4},
"bar": {}, "bar": {},
"baz": {false, true}, "baz": {false, true},
@@ -33,7 +33,7 @@ func TestCartesianProduct(t *testing.T) {
output = CartesianProduct(input) output = CartesianProduct(input)
assert.Len(output, 0) assert.Len(output, 0)
input = map[string][]interface{}{} input = map[string][]any{}
output = CartesianProduct(input) output = CartesianProduct(input)
assert.Len(output, 0) assert.Len(output, 0)
} }
+4 -5
View File
@@ -72,6 +72,7 @@ func (p *Pen) drawTopBars(buf io.Writer, labels ...string) {
} }
fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, "\n")
} }
func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) { func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) {
style := styleDefs[p.style] style := styleDefs[p.style]
for _, label := range labels { for _, label := range labels {
@@ -83,6 +84,7 @@ func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) {
} }
fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, "\n")
} }
func (p *Pen) drawLabels(buf io.Writer, labels ...string) { func (p *Pen) drawLabels(buf io.Writer, labels ...string) {
style := styleDefs[p.style] style := styleDefs[p.style]
for _, label := range labels { for _, label := range labels {
@@ -125,11 +127,8 @@ func (p *Pen) DrawBoxes(labels ...string) *Drawing {
// Draw to writer // Draw to writer
func (d *Drawing) Draw(writer io.Writer, centerOnWidth int) { func (d *Drawing) Draw(writer io.Writer, centerOnWidth int) {
padSize := (centerOnWidth - d.GetWidth()) / 2 padSize := max((centerOnWidth-d.GetWidth())/2, 0)
if padSize < 0 { for l := range strings.SplitSeq(d.buf.String(), "\n") {
padSize = 0
}
for _, l := range strings.Split(d.buf.String(), "\n") {
if len(l) > 0 { if len(l) > 0 {
padding := strings.Repeat(" ", padSize) padding := strings.Repeat(" ", padSize)
fmt.Fprintf(writer, "%s%s\n", padding, l) fmt.Fprintf(writer, "%s%s\n", padding, l)
+6 -6
View File
@@ -19,7 +19,7 @@ func (w Warning) Error() string {
} }
// Warningf create a warning // Warningf create a warning
func Warningf(format string, args ...interface{}) Warning { func Warningf(format string, args ...any) Warning {
w := Warning{ w := Warning{
Message: fmt.Sprintf(format, args...), Message: fmt.Sprintf(format, args...),
} }
@@ -33,7 +33,7 @@ type Executor func(ctx context.Context) error
type Conditional func(ctx context.Context) bool type Conditional func(ctx context.Context) bool
// NewInfoExecutor is an executor that logs messages // 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 { return func(ctx context.Context) error {
logger := Logger(ctx) logger := Logger(ctx)
logger.Infof(format, args...) logger.Infof(format, args...)
@@ -42,7 +42,7 @@ func NewInfoExecutor(format string, args ...interface{}) Executor {
} }
// NewDebugExecutor is an executor that logs messages // 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 { return func(ctx context.Context) error {
logger := Logger(ctx) logger := Logger(ctx)
logger.Debugf(format, args...) logger.Debugf(format, args...)
@@ -69,7 +69,7 @@ func NewPipelineExecutor(executors ...Executor) Executor {
} }
// NewConditionalExecutor creates a new executor based on conditions // 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 { return func(ctx context.Context) error {
if conditional(ctx) { if conditional(ctx) {
if trueExecutor != nil { if trueExecutor != nil {
@@ -128,14 +128,14 @@ func NewParallelExecutor(parallel int, executors ...Executor) Executor {
}(i, work, errs) }(i, work, errs)
} }
for i := 0; i < len(executors); i++ { for i := range executors {
work <- executors[i] work <- executors[i]
} }
close(work) close(work)
// Executor waits all executors to cleanup these resources. // Executor waits all executors to cleanup these resources.
var firstErr error var firstErr error
for i := 0; i < len(executors); i++ { for range executors {
err := <-errs err := <-errs
if firstErr == nil { if firstErr == nil {
firstErr = err firstErr = err
+16 -16
View File
@@ -14,24 +14,24 @@ import (
func TestMaxParallel2Quick(t *testing.T) { func TestMaxParallel2Quick(t *testing.T) {
ctx := context.Background() ctx := context.Background()
var currentRunning int32 var currentRunning atomic.Int32
var maxSimultaneous int32 var maxSimultaneous atomic.Int32
executors := make([]Executor, 4) executors := make([]Executor, 4)
for i := 0; i < 4; i++ { for i := range 4 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
current := atomic.AddInt32(&currentRunning, 1) current := currentRunning.Add(1)
// Update max if needed // Update max if needed
for { for {
maxValue := atomic.LoadInt32(&maxSimultaneous) maxValue := maxSimultaneous.Load()
if current <= maxValue || atomic.CompareAndSwapInt32(&maxSimultaneous, maxValue, current) { if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
break break
} }
} }
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
atomic.AddInt32(&currentRunning, -1) currentRunning.Add(-1)
return nil return nil
} }
} }
@@ -39,7 +39,7 @@ func TestMaxParallel2Quick(t *testing.T) {
err := NewParallelExecutor(2, executors...)(ctx) err := NewParallelExecutor(2, executors...)(ctx)
assert.NoError(t, err) 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") "Should not exceed max-parallel: 2")
} }
@@ -47,16 +47,16 @@ func TestMaxParallel2Quick(t *testing.T) {
func TestMaxParallel1Sequential(t *testing.T) { func TestMaxParallel1Sequential(t *testing.T) {
ctx := context.Background() ctx := context.Background()
var currentRunning int32 var currentRunning atomic.Int32
var maxSimultaneous int32 var maxSimultaneous atomic.Int32
var executionOrder []int var executionOrder []int
var orderMutex sync.Mutex var orderMutex sync.Mutex
executors := make([]Executor, 5) executors := make([]Executor, 5)
for i := 0; i < 5; i++ { for i := range 5 {
taskID := i taskID := i
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
current := atomic.AddInt32(&currentRunning, 1) current := currentRunning.Add(1)
// Track execution order // Track execution order
orderMutex.Lock() orderMutex.Lock()
@@ -65,14 +65,14 @@ func TestMaxParallel1Sequential(t *testing.T) {
// Update max if needed // Update max if needed
for { for {
maxValue := atomic.LoadInt32(&maxSimultaneous) maxValue := maxSimultaneous.Load()
if current <= maxValue || atomic.CompareAndSwapInt32(&maxSimultaneous, maxValue, current) { if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
break break
} }
} }
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
atomic.AddInt32(&currentRunning, -1) currentRunning.Add(-1)
return nil return nil
} }
} }
@@ -80,7 +80,7 @@ func TestMaxParallel1Sequential(t *testing.T) {
err := NewParallelExecutor(1, executors...)(ctx) err := NewParallelExecutor(1, executors...)(ctx)
assert.NoError(t, err) 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") "max-parallel: 1 should only run 1 task at a time")
assert.Len(t, executionOrder, 5, "All 5 tasks should have executed") assert.Len(t, executionOrder, 5, "All 5 tasks should have executed")
} }
+24 -24
View File
@@ -13,21 +13,21 @@ import (
// TestMaxParallelJobExecution tests actual job execution with max-parallel // TestMaxParallelJobExecution tests actual job execution with max-parallel
func TestMaxParallelJobExecution(t *testing.T) { func TestMaxParallelJobExecution(t *testing.T) {
t.Run("MaxParallel=1 Sequential", func(t *testing.T) { t.Run("MaxParallel=1 Sequential", func(t *testing.T) {
var currentRunning int32 var currentRunning atomic.Int32
var maxConcurrent int32 var maxConcurrent int32
var executionOrder []int var executionOrder []int
var mu sync.Mutex var mu sync.Mutex
executors := make([]Executor, 5) executors := make([]Executor, 5)
for i := 0; i < 5; i++ { for i := range 5 {
taskID := i taskID := i
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
current := atomic.AddInt32(&currentRunning, 1) current := currentRunning.Add(1)
// Track max concurrent // Track max concurrent
for { for {
max := atomic.LoadInt32(&maxConcurrent) maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) { if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break break
} }
} }
@@ -37,7 +37,7 @@ func TestMaxParallelJobExecution(t *testing.T) {
mu.Unlock() mu.Unlock()
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
atomic.AddInt32(&currentRunning, -1) currentRunning.Add(-1)
return nil return nil
} }
} }
@@ -51,23 +51,23 @@ func TestMaxParallelJobExecution(t *testing.T) {
}) })
t.Run("MaxParallel=3 Limited", func(t *testing.T) { t.Run("MaxParallel=3 Limited", func(t *testing.T) {
var currentRunning int32 var currentRunning atomic.Int32
var maxConcurrent int32 var maxConcurrent int32
executors := make([]Executor, 10) executors := make([]Executor, 10)
for i := 0; i < 10; i++ { for i := range 10 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
current := atomic.AddInt32(&currentRunning, 1) current := currentRunning.Add(1)
for { for {
max := atomic.LoadInt32(&maxConcurrent) maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) { if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break break
} }
} }
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
atomic.AddInt32(&currentRunning, -1) currentRunning.Add(-1)
return nil return nil
} }
} }
@@ -82,22 +82,22 @@ func TestMaxParallelJobExecution(t *testing.T) {
t.Run("MaxParallel=0 Uses1Worker", func(t *testing.T) { t.Run("MaxParallel=0 Uses1Worker", func(t *testing.T) {
var maxConcurrent int32 var maxConcurrent int32
var currentRunning int32 var currentRunning atomic.Int32
executors := make([]Executor, 5) executors := make([]Executor, 5)
for i := 0; i < 5; i++ { for i := range 5 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
current := atomic.AddInt32(&currentRunning, 1) current := currentRunning.Add(1)
for { for {
max := atomic.LoadInt32(&maxConcurrent) maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= max || atomic.CompareAndSwapInt32(&maxConcurrent, max, current) { if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break break
} }
} }
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
atomic.AddInt32(&currentRunning, -1) currentRunning.Add(-1)
return nil return nil
} }
} }
@@ -117,7 +117,7 @@ func TestMaxParallelWithErrors(t *testing.T) {
var successCount int32 var successCount int32
executors := make([]Executor, 5) executors := make([]Executor, 5)
for i := 0; i < 5; i++ { for i := range 5 {
taskID := i taskID := i
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
if taskID == 2 { if taskID == 2 {
@@ -143,7 +143,7 @@ func TestMaxParallelWithErrors(t *testing.T) {
var startedCount int32 var startedCount int32
executors := make([]Executor, 10) executors := make([]Executor, 10)
for i := 0; i < 10; i++ { for i := range 10 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
atomic.AddInt32(&startedCount, 1) atomic.AddInt32(&startedCount, 1)
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -175,7 +175,7 @@ func TestMaxParallelPerformance(t *testing.T) {
t.Run("ParallelFasterThanSequential", func(t *testing.T) { t.Run("ParallelFasterThanSequential", func(t *testing.T) {
executors := make([]Executor, 10) executors := make([]Executor, 10)
for i := 0; i < 10; i++ { for i := range 10 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
return nil return nil
@@ -203,7 +203,7 @@ func TestMaxParallelPerformance(t *testing.T) {
t.Run("OptimalWorkerCount", func(t *testing.T) { t.Run("OptimalWorkerCount", func(t *testing.T) {
executors := make([]Executor, 20) executors := make([]Executor, 20)
for i := 0; i < 20; i++ { for i := range 20 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
return nil return nil
@@ -236,7 +236,7 @@ func TestMaxParallelResourceSharing(t *testing.T) {
var mu sync.Mutex var mu sync.Mutex
executors := make([]Executor, 100) executors := make([]Executor, 100)
for i := 0; i < 100; i++ { for i := range 100 {
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
mu.Lock() mu.Lock()
sharedCounter++ sharedCounter++
@@ -256,7 +256,7 @@ func TestMaxParallelResourceSharing(t *testing.T) {
resultChan := make(chan int, 50) resultChan := make(chan int, 50)
executors := make([]Executor, 50) executors := make([]Executor, 50)
for i := 0; i < 50; i++ { for i := range 50 {
taskID := i taskID := i
executors[i] = func(ctx context.Context) error { executors[i] = func(ctx context.Context) error {
resultChan <- taskID resultChan <- taskID
+4 -4
View File
@@ -2,7 +2,7 @@ package common
import ( import (
"context" "context"
"fmt" "errors"
"testing" "testing"
"time" "time"
@@ -19,7 +19,7 @@ func TestNewWorkflow(t *testing.T) {
assert.Nil(emptyWorkflow(ctx)) assert.Nil(emptyWorkflow(ctx))
// error case // error case
errorWorkflow := NewErrorExecutor(fmt.Errorf("test error")) errorWorkflow := NewErrorExecutor(errors.New("test error"))
assert.NotNil(errorWorkflow(ctx)) assert.NotNil(errorWorkflow(ctx))
// multiple success case // multiple success case
@@ -122,7 +122,7 @@ func TestNewParallelExecutorFailed(t *testing.T) {
count := 0 count := 0
errorWorkflow := NewPipelineExecutor(func(ctx context.Context) error { errorWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
count++ count++
return fmt.Errorf("fake error") return errors.New("fake error")
}) })
err := NewParallelExecutor(1, errorWorkflow)(ctx) err := NewParallelExecutor(1, errorWorkflow)(ctx)
assert.Equal(1, count) assert.Equal(1, count)
@@ -135,7 +135,7 @@ func TestNewParallelExecutorCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
errExpected := fmt.Errorf("fake error") errExpected := errors.New("fake error")
count := 0 count := 0
successWorkflow := NewPipelineExecutor(func(ctx context.Context) error { successWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
+3 -3
View File
@@ -7,7 +7,7 @@ import (
) )
// CopyFile copy file // CopyFile copy file
func CopyFile(source string, dest string) (err error) { func CopyFile(source, dest string) (err error) {
sourcefile, err := os.Open(source) sourcefile, err := os.Open(source)
if err != nil { if err != nil {
return err return err
@@ -30,11 +30,11 @@ func CopyFile(source string, dest string) (err error) {
} }
} }
return return err
} }
// CopyDir recursive copy of directory // 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 // get properties of source dir
sourceinfo, err := os.Stat(source) sourceinfo, err := os.Stat(source)
if err != nil { if err != nil {
+10 -13
View File
@@ -11,6 +11,8 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/nektos/act/pkg/common"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing" "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/go-git/go-git/v5/plumbing/transport/http"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/nektos/act/pkg/common"
) )
var ( var (
@@ -52,7 +52,7 @@ func (e *Error) Commit() string {
} }
// FindGitRevision get the current git revision // 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) logger := common.Logger(ctx)
gitDir, err := git.PlainOpenWithOptions( gitDir, err := git.PlainOpenWithOptions(
@@ -62,7 +62,6 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
EnableDotGitCommonDir: true, EnableDotGitCommonDir: true,
}, },
) )
if err != nil { if err != nil {
logger.WithError(err).Error("path", file, "not located inside a git repository") logger.WithError(err).Error("path", file, "not located inside a git repository")
return "", "", err return "", "", err
@@ -74,7 +73,7 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
} }
if head.Hash().IsZero() { 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() hash := head.Hash().String()
@@ -96,8 +95,8 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
logger.Debugf("HEAD points to '%s'", ref) logger.Debugf("HEAD points to '%s'", ref)
// Prefer the git library to iterate over the references and find a matching tag or branch. // Prefer the git library to iterate over the references and find a matching tag or branch.
var refTag = "" refTag := ""
var refBranch = "" refBranch := ""
repo, err := git.PlainOpenWithOptions( repo, err := git.PlainOpenWithOptions(
file, file,
&git.PlainOpenOptions{ &git.PlainOpenOptions{
@@ -105,7 +104,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
EnableDotGitCommonDir: true, EnableDotGitCommonDir: true,
}, },
) )
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -144,7 +142,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
return nil return nil
}) })
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -198,7 +195,7 @@ func findGitRemoteURL(_ context.Context, file, remoteName string) (string, error
return remote.Config().URLs[0], nil 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 { if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
return "CodeCommit", matches[2], nil return "CodeCommit", matches[2], nil
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != 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 return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
} else if githubInstance != "github.com" { } else if githubInstance != "github.com" {
gheHTTPRegex := regexp.MustCompile(fmt.Sprintf(`^https?://%s/(.+)/(.+?)(?:.git)?$`, githubInstance)) 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 { if matches := gheHTTPRegex.FindStringSubmatch(url); matches != nil {
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
} else if matches := gheSSHRegex.FindStringSubmatch(url); matches != 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 // NewGitCloneExecutor creates an executor to clone git repos
// //
//nolint:gocyclo //nolint:gocyclo // function handles many cases
func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor { func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
return func(ctx context.Context) error { return func(ctx context.Context) error {
logger := common.Logger(ctx) logger := common.Logger(ctx)
@@ -302,7 +299,7 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
cloneLock.Lock() cloneLock.Lock()
defer cloneLock.Unlock() 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) r, err := CloneIfRequired(ctx, refName, input, logger)
if err != nil { if err != nil {
return err return err
+2 -7
View File
@@ -17,7 +17,7 @@ import (
func TestFindGitSlug(t *testing.T) { func TestFindGitSlug(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var slugTests = []struct { slugTests := []struct {
url string // input url string // input
provider string // expected result provider string // expected result
slug string // expected result slug string // expected result
@@ -44,10 +44,7 @@ func TestFindGitSlug(t *testing.T) {
} }
func testDir(t *testing.T) string { func testDir(t *testing.T) string {
basedir, err := os.MkdirTemp("", "act-test") return t.TempDir()
require.NoError(t, err)
t.Cleanup(func() { _ = os.RemoveAll(basedir) })
return basedir
} }
func cleanGitHooks(dir string) error { 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) { t.Run(name, func(t *testing.T) {
dir := filepath.Join(basedir, name) dir := filepath.Join(basedir, name)
require.NoError(t, os.MkdirAll(dir, 0o755)) require.NoError(t, os.MkdirAll(dir, 0o755))
+4 -3
View File
@@ -4,8 +4,9 @@ import (
"context" "context"
"io" "io"
"github.com/docker/go-connections/nat"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/docker/go-connections/nat"
) )
// NewContainerInput the input for the New function // NewContainerInput the input for the New function
@@ -45,11 +46,11 @@ type FileEntry struct {
// Container for managing docker run containers // Container for managing docker run containers
type Container interface { type Container interface {
Create(capAdd []string, capDrop []string) common.Executor Create(capAdd, capDrop []string) common.Executor
ConnectToNetwork(name string) common.Executor ConnectToNetwork(name string) common.Executor
Copy(destPath string, files ...*FileEntry) common.Executor Copy(destPath string, files ...*FileEntry) common.Executor
CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error 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) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error)
Pull(forcePull bool) common.Executor Pull(forcePull bool) common.Executor
Start(attach bool) common.Executor Start(attach bool) common.Executor
+2 -1
View File
@@ -6,10 +6,11 @@ import (
"context" "context"
"strings" "strings"
"github.com/nektos/act/pkg/common"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/config/credentials"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/nektos/act/pkg/common"
) )
func LoadDockerAuthConfig(ctx context.Context, image string) (registry.AuthConfig, error) { func LoadDockerAuthConfig(ctx context.Context, image string) (registry.AuthConfig, error) {
+5 -5
View File
@@ -8,14 +8,13 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/nektos/act/pkg/common"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
// github.com/docker/docker/builder/dockerignore is deprecated // github.com/docker/docker/builder/dockerignore is deprecated
"github.com/moby/buildkit/frontend/dockerfile/dockerignore" "github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/patternmatcher" "github.com/moby/patternmatcher"
"github.com/nektos/act/pkg/common"
) )
// NewDockerBuildExecutor function to create a run executor for the container // NewDockerBuildExecutor function to create a run executor for the container
@@ -69,7 +68,8 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor {
return nil 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) 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 // 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 // removed. The daemon will remove them for us, if needed, after it
// parses the Dockerfile. Ignore errors here, as they will have been // parses the Dockerfile. Ignore errors here, as they will have been
// caught by validateContextDirectory above. // caught by validateContextDirectory above.
var includes = []string{"."} includes := []string{"."}
keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes) keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes)
keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes) keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes)
if keepThem1 || keepThem2 { if keepThem1 || keepThem2 {
+10 -14
View File
@@ -7,7 +7,7 @@
// See DOCKER_LICENSE for the full license text. // 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 package container
import ( import (
@@ -19,6 +19,7 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -37,9 +38,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var ( var deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
)
// containerOptions is a data object with all the options for creating a container // containerOptions is a data object with all the options for creating a container
type containerOptions struct { type containerOptions struct {
@@ -322,7 +321,7 @@ type containerConfig struct {
// a HostConfig and returns them with the specified command. // a HostConfig and returns them with the specified command.
// If the specified args are not valid, it will return an error. // 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) { func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) {
var ( var (
attachStdin = copts.attach.Get("stdin") 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") return nil, errors.Errorf("--health-retries cannot be negative")
} }
if copts.healthStartPeriod < 0 { 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{ healthConfig = &container.HealthConfig{
@@ -722,7 +721,6 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
) )
for i, n := range copts.netMode.Value() { for i, n := range copts.netMode.Value() {
n := n
if container.NetworkMode(n.Target).IsUserDefined() { if container.NetworkMode(n.Target).IsUserDefined() {
hasUserDefined = true hasUserDefined = true
} else { } else {
@@ -837,7 +835,7 @@ func convertToStandardNotation(ports []string) ([]string, error) {
for _, publish := range ports { for _, publish := range ports {
if strings.Contains(publish, "=") { if strings.Contains(publish, "=") {
params := map[string]string{"protocol": "tcp"} params := map[string]string{"protocol": "tcp"}
for _, param := range strings.Split(publish, ",") { for param := range strings.SplitSeq(publish, ",") {
opt := strings.Split(param, "=") opt := strings.Split(param, "=")
if len(opt) < 2 { if len(opt) < 2 {
return optsList, errors.Errorf("invalid publish opts format (should be name=value but got '%s')", param) 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. // validDeviceMode checks if the mode for device is valid or not.
// Valid mode is a composition of r (read), w (write), and m (mknod). // Valid mode is a composition of r (read), w (write), and m (mknod).
func validDeviceMode(mode string) bool { func validDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{ legalDeviceMode := map[rune]bool{
'r': true, 'r': true,
'w': true, 'w': true,
'm': true, 'm': true,
@@ -1006,7 +1004,7 @@ func validDeviceMode(mode string) bool {
} }
// validateDevice validates a path for devices // validateDevice validates a path for devices
func validateDevice(val string, serverOS string) (string, error) { func validateDevice(val, serverOS string) (string, error) {
switch serverOS { switch serverOS {
case "linux": case "linux":
return validateLinuxPath(val, validDeviceMode) 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. // validateAttach validates that the specified string is a valid attach option.
func validateAttach(val string) (string, error) { func validateAttach(val string) (string, error) {
s := strings.ToLower(val) s := strings.ToLower(val)
for _, str := range []string{"stdin", "stdout", "stderr"} { if slices.Contains([]string{"stdin", "stdout", "stderr"}, s) {
if s == str { return s, nil
return s, nil
}
} }
return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR") return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR")
} }
+7 -12
View File
@@ -6,7 +6,7 @@
// See DOCKER_LICENSE for the full license text. // 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 package container
import ( import (
@@ -135,7 +135,6 @@ func TestParseRunAttach(t *testing.T) {
}, },
} }
for _, tc := range tests { for _, tc := range tests {
tc := tc
t.Run(tc.input, func(t *testing.T) { t.Run(tc.input, func(t *testing.T) {
config, _ := mustParse(t, tc.input) config, _ := mustParse(t, tc.input)
assert.Equal(t, config.AttachStdin, tc.expected.AttachStdin) 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) { func TestParseWithVolumes(t *testing.T) {
// A single volume // A single volume
arr, tryit := setupPlatformVolume([]string{`/tmp`}, []string{`c:\tmp`}) arr, tryit := setupPlatformVolume([]string{`/tmp`}, []string{`c:\tmp`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil { 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]) 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 // setupPlatformVolume takes two arrays of volume specs - a Unix style
// spec and a Windows style spec. Depending on the platform being unit tested, // 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 // it returns one of them, along with a volume string that would be passed
// on the docker CLI (e.g. -v /bar -v /foo). // 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 var a []string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
a = w a = w
@@ -340,7 +337,7 @@ func TestParseHostname(t *testing.T) {
hostnameWithDomain := "--hostname=hostname.domainname" hostnameWithDomain := "--hostname=hostname.domainname"
hostnameWithDomainTld := "--hostname=hostname.domainname.tld" hostnameWithDomainTld := "--hostname=hostname.domainname.tld"
for hostname, expectedHostname := range validHostnames { 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) 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) t.Fatalf("Expected %v, got %v", deviceMapping, hostconfig.Devices)
} }
} }
} }
func TestParseNetworkConfig(t *testing.T) { func TestParseNetworkConfig(t *testing.T) {
@@ -634,7 +630,7 @@ func TestParseModes(t *testing.T) {
} }
// uts ko // 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") assert.ErrorContains(t, err, "--uts: invalid UTS mode")
// uts ok // uts ok
@@ -678,7 +674,7 @@ func TestParseRestartPolicy(t *testing.T) {
}, },
} }
for restart, expectedError := range invalids { 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) 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) { func TestParseRestartPolicyAutoRemove(t *testing.T) {
expected := "Conflicting options: --restart and --rm" 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 { if err == nil || err.Error() != expected {
t.Fatalf("Expected error %v, but got none", expected) t.Fatalf("Expected error %v, but got none", expected)
} }
@@ -967,7 +963,6 @@ func TestConvertToStandardNotation(t *testing.T) {
for key, ports := range valid { for key, ports := range valid {
convertedPorts, err := convertToStandardNotation(ports) convertedPorts, err := convertToStandardNotation(ports)
if err != nil { if err != nil {
assert.NilError(t, err) assert.NilError(t, err)
} }
+2 -2
View File
@@ -12,7 +12,7 @@ import (
// ImageExistsLocally returns a boolean indicating if an image with the // ImageExistsLocally returns a boolean indicating if an image with the
// requested name, tag and architecture exists in the local docker image store // 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) cli, err := GetDockerClient(ctx)
if err != nil { if err != nil {
return false, err 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 // RemoveImage removes image from local store, the function is used to run different
// container image architectures // 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) cli, err := GetDockerClient(ctx)
if err != nil { if err != nil {
return false, err return false, err
+1 -1
View File
@@ -74,7 +74,7 @@ func logDockerResponse(logger logrus.FieldLogger, dockerResponse io.ReadCloser,
return nil 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 { if isError {
logger.Errorf(format, args...) logger.Errorf(format, args...)
} else { } else {
+2 -2
View File
@@ -5,9 +5,9 @@ package container
import ( import (
"context" "context"
"github.com/docker/docker/api/types"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/docker/docker/api/types"
) )
func NewDockerNetworkCreateExecutor(name string) common.Executor { func NewDockerNetworkCreateExecutor(name string) common.Executor {
+3 -3
View File
@@ -9,11 +9,11 @@ import (
"fmt" "fmt"
"strings" "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"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/nektos/act/pkg/common"
) )
// NewDockerPullExecutor function to create a run executor for the container // NewDockerPullExecutor function to create a run executor for the container
-1
View File
@@ -5,7 +5,6 @@ import (
"testing" "testing"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert" assert "github.com/stretchr/testify/assert"
) )
+11 -12
View File
@@ -16,6 +16,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/filecollector"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
"github.com/docker/cli/cli/compose/loader" "github.com/docker/cli/cli/compose/loader"
"github.com/docker/cli/cli/connhelper" "github.com/docker/cli/cli/connhelper"
@@ -23,7 +26,6 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/go-git/go-billy/v5/helper/polyfill" "github.com/go-git/go-billy/v5/helper/polyfill"
@@ -36,9 +38,6 @@ import (
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"golang.org/x/term" "golang.org/x/term"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/filecollector"
) )
// NewContainer creates a reference to a container // 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 { func (cr *containerReference) connectToNetwork(name string, aliases []string) common.Executor {
return func(ctx context.Context) error { 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, Aliases: aliases,
}) })
} }
@@ -85,7 +84,7 @@ func supportsContainerImagePlatform(ctx context.Context, cli client.APIClient) b
return constraint.Check(sv) 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. 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). 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( Then(
@@ -142,7 +141,7 @@ func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.
).IfNot(common.Dryrun) ).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( return common.NewPipelineExecutor(
common.NewInfoExecutor("%sdocker cp src=%s dst=%s", logPrefix, srcPath, destPath), common.NewInfoExecutor("%sdocker cp src=%s dst=%s", logPrefix, srcPath, destPath),
cr.copyDir(destPath, srcPath, useGitIgnore), 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) { func (cr *containerReference) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) {
if common.Dryrun(ctx) { 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) a, _, err := cr.cli.CopyFromContainer(ctx, cr.id, srcPath)
return a, err return a, err
@@ -190,7 +189,7 @@ func (cr *containerReference) Remove() common.Executor {
).IfNot(common.Dryrun) ).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 out := cr.input.Stdout
err := cr.input.Stderr err := cr.input.Stderr
@@ -429,7 +428,7 @@ func (cr *containerReference) mergeContainerConfigs(ctx context.Context, config
return config, hostConfig, nil 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 { return func(ctx context.Context) error {
if cr.id != "" { if cr.id != "" {
return nil return nil
@@ -682,7 +681,7 @@ func (cr *containerReference) tryReadGID() common.Executor {
return cr.tryReadID("-g", func(id int) { cr.GID = id }) 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) logger := common.Logger(ctx)
cmdResponse := make(chan error) cmdResponse := make(chan error)
@@ -753,7 +752,7 @@ func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string
return nil 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 { return func(ctx context.Context) error {
logger := common.Logger(ctx) logger := common.Logger(ctx)
tarFile, err := os.CreateTemp("", "act") tarFile, err := os.CreateTemp("", "act")
+4 -4
View File
@@ -4,7 +4,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"fmt" "errors"
"io" "io"
"net" "net"
"strings" "strings"
@@ -81,7 +81,7 @@ func (m *mockDockerClient) ContainerExecInspect(ctx context.Context, execID stri
return args.Get(0).(types.ContainerExecInspect), args.Error(1) 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) args := m.Called(ctx, id, path, content, options)
return args.Error(0) return args.Error(0)
} }
@@ -203,7 +203,7 @@ func TestDockerCopyTarStreamErrorInCopyFiles(t *testing.T) {
conn := &mockConn{} conn := &mockConn{}
merr := fmt.Errorf("Failure") merr := errors.New("Failure")
client := &mockDockerClient{} client := &mockDockerClient{}
client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr) client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr)
@@ -228,7 +228,7 @@ func TestDockerCopyTarStreamErrorInMkdir(t *testing.T) {
conn := &mockConn{} conn := &mockConn{}
merr := fmt.Errorf("Failure") merr := errors.New("Failure")
client := &mockDockerClient{} client := &mockDockerClient{}
client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil) client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil)
+2 -2
View File
@@ -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 // 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. // is not found in the `daemonPath`, the function returns false.
func isDockerHostURI(daemonPath string) bool { func isDockerHostURI(daemonPath string) bool {
if protoIndex := strings.Index(daemonPath, "://"); protoIndex != -1 { if before, _, ok := strings.Cut(daemonPath, "://"); ok {
scheme := daemonPath[:protoIndex] scheme := before
if strings.IndexFunc(scheme, func(r rune) bool { if strings.IndexFunc(scheme, func(r rune) bool {
return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z')
}) == -1 { }) == -1 {
+4 -4
View File
@@ -19,7 +19,7 @@ func TestGetSocketAndHostWithSocket(t *testing.T) {
CommonSocketLocations = originalCommonSocketLocations CommonSocketLocations = originalCommonSocketLocations
dockerHost := "unix:///my/docker/host.sock" dockerHost := "unix:///my/docker/host.sock"
socketURI := "/path/to/my.socket" socketURI := "/path/to/my.socket"
os.Setenv("DOCKER_HOST", dockerHost) t.Setenv("DOCKER_HOST", dockerHost)
// Act // Act
ret, err := GetSocketAndHost(socketURI) ret, err := GetSocketAndHost(socketURI)
@@ -32,7 +32,7 @@ func TestGetSocketAndHostWithSocket(t *testing.T) {
func TestGetSocketAndHostNoSocket(t *testing.T) { func TestGetSocketAndHostNoSocket(t *testing.T) {
// Arrange // Arrange
dockerHost := "unix:///my/docker/host.sock" dockerHost := "unix:///my/docker/host.sock"
os.Setenv("DOCKER_HOST", dockerHost) t.Setenv("DOCKER_HOST", dockerHost)
// Act // Act
ret, err := GetSocketAndHost("") ret, err := GetSocketAndHost("")
@@ -63,7 +63,7 @@ func TestGetSocketAndHostDontMount(t *testing.T) {
// Arrange // Arrange
CommonSocketLocations = originalCommonSocketLocations CommonSocketLocations = originalCommonSocketLocations
dockerHost := "unix:///my/docker/host.sock" dockerHost := "unix:///my/docker/host.sock"
os.Setenv("DOCKER_HOST", dockerHost) t.Setenv("DOCKER_HOST", dockerHost)
// Act // Act
ret, err := GetSocketAndHost("-") 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 // > This happens if neither DOCKER_HOST nor --container-daemon-socket has a value, but socketLocation() returns a URI
func TestGetSocketAndHostNoHostNoSocketDefaultLocation(t *testing.T) { func TestGetSocketAndHostNoHostNoSocketDefaultLocation(t *testing.T) {
// Arrange // Arrange
mySocketFile, tmpErr := os.CreateTemp("", "act-*.sock") mySocketFile, tmpErr := os.CreateTemp(t.TempDir(), "act-*.sock")
mySocket := mySocketFile.Name() mySocket := mySocketFile.Name()
unixSocket := "unix://" + mySocket unixSocket := "unix://" + mySocket
defer os.RemoveAll(mySocket) defer os.RemoveAll(mySocket)
+4 -3
View File
@@ -6,20 +6,21 @@ import (
"context" "context"
"runtime" "runtime"
"github.com/docker/docker/api/types"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/docker/docker/api/types"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// ImageExistsLocally returns a boolean indicating if an image with the // ImageExistsLocally returns a boolean indicating if an image with the
// requested name, tag and architecture exists in the local docker image store // 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") return false, errors.New("Unsupported Operation")
} }
// RemoveImage removes image from local store, the function is used to run different // RemoveImage removes image from local store, the function is used to run different
// container image architectures // 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") return false, errors.New("Unsupported Operation")
} }
+2 -1
View File
@@ -5,9 +5,10 @@ package container
import ( import (
"context" "context"
"github.com/nektos/act/pkg/common"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume" "github.com/docker/docker/api/types/volume"
"github.com/nektos/act/pkg/common"
) )
func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executor { func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executor {
+1 -1
View File
@@ -9,7 +9,7 @@ type ExecutionsEnvironment interface {
GetPathVariableName() string GetPathVariableName() string
DefaultPathVariable() string DefaultPathVariable() string
JoinPathVariable(...string) 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 // On windows PATH and Path are the same key
IsEnvironmentCaseInsensitive() bool IsEnvironmentCaseInsensitive() bool
} }
+15 -14
View File
@@ -15,14 +15,14 @@ import (
"strings" "strings"
"time" "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/helper/polyfill"
"github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5/plumbing/format/gitignore" "github.com/go-git/go-git/v5/plumbing/format/gitignore"
"golang.org/x/term" "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 { type HostEnvironment struct {
@@ -35,7 +35,7 @@ type HostEnvironment struct {
StdOut io.Writer 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 func(ctx context.Context) error {
return nil return nil
} }
@@ -86,7 +86,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
continue continue
} }
if ctx.Err() != nil { 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 { if err := cp.WriteFile(ti.Name, ti.FileInfo(), ti.Linkname, tr); err != nil {
return err 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 { return func(ctx context.Context) error {
logger := common.Logger(ctx) logger := common.Logger(ctx)
srcPrefix := filepath.Dir(srcPath) 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) { func lookupPathHost(cmd string, env map[string]string, writer io.Writer) (string, error) {
f, err := lookpath.LookPath2(cmd, &localEnv{env: env}) f, err := lookpath.LookPath2(cmd, &localEnv{env: env})
if err != nil { 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 { if _, _err := writer.Write([]byte(err + "\n")); _err != nil {
return "", fmt.Errorf("%v: %w", err, _err) 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 { if tty != nil {
writer.AutoStop = true 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") common.Logger(ctx).Debug("Failed to write EOT")
} }
} }
@@ -408,9 +408,10 @@ func (e *HostEnvironment) GetActPath() string {
} }
func (*HostEnvironment) GetPathVariableName() string { func (*HostEnvironment) GetPathVariableName() string {
if runtime.GOOS == "plan9" { switch runtime.GOOS {
case "plan9":
return "path" return "path"
} else if runtime.GOOS == "windows" { case "windows":
return "Path" // Actually we need a case insensitive map return "Path" // Actually we need a case insensitive map
} }
return "PATH" return "PATH"
@@ -449,8 +450,8 @@ func goOsToActionOs(os string) string {
return os return os
} }
func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]interface{} { func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]any {
return map[string]interface{}{ return map[string]any{
"os": goOsToActionOs(runtime.GOOS), "os": goOsToActionOs(runtime.GOOS),
"arch": goArchToActionArch(runtime.GOARCH), "arch": goArchToActionArch(runtime.GOARCH),
"temp": e.TmpDir, "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 org := e.StdOut
e.StdOut = stdout e.StdOut = stdout
return org, org return org, org
+12 -16
View File
@@ -16,9 +16,7 @@ import (
var _ ExecutionsEnvironment = &HostEnvironment{} var _ ExecutionsEnvironment = &HostEnvironment{}
func TestCopyDir(t *testing.T) { func TestCopyDir(t *testing.T) {
dir, err := os.MkdirTemp("", "test-host-env-*") dir := t.TempDir()
assert.NoError(t, err)
defer os.RemoveAll(dir)
ctx := context.Background() ctx := context.Background()
e := &HostEnvironment{ e := &HostEnvironment{
Path: filepath.Join(dir, "path"), Path: filepath.Join(dir, "path"),
@@ -28,18 +26,16 @@ func TestCopyDir(t *testing.T) {
StdOut: os.Stdout, StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"), Workdir: path.Join("testdata", "scratch"),
} }
_ = os.MkdirAll(e.Path, 0700) _ = os.MkdirAll(e.Path, 0o700)
_ = os.MkdirAll(e.TmpDir, 0700) _ = os.MkdirAll(e.TmpDir, 0o700)
_ = os.MkdirAll(e.ToolCache, 0700) _ = os.MkdirAll(e.ToolCache, 0o700)
_ = os.MkdirAll(e.ActPath, 0700) _ = os.MkdirAll(e.ActPath, 0o700)
err = e.CopyDir(e.Workdir, e.Path, true)(ctx) err := e.CopyDir(e.Workdir, e.Path, true)(ctx)
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestGetContainerArchive(t *testing.T) { func TestGetContainerArchive(t *testing.T) {
dir, err := os.MkdirTemp("", "test-host-env-*") dir := t.TempDir()
assert.NoError(t, err)
defer os.RemoveAll(dir)
ctx := context.Background() ctx := context.Background()
e := &HostEnvironment{ e := &HostEnvironment{
Path: filepath.Join(dir, "path"), Path: filepath.Join(dir, "path"),
@@ -49,12 +45,12 @@ func TestGetContainerArchive(t *testing.T) {
StdOut: os.Stdout, StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"), Workdir: path.Join("testdata", "scratch"),
} }
_ = os.MkdirAll(e.Path, 0700) _ = os.MkdirAll(e.Path, 0o700)
_ = os.MkdirAll(e.TmpDir, 0700) _ = os.MkdirAll(e.TmpDir, 0o700)
_ = os.MkdirAll(e.ToolCache, 0700) _ = os.MkdirAll(e.ToolCache, 0o700)
_ = os.MkdirAll(e.ActPath, 0700) _ = os.MkdirAll(e.ActPath, 0o700)
expectedContent := []byte("sdde/7sh") 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) assert.NoError(t, err)
archive, err := e.GetContainerArchive(ctx, e.Path) archive, err := e.GetContainerArchive(ctx, e.Path)
assert.NoError(t, err) assert.NoError(t, err)
@@ -10,8 +10,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type LinuxContainerEnvironmentExtensions struct { type LinuxContainerEnvironmentExtensions struct{}
}
// Resolves the equivalent host path inside the container // 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 // 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, ":") return strings.Join(paths, ":")
} }
func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]interface{} { func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]any {
return map[string]interface{}{ return map[string]any{
"os": "Linux", "os": "Linux",
"arch": RunnerArch(ctx), "arch": RunnerArch(ctx),
"temp": "/tmp", "temp": "/tmp",
@@ -31,22 +31,17 @@ func TestContainerPath(t *testing.T) {
for _, v := range []containerPathJob{ for _, v := range []containerPathJob{
{"/mnt/c/Users/act/go/src/github.com/nektos/act", "C:\\Users\\act\\go\\src\\github.com\\nektos\\act\\", ""}, {"/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/f/work/dir", `F:\work\dir`, ""},
{"/mnt/c/windows/to/unix", "windows\\to\\unix", fmt.Sprintf("%s\\", rootDrive)}, {"/mnt/c/windows/to/unix", "windows\\to\\unix", rootDrive + "\\"},
{fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", fmt.Sprintf("%s\\", rootDrive)}, {fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", rootDrive + "\\"},
} { } {
if v.workDir != "" { if v.workDir != "" {
if err := os.Chdir(v.workDir); err != nil { t.Chdir(v.workDir)
log.Error(err)
t.Fail()
}
} }
assert.Equal(t, v.destinationPath, linuxcontainerext.ToContainerPath(v.sourcePath)) assert.Equal(t, v.destinationPath, linuxcontainerext.ToContainerPath(v.sourcePath))
} }
if err := os.Chdir(cwd); err != nil { t.Chdir(cwd)
log.Error(err)
}
} else { } else {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
+13 -12
View File
@@ -4,6 +4,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
@@ -13,9 +14,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/rhysd/actionlint" "github.com/rhysd/actionlint"
) )
@@ -66,7 +67,7 @@ const (
func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.Value) (string, error) { func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.Value) (string, error) {
input := impl.coerceToString(str).String() input := impl.coerceToString(str).String()
output := "" var output strings.Builder
replacementIndex := "" replacementIndex := ""
state := passThrough state := passThrough
@@ -81,13 +82,13 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
state = bracketClose state = bracketClose
default: default:
output += string(character) output.WriteRune(character)
} }
case bracketOpen: // found { case bracketOpen: // found {
switch character { switch character {
case '{': case '{':
output += "{" output.WriteString("{")
replacementIndex = "" replacementIndex = ""
state = passThrough 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) 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 state = passThrough
@@ -114,7 +115,7 @@ func (impl *interperterImpl) format(str reflect.Value, replaceValue ...reflect.V
case bracketClose: // found } case bracketClose: // found }
switch character { switch character {
case '}': case '}':
output += "}" output.WriteString("}")
replacementIndex = "" replacementIndex = ""
state = passThrough 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() separator := impl.coerceToString(sep).String()
switch array.Kind() { switch array.Kind() {
case reflect.Slice: case reflect.Slice:
@@ -165,12 +166,12 @@ func (impl *interperterImpl) toJSON(value reflect.Value) (string, error) {
return string(json), nil 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 { if value.Kind() != reflect.String {
return nil, fmt.Errorf("Cannot parse non-string type %v as JSON", value.Kind()) 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) err := json.Unmarshal([]byte(value.String()), &data)
if err != nil { if err != nil {
@@ -195,7 +196,7 @@ func (impl *interperterImpl) hashFiles(paths ...reflect.Value) (string, error) {
} }
ps = append(ps, gitignore.ParsePattern(cleanPath, nil)) ps = append(ps, gitignore.ParsePattern(cleanPath, nil))
} else { } else {
return "", fmt.Errorf("Non-string path passed to hashFiles") return "", errors.New("Non-string path passed to hashFiles")
} }
} }
+11 -10
View File
@@ -5,13 +5,14 @@ import (
"testing" "testing"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestFunctionContains(t *testing.T) { func TestFunctionContains(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"contains('search', 'item') }}", false, "contains-str-str"}, {"contains('search', 'item') }}", false, "contains-str-str"},
@@ -48,7 +49,7 @@ func TestFunctionContains(t *testing.T) {
func TestFunctionStartsWith(t *testing.T) { func TestFunctionStartsWith(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"startsWith('search', 'se') }}", true, "startswith-string"}, {"startsWith('search', 'se') }}", true, "startswith-string"},
@@ -77,7 +78,7 @@ func TestFunctionStartsWith(t *testing.T) {
func TestFunctionEndsWith(t *testing.T) { func TestFunctionEndsWith(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"endsWith('search', 'ch') }}", true, "endsWith-string"}, {"endsWith('search', 'ch') }}", true, "endsWith-string"},
@@ -106,7 +107,7 @@ func TestFunctionEndsWith(t *testing.T) {
func TestFunctionJoin(t *testing.T) { func TestFunctionJoin(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"join(fromJSON('[\"a\", \"b\"]'), ',')", "a,b", "join-arr"}, {"join(fromJSON('[\"a\", \"b\"]'), ',')", "a,b", "join-arr"},
@@ -133,7 +134,7 @@ func TestFunctionJoin(t *testing.T) {
func TestFunctionToJSON(t *testing.T) { func TestFunctionToJSON(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"toJSON(env) }}", "{\n \"key\": \"value\"\n}", "toJSON"}, {"toJSON(env) }}", "{\n \"key\": \"value\"\n}", "toJSON"},
@@ -159,10 +160,10 @@ func TestFunctionToJSON(t *testing.T) {
func TestFunctionFromJSON(t *testing.T) { func TestFunctionFromJSON(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"fromJSON('{\"foo\":\"bar\"}') }}", map[string]interface{}{ {"fromJSON('{\"foo\":\"bar\"}') }}", map[string]any{
"foo": "bar", "foo": "bar",
}, "fromJSON"}, }, "fromJSON"},
} }
@@ -182,7 +183,7 @@ func TestFunctionFromJSON(t *testing.T) {
func TestFunctionHashFiles(t *testing.T) { func TestFunctionHashFiles(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"hashFiles('**/non-extant-files') }}", "", "hash-non-existing-file"}, {"hashFiles('**/non-extant-files') }}", "", "hash-non-existing-file"},
@@ -212,8 +213,8 @@ func TestFunctionHashFiles(t *testing.T) {
func TestFunctionFormat(t *testing.T) { func TestFunctionFormat(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
error interface{} error any
name string name string
}{ }{
{"format('text')", "text", nil, "format-plain-string"}, {"format('text')", "text", nil, "format-plain-string"},
+32 -30
View File
@@ -2,12 +2,14 @@ package exprparser
import ( import (
"encoding" "encoding"
"errors"
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
"strings" "strings"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"github.com/rhysd/actionlint" "github.com/rhysd/actionlint"
) )
@@ -17,14 +19,14 @@ type EvaluationEnvironment struct {
Job *model.JobContext Job *model.JobContext
Jobs *map[string]*model.WorkflowCallResult Jobs *map[string]*model.WorkflowCallResult
Steps map[string]*model.StepResult Steps map[string]*model.StepResult
Runner map[string]interface{} Runner map[string]any
Secrets map[string]string Secrets map[string]string
Vars map[string]string Vars map[string]string
Strategy map[string]interface{} Strategy map[string]any
Matrix map[string]interface{} Matrix map[string]any
Needs map[string]Needs Needs map[string]Needs
Inputs map[string]interface{} Inputs map[string]any
HashFiles func([]reflect.Value) (interface{}, error) HashFiles func([]reflect.Value) (any, error)
} }
type Needs struct { type Needs struct {
@@ -63,7 +65,7 @@ func (dsc DefaultStatusCheck) String() string {
} }
type Interpreter interface { type Interpreter interface {
Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (interface{}, error) Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (any, error)
} }
type interperterImpl struct { 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, "${{") input = strings.TrimPrefix(input, "${{")
if defaultStatusCheck != DefaultStatusCheckNone && input == "" { if defaultStatusCheck != DefaultStatusCheckNone && input == "" {
input = "success()" input = "success()"
@@ -117,7 +119,7 @@ func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultSt
return result, err2 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) { switch node := exprNode.(type) {
case *actionlint.VariableNode: case *actionlint.VariableNode:
return impl.evaluateVariable(node) return impl.evaluateVariable(node)
@@ -150,8 +152,8 @@ func (impl *interperterImpl) evaluateNode(exprNode actionlint.ExprNode) (interfa
} }
} }
//nolint:gocyclo //nolint:gocyclo // function handles many cases
func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableNode) (interface{}, error) { func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableNode) (any, error) {
switch strings.ToLower(variableNode.Name) { switch strings.ToLower(variableNode.Name) {
case "github": case "github":
return impl.env.Github, nil return impl.env.Github, nil
@@ -163,7 +165,7 @@ func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableN
return impl.env.Job, nil return impl.env.Job, nil
case "jobs": case "jobs":
if impl.env.Jobs == nil { if impl.env.Jobs == nil {
return nil, fmt.Errorf("Unavailable context: jobs") return nil, errors.New("Unavailable context: jobs")
} }
return impl.env.Jobs, nil return impl.env.Jobs, nil
case "steps": 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) left, err := impl.evaluateNode(indexAccessNode.Operand)
if err != nil { if err != nil {
return nil, err 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) left, err := impl.evaluateNode(objectDerefNode.Receiver)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -235,7 +237,7 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj
return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property) 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) left, err := impl.evaluateNode(arrayDerefNode.Receiver)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -244,17 +246,17 @@ func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.Array
return impl.getSafeValue(reflect.ValueOf(left)), nil 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() { switch left.Kind() {
case reflect.Ptr: case reflect.Ptr:
return impl.getPropertyValue(left.Elem(), property) return impl.getPropertyValue(left.Elem(), property)
case reflect.Struct: case reflect.Struct:
leftType := left.Type() leftType := left.Type()
for i := 0; i < leftType.NumField(); i++ { for field := range leftType.Fields() {
jsonName := leftType.Field(i).Tag.Get("json") jsonName := field.Tag.Get("json")
if jsonName == property { if jsonName == property {
property = leftType.Field(i).Name property = field.Name
break break
} }
} }
@@ -298,7 +300,7 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin
return nil, nil return nil, nil
case reflect.Slice: case reflect.Slice:
var values []interface{} var values []any
for i := 0; i < left.Len(); i++ { for i := 0; i < left.Len(); i++ {
value, err := impl.getPropertyValue(left.Index(i).Elem(), property) 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 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 { if value.Kind() == reflect.Ptr {
return impl.getMapValue(value.Elem()) return impl.getMapValue(value.Elem())
} }
@@ -323,7 +325,7 @@ func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, erro
return value.Interface(), nil 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) operand, err := impl.evaluateNode(notNode.Operand)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -332,7 +334,7 @@ func (impl *interperterImpl) evaluateNot(notNode *actionlint.NotOpNode) (interfa
return !IsTruthy(operand), nil 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) left, err := impl.evaluateNode(compareNode.Left)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -349,7 +351,7 @@ func (impl *interperterImpl) evaluateCompare(compareNode *actionlint.CompareOpNo
return impl.compareValues(leftValue, rightValue, compareNode.Kind) 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 leftValue.Kind() != rightValue.Kind() {
if !impl.isNumber(leftValue) { if !impl.isNumber(leftValue) {
leftValue = impl.coerceToNumber(leftValue) leftValue = impl.coerceToNumber(leftValue)
@@ -461,7 +463,7 @@ func (impl *interperterImpl) coerceToString(value reflect.Value) reflect.Value {
return 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 { switch kind {
case actionlint.CompareOpNodeKindLess: case actionlint.CompareOpNodeKindLess:
return left < right, nil 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 { switch kind {
case actionlint.CompareOpNodeKindLess: case actionlint.CompareOpNodeKindLess:
return left < right, nil 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) value := reflect.ValueOf(input)
switch value.Kind() { switch value.Kind() {
case reflect.Bool: 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() { switch value.Kind() {
case reflect.Invalid: case reflect.Invalid:
return nil return nil
@@ -549,7 +551,7 @@ func (impl *interperterImpl) getSafeValue(value reflect.Value) interface{} {
return 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) left, err := impl.evaluateNode(compareNode.Left)
if err != nil { if err != nil {
return nil, err 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()) return nil, fmt.Errorf("Unable to compare incompatibles types '%s' and '%s'", leftValue.Kind(), rightValue.Kind())
} }
//nolint:gocyclo //nolint:gocyclo // function handles many cases
func (impl *interperterImpl) evaluateFuncCall(funcCallNode *actionlint.FuncCallNode) (interface{}, error) { func (impl *interperterImpl) evaluateFuncCall(funcCallNode *actionlint.FuncCallNode) (any, error) {
args := make([]reflect.Value, 0) args := make([]reflect.Value, 0)
for _, arg := range funcCallNode.Args { for _, arg := range funcCallNode.Args {
+17 -16
View File
@@ -5,13 +5,14 @@ import (
"testing" "testing"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestLiterals(t *testing.T) { func TestLiterals(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"true", true, "true"}, {"true", true, "true"},
@@ -40,7 +41,7 @@ func TestLiterals(t *testing.T) {
func TestOperators(t *testing.T) { func TestOperators(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
error string error string
}{ }{
@@ -68,7 +69,7 @@ func TestOperators(t *testing.T) {
{`true && false`, false, "and", ""}, {`true && false`, false, "and", ""},
{`true || false`, true, "or", ""}, {`true || false`, true, "or", ""},
{`fromJSON('{}') && true`, true, "and-boolean-object", ""}, {`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.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.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", ""}, {"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{ env := &EvaluationEnvironment{
Github: &model.GithubContext{ Github: &model.GithubContext{
Action: "push", Action: "push",
Event: map[string]interface{}{ Event: map[string]any{
"commits": []interface{}{ "commits": []any{
map[string]interface{}{ map[string]any{
"author": map[string]interface{}{ "author": map[string]any{
"username": "someone", "username": "someone",
}, },
}, },
map[string]interface{}{ map[string]any{
"author": map[string]interface{}{ "author": map[string]any{
"username": "someone-else", "username": "someone-else",
}, },
}, },
@@ -114,7 +115,7 @@ func TestOperators(t *testing.T) {
func TestOperatorsCompare(t *testing.T) { func TestOperatorsCompare(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"!null", true, "not-null"}, {"!null", true, "not-null"},
@@ -162,7 +163,7 @@ func TestOperatorsCompare(t *testing.T) {
func TestOperatorsBooleanEvaluation(t *testing.T) { func TestOperatorsBooleanEvaluation(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
// true && // true &&
@@ -529,7 +530,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
func TestContexts(t *testing.T) { func TestContexts(t *testing.T) {
table := []struct { table := []struct {
input string input string
expected interface{} expected any
name string name string
}{ }{
{"github.action", "push", "github-context"}, {"github.action", "push", "github-context"},
@@ -586,7 +587,7 @@ func TestContexts(t *testing.T) {
Conclusion: model.StepStatusSkipped, Conclusion: model.StepStatusSkipped,
}, },
}, },
Runner: map[string]interface{}{ Runner: map[string]any{
"os": "Linux", "os": "Linux",
"temp": "/tmp", "temp": "/tmp",
"tool_cache": "/opt/hostedtoolcache", "tool_cache": "/opt/hostedtoolcache",
@@ -597,10 +598,10 @@ func TestContexts(t *testing.T) {
Vars: map[string]string{ Vars: map[string]string{
"name": "value", "name": "value",
}, },
Strategy: map[string]interface{}{ Strategy: map[string]any{
"fail-fast": true, "fail-fast": true,
}, },
Matrix: map[string]interface{}{ Matrix: map[string]any{
"os": "Linux", "os": "Linux",
}, },
Needs: map[string]Needs{ Needs: map[string]Needs{
@@ -611,7 +612,7 @@ func TestContexts(t *testing.T) {
Result: "success", Result: "success",
}, },
}, },
Inputs: map[string]interface{}{ Inputs: map[string]any{
"name": "value", "name": "value",
}, },
} }
+4 -4
View File
@@ -3,6 +3,7 @@ package filecollector
import ( import (
"archive/tar" "archive/tar"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
@@ -97,8 +98,7 @@ type Fs interface {
Readlink(path string) (string, error) Readlink(path string) (string, error)
} }
type DefaultFs struct { type DefaultFs struct{}
}
func (*DefaultFs) Walk(root string, fn filepath.WalkFunc) error { func (*DefaultFs) Walk(root string, fn filepath.WalkFunc) error {
return filepath.Walk(root, fn) return filepath.Walk(root, fn)
@@ -124,7 +124,7 @@ func (*DefaultFs) Readlink(path string) (string, error) {
return os.Readlink(path) return os.Readlink(path)
} }
//nolint:gocyclo //nolint:gocyclo // function handles many cases
func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc { func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc {
i, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...))) i, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...)))
return func(file string, fi os.FileInfo, err error) error { 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 { if ctx != nil {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return fmt.Errorf("copy cancelled") return errors.New("copy cancelled")
default: default:
} }
} }
+1 -1
View File
@@ -27,7 +27,7 @@ func (mfs *memoryFs) walk(root string, fn filepath.WalkFunc) error {
if err != nil { if err != nil {
return err return err
} }
for i := 0; i < len(dir); i++ { for i := range dir {
filename := filepath.Join(root, dir[i].Name()) filename := filepath.Join(root, dir[i].Name())
err = fn(filename, dir[i], nil) err = fn(filename, dir[i], nil)
if dir[i].IsDir() { if dir[i].IsDir() {
+1 -2
View File
@@ -6,8 +6,7 @@ type Env interface {
Getenv(name string) string Getenv(name string) string
} }
type defaultEnv struct { type defaultEnv struct{}
}
func (*defaultEnv) Getenv(name string) string { func (*defaultEnv) Getenv(name string) string {
return os.Getenv(name) return os.Getenv(name)
+1 -1
View File
@@ -20,7 +20,7 @@ func findExecutable(file string) error {
if err != nil { if err != nil {
return err return err
} }
if m := d.Mode(); !m.IsDir() && m&0111 != 0 { if m := d.Mode(); !m.IsDir() && m&0o111 != 0 {
return nil return nil
} }
return fs.ErrPermission return fs.ErrPermission
Loaded 50 of 89 files, more files were not shown because too many files have changed in this diff. Show more