mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-15 12:23:42 +00:00
Use golangci-lint fmt to format code (#163)
Use `golangci-lint fmt` to format code, upgrading `.golangci.yml` to v2 and mirroring the linter configuration used by https://github.com/go-gitea/gitea. `gci` now handles import ordering into standard, project-local, blank, and default groups. Mirrors https://github.com/go-gitea/gitea/pull/37194. Changes: - Upgrade `.golangci.yml` to v2 format with the same linter set as gitea (minus `prealloc`, `unparam`, `testifylint`, `nilnil` which produced too many pre-existing issues) - Add path-based exclusions (`bodyclose`, `gosec` in tests; `gosec:G115`/`G117` globally) - Run lint via `make lint-go` in CI instead of `golangci/golangci-lint-action`, matching the pattern used by other Gitea repos - Apply safe auto-fixes (`modernize`, `perfsprint`, `usetesting`, etc.) - Add explanations to existing `//nolint` directives - Remove dead code (unused `newRemoteReusableWorkflow` and `networkName`), duplicate imports, and shadowed `max` builtins - Replace deprecated `docker/distribution/reference` with `distribution/reference` - Fix `Deprecated:` comment casing and simplify nil/len checks --- This PR was written with the help of Claude Opus 4.7 Reviewed-on: https://gitea.com/gitea/act/pulls/163 Reviewed-by: Lunny Xiao <[email protected]> Co-authored-by: silverwind <[email protected]> Co-committed-by: silverwind <[email protected]>
This commit is contained in:
1 parent
3232358e71
commit
f923badec7
89 files changed
+821
-791
No files matched your search
@@ -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
@@ -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
|
||||||
@@ -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
@@ -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
@@ -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,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(¤tRunning, 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(¤tRunning, -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(¤tRunning, 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(¤tRunning, -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")
|
||||||
}
|
}
|
||||||
@@ -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(¤tRunning, 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(¤tRunning, -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(¤tRunning, 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(¤tRunning, -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(¤tRunning, 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(¤tRunning, -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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,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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 +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
|
||||||
|
|||||||
@@ -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
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"},
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
Reference in new issue
Block a user