diff --git a/.github/actions/choco/Dockerfile b/.github/actions/choco/Dockerfile index d301f0a..aabcb3a 100644 --- a/.github/actions/choco/Dockerfile +++ b/.github/actions/choco/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 +FROM alpine:3.17 ARG CHOCOVERSION=1.1.0 diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml new file mode 100644 index 0000000..09cb3da --- /dev/null +++ b/.github/actions/run-tests/action.yml @@ -0,0 +1,77 @@ +name: 'run-tests' +description: 'Runs go test and upload a step summary' +inputs: + filter: + description: 'The go test pattern for the tests to run' + required: false + default: '' + upload-logs-name: + description: 'Choose the name of the log artifact' + required: false + default: logs-${{ github.job }}-${{ strategy.job-index }} + upload-logs: + description: 'If true uploads logs of each tests as an artifact' + required: false + default: 'true' +runs: + using: composite + steps: + - uses: actions/github-script@v6 + with: + github-token: none # No reason to grant access to the GITHUB_TOKEN + script: | + let myOutput = ''; + var fs = require('fs'); + var uploadLogs = process.env.UPLOAD_LOGS === 'true'; + if(uploadLogs) { + await io.mkdirP('logs'); + } + var filename = null; + const options = {}; + options.ignoreReturnCode = true; + options.env = Object.assign({}, process.env); + delete options.env.ACTIONS_RUNTIME_URL; + delete options.env.ACTIONS_RUNTIME_TOKEN; + delete options.env.ACTIONS_CACHE_URL; + options.listeners = { + stdout: (data) => { + for(line of data.toString().split('\n')) { + if(/^\s*(===\s[^\s]+\s|---\s[^\s]+:\s)/.test(line)) { + if(uploadLogs) { + var runprefix = "=== RUN "; + if(line.startsWith(runprefix)) { + filename = "logs/" + line.substring(runprefix.length).replace(/[^A-Za-z0-9]/g, '-') + ".txt"; + fs.writeFileSync(filename, line + "\n"); + } else if(filename) { + fs.appendFileSync(filename, line + "\n"); + filename = null; + } + } + myOutput += line + "\n"; + } else if(filename) { + fs.appendFileSync(filename, line + "\n"); + } + } + } + }; + var args = ['test', '-v', '-cover', '-coverprofile=coverage.txt', '-covermode=atomic', '-timeout', '15m']; + var filter = process.env.FILTER; + if(filter) { + args.push('-run'); + args.push(filter); + } + args.push('./...'); + var exitcode = await exec.exec('go', args, options); + if(process.env.GITHUB_STEP_SUMMARY) { + core.summary.addCodeBlock(myOutput); + await core.summary.write(); + } + process.exit(exitcode); + env: + FILTER: ${{ inputs.filter }} + UPLOAD_LOGS: ${{ inputs.upload-logs }} + - uses: actions/upload-artifact@v3 + if: always() && inputs.upload-logs == 'true' && !env.ACT + with: + name: ${{ inputs.upload-logs-name }} + path: logs diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ffd9b7e..69365dd 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -19,10 +19,10 @@ jobs: with: go-version: ${{ env.GO_VERSION }} check-latest: true - - uses: golangci/golangci-lint-action@v3.3.1 + - uses: golangci/golangci-lint-action@v3.4.0 with: version: v1.47.2 - - uses: megalinter/megalinter/flavors/go@v6.15.0 + - uses: megalinter/megalinter/flavors/go@v6.20.0 env: DEFAULT_BRANCH: master GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -50,7 +50,10 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - run: go test -v -cover -coverprofile=coverage.txt -covermode=atomic -timeout 15m ./... + - name: Run Tests + uses: ./.github/actions/run-tests + with: + upload-logs-name: logs-linux - name: Upload Codecov report uses: codecov/codecov-action@v3.1.1 with: @@ -73,8 +76,11 @@ jobs: with: go-version: ${{ env.GO_VERSION }} check-latest: true - - run: go test -v -run ^TestRunEventHostEnvironment$ ./... - # TODO merge coverage with test-linux + - name: Run Tests + uses: ./.github/actions/run-tests + with: + filter: '^TestRunEventHostEnvironment$' + upload-logs-name: logs-${{ matrix.os }} snapshot: name: snapshot @@ -93,7 +99,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 with: version: latest args: release --snapshot --rm-dist diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cda8a0d..69b5aec 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 with: version: latest args: release --rm-dist @@ -39,3 +39,29 @@ jobs: version: ${{ github.ref }} apiKey: ${{ secrets.CHOCO_APIKEY }} push: true + - name: GitHub CLI extension + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GORELEASER_GITHUB_TOKEN }} + script: | + const mainRef = (await github.rest.git.getRef({ + owner: 'nektos', + repo: 'gh-act', + ref: 'heads/main', + })).data; + console.log(mainRef); + github.rest.git.createRef({ + owner: 'nektos', + repo: 'gh-act', + ref: context.ref, + sha: mainRef.object.sha, + }); + winget: + needs: release + runs-on: windows-latest # Action can only run on Windows + steps: + - uses: vedantmgoyal2009/winget-releaser@v2 + with: + identifier: nektos.act + installers-regex: '_Windows_\w+\.zip$' + token: ${{ secrets.WINGET_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index dd2f07c..9181062 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,7 +8,7 @@ jobs: name: Stale runs-on: ubuntu-latest steps: - - uses: actions/stale@v6 + - uses: actions/stale@v7 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'Issue is stale and will be closed in 14 days unless there is new activity' @@ -19,5 +19,5 @@ jobs: exempt-pr-labels: 'stale-exempt' remove-stale-when-updated: 'True' operations-per-run: 500 - days-before-stale: 30 + days-before-stale: 180 days-before-close: 14 diff --git a/.mega-linter.yml b/.mega-linter.yml index 123f16d..c17e218 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -14,7 +14,7 @@ DISABLE_LINTERS: - MARKDOWN_MARKDOWN_LINK_CHECK - REPOSITORY_CHECKOV - REPOSITORY_TRIVY -FILTER_REGEX_EXCLUDE: (.*testdata/*|install.sh|pkg/container/docker_cli.go|pkg/container/DOCKER_LICENSE) +FILTER_REGEX_EXCLUDE: (.*testdata/*|install.sh|pkg/container/docker_cli.go|pkg/container/DOCKER_LICENSE|VERSION) MARKDOWN_MARKDOWNLINT_CONFIG_FILE: .markdownlint.yml PARALLEL: false PRINT_ALPACA: false diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f4495d..3e305ae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "go.lintTool": "golangci-lint", "go.lintFlags": ["--fix"], + "go.testTimeout": "300s", "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, diff --git a/Makefile b/Makefile index bdf9385..5929090 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,11 @@ ifneq ($(shell git status -s),) @echo "Unable to promote a dirty workspace" @exit 1 endif + echo -n $(NEW_VERSION) > VERSION + git add VERSION + git commit -m "chore: bump VERSION to $(NEW_VERSION)" git tag -a -m "releasing v$(NEW_VERSION)" v$(NEW_VERSION) + git push origin master git push origin v$(NEW_VERSION) .PHONY: snapshot @@ -105,3 +109,5 @@ snapshot: --rm-dist \ --single-target \ --snapshot + +.PHONY: clean all diff --git a/README.md b/README.md index f5cc198..ee6bef9 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,14 @@ choco install act-cli scoop install act ``` +### [Winget](https://learn.microsoft.com/en-us/windows/package-manager/) (Windows) + +[![Winget package](https://repology.org/badge/version-for-repo/winget/act-run-github-actions.svg)](https://repology.org/project/act-run-github-actions/versions) + +```shell +winget install nektos.act +``` + ### [AUR](https://aur.archlinux.org/packages/act/) (Linux) [![aur-shield](https://img.shields.io/aur/version/act)](https://aur.archlinux.org/packages/act/) @@ -133,6 +141,14 @@ Using the latest [Nix command](https://nixos.wiki/wiki/Nix_command), you can run nix run nixpkgs#act ``` +## Installation as GitHub CLI extension + +Act can be installed as a [GitHub CLI](https://cli.github.com/) extension: + +```sh +gh extension install nektos/gh-act +``` + ## Other install options ### Bash script @@ -140,7 +156,7 @@ nix run nixpkgs#act Run this command in your terminal: ```shell -curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash +curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash ``` ### Manual download @@ -188,49 +204,6 @@ act -v When running `act` for the first time, it will ask you to choose image to be used as default. It will save that information to `~/.actrc`, please refer to [Configuration](#configuration) for more information about `.actrc` and to [Runners](#runners) for information about used/available Docker images. -# Flags - -```none - -a, --actor string user that triggered the event (default "nektos/act") - --replace-ghe-action-with-github-com If you are using GitHub Enterprise Server and allow specified actions from GitHub (github.com), you can set actions on this. (e.g. --replace-ghe-action-with-github-com=github/super-linter) - --replace-ghe-action-token-with-github-com If you are using replace-ghe-action-with-github-com and you want to use private actions on GitHub, you have to set personal access token - --artifact-server-path string Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start. - --artifact-server-port string Defines the port where the artifact server listens (will only bind to localhost). (default "34567") - -b, --bind bind working directory to container, rather than copy - --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms. - --container-cap-add stringArray kernel capabilities to add to the workflow containers (e.g. --container-cap-add SYS_PTRACE) - --container-cap-drop stringArray kernel capabilities to remove from the workflow containers (e.g. --container-cap-drop SYS_PTRACE) - --container-daemon-socket string Path to Docker daemon socket which will be mounted to containers (default "/var/run/docker.sock") - --defaultbranch string the name of the main branch - --detect-event Use first event type from workflow as event that triggered the workflow - -C, --directory string working directory (default ".") - -n, --dryrun dryrun mode - --env stringArray env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv) - --env-file string environment file to read and use as env in the containers (default ".env") - -e, --eventpath string path to event JSON file - --github-instance string GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server. (default "github.com") - -g, --graph draw workflows - -h, --help help for act - --insecure-secrets NOT RECOMMENDED! Doesn't hide secrets while printing logs. - -j, --job string run job - -l, --list list workflows - --no-recurse Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag - -P, --platform stringArray custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04) - --privileged use privileged mode - -p, --pull pull docker image(s) even if already present - -q, --quiet disable logging of output from steps - --rebuild rebuild local action docker image(s) even if already present - -r, --reuse don't remove container(s) on successfully completed workflow(s) to maintain state between runs - --rm automatically remove container(s)/volume(s) after a workflow(s) failure - -s, --secret stringArray secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret) - --secret-file string file with list of secrets to read from (e.g. --secret-file .secrets) (default ".secrets") - --use-gitignore Controls whether paths specified in .gitignore should be copied into container (default true) - --userns string user namespace to use - -v, --verbose verbose output - -w, --watch watch the contents of the local repo and run when files change - -W, --workflows string path to workflow file(s) (default "./.github/workflows/") -``` - ## `GITHUB_TOKEN` GitHub [automatically provides](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) a `GITHUB_TOKEN` secret when running workflows inside GitHub. @@ -367,10 +340,41 @@ MY_ENV_VAR=MY_ENV_VAR_VALUE MY_2ND_ENV_VAR="my 2nd env var value" ``` +# Skipping jobs + +You cannot use the `env` context in job level if conditions, but you can add a custom event property to the `github` context. You can use this method also on step level if conditions. + +```yml +on: push +jobs: + deploy: + if: ${{ !github.event.act }} # skip during local actions testing + runs-on: ubuntu-latest + steps: + - run: exit 0 +``` + +And use this `event.json` file with act otherwise the Job will run: + +```json +{ + "act": true +} +``` + +Run act like + +```sh +act -e event.json +``` + +_Hint: you can add / append `-e event.json` as a line into `./.actrc`_ + # Skipping steps Act adds a special environment variable `ACT` that can be used to skip a step that you don't want to run locally. E.g. a step that posts a Slack message or bumps a version number. +**You cannot use this method in job level if conditions, see [Skipping jobs](#skipping-jobs)** ```yml - name: Some step @@ -402,7 +406,7 @@ act pull_request -e pull-request.json Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected. -## Pass Inputs to Manually Triggered Workflows +# Pass Inputs to Manually Triggered Workflows Example workflow file @@ -428,6 +432,14 @@ jobs: echo "Hello ${{ github.event.inputs.NAME }} and ${{ github.event.inputs.SOME_VALUE }}!" ``` +## via input or input-file flag + +- `act --input NAME=somevalue` - use `somevalue` as the value for `NAME` input. +- `act --input-file my.input` - load input values from `my.input` file. + - input file format is the same as `.env` format + +## via JSON + Example JSON payload file conveniently named `payload.json` ```json diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..653655d --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.2.43 \ No newline at end of file diff --git a/cmd/input.go b/cmd/input.go index 2de0fd2..37655a5 100644 --- a/cmd/input.go +++ b/cmd/input.go @@ -17,12 +17,14 @@ type Input struct { bindWorkdir bool secrets []string envs []string + inputs []string platforms []string dryrun bool forcePull bool forceRebuild bool noOutput bool envfile string + inputfile string secretfile string insecureSecrets bool defaultBranch string @@ -30,6 +32,7 @@ type Input struct { usernsMode string containerArchitecture string containerDaemonSocket string + containerOptions string noWorkflowRecurse bool useGitIgnore bool githubInstance string @@ -37,6 +40,7 @@ type Input struct { containerCapDrop []string autoRemove bool artifactServerPath string + artifactServerAddr string artifactServerPort string jsonLogger bool noSkipCheckout bool @@ -83,3 +87,8 @@ func (i *Input) WorkflowsPath() string { func (i *Input) EventPath() string { return i.resolve(i.eventPath) } + +// Inputfile returns the path to the input file +func (i *Input) Inputfile() string { + return i.resolve(i.inputfile) +} diff --git a/cmd/notices.go b/cmd/notices.go new file mode 100644 index 0000000..bd03aa3 --- /dev/null +++ b/cmd/notices.go @@ -0,0 +1,150 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/mitchellh/go-homedir" + log "github.com/sirupsen/logrus" +) + +type Notice struct { + Level string `json:"level"` + Message string `json:"message"` +} + +func displayNotices(input *Input) { + select { + case notices := <-noticesLoaded: + if len(notices) > 0 { + noticeLogger := log.New() + if input.jsonLogger { + noticeLogger.SetFormatter(&log.JSONFormatter{}) + } else { + noticeLogger.SetFormatter(&log.TextFormatter{ + DisableQuote: true, + DisableTimestamp: true, + PadLevelText: true, + }) + } + + fmt.Printf("\n") + for _, notice := range notices { + level, err := log.ParseLevel(notice.Level) + if err != nil { + level = log.InfoLevel + } + noticeLogger.Log(level, notice.Message) + } + } + case <-time.After(time.Second * 1): + log.Debugf("Timeout waiting for notices") + } +} + +var noticesLoaded = make(chan []Notice) + +func loadVersionNotices(version string) { + go func() { + noticesLoaded <- getVersionNotices(version) + }() +} + +const NoticeURL = "https://api.nektosact.com/notices" + +func getVersionNotices(version string) []Notice { + if os.Getenv("ACT_DISABLE_VERSION_CHECK") == "1" { + return nil + } + + noticeURL, err := url.Parse(NoticeURL) + if err != nil { + log.Error(err) + return nil + } + query := noticeURL.Query() + query.Add("os", runtime.GOOS) + query.Add("arch", runtime.GOARCH) + query.Add("version", version) + + noticeURL.RawQuery = query.Encode() + + client := &http.Client{} + req, err := http.NewRequest("GET", noticeURL.String(), nil) + if err != nil { + log.Debug(err) + return nil + } + + etag := loadNoticesEtag() + if etag != "" { + log.Debugf("Conditional GET for notices etag=%s", etag) + req.Header.Set("If-None-Match", etag) + } + + resp, err := client.Do(req) + if err != nil { + log.Debug(err) + return nil + } + + newEtag := resp.Header.Get("Etag") + if newEtag != "" { + log.Debugf("Saving notices etag=%s", newEtag) + saveNoticesEtag(newEtag) + } + + defer resp.Body.Close() + notices := []Notice{} + if resp.StatusCode == 304 { + log.Debug("No new notices") + return nil + } + if err := json.NewDecoder(resp.Body).Decode(¬ices); err != nil { + log.Debug(err) + return nil + } + + return notices +} + +func loadNoticesEtag() string { + p := etagPath() + content, err := os.ReadFile(p) + if err != nil { + log.Debugf("Unable to load etag from %s: %e", p, err) + } + return strings.TrimSuffix(string(content), "\n") +} + +func saveNoticesEtag(etag string) { + p := etagPath() + err := os.WriteFile(p, []byte(strings.TrimSuffix(etag, "\n")), 0o600) + if err != nil { + log.Debugf("Unable to save etag to %s: %e", p, err) + } +} + +func etagPath() string { + var xdgCache string + var ok bool + if xdgCache, ok = os.LookupEnv("XDG_CACHE_HOME"); !ok || xdgCache == "" { + if home, err := homedir.Dir(); err == nil { + xdgCache = filepath.Join(home, ".cache") + } else if xdgCache, err = filepath.Abs("."); err != nil { + log.Fatal(err) + } + } + dir := filepath.Join(xdgCache, "act") + if err := os.MkdirAll(dir, 0o777); err != nil { + log.Fatal(err) + } + return filepath.Join(dir, ".notices.etag") +} diff --git a/cmd/root.go b/cmd/root.go index 3073b0f..e5c0479 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" + "github.com/adrg/xdg" "github.com/andreaskoch/go-fswatch" "github.com/joho/godotenv" "github.com/mitchellh/go-homedir" @@ -30,13 +31,14 @@ import ( func Execute(ctx context.Context, version string) { input := new(Input) var rootCmd = &cobra.Command{ - Use: "act [event name to run] [flags]\n\nIf no event name passed, will default to \"on: push\"\nIf actions handles only one event it will be used as default instead of \"on: push\"", - Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.", - Args: cobra.MaximumNArgs(1), - RunE: newRunCommand(ctx, input), - PersistentPreRun: setupLogging, - Version: version, - SilenceUsage: true, + Use: "act [event name to run] [flags]\n\nIf no event name passed, will default to \"on: push\"\nIf actions handles only one event it will be used as default instead of \"on: push\"", + Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.", + Args: cobra.MaximumNArgs(1), + RunE: newRunCommand(ctx, input), + PersistentPreRun: setup(input), + PersistentPostRun: cleanup(input), + Version: version, + SilenceUsage: true, } rootCmd.Flags().BoolP("watch", "w", false, "watch the contents of the local repo and run when files change") rootCmd.Flags().BoolP("list", "l", false, "list workflows") @@ -47,11 +49,12 @@ func Execute(ctx context.Context, version string) { rootCmd.Flags().StringVar(&input.remoteName, "remote-name", "origin", "git remote name that will be used to retrieve url of git repo") rootCmd.Flags().StringArrayVarP(&input.secrets, "secret", "s", []string{}, "secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)") rootCmd.Flags().StringArrayVarP(&input.envs, "env", "", []string{}, "env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv)") + rootCmd.Flags().StringArrayVarP(&input.inputs, "input", "", []string{}, "action input to make available to actions (e.g. --input myinput=foo)") rootCmd.Flags().StringArrayVarP(&input.platforms, "platform", "P", []string{}, "custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)") rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "don't remove container(s) on successfully completed workflow(s) to maintain state between runs") rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy") - rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) even if already present") - rootCmd.Flags().BoolVarP(&input.forceRebuild, "rebuild", "", false, "rebuild local action docker image(s) even if already present") + rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", true, "pull docker image(s) even if already present") + rootCmd.Flags().BoolVarP(&input.forceRebuild, "rebuild", "", true, "rebuild local action docker image(s) even if already present") rootCmd.Flags().BoolVarP(&input.autodetectEvent, "detect-event", "", false, "Use first event type from workflow as event that triggered the workflow") rootCmd.Flags().StringVarP(&input.eventPath, "eventpath", "e", "", "path to event JSON file") rootCmd.Flags().StringVar(&input.defaultBranch, "defaultbranch", "", "the name of the main branch") @@ -74,11 +77,14 @@ func Execute(ctx context.Context, version string) { rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)") rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.") rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers") + rootCmd.PersistentFlags().StringVarP(&input.inputfile, "input-file", "", ".input", "input file to read and use as action input") rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.") rootCmd.PersistentFlags().StringVarP(&input.containerDaemonSocket, "container-daemon-socket", "", "/var/run/docker.sock", "Path to Docker daemon socket which will be mounted to containers") + rootCmd.PersistentFlags().StringVarP(&input.containerOptions, "container-options", "", "", "Custom docker container options for the job container without an options property in the job definition") rootCmd.PersistentFlags().StringVarP(&input.githubInstance, "github-instance", "", "github.com", "GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server.") rootCmd.PersistentFlags().StringVarP(&input.artifactServerPath, "artifact-server-path", "", "", "Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start.") - rootCmd.PersistentFlags().StringVarP(&input.artifactServerPort, "artifact-server-port", "", "34567", "Defines the port where the artifact server listens (will only bind to localhost).") + rootCmd.PersistentFlags().StringVarP(&input.artifactServerAddr, "artifact-server-addr", "", common.GetOutboundIP().String(), "Defines the address to which the artifact server binds.") + rootCmd.PersistentFlags().StringVarP(&input.artifactServerPort, "artifact-server-port", "", "34567", "Defines the port where the artifact server listens.") rootCmd.PersistentFlags().BoolVarP(&input.noSkipCheckout, "no-skip-checkout", "", false, "Do not skip actions/checkout") rootCmd.SetArgs(args()) @@ -93,18 +99,21 @@ func configLocations() []string { log.Fatal(err) } + configFileName := ".actrc" + // reference: https://specifications.freedesktop.org/basedir-spec/latest/ar01s03.html var actrcXdg string - if xdg, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok && xdg != "" { - actrcXdg = filepath.Join(xdg, ".actrc") - } else { - actrcXdg = filepath.Join(home, ".config", ".actrc") + for _, fileName := range []string{"act/actrc", configFileName} { + if foundConfig, err := xdg.SearchConfigFile(fileName); foundConfig != "" && err == nil { + actrcXdg = foundConfig + break + } } return []string{ - filepath.Join(home, ".actrc"), + filepath.Join(home, configFileName), actrcXdg, - filepath.Join(".", ".actrc"), + filepath.Join(".", configFileName), } } @@ -241,13 +250,37 @@ func readArgsFile(file string, split bool) []string { return args } -func setupLogging(cmd *cobra.Command, _ []string) { - verbose, _ := cmd.Flags().GetBool("verbose") - if verbose { - log.SetLevel(log.DebugLevel) +func setup(inputs *Input) func(*cobra.Command, []string) { + return func(cmd *cobra.Command, _ []string) { + verbose, _ := cmd.Flags().GetBool("verbose") + if verbose { + log.SetLevel(log.DebugLevel) + } + loadVersionNotices(cmd.Version) } } +func cleanup(inputs *Input) func(*cobra.Command, []string) { + return func(cmd *cobra.Command, _ []string) { + displayNotices(inputs) + } +} + +func parseEnvs(env []string, envs map[string]string) bool { + if env != nil { + for _, envVar := range env { + e := strings.SplitN(envVar, `=`, 2) + if len(e) == 2 { + envs[e[0]] = e[1] + } else { + envs[e[0]] = "" + } + } + return true + } + return false +} + func readEnvs(path string, envs map[string]string) bool { if _, err := os.Stat(path); err == nil { env, err := godotenv.Read(path) @@ -284,18 +317,14 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str log.Debugf("Loading environment from %s", input.Envfile()) envs := make(map[string]string) - if input.envs != nil { - for _, envVar := range input.envs { - e := strings.SplitN(envVar, `=`, 2) - if len(e) == 2 { - envs[e[0]] = e[1] - } else { - envs[e[0]] = "" - } - } - } + _ = parseEnvs(input.envs, envs) _ = readEnvs(input.Envfile(), envs) + log.Debugf("Loading action inputs from %s", input.Inputfile()) + inputs := make(map[string]string) + _ = parseEnvs(input.inputs, inputs) + _ = readEnvs(input.Inputfile(), inputs) + log.Debugf("Loading secrets from %s", input.Secretfile()) secrets := newSecrets(input.secrets) _ = readEnvs(input.Secretfile(), secrets) @@ -329,7 +358,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str var filterPlan *model.Plan // Determine the event name to be filtered - var filterEventName string = "" + var filterEventName string if len(args) > 0 { log.Debugf("Using first passed in arguments event for filtering: %s", args[0]) @@ -341,23 +370,35 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str filterEventName = events[0] } + var plannerErr error if jobID != "" { log.Debugf("Preparing plan with a job: %s", jobID) - filterPlan = planner.PlanJob(jobID) + filterPlan, plannerErr = planner.PlanJob(jobID) } else if filterEventName != "" { log.Debugf("Preparing plan for a event: %s", filterEventName) - filterPlan = planner.PlanEvent(filterEventName) + filterPlan, plannerErr = planner.PlanEvent(filterEventName) } else { log.Debugf("Preparing plan with all jobs") - filterPlan = planner.PlanAll() + filterPlan, plannerErr = planner.PlanAll() + } + if filterPlan == nil && plannerErr != nil { + return plannerErr } if list { - return printList(filterPlan) + err = printList(filterPlan) + if err != nil { + return err + } + return plannerErr } if graph { - return drawGraph(filterPlan) + err = drawGraph(filterPlan) + if err != nil { + return err + } + return plannerErr } // plan with triggered jobs @@ -385,10 +426,13 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str // build the plan for this run if jobID != "" { log.Debugf("Planning job: %s", jobID) - plan = planner.PlanJob(jobID) + plan, plannerErr = planner.PlanJob(jobID) } else { log.Debugf("Planning jobs for event: %s", eventName) - plan = planner.PlanEvent(eventName) + plan, plannerErr = planner.PlanEvent(eventName) + } + if plan == nil && plannerErr != nil { + return plannerErr } // check to see if the main branch was defined @@ -414,6 +458,19 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str input.platforms = readArgsFile(cfgLocations[0], true) } } + deprecationWarning := "--%s is deprecated and will be removed soon, please switch to cli: `--container-options \"%[2]s\"` or `.actrc`: `--container-options %[2]s`." + if input.privileged { + log.Warnf(deprecationWarning, "privileged", "--privileged") + } + if len(input.usernsMode) > 0 { + log.Warnf(deprecationWarning, "userns", fmt.Sprintf("--userns=%s", input.usernsMode)) + } + if len(input.containerCapAdd) > 0 { + log.Warnf(deprecationWarning, "container-cap-add", fmt.Sprintf("--cap-add=%s", input.containerCapAdd)) + } + if len(input.containerCapDrop) > 0 { + log.Warnf(deprecationWarning, "container-cap-drop", fmt.Sprintf("--cap-drop=%s", input.containerCapDrop)) + } // run the plan config := &runner.Config{ @@ -430,6 +487,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str JSONLogger: input.jsonLogger, Env: envs, Secrets: secrets, + Inputs: inputs, Token: secrets["GITHUB_TOKEN"], InsecureSecrets: input.insecureSecrets, Platforms: input.newPlatforms(), @@ -437,12 +495,14 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str UsernsMode: input.usernsMode, ContainerArchitecture: input.containerArchitecture, ContainerDaemonSocket: input.containerDaemonSocket, + ContainerOptions: input.containerOptions, UseGitIgnore: input.useGitIgnore, GitHubInstance: input.githubInstance, ContainerCapAdd: input.containerCapAdd, ContainerCapDrop: input.containerCapDrop, AutoRemove: input.autoRemove, ArtifactServerPath: input.artifactServerPath, + ArtifactServerAddr: input.artifactServerAddr, ArtifactServerPort: input.artifactServerPort, NoSkipCheckout: input.noSkipCheckout, RemoteName: input.remoteName, @@ -454,20 +514,28 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str return err } - cancel := artifacts.Serve(ctx, input.artifactServerPath, input.artifactServerPort) + cancel := artifacts.Serve(ctx, input.artifactServerPath, input.artifactServerAddr, input.artifactServerPort) ctx = common.WithDryrun(ctx, input.dryrun) if watch, err := cmd.Flags().GetBool("watch"); err != nil { return err } else if watch { - return watchAndRun(ctx, r.NewPlanExecutor(plan)) + err = watchAndRun(ctx, r.NewPlanExecutor(plan)) + if err != nil { + return err + } + return plannerErr } executor := r.NewPlanExecutor(plan).Finally(func(ctx context.Context) error { cancel() return nil }) - return executor(ctx) + err = executor(ctx) + if err != nil { + return err + } + return plannerErr } } @@ -492,7 +560,7 @@ func defaultImageSurvey(actrc string) error { case "Medium": option = "-P ubuntu-latest=catthehacker/ubuntu:act-latest\n-P ubuntu-22.04=catthehacker/ubuntu:act-22.04\n-P ubuntu-20.04=catthehacker/ubuntu:act-20.04\n-P ubuntu-18.04=catthehacker/ubuntu:act-18.04\n" case "Micro": - option = "-P ubuntu-latest=node:16-buster-slim\n-P -P ubuntu-22.04=node:16-bullseye-slim\n ubuntu-20.04=node:16-buster-slim\n-P ubuntu-18.04=node:16-buster-slim\n" + option = "-P ubuntu-latest=node:16-buster-slim\n-P ubuntu-22.04=node:16-bullseye-slim\n-P ubuntu-20.04=node:16-buster-slim\n-P ubuntu-18.04=node:16-buster-slim\n" } f, err := os.Create(actrc) diff --git a/go.mod b/go.mod index 3185bf5..86b2f04 100644 --- a/go.mod +++ b/go.mod @@ -5,79 +5,77 @@ go 1.18 require ( github.com/AlecAivazis/survey/v2 v2.3.6 github.com/Masterminds/semver v1.5.0 + github.com/adrg/xdg v0.4.0 github.com/andreaskoch/go-fswatch v1.0.0 github.com/creack/pty v1.1.18 - github.com/docker/cli v20.10.21+incompatible + github.com/docker/cli v23.0.1+incompatible github.com/docker/distribution v2.8.1+incompatible - github.com/docker/docker v20.10.21+incompatible + github.com/docker/docker v23.0.1+incompatible github.com/docker/go-connections v0.4.0 - github.com/go-git/go-billy/v5 v5.3.1 + github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.4.2 - github.com/go-ini/ini v1.67.0 github.com/imdario/mergo v0.3.13 - github.com/joho/godotenv v1.4.0 + github.com/joho/godotenv v1.5.1 github.com/julienschmidt/httprouter v1.3.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.17 github.com/mitchellh/go-homedir v1.1.0 - github.com/moby/buildkit v0.10.6 - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 - github.com/opencontainers/selinux v1.10.2 + github.com/moby/buildkit v0.11.4 + github.com/moby/patternmatcher v0.5.0 + github.com/opencontainers/image-spec v1.1.0-rc2 + github.com/opencontainers/selinux v1.11.0 github.com/pkg/errors v0.9.1 - github.com/rhysd/actionlint v1.6.22 + github.com/rhysd/actionlint v1.6.23 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.1 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 + golang.org/x/term v0.6.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.4.0 ) require ( github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/Microsoft/hcsshim v0.9.3 // indirect github.com/ProtonMail/go-crypto v0.0.0-20220404123522-616f957b79ad // indirect github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/containerd/cgroups v1.0.3 // indirect - github.com/containerd/containerd v1.6.6 // indirect + github.com/containerd/containerd v1.6.18 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/docker-credential-helpers v0.6.4 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.15.12 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/moby/sys/mount v0.3.1 // indirect - github.com/moby/sys/mountinfo v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/term v0.0.0-20200312100748-672ec06f55cd // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/runc v1.1.2 // indirect + github.com/opencontainers/runc v1.1.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.3.4 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f // indirect - go.opencensus.io v0.23.0 // indirect - golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect - golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect - golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect - golang.org/x/text v0.3.7 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/crypto v0.2.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.7.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 492e2b0..c02c32d 100644 --- a/go.sum +++ b/go.sum @@ -1,480 +1,101 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= -github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220404123522-616f957b79ad h1:K3cVQxnwoVf5R2XLZknct3+tJWocEuJUmF7ZGwB2FK8= github.com/ProtonMail/go-crypto v0.0.0-20220404123522-616f957b79ad/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/ZauberNerd/go-git/v5 v5.4.3-0.20220315170230-29ec1bc1e5db h1:b0xyxkCQ0PQEH7gFQ8D+xa9lb+bur6RgVsRBodaqza4= github.com/ZauberNerd/go-git/v5 v5.4.3-0.20220315170230-29ec1bc1e5db/go.mod h1:U7oc8MDRtQhVD6StooNkBMVsh/Y4J/2Vl36Mo4IclvM= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/andreaskoch/go-fswatch v1.0.0 h1:la8nP/HiaFCxP2IM6NZNUCoxgLWuyNFgH0RligBbnJU= github.com/andreaskoch/go-fswatch v1.0.0/go.mod h1:r5/iV+4jfwoY2sYqBkg8vpF04ehOvEl4qPptVGdxmqo= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= -github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.3-0.20220330195504-d132b287edc8 h1:yGFEcFNMhze29DxAAB33v/1OMRYF/cM9iwwgV2P0ZrE= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= +github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= -github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= -github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= +github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -482,667 +103,200 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/buildkit v0.10.6 h1:DJlEuLIgnu34HQKF4n9Eg6q2YqQVC0eOpMb4p2eRS2w= -github.com/moby/buildkit v0.10.6/go.mod h1:tQuuyTWtOb9D+RE425cwOCUkX0/oZ+5iBZ+uWpWQ9bU= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/sys/mount v0.3.1 h1:RX1K0x95oR8j5P1YefKDt7tE1C2kCCixV0H8Aza3GaI= -github.com/moby/sys/mount v0.3.1/go.mod h1:6IZknFQiqjLpwuYJD5Zk0qYEuJiws36M88MIXnZHya0= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/buildkit v0.11.4 h1:mleVHr+n7HUD65QNUkgkT3d8muTzhYUoHE9FM3Ej05s= +github.com/moby/buildkit v0.11.4/go.mod h1:P5Qi041LvCfhkfYBHry+Rwoo3Wi6H971J2ggE+PcIoo= +github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.0 h1:gUDhXQx58YNrpHlK4nSL+7y2pxFZkUcXqzFDKWdC0Oo= -github.com/moby/sys/mountinfo v0.6.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= -github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opencontainers/selinux v1.10.2 h1:NFy2xCsjn7+WspbfZkUd5zyVeisV7VFbPSP96+8/ha4= -github.com/opencontainers/selinux v1.10.2/go.mod h1:cARutUbaUrlRClyvxOICCgKixCs6L05aUsohzA3EkHQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rhysd/actionlint v1.6.22 h1:cAEf2PGNwJXhdcTVF2xS/0ORqWS+ueUHwjQYsqFsGSk= -github.com/rhysd/actionlint v1.6.22/go.mod h1:gIKOdxtV40mBOcD0ZR8EBa8NqjEXToAZioroS3oedMg= +github.com/rhysd/actionlint v1.6.23 h1:041VOXgZddfvSJa9Il+WT3Iwuo/j0Nmu4bhpAScrds4= +github.com/rhysd/actionlint v1.6.23/go.mod h1:o5qc1K3I9taGMBhL7mVkpRd64hx3YqI+3t8ewGfYXfE= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f h1:mvXjJIHRZyhNuGassLTcXTwjiWq7NmjdavZsUnmFybQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= -golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -1152,52 +306,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/main.go b/main.go index 41cf7c4..37b0fec 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "context" + _ "embed" "os" "os/signal" "syscall" @@ -9,7 +10,8 @@ import ( "github.com/nektos/act/cmd" ) -var version = "v0.2.27-dev" // Manually bump after tagging next release +//go:embed VERSION +var version string func main() { ctx := context.Background() diff --git a/pkg/artifacts/server.go b/pkg/artifacts/server.go index 06a7706..d0c7a6a 100644 --- a/pkg/artifacts/server.go +++ b/pkg/artifacts/server.go @@ -9,12 +9,12 @@ import ( "io/fs" "net/http" "os" - "path" "path/filepath" "strings" "time" "github.com/julienschmidt/httprouter" + "github.com/nektos/act/pkg/common" ) @@ -46,28 +46,34 @@ type ResponseMessage struct { Message string `json:"message"` } -type MkdirFS interface { - fs.FS - MkdirAll(path string, perm fs.FileMode) error - Open(name string) (fs.File, error) - OpenAtEnd(name string) (fs.File, error) +type WritableFile interface { + io.WriteCloser } -type MkdirFsImpl struct { - dir string - fs.FS +type WriteFS interface { + OpenWritable(name string) (WritableFile, error) + OpenAppendable(name string) (WritableFile, error) } -func (fsys MkdirFsImpl) MkdirAll(path string, perm fs.FileMode) error { - return os.MkdirAll(fsys.dir+"/"+path, perm) +type readWriteFSImpl struct { } -func (fsys MkdirFsImpl) Open(name string) (fs.File, error) { - return os.OpenFile(fsys.dir+"/"+name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) +func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) { + return os.Open(name) } -func (fsys MkdirFsImpl) OpenAtEnd(name string) (fs.File, error) { - file, err := os.OpenFile(fsys.dir+"/"+name, os.O_CREATE|os.O_RDWR, 0644) +func (fwfs readWriteFSImpl) OpenWritable(name string) (WritableFile, error) { + if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil { + return nil, err + } + return os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o644) +} + +func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) { + if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil { + return nil, err + } + file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return nil, err @@ -77,13 +83,16 @@ func (fsys MkdirFsImpl) OpenAtEnd(name string) (fs.File, error) { if err != nil { return nil, err } - return file, nil } var gzipExtension = ".gz__" -func uploads(router *httprouter.Router, fsys MkdirFS) { +func safeResolve(baseDir string, relPath string) string { + return filepath.Join(baseDir, filepath.Clean(filepath.Join(string(os.PathSeparator), relPath))) +} + +func uploads(router *httprouter.Router, baseDir string, fsys WriteFS) { router.POST("/_apis/pipelines/workflows/:runId/artifacts", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { runID := params.ByName("runId") @@ -108,19 +117,15 @@ func uploads(router *httprouter.Router, fsys MkdirFS) { itemPath += gzipExtension } - filePath := fmt.Sprintf("%s/%s", runID, itemPath) + safeRunPath := safeResolve(baseDir, runID) + safePath := safeResolve(safeRunPath, itemPath) - err := fsys.MkdirAll(path.Dir(filePath), os.ModePerm) - if err != nil { - panic(err) - } - - file, err := func() (fs.File, error) { + file, err := func() (WritableFile, error) { contentRange := req.Header.Get("Content-Range") if contentRange != "" && !strings.HasPrefix(contentRange, "bytes 0-") { - return fsys.OpenAtEnd(filePath) + return fsys.OpenAppendable(safePath) } - return fsys.Open(filePath) + return fsys.OpenWritable(safePath) }() if err != nil { @@ -170,11 +175,13 @@ func uploads(router *httprouter.Router, fsys MkdirFS) { }) } -func downloads(router *httprouter.Router, fsys fs.FS) { +func downloads(router *httprouter.Router, baseDir string, fsys fs.FS) { router.GET("/_apis/pipelines/workflows/:runId/artifacts", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { runID := params.ByName("runId") - entries, err := fs.ReadDir(fsys, runID) + safePath := safeResolve(baseDir, runID) + + entries, err := fs.ReadDir(fsys, safePath) if err != nil { panic(err) } @@ -204,12 +211,12 @@ func downloads(router *httprouter.Router, fsys fs.FS) { router.GET("/download/:container", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { container := params.ByName("container") itemPath := req.URL.Query().Get("itemPath") - dirPath := fmt.Sprintf("%s/%s", container, itemPath) + safePath := safeResolve(baseDir, filepath.Join(container, itemPath)) var files []ContainerItem - err := fs.WalkDir(fsys, dirPath, func(path string, entry fs.DirEntry, err error) error { + err := fs.WalkDir(fsys, safePath, func(path string, entry fs.DirEntry, err error) error { if !entry.IsDir() { - rel, err := filepath.Rel(dirPath, path) + rel, err := filepath.Rel(safePath, path) if err != nil { panic(err) } @@ -218,7 +225,7 @@ func downloads(router *httprouter.Router, fsys fs.FS) { rel = strings.TrimSuffix(rel, gzipExtension) files = append(files, ContainerItem{ - Path: fmt.Sprintf("%s/%s", itemPath, rel), + Path: filepath.Join(itemPath, rel), ItemType: "file", ContentLocation: fmt.Sprintf("http://%s/artifact/%s/%s/%s", req.Host, container, itemPath, rel), }) @@ -245,10 +252,12 @@ func downloads(router *httprouter.Router, fsys fs.FS) { router.GET("/artifact/*path", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { path := params.ByName("path")[1:] - file, err := fsys.Open(path) + safePath := safeResolve(baseDir, path) + + file, err := fsys.Open(safePath) if err != nil { // try gzip file - file, err = fsys.Open(path + gzipExtension) + file, err = fsys.Open(safePath + gzipExtension) if err != nil { panic(err) } @@ -262,7 +271,7 @@ func downloads(router *httprouter.Router, fsys fs.FS) { }) } -func Serve(ctx context.Context, artifactPath string, port string) context.CancelFunc { +func Serve(ctx context.Context, artifactPath string, addr string, port string) context.CancelFunc { serverContext, cancel := context.WithCancel(ctx) logger := common.Logger(serverContext) @@ -273,20 +282,19 @@ func Serve(ctx context.Context, artifactPath string, port string) context.Cancel router := httprouter.New() logger.Debugf("Artifacts base path '%s'", artifactPath) - fs := os.DirFS(artifactPath) - uploads(router, MkdirFsImpl{artifactPath, fs}) - downloads(router, fs) - ip := common.GetOutboundIP().String() + fsys := readWriteFSImpl{} + uploads(router, artifactPath, fsys) + downloads(router, artifactPath, fsys) server := &http.Server{ - Addr: fmt.Sprintf("%s:%s", ip, port), + Addr: fmt.Sprintf("%s:%s", addr, port), ReadHeaderTimeout: 2 * time.Second, Handler: router, } // run server go func() { - logger.Infof("Start server on http://%s:%s", ip, port) + logger.Infof("Start server on http://%s:%s", addr, port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Fatal(err) } diff --git a/pkg/artifacts/server_test.go b/pkg/artifacts/server_test.go index f1c09a3..943820c 100644 --- a/pkg/artifacts/server_test.go +++ b/pkg/artifacts/server_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/fs" "net/http" "net/http/httptest" "os" @@ -21,44 +20,43 @@ import ( "github.com/stretchr/testify/assert" ) -type MapFsImpl struct { - fstest.MapFS +type writableMapFile struct { + fstest.MapFile } -func (fsys MapFsImpl) MkdirAll(path string, perm fs.FileMode) error { - // mocked no-op - return nil -} - -type WritableFile struct { - fs.File - fsys fstest.MapFS - path string -} - -func (file WritableFile) Write(data []byte) (int, error) { - file.fsys[file.path].Data = data +func (f *writableMapFile) Write(data []byte) (int, error) { + f.Data = data return len(data), nil } -func (fsys MapFsImpl) Open(path string) (fs.File, error) { - var file = fstest.MapFile{ - Data: []byte("content2"), - } - fsys.MapFS[path] = &file - - result, err := fsys.MapFS.Open(path) - return WritableFile{result, fsys.MapFS, path}, err +func (f *writableMapFile) Close() error { + return nil } -func (fsys MapFsImpl) OpenAtEnd(path string) (fs.File, error) { - var file = fstest.MapFile{ - Data: []byte("content2"), - } - fsys.MapFS[path] = &file +type writeMapFS struct { + fstest.MapFS +} - result, err := fsys.MapFS.Open(path) - return WritableFile{result, fsys.MapFS, path}, err +func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) { + var file = &writableMapFile{ + MapFile: fstest.MapFile{ + Data: []byte("content2"), + }, + } + fsys.MapFS[name] = &file.MapFile + + return file, nil +} + +func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) { + var file = &writableMapFile{ + MapFile: fstest.MapFile{ + Data: []byte("content2"), + }, + } + fsys.MapFS[name] = &file.MapFile + + return file, nil } func TestNewArtifactUploadPrepare(t *testing.T) { @@ -67,7 +65,7 @@ func TestNewArtifactUploadPrepare(t *testing.T) { var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) router := httprouter.New() - uploads(router, MapFsImpl{memfs}) + uploads(router, "artifact/server/path", writeMapFS{memfs}) req, _ := http.NewRequest("POST", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil) rr := httptest.NewRecorder() @@ -93,7 +91,7 @@ func TestArtifactUploadBlob(t *testing.T) { var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) router := httprouter.New() - uploads(router, MapFsImpl{memfs}) + uploads(router, "artifact/server/path", writeMapFS{memfs}) req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=some/file", strings.NewReader("content")) rr := httptest.NewRecorder() @@ -111,7 +109,7 @@ func TestArtifactUploadBlob(t *testing.T) { } assert.Equal("success", response.Message) - assert.Equal("content", string(memfs["1/some/file"].Data)) + assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data)) } func TestFinalizeArtifactUpload(t *testing.T) { @@ -120,7 +118,7 @@ func TestFinalizeArtifactUpload(t *testing.T) { var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) router := httprouter.New() - uploads(router, MapFsImpl{memfs}) + uploads(router, "artifact/server/path", writeMapFS{memfs}) req, _ := http.NewRequest("PATCH", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil) rr := httptest.NewRecorder() @@ -144,13 +142,13 @@ func TestListArtifacts(t *testing.T) { assert := assert.New(t) var memfs = fstest.MapFS(map[string]*fstest.MapFile{ - "1/file.txt": { + "artifact/server/path/1/file.txt": { Data: []byte(""), }, }) router := httprouter.New() - downloads(router, memfs) + downloads(router, "artifact/server/path", memfs) req, _ := http.NewRequest("GET", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil) rr := httptest.NewRecorder() @@ -176,13 +174,13 @@ func TestListArtifactContainer(t *testing.T) { assert := assert.New(t) var memfs = fstest.MapFS(map[string]*fstest.MapFile{ - "1/some/file": { + "artifact/server/path/1/some/file": { Data: []byte(""), }, }) router := httprouter.New() - downloads(router, memfs) + downloads(router, "artifact/server/path", memfs) req, _ := http.NewRequest("GET", "http://localhost/download/1?itemPath=some/file", nil) rr := httptest.NewRecorder() @@ -200,7 +198,7 @@ func TestListArtifactContainer(t *testing.T) { } assert.Equal(1, len(response.Value)) - assert.Equal("some/file/.", response.Value[0].Path) + assert.Equal("some/file", response.Value[0].Path) assert.Equal("file", response.Value[0].ItemType) assert.Equal("http://localhost/artifact/1/some/file/.", response.Value[0].ContentLocation) } @@ -209,13 +207,13 @@ func TestDownloadArtifactFile(t *testing.T) { assert := assert.New(t) var memfs = fstest.MapFS(map[string]*fstest.MapFile{ - "1/some/file": { + "artifact/server/path/1/some/file": { Data: []byte("content"), }, }) router := httprouter.New() - downloads(router, memfs) + downloads(router, "artifact/server/path", memfs) req, _ := http.NewRequest("GET", "http://localhost/artifact/1/some/file", nil) rr := httptest.NewRecorder() @@ -240,7 +238,8 @@ type TestJobFileInfo struct { containerArchitecture string } -var aritfactsPath = path.Join(os.TempDir(), "test-artifacts") +var artifactsPath = path.Join(os.TempDir(), "test-artifacts") +var artifactsAddr = "127.0.0.1" var artifactsPort = "12345" func TestArtifactFlow(t *testing.T) { @@ -250,7 +249,7 @@ func TestArtifactFlow(t *testing.T) { ctx := context.Background() - cancel := Serve(ctx, aritfactsPath, artifactsPort) + cancel := Serve(ctx, artifactsPath, artifactsAddr, artifactsPort) defer cancel() platforms := map[string]string{ @@ -259,6 +258,7 @@ func TestArtifactFlow(t *testing.T) { tables := []TestJobFileInfo{ {"testdata", "upload-and-download", "push", "", platforms, ""}, + {"testdata", "GHSL-2023-004", "push", "", platforms, ""}, } log.SetLevel(log.DebugLevel) @@ -271,7 +271,7 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) { t.Run(tjfi.workflowPath, func(t *testing.T) { fmt.Printf("::group::%s\n", tjfi.workflowPath) - if err := os.RemoveAll(aritfactsPath); err != nil { + if err := os.RemoveAll(artifactsPath); err != nil { panic(err) } @@ -286,7 +286,8 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) { ReuseContainers: false, ContainerArchitecture: tjfi.containerArchitecture, GitHubInstance: "github.com", - ArtifactServerPath: aritfactsPath, + ArtifactServerPath: artifactsPath, + ArtifactServerAddr: artifactsAddr, ArtifactServerPort: artifactsPort, } @@ -296,15 +297,96 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) { planner, err := model.NewWorkflowPlanner(fullWorkflowPath, true) assert.Nil(t, err, fullWorkflowPath) - plan := planner.PlanEvent(tjfi.eventName) - - err = runner.NewPlanExecutor(plan)(ctx) - if tjfi.errorMessage == "" { - assert.Nil(t, err, fullWorkflowPath) + plan, err := planner.PlanEvent(tjfi.eventName) + if err == nil { + err = runner.NewPlanExecutor(plan)(ctx) + if tjfi.errorMessage == "" { + assert.Nil(t, err, fullWorkflowPath) + } else { + assert.Error(t, err, tjfi.errorMessage) + } } else { - assert.Error(t, err, tjfi.errorMessage) + assert.Nil(t, plan) } fmt.Println("::endgroup::") }) } + +func TestMkdirFsImplSafeResolve(t *testing.T) { + assert := assert.New(t) + + baseDir := "/foo/bar" + + tests := map[string]struct { + input string + want string + }{ + "simple": {input: "baz", want: "/foo/bar/baz"}, + "nested": {input: "baz/blue", want: "/foo/bar/baz/blue"}, + "dots in middle": {input: "baz/../../blue", want: "/foo/bar/blue"}, + "leading dots": {input: "../../parent", want: "/foo/bar/parent"}, + "root path": {input: "/root", want: "/foo/bar/root"}, + "root": {input: "/", want: "/foo/bar"}, + "empty": {input: "", want: "/foo/bar"}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(tc.want, safeResolve(baseDir, tc.input)) + }) + } +} + +func TestDownloadArtifactFileUnsafePath(t *testing.T) { + assert := assert.New(t) + + var memfs = fstest.MapFS(map[string]*fstest.MapFile{ + "artifact/server/path/some/file": { + Data: []byte("content"), + }, + }) + + router := httprouter.New() + downloads(router, "artifact/server/path", memfs) + + req, _ := http.NewRequest("GET", "http://localhost/artifact/2/../../some/file", nil) + rr := httptest.NewRecorder() + + router.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + assert.FailNow(fmt.Sprintf("Wrong status: %d", status)) + } + + data := rr.Body.Bytes() + + assert.Equal("content", string(data)) +} + +func TestArtifactUploadBlobUnsafePath(t *testing.T) { + assert := assert.New(t) + + var memfs = fstest.MapFS(map[string]*fstest.MapFile{}) + + router := httprouter.New() + uploads(router, "artifact/server/path", writeMapFS{memfs}) + + req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=../../some/file", strings.NewReader("content")) + rr := httptest.NewRecorder() + + router.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + assert.Fail("Wrong status") + } + + response := ResponseMessage{} + err := json.Unmarshal(rr.Body.Bytes(), &response) + if err != nil { + panic(err) + } + + assert.Equal("success", response.Message) + assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data)) +} diff --git a/pkg/artifacts/testdata/GHSL-2023-004/artifacts.yml b/pkg/artifacts/testdata/GHSL-2023-004/artifacts.yml new file mode 100644 index 0000000..e717f14 --- /dev/null +++ b/pkg/artifacts/testdata/GHSL-2023-004/artifacts.yml @@ -0,0 +1,43 @@ + +name: "GHSL-2023-0004" +on: push + +jobs: + test-artifacts: + runs-on: ubuntu-latest + steps: + - run: echo "hello world" > test.txt + - name: curl upload + uses: wei/curl@v1 + with: + args: -s --fail ${ACTIONS_RUNTIME_URL}upload/1?itemPath=../../my-artifact/secret.txt --upload-file test.txt + - uses: actions/download-artifact@v2 + with: + name: my-artifact + path: test-artifacts + - name: 'Verify Artifact #1' + run: | + file="test-artifacts/secret.txt" + if [ ! -f $file ] ; then + echo "Expected file does not exist" + exit 1 + fi + if [ "$(cat $file)" != "hello world" ] ; then + echo "File contents of downloaded artifact are incorrect" + exit 1 + fi + - name: Verify download should work by clean extra dots + uses: wei/curl@v1 + with: + args: --path-as-is -s -o out.txt --fail ${ACTIONS_RUNTIME_URL}artifact/1/../../../1/my-artifact/secret.txt + - name: 'Verify download content' + run: | + file="out.txt" + if [ ! -f $file ] ; then + echo "Expected file does not exist" + exit 1 + fi + if [ "$(cat $file)" != "hello world" ] ; then + echo "File contents of downloaded artifact are incorrect" + exit 1 + fi diff --git a/pkg/common/git/git.go b/pkg/common/git/git.go index d03dada..954c2cc 100644 --- a/pkg/common/git/git.go +++ b/pkg/common/git/git.go @@ -7,20 +7,19 @@ import ( "io" "os" "path" - "path/filepath" "regexp" "strings" "sync" - "github.com/nektos/act/pkg/common" - "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/go-ini/ini" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" + + "github.com/nektos/act/pkg/common" ) var ( @@ -55,41 +54,40 @@ func (e *Error) Commit() string { // FindGitRevision get the current git revision func FindGitRevision(ctx context.Context, file string) (shortSha string, sha string, err error) { logger := common.Logger(ctx) - gitDir, err := findGitDirectory(file) + + gitDir, err := git.PlainOpenWithOptions( + file, + &git.PlainOpenOptions{ + DetectDotGit: true, + EnableDotGitCommonDir: true, + }, + ) + + if err != nil { + logger.WithError(err).Error("path", file, "not located inside a git repository") + return "", "", err + } + + head, err := gitDir.Reference(plumbing.HEAD, true) if err != nil { return "", "", err } - bts, err := os.ReadFile(filepath.Join(gitDir, "HEAD")) - if err != nil { - return "", "", err + if head.Hash().IsZero() { + return "", "", fmt.Errorf("HEAD sha1 could not be resolved") } - var ref = strings.TrimSpace(strings.TrimPrefix(string(bts), "ref:")) - var refBuf []byte - if strings.HasPrefix(ref, "refs/") { - // load commitid ref - refBuf, err = os.ReadFile(filepath.Join(gitDir, ref)) - if err != nil { - return "", "", err - } - } else { - refBuf = []byte(ref) - } + hash := head.Hash().String() - logger.Debugf("Found revision: %s", refBuf) - return string(refBuf[:7]), strings.TrimSpace(string(refBuf)), nil + logger.Debugf("Found revision: %s", hash) + return hash[:7], strings.TrimSpace(hash), nil } // FindGitRef get the current git ref func FindGitRef(ctx context.Context, file string) (string, error) { logger := common.Logger(ctx) - gitDir, err := findGitDirectory(file) - if err != nil { - return "", err - } - logger.Debugf("Loading revision from git directory '%s'", gitDir) + logger.Debugf("Loading revision from git directory") _, ref, err := FindGitRevision(ctx, file) if err != nil { return "", err @@ -100,28 +98,58 @@ func FindGitRef(ctx context.Context, file string) (string, error) { // Prefer the git library to iterate over the references and find a matching tag or branch. var refTag = "" var refBranch = "" - r, err := git.PlainOpen(filepath.Join(gitDir, "..")) - if err == nil { - iter, err := r.References() - if err == nil { - for { - r, err := iter.Next() - if r == nil || err != nil { - break - } - // logger.Debugf("Reference: name=%s sha=%s", r.Name().String(), r.Hash().String()) - if r.Hash().String() == ref { - if r.Name().IsTag() { - refTag = r.Name().String() - } - if r.Name().IsBranch() { - refBranch = r.Name().String() - } - } - } - iter.Close() - } + repo, err := git.PlainOpenWithOptions( + file, + &git.PlainOpenOptions{ + DetectDotGit: true, + EnableDotGitCommonDir: true, + }, + ) + + if err != nil { + return "", err } + + iter, err := repo.References() + if err != nil { + return "", err + } + + // find the reference that matches the revision's has + err = iter.ForEach(func(r *plumbing.Reference) error { + /* tags and branches will have the same hash + * when a user checks out a tag, it is not mentioned explicitly + * in the go-git package, we must identify the revision + * then check if any tag matches that revision, + * if so then we checked out a tag + * else we look for branches and if matches, + * it means we checked out a branch + * + * If a branches matches first we must continue and check all tags (all references) + * in case we match with a tag later in the interation + */ + if r.Hash().String() == ref { + if r.Name().IsTag() { + refTag = r.Name().String() + } + if r.Name().IsBranch() { + refBranch = r.Name().String() + } + } + + // we found what we where looking for + if refTag != "" && refBranch != "" { + return storer.ErrStop + } + + return nil + }) + + if err != nil { + return "", err + } + + // order matters here see above comment. if refTag != "" { return refTag, nil } @@ -129,39 +157,7 @@ func FindGitRef(ctx context.Context, file string) (string, error) { return refBranch, nil } - // If the above doesn't work, fall back to the old way - - // try tags first - tag, err := findGitPrettyRef(ctx, ref, gitDir, "refs/tags") - if err != nil || tag != "" { - return tag, err - } - // and then branches - return findGitPrettyRef(ctx, ref, gitDir, "refs/heads") -} - -func findGitPrettyRef(ctx context.Context, head, root, sub string) (string, error) { - var name string - var err = filepath.Walk(filepath.Join(root, sub), func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if name != "" || info.IsDir() { - return nil - } - var bts []byte - if bts, err = os.ReadFile(path); err != nil { - return err - } - var pointsTo = strings.TrimSpace(string(bts)) - if head == pointsTo { - // On Windows paths are separated with backslash character so they should be replaced to provide proper git refs format - name = strings.TrimPrefix(strings.ReplaceAll(strings.Replace(path, root, "", 1), `\`, `/`), "/") - common.Logger(ctx).Debugf("HEAD matches %s", name) - } - return nil - }) - return name, err + return "", fmt.Errorf("failed to identify reference (tag/branch) for the checked-out revision '%s'", ref) } // FindGithubRepo get the repo @@ -179,26 +175,27 @@ func FindGithubRepo(ctx context.Context, file, githubInstance, remoteName string } func findGitRemoteURL(ctx context.Context, file, remoteName string) (string, error) { - gitDir, err := findGitDirectory(file) + repo, err := git.PlainOpenWithOptions( + file, + &git.PlainOpenOptions{ + DetectDotGit: true, + EnableDotGitCommonDir: true, + }, + ) if err != nil { return "", err } - common.Logger(ctx).Debugf("Loading slug from git directory '%s'", gitDir) - gitconfig, err := ini.InsensitiveLoad(fmt.Sprintf("%s/config", gitDir)) + remote, err := repo.Remote(remoteName) if err != nil { return "", err } - remote, err := gitconfig.GetSection(fmt.Sprintf(`remote "%s"`, remoteName)) - if err != nil { - return "", err + + if len(remote.Config().URLs) < 1 { + return "", fmt.Errorf("remote '%s' exists but has no URL", remoteName) } - urlKey, err := remote.GetKey("url") - if err != nil { - return "", err - } - url := urlKey.String() - return url, nil + + return remote.Config().URLs[0], nil } func findGitSlug(url string, githubInstance string) (string, string, error) { @@ -222,35 +219,6 @@ func findGitSlug(url string, githubInstance string) (string, string, error) { return "", url, nil } -func findGitDirectory(fromFile string) (string, error) { - absPath, err := filepath.Abs(fromFile) - if err != nil { - return "", err - } - - fi, err := os.Stat(absPath) - if err != nil { - return "", err - } - - var dir string - if fi.Mode().IsDir() { - dir = absPath - } else { - dir = filepath.Dir(absPath) - } - - gitPath := filepath.Join(dir, ".git") - fi, err = os.Stat(gitPath) - if err == nil && fi.Mode().IsDir() { - return gitPath, nil - } else if dir == "/" || dir == "C:\\" || dir == "c:\\" { - return "", &Error{err: ErrNoRepo} - } - - return findGitDirectory(filepath.Dir(dir)) -} - // NewGitCloneExecutorInput the input for the NewGitCloneExecutor type NewGitCloneExecutorInput struct { URL string @@ -292,7 +260,7 @@ func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input return nil, err } - if err = os.Chmod(input.Dir, 0755); err != nil { + if err = os.Chmod(input.Dir, 0o755); err != nil { return nil, err } } diff --git a/pkg/common/git/git_test.go b/pkg/common/git/git_test.go index 9798193..6ad66b6 100644 --- a/pkg/common/git/git_test.go +++ b/pkg/common/git/git_test.go @@ -82,12 +82,19 @@ func TestFindGitRemoteURL(t *testing.T) { assert.NoError(err) remoteURL := "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo-name" - err = gitCmd("config", "-f", fmt.Sprintf("%s/.git/config", basedir), "--add", "remote.origin.url", remoteURL) + err = gitCmd("-C", basedir, "remote", "add", "origin", remoteURL) assert.NoError(err) u, err := findGitRemoteURL(context.Background(), basedir, "origin") assert.NoError(err) assert.Equal(remoteURL, u) + + remoteURL = "git@github.com/AwesomeOwner/MyAwesomeRepo.git" + err = gitCmd("-C", basedir, "remote", "add", "upstream", remoteURL) + assert.NoError(err) + u, err = findGitRemoteURL(context.Background(), basedir, "upstream") + assert.NoError(err) + assert.Equal(remoteURL, u) } func TestGitFindRef(t *testing.T) { @@ -160,7 +167,7 @@ func TestGitFindRef(t *testing.T) { name := name t.Run(name, func(t *testing.T) { dir := filepath.Join(basedir, name) - require.NoError(t, os.MkdirAll(dir, 0755)) + require.NoError(t, os.MkdirAll(dir, 0o755)) require.NoError(t, gitCmd("-C", dir, "init", "--initial-branch=master")) require.NoError(t, cleanGitHooks(dir)) tt.Prepare(t, dir) diff --git a/pkg/container/container_types.go b/pkg/container/container_types.go new file mode 100644 index 0000000..063b422 --- /dev/null +++ b/pkg/container/container_types.go @@ -0,0 +1,73 @@ +package container + +import ( + "context" + "io" + + "github.com/nektos/act/pkg/common" +) + +// NewContainerInput the input for the New function +type NewContainerInput struct { + Image string + Username string + Password string + Entrypoint []string + Cmd []string + WorkingDir string + Env []string + Binds []string + Mounts map[string]string + Name string + Stdout io.Writer + Stderr io.Writer + NetworkMode string + Privileged bool + UsernsMode string + Platform string + Options string + + // Gitea specific + AutoRemove bool +} + +// FileEntry is a file to copy to a container +type FileEntry struct { + Name string + Mode int64 + Body string +} + +// Container for managing docker run containers +type Container interface { + Create(capAdd []string, capDrop []string) common.Executor + Copy(destPath string, files ...*FileEntry) common.Executor + CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor + GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) + Pull(forcePull bool) common.Executor + Start(attach bool) common.Executor + Exec(command []string, env map[string]string, user, workdir string) common.Executor + UpdateFromEnv(srcPath string, env *map[string]string) common.Executor + UpdateFromImageEnv(env *map[string]string) common.Executor + Remove() common.Executor + Close() common.Executor + ReplaceLogWriter(io.Writer, io.Writer) (io.Writer, io.Writer) +} + +// NewDockerBuildExecutorInput the input for the NewDockerBuildExecutor function +type NewDockerBuildExecutorInput struct { + ContextDir string + Dockerfile string + Container Container + ImageTag string + Platform string +} + +// NewDockerPullExecutorInput the input for the NewDockerPullExecutor function +type NewDockerPullExecutorInput struct { + Image string + ForcePull bool + Platform string + Username string + Password string +} diff --git a/pkg/container/docker_auth.go b/pkg/container/docker_auth.go index 7d2fc4a..e47fe64 100644 --- a/pkg/container/docker_auth.go +++ b/pkg/container/docker_auth.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( @@ -36,3 +38,24 @@ func LoadDockerAuthConfig(ctx context.Context, image string) (types.AuthConfig, return types.AuthConfig(authConfig), nil } + +func LoadDockerAuthConfigs(ctx context.Context) map[string]types.AuthConfig { + logger := common.Logger(ctx) + config, err := config.Load(config.Dir()) + if err != nil { + logger.Warnf("Could not load docker config: %v", err) + return nil + } + + if !config.ContainsAuth() { + config.CredentialsStore = credentials.DetectDefaultStore(config.CredentialsStore) + } + + creds, _ := config.GetAllCredentials() + authConfigs := make(map[string]types.AuthConfig, len(creds)) + for k, v := range creds { + authConfigs[k] = types.AuthConfig(v) + } + + return authConfigs +} diff --git a/pkg/container/docker_build.go b/pkg/container/docker_build.go index 17e2c7b..7215023 100644 --- a/pkg/container/docker_build.go +++ b/pkg/container/docker_build.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( @@ -8,22 +10,14 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/fileutils" // github.com/docker/docker/builder/dockerignore is deprecated "github.com/moby/buildkit/frontend/dockerfile/dockerignore" + "github.com/moby/patternmatcher" "github.com/nektos/act/pkg/common" ) -// NewDockerBuildExecutorInput the input for the NewDockerBuildExecutor function -type NewDockerBuildExecutorInput struct { - ContextDir string - Container Container - ImageTag string - Platform string -} - // NewDockerBuildExecutor function to create a run executor for the container func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor { return func(ctx context.Context) error { @@ -47,15 +41,17 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor { tags := []string{input.ImageTag} options := types.ImageBuildOptions{ - Tags: tags, - Remove: true, - Platform: input.Platform, + Tags: tags, + Remove: true, + Platform: input.Platform, + AuthConfigs: LoadDockerAuthConfigs(ctx), + Dockerfile: input.Dockerfile, } var buildContext io.ReadCloser if input.Container != nil { buildContext, err = input.Container.GetContainerArchive(ctx, input.ContextDir+"/.") } else { - buildContext, err = createBuildContext(ctx, input.ContextDir, "Dockerfile") + buildContext, err = createBuildContext(ctx, input.ContextDir, input.Dockerfile) } if err != nil { return err @@ -101,8 +97,8 @@ func createBuildContext(ctx context.Context, contextDir string, relDockerfile st // parses the Dockerfile. Ignore errors here, as they will have been // caught by validateContextDirectory above. var includes = []string{"."} - keepThem1, _ := fileutils.Matches(".dockerignore", excludes) - keepThem2, _ := fileutils.Matches(relDockerfile, excludes) + keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes) + keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes) if keepThem1 || keepThem2 { includes = append(includes, ".dockerignore", relDockerfile) } diff --git a/pkg/container/docker_cli.go b/pkg/container/docker_cli.go index 60c9fe8..a1481c3 100644 --- a/pkg/container/docker_cli.go +++ b/pkg/container/docker_cli.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + // This file is exact copy of https://github.com/docker/cli/blob/9ac8584acfd501c3f4da0e845e3a40ed15c85041/cli/command/container/opts.go // appended with license information. // diff --git a/pkg/container/docker_cli_test.go b/pkg/container/docker_cli_test.go index cdd91f6..a6445be 100644 --- a/pkg/container/docker_cli_test.go +++ b/pkg/container/docker_cli_test.go @@ -663,8 +663,8 @@ func TestRunFlagsParseShmSize(t *testing.T) { func TestParseRestartPolicy(t *testing.T) { invalids := map[string]string{ - "always:2:3": "invalid restart policy format", - "on-failure:invalid": "maximum retry count must be an integer", + "always:2:3": "invalid restart policy format: maximum retry count must be an integer", + "on-failure:invalid": "invalid restart policy format: maximum retry count must be an integer", } valids := map[string]container.RestartPolicy{ "": {}, diff --git a/pkg/container/docker_images.go b/pkg/container/docker_images.go index e23699e..2277230 100644 --- a/pkg/container/docker_images.go +++ b/pkg/container/docker_images.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( @@ -5,7 +7,7 @@ import ( "fmt" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" ) // ImageExistsLocally returns a boolean indicating if an image with the @@ -17,33 +19,15 @@ func ImageExistsLocally(ctx context.Context, imageName string, platform string) } defer cli.Close() - filters := filters.NewArgs() - filters.Add("reference", imageName) - - imageListOptions := types.ImageListOptions{ - Filters: filters, - } - - images, err := cli.ImageList(ctx, imageListOptions) - if err != nil { + inspectImage, _, err := cli.ImageInspectWithRaw(ctx, imageName) + if client.IsErrNotFound(err) { + return false, nil + } else if err != nil { return false, err } - if len(images) > 0 { - if platform == "any" || platform == "" { - return true, nil - } - for _, v := range images { - inspectImage, _, err := cli.ImageInspectWithRaw(ctx, v.ID) - if err != nil { - return false, err - } - - if fmt.Sprintf("%s/%s", inspectImage.Os, inspectImage.Architecture) == platform { - return true, nil - } - } - return false, nil + if platform == "" || platform == "any" || fmt.Sprintf("%s/%s", inspectImage.Os, inspectImage.Architecture) == platform { + return true, nil } return false, nil @@ -52,38 +36,25 @@ func ImageExistsLocally(ctx context.Context, imageName string, platform string) // RemoveImage removes image from local store, the function is used to run different // container image architectures func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) { - if exists, err := ImageExistsLocally(ctx, imageName, "any"); !exists { - return false, err - } - cli, err := GetDockerClient(ctx) if err != nil { return false, err } + defer cli.Close() - filters := filters.NewArgs() - filters.Add("reference", imageName) - - imageListOptions := types.ImageListOptions{ - Filters: filters, - } - - images, err := cli.ImageList(ctx, imageListOptions) - if err != nil { + inspectImage, _, err := cli.ImageInspectWithRaw(ctx, imageName) + if client.IsErrNotFound(err) { + return false, nil + } else if err != nil { return false, err } - if len(images) > 0 { - for _, v := range images { - if _, err = cli.ImageRemove(ctx, v.ID, types.ImageRemoveOptions{ - Force: force, - PruneChildren: pruneChildren, - }); err != nil { - return false, err - } - } - return true, nil + if _, err = cli.ImageRemove(ctx, inspectImage.ID, types.ImageRemoveOptions{ + Force: force, + PruneChildren: pruneChildren, + }); err != nil { + return false, err } - return false, nil + return true, nil } diff --git a/pkg/container/docker_logger.go b/pkg/container/docker_logger.go index b6b2f15..f2c21e6 100644 --- a/pkg/container/docker_logger.go +++ b/pkg/container/docker_logger.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( diff --git a/pkg/container/docker_pull.go b/pkg/container/docker_pull.go index 1eb04e1..75bfed1 100644 --- a/pkg/container/docker_pull.go +++ b/pkg/container/docker_pull.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( @@ -12,15 +14,6 @@ import ( "github.com/nektos/act/pkg/common" ) -// NewDockerPullExecutorInput the input for the NewDockerPullExecutor function -type NewDockerPullExecutorInput struct { - Image string - ForcePull bool - Platform string - Username string - Password string -} - // NewDockerPullExecutor function to create a run executor for the container func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor { return func(ctx context.Context) error { diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index 351cf32..5afd8e0 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -1,8 +1,9 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( "archive/tar" - "bufio" "bytes" "context" "errors" @@ -38,53 +39,6 @@ import ( "github.com/nektos/act/pkg/common" ) -// NewContainerInput the input for the New function -type NewContainerInput struct { - Image string - Username string - Password string - Entrypoint []string - Cmd []string - WorkingDir string - Env []string - Binds []string - Mounts map[string]string - Name string - Stdout io.Writer - Stderr io.Writer - NetworkMode string - Privileged bool - UsernsMode string - Platform string - Options string - - AutoRemove bool -} - -// FileEntry is a file to copy to a container -type FileEntry struct { - Name string - Mode int64 - Body string -} - -// Container for managing docker run containers -type Container interface { - Create(capAdd []string, capDrop []string) common.Executor - Copy(destPath string, files ...*FileEntry) common.Executor - CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor - GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) - Pull(forcePull bool) common.Executor - Start(attach bool) common.Executor - Exec(command []string, env map[string]string, user, workdir string) common.Executor - UpdateFromEnv(srcPath string, env *map[string]string) common.Executor - UpdateFromImageEnv(env *map[string]string) common.Executor - UpdateFromPath(env *map[string]string) common.Executor - Remove() common.Executor - Close() common.Executor - ReplaceLogWriter(io.Writer, io.Writer) (io.Writer, io.Writer) -} - // NewContainer creates a reference to a container func NewContainer(input *NewContainerInput) ExecutionsEnvironment { cr := new(containerReference) @@ -190,17 +144,13 @@ func (cr *containerReference) GetContainerArchive(ctx context.Context, srcPath s } func (cr *containerReference) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor { - return cr.extractEnv(srcPath, env).IfNot(common.Dryrun) + return parseEnvFile(cr, srcPath, env).IfNot(common.Dryrun) } func (cr *containerReference) UpdateFromImageEnv(env *map[string]string) common.Executor { return cr.extractFromImageEnv(env).IfNot(common.Dryrun) } -func (cr *containerReference) UpdateFromPath(env *map[string]string) common.Executor { - return cr.extractPath(env).IfNot(common.Dryrun) -} - func (cr *containerReference) Exec(command []string, env map[string]string, user, workdir string) common.Executor { return common.NewPipelineExecutor( common.NewInfoExecutor("%sdocker exec cmd=[%s] user=%s workdir=%s", logPrefix, strings.Join(command, " "), user, workdir), @@ -413,10 +363,16 @@ func (cr *containerReference) mergeContainerConfigs(ctx context.Context, config logger.Debugf("Custom container.HostConfig from options ==> %+v", containerConfig.HostConfig) + hostConfig.Binds = append(hostConfig.Binds, containerConfig.HostConfig.Binds...) + hostConfig.Mounts = append(hostConfig.Mounts, containerConfig.HostConfig.Mounts...) + binds := hostConfig.Binds + mounts := hostConfig.Mounts err = mergo.Merge(hostConfig, containerConfig.HostConfig, mergo.WithOverride) if err != nil { return nil, nil, fmt.Errorf("Cannot merge container.HostConfig options: '%s': '%w'", input.Options, err) } + hostConfig.Binds = binds + hostConfig.Mounts = mounts logger.Debugf("Merged container.HostConfig ==> %+v", hostConfig) return config, hostConfig, nil @@ -500,59 +456,6 @@ func (cr *containerReference) create(capAdd []string, capDrop []string) common.E } } -var singleLineEnvPattern, multiLineEnvPattern *regexp.Regexp - -func (cr *containerReference) extractEnv(srcPath string, env *map[string]string) common.Executor { - if singleLineEnvPattern == nil { - // Single line pattern matches: - // SOME_VAR=data=moredata - // SOME_VAR=datamoredata - singleLineEnvPattern = regexp.MustCompile(`^([^=]*)\=(.*)$`) - multiLineEnvPattern = regexp.MustCompile(`^([^<]+)<<([\w-]+)$`) - } - - localEnv := *env - return func(ctx context.Context) error { - envTar, _, err := cr.cli.CopyFromContainer(ctx, cr.id, srcPath) - if err != nil { - return nil - } - defer envTar.Close() - - reader := tar.NewReader(envTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return fmt.Errorf("failed to read tar archive: %w", err) - } - s := bufio.NewScanner(reader) - multiLineEnvKey := "" - multiLineEnvDelimiter := "" - multiLineEnvContent := "" - for s.Scan() { - line := s.Text() - if singleLineEnv := singleLineEnvPattern.FindStringSubmatch(line); singleLineEnv != nil { - localEnv[singleLineEnv[1]] = singleLineEnv[2] - } - if line == multiLineEnvDelimiter { - localEnv[multiLineEnvKey] = multiLineEnvContent - multiLineEnvKey, multiLineEnvDelimiter, multiLineEnvContent = "", "", "" - } - if multiLineEnvKey != "" && multiLineEnvDelimiter != "" { - if multiLineEnvContent != "" { - multiLineEnvContent += "\n" - } - multiLineEnvContent += line - } - if multiLineEnvStart := multiLineEnvPattern.FindStringSubmatch(line); multiLineEnvStart != nil { - multiLineEnvKey = multiLineEnvStart[1] - multiLineEnvDelimiter = multiLineEnvStart[2] - } - } - env = &localEnv - return nil - } -} - func (cr *containerReference) extractFromImageEnv(env *map[string]string) common.Executor { envMap := *env return func(ctx context.Context) error { @@ -585,31 +488,6 @@ func (cr *containerReference) extractFromImageEnv(env *map[string]string) common } } -func (cr *containerReference) extractPath(env *map[string]string) common.Executor { - localEnv := *env - return func(ctx context.Context) error { - pathTar, _, err := cr.cli.CopyFromContainer(ctx, cr.id, localEnv["GITHUB_PATH"]) - if err != nil { - return fmt.Errorf("failed to copy from container: %w", err) - } - defer pathTar.Close() - - reader := tar.NewReader(pathTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return fmt.Errorf("failed to read tar archive: %w", err) - } - s := bufio.NewScanner(reader) - for s.Scan() { - line := s.Text() - localEnv["PATH"] = fmt.Sprintf("%s:%s", line, localEnv["PATH"]) - } - - env = &localEnv - return nil - } -} - func (cr *containerReference) exec(cmd []string, env map[string]string, user, workdir string) common.Executor { return func(ctx context.Context) error { logger := common.Logger(ctx) @@ -706,7 +584,7 @@ func (cr *containerReference) tryReadID(opt string, cbk func(id int)) common.Exe } exp := regexp.MustCompile(`\d+\n`) found := exp.FindString(sid) - id, err := strconv.ParseInt(found[:len(found)-1], 10, 32) + id, err := strconv.ParseInt(strings.TrimSpace(found), 10, 32) if err != nil { return nil } diff --git a/pkg/container/docker_stub.go b/pkg/container/docker_stub.go new file mode 100644 index 0000000..b28c90d --- /dev/null +++ b/pkg/container/docker_stub.go @@ -0,0 +1,57 @@ +//go:build WITHOUT_DOCKER || !(linux || darwin || windows) + +package container + +import ( + "context" + "runtime" + + "github.com/docker/docker/api/types" + "github.com/nektos/act/pkg/common" + "github.com/pkg/errors" +) + +// ImageExistsLocally returns a boolean indicating if an image with the +// requested name, tag and architecture exists in the local docker image store +func ImageExistsLocally(ctx context.Context, imageName string, platform string) (bool, error) { + return false, errors.New("Unsupported Operation") +} + +// RemoveImage removes image from local store, the function is used to run different +// container image architectures +func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) { + return false, errors.New("Unsupported Operation") +} + +// NewDockerBuildExecutor function to create a run executor for the container +func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor { + return func(ctx context.Context) error { + return errors.New("Unsupported Operation") + } +} + +// NewDockerPullExecutor function to create a run executor for the container +func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor { + return func(ctx context.Context) error { + return errors.New("Unsupported Operation") + } +} + +// NewContainer creates a reference to a container +func NewContainer(input *NewContainerInput) ExecutionsEnvironment { + return nil +} + +func RunnerArch(ctx context.Context) string { + return runtime.GOOS +} + +func GetHostInfo(ctx context.Context) (info types.Info, err error) { + return types.Info{}, nil +} + +func NewDockerVolumeRemoveExecutor(volume string, force bool) common.Executor { + return func(ctx context.Context) error { + return nil + } +} diff --git a/pkg/container/docker_volume.go b/pkg/container/docker_volume.go index 5a6d476..6eafd33 100644 --- a/pkg/container/docker_volume.go +++ b/pkg/container/docker_volume.go @@ -1,3 +1,5 @@ +//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows)) + package container import ( diff --git a/pkg/container/file_collector.go b/pkg/container/file_collector.go index a4143ed..b4be0e8 100644 --- a/pkg/container/file_collector.go +++ b/pkg/container/file_collector.go @@ -65,7 +65,7 @@ type copyCollector struct { func (cc *copyCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error { fdestpath := filepath.Join(cc.DstDir, fpath) - if err := os.MkdirAll(filepath.Dir(fdestpath), 0777); err != nil { + if err := os.MkdirAll(filepath.Dir(fdestpath), 0o777); err != nil { return err } if f == nil { diff --git a/pkg/container/file_collector_test.go b/pkg/container/file_collector_test.go index 86b8003..241fd34 100644 --- a/pkg/container/file_collector_test.go +++ b/pkg/container/file_collector_test.go @@ -76,7 +76,7 @@ func (mfs *memoryFs) Readlink(path string) (string, error) { func TestIgnoredTrackedfile(t *testing.T) { fs := memfs.New() - _ = fs.MkdirAll("mygitrepo/.git", 0777) + _ = fs.MkdirAll("mygitrepo/.git", 0o777) dotgit, _ := fs.Chroot("mygitrepo/.git") worktree, _ := fs.Chroot("mygitrepo") repo, _ := git.Init(filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault()), worktree) diff --git a/pkg/container/host_environment.go b/pkg/container/host_environment.go index b404e86..5d8c7dc 100644 --- a/pkg/container/host_environment.go +++ b/pkg/container/host_environment.go @@ -2,9 +2,9 @@ package container import ( "archive/tar" - "bufio" "bytes" "context" + "errors" "fmt" "io" "io/fs" @@ -15,14 +15,13 @@ import ( "strings" "time" - "errors" - "github.com/go-git/go-billy/v5/helper/polyfill" "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5/plumbing/format/gitignore" + "golang.org/x/term" + "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/lookpath" - "golang.org/x/term" ) type HostEnvironment struct { @@ -50,7 +49,7 @@ func (e *HostEnvironment) Close() common.Executor { func (e *HostEnvironment) Copy(destPath string, files ...*FileEntry) common.Executor { return func(ctx context.Context) error { for _, f := range files { - if err := os.MkdirAll(filepath.Dir(filepath.Join(destPath, f.Name)), 0777); err != nil { + if err := os.MkdirAll(filepath.Dir(filepath.Join(destPath, f.Name)), 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(destPath, f.Name), []byte(f.Body), fs.FileMode(f.Mode)); err != nil { @@ -341,77 +340,7 @@ func (e *HostEnvironment) Exec(command []string /*cmdline string, */, env map[st } func (e *HostEnvironment) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor { - localEnv := *env - return func(ctx context.Context) error { - envTar, err := e.GetContainerArchive(ctx, srcPath) - if err != nil { - return nil - } - defer envTar.Close() - reader := tar.NewReader(envTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return err - } - s := bufio.NewScanner(reader) - for s.Scan() { - line := s.Text() - singleLineEnv := strings.Index(line, "=") - multiLineEnv := strings.Index(line, "<<") - if singleLineEnv != -1 && (multiLineEnv == -1 || singleLineEnv < multiLineEnv) { - localEnv[line[:singleLineEnv]] = line[singleLineEnv+1:] - } else if multiLineEnv != -1 { - multiLineEnvContent := "" - multiLineEnvDelimiter := line[multiLineEnv+2:] - delimiterFound := false - for s.Scan() { - content := s.Text() - if content == multiLineEnvDelimiter { - delimiterFound = true - break - } - if multiLineEnvContent != "" { - multiLineEnvContent += "\n" - } - multiLineEnvContent += content - } - if !delimiterFound { - return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter) - } - localEnv[line[:multiLineEnv]] = multiLineEnvContent - } else { - return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line) - } - } - env = &localEnv - return nil - } -} - -func (e *HostEnvironment) UpdateFromPath(env *map[string]string) common.Executor { - localEnv := *env - return func(ctx context.Context) error { - pathTar, err := e.GetContainerArchive(ctx, localEnv["GITHUB_PATH"]) - if err != nil { - return err - } - defer pathTar.Close() - - reader := tar.NewReader(pathTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return err - } - s := bufio.NewScanner(reader) - for s.Scan() { - line := s.Text() - pathSep := string(filepath.ListSeparator) - localEnv[e.GetPathVariableName()] = fmt.Sprintf("%s%s%s", line, pathSep, localEnv[e.GetPathVariableName()]) - } - - env = &localEnv - return nil - } + return parseEnvFile(e, srcPath, env) } func (e *HostEnvironment) Remove() common.Executor { @@ -454,10 +383,32 @@ func (*HostEnvironment) JoinPathVariable(paths ...string) string { return strings.Join(paths, string(filepath.ListSeparator)) } +func goArchToActionArch(arch string) string { + archMapper := map[string]string{ + "x86_64": "X64", + "386": "x86", + "aarch64": "arm64", + } + if arch, ok := archMapper[arch]; ok { + return arch + } + return arch +} + +func goOsToActionOs(os string) string { + osMapper := map[string]string{ + "darwin": "macOS", + } + if os, ok := osMapper[os]; ok { + return os + } + return os +} + func (e *HostEnvironment) GetRunnerContext(ctx context.Context) map[string]interface{} { return map[string]interface{}{ - "os": runtime.GOOS, - "arch": runtime.GOARCH, + "os": goOsToActionOs(runtime.GOOS), + "arch": goArchToActionArch(runtime.GOARCH), "temp": e.TmpDir, "tool_cache": e.ToolCache, } diff --git a/pkg/container/parse_env_file.go b/pkg/container/parse_env_file.go new file mode 100644 index 0000000..ee79b7e --- /dev/null +++ b/pkg/container/parse_env_file.go @@ -0,0 +1,60 @@ +package container + +import ( + "archive/tar" + "bufio" + "context" + "fmt" + "io" + "strings" + + "github.com/nektos/act/pkg/common" +) + +func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor { + localEnv := *env + return func(ctx context.Context) error { + envTar, err := e.GetContainerArchive(ctx, srcPath) + if err != nil { + return nil + } + defer envTar.Close() + reader := tar.NewReader(envTar) + _, err = reader.Next() + if err != nil && err != io.EOF { + return err + } + s := bufio.NewScanner(reader) + for s.Scan() { + line := s.Text() + singleLineEnv := strings.Index(line, "=") + multiLineEnv := strings.Index(line, "<<") + if singleLineEnv != -1 && (multiLineEnv == -1 || singleLineEnv < multiLineEnv) { + localEnv[line[:singleLineEnv]] = line[singleLineEnv+1:] + } else if multiLineEnv != -1 { + multiLineEnvContent := "" + multiLineEnvDelimiter := line[multiLineEnv+2:] + delimiterFound := false + for s.Scan() { + content := s.Text() + if content == multiLineEnvDelimiter { + delimiterFound = true + break + } + if multiLineEnvContent != "" { + multiLineEnvContent += "\n" + } + multiLineEnvContent += content + } + if !delimiterFound { + return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter) + } + localEnv[line[:multiLineEnv]] = multiLineEnvContent + } else { + return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line) + } + } + env = &localEnv + return nil + } +} diff --git a/pkg/exprparser/functions.go b/pkg/exprparser/functions.go index 047a0e3..83b2a08 100644 --- a/pkg/exprparser/functions.go +++ b/pkg/exprparser/functions.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/go-git/go-git/v5/plumbing/format/gitignore" + "github.com/nektos/act/pkg/model" "github.com/rhysd/actionlint" ) @@ -202,6 +203,9 @@ func (impl *interperterImpl) hashFiles(paths ...reflect.Value) (string, error) { var files []string if err := filepath.Walk(impl.config.WorkingDir, func(path string, fi fs.FileInfo, err error) error { + if err != nil { + return err + } sansPrefix := strings.TrimPrefix(path, impl.config.WorkingDir+string(filepath.Separator)) parts := strings.Split(sansPrefix, string(filepath.Separator)) if fi.IsDir() || !matcher.Match(parts, fi.IsDir()) { diff --git a/pkg/exprparser/interpreter.go b/pkg/exprparser/interpreter.go index 7b76f3b..ef3e8e1 100644 --- a/pkg/exprparser/interpreter.go +++ b/pkg/exprparser/interpreter.go @@ -15,15 +15,21 @@ type EvaluationEnvironment struct { Github *model.GithubContext Env map[string]string Job *model.JobContext + Jobs *map[string]*model.WorkflowCallResult Steps map[string]*model.StepResult Runner map[string]interface{} Secrets map[string]string Strategy map[string]interface{} Matrix map[string]interface{} - Needs map[string]map[string]map[string]string + Needs map[string]Needs Inputs map[string]interface{} } +type Needs struct { + Outputs map[string]string `json:"outputs"` + Result string `json:"result"` +} + type Config struct { Run *model.Run WorkingDir string @@ -150,6 +156,11 @@ func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableN return impl.env.Env, nil case "job": return impl.env.Job, nil + case "jobs": + if impl.env.Jobs == nil { + return nil, fmt.Errorf("Unavailable context: jobs") + } + return impl.env.Jobs, nil case "steps": return impl.env.Steps, nil case "runner": @@ -361,8 +372,16 @@ func (impl *interperterImpl) compareValues(leftValue reflect.Value, rightValue r return impl.compareNumber(leftValue.Float(), rightValue.Float(), kind) + case reflect.Invalid: + if rightValue.Kind() == reflect.Invalid { + return true, nil + } + + // not possible situation - params are converted to the same type in code above + return nil, fmt.Errorf("Compare params of Invalid type: left: %+v, right: %+v", leftValue.Kind(), rightValue.Kind()) + default: - return nil, fmt.Errorf("TODO: evaluateCompare not implemented! left: %+v, right: %+v", leftValue.Kind(), rightValue.Kind()) + return nil, fmt.Errorf("Compare not implemented for types: left: %+v, right: %+v", leftValue.Kind(), rightValue.Kind()) } } diff --git a/pkg/exprparser/interpreter_test.go b/pkg/exprparser/interpreter_test.go index 2547aae..01eb25f 100644 --- a/pkg/exprparser/interpreter_test.go +++ b/pkg/exprparser/interpreter_test.go @@ -69,6 +69,11 @@ func TestOperators(t *testing.T) { {`true || false`, true, "or", ""}, {`fromJSON('{}') && true`, true, "and-boolean-object", ""}, {`fromJSON('{}') || false`, make(map[string]interface{}), "or-boolean-object", ""}, + {"github.event.commits[0].author.username != github.event.commits[1].author.username", true, "property-comparison1", ""}, + {"github.event.commits[0].author.username1 != github.event.commits[1].author.username", true, "property-comparison2", ""}, + {"github.event.commits[0].author.username != github.event.commits[1].author.username1", true, "property-comparison3", ""}, + {"github.event.commits[0].author.username1 != github.event.commits[1].author.username2", true, "property-comparison4", ""}, + {"secrets != env", nil, "property-comparison5", "Compare not implemented for types: left: map, right: map"}, } env := &EvaluationEnvironment{ @@ -555,6 +560,7 @@ func TestContexts(t *testing.T) { {"strategy.fail-fast", true, "strategy-context"}, {"matrix.os", "Linux", "matrix-context"}, {"needs.job-id.outputs.output-name", "value", "needs-context"}, + {"needs.job-id.result", "success", "needs-context"}, {"inputs.name", "value", "inputs-context"}, } @@ -593,11 +599,12 @@ func TestContexts(t *testing.T) { Matrix: map[string]interface{}{ "os": "Linux", }, - Needs: map[string]map[string]map[string]string{ + Needs: map[string]Needs{ "job-id": { - "outputs": { + Outputs: map[string]string{ "output-name": "value", }, + Result: "success", }, }, Inputs: map[string]interface{}{ diff --git a/pkg/model/github_context.go b/pkg/model/github_context.go index 86172df..e4c31fc 100644 --- a/pkg/model/github_context.go +++ b/pkg/model/github_context.go @@ -3,6 +3,7 @@ package model import ( "context" "fmt" + "strings" "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common/git" @@ -89,26 +90,22 @@ func withDefaultBranch(ctx context.Context, b string, event map[string]interface var findGitRef = git.FindGitRef var findGitRevision = git.FindGitRevision -func (ghc *GithubContext) SetRefAndSha(ctx context.Context, defaultBranch string, repoPath string) { +func (ghc *GithubContext) SetRef(ctx context.Context, defaultBranch string, repoPath string) { logger := common.Logger(ctx) + // https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads switch ghc.EventName { case "pull_request_target": ghc.Ref = fmt.Sprintf("refs/heads/%s", ghc.BaseRef) - ghc.Sha = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "sha")) case "pull_request", "pull_request_review", "pull_request_review_comment": ghc.Ref = fmt.Sprintf("refs/pull/%.0f/merge", ghc.Event["number"]) case "deployment", "deployment_status": ghc.Ref = asString(nestedMapLookup(ghc.Event, "deployment", "ref")) - ghc.Sha = asString(nestedMapLookup(ghc.Event, "deployment", "sha")) case "release": - ghc.Ref = asString(nestedMapLookup(ghc.Event, "release", "tag_name")) + ghc.Ref = fmt.Sprintf("refs/tags/%s", asString(nestedMapLookup(ghc.Event, "release", "tag_name"))) case "push", "create", "workflow_dispatch": ghc.Ref = asString(ghc.Event["ref"]) - if deleted, ok := ghc.Event["deleted"].(bool); ok && !deleted { - ghc.Sha = asString(ghc.Event["after"]) - } default: defaultBranch := asString(nestedMapLookup(ghc.Event, "repository", "default_branch")) if defaultBranch != "" { @@ -136,6 +133,23 @@ func (ghc *GithubContext) SetRefAndSha(ctx context.Context, defaultBranch string ghc.Ref = fmt.Sprintf("refs/heads/%s", asString(nestedMapLookup(ghc.Event, "repository", "default_branch"))) } } +} + +func (ghc *GithubContext) SetSha(ctx context.Context, repoPath string) { + logger := common.Logger(ctx) + + // https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows + // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads + switch ghc.EventName { + case "pull_request_target": + ghc.Sha = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "sha")) + case "deployment", "deployment_status": + ghc.Sha = asString(nestedMapLookup(ghc.Event, "deployment", "sha")) + case "push", "create", "workflow_dispatch": + if deleted, ok := ghc.Event["deleted"].(bool); ok && !deleted { + ghc.Sha = asString(ghc.Event["after"]) + } + } if ghc.Sha == "" { _, sha, err := findGitRevision(ctx, repoPath) @@ -146,3 +160,51 @@ func (ghc *GithubContext) SetRefAndSha(ctx context.Context, defaultBranch string } } } + +func (ghc *GithubContext) SetRepositoryAndOwner(ctx context.Context, githubInstance string, remoteName string, repoPath string) { + if ghc.Repository == "" { + repo, err := git.FindGithubRepo(ctx, repoPath, githubInstance, remoteName) + if err != nil { + common.Logger(ctx).Warningf("unable to get git repo: %v", err) + return + } + ghc.Repository = repo + } + ghc.RepositoryOwner = strings.Split(ghc.Repository, "/")[0] +} + +func (ghc *GithubContext) SetRefTypeAndName() { + var refType, refName string + + // https://docs.github.com/en/actions/learn-github-actions/environment-variables + if strings.HasPrefix(ghc.Ref, "refs/tags/") { + refType = "tag" + refName = ghc.Ref[len("refs/tags/"):] + } else if strings.HasPrefix(ghc.Ref, "refs/heads/") { + refType = "branch" + refName = ghc.Ref[len("refs/heads/"):] + } else if strings.HasPrefix(ghc.Ref, "refs/pull/") { + refType = "" + refName = ghc.Ref[len("refs/pull/"):] + } + + if ghc.RefType == "" { + ghc.RefType = refType + } + + if ghc.RefName == "" { + ghc.RefName = refName + } +} + +func (ghc *GithubContext) SetBaseAndHeadRef() { + if ghc.EventName == "pull_request" || ghc.EventName == "pull_request_target" { + if ghc.BaseRef == "" { + ghc.BaseRef = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "ref")) + } + + if ghc.HeadRef == "" { + ghc.HeadRef = asString(nestedMapLookup(ghc.Event, "pull_request", "head", "ref")) + } + } +} diff --git a/pkg/model/github_context_test.go b/pkg/model/github_context_test.go index a290094..ed08e23 100644 --- a/pkg/model/github_context_test.go +++ b/pkg/model/github_context_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSetRefAndSha(t *testing.T) { +func TestSetRef(t *testing.T) { log.SetLevel(log.DebugLevel) oldFindGitRef := findGitRef @@ -29,38 +29,31 @@ func TestSetRefAndSha(t *testing.T) { eventName string event map[string]interface{} ref string - sha string + refName string }{ { eventName: "pull_request_target", - event: map[string]interface{}{ - "pull_request": map[string]interface{}{ - "base": map[string]interface{}{ - "sha": "pr-base-sha", - }, - }, - }, - ref: "refs/heads/master", - sha: "pr-base-sha", + event: map[string]interface{}{}, + ref: "refs/heads/master", + refName: "master", }, { eventName: "pull_request", event: map[string]interface{}{ "number": 1234., }, - ref: "refs/pull/1234/merge", - sha: "1234fakesha", + ref: "refs/pull/1234/merge", + refName: "1234/merge", }, { eventName: "deployment", event: map[string]interface{}{ "deployment": map[string]interface{}{ "ref": "refs/heads/somebranch", - "sha": "deployment-sha", }, }, - ref: "refs/heads/somebranch", - sha: "deployment-sha", + ref: "refs/heads/somebranch", + refName: "somebranch", }, { eventName: "release", @@ -69,18 +62,16 @@ func TestSetRefAndSha(t *testing.T) { "tag_name": "v1.0.0", }, }, - ref: "v1.0.0", - sha: "1234fakesha", + ref: "refs/tags/v1.0.0", + refName: "v1.0.0", }, { eventName: "push", event: map[string]interface{}{ - "ref": "refs/heads/somebranch", - "after": "push-sha", - "deleted": false, + "ref": "refs/heads/somebranch", }, - ref: "refs/heads/somebranch", - sha: "push-sha", + ref: "refs/heads/somebranch", + refName: "somebranch", }, { eventName: "unknown", @@ -89,14 +80,14 @@ func TestSetRefAndSha(t *testing.T) { "default_branch": "main", }, }, - ref: "refs/heads/main", - sha: "1234fakesha", + ref: "refs/heads/main", + refName: "main", }, { eventName: "no-event", event: map[string]interface{}{}, ref: "refs/heads/master", - sha: "1234fakesha", + refName: "master", }, } @@ -108,10 +99,11 @@ func TestSetRefAndSha(t *testing.T) { Event: table.event, } - ghc.SetRefAndSha(context.Background(), "main", "/some/dir") + ghc.SetRef(context.Background(), "main", "/some/dir") + ghc.SetRefTypeAndName() assert.Equal(t, table.ref, ghc.Ref) - assert.Equal(t, table.sha, ghc.Sha) + assert.Equal(t, table.refName, ghc.RefName) }) } @@ -125,9 +117,96 @@ func TestSetRefAndSha(t *testing.T) { Event: map[string]interface{}{}, } - ghc.SetRefAndSha(context.Background(), "", "/some/dir") + ghc.SetRef(context.Background(), "", "/some/dir") assert.Equal(t, "refs/heads/master", ghc.Ref) - assert.Equal(t, "1234fakesha", ghc.Sha) }) } + +func TestSetSha(t *testing.T) { + log.SetLevel(log.DebugLevel) + + oldFindGitRef := findGitRef + oldFindGitRevision := findGitRevision + defer func() { findGitRef = oldFindGitRef }() + defer func() { findGitRevision = oldFindGitRevision }() + + findGitRef = func(ctx context.Context, file string) (string, error) { + return "refs/heads/master", nil + } + + findGitRevision = func(ctx context.Context, file string) (string, string, error) { + return "", "1234fakesha", nil + } + + tables := []struct { + eventName string + event map[string]interface{} + sha string + }{ + { + eventName: "pull_request_target", + event: map[string]interface{}{ + "pull_request": map[string]interface{}{ + "base": map[string]interface{}{ + "sha": "pr-base-sha", + }, + }, + }, + sha: "pr-base-sha", + }, + { + eventName: "pull_request", + event: map[string]interface{}{ + "number": 1234., + }, + sha: "1234fakesha", + }, + { + eventName: "deployment", + event: map[string]interface{}{ + "deployment": map[string]interface{}{ + "sha": "deployment-sha", + }, + }, + sha: "deployment-sha", + }, + { + eventName: "release", + event: map[string]interface{}{}, + sha: "1234fakesha", + }, + { + eventName: "push", + event: map[string]interface{}{ + "after": "push-sha", + "deleted": false, + }, + sha: "push-sha", + }, + { + eventName: "unknown", + event: map[string]interface{}{}, + sha: "1234fakesha", + }, + { + eventName: "no-event", + event: map[string]interface{}{}, + sha: "1234fakesha", + }, + } + + for _, table := range tables { + t.Run(table.eventName, func(t *testing.T) { + ghc := &GithubContext{ + EventName: table.eventName, + BaseRef: "master", + Event: table.event, + } + + ghc.SetSha(context.Background(), "/some/dir") + + assert.Equal(t, table.sha, ghc.Sha) + }) + } +} diff --git a/pkg/model/planner.go b/pkg/model/planner.go index 94cec51..970226a 100644 --- a/pkg/model/planner.go +++ b/pkg/model/planner.go @@ -15,9 +15,9 @@ import ( // WorkflowPlanner contains methods for creating plans type WorkflowPlanner interface { - PlanEvent(eventName string) *Plan - PlanJob(jobName string) *Plan - PlanAll() *Plan + PlanEvent(eventName string) (*Plan, error) + PlanJob(jobName string) (*Plan, error) + PlanAll() (*Plan, error) GetEvents() []string } @@ -176,47 +176,76 @@ type workflowPlanner struct { } // PlanEvent builds a new list of runs to execute in parallel for an event name -func (wp *workflowPlanner) PlanEvent(eventName string) *Plan { +func (wp *workflowPlanner) PlanEvent(eventName string) (*Plan, error) { plan := new(Plan) if len(wp.workflows) == 0 { - log.Debugf("no events found for workflow: %s", eventName) + log.Debug("no workflows found by planner") + return plan, nil } + var lastErr error for _, w := range wp.workflows { - for _, e := range w.On() { + events := w.On() + if len(events) == 0 { + log.Debugf("no events found for workflow: %s", w.File) + continue + } + + for _, e := range events { if e == eventName { - plan.mergeStages(createStages(w, w.GetJobIDs()...)) + stages, err := createStages(w, w.GetJobIDs()...) + if err != nil { + log.Warn(err) + lastErr = err + } else { + plan.mergeStages(stages) + } } } } - return plan + return plan, lastErr } // PlanJob builds a new run to execute in parallel for a job name -func (wp *workflowPlanner) PlanJob(jobName string) *Plan { +func (wp *workflowPlanner) PlanJob(jobName string) (*Plan, error) { plan := new(Plan) if len(wp.workflows) == 0 { log.Debugf("no jobs found for workflow: %s", jobName) } + var lastErr error for _, w := range wp.workflows { - plan.mergeStages(createStages(w, jobName)) + stages, err := createStages(w, jobName) + if err != nil { + log.Warn(err) + lastErr = err + } else { + plan.mergeStages(stages) + } } - return plan + return plan, lastErr } // PlanAll builds a new run to execute in parallel all -func (wp *workflowPlanner) PlanAll() *Plan { +func (wp *workflowPlanner) PlanAll() (*Plan, error) { plan := new(Plan) if len(wp.workflows) == 0 { - log.Debugf("no jobs found for loaded workflows") + log.Debug("no workflows found by planner") + return plan, nil } + var lastErr error for _, w := range wp.workflows { - plan.mergeStages(createStages(w, w.GetJobIDs()...)) + stages, err := createStages(w, w.GetJobIDs()...) + if err != nil { + log.Warn(err) + lastErr = err + } else { + plan.mergeStages(stages) + } } - return plan + return plan, lastErr } // GetEvents gets all the events in the workflows file @@ -289,7 +318,7 @@ func (p *Plan) mergeStages(stages []*Stage) { p.Stages = newStages } -func createStages(w *Workflow, jobIDs ...string) []*Stage { +func createStages(w *Workflow, jobIDs ...string) ([]*Stage, error) { // first, build a list of all the necessary jobs to run, and their dependencies jobDependencies := make(map[string][]string) for len(jobIDs) > 0 { @@ -306,6 +335,8 @@ func createStages(w *Workflow, jobIDs ...string) []*Stage { jobIDs = newJobIDs } + var err error + // next, build an execution graph stages := make([]*Stage, 0) for len(jobDependencies) > 0 { @@ -321,12 +352,16 @@ func createStages(w *Workflow, jobIDs ...string) []*Stage { } } if len(stage.Runs) == 0 { - log.Fatalf("Unable to build dependency graph!") + return nil, fmt.Errorf("unable to build dependency graph for %s (%s)", w.Name, w.File) } stages = append(stages, stage) } - return stages + if len(stages) == 0 && err != nil { + return nil, err + } + + return stages, nil } // return true iff all strings in srcList exist in at least one of the stages diff --git a/pkg/model/step_result.go b/pkg/model/step_result.go index 49b7705..86e5ebf 100644 --- a/pkg/model/step_result.go +++ b/pkg/model/step_result.go @@ -42,5 +42,4 @@ type StepResult struct { Outputs map[string]string `json:"outputs"` Conclusion stepStatus `json:"conclusion"` Outcome stepStatus `json:"outcome"` - State map[string]string } diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go index b898d15..9c05bcb 100644 --- a/pkg/model/workflow.go +++ b/pkg/model/workflow.go @@ -124,6 +124,48 @@ func (w *Workflow) WorkflowDispatchConfig() *WorkflowDispatch { return &config } +type WorkflowCallInput struct { + Description string `yaml:"description"` + Required bool `yaml:"required"` + Default string `yaml:"default"` + Type string `yaml:"type"` +} + +type WorkflowCallOutput struct { + Description string `yaml:"description"` + Value string `yaml:"value"` +} + +type WorkflowCall struct { + Inputs map[string]WorkflowCallInput `yaml:"inputs"` + Outputs map[string]WorkflowCallOutput `yaml:"outputs"` +} + +type WorkflowCallResult struct { + Outputs map[string]string +} + +func (w *Workflow) WorkflowCallConfig() *WorkflowCall { + if w.RawOn.Kind != yaml.MappingNode { + return nil + } + + var val map[string]yaml.Node + err := w.RawOn.Decode(&val) + if err != nil { + log.Fatal(err) + } + + var config WorkflowCall + node := val["workflow_call"] + err = node.Decode(&config) + if err != nil { + log.Fatal(err) + } + + return &config +} + // Job is the structure of one job in a workflow type Job struct { Name string `yaml:"name"` @@ -139,6 +181,8 @@ type Job struct { Defaults Defaults `yaml:"defaults"` Outputs map[string]string `yaml:"outputs"` Uses string `yaml:"uses"` + With map[string]interface{} `yaml:"with"` + RawSecrets yaml.Node `yaml:"secrets"` Result string } @@ -193,6 +237,34 @@ func (s Strategy) GetFailFast() bool { return failFast } +func (j *Job) InheritSecrets() bool { + if j.RawSecrets.Kind != yaml.ScalarNode { + return false + } + + var val string + err := j.RawSecrets.Decode(&val) + if err != nil { + log.Fatal(err) + } + + return val == "inherit" +} + +func (j *Job) Secrets() map[string]string { + if j.RawSecrets.Kind != yaml.MappingNode { + return nil + } + + var val map[string]string + err := j.RawSecrets.Decode(&val) + if err != nil { + log.Fatal(err) + } + + return val +} + // Container details for the job func (j *Job) Container() *ContainerSpec { var val *ContainerSpec @@ -483,16 +555,8 @@ func (s *Step) String() string { } // Environments returns string-based key=value map for a step -// Note: all keys are uppercase func (s *Step) Environment() map[string]string { - env := environment(s.Env) - - for k, v := range env { - delete(env, k) - env[strings.ToUpper(k)] = v - } - - return env + return environment(s.Env) } // GetEnv gets the env for a step diff --git a/pkg/model/workflow_test.go b/pkg/model/workflow_test.go index 80c6795..5a0656d 100644 --- a/pkg/model/workflow_test.go +++ b/pkg/model/workflow_test.go @@ -323,7 +323,8 @@ func TestReadWorkflow_Strategy(t *testing.T) { w, err := NewWorkflowPlanner("testdata/strategy/push.yml", true) assert.NoError(t, err) - p := w.PlanJob("strategy-only-max-parallel") + p, err := w.PlanJob("strategy-only-max-parallel") + assert.NoError(t, err) assert.Equal(t, len(p.Stages), 1) assert.Equal(t, len(p.Stages[0].Runs), 1) diff --git a/pkg/runner/action.go b/pkg/runner/action.go index 7de9d6b..c3bcb3f 100644 --- a/pkg/runner/action.go +++ b/pkg/runner/action.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/kballard/go-shellquote" + "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/container" "github.com/nektos/act/pkg/model" @@ -29,10 +30,9 @@ type actionStep interface { type readAction func(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) -type ( - actionYamlReader func(filename string) (io.Reader, io.Closer, error) - fileWriter func(filename string, data []byte, perm fs.FileMode) error -) +type actionYamlReader func(filename string) (io.Reader, io.Closer, error) + +type fileWriter func(filename string, data []byte, perm fs.FileMode) error type runAction func(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor @@ -156,6 +156,8 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Main)} logger.Debugf("executing remote job container: %s", containerArgs) + rc.ApplyExtraPath(ctx, step.getEnv()) + return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingDocker: location := actionLocation @@ -235,14 +237,17 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based var prepImage common.Executor var image string + forcePull := false if strings.HasPrefix(action.Runs.Image, "docker://") { image = strings.TrimPrefix(action.Runs.Image, "docker://") + // Apply forcePull only for prebuild docker images + forcePull = rc.Config.ForcePull } else { // "-dockeraction" enshures that "./", "./test " won't get converted to "act-:latest", "act-test-:latest" which are invalid docker image names image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest") image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-")) image = strings.ToLower(image) - contextDir := filepath.Join(basedir, action.Runs.Main) + contextDir, fileName := filepath.Split(filepath.Join(basedir, action.Runs.Image)) anyArchExists, err := container.ImageExistsLocally(ctx, image, "any") if err != nil { @@ -272,6 +277,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based } prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ ContextDir: contextDir, + Dockerfile: fileName, ImageTag: image, Container: actionContainer, Platform: rc.Config.ContainerArchitecture, @@ -303,7 +309,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based stepContainer := newStepContainer(ctx, step, image, cmd, entrypoint) return common.NewPipelineExecutor( prepImage, - stepContainer.Pull(rc.Config.ForcePull), + stepContainer.Pull(forcePull), stepContainer.Remove().IfBool(!rc.Config.ReuseContainers), stepContainer.Create(rc.Config.ContainerCapAdd, rc.Config.ContainerCapDrop), stepContainer.Start(true), @@ -364,7 +370,10 @@ func newStepContainer(ctx context.Context, step step, image string, cmd []string envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp")) binds, mounts := rc.GetBindsAndMounts() - + networkMode := fmt.Sprintf("container:%s", rc.jobContainerName()) + if rc.IsHostEnv(ctx) { + networkMode = "default" + } stepContainer := container.NewContainer(&container.NewContainerInput{ Cmd: cmd, Entrypoint: entrypoint, @@ -375,22 +384,23 @@ func newStepContainer(ctx context.Context, step step, image string, cmd []string Name: createSimpleContainerName(rc.jobContainerName(), "STEP-"+stepModel.ID), Env: envList, Mounts: mounts, - NetworkMode: fmt.Sprintf("container:%s", rc.jobContainerName()), + NetworkMode: networkMode, Binds: binds, Stdout: logWriter, Stderr: logWriter, Privileged: rc.Config.Privileged, UsernsMode: rc.Config.UsernsMode, Platform: rc.Config.ContainerArchitecture, + Options: rc.Config.ContainerOptions, AutoRemove: rc.Config.AutoRemove, }) return stepContainer } func populateEnvsFromSavedState(env *map[string]string, step actionStep, rc *RunContext) { - stepResult := rc.StepResults[step.getStepModel().ID] - if stepResult != nil { - for name, value := range stepResult.State { + state, ok := rc.IntraActionState[step.getStepModel().ID] + if ok { + for name, value := range state { envName := fmt.Sprintf("STATE_%s", name) (*env)[envName] = value } @@ -503,6 +513,8 @@ func runPreStep(step actionStep) common.Executor { containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Pre)} logger.Debugf("executing remote job container: %s", containerArgs) + rc.ApplyExtraPath(ctx, step.getEnv()) + return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingComposite: @@ -510,7 +522,10 @@ func runPreStep(step actionStep) common.Executor { step.getCompositeRunContext(ctx) } - return step.getCompositeSteps().pre(ctx) + if steps := step.getCompositeSteps(); steps != nil && steps.pre != nil { + return steps.pre(ctx) + } + return fmt.Errorf("missing steps in composite action") case model.ActionRunsUsingGo: // defaults in pre steps were missing, however provided inputs are available @@ -626,6 +641,8 @@ func runPostStep(step actionStep) common.Executor { containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Post)} logger.Debugf("executing remote job container: %s", containerArgs) + rc.ApplyExtraPath(ctx, step.getEnv()) + return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingComposite: @@ -633,7 +650,10 @@ func runPostStep(step actionStep) common.Executor { return err } - return step.getCompositeSteps().post(ctx) + if steps := step.getCompositeSteps(); steps != nil && steps.post != nil { + return steps.post(ctx) + } + return fmt.Errorf("missing steps in composite action") case model.ActionRunsUsingGo: populateEnvsFromSavedState(step.getEnv(), step, rc) diff --git a/pkg/runner/action_composite.go b/pkg/runner/action_composite.go index 645ef6d..ff347e9 100644 --- a/pkg/runner/action_composite.go +++ b/pkg/runner/action_composite.go @@ -66,6 +66,7 @@ func newCompositeRunContext(ctx context.Context, parent *RunContext, step action JobContainer: parent.JobContainer, ActionPath: actionPath, Env: env, + GlobalEnv: parent.GlobalEnv, Masks: parent.Masks, ExtraPath: parent.ExtraPath, Parent: parent, @@ -85,6 +86,10 @@ func execAsComposite(step actionStep) common.Executor { steps := step.getCompositeSteps() + if steps == nil || steps.main == nil { + return fmt.Errorf("missing steps in composite action") + } + ctx = WithCompositeLogger(ctx, &compositeRC.Masks) err := steps.main(ctx) @@ -99,6 +104,14 @@ func execAsComposite(step actionStep) common.Executor { rc.Masks = append(rc.Masks, compositeRC.Masks...) rc.ExtraPath = compositeRC.ExtraPath + // compositeRC.Env is dirty, contains INPUT_ and merged step env, only rely on compositeRC.GlobalEnv + for k, v := range compositeRC.GlobalEnv { + rc.Env[k] = v + if rc.GlobalEnv == nil { + rc.GlobalEnv = map[string]string{} + } + rc.GlobalEnv[k] = v + } return err } diff --git a/pkg/runner/action_test.go b/pkg/runner/action_test.go index 0b23085..36ee14f 100644 --- a/pkg/runner/action_test.go +++ b/pkg/runner/action_test.go @@ -201,10 +201,11 @@ func TestActionRunner(t *testing.T) { }, CurrentStep: "post-step", StepResults: map[string]*model.StepResult{ + "step": {}, + }, + IntraActionState: map[string]map[string]string{ "step": { - State: map[string]string{ - "name": "state value", - }, + "name": "state value", }, }, }, diff --git a/pkg/runner/command.go b/pkg/runner/command.go old mode 100755 new mode 100644 index a68be16..f14eb7a --- a/pkg/runner/command.go +++ b/pkg/runner/command.go @@ -16,22 +16,27 @@ func init() { commandPatternADO = regexp.MustCompile("^##\\[([^ ]+)( (.+))?]([^\r\n]*)[\r\n]+$") } +func tryParseRawActionCommand(line string) (command string, kvPairs map[string]string, arg string, ok bool) { + if m := commandPatternGA.FindStringSubmatch(line); m != nil { + command = m[1] + kvPairs = parseKeyValuePairs(m[3], ",") + arg = m[4] + ok = true + } else if m := commandPatternADO.FindStringSubmatch(line); m != nil { + command = m[1] + kvPairs = parseKeyValuePairs(m[3], ";") + arg = m[4] + ok = true + } + return +} + func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler { logger := common.Logger(ctx) resumeCommand := "" return func(line string) bool { - var command string - var kvPairs map[string]string - var arg string - if m := commandPatternGA.FindStringSubmatch(line); m != nil { - command = m[1] - kvPairs = parseKeyValuePairs(m[3], ",") - arg = m[4] - } else if m := commandPatternADO.FindStringSubmatch(line); m != nil { - command = m[1] - kvPairs = parseKeyValuePairs(m[3], ";") - arg = m[4] - } else { + command, kvPairs, arg, ok := tryParseRawActionCommand(line) + if !ok { return true } @@ -66,6 +71,8 @@ func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler { case "save-state": logger.Infof(" \U0001f4be %s", line) rc.saveState(ctx, kvPairs, arg) + case "add-matcher": + logger.Infof(" \U00002753 add-matcher %s", arg) default: logger.Infof(" \U00002753 %s", line) } @@ -75,11 +82,17 @@ func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler { } func (rc *RunContext) setEnv(ctx context.Context, kvPairs map[string]string, arg string) { - common.Logger(ctx).Infof(" \U00002699 ::set-env:: %s=%s", kvPairs["name"], arg) + name := kvPairs["name"] + common.Logger(ctx).Infof(" \U00002699 ::set-env:: %s=%s", name, arg) if rc.Env == nil { rc.Env = make(map[string]string) } - rc.Env[kvPairs["name"]] = arg + rc.Env[name] = arg + // for composite action GITHUB_ENV and set-env passing + if rc.GlobalEnv == nil { + rc.GlobalEnv = map[string]string{} + } + rc.GlobalEnv[name] = arg } func (rc *RunContext) setOutput(ctx context.Context, kvPairs map[string]string, arg string) { logger := common.Logger(ctx) @@ -101,7 +114,13 @@ func (rc *RunContext) setOutput(ctx context.Context, kvPairs map[string]string, } func (rc *RunContext) addPath(ctx context.Context, arg string) { common.Logger(ctx).Infof(" \U00002699 ::add-path:: %s", arg) - rc.ExtraPath = append(rc.ExtraPath, arg) + extraPath := []string{arg} + for _, v := range rc.ExtraPath { + if v != arg { + extraPath = append(extraPath, v) + } + } + rc.ExtraPath = extraPath } func parseKeyValuePairs(kvPairs string, separator string) map[string]string { @@ -147,13 +166,16 @@ func unescapeKvPairs(kvPairs map[string]string) map[string]string { } func (rc *RunContext) saveState(ctx context.Context, kvPairs map[string]string, arg string) { - if rc.CurrentStep != "" { - stepResult := rc.StepResults[rc.CurrentStep] - if stepResult != nil { - if stepResult.State == nil { - stepResult.State = map[string]string{} - } - stepResult.State[kvPairs["name"]] = arg + stepID := rc.CurrentStep + if stepID != "" { + if rc.IntraActionState == nil { + rc.IntraActionState = map[string]map[string]string{} } + state, ok := rc.IntraActionState[stepID] + if !ok { + state = map[string]string{} + rc.IntraActionState[stepID] = state + } + state[kvPairs["name"]] = arg } } diff --git a/pkg/runner/command_test.go b/pkg/runner/command_test.go index 0b6ec8c..57c7de5 100644 --- a/pkg/runner/command_test.go +++ b/pkg/runner/command_test.go @@ -64,7 +64,7 @@ func TestAddpath(t *testing.T) { a.Equal("/zoo", rc.ExtraPath[0]) handler("::add-path::/boo\n") - a.Equal("/boo", rc.ExtraPath[1]) + a.Equal("/boo", rc.ExtraPath[0]) } func TestStopCommands(t *testing.T) { @@ -102,7 +102,7 @@ func TestAddpathADO(t *testing.T) { a.Equal("/zoo", rc.ExtraPath[0]) handler("##[add-path]/boo\n") - a.Equal("/boo", rc.ExtraPath[1]) + a.Equal("/boo", rc.ExtraPath[0]) } func TestAddmask(t *testing.T) { @@ -177,11 +177,7 @@ func TestAddmaskUsemask(t *testing.T) { func TestSaveState(t *testing.T) { rc := &RunContext{ CurrentStep: "step", - StepResults: map[string]*model.StepResult{ - "step": { - State: map[string]string{}, - }, - }, + StepResults: map[string]*model.StepResult{}, } ctx := context.Background() @@ -189,5 +185,5 @@ func TestSaveState(t *testing.T) { handler := rc.commandHandler(ctx) handler("::save-state name=state-name::state-value\n") - assert.Equal(t, "state-value", rc.StepResults["step"].State["state-name"]) + assert.Equal(t, "state-value", rc.IntraActionState["step"]["state-name"]) } diff --git a/pkg/runner/container_mock_test.go b/pkg/runner/container_mock_test.go index 0de0781..04d6261 100644 --- a/pkg/runner/container_mock_test.go +++ b/pkg/runner/container_mock_test.go @@ -2,6 +2,7 @@ package runner import ( "context" + "io" "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/container" @@ -49,11 +50,6 @@ func (cm *containerMock) UpdateFromImageEnv(env *map[string]string) common.Execu return args.Get(0).(func(context.Context) error) } -func (cm *containerMock) UpdateFromPath(env *map[string]string) common.Executor { - args := cm.Called(env) - return args.Get(0).(func(context.Context) error) -} - func (cm *containerMock) Copy(destPath string, files ...*container.FileEntry) common.Executor { args := cm.Called(destPath, files) return args.Get(0).(func(context.Context) error) @@ -63,7 +59,17 @@ func (cm *containerMock) CopyDir(destPath string, srcPath string, useGitIgnore b args := cm.Called(destPath, srcPath, useGitIgnore) return args.Get(0).(func(context.Context) error) } + func (cm *containerMock) Exec(command []string, env map[string]string, user, workdir string) common.Executor { args := cm.Called(command, env, user, workdir) return args.Get(0).(func(context.Context) error) } + +func (cm *containerMock) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) { + args := cm.Called(ctx, srcPath) + err, hasErr := args.Get(1).(error) + if !hasErr { + err = nil + } + return args.Get(0).(io.ReadCloser), err +} diff --git a/pkg/runner/expression.go b/pkg/runner/expression.go index c2257b1..ca40e95 100644 --- a/pkg/runner/expression.go +++ b/pkg/runner/expression.go @@ -21,8 +21,14 @@ type ExpressionEvaluator interface { // NewExpressionEvaluator creates a new evaluator func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEvaluator { + return rc.NewExpressionEvaluatorWithEnv(ctx, rc.GetEnv()) +} + +func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map[string]string) ExpressionEvaluator { + var workflowCallResult map[string]*model.WorkflowCallResult + // todo: cleanup EvaluationEnvironment creation - using := make(map[string]map[string]map[string]string) + using := make(map[string]exprparser.Needs) strategy := make(map[string]interface{}) if rc.Run != nil { job := rc.Run.Job() @@ -35,8 +41,26 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval jobNeeds := rc.Run.Job().Needs() for _, needs := range jobNeeds { - using[needs] = map[string]map[string]string{ - "outputs": jobs[needs].Outputs, + using[needs] = exprparser.Needs{ + Outputs: jobs[needs].Outputs, + Result: jobs[needs].Result, + } + } + + // only setup jobs context in case of workflow_call + // and existing expression evaluator (this means, jobs are at + // least ready to run) + if rc.caller != nil && rc.ExprEval != nil { + workflowCallResult = map[string]*model.WorkflowCallResult{} + + for jobName, job := range jobs { + result := model.WorkflowCallResult{ + Outputs: map[string]string{}, + } + for k, v := range job.Outputs { + result.Outputs[k] = v + } + workflowCallResult[jobName] = &result } } } @@ -46,12 +70,13 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval ee := &exprparser.EvaluationEnvironment{ Github: ghc, - Env: rc.GetEnv(), + Env: env, Job: rc.getJobContext(), + Jobs: &workflowCallResult, // todo: should be unavailable // but required to interpolate/evaluate the step outputs on the job Steps: rc.getStepsContext(), - Secrets: rc.Config.Secrets, + Secrets: getWorkflowSecrets(ctx, rc), Strategy: strategy, Matrix: rc.Matrix, Needs: using, @@ -82,10 +107,11 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step) jobs := rc.Run.Workflow.Jobs jobNeeds := rc.Run.Job().Needs() - using := make(map[string]map[string]map[string]string) + using := make(map[string]exprparser.Needs) for _, needs := range jobNeeds { - using[needs] = map[string]map[string]string{ - "outputs": jobs[needs].Outputs, + using[needs] = exprparser.Needs{ + Outputs: jobs[needs].Outputs, + Result: jobs[needs].Result, } } @@ -97,7 +123,7 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step) Env: *step.getEnv(), Job: rc.getJobContext(), Steps: rc.getStepsContext(), - Secrets: rc.Config.Secrets, + Secrets: getWorkflowSecrets(ctx, rc), Strategy: strategy, Matrix: rc.Matrix, Needs: using, @@ -311,6 +337,8 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]interface{} { inputs := map[string]interface{}{} + setupWorkflowInputs(ctx, &inputs, rc) + var env map[string]string if step != nil { env = *step.getEnv() @@ -343,3 +371,54 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod return inputs } + +func setupWorkflowInputs(ctx context.Context, inputs *map[string]interface{}, rc *RunContext) { + if rc.caller != nil { + config := rc.Run.Workflow.WorkflowCallConfig() + + for name, input := range config.Inputs { + value := rc.caller.runContext.Run.Job().With[name] + if value != nil { + if str, ok := value.(string); ok { + // evaluate using the calling RunContext (outside) + value = rc.caller.runContext.ExprEval.Interpolate(ctx, str) + } + } + + if value == nil && config != nil && config.Inputs != nil { + value = input.Default + if rc.ExprEval != nil { + if str, ok := value.(string); ok { + // evaluate using the called RunContext (inside) + value = rc.ExprEval.Interpolate(ctx, str) + } + } + } + + (*inputs)[name] = value + } + } +} + +func getWorkflowSecrets(ctx context.Context, rc *RunContext) map[string]string { + if rc.caller != nil { + job := rc.caller.runContext.Run.Job() + secrets := job.Secrets() + + if secrets == nil && job.InheritSecrets() { + secrets = rc.caller.runContext.Config.Secrets + } + + if secrets == nil { + secrets = map[string]string{} + } + + for k, v := range secrets { + secrets[k] = rc.caller.runContext.ExprEval.Interpolate(ctx, v) + } + + return secrets + } + + return rc.Config.Secrets +} diff --git a/pkg/runner/job_executor.go b/pkg/runner/job_executor.go index 5967dd1..1fcb00b 100644 --- a/pkg/runner/job_executor.go +++ b/pkg/runner/job_executor.go @@ -96,21 +96,18 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo } postExecutor = postExecutor.Finally(func(ctx context.Context) error { - logger := common.Logger(ctx) jobError := common.JobError(ctx) - if jobError != nil { - info.result("failure") - logger.WithField("jobResult", "failure").Infof("\U0001F3C1 Job failed") - } else { - err := info.stopContainer()(ctx) - if err != nil { - return err - } - info.result("success") - logger.WithField("jobResult", "success").Infof("\U0001F3C1 Job succeeded") + var err error + if rc.Config.AutoRemove || jobError == nil { + // always allow 1 min for stopping and removing the runner, even if we were cancelled + ctx, cancel := context.WithTimeout(common.WithLogger(context.Background(), common.Logger(ctx)), time.Minute) + defer cancel() + err = info.stopContainer()(ctx) } + setJobResult(ctx, info, rc, jobError == nil) + setJobOutputs(ctx, rc) - return nil + return err }) pipeline := make([]common.Executor, 0) @@ -123,7 +120,7 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo if ctx.Err() == context.Canceled { // in case of an aborted run, we still should execute the // post steps to allow cleanup. - ctx, cancel = context.WithTimeout(WithJobLogger(context.Background(), rc.Run.JobID, rc.String(), rc.Config, &rc.Masks, rc.Matrix), 5*time.Minute) + ctx, cancel = context.WithTimeout(common.WithLogger(context.Background(), common.Logger(ctx)), 5*time.Minute) defer cancel() } return postExecutor(ctx) @@ -132,6 +129,49 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo Finally(info.closeContainer())) } +func setJobResult(ctx context.Context, info jobInfo, rc *RunContext, success bool) { + logger := common.Logger(ctx) + + jobResult := "success" + // we have only one result for a whole matrix build, so we need + // to keep an existing result state if we run a matrix + if len(info.matrix()) > 0 && rc.Run.Job().Result != "" { + jobResult = rc.Run.Job().Result + } + + if !success { + jobResult = "failure" + } + + info.result(jobResult) + if rc.caller != nil { + // set reusable workflow job result + rc.caller.runContext.result(jobResult) + } + + jobResultMessage := "succeeded" + if jobResult != "success" { + jobResultMessage = "failed" + } + + logger.WithField("jobResult", jobResult).Infof("\U0001F3C1 Job %s", jobResultMessage) +} + +func setJobOutputs(ctx context.Context, rc *RunContext) { + if rc.caller != nil { + // map outputs for reusable workflows + callerOutputs := make(map[string]string) + + ee := rc.NewExpressionEvaluator(ctx) + + for k, v := range rc.Run.Workflow.WorkflowCallConfig().Outputs { + callerOutputs[k] = ee.Interpolate(ctx, ee.Interpolate(ctx, v.Value)) + } + + rc.caller.runContext.Run.Job().Outputs = callerOutputs + } +} + func useStepLogger(rc *RunContext, stepModel *model.Step, stage stepStage, executor common.Executor) common.Executor { return func(ctx context.Context) error { ctx = withStepLogger(ctx, stepModel.Number, stepModel.ID, rc.ExprEval.Interpolate(ctx, stepModel.String()), stage.String()) diff --git a/pkg/runner/job_executor_test.go b/pkg/runner/job_executor_test.go index e00a4fd..87c5888 100644 --- a/pkg/runner/job_executor_test.go +++ b/pkg/runner/job_executor_test.go @@ -15,15 +15,15 @@ import ( func TestJobExecutor(t *testing.T) { tables := []TestJobFileInfo{ - {workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms}, - {workdir, "uses-github-empty", "push", "Expected format {org}/{repo}[/path]@ref", platforms}, - {workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms}, - {workdir, "uses-github-root", "push", "", platforms}, - {workdir, "uses-github-path", "push", "", platforms}, - {workdir, "uses-docker-url", "push", "", platforms}, - {workdir, "uses-github-full-sha", "push", "", platforms}, - {workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms}, - {workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms}, + {workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms, secrets}, + {workdir, "uses-github-empty", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets}, + {workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets}, + {workdir, "uses-github-root", "push", "", platforms, secrets}, + {workdir, "uses-github-path", "push", "", platforms, secrets}, + {workdir, "uses-docker-url", "push", "", platforms, secrets}, + {workdir, "uses-github-full-sha", "push", "", platforms, secrets}, + {workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms, secrets}, + {workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms, secrets}, } // These tests are sufficient to only check syntax. ctx := common.WithDryrun(context.Background(), true) diff --git a/pkg/runner/logger.go b/pkg/runner/logger.go index f3cebce..ca9a639 100644 --- a/pkg/runner/logger.go +++ b/pkg/runner/logger.go @@ -57,38 +57,59 @@ func WithMasks(ctx context.Context, masks *[]string) context.Context { return context.WithValue(ctx, masksContextKeyVal, masks) } +type JobLoggerFactory interface { + WithJobLogger() *logrus.Logger +} + +type jobLoggerFactoryContextKey string + +var jobLoggerFactoryContextKeyVal = (jobLoggerFactoryContextKey)("jobloggerkey") + +func WithJobLoggerFactory(ctx context.Context, factory JobLoggerFactory) context.Context { + return context.WithValue(ctx, jobLoggerFactoryContextKeyVal, factory) +} + // WithJobLogger attaches a new logger to context that is aware of steps func WithJobLogger(ctx context.Context, jobID string, jobName string, config *Config, masks *[]string, matrix map[string]interface{}) context.Context { - mux.Lock() - defer mux.Unlock() - - var formatter logrus.Formatter - if config.JSONLogger { - formatter = &jobLogJSONFormatter{ - formatter: &logrus.JSONFormatter{}, - masker: valueMasker(config.InsecureSecrets, config.Secrets), - } - } else { - formatter = &jobLogFormatter{ - color: colors[nextColor%len(colors)], - masker: valueMasker(config.InsecureSecrets, config.Secrets), - } - } - - nextColor++ ctx = WithMasks(ctx, masks) - logger := logrus.New() - if hook := common.LoggerHook(ctx); hook != nil { - logger.AddHook(hook) - } - logger.SetFormatter(formatter) - logger.SetOutput(os.Stdout) - if config.JobLoggerLevel != nil { - logger.SetLevel(*config.JobLoggerLevel) + var logger *logrus.Logger + if jobLoggerFactory, ok := ctx.Value(jobLoggerFactoryContextKeyVal).(JobLoggerFactory); ok && jobLoggerFactory != nil { + logger = jobLoggerFactory.WithJobLogger() } else { - logger.SetLevel(logrus.TraceLevel) + var formatter logrus.Formatter + if config.JSONLogger { + formatter = &logrus.JSONFormatter{} + } else { + mux.Lock() + defer mux.Unlock() + nextColor++ + formatter = &jobLogFormatter{ + color: colors[nextColor%len(colors)], + } + } + + logger = logrus.New() + logger.SetOutput(os.Stdout) + logger.SetLevel(logrus.GetLevel()) + logger.SetFormatter(formatter) } + + { // Adapt to Gitea + if hook := common.LoggerHook(ctx); hook != nil { + logger.AddHook(hook) + } + if config.JobLoggerLevel != nil { + logger.SetLevel(*config.JobLoggerLevel) + } else { + logger.SetLevel(logrus.TraceLevel) + } + } + + logger.SetFormatter(&maskedFormatter{ + Formatter: logger.Formatter, + masker: valueMasker(config.InsecureSecrets, config.Secrets), + }) rtn := logger.WithFields(logrus.Fields{ "job": jobName, "jobID": jobID, @@ -157,16 +178,22 @@ func valueMasker(insecureSecrets bool, secrets map[string]string) entryProcessor } } -type jobLogFormatter struct { - color int +type maskedFormatter struct { + logrus.Formatter masker entryProcessor } +func (f *maskedFormatter) Format(entry *logrus.Entry) ([]byte, error) { + return f.Formatter.Format(f.masker(entry)) +} + +type jobLogFormatter struct { + color int +} + func (f *jobLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { b := &bytes.Buffer{} - entry = f.masker(entry) - if f.isColored(entry) { f.printColored(b, entry) } else { @@ -233,12 +260,3 @@ func checkIfTerminal(w io.Writer) bool { return false } } - -type jobLogJSONFormatter struct { - masker entryProcessor - formatter *logrus.JSONFormatter -} - -func (f *jobLogJSONFormatter) Format(entry *logrus.Entry) ([]byte, error) { - return f.formatter.Format(f.masker(entry)) -} diff --git a/pkg/runner/reusable_workflow.go b/pkg/runner/reusable_workflow.go new file mode 100644 index 0000000..1ffa22b --- /dev/null +++ b/pkg/runner/reusable_workflow.go @@ -0,0 +1,129 @@ +package runner + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "path" + "regexp" + "sync" + + "github.com/nektos/act/pkg/common" + "github.com/nektos/act/pkg/common/git" + "github.com/nektos/act/pkg/model" +) + +func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor { + return newReusableWorkflowExecutor(rc, rc.Config.Workdir, rc.Run.Job().Uses) +} + +func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor { + uses := rc.Run.Job().Uses + + remoteReusableWorkflow := newRemoteReusableWorkflow(uses) + if remoteReusableWorkflow == nil { + return common.NewErrorExecutor(fmt.Errorf("expected format {owner}/{repo}/.github/workflows/{filename}@{ref}. Actual '%s' Input string was not in a correct format", uses)) + } + remoteReusableWorkflow.URL = rc.Config.GitHubInstance + + workflowDir := fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(uses)) + + return common.NewPipelineExecutor( + newMutexExecutor(cloneIfRequired(rc, *remoteReusableWorkflow, workflowDir)), + newReusableWorkflowExecutor(rc, workflowDir, fmt.Sprintf("./.github/workflows/%s", remoteReusableWorkflow.Filename)), + ) +} + +var ( + executorLock sync.Mutex +) + +func newMutexExecutor(executor common.Executor) common.Executor { + return func(ctx context.Context) error { + executorLock.Lock() + defer executorLock.Unlock() + + return executor(ctx) + } +} + +func cloneIfRequired(rc *RunContext, remoteReusableWorkflow remoteReusableWorkflow, targetDirectory string) common.Executor { + return common.NewConditionalExecutor( + func(ctx context.Context) bool { + _, err := os.Stat(targetDirectory) + notExists := errors.Is(err, fs.ErrNotExist) + return notExists + }, + git.NewGitCloneExecutor(git.NewGitCloneExecutorInput{ + URL: remoteReusableWorkflow.CloneURL(), + Ref: remoteReusableWorkflow.Ref, + Dir: targetDirectory, + Token: rc.Config.Token, + }), + nil, + ) +} + +func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string) common.Executor { + return func(ctx context.Context) error { + planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), true) + if err != nil { + return err + } + + plan, err := planner.PlanEvent("workflow_call") + if err != nil { + return err + } + + runner, err := NewReusableWorkflowRunner(rc) + if err != nil { + return err + } + + return runner.NewPlanExecutor(plan)(ctx) + } +} + +func NewReusableWorkflowRunner(rc *RunContext) (Runner, error) { + runner := &runnerImpl{ + config: rc.Config, + eventJSON: rc.EventJSON, + caller: &caller{ + runContext: rc, + }, + } + + return runner.configure() +} + +type remoteReusableWorkflow struct { + URL string + Org string + Repo string + Filename string + Ref string +} + +func (r *remoteReusableWorkflow) CloneURL() string { + return fmt.Sprintf("https://%s/%s/%s", r.URL, r.Org, r.Repo) +} + +func newRemoteReusableWorkflow(uses string) *remoteReusableWorkflow { + // GitHub docs: + // https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_iduses + r := regexp.MustCompile(`^([^/]+)/([^/]+)/.github/workflows/([^@]+)@(.*)$`) + matches := r.FindStringSubmatch(uses) + if len(matches) != 5 { + return nil + } + return &remoteReusableWorkflow{ + Org: matches[1], + Repo: matches[2], + Filename: matches[3], + Ref: matches[4], + URL: "github.com", + } +} diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 736a0b8..31d19b5 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -1,12 +1,16 @@ package runner import ( + "archive/tar" + "bufio" "context" "crypto/rand" + "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" + "io" "os" "path/filepath" "regexp" @@ -19,7 +23,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/nektos/act/pkg/common" - "github.com/nektos/act/pkg/common/git" "github.com/nektos/act/pkg/container" "github.com/nektos/act/pkg/exprparser" "github.com/nektos/act/pkg/model" @@ -33,9 +36,11 @@ type RunContext struct { Run *model.Run EventJSON string Env map[string]string + GlobalEnv map[string]string // to pass env changes of GITHUB_ENV and set-env correctly, due to dirty Env field ExtraPath []string CurrentStep string StepResults map[string]*model.StepResult + IntraActionState map[string]map[string]string ExprEval ExpressionEvaluator JobContainer container.ExecutionsEnvironment OutputMappings map[MappableOutput]MappableOutput @@ -44,6 +49,7 @@ type RunContext struct { Parent *RunContext Masks []string cleanUpJobContainer common.Executor + caller *caller // job calling this RunContext (reusable workflows) } func (rc *RunContext) AddMask(mask string) { @@ -56,7 +62,13 @@ type MappableOutput struct { } func (rc *RunContext) String() string { - return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name) + name := fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name) + if rc.caller != nil { + // prefix the reusable workflow with the caller job + // this is required to create unique container names + name = fmt.Sprintf("%s/%s", rc.caller.runContext.Run.JobID, name) + } + return name } // GetEnv returns the env for the context @@ -145,15 +157,15 @@ func (rc *RunContext) startHostEnvironment() common.Executor { _, _ = rand.Read(randBytes) miscpath := filepath.Join(cacheDir, hex.EncodeToString(randBytes)) actPath := filepath.Join(miscpath, "act") - if err := os.MkdirAll(actPath, 0777); err != nil { + if err := os.MkdirAll(actPath, 0o777); err != nil { return err } path := filepath.Join(miscpath, "hostexecutor") - if err := os.MkdirAll(path, 0777); err != nil { + if err := os.MkdirAll(path, 0o777); err != nil { return err } runnerTmp := filepath.Join(miscpath, "tmp") - if err := os.MkdirAll(runnerTmp, 0777); err != nil { + if err := os.MkdirAll(runnerTmp, 0o777); err != nil { return err } toolCache := filepath.Join(cacheDir, "tool_cache") @@ -169,29 +181,28 @@ func (rc *RunContext) startHostEnvironment() common.Executor { StdOut: logWriter, } rc.cleanUpJobContainer = rc.JobContainer.Remove() - rc.Env["RUNNER_TOOL_CACHE"] = toolCache - rc.Env["RUNNER_OS"] = runtime.GOOS - rc.Env["RUNNER_ARCH"] = runtime.GOARCH - rc.Env["RUNNER_TEMP"] = runnerTmp + for k, v := range rc.JobContainer.GetRunnerContext(ctx) { + if v, ok := v.(string); ok { + rc.Env[fmt.Sprintf("RUNNER_%s", strings.ToUpper(k))] = v + } + } for _, env := range os.Environ() { - i := strings.Index(env, "=") - if i > 0 { - rc.Env[env[0:i]] = env[i+1:] + if k, v, ok := strings.Cut(env, "="); ok { + // don't override + if _, ok := rc.Env[k]; !ok { + rc.Env[k] = v + } } } return common.NewPipelineExecutor( rc.JobContainer.Copy(rc.JobContainer.GetActPath()+"/", &container.FileEntry{ Name: "workflow/event.json", - Mode: 0644, + Mode: 0o644, Body: rc.EventJSON, }, &container.FileEntry{ Name: "workflow/envs.txt", - Mode: 0666, - Body: "", - }, &container.FileEntry{ - Name: "workflow/paths.txt", - Mode: 0666, + Mode: 0o666, Body: "", }), )(ctx) @@ -226,6 +237,7 @@ func (rc *RunContext) startJobContainer() common.Executor { envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_OS", "Linux")) envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_ARCH", container.RunnerArch(ctx))) envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp")) + envList = append(envList, fmt.Sprintf("%s=%s", "LANG", "C.UTF-8")) // Use same locale as GitHub Actions ext := container.LinuxContainerEnvironmentExtensions{} binds, mounts := rc.GetBindsAndMounts() @@ -268,19 +280,13 @@ func (rc *RunContext) startJobContainer() common.Executor { rc.stopJobContainer(), rc.JobContainer.Create(rc.Config.ContainerCapAdd, rc.Config.ContainerCapDrop), rc.JobContainer.Start(false), - rc.JobContainer.UpdateFromImageEnv(&rc.Env), - rc.JobContainer.UpdateFromEnv("/etc/environment", &rc.Env), rc.JobContainer.Copy(rc.JobContainer.GetActPath()+"/", &container.FileEntry{ Name: "workflow/event.json", - Mode: 0644, + Mode: 0o644, Body: rc.EventJSON, }, &container.FileEntry{ Name: "workflow/envs.txt", - Mode: 0666, - Body: "", - }, &container.FileEntry{ - Name: "workflow/paths.txt", - Mode: 0666, + Mode: 0o666, Body: "", }), )(ctx) @@ -293,6 +299,51 @@ func (rc *RunContext) execJobContainer(cmd []string, env map[string]string, user } } +func (rc *RunContext) ApplyExtraPath(ctx context.Context, env *map[string]string) { + if rc.ExtraPath != nil && len(rc.ExtraPath) > 0 { + path := rc.JobContainer.GetPathVariableName() + if (*env)[path] == "" { + cenv := map[string]string{} + var cpath string + if err := rc.JobContainer.UpdateFromImageEnv(&cenv)(ctx); err == nil { + if p, ok := cenv[path]; ok { + cpath = p + } + } + if len(cpath) == 0 { + cpath = rc.JobContainer.DefaultPathVariable() + } + (*env)[path] = cpath + } + (*env)[path] = rc.JobContainer.JoinPathVariable(append(rc.ExtraPath, (*env)[path])...) + } +} + +func (rc *RunContext) UpdateExtraPath(ctx context.Context, githubEnvPath string) error { + if common.Dryrun(ctx) { + return nil + } + pathTar, err := rc.JobContainer.GetContainerArchive(ctx, githubEnvPath) + if err != nil { + return err + } + defer pathTar.Close() + + reader := tar.NewReader(pathTar) + _, err = reader.Next() + if err != nil && err != io.EOF { + return err + } + s := bufio.NewScanner(reader) + for s.Scan() { + line := s.Text() + if len(line) > 0 { + rc.addPath(ctx, line) + } + } + return nil +} + // stopJobContainer removes the job container (if it exists) and its volume (if it exists) if !rc.Config.ReuseContainers func (rc *RunContext) stopJobContainer() common.Executor { return func(ctx context.Context) error { @@ -335,14 +386,18 @@ func (rc *RunContext) interpolateOutputs() common.Executor { func (rc *RunContext) startContainer() common.Executor { return func(ctx context.Context) error { - image := rc.platformImage(ctx) - if strings.EqualFold(image, "-self-hosted") { + if rc.IsHostEnv(ctx) { return rc.startHostEnvironment()(ctx) } return rc.startJobContainer()(ctx) } } +func (rc *RunContext) IsHostEnv(ctx context.Context) bool { + image := rc.platformImage(ctx) + return strings.EqualFold(image, "-self-hosted") +} + func (rc *RunContext) stopContainer() common.Executor { return rc.stopJobContainer() } @@ -370,16 +425,25 @@ func (rc *RunContext) steps() []*model.Step { // Executor returns a pipeline executor for all the steps in the job func (rc *RunContext) Executor() common.Executor { + var executor common.Executor + + switch rc.Run.Job().Type() { + case model.JobTypeDefault: + executor = newJobExecutor(rc, &stepFactoryImpl{}, rc) + case model.JobTypeReusableWorkflowLocal: + executor = newLocalReusableWorkflowExecutor(rc) + case model.JobTypeReusableWorkflowRemote: + executor = newRemoteReusableWorkflowExecutor(rc) + } + return func(ctx context.Context) error { - isEnabled, err := rc.isEnabled(ctx) + res, err := rc.isEnabled(ctx) if err != nil { return err } - - if isEnabled { - return newJobExecutor(rc, &stepFactoryImpl{}, rc)(ctx) + if res { + return executor(ctx) } - return nil } } @@ -421,7 +485,7 @@ func (rc *RunContext) options(ctx context.Context) string { job := rc.Run.Job() c := job.Container() if c == nil { - return "" + return rc.Config.ContainerOptions } return c.Options @@ -439,6 +503,10 @@ func (rc *RunContext) isEnabled(ctx context.Context) (bool, error) { return false, nil } + if job.Type() != model.JobTypeDefault { + return true, nil + } + img := rc.platformImage(ctx) if img == "" { if job.RunsOn() == nil { @@ -466,26 +534,16 @@ func mergeMaps(maps ...map[string]string) map[string]string { // deprecated: use createSimpleContainerName func createContainerName(parts ...string) string { - name := make([]string, 0) + name := strings.Join(parts, "-") pattern := regexp.MustCompile("[^a-zA-Z0-9]") - partLen := (30 / len(parts)) - 1 - for i, part := range parts { - if i == len(parts)-1 { - name = append(name, pattern.ReplaceAllString(part, "-")) - } else { - // If any part has a '-' on the end it is likely part of a matrix job. - // Let's preserve the number to prevent clashes in container names. - re := regexp.MustCompile("-[0-9]+$") - num := re.FindStringSubmatch(part) - if len(num) > 0 { - name = append(name, trimToLen(pattern.ReplaceAllString(part, "-"), partLen-len(num[0]))) - name = append(name, num[0]) - } else { - name = append(name, trimToLen(pattern.ReplaceAllString(part, "-"), partLen)) - } - } - } - return strings.ReplaceAll(strings.Trim(strings.Join(name, "-"), "-"), "--", "-") + name = pattern.ReplaceAllString(name, "-") + name = strings.ReplaceAll(name, "--", "-") + hash := sha256.Sum256([]byte(name)) + + // SHA256 is 64 hex characters. So trim name to 63 characters to make room for the hash and separator + trimmedName := strings.Trim(trimToLen(name, 63), "-") + + return fmt.Sprintf("%s-%x", trimmedName, hash) } func createSimpleContainerName(parts ...string) string { @@ -542,11 +600,20 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext EventName: rc.Config.EventName, Action: rc.CurrentStep, Token: rc.Config.Token, + Job: rc.Run.JobID, ActionPath: rc.ActionPath, RepositoryOwner: rc.Config.Env["GITHUB_REPOSITORY_OWNER"], RetentionDays: rc.Config.Env["GITHUB_RETENTION_DAYS"], RunnerPerflog: rc.Config.Env["RUNNER_PERFLOG"], RunnerTrackingID: rc.Config.Env["RUNNER_TRACKING_ID"], + Repository: rc.Config.Env["GITHUB_REPOSITORY"], + Ref: rc.Config.Env["GITHUB_REF"], + Sha: rc.Config.Env["SHA_REF"], + RefName: rc.Config.Env["GITHUB_REF_NAME"], + RefType: rc.Config.Env["GITHUB_REF_TYPE"], + BaseRef: rc.Config.Env["GITHUB_BASE_REF"], + HeadRef: rc.Config.Env["GITHUB_HEAD_REF"], + Workspace: rc.Config.Env["GITHUB_WORKSPACE"], } if rc.JobContainer != nil { ghc.EventPath = rc.JobContainer.GetActPath() + "/workflow/event.json" @@ -575,58 +642,45 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext ghc.Actor = "nektos/act" } - if preset := rc.Config.PresetGitHubContext; preset != nil { - ghc.Event = preset.Event - ghc.RunID = preset.RunID - ghc.RunNumber = preset.RunNumber - ghc.Actor = preset.Actor - ghc.Repository = preset.Repository - ghc.EventName = preset.EventName - ghc.Sha = preset.Sha - ghc.Ref = preset.Ref - ghc.RefName = preset.RefName - ghc.RefType = preset.RefType - ghc.HeadRef = preset.HeadRef - ghc.BaseRef = preset.BaseRef - ghc.Token = preset.Token - ghc.RepositoryOwner = preset.RepositoryOwner - ghc.RetentionDays = preset.RetentionDays - return ghc - } - - repoPath := rc.Config.Workdir - repo, err := git.FindGithubRepo(ctx, repoPath, rc.Config.GitHubInstance, rc.Config.RemoteName) - if err != nil { - logger.Warningf("unable to get git repo: %v", err) - } else { - ghc.Repository = repo - if ghc.RepositoryOwner == "" { - ghc.RepositoryOwner = strings.Split(repo, "/")[0] + { // Adapt to Gitea + if preset := rc.Config.PresetGitHubContext; preset != nil { + ghc.Event = preset.Event + ghc.RunID = preset.RunID + ghc.RunNumber = preset.RunNumber + ghc.Actor = preset.Actor + ghc.Repository = preset.Repository + ghc.EventName = preset.EventName + ghc.Sha = preset.Sha + ghc.Ref = preset.Ref + ghc.RefName = preset.RefName + ghc.RefType = preset.RefType + ghc.HeadRef = preset.HeadRef + ghc.BaseRef = preset.BaseRef + ghc.Token = preset.Token + ghc.RepositoryOwner = preset.RepositoryOwner + ghc.RetentionDays = preset.RetentionDays + return ghc } } if rc.EventJSON != "" { - err = json.Unmarshal([]byte(rc.EventJSON), &ghc.Event) + err := json.Unmarshal([]byte(rc.EventJSON), &ghc.Event) if err != nil { logger.Errorf("Unable to Unmarshal event '%s': %v", rc.EventJSON, err) } } - if ghc.EventName == "pull_request" || ghc.EventName == "pull_request_target" { - ghc.BaseRef = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "ref")) - ghc.HeadRef = asString(nestedMapLookup(ghc.Event, "pull_request", "head", "ref")) + ghc.SetBaseAndHeadRef() + repoPath := rc.Config.Workdir + ghc.SetRepositoryAndOwner(ctx, rc.Config.GitHubInstance, rc.Config.RemoteName, repoPath) + if ghc.Ref == "" { + ghc.SetRef(ctx, rc.Config.DefaultBranch, repoPath) + } + if ghc.Sha == "" { + ghc.SetSha(ctx, repoPath) } - ghc.SetRefAndSha(ctx, rc.Config.DefaultBranch, repoPath) - - // https://docs.github.com/en/actions/learn-github-actions/environment-variables - if strings.HasPrefix(ghc.Ref, "refs/tags/") { - ghc.RefType = "tag" - ghc.RefName = ghc.Ref[len("refs/tags/"):] - } else if strings.HasPrefix(ghc.Ref, "refs/heads/") { - ghc.RefType = "branch" - ghc.RefName = ghc.Ref[len("refs/heads/"):] - } + ghc.SetRefTypeAndName() return ghc } @@ -657,15 +711,6 @@ func isLocalCheckout(ghc *model.GithubContext, step *model.Step) bool { return true } -func asString(v interface{}) string { - if v == nil { - return "" - } else if s, ok := v.(string); ok { - return s - } - return "" -} - func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) { var ok bool @@ -685,8 +730,6 @@ func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubContext, env map[string]string) map[string]string { env["CI"] = "true" - env["GITHUB_ENV"] = rc.JobContainer.GetActPath() + "/workflow/envs.txt" - env["GITHUB_PATH"] = rc.JobContainer.GetActPath() + "/workflow/paths.txt" env["GITHUB_WORKFLOW"] = github.Workflow env["GITHUB_RUN_ID"] = github.RunID env["GITHUB_RUN_NUMBER"] = github.RunNumber @@ -705,27 +748,45 @@ func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubCon env["GITHUB_REF_NAME"] = github.RefName env["GITHUB_REF_TYPE"] = github.RefType env["GITHUB_TOKEN"] = github.Token - env["GITHUB_SERVER_URL"] = "https://github.com" - env["GITHUB_API_URL"] = "https://api.github.com" - env["GITHUB_GRAPHQL_URL"] = "https://api.github.com/graphql" - env["GITHUB_BASE_REF"] = github.BaseRef - env["GITHUB_HEAD_REF"] = github.HeadRef - env["GITHUB_JOB"] = rc.JobName + env["GITHUB_JOB"] = github.Job env["GITHUB_REPOSITORY_OWNER"] = github.RepositoryOwner env["GITHUB_RETENTION_DAYS"] = github.RetentionDays env["RUNNER_PERFLOG"] = github.RunnerPerflog env["RUNNER_TRACKING_ID"] = github.RunnerTrackingID + env["GITHUB_BASE_REF"] = github.BaseRef + env["GITHUB_HEAD_REF"] = github.HeadRef + + defaultServerURL := "https://github.com" + defaultAPIURL := "https://api.github.com" + defaultGraphqlURL := "https://api.github.com/graphql" + if rc.Config.GitHubInstance != "github.com" { - hasProtocol := strings.HasPrefix(rc.Config.GitHubInstance, "http://") || strings.HasPrefix(rc.Config.GitHubInstance, "https://") - if hasProtocol { - env["GITHUB_SERVER_URL"] = rc.Config.GitHubInstance - env["GITHUB_API_URL"] = fmt.Sprintf("%s/api/v1", rc.Config.GitHubInstance) - env["GITHUB_GRAPHQL_URL"] = "" // disable graphql url because Gitea doesn't support that - } else { - env["GITHUB_SERVER_URL"] = fmt.Sprintf("https://%s", rc.Config.GitHubInstance) - env["GITHUB_API_URL"] = fmt.Sprintf("https://%s/api/v1", rc.Config.GitHubInstance) - env["GITHUB_GRAPHQL_URL"] = "" // disable graphql url because Gitea doesn't support that + defaultServerURL = fmt.Sprintf("https://%s", rc.Config.GitHubInstance) + defaultAPIURL = fmt.Sprintf("https://%s/api/v3", rc.Config.GitHubInstance) + defaultGraphqlURL = fmt.Sprintf("https://%s/api/graphql", rc.Config.GitHubInstance) + } + + { // Adapt to Gitea + instance := rc.Config.GitHubInstance + if !strings.HasPrefix(instance, "http://") && + !strings.HasPrefix(instance, "https://") { + instance = "https://" + instance } + defaultServerURL = instance + defaultAPIURL = instance + "/api/v1" // the version of Gitea is v1 + defaultGraphqlURL = "" // Gitea doesn't support graphql + } + + if env["GITHUB_SERVER_URL"] == "" { + env["GITHUB_SERVER_URL"] = defaultServerURL + } + + if env["GITHUB_API_URL"] == "" { + env["GITHUB_API_URL"] = defaultAPIURL + } + + if env["GITHUB_GRAPHQL_URL"] == "" { + env["GITHUB_GRAPHQL_URL"] = defaultGraphqlURL } if rc.Config.ArtifactServerPath != "" { @@ -754,7 +815,7 @@ func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubCon func setActionRuntimeVars(rc *RunContext, env map[string]string) { actionsRuntimeURL := os.Getenv("ACTIONS_RUNTIME_URL") if actionsRuntimeURL == "" { - actionsRuntimeURL = fmt.Sprintf("http://%s:%s/", common.GetOutboundIP().String(), rc.Config.ArtifactServerPort) + actionsRuntimeURL = fmt.Sprintf("http://%s:%s/", rc.Config.ArtifactServerAddr, rc.Config.ArtifactServerPort) } env["ACTIONS_RUNTIME_URL"] = actionsRuntimeURL diff --git a/pkg/runner/run_context_test.go b/pkg/runner/run_context_test.go index 287cb46..86ad44e 100644 --- a/pkg/runner/run_context_test.go +++ b/pkg/runner/run_context_test.go @@ -144,6 +144,7 @@ func TestRunContext_EvalBool(t *testing.T) { // Check github context {in: "github.actor == 'nektos/act'", out: true}, {in: "github.actor == 'unknown'", out: false}, + {in: "github.job == 'job1'", out: true}, // The special ACT flag {in: "${{ env.ACT }}", out: true}, {in: "${{ !env.ACT }}", out: false}, @@ -364,6 +365,7 @@ func TestGetGitHubContext(t *testing.T) { StepResults: map[string]*model.StepResult{}, OutputMappings: map[MappableOutput]MappableOutput{}, } + rc.Run.JobID = "job1" ghc := rc.getGithubContext(context.Background()) @@ -392,6 +394,7 @@ func TestGetGitHubContext(t *testing.T) { assert.Equal(t, ghc.RepositoryOwner, owner) assert.Equal(t, ghc.RunnerPerflog, "/dev/null") assert.Equal(t, ghc.Token, rc.Config.Secrets["GITHUB_TOKEN"]) + assert.Equal(t, ghc.Job, "job1") } func TestGetGithubContextRef(t *testing.T) { @@ -410,7 +413,7 @@ func TestGetGithubContextRef(t *testing.T) { {event: "pull_request_target", json: `{"pull_request":{"base":{"ref": "main"}}}`, ref: "refs/heads/main"}, {event: "deployment", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, {event: "deployment_status", json: `{"deployment": {"ref": "tag-name"}}`, ref: "tag-name"}, - {event: "release", json: `{"release": {"tag_name": "tag-name"}}`, ref: "tag-name"}, + {event: "release", json: `{"release": {"tag_name": "tag-name"}}`, ref: "refs/tags/tag-name"}, } for _, data := range table { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 5aa1aae..d5c81b8 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -2,6 +2,7 @@ package runner import ( "context" + "encoding/json" "fmt" "os" "time" @@ -32,6 +33,7 @@ type Config struct { LogOutput bool // log the output from docker run JSONLogger bool // use json or text logger Env map[string]string // env for containers + Inputs map[string]string // manually passed action inputs Secrets map[string]string // list of secrets Token string // GitHub token InsecureSecrets bool // switch hiding output when printing to terminal @@ -40,12 +42,14 @@ type Config struct { UsernsMode string // user namespace to use ContainerArchitecture string // Desired OS/architecture platform for running containers ContainerDaemonSocket string // Path to Docker daemon socket + ContainerOptions string // Options for the job container UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true GitHubInstance string // GitHub instance to use, default "github.com" ContainerCapAdd []string // list of kernel capabilities to add to the containers ContainerCapDrop []string // list of kernel capabilities to remove from the containers AutoRemove bool // controls if the container is automatically removed upon workflow completion ArtifactServerPath string // the path where the artifact server stores uploads + ArtifactServerAddr string // the address the artifact server binds to ArtifactServerPort string // the port the artifact server binds to NoSkipCheckout bool // do not skip actions/checkout RemoteName string // remote name in local git repo config @@ -62,9 +66,14 @@ type Config struct { JobLoggerLevel *log.Level // the level of job logger } +type caller struct { + runContext *RunContext +} + type runnerImpl struct { config *Config eventJSON string + caller *caller // the job calling this runner (caller of a reusable workflow) } // New Creates a new Runner @@ -73,40 +82,46 @@ func New(runnerConfig *Config) (Runner, error) { config: runnerConfig, } + return runner.configure() +} + +func (runner *runnerImpl) configure() (Runner, error) { runner.eventJSON = "{}" - if runnerConfig.EventJSON != "" { - runner.eventJSON = runnerConfig.EventJSON - } else if runnerConfig.EventPath != "" { + if runner.config.EventJSON != "" { + runner.eventJSON = runner.config.EventJSON + } else if runner.config.EventPath != "" { log.Debugf("Reading event.json from %s", runner.config.EventPath) eventJSONBytes, err := os.ReadFile(runner.config.EventPath) if err != nil { return nil, err } runner.eventJSON = string(eventJSONBytes) + } else if len(runner.config.Inputs) != 0 { + eventMap := map[string]map[string]string{ + "inputs": runner.config.Inputs, + } + eventJSON, err := json.Marshal(eventMap) + if err != nil { + return nil, err + } + runner.eventJSON = string(eventJSON) } return runner, nil } // NewPlanExecutor ... -// -//nolint:gocyclo func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { maxJobNameLen := 0 stagePipeline := make([]common.Executor, 0) for i := range plan.Stages { - s := i stage := plan.Stages[i] stagePipeline = append(stagePipeline, func(ctx context.Context) error { pipeline := make([]common.Executor, 0) - for r, run := range stage.Runs { + for _, run := range stage.Runs { stageExecutor := make([]common.Executor, 0) job := run.Job() - if job.Uses != "" { - return fmt.Errorf("reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)") - } - if job.Strategy != nil { strategyRc := runner.newRunContext(ctx, run, nil) if err := strategyRc.NewExpressionEvaluator(ctx).EvaluateYamlNode(ctx, &job.Strategy.RawMatrix); err != nil { @@ -134,29 +149,8 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { maxJobNameLen = len(rc.String()) } stageExecutor = append(stageExecutor, func(ctx context.Context) error { - logger := common.Logger(ctx) jobName := fmt.Sprintf("%-*s", maxJobNameLen, rc.String()) - return rc.Executor().Finally(func(ctx context.Context) error { - isLastRunningContainer := func(currentStage int, currentRun int) bool { - return currentStage == len(plan.Stages)-1 && currentRun == len(stage.Runs)-1 - } - - if runner.config.AutoRemove && isLastRunningContainer(s, r) { - var cancel context.CancelFunc - if ctx.Err() == context.Canceled { - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - } - - log.Infof("Cleaning up container for job %s", rc.JobName) - - if err := rc.stopJobContainer()(ctx); err != nil { - logger.Errorf("Error while cleaning container: %v", err) - } - } - - return nil - })(common.WithJobErrorContainer(WithJobLogger(ctx, rc.Run.JobID, jobName, rc.Config, &rc.Masks, matrix))) + return rc.Executor()(common.WithJobErrorContainer(WithJobLogger(ctx, rc.Run.JobID, jobName, rc.Config, &rc.Masks, matrix))) }) } pipeline = append(pipeline, common.NewParallelExecutor(maxParallel, stageExecutor...)) @@ -196,8 +190,10 @@ func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, mat EventJSON: runner.eventJSON, StepResults: make(map[string]*model.StepResult), Matrix: matrix, + caller: runner.caller, } rc.ExprEval = rc.NewExpressionEvaluator(ctx) rc.Name = rc.ExprEval.Interpolate(ctx, run.String()) + return rc } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 9cb4ff4..60a8193 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -1,8 +1,10 @@ package runner import ( + "bytes" "context" "fmt" + "io" "os" "path/filepath" "runtime" @@ -22,6 +24,7 @@ var ( platforms map[string]string logLevel = log.DebugLevel workdir = "testdata" + secrets map[string]string ) func init() { @@ -42,14 +45,103 @@ func init() { if wd, err := filepath.Abs(workdir); err == nil { workdir = wd } + + secrets = map[string]string{} +} + +func TestNoWorkflowsFoundByPlanner(t *testing.T) { + planner, err := model.NewWorkflowPlanner("res", true) + assert.NoError(t, err) + + out := log.StandardLogger().Out + var buf bytes.Buffer + log.SetOutput(&buf) + log.SetLevel(log.DebugLevel) + plan, err := planner.PlanEvent("pull_request") + assert.NotNil(t, plan) + assert.NoError(t, err) + assert.Contains(t, buf.String(), "no workflows found by planner") + buf.Reset() + plan, err = planner.PlanAll() + assert.NotNil(t, plan) + assert.NoError(t, err) + assert.Contains(t, buf.String(), "no workflows found by planner") + log.SetOutput(out) +} + +func TestGraphMissingEvent(t *testing.T) { + planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-event.yml", true) + assert.NoError(t, err) + + out := log.StandardLogger().Out + var buf bytes.Buffer + log.SetOutput(&buf) + log.SetLevel(log.DebugLevel) + + plan, err := planner.PlanEvent("push") + assert.NoError(t, err) + assert.NotNil(t, plan) + assert.Equal(t, 0, len(plan.Stages)) + + assert.Contains(t, buf.String(), "no events found for workflow: no-event.yml") + log.SetOutput(out) +} + +func TestGraphMissingFirst(t *testing.T) { + planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-first.yml", true) + assert.NoError(t, err) + + plan, err := planner.PlanEvent("push") + assert.EqualError(t, err, "unable to build dependency graph for no first (no-first.yml)") + assert.NotNil(t, plan) + assert.Equal(t, 0, len(plan.Stages)) +} + +func TestGraphWithMissing(t *testing.T) { + planner, err := model.NewWorkflowPlanner("testdata/issue-1595/missing.yml", true) + assert.NoError(t, err) + + out := log.StandardLogger().Out + var buf bytes.Buffer + log.SetOutput(&buf) + log.SetLevel(log.DebugLevel) + + plan, err := planner.PlanEvent("push") + assert.NotNil(t, plan) + assert.Equal(t, 0, len(plan.Stages)) + assert.EqualError(t, err, "unable to build dependency graph for missing (missing.yml)") + assert.Contains(t, buf.String(), "unable to build dependency graph for missing (missing.yml)") + log.SetOutput(out) +} + +func TestGraphWithSomeMissing(t *testing.T) { + log.SetLevel(log.DebugLevel) + + planner, err := model.NewWorkflowPlanner("testdata/issue-1595/", true) + assert.NoError(t, err) + + out := log.StandardLogger().Out + var buf bytes.Buffer + log.SetOutput(&buf) + log.SetLevel(log.DebugLevel) + + plan, err := planner.PlanAll() + assert.Error(t, err, "unable to build dependency graph for no first (no-first.yml)") + assert.NotNil(t, plan) + assert.Equal(t, 1, len(plan.Stages)) + assert.Contains(t, buf.String(), "unable to build dependency graph for missing (missing.yml)") + assert.Contains(t, buf.String(), "unable to build dependency graph for no first (no-first.yml)") + log.SetOutput(out) } func TestGraphEvent(t *testing.T) { planner, err := model.NewWorkflowPlanner("testdata/basic", true) - assert.Nil(t, err) + assert.NoError(t, err) - plan := planner.PlanEvent("push") - assert.Nil(t, err) + plan, err := planner.PlanEvent("push") + assert.NoError(t, err) + assert.NotNil(t, plan) + assert.NotNil(t, plan.Stages) assert.Equal(t, len(plan.Stages), 3, "stages") assert.Equal(t, len(plan.Stages[0].Runs), 1, "stage0.runs") assert.Equal(t, len(plan.Stages[1].Runs), 1, "stage1.runs") @@ -58,8 +150,10 @@ func TestGraphEvent(t *testing.T) { assert.Equal(t, plan.Stages[1].Runs[0].JobID, "build", "jobid") assert.Equal(t, plan.Stages[2].Runs[0].JobID, "test", "jobid") - plan = planner.PlanEvent("release") - assert.Equal(t, len(plan.Stages), 0, "stages") + plan, err = planner.PlanEvent("release") + assert.NoError(t, err) + assert.NotNil(t, plan) + assert.Equal(t, 0, len(plan.Stages)) } type TestJobFileInfo struct { @@ -68,6 +162,7 @@ type TestJobFileInfo struct { eventName string errorMessage string platforms map[string]string + secrets map[string]string } func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config) { @@ -88,6 +183,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config ReuseContainers: false, Env: cfg.Env, Secrets: cfg.Secrets, + Inputs: cfg.Inputs, GitHubInstance: "github.com", ContainerArchitecture: cfg.ContainerArchitecture, } @@ -98,13 +194,15 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config planner, err := model.NewWorkflowPlanner(fullWorkflowPath, true) assert.Nil(t, err, fullWorkflowPath) - plan := planner.PlanEvent(j.eventName) - - err = runner.NewPlanExecutor(plan)(ctx) - if j.errorMessage == "" { - assert.Nil(t, err, fullWorkflowPath) - } else { - assert.Error(t, err, j.errorMessage) + plan, err := planner.PlanEvent(j.eventName) + assert.True(t, (err == nil) != (plan == nil), "PlanEvent should return either a plan or an error") + if err == nil && plan != nil { + err = runner.NewPlanExecutor(plan)(ctx) + if j.errorMessage == "" { + assert.Nil(t, err, fullWorkflowPath) + } else { + assert.Error(t, err, j.errorMessage) + } } fmt.Println("::endgroup::") @@ -119,81 +217,96 @@ func TestRunEvent(t *testing.T) { tables := []TestJobFileInfo{ // Shells - {workdir, "shells/defaults", "push", "", platforms}, + {workdir, "shells/defaults", "push", "", platforms, secrets}, // TODO: figure out why it fails // {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh - {workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}}, // custom image with pwsh - {workdir, "shells/bash", "push", "", platforms}, - {workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}}, // slim doesn't have python - {workdir, "shells/sh", "push", "", platforms}, + {workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, secrets}, // custom image with pwsh + {workdir, "shells/bash", "push", "", platforms, secrets}, + {workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}, secrets}, // slim doesn't have python + {workdir, "shells/sh", "push", "", platforms, secrets}, // Local action - {workdir, "local-action-docker-url", "push", "", platforms}, - {workdir, "local-action-dockerfile", "push", "", platforms}, - {workdir, "local-action-via-composite-dockerfile", "push", "", platforms}, - {workdir, "local-action-js", "push", "", platforms}, + {workdir, "local-action-docker-url", "push", "", platforms, secrets}, + {workdir, "local-action-dockerfile", "push", "", platforms, secrets}, + {workdir, "local-action-via-composite-dockerfile", "push", "", platforms, secrets}, + {workdir, "local-action-js", "push", "", platforms, secrets}, // Uses - {workdir, "uses-composite", "push", "", platforms}, - {workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms}, - {workdir, "uses-nested-composite", "push", "", platforms}, - {workdir, "remote-action-composite-js-pre-with-defaults", "push", "", platforms}, - {workdir, "uses-workflow", "push", "reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)", platforms}, - {workdir, "uses-docker-url", "push", "", platforms}, - {workdir, "act-composite-env-test", "push", "", platforms}, + {workdir, "uses-composite", "push", "", platforms, secrets}, + {workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms, secrets}, + {workdir, "uses-nested-composite", "push", "", platforms, secrets}, + {workdir, "remote-action-composite-js-pre-with-defaults", "push", "", platforms, secrets}, + {workdir, "uses-workflow", "push", "", platforms, map[string]string{"secret": "keep_it_private"}}, + {workdir, "uses-workflow", "pull_request", "", platforms, map[string]string{"secret": "keep_it_private"}}, + {workdir, "uses-docker-url", "push", "", platforms, secrets}, + {workdir, "act-composite-env-test", "push", "", platforms, secrets}, // Eval - {workdir, "evalmatrix", "push", "", platforms}, - {workdir, "evalmatrixneeds", "push", "", platforms}, - {workdir, "evalmatrixneeds2", "push", "", platforms}, - {workdir, "evalmatrix-merge-map", "push", "", platforms}, - {workdir, "evalmatrix-merge-array", "push", "", platforms}, - {workdir, "issue-1195", "push", "", platforms}, + {workdir, "evalmatrix", "push", "", platforms, secrets}, + {workdir, "evalmatrixneeds", "push", "", platforms, secrets}, + {workdir, "evalmatrixneeds2", "push", "", platforms, secrets}, + {workdir, "evalmatrix-merge-map", "push", "", platforms, secrets}, + {workdir, "evalmatrix-merge-array", "push", "", platforms, secrets}, + {workdir, "issue-1195", "push", "", platforms, secrets}, - {workdir, "basic", "push", "", platforms}, - {workdir, "fail", "push", "exit with `FAILURE`: 1", platforms}, - {workdir, "runs-on", "push", "", platforms}, - {workdir, "checkout", "push", "", platforms}, - {workdir, "job-container", "push", "", platforms}, - {workdir, "job-container-non-root", "push", "", platforms}, - {workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms}, - {workdir, "container-hostname", "push", "", platforms}, - {workdir, "remote-action-docker", "push", "", platforms}, - {workdir, "remote-action-js", "push", "", platforms}, - {workdir, "remote-action-js", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:runner-latest"}}, // Test if this works with non root container - {workdir, "matrix", "push", "", platforms}, - {workdir, "matrix-include-exclude", "push", "", platforms}, - {workdir, "commands", "push", "", platforms}, - {workdir, "workdir", "push", "", platforms}, - {workdir, "defaults-run", "push", "", platforms}, - {workdir, "composite-fail-with-output", "push", "", platforms}, - {workdir, "issue-597", "push", "", platforms}, - {workdir, "issue-598", "push", "", platforms}, - {workdir, "if-env-act", "push", "", platforms}, - {workdir, "env-and-path", "push", "", platforms}, - {workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms}, - {workdir, "outputs", "push", "", platforms}, - {workdir, "networking", "push", "", platforms}, - {workdir, "steps-context/conclusion", "push", "", platforms}, - {workdir, "steps-context/outcome", "push", "", platforms}, - {workdir, "job-status-check", "push", "job 'fail' failed", platforms}, - {workdir, "if-expressions", "push", "Job 'mytest' failed", platforms}, - {workdir, "actions-environment-and-context-tests", "push", "", platforms}, - {workdir, "uses-action-with-pre-and-post-step", "push", "", platforms}, - {workdir, "evalenv", "push", "", platforms}, - {workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms}, - {workdir, "workflow_dispatch", "workflow_dispatch", "", platforms}, - {workdir, "workflow_dispatch_no_inputs_mapping", "workflow_dispatch", "", platforms}, - {workdir, "workflow_dispatch-scalar", "workflow_dispatch", "", platforms}, - {workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms}, - {"../model/testdata", "strategy", "push", "", platforms}, // TODO: move all testdata into pkg so we can validate it with planner and runner + {workdir, "basic", "push", "", platforms, secrets}, + {workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets}, + {workdir, "runs-on", "push", "", platforms, secrets}, + {workdir, "checkout", "push", "", platforms, secrets}, + {workdir, "job-container", "push", "", platforms, secrets}, + {workdir, "job-container-non-root", "push", "", platforms, secrets}, + {workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets}, + {workdir, "container-hostname", "push", "", platforms, secrets}, + {workdir, "remote-action-docker", "push", "", platforms, secrets}, + {workdir, "remote-action-js", "push", "", platforms, secrets}, + {workdir, "remote-action-js-node-user", "push", "", platforms, secrets}, // Test if this works with non root container + {workdir, "matrix", "push", "", platforms, secrets}, + {workdir, "matrix-include-exclude", "push", "", platforms, secrets}, + {workdir, "matrix-exitcode", "push", "Job 'test' failed", platforms, secrets}, + {workdir, "commands", "push", "", platforms, secrets}, + {workdir, "workdir", "push", "", platforms, secrets}, + {workdir, "defaults-run", "push", "", platforms, secrets}, + {workdir, "composite-fail-with-output", "push", "", platforms, secrets}, + {workdir, "issue-597", "push", "", platforms, secrets}, + {workdir, "issue-598", "push", "", platforms, secrets}, + {workdir, "if-env-act", "push", "", platforms, secrets}, + {workdir, "env-and-path", "push", "", platforms, secrets}, + {workdir, "environment-files", "push", "", platforms, secrets}, + {workdir, "GITHUB_STATE", "push", "", platforms, secrets}, + {workdir, "environment-files-parser-bug", "push", "", platforms, secrets}, + {workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets}, + {workdir, "outputs", "push", "", platforms, secrets}, + {workdir, "networking", "push", "", platforms, secrets}, + {workdir, "steps-context/conclusion", "push", "", platforms, secrets}, + {workdir, "steps-context/outcome", "push", "", platforms, secrets}, + {workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets}, + {workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets}, + {workdir, "actions-environment-and-context-tests", "push", "", platforms, secrets}, + {workdir, "uses-action-with-pre-and-post-step", "push", "", platforms, secrets}, + {workdir, "evalenv", "push", "", platforms, secrets}, + {workdir, "docker-action-custom-path", "push", "", platforms, secrets}, + {workdir, "GITHUB_ENV-use-in-env-ctx", "push", "", platforms, secrets}, + {workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets}, + {workdir, "workflow_dispatch", "workflow_dispatch", "", platforms, secrets}, + {workdir, "workflow_dispatch_no_inputs_mapping", "workflow_dispatch", "", platforms, secrets}, + {workdir, "workflow_dispatch-scalar", "workflow_dispatch", "", platforms, secrets}, + {workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms, secrets}, + {workdir, "job-needs-context-contains-result", "push", "", platforms, secrets}, + {"../model/testdata", "strategy", "push", "", platforms, secrets}, // TODO: move all testdata into pkg so we can validate it with planner and runner // {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes - {"../model/testdata", "container-volumes", "push", "", platforms}, + {"../model/testdata", "container-volumes", "push", "", platforms, secrets}, + {workdir, "path-handling", "push", "", platforms, secrets}, + {workdir, "do-not-leak-step-env-in-composite", "push", "", platforms, secrets}, + {workdir, "set-env-step-env-override", "push", "", platforms, secrets}, + {workdir, "set-env-new-env-file-per-step", "push", "", platforms, secrets}, + {workdir, "no-panic-on-invalid-composite-action", "push", "jobs failed due to invalid action", platforms, secrets}, } for _, table := range tables { t.Run(table.workflowPath, func(t *testing.T) { - config := &Config{} + config := &Config{ + Secrets: table.secrets, + } eventFile := filepath.Join(workdir, table.workflowPath, "event.json") if _, err := os.Stat(eventFile); err == nil { @@ -221,51 +334,51 @@ func TestRunEventHostEnvironment(t *testing.T) { tables = append(tables, []TestJobFileInfo{ // Shells - {workdir, "shells/defaults", "push", "", platforms}, - {workdir, "shells/pwsh", "push", "", platforms}, - {workdir, "shells/bash", "push", "", platforms}, - {workdir, "shells/python", "push", "", platforms}, - {workdir, "shells/sh", "push", "", platforms}, + {workdir, "shells/defaults", "push", "", platforms, secrets}, + {workdir, "shells/pwsh", "push", "", platforms, secrets}, + {workdir, "shells/bash", "push", "", platforms, secrets}, + {workdir, "shells/python", "push", "", platforms, secrets}, + {workdir, "shells/sh", "push", "", platforms, secrets}, // Local action - {workdir, "local-action-js", "push", "", platforms}, + {workdir, "local-action-js", "push", "", platforms, secrets}, // Uses - {workdir, "uses-composite", "push", "", platforms}, - {workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms}, - {workdir, "uses-nested-composite", "push", "", platforms}, - {workdir, "act-composite-env-test", "push", "", platforms}, + {workdir, "uses-composite", "push", "", platforms, secrets}, + {workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms, secrets}, + {workdir, "uses-nested-composite", "push", "", platforms, secrets}, + {workdir, "act-composite-env-test", "push", "", platforms, secrets}, // Eval - {workdir, "evalmatrix", "push", "", platforms}, - {workdir, "evalmatrixneeds", "push", "", platforms}, - {workdir, "evalmatrixneeds2", "push", "", platforms}, - {workdir, "evalmatrix-merge-map", "push", "", platforms}, - {workdir, "evalmatrix-merge-array", "push", "", platforms}, - {workdir, "issue-1195", "push", "", platforms}, + {workdir, "evalmatrix", "push", "", platforms, secrets}, + {workdir, "evalmatrixneeds", "push", "", platforms, secrets}, + {workdir, "evalmatrixneeds2", "push", "", platforms, secrets}, + {workdir, "evalmatrix-merge-map", "push", "", platforms, secrets}, + {workdir, "evalmatrix-merge-array", "push", "", platforms, secrets}, + {workdir, "issue-1195", "push", "", platforms, secrets}, - {workdir, "fail", "push", "exit with `FAILURE`: 1", platforms}, - {workdir, "runs-on", "push", "", platforms}, - {workdir, "checkout", "push", "", platforms}, - {workdir, "remote-action-js", "push", "", platforms}, - {workdir, "matrix", "push", "", platforms}, - {workdir, "matrix-include-exclude", "push", "", platforms}, - {workdir, "commands", "push", "", platforms}, - {workdir, "defaults-run", "push", "", platforms}, - {workdir, "composite-fail-with-output", "push", "", platforms}, - {workdir, "issue-597", "push", "", platforms}, - {workdir, "issue-598", "push", "", platforms}, - {workdir, "if-env-act", "push", "", platforms}, - {workdir, "env-and-path", "push", "", platforms}, - {workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms}, - {workdir, "outputs", "push", "", platforms}, - {workdir, "steps-context/conclusion", "push", "", platforms}, - {workdir, "steps-context/outcome", "push", "", platforms}, - {workdir, "job-status-check", "push", "job 'fail' failed", platforms}, - {workdir, "if-expressions", "push", "Job 'mytest' failed", platforms}, - {workdir, "uses-action-with-pre-and-post-step", "push", "", platforms}, - {workdir, "evalenv", "push", "", platforms}, - {workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms}, + {workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets}, + {workdir, "runs-on", "push", "", platforms, secrets}, + {workdir, "checkout", "push", "", platforms, secrets}, + {workdir, "remote-action-js", "push", "", platforms, secrets}, + {workdir, "matrix", "push", "", platforms, secrets}, + {workdir, "matrix-include-exclude", "push", "", platforms, secrets}, + {workdir, "commands", "push", "", platforms, secrets}, + {workdir, "defaults-run", "push", "", platforms, secrets}, + {workdir, "composite-fail-with-output", "push", "", platforms, secrets}, + {workdir, "issue-597", "push", "", platforms, secrets}, + {workdir, "issue-598", "push", "", platforms, secrets}, + {workdir, "if-env-act", "push", "", platforms, secrets}, + {workdir, "env-and-path", "push", "", platforms, secrets}, + {workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets}, + {workdir, "outputs", "push", "", platforms, secrets}, + {workdir, "steps-context/conclusion", "push", "", platforms, secrets}, + {workdir, "steps-context/outcome", "push", "", platforms, secrets}, + {workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets}, + {workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets}, + {workdir, "uses-action-with-pre-and-post-step", "push", "", platforms, secrets}, + {workdir, "evalenv", "push", "", platforms, secrets}, + {workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets}, }...) } if runtime.GOOS == "windows" { @@ -274,16 +387,22 @@ func TestRunEventHostEnvironment(t *testing.T) { } tables = append(tables, []TestJobFileInfo{ - {workdir, "windows-prepend-path", "push", "", platforms}, - {workdir, "windows-add-env", "push", "", platforms}, + {workdir, "windows-prepend-path", "push", "", platforms, secrets}, + {workdir, "windows-add-env", "push", "", platforms, secrets}, }...) } else { platforms := map[string]string{ - "self-hosted": "-self-hosted", + "self-hosted": "-self-hosted", + "ubuntu-latest": "-self-hosted", } tables = append(tables, []TestJobFileInfo{ - {workdir, "nix-prepend-path", "push", "", platforms}, + {workdir, "nix-prepend-path", "push", "", platforms, secrets}, + {workdir, "inputs-via-env-context", "push", "", platforms, secrets}, + {workdir, "do-not-leak-step-env-in-composite", "push", "", platforms, secrets}, + {workdir, "set-env-step-env-override", "push", "", platforms, secrets}, + {workdir, "set-env-new-env-file-per-step", "push", "", platforms, secrets}, + {workdir, "no-panic-on-invalid-composite-action", "push", "jobs failed due to invalid action", platforms, secrets}, }...) } @@ -303,17 +422,17 @@ func TestDryrunEvent(t *testing.T) { tables := []TestJobFileInfo{ // Shells - {workdir, "shells/defaults", "push", "", platforms}, - {workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}}, // custom image with pwsh - {workdir, "shells/bash", "push", "", platforms}, - {workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}}, // slim doesn't have python - {workdir, "shells/sh", "push", "", platforms}, + {workdir, "shells/defaults", "push", "", platforms, secrets}, + {workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, secrets}, // custom image with pwsh + {workdir, "shells/bash", "push", "", platforms, secrets}, + {workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}, secrets}, // slim doesn't have python + {workdir, "shells/sh", "push", "", platforms, secrets}, // Local action - {workdir, "local-action-docker-url", "push", "", platforms}, - {workdir, "local-action-dockerfile", "push", "", platforms}, - {workdir, "local-action-via-composite-dockerfile", "push", "", platforms}, - {workdir, "local-action-js", "push", "", platforms}, + {workdir, "local-action-docker-url", "push", "", platforms, secrets}, + {workdir, "local-action-dockerfile", "push", "", platforms, secrets}, + {workdir, "local-action-via-composite-dockerfile", "push", "", platforms, secrets}, + {workdir, "local-action-js", "push", "", platforms, secrets}, } for _, table := range tables { @@ -323,6 +442,30 @@ func TestDryrunEvent(t *testing.T) { } } +func TestDockerActionForcePullForceRebuild(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := context.Background() + + config := &Config{ + ForcePull: true, + ForceRebuild: true, + } + + tables := []TestJobFileInfo{ + {workdir, "local-action-dockerfile", "push", "", platforms, secrets}, + {workdir, "local-action-via-composite-dockerfile", "push", "", platforms, secrets}, + } + + for _, table := range tables { + t.Run(table.workflowPath, func(t *testing.T) { + table.runTest(ctx, t, config) + }) + } +} + func TestRunDifferentArchitecture(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") @@ -339,6 +482,17 @@ func TestRunDifferentArchitecture(t *testing.T) { tjfi.runTest(context.Background(), t, &Config{ContainerArchitecture: "linux/arm64"}) } +type maskJobLoggerFactory struct { + Output bytes.Buffer +} + +func (f *maskJobLoggerFactory) WithJobLogger() *log.Logger { + logger := log.New() + logger.SetOutput(io.MultiWriter(&f.Output, os.Stdout)) + logger.SetLevel(log.DebugLevel) + return logger +} + func TestMaskValues(t *testing.T) { assertNoSecret := func(text string, secret string) { index := strings.Index(text, "composite secret") @@ -362,9 +516,9 @@ func TestMaskValues(t *testing.T) { platforms: platforms, } - output := captureOutput(t, func() { - tjfi.runTest(context.Background(), t, &Config{}) - }) + logger := &maskJobLoggerFactory{} + tjfi.runTest(WithJobLoggerFactory(common.WithLogger(context.Background(), logger.WithJobLogger()), logger), t, &Config{}) + output := logger.Output.String() assertNoSecret(output, "secret value") assertNoSecret(output, "YWJjCg==") @@ -392,6 +546,27 @@ func TestRunEventSecrets(t *testing.T) { tjfi.runTest(context.Background(), t, &Config{Secrets: secrets, Env: env}) } +func TestRunActionInputs(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + workflowPath := "input-from-cli" + + tjfi := TestJobFileInfo{ + workdir: workdir, + workflowPath: workflowPath, + eventName: "workflow_dispatch", + errorMessage: "", + platforms: platforms, + } + + inputs := map[string]string{ + "SOME_INPUT": "input", + } + + tjfi.runTest(context.Background(), t, &Config{Inputs: inputs}) +} + func TestRunEventPullRequest(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") diff --git a/pkg/runner/step.go b/pkg/runner/step.go index f730ac1..7cc355f 100644 --- a/pkg/runner/step.go +++ b/pkg/runner/step.go @@ -44,16 +44,16 @@ func (s stepStage) String() string { return "Unknown" } -func (s stepStage) getStepName(stepModel *model.Step) string { - switch s { - case stepStagePre: - return fmt.Sprintf("pre-%s", stepModel.ID) - case stepStageMain: - return stepModel.ID - case stepStagePost: - return fmt.Sprintf("post-%s", stepModel.ID) +func processRunnerEnvFileCommand(ctx context.Context, fileName string, rc *RunContext, setter func(context.Context, map[string]string, string)) error { + env := map[string]string{} + err := rc.JobContainer.UpdateFromEnv(path.Join(rc.JobContainer.GetActPath(), fileName), &env)(ctx) + if err != nil { + return err } - return "unknown" + for k, v := range env { + setter(ctx, map[string]string{"name": k}, v) + } + return nil } func runStepExecutor(step step, stage stepStage, executor common.Executor) common.Executor { @@ -63,13 +63,16 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo stepModel := step.getStepModel() ifExpression := step.getIfExpression(ctx, stage) - rc.CurrentStep = stage.getStepName(stepModel) + rc.CurrentStep = stepModel.ID - rc.StepResults[rc.CurrentStep] = &model.StepResult{ + stepResult := &model.StepResult{ Outcome: model.StepStatusSuccess, Conclusion: model.StepStatusSuccess, Outputs: make(map[string]string), } + if stage == stepStageMain { + rc.StepResults[rc.CurrentStep] = stepResult + } err := setupEnv(ctx, step) if err != nil { @@ -78,15 +81,15 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo runStep, err := isStepEnabled(ctx, ifExpression, step, stage) if err != nil { - rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure - rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure + stepResult.Conclusion = model.StepStatusFailure + stepResult.Outcome = model.StepStatusFailure return err } if !runStep { - rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusSkipped - rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusSkipped - logger.WithField("stepResult", rc.StepResults[rc.CurrentStep].Outcome).Debugf("Skipping step '%s' due to '%s'", stepModel, ifExpression) + stepResult.Conclusion = model.StepStatusSkipped + stepResult.Outcome = model.StepStatusSkipped + logger.WithField("stepResult", stepResult.Outcome).Debugf("Skipping step '%s' due to '%s'", stepModel, ifExpression) return nil } @@ -98,58 +101,79 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo // Prepare and clean Runner File Commands actPath := rc.JobContainer.GetActPath() + outputFileCommand := path.Join("workflow", "outputcmd.txt") - stateFileCommand := path.Join("workflow", "statecmd.txt") (*step.getEnv())["GITHUB_OUTPUT"] = path.Join(actPath, outputFileCommand) + + stateFileCommand := path.Join("workflow", "statecmd.txt") (*step.getEnv())["GITHUB_STATE"] = path.Join(actPath, stateFileCommand) + + pathFileCommand := path.Join("workflow", "pathcmd.txt") + (*step.getEnv())["GITHUB_PATH"] = path.Join(actPath, pathFileCommand) + + envFileCommand := path.Join("workflow", "envs.txt") + (*step.getEnv())["GITHUB_ENV"] = path.Join(actPath, envFileCommand) + + summaryFileCommand := path.Join("workflow", "SUMMARY.md") + (*step.getEnv())["GITHUB_STEP_SUMMARY"] = path.Join(actPath, summaryFileCommand) + _ = rc.JobContainer.Copy(actPath, &container.FileEntry{ Name: outputFileCommand, - Mode: 0666, + Mode: 0o666, }, &container.FileEntry{ Name: stateFileCommand, + Mode: 0o666, + }, &container.FileEntry{ + Name: pathFileCommand, + Mode: 0o666, + }, &container.FileEntry{ + Name: envFileCommand, Mode: 0666, + }, &container.FileEntry{ + Name: summaryFileCommand, + Mode: 0o666, })(ctx) err = executor(ctx) if err == nil { - logger.WithField("stepResult", rc.StepResults[rc.CurrentStep].Outcome).Infof(" \u2705 Success - %s %s", stage, stepString) + logger.WithField("stepResult", stepResult.Outcome).Infof(" \u2705 Success - %s %s", stage, stepString) } else { - rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure + stepResult.Outcome = model.StepStatusFailure continueOnError, parseErr := isContinueOnError(ctx, stepModel.RawContinueOnError, step, stage) if parseErr != nil { - rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure + stepResult.Conclusion = model.StepStatusFailure return parseErr } if continueOnError { logger.Infof("Failed but continue next step") err = nil - rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusSuccess + stepResult.Conclusion = model.StepStatusSuccess } else { - rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure + stepResult.Conclusion = model.StepStatusFailure } - logger.WithField("stepResult", rc.StepResults[rc.CurrentStep].Outcome).Errorf(" \u274C Failure - %s %s", stage, stepString) + logger.WithField("stepResult", stepResult.Outcome).Errorf(" \u274C Failure - %s %s", stage, stepString) } // Process Runner File Commands orgerr := err - state := map[string]string{} - err = rc.JobContainer.UpdateFromEnv(path.Join(actPath, stateFileCommand), &state)(ctx) + err = processRunnerEnvFileCommand(ctx, envFileCommand, rc, rc.setEnv) if err != nil { return err } - for k, v := range state { - rc.saveState(ctx, map[string]string{"name": k}, v) - } - output := map[string]string{} - err = rc.JobContainer.UpdateFromEnv(path.Join(actPath, outputFileCommand), &output)(ctx) + err = processRunnerEnvFileCommand(ctx, stateFileCommand, rc, rc.saveState) if err != nil { return err } - for k, v := range output { - rc.setOutput(ctx, map[string]string{"name": k}, v) + err = processRunnerEnvFileCommand(ctx, outputFileCommand, rc, rc.setOutput) + if err != nil { + return err + } + err = rc.UpdateExtraPath(ctx, path.Join(actPath, pathFileCommand)) + if err != nil { + return err } if orgerr != nil { return orgerr @@ -162,24 +186,22 @@ func setupEnv(ctx context.Context, step step) error { rc := step.getRunContext() mergeEnv(ctx, step) - err := rc.JobContainer.UpdateFromImageEnv(step.getEnv())(ctx) - if err != nil { - return err - } - err = rc.JobContainer.UpdateFromEnv((*step.getEnv())["GITHUB_ENV"], step.getEnv())(ctx) - if err != nil { - return err - } - err = rc.JobContainer.UpdateFromPath(step.getEnv())(ctx) - if err != nil { - return err - } // merge step env last, since it should not be overwritten mergeIntoMap(step.getEnv(), step.getStepModel().GetEnv()) exprEval := rc.NewExpressionEvaluator(ctx) for k, v := range *step.getEnv() { - (*step.getEnv())[k] = exprEval.Interpolate(ctx, v) + if !strings.HasPrefix(k, "INPUT_") { + (*step.getEnv())[k] = exprEval.Interpolate(ctx, v) + } + } + // after we have an evaluated step context, update the expressions evaluator with a new env context + // you can use step level env in the with property of a uses construct + exprEval = rc.NewExpressionEvaluatorWithEnv(ctx, *step.getEnv()) + for k, v := range *step.getEnv() { + if strings.HasPrefix(k, "INPUT_") { + (*step.getEnv())[k] = exprEval.Interpolate(ctx, v) + } } common.Logger(ctx).Debugf("setupEnv => %v", *step.getEnv()) @@ -199,14 +221,6 @@ func mergeEnv(ctx context.Context, step step) { mergeIntoMap(env, rc.GetEnv()) } - path := rc.JobContainer.GetPathVariableName() - if (*env)[path] == "" { - (*env)[path] = rc.JobContainer.DefaultPathVariable() - } - if rc.ExtraPath != nil && len(rc.ExtraPath) > 0 { - (*env)[path] = rc.JobContainer.JoinPathVariable(append(rc.ExtraPath, (*env)[path])...) - } - rc.withGithubEnv(ctx, step.getGithubContext(ctx), *env) } diff --git a/pkg/runner/step_action_local_test.go b/pkg/runner/step_action_local_test.go index 6390289..5fe7f29 100644 --- a/pkg/runner/step_action_local_test.go +++ b/pkg/runner/step_action_local_test.go @@ -1,7 +1,9 @@ package runner import ( + "bytes" "context" + "io" "path/filepath" "strings" "testing" @@ -67,7 +69,7 @@ func TestStepActionLocalTest(t *testing.T) { salm.On("readAction", sal.Step, filepath.Clean("/tmp/path/to/action"), "", mock.Anything, mock.Anything). Return(&model.Action{}, nil) - cm.On("UpdateFromImageEnv", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(ctx context.Context) error { return nil }) @@ -75,14 +77,6 @@ func TestStepActionLocalTest(t *testing.T) { return nil }) - cm.On("UpdateFromPath", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { - return nil - }) - - cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(ctx context.Context) error { - return nil - }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -91,6 +85,8 @@ func TestStepActionLocalTest(t *testing.T) { return nil }) + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) + salm.On("runAction", sal, filepath.Clean("/tmp/path/to/action"), (*remoteAction)(nil)).Return(func(ctx context.Context) error { return nil }) @@ -107,13 +103,12 @@ func TestStepActionLocalTest(t *testing.T) { func TestStepActionLocalPost(t *testing.T) { table := []struct { - name string - stepModel *model.Step - actionModel *model.Action - initialStepResults map[string]*model.StepResult - expectedPostStepResult *model.StepResult - err error - mocks struct { + name string + stepModel *model.Step + actionModel *model.Action + initialStepResults map[string]*model.StepResult + err error + mocks struct { env bool exec bool } @@ -138,11 +133,6 @@ func TestStepActionLocalPost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSuccess, - Outcome: model.StepStatusSuccess, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool @@ -171,11 +161,6 @@ func TestStepActionLocalPost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSuccess, - Outcome: model.StepStatusSuccess, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool @@ -204,16 +189,11 @@ func TestStepActionLocalPost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSkipped, - Outcome: model.StepStatusSkipped, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool }{ - env: true, + env: false, exec: false, }, }, @@ -238,7 +218,6 @@ func TestStepActionLocalPost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: nil, mocks: struct { env bool exec bool @@ -277,11 +256,6 @@ func TestStepActionLocalPost(t *testing.T) { } sal.RunContext.ExprEval = sal.RunContext.NewExpressionEvaluator(ctx) - if tt.mocks.env { - cm.On("UpdateFromImageEnv", &sal.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", &sal.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromPath", &sal.env).Return(func(ctx context.Context) error { return nil }) - } if tt.mocks.exec { suffixMatcher := func(suffix string) interface{} { return mock.MatchedBy(func(array []string) bool { @@ -294,6 +268,10 @@ func TestStepActionLocalPost(t *testing.T) { return nil }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + return nil + }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -301,12 +279,14 @@ func TestStepActionLocalPost(t *testing.T) { cm.On("UpdateFromEnv", "/var/run/act/workflow/outputcmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) + + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) } err := sal.post()(ctx) assert.Equal(t, tt.err, err) - assert.Equal(t, tt.expectedPostStepResult, sal.RunContext.StepResults["post-step"]) + assert.Equal(t, sal.RunContext.StepResults["post-step"], (*model.StepResult)(nil)) cm.AssertExpectations(t) }) } diff --git a/pkg/runner/step_action_remote.go b/pkg/runner/step_action_remote.go index ea63e95..953a414 100644 --- a/pkg/runner/step_action_remote.go +++ b/pkg/runner/step_action_remote.go @@ -11,11 +11,11 @@ import ( "regexp" "strings" + gogit "github.com/go-git/go-git/v5" + "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common/git" "github.com/nektos/act/pkg/model" - - gogit "github.com/go-git/go-git/v5" ) type stepActionRemote struct { @@ -197,6 +197,7 @@ func (sar *stepActionRemote) getCompositeRunContext(ctx context.Context) *RunCon // was already created during the pre stage) env := evaluateCompositeInputAndEnv(ctx, sar.RunContext, sar) sar.compositeRunContext.Env = env + sar.compositeRunContext.ExtraPath = sar.RunContext.ExtraPath } return sar.compositeRunContext } diff --git a/pkg/runner/step_action_remote_test.go b/pkg/runner/step_action_remote_test.go index e68214c..3199419 100644 --- a/pkg/runner/step_action_remote_test.go +++ b/pkg/runner/step_action_remote_test.go @@ -1,18 +1,20 @@ package runner import ( + "bytes" "context" "errors" + "io" "strings" "testing" - "github.com/nektos/act/pkg/common" - "github.com/nektos/act/pkg/common/git" - "github.com/nektos/act/pkg/model" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "gopkg.in/yaml.v3" + + "github.com/nektos/act/pkg/common" + "github.com/nektos/act/pkg/common/git" + "github.com/nektos/act/pkg/model" ) type stepActionRemoteMocks struct { @@ -163,11 +165,6 @@ func TestStepActionRemote(t *testing.T) { }) } - if tt.mocks.env { - cm.On("UpdateFromImageEnv", &sar.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", &sar.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromPath", &sar.env).Return(func(ctx context.Context) error { return nil }) - } if tt.mocks.read { sarm.On("readAction", sar.Step, suffixMatcher("act/remote-action@v1"), "", mock.Anything, mock.Anything).Return(&model.Action{}, nil) } @@ -178,6 +175,10 @@ func TestStepActionRemote(t *testing.T) { return nil }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + return nil + }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -185,6 +186,8 @@ func TestStepActionRemote(t *testing.T) { cm.On("UpdateFromEnv", "/var/run/act/workflow/outputcmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) + + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) } err := sar.pre()(ctx) @@ -412,14 +415,14 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) { func TestStepActionRemotePost(t *testing.T) { table := []struct { - name string - stepModel *model.Step - actionModel *model.Action - initialStepResults map[string]*model.StepResult - expectedEnv map[string]string - expectedPostStepResult *model.StepResult - err error - mocks struct { + name string + stepModel *model.Step + actionModel *model.Action + initialStepResults map[string]*model.StepResult + IntraActionState map[string]map[string]string + expectedEnv map[string]string + err error + mocks struct { env bool exec bool } @@ -442,19 +445,16 @@ func TestStepActionRemotePost(t *testing.T) { Conclusion: model.StepStatusSuccess, Outcome: model.StepStatusSuccess, Outputs: map[string]string{}, - State: map[string]string{ - "key": "value", - }, + }, + }, + IntraActionState: map[string]map[string]string{ + "step": { + "key": "value", }, }, expectedEnv: map[string]string{ "STATE_key": "value", }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSuccess, - Outcome: model.StepStatusSuccess, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool @@ -483,11 +483,6 @@ func TestStepActionRemotePost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSuccess, - Outcome: model.StepStatusSuccess, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool @@ -516,11 +511,6 @@ func TestStepActionRemotePost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: &model.StepResult{ - Conclusion: model.StepStatusSkipped, - Outcome: model.StepStatusSkipped, - Outputs: map[string]string{}, - }, mocks: struct { env bool exec bool @@ -550,7 +540,6 @@ func TestStepActionRemotePost(t *testing.T) { Outputs: map[string]string{}, }, }, - expectedPostStepResult: nil, mocks: struct { env bool exec bool @@ -582,18 +571,14 @@ func TestStepActionRemotePost(t *testing.T) { }, }, }, - StepResults: tt.initialStepResults, + StepResults: tt.initialStepResults, + IntraActionState: tt.IntraActionState, }, Step: tt.stepModel, action: tt.actionModel, } sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx) - if tt.mocks.env { - cm.On("UpdateFromImageEnv", &sar.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", &sar.env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromPath", &sar.env).Return(func(ctx context.Context) error { return nil }) - } if tt.mocks.exec { cm.On("Exec", []string{"node", "/var/run/act/actions/remote-action@v1/post.js"}, sar.env, "", "").Return(func(ctx context.Context) error { return tt.err }) @@ -601,6 +586,10 @@ func TestStepActionRemotePost(t *testing.T) { return nil }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + return nil + }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -608,6 +597,8 @@ func TestStepActionRemotePost(t *testing.T) { cm.On("UpdateFromEnv", "/var/run/act/workflow/outputcmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) + + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) } err := sar.post()(ctx) @@ -618,7 +609,8 @@ func TestStepActionRemotePost(t *testing.T) { assert.Equal(t, value, sar.env[key]) } } - assert.Equal(t, tt.expectedPostStepResult, sar.RunContext.StepResults["post-step"]) + // Enshure that StepResults is nil in this test + assert.Equal(t, sar.RunContext.StepResults["post-step"], (*model.StepResult)(nil)) cm.AssertExpectations(t) }) } diff --git a/pkg/runner/step_docker_test.go b/pkg/runner/step_docker_test.go index 2008357..3d90ac3 100644 --- a/pkg/runner/step_docker_test.go +++ b/pkg/runner/step_docker_test.go @@ -1,7 +1,9 @@ package runner import ( + "bytes" "context" + "io" "testing" "github.com/nektos/act/pkg/container" @@ -55,18 +57,6 @@ func TestStepDockerMain(t *testing.T) { } sd.RunContext.ExprEval = sd.RunContext.NewExpressionEvaluator(ctx) - cm.On("UpdateFromImageEnv", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { - return nil - }) - - cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { - return nil - }) - - cm.On("UpdateFromPath", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { - return nil - }) - cm.On("Pull", false).Return(func(ctx context.Context) error { return nil }) @@ -91,6 +81,10 @@ func TestStepDockerMain(t *testing.T) { return nil }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + return nil + }) + cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -99,6 +93,8 @@ func TestStepDockerMain(t *testing.T) { return nil }) + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) + err := sd.main()(ctx) assert.Nil(t, err) diff --git a/pkg/runner/step_run.go b/pkg/runner/step_run.go index a74f781..ca77d56 100644 --- a/pkg/runner/step_run.go +++ b/pkg/runner/step_run.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/kballard/go-shellquote" + "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/container" "github.com/nektos/act/pkg/model" @@ -30,6 +31,7 @@ func (sr *stepRun) main() common.Executor { return runStepExecutor(sr, stepStageMain, common.NewPipelineExecutor( sr.setupShellCommandExecutor(), func(ctx context.Context) error { + sr.getRunContext().ApplyExtraPath(ctx, &sr.env) return sr.getRunContext().JobContainer.Exec(sr.cmd, sr.env, "", sr.Step.WorkingDirectory)(ctx) }, )) @@ -71,7 +73,7 @@ func (sr *stepRun) setupShellCommandExecutor() common.Executor { rc := sr.getRunContext() return rc.JobContainer.Copy(rc.JobContainer.GetActPath(), &container.FileEntry{ Name: scriptName, - Mode: 0755, + Mode: 0o755, Body: script, })(ctx) } diff --git a/pkg/runner/step_run_test.go b/pkg/runner/step_run_test.go index e5cde12..fc5e659 100644 --- a/pkg/runner/step_run_test.go +++ b/pkg/runner/step_run_test.go @@ -1,20 +1,23 @@ package runner import ( + "bytes" "context" + "io" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/nektos/act/pkg/container" "github.com/nektos/act/pkg/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" ) func TestStepRun(t *testing.T) { cm := &containerMock{} fileEntry := &container.FileEntry{ Name: "workflow/1.sh", - Mode: 0755, + Mode: 0o755, Body: "\ncmd\n", } @@ -53,7 +56,7 @@ func TestStepRun(t *testing.T) { return nil }) - cm.On("UpdateFromImageEnv", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { + cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(ctx context.Context) error { return nil }) @@ -61,14 +64,6 @@ func TestStepRun(t *testing.T) { return nil }) - cm.On("UpdateFromPath", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { - return nil - }) - - cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(ctx context.Context) error { - return nil - }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/statecmd.txt", mock.AnythingOfType("*map[string]string")).Return(func(ctx context.Context) error { return nil }) @@ -79,6 +74,8 @@ func TestStepRun(t *testing.T) { ctx := context.Background() + cm.On("GetContainerArchive", ctx, "/var/run/act/workflow/pathcmd.txt").Return(io.NopCloser(&bytes.Buffer{}), nil) + err := sr.main()(ctx) assert.Nil(t, err) diff --git a/pkg/runner/step_test.go b/pkg/runner/step_test.go index b72f997..4fc7765 100644 --- a/pkg/runner/step_test.go +++ b/pkg/runner/step_test.go @@ -134,7 +134,6 @@ func TestSetupEnv(t *testing.T) { Env: map[string]string{ "RC_KEY": "rcvalue", }, - ExtraPath: []string{"/path/to/extra/file"}, JobContainer: cm, } step := &model.Step{ @@ -142,19 +141,13 @@ func TestSetupEnv(t *testing.T) { "STEP_WITH": "with-value", }, } - env := map[string]string{ - "PATH": "", - } + env := map[string]string{} sm.On("getRunContext").Return(rc) sm.On("getGithubContext").Return(rc) sm.On("getStepModel").Return(step) sm.On("getEnv").Return(&env) - cm.On("UpdateFromImageEnv", &env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromEnv", "/var/run/act/workflow/envs.txt", &env).Return(func(ctx context.Context) error { return nil }) - cm.On("UpdateFromPath", &env).Return(func(ctx context.Context) error { return nil }) - err := setupEnv(context.Background(), sm) assert.Nil(t, err) @@ -178,13 +171,11 @@ func TestSetupEnv(t *testing.T) { "GITHUB_ACTION_REPOSITORY": "", "GITHUB_API_URL": "https:///api/v3", "GITHUB_BASE_REF": "", - "GITHUB_ENV": "/var/run/act/workflow/envs.txt", "GITHUB_EVENT_NAME": "", "GITHUB_EVENT_PATH": "/var/run/act/workflow/event.json", "GITHUB_GRAPHQL_URL": "https:///api/graphql", "GITHUB_HEAD_REF": "", - "GITHUB_JOB": "", - "GITHUB_PATH": "/var/run/act/workflow/paths.txt", + "GITHUB_JOB": "1", "GITHUB_RETENTION_DAYS": "0", "GITHUB_RUN_ID": "runId", "GITHUB_RUN_NUMBER": "1", @@ -192,7 +183,6 @@ func TestSetupEnv(t *testing.T) { "GITHUB_TOKEN": "", "GITHUB_WORKFLOW": "", "INPUT_STEP_WITH": "with-value", - "PATH": "/path/to/extra/file:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "RC_KEY": "rcvalue", "RUNNER_PERFLOG": "/dev/null", "RUNNER_TRACKING_ID": "", diff --git a/pkg/runner/testdata/.github/workflows/local-reusable-workflow.yml b/pkg/runner/testdata/.github/workflows/local-reusable-workflow.yml new file mode 100644 index 0000000..d32dc5b --- /dev/null +++ b/pkg/runner/testdata/.github/workflows/local-reusable-workflow.yml @@ -0,0 +1,82 @@ +name: reusable + +on: + workflow_call: + inputs: + string_required: + required: true + type: string + string_optional: + required: false + type: string + default: string + bool_required: + required: true + type: boolean + bool_optional: + required: false + type: boolean + default: true + number_required: + required: true + type: number + number_optional: + required: false + type: number + default: ${{ 1 }} + outputs: + output: + description: "A workflow output" + value: ${{ jobs.reusable_workflow_job.outputs.job-output }} + +jobs: + reusable_workflow_job: + runs-on: ubuntu-latest + steps: + - name: test required string + run: | + echo inputs.string_required=${{ inputs.string_required }} + [[ "${{ inputs.string_required == 'string' }}" = "true" ]] || exit 1 + + - name: test optional string + run: | + echo inputs.string_optional=${{ inputs.string_optional }} + [[ "${{ inputs.string_optional == 'string' }}" = "true" ]] || exit 1 + + - name: test required bool + run: | + echo inputs.bool_required=${{ inputs.bool_required }} + [[ "${{ inputs.bool_required }}" = "true" ]] || exit 1 + + - name: test optional bool + run: | + echo inputs.bool_optional=${{ inputs.bool_optional }} + [[ "${{ inputs.bool_optional }}" = "true" ]] || exit 1 + + - name: test required number + run: | + echo inputs.number_required=${{ inputs.number_required }} + [[ "${{ inputs.number_required == 1 }}" = "true" ]] || exit 1 + + - name: test optional number + run: | + echo inputs.number_optional=${{ inputs.number_optional }} + [[ "${{ inputs.number_optional == 1 }}" = "true" ]] || exit 1 + + - name: test secret + run: | + echo secrets.secret=${{ secrets.secret }} + [[ "${{ secrets.secret == 'keep_it_private' }}" = "true" ]] || exit 1 + + - name: test github.event_name is never workflow_call + run: | + echo github.event_name=${{ github.event_name }} + [[ "${{ github.event_name != 'workflow_call' }}" = "true" ]] || exit 1 + + - name: test output + id: output_test + run: | + echo "value=${{ inputs.string_required }}" >> $GITHUB_OUTPUT + + outputs: + job-output: ${{ steps.output_test.outputs.value }} diff --git a/pkg/runner/testdata/GITHUB_ENV-use-in-env-ctx/push.yml b/pkg/runner/testdata/GITHUB_ENV-use-in-env-ctx/push.yml new file mode 100644 index 0000000..c7b75a0 --- /dev/null +++ b/pkg/runner/testdata/GITHUB_ENV-use-in-env-ctx/push.yml @@ -0,0 +1,27 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + env: + MYGLOBALENV3: myglobalval3 + steps: + - run: | + echo MYGLOBALENV1=myglobalval1 > $GITHUB_ENV + echo "::set-env name=MYGLOBALENV2::myglobalval2" + - uses: nektos/act-test-actions/script@main + with: + main: | + env + [[ "$MYGLOBALENV1" = "${{ env.MYGLOBALENV1 }}" ]] + [[ "$MYGLOBALENV1" = "${{ env.MYGLOBALENV1ALIAS }}" ]] + [[ "$MYGLOBALENV1" = "$MYGLOBALENV1ALIAS" ]] + [[ "$MYGLOBALENV2" = "${{ env.MYGLOBALENV2 }}" ]] + [[ "$MYGLOBALENV2" = "${{ env.MYGLOBALENV2ALIAS }}" ]] + [[ "$MYGLOBALENV2" = "$MYGLOBALENV2ALIAS" ]] + [[ "$MYGLOBALENV3" = "${{ env.MYGLOBALENV3 }}" ]] + [[ "$MYGLOBALENV3" = "${{ env.MYGLOBALENV3ALIAS }}" ]] + [[ "$MYGLOBALENV3" = "$MYGLOBALENV3ALIAS" ]] + env: + MYGLOBALENV1ALIAS: ${{ env.MYGLOBALENV1 }} + MYGLOBALENV2ALIAS: ${{ env.MYGLOBALENV2 }} + MYGLOBALENV3ALIAS: ${{ env.MYGLOBALENV3 }} \ No newline at end of file diff --git a/pkg/runner/testdata/GITHUB_STATE/push.yml b/pkg/runner/testdata/GITHUB_STATE/push.yml new file mode 100644 index 0000000..61afc07 --- /dev/null +++ b/pkg/runner/testdata/GITHUB_STATE/push.yml @@ -0,0 +1,48 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - uses: nektos/act-test-actions/script@main + with: + pre: | + env + echo mystate0=mystateval > $GITHUB_STATE + echo "::save-state name=mystate1::mystateval" + main: | + env + echo mystate2=mystateval > $GITHUB_STATE + echo "::save-state name=mystate3::mystateval" + post: | + env + [ "$STATE_mystate0" = "mystateval" ] + [ "$STATE_mystate1" = "mystateval" ] + [ "$STATE_mystate2" = "mystateval" ] + [ "$STATE_mystate3" = "mystateval" ] + test-id-collision-bug: + runs-on: ubuntu-latest + steps: + - uses: nektos/act-test-actions/script@main + id: script + with: + pre: | + env + echo mystate0=mystateval > $GITHUB_STATE + echo "::save-state name=mystate1::mystateval" + main: | + env + echo mystate2=mystateval > $GITHUB_STATE + echo "::save-state name=mystate3::mystateval" + post: | + env + [ "$STATE_mystate0" = "mystateval" ] + [ "$STATE_mystate1" = "mystateval" ] + [ "$STATE_mystate2" = "mystateval" ] + [ "$STATE_mystate3" = "mystateval" ] + - uses: nektos/act-test-actions/script@main + id: pre-script + with: + main: | + env + echo mystate0=mystateerror > $GITHUB_STATE + echo "::save-state name=mystate1::mystateerror" \ No newline at end of file diff --git a/pkg/runner/testdata/actions-environment-and-context-tests/push.yml b/pkg/runner/testdata/actions-environment-and-context-tests/push.yml index db3c341..1d799d5 100644 --- a/pkg/runner/testdata/actions-environment-and-context-tests/push.yml +++ b/pkg/runner/testdata/actions-environment-and-context-tests/push.yml @@ -11,3 +11,5 @@ jobs: - uses: './actions-environment-and-context-tests/docker' - uses: 'nektos/act-test-actions/js@main' - uses: 'nektos/act-test-actions/docker@main' + - uses: 'nektos/act-test-actions/docker-file@main' + - uses: 'nektos/act-test-actions/docker-relative-context/action@main' diff --git a/pkg/runner/testdata/do-not-leak-step-env-in-composite/push.yml b/pkg/runner/testdata/do-not-leak-step-env-in-composite/push.yml new file mode 100644 index 0000000..1bebab0 --- /dev/null +++ b/pkg/runner/testdata/do-not-leak-step-env-in-composite/push.yml @@ -0,0 +1,18 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - run: | + runs: + using: composite + steps: + - run: exit 1 + shell: bash + if: env.LEAK_ENV != 'val' + shell: cp {0} action.yml + - uses: ./ + env: + LEAK_ENV: val + - run: exit 1 + if: env.LEAK_ENV == 'val' \ No newline at end of file diff --git a/pkg/runner/testdata/docker-action-custom-path/push.yml b/pkg/runner/testdata/docker-action-custom-path/push.yml new file mode 100644 index 0000000..37bbf41 --- /dev/null +++ b/pkg/runner/testdata/docker-action-custom-path/push.yml @@ -0,0 +1,12 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - run: | + FROM ubuntu:latest + ENV PATH="/opt/texlive/texdir/bin/x86_64-linuxmusl:${PATH}" + ENV ORG_PATH="${PATH}" + ENTRYPOINT [ "bash", "-c", "echo \"PATH=$PATH\" && echo \"ORG_PATH=$ORG_PATH\" && [[ \"$PATH\" = \"$ORG_PATH\" ]]" ] + shell: mv {0} Dockerfile + - uses: ./ \ No newline at end of file diff --git a/pkg/runner/testdata/environment-files-parser-bug/push.yaml b/pkg/runner/testdata/environment-files-parser-bug/push.yaml new file mode 100644 index 0000000..a64546c --- /dev/null +++ b/pkg/runner/testdata/environment-files-parser-bug/push.yaml @@ -0,0 +1,13 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - run: | + echo "test< $GITHUB_ENV + echo "x=Thats really Weird" >> $GITHUB_ENV + echo "World" >> $GITHUB_ENV + - if: env.test != 'x=Thats really Weird' + run: exit 1 + - if: env.x == 'Thats really Weird' # This assert is triggered by the broken impl of act + run: exit 1 \ No newline at end of file diff --git a/pkg/runner/testdata/environment-files/push.yaml b/pkg/runner/testdata/environment-files/push.yaml new file mode 100644 index 0000000..a6ac36c --- /dev/null +++ b/pkg/runner/testdata/environment-files/push.yaml @@ -0,0 +1,101 @@ +name: environment-files +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: "Append to $GITHUB_PATH" + run: | + echo "$HOME/someFolder" >> $GITHUB_PATH + - name: "Append some more to $GITHUB_PATH" + run: | + echo "$HOME/someOtherFolder" >> $GITHUB_PATH + - name: "Check PATH" + run: | + echo "${PATH}" + if [[ ! "${PATH}" =~ .*"$HOME/"someOtherFolder.*"$HOME/"someFolder.* ]]; then + echo "${PATH} doesn't match .*someOtherFolder.*someFolder.*" + exit 1 + fi + - name: "Prepend" + run: | + if ls | grep -q 'called ls' ; then + echo 'ls was overridden already?' + exit 2 + fi + path_add=$(mktemp -d) + cat > $path_add/ls <> $GITHUB_PATH + - name: "Verify prepend" + run: | + if ! ls | grep -q 'called ls' ; then + echo 'ls was not overridden' + exit 2 + fi + - name: "Write single line env to $GITHUB_ENV" + run: | + echo "KEY=value" >> $GITHUB_ENV + - name: "Check single line env" + run: | + if [[ "${KEY}" != "value" ]]; then + echo "${KEY} doesn't == 'value'" + exit 1 + fi + - name: "Write single line env with more than one 'equals' signs to $GITHUB_ENV" + run: | + echo "KEY=value=anothervalue" >> $GITHUB_ENV + - name: "Check single line env" + run: | + if [[ "${KEY}" != "value=anothervalue" ]]; then + echo "${KEY} doesn't == 'value=anothervalue'" + exit 1 + fi + - name: "Write multiline env to $GITHUB_ENV" + run: | + echo 'KEY2<> $GITHUB_ENV + echo value2 >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + - name: "Check multiline line env" + run: | + if [[ "${KEY2}" != "value2" ]]; then + echo "${KEY2} doesn't == 'value'" + exit 1 + fi + - name: "Write multiline env with UUID to $GITHUB_ENV" + run: | + echo 'KEY3<> $GITHUB_ENV + echo value3 >> $GITHUB_ENV + echo 'ghadelimiter_b8273c6d-d535-419a-a010-b0aaac240e36' >> $GITHUB_ENV + - name: "Check multiline env with UUID to $GITHUB_ENV" + run: | + if [[ "${KEY3}" != "value3" ]]; then + echo "${KEY3} doesn't == 'value3'" + exit 1 + fi + - name: "Write single line output to $GITHUB_OUTPUT" + id: write-single-output + run: | + echo "KEY=value" >> $GITHUB_OUTPUT + - name: "Check single line output" + run: | + if [[ "${{ steps.write-single-output.outputs.KEY }}" != "value" ]]; then + echo "${{ steps.write-single-output.outputs.KEY }} doesn't == 'value'" + exit 1 + fi + - name: "Write multiline output to $GITHUB_OUTPUT" + id: write-multi-output + run: | + echo 'KEY2<> $GITHUB_OUTPUT + echo value2 >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + - name: "Check multiline output" + run: | + if [[ "${{ steps.write-multi-output.outputs.KEY2 }}" != "value2" ]]; then + echo "${{ steps.write-multi-output.outputs.KEY2 }} doesn't == 'value2'" + exit 1 + fi \ No newline at end of file diff --git a/pkg/runner/testdata/environment-variables/push.yml b/pkg/runner/testdata/environment-variables/push.yml new file mode 100644 index 0000000..37218ad --- /dev/null +++ b/pkg/runner/testdata/environment-variables/push.yml @@ -0,0 +1,33 @@ +name: environment variables +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Test on job level + run: | + echo \$UPPER=$UPPER + echo \$upper=$upper + echo \$LOWER=$LOWER + echo \$lower=$lower + [[ "$UPPER" = "UPPER" ]] || exit 1 + [[ "$upper" = "" ]] || exit 1 + [[ "$LOWER" = "" ]] || exit 1 + [[ "$lower" = "lower" ]] || exit 1 + - name: Test on step level + run: | + echo \$UPPER=$UPPER + echo \$upper=$upper + echo \$LOWER=$LOWER + echo \$lower=$lower + [[ "$UPPER" = "upper" ]] || exit 1 + [[ "$upper" = "" ]] || exit 1 + [[ "$LOWER" = "" ]] || exit 1 + [[ "$lower" = "LOWER" ]] || exit 1 + env: + UPPER: upper + lower: LOWER + env: + UPPER: UPPER + lower: lower diff --git a/pkg/runner/testdata/input-from-cli/input.yml b/pkg/runner/testdata/input-from-cli/input.yml new file mode 100644 index 0000000..42d3460 --- /dev/null +++ b/pkg/runner/testdata/input-from-cli/input.yml @@ -0,0 +1,21 @@ +on: + workflow_dispatch: + inputs: + NAME: + description: "A random input name for the workflow" + type: string + required: true + SOME_VALUE: + description: "Some other input to pass" + type: string + required: true + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Test with inputs + run: | + [ -z "${{ github.event.inputs.SOME_INPUT }}" ] && exit 1 || exit 0 diff --git a/pkg/runner/testdata/inputs-via-env-context/action.yml b/pkg/runner/testdata/inputs-via-env-context/action.yml new file mode 100644 index 0000000..4ea270d --- /dev/null +++ b/pkg/runner/testdata/inputs-via-env-context/action.yml @@ -0,0 +1,8 @@ +inputs: + test-env-input: {} +runs: + using: composite + steps: + - run: | + exit ${{ inputs.test-env-input == env.test-env-input && '0' || '1'}} + shell: bash diff --git a/pkg/runner/testdata/inputs-via-env-context/push.yml b/pkg/runner/testdata/inputs-via-env-context/push.yml new file mode 100644 index 0000000..07fadeb --- /dev/null +++ b/pkg/runner/testdata/inputs-via-env-context/push.yml @@ -0,0 +1,15 @@ +on: push +jobs: + test-inputs-via-env-context: + runs-on: self-hosted + steps: + - uses: actions/checkout@v3 + - uses: ./inputs-via-env-context + with: + test-env-input: ${{ env.test-env-input }} + env: + test-env-input: ${{ github.event_name }}/${{ github.run_id }} + - run: | + exit ${{ env.test-env-input == format('{0}/{1}', github.event_name, github.run_id) && '0' || '1' }} + env: + test-env-input: ${{ github.event_name }}/${{ github.run_id }} \ No newline at end of file diff --git a/pkg/runner/testdata/issue-1595/missing.yml b/pkg/runner/testdata/issue-1595/missing.yml new file mode 100644 index 0000000..3b4adf4 --- /dev/null +++ b/pkg/runner/testdata/issue-1595/missing.yml @@ -0,0 +1,16 @@ +name: missing +on: push + +jobs: + second: + runs-on: ubuntu-latest + needs: first + steps: + - run: echo How did you get here? + shell: bash + + standalone: + runs-on: ubuntu-latest + steps: + - run: echo Hello world + shell: bash diff --git a/pkg/runner/testdata/issue-1595/no-event.yml b/pkg/runner/testdata/issue-1595/no-event.yml new file mode 100644 index 0000000..2140a0b --- /dev/null +++ b/pkg/runner/testdata/issue-1595/no-event.yml @@ -0,0 +1,8 @@ +name: no event + +jobs: + stuck: + runs-on: ubuntu-latest + steps: + - run: echo How did you get here? + shell: bash diff --git a/pkg/runner/testdata/issue-1595/no-first.yml b/pkg/runner/testdata/issue-1595/no-first.yml new file mode 100644 index 0000000..48d4b55 --- /dev/null +++ b/pkg/runner/testdata/issue-1595/no-first.yml @@ -0,0 +1,10 @@ +name: no first +on: push + +jobs: + second: + runs-on: ubuntu-latest + needs: first + steps: + - run: echo How did you get here? + shell: bash diff --git a/pkg/runner/testdata/job-needs-context-contains-result/push.yml b/pkg/runner/testdata/job-needs-context-contains-result/push.yml new file mode 100644 index 0000000..0ecbcea --- /dev/null +++ b/pkg/runner/testdata/job-needs-context-contains-result/push.yml @@ -0,0 +1,15 @@ +on: + push: +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: exit 0 + assert: + needs: test + if: | + ( always() && !cancelled() ) && ( + ( needs.test.result != 'success' || !success() ) ) + runs-on: ubuntu-latest + steps: + - run: exit 1 diff --git a/pkg/runner/testdata/matrix-exitcode/push.yml b/pkg/runner/testdata/matrix-exitcode/push.yml new file mode 100644 index 0000000..0f5d335 --- /dev/null +++ b/pkg/runner/testdata/matrix-exitcode/push.yml @@ -0,0 +1,16 @@ +name: test + +on: push + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + val: ["success", "failure"] + fail-fast: false + steps: + - name: test + run: | + echo "Expected job result: ${{ matrix.val }}" + [[ "${{ matrix.val }}" = "success" ]] || exit 1 diff --git a/pkg/runner/testdata/no-panic-on-invalid-composite-action/push.yml b/pkg/runner/testdata/no-panic-on-invalid-composite-action/push.yml new file mode 100644 index 0000000..6b9e4ae --- /dev/null +++ b/pkg/runner/testdata/no-panic-on-invalid-composite-action/push.yml @@ -0,0 +1,29 @@ +on: push +jobs: + local-invalid-step: + runs-on: ubuntu-latest + steps: + - run: | + runs: + using: composite + steps: + - name: Foo + - uses: Foo/Bar + shell: cp {0} action.yml + - uses: ./ + local-missing-steps: + runs-on: ubuntu-latest + steps: + - run: | + runs: + using: composite + shell: cp {0} action.yml + - uses: ./ + remote-invalid-step: + runs-on: ubuntu-latest + steps: + - uses: nektos/act-test-actions/invalid-composite-action/invalid-step@main + remote-missing-steps: + runs-on: ubuntu-latest + steps: + - uses: nektos/act-test-actions/invalid-composite-action/missing-steps@main \ No newline at end of file diff --git a/pkg/runner/testdata/path-handling/action.yml b/pkg/runner/testdata/path-handling/action.yml new file mode 100644 index 0000000..8db98c5 --- /dev/null +++ b/pkg/runner/testdata/path-handling/action.yml @@ -0,0 +1,21 @@ +name: output action +description: output action + +inputs: + input: + description: some input + required: false + +outputs: + job-output: + description: some output + value: ${{ steps.gen-out.outputs.step-output }} + +runs: + using: composite + steps: + - name: run step + id: gen-out + run: | + echo "::set-output name=step-output::" + shell: bash diff --git a/pkg/runner/testdata/path-handling/push.yml b/pkg/runner/testdata/path-handling/push.yml new file mode 100644 index 0000000..812c8b8 --- /dev/null +++ b/pkg/runner/testdata/path-handling/push.yml @@ -0,0 +1,39 @@ +name: path tests +on: push +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: "Append to $GITHUB_PATH" + run: | + echo "/opt/hostedtoolcache/node/18.99/x64/bin" >> $GITHUB_PATH + + - name: test path (after setup) + run: | + if ! echo "$PATH" |grep "/opt/hostedtoolcache/node/18.*/\(x64\|arm64\)/bin" ; then + echo "Node binaries not in path: $PATH" + exit 1 + fi + + - id: action-with-output + uses: ./path-handling/ + + - name: test path (after local action) + run: | + if ! echo "$PATH" |grep "/opt/hostedtoolcache/node/18.*/\(x64\|arm64\)/bin" ; then + echo "Node binaries not in path: $PATH" + exit 1 + fi + + - uses: nektos/act-test-actions/composite@main + with: + input: some input + + - name: test path (after remote action) + run: | + if ! echo "$PATH" |grep "/opt/hostedtoolcache/node/18.*/\(x64\|arm64\)/bin" ; then + echo "Node binaries not in path: $PATH" + exit 1 + fi diff --git a/pkg/runner/testdata/remote-action-js-node-user/push.yml b/pkg/runner/testdata/remote-action-js-node-user/push.yml new file mode 100644 index 0000000..8bf45da --- /dev/null +++ b/pkg/runner/testdata/remote-action-js-node-user/push.yml @@ -0,0 +1,30 @@ +name: remote-action-js +on: push + +jobs: + test: + runs-on: ubuntu-latest + container: + image: node:16-buster-slim + options: --user node + steps: + - name: check permissions of env files + id: test + run: | + echo "USER: $(id -un) expected: node" + [[ "$(id -un)" = "node" ]] + echo "TEST=Value" >> $GITHUB_OUTPUT + shell: bash + + - name: check if file command worked + if: steps.test.outputs.test != 'Value' + run: | + echo "steps.test.outputs.test=${{ steps.test.outputs.test || 'missing value!' }}" + exit 1 + shell: bash + + - uses: actions/hello-world-javascript-action@v1 + with: + who-to-greet: 'Mona the Octocat' + + - uses: cloudposse/actions/github/slash-command-dispatch@0.14.0 diff --git a/pkg/runner/testdata/set-env-new-env-file-per-step/push.yml b/pkg/runner/testdata/set-env-new-env-file-per-step/push.yml new file mode 100644 index 0000000..34f4bad --- /dev/null +++ b/pkg/runner/testdata/set-env-new-env-file-per-step/push.yml @@ -0,0 +1,15 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + env: + MY_ENV: test + steps: + - run: exit 1 + if: env.MY_ENV != 'test' + - run: echo "MY_ENV=test2" > $GITHUB_ENV + - run: exit 1 + if: env.MY_ENV != 'test2' + - run: echo "MY_ENV=returnedenv" > $GITHUB_ENV + - run: exit 1 + if: env.MY_ENV != 'returnedenv' \ No newline at end of file diff --git a/pkg/runner/testdata/set-env-step-env-override/push.yml b/pkg/runner/testdata/set-env-step-env-override/push.yml new file mode 100644 index 0000000..f35ef87 --- /dev/null +++ b/pkg/runner/testdata/set-env-step-env-override/push.yml @@ -0,0 +1,24 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + env: + MY_ENV: test + steps: + - run: exit 1 + if: env.MY_ENV != 'test' + - run: | + runs: + using: composite + steps: + - run: exit 1 + shell: bash + if: env.MY_ENV != 'val' + - run: echo "MY_ENV=returnedenv" > $GITHUB_ENV + shell: bash + shell: cp {0} action.yml + - uses: ./ + env: + MY_ENV: val + - run: exit 1 + if: env.MY_ENV != 'returnedenv' \ No newline at end of file diff --git a/pkg/runner/testdata/uses-nested-composite/composite_action2/action.yml b/pkg/runner/testdata/uses-nested-composite/composite_action2/action.yml index 2fae40c..4aec9a8 100644 --- a/pkg/runner/testdata/uses-nested-composite/composite_action2/action.yml +++ b/pkg/runner/testdata/uses-nested-composite/composite_action2/action.yml @@ -9,23 +9,22 @@ inputs: runs: using: "composite" steps: -# The output of actions/setup-node@v2 seems to fail the workflow -# - uses: actions/setup-node@v2 -# with: -# node-version: '16' -# - run: | -# console.log(process.version); -# console.log("Hi from node"); -# console.log("${{ inputs.test_input_optional }}"); -# if("${{ inputs.test_input_optional }}" !== "Test") { -# console.log("Invalid input test_input_optional expected \"Test\" as value"); -# process.exit(1); -# } -# if(!process.version.startsWith('v16')) { -# console.log("Expected node v16, but got " + process.version); -# process.exit(1); -# } -# shell: node {0} + - uses: actions/setup-node@v3 + with: + node-version: '16' + - run: | + console.log(process.version); + console.log("Hi from node"); + console.log("${{ inputs.test_input_optional }}"); + if("${{ inputs.test_input_optional }}" !== "Test") { + console.log("Invalid input test_input_optional expected \"Test\" as value"); + process.exit(1); + } + if(!process.version.startsWith('v16')) { + console.log("Expected node v16, but got " + process.version); + process.exit(1); + } + shell: node {0} - uses: ./uses-composite/composite_action id: composite with: diff --git a/pkg/runner/testdata/uses-workflow/local-workflow.yml b/pkg/runner/testdata/uses-workflow/local-workflow.yml new file mode 100644 index 0000000..070e4d0 --- /dev/null +++ b/pkg/runner/testdata/uses-workflow/local-workflow.yml @@ -0,0 +1,36 @@ +name: local-reusable-workflows +on: pull_request + +jobs: + reusable-workflow: + uses: ./.github/workflows/local-reusable-workflow.yml + with: + string_required: string + bool_required: ${{ true }} + number_required: 1 + secrets: + secret: keep_it_private + + reusable-workflow-with-inherited-secrets: + uses: ./.github/workflows/local-reusable-workflow.yml + with: + string_required: string + bool_required: ${{ true }} + number_required: 1 + secrets: inherit + + output-test: + runs-on: ubuntu-latest + needs: + - reusable-workflow + - reusable-workflow-with-inherited-secrets + steps: + - name: output with secrets map + run: | + echo reusable-workflow.output=${{ needs.reusable-workflow.outputs.output }} + [[ "${{ needs.reusable-workflow.outputs.output == 'string' }}" = "true" ]] || exit 1 + + - name: output with inherited secrets + run: | + echo reusable-workflow-with-inherited-secrets.output=${{ needs.reusable-workflow-with-inherited-secrets.outputs.output }} + [[ "${{ needs.reusable-workflow-with-inherited-secrets.outputs.output == 'string' }}" = "true" ]] || exit 1 diff --git a/pkg/runner/testdata/uses-workflow/push.yml b/pkg/runner/testdata/uses-workflow/push.yml index 855dacf..ddc37b8 100644 --- a/pkg/runner/testdata/uses-workflow/push.yml +++ b/pkg/runner/testdata/uses-workflow/push.yml @@ -2,8 +2,34 @@ on: push jobs: reusable-workflow: - uses: nektos/act-tests/.github/workflows/reusable-workflow.yml@master + uses: nektos/act-test-actions/.github/workflows/reusable-workflow.yml@main with: - username: mona + string_required: string + bool_required: ${{ true }} + number_required: 1 secrets: - envPATH: ${{ secrets.envPAT }} + secret: keep_it_private + + reusable-workflow-with-inherited-secrets: + uses: nektos/act-test-actions/.github/workflows/reusable-workflow.yml@main + with: + string_required: string + bool_required: ${{ true }} + number_required: 1 + secrets: inherit + + output-test: + runs-on: ubuntu-latest + needs: + - reusable-workflow + - reusable-workflow-with-inherited-secrets + steps: + - name: output with secrets map + run: | + echo reusable-workflow.output=${{ needs.reusable-workflow.outputs.output }} + [[ "${{ needs.reusable-workflow.outputs.output == 'string' }}" = "true" ]] || exit 1 + + - name: output with inherited secrets + run: | + echo reusable-workflow-with-inherited-secrets.output=${{ needs.reusable-workflow-with-inherited-secrets.outputs.output }} + [[ "${{ needs.reusable-workflow-with-inherited-secrets.outputs.output == 'string' }}" = "true" ]] || exit 1 diff --git a/pkg/workflowpattern/trace_writer.go b/pkg/workflowpattern/trace_writer.go new file mode 100644 index 0000000..d5d990f --- /dev/null +++ b/pkg/workflowpattern/trace_writer.go @@ -0,0 +1,18 @@ +package workflowpattern + +import "fmt" + +type TraceWriter interface { + Info(string, ...interface{}) +} + +type EmptyTraceWriter struct{} + +func (*EmptyTraceWriter) Info(string, ...interface{}) { +} + +type StdOutTraceWriter struct{} + +func (*StdOutTraceWriter) Info(format string, args ...interface{}) { + fmt.Printf(format+"\n", args...) +} diff --git a/pkg/workflowpattern/workflow_pattern.go b/pkg/workflowpattern/workflow_pattern.go new file mode 100644 index 0000000..cc03e40 --- /dev/null +++ b/pkg/workflowpattern/workflow_pattern.go @@ -0,0 +1,196 @@ +package workflowpattern + +import ( + "fmt" + "regexp" + "strings" +) + +type WorkflowPattern struct { + Pattern string + Negative bool + Regex *regexp.Regexp +} + +func CompilePattern(rawpattern string) (*WorkflowPattern, error) { + negative := false + pattern := rawpattern + if strings.HasPrefix(rawpattern, "!") { + negative = true + pattern = rawpattern[1:] + } + rpattern, err := PatternToRegex(pattern) + if err != nil { + return nil, err + } + regex, err := regexp.Compile(rpattern) + if err != nil { + return nil, err + } + return &WorkflowPattern{ + Pattern: pattern, + Negative: negative, + Regex: regex, + }, nil +} + +//nolint:gocyclo +func PatternToRegex(pattern string) (string, error) { + var rpattern strings.Builder + rpattern.WriteString("^") + pos := 0 + errors := map[int]string{} + for pos < len(pattern) { + switch pattern[pos] { + case '*': + if pos+1 < len(pattern) && pattern[pos+1] == '*' { + if pos+2 < len(pattern) && pattern[pos+2] == '/' { + rpattern.WriteString("(.+/)?") + pos += 3 + } else { + rpattern.WriteString(".*") + pos += 2 + } + } else { + rpattern.WriteString("[^/]*") + pos++ + } + case '+', '?': + if pos > 0 { + rpattern.WriteByte(pattern[pos]) + } else { + rpattern.WriteString(regexp.QuoteMeta(string([]byte{pattern[pos]}))) + } + pos++ + case '[': + rpattern.WriteByte(pattern[pos]) + pos++ + if pos < len(pattern) && pattern[pos] == ']' { + errors[pos] = "Unexpected empty brackets '[]'" + pos++ + break + } + validChar := func(a, b, test byte) bool { + return test >= a && test <= b + } + startPos := pos + for pos < len(pattern) && pattern[pos] != ']' { + switch pattern[pos] { + case '-': + if pos <= startPos || pos+1 >= len(pattern) { + errors[pos] = "Invalid range" + pos++ + break + } + validRange := func(a, b byte) bool { + return validChar(a, b, pattern[pos-1]) && validChar(a, b, pattern[pos+1]) && pattern[pos-1] <= pattern[pos+1] + } + if !validRange('A', 'z') && !validRange('0', '9') { + errors[pos] = "Ranges can only include a-z, A-Z, A-z, and 0-9" + pos++ + break + } + rpattern.WriteString(pattern[pos : pos+2]) + pos += 2 + default: + if !validChar('A', 'z', pattern[pos]) && !validChar('0', '9', pattern[pos]) { + errors[pos] = "Ranges can only include a-z, A-Z and 0-9" + pos++ + break + } + rpattern.WriteString(regexp.QuoteMeta(string([]byte{pattern[pos]}))) + pos++ + } + } + if pos >= len(pattern) || pattern[pos] != ']' { + errors[pos] = "Missing closing bracket ']' after '['" + pos++ + } + rpattern.WriteString("]") + pos++ + case '\\': + if pos+1 >= len(pattern) { + errors[pos] = "Missing symbol after \\" + pos++ + break + } + rpattern.WriteString(regexp.QuoteMeta(string([]byte{pattern[pos+1]}))) + pos += 2 + default: + rpattern.WriteString(regexp.QuoteMeta(string([]byte{pattern[pos]}))) + pos++ + } + } + if len(errors) > 0 { + var errorMessage strings.Builder + for position, err := range errors { + if errorMessage.Len() > 0 { + errorMessage.WriteString(", ") + } + errorMessage.WriteString(fmt.Sprintf("Position: %d Error: %s", position, err)) + } + return "", fmt.Errorf("invalid Pattern '%s': %s", pattern, errorMessage.String()) + } + rpattern.WriteString("$") + return rpattern.String(), nil +} + +func CompilePatterns(patterns ...string) ([]*WorkflowPattern, error) { + ret := []*WorkflowPattern{} + for _, pattern := range patterns { + cp, err := CompilePattern(pattern) + if err != nil { + return nil, err + } + ret = append(ret, cp) + } + return ret, nil +} + +// returns true if the workflow should be skipped paths/branches +func Skip(sequence []*WorkflowPattern, input []string, traceWriter TraceWriter) bool { + if len(sequence) == 0 { + return false + } + for _, file := range input { + matched := false + for _, item := range sequence { + if item.Regex.MatchString(file) { + pattern := item.Pattern + if item.Negative { + matched = false + traceWriter.Info("%s excluded by pattern %s", file, pattern) + } else { + matched = true + traceWriter.Info("%s included by pattern %s", file, pattern) + } + } + } + if matched { + return false + } + } + return true +} + +// returns true if the workflow should be skipped paths-ignore/branches-ignore +func Filter(sequence []*WorkflowPattern, input []string, traceWriter TraceWriter) bool { + if len(sequence) == 0 { + return false + } + for _, file := range input { + matched := false + for _, item := range sequence { + if item.Regex.MatchString(file) == !item.Negative { + pattern := item.Pattern + traceWriter.Info("%s ignored by pattern %s", file, pattern) + matched = true + break + } + } + if !matched { + return false + } + } + return true +} diff --git a/pkg/workflowpattern/workflow_pattern_test.go b/pkg/workflowpattern/workflow_pattern_test.go new file mode 100644 index 0000000..a62d529 --- /dev/null +++ b/pkg/workflowpattern/workflow_pattern_test.go @@ -0,0 +1,414 @@ +package workflowpattern + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMatchPattern(t *testing.T) { + kases := []struct { + inputs []string + patterns []string + skipResult bool + filterResult bool + }{ + { + patterns: []string{"*"}, + inputs: []string{"path/with/slash"}, + skipResult: true, + filterResult: false, + }, + { + patterns: []string{"path/a", "path/b", "path/c"}, + inputs: []string{"meta", "path/b", "otherfile"}, + skipResult: false, + filterResult: false, + }, + { + patterns: []string{"path/a", "path/b", "path/c"}, + inputs: []string{"path/b"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"path/a", "path/b", "path/c"}, + inputs: []string{"path/c", "path/b"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"path/a", "path/b", "path/c"}, + inputs: []string{"path/c", "path/b", "path/a"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"path/a", "path/b", "path/c"}, + inputs: []string{"path/c", "path/b", "path/d", "path/a"}, + skipResult: false, + filterResult: false, + }, + { + patterns: []string{}, + inputs: []string{}, + skipResult: false, + filterResult: false, + }, + { + patterns: []string{"\\!file"}, + inputs: []string{"!file"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"escape\\\\backslash"}, + inputs: []string{"escape\\backslash"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{".yml"}, + inputs: []string{"fyml"}, + skipResult: true, + filterResult: false, + }, + // https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#patterns-to-match-branches-and-tags + { + patterns: []string{"feature/*"}, + inputs: []string{"feature/my-branch"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"feature/*"}, + inputs: []string{"feature/your-branch"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"feature/**"}, + inputs: []string{"feature/beta-a/my-branch"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"feature/**"}, + inputs: []string{"feature/beta-a/my-branch"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"feature/**"}, + inputs: []string{"feature/mona/the/octocat"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"main", "releases/mona-the-octocat"}, + inputs: []string{"main"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"main", "releases/mona-the-octocat"}, + inputs: []string{"releases/mona-the-octocat"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*"}, + inputs: []string{"main"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*"}, + inputs: []string{"releases"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**"}, + inputs: []string{"all/the/branches"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**"}, + inputs: []string{"every/tag"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*feature"}, + inputs: []string{"mona-feature"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*feature"}, + inputs: []string{"feature"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*feature"}, + inputs: []string{"ver-10-feature"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"v2*"}, + inputs: []string{"v2"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"v2*"}, + inputs: []string{"v2.0"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"v2*"}, + inputs: []string{"v2.9"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"v[12].[0-9]+.[0-9]+"}, + inputs: []string{"v1.10.1"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"v[12].[0-9]+.[0-9]+"}, + inputs: []string{"v2.0.0"}, + skipResult: false, + filterResult: true, + }, + // https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#patterns-to-match-file-paths + { + patterns: []string{"*"}, + inputs: []string{"README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*"}, + inputs: []string{"server.rb"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.jsx?"}, + inputs: []string{"page.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.jsx?"}, + inputs: []string{"page.jsx"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**"}, + inputs: []string{"all/the/files.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.js"}, + inputs: []string{"app.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.js"}, + inputs: []string{"index.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**.js"}, + inputs: []string{"index.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**.js"}, + inputs: []string{"js/index.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**.js"}, + inputs: []string{"src/js/app.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/*"}, + inputs: []string{"docs/README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/*"}, + inputs: []string{"docs/file.txt"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/**"}, + inputs: []string{"docs/README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/**"}, + inputs: []string{"docs/mona/octocat.txt"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/**/*.md"}, + inputs: []string{"docs/README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/**/*.md"}, + inputs: []string{"docs/mona/hello-world.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"docs/**/*.md"}, + inputs: []string{"docs/a/markdown/file.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/docs/**"}, + inputs: []string{"docs/hello.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/docs/**"}, + inputs: []string{"dir/docs/my-file.txt"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/docs/**"}, + inputs: []string{"space/docs/plan/space.doc"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/README.md"}, + inputs: []string{"README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/README.md"}, + inputs: []string{"js/README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/*src/**"}, + inputs: []string{"a/src/app.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/*src/**"}, + inputs: []string{"my-src/code/js/app.js"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/*-post.md"}, + inputs: []string{"my-post.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/*-post.md"}, + inputs: []string{"path/their-post.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/migrate-*.sql"}, + inputs: []string{"migrate-10909.sql"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/migrate-*.sql"}, + inputs: []string{"db/migrate-v1.0.sql"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"**/migrate-*.sql"}, + inputs: []string{"db/sept/migrate-v1.sql"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md"}, + inputs: []string{"hello.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md"}, + inputs: []string{"README.md"}, + skipResult: true, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md"}, + inputs: []string{"docs/hello.md"}, + skipResult: true, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md", "README*"}, + inputs: []string{"hello.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md", "README*"}, + inputs: []string{"README.md"}, + skipResult: false, + filterResult: true, + }, + { + patterns: []string{"*.md", "!README.md", "README*"}, + inputs: []string{"README.doc"}, + skipResult: false, + filterResult: true, + }, + } + + for _, kase := range kases { + t.Run(strings.Join(kase.patterns, ","), func(t *testing.T) { + patterns, err := CompilePatterns(kase.patterns...) + assert.NoError(t, err) + + assert.EqualValues(t, kase.skipResult, Skip(patterns, kase.inputs, &StdOutTraceWriter{}), "skipResult") + assert.EqualValues(t, kase.filterResult, Filter(patterns, kase.inputs, &StdOutTraceWriter{}), "filterResult") + }) + } +}