diff --git a/vendor/github.com/sethvargo/go-githubactions/.gitignore b/vendor/github.com/sethvargo/go-githubactions/.gitignore new file mode 100644 index 0000000..2d83068 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/.gitignore @@ -0,0 +1 @@ +coverage.out diff --git a/vendor/github.com/sethvargo/go-githubactions/AUTHORS b/vendor/github.com/sethvargo/go-githubactions/AUTHORS new file mode 100644 index 0000000..4d29cb9 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/AUTHORS @@ -0,0 +1,7 @@ +# This is the list of authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. + +Seth Vargo diff --git a/vendor/github.com/sethvargo/go-githubactions/LICENSE b/vendor/github.com/sethvargo/go-githubactions/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/sethvargo/go-githubactions/Makefile b/vendor/github.com/sethvargo/go-githubactions/Makefile new file mode 100644 index 0000000..5593c15 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/Makefile @@ -0,0 +1,31 @@ +# Copyright 2020 The Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +test: + @go test \ + -count=1 \ + -short \ + -shuffle=on \ + -timeout=5m \ + ./... +.PHONY: test + +test-acc: + @go test \ + -count=1 \ + -race \ + -shuffle=on \ + -timeout=10m \ + ./... +.PHONY: test-acc diff --git a/vendor/github.com/sethvargo/go-githubactions/README.md b/vendor/github.com/sethvargo/go-githubactions/README.md new file mode 100644 index 0000000..ec3cfe8 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/README.md @@ -0,0 +1,152 @@ +# GitHub Actions SDK (Go) + +[![Go Reference](https://pkg.go.dev/badge/github.com/sethvargo/go-githubactions.svg)](https://pkg.go.dev/github.com/sethvargo/go-githubactions) +[![unit](https://github.com/sethvargo/go-githubactions/actions/workflows/unit.yml/badge.svg)](https://github.com/sethvargo/go-githubactions/actions/workflows/unit.yml) + +This library provides an SDK for authoring [GitHub Actions][gh-actions] in Go. It has no external dependencies and provides a Go-like interface for interacting with GitHub Actions' build system. + + +## Installation + +Download the library: + +```text +$ go get -u github.com/sethvargo/go-githubactions/... +``` + + +## Usage + +The easiest way to use the library is by importing it and invoking the functions +at the root: + +```go +import ( + "github.com/sethvargo/go-githubactions" +) + +func main() { + val := githubactions.GetInput("val") + if val == "" { + githubactions.Fatalf("missing 'val'") + } +} +``` + +You can also create an instance with custom fields that will be included in log messages: + +```go +import ( + "github.com/sethvargo/go-githubactions" +) + +func main() { + actions := githubactions.WithFieldsMap(map[string]string{ + "file": "myfile.js", + "line": "100", + }) + + val := actions.GetInput("val") + if val == "" { + actions.Fatalf("missing 'val'") + } +} +``` + +For more examples and API documentation, please see the [Go docs][godoc]. + + +## Publishing + +There are multiple ways to publish GitHub Actions written in Go: + +- [Composite actions](https://github.com/FerretDB/github-actions/blob/2ae30fd2cdb635d8aefdaf9f770257e156c9f77b/extract-docker-tag/action.yml) +- [Pre-compiled binaries with a shim](https://full-stack.blend.com/how-we-write-github-actions-in-go.html) +- Docker containers (see below) + +By default, GitHub Actions expects actions to be written in Node.js. For other languages like Go, you need to provide a `Dockerfile` and entrypoint instructions in an `action.yml` file: + +```dockerfile +# your-repo/Dockerfile +FROM golang:1.18 +WORKDIR /src +COPY . . +RUN go build -o /bin/app . +ENTRYPOINT ["/bin/app"] +``` + +```yaml +# your-repo/action.yml +name: My action +author: My name +description: My description + +runs: + using: docker + image: Dockerfile +``` + +And then users can import your action by the repository name: + +```yaml +# their-repo/.github/workflows/thing.yml +steps: +- name: My action + uses: username/repo@latest +``` + +However, this will clone the entire repo and compile the Go code each time the action runs. Worse, it uses the Go base container which is a few hundred MBs and includes a ton of unnecessary things. + +Fortunately, GitHub Actions can also source from a Docker container directly from Docker Hub: + +```yaml +steps: +- name: My action + uses: docker://username/repo:latest +``` + +Now we can precompile and publish our Go Action as a Docker container, but we need to make it much, much smaller first. This can be achieved using multi-stage Docker builds: + +```dockerfile +FROM golang:1.18 AS builder + +ENV GO111MODULE=on \ + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 + +RUN apt-get -qq update && \ + apt-get -yqq install upx + +WORKDIR /src +COPY . . + +RUN go build \ + -ldflags "-s -w -extldflags '-static'" \ + -o /bin/app \ + . \ + && strip /bin/app \ + && upx -q -9 /bin/app + +RUN echo "nobody:x:65534:65534:Nobody:/:" > /etc_passwd + + + +FROM scratch + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /etc_passwd /etc/passwd +COPY --from=builder --chown=65534:0 /bin/app /app + +USER nobody +ENTRYPOINT ["/app"] +``` + +The first step, uses a fat container to build, strip, and compress the compiled Go binary. Then, in the second step, the compiled and compressed binary is copied into a scratch (bare) container along with some SSL certificates and a `nobody` user in which to execute the container. + +This will usually produce an image that is less than 10MB in size, making for +much faster builds. + + +[gh-actions]: https://github.com/features/actions +[godoc]: https://godoc.org/github.com/sethvargo/go-githubactions diff --git a/vendor/github.com/sethvargo/go-githubactions/actions.go b/vendor/github.com/sethvargo/go-githubactions/actions.go new file mode 100644 index 0000000..2db421c --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/actions.go @@ -0,0 +1,686 @@ +// Copyright 2020 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package githubactions provides an SDK for authoring GitHub Actions in Go. It +// has no external dependencies and provides a Go-like interface for interacting +// with GitHub Actions' build system. +package githubactions + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "html/template" + "io" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" +) + +var ( + // osExit allows `os.Exit()` to be stubbed during testing. + osExit = os.Exit +) + +const ( + addMaskCmd = "add-mask" + + envCmd = "env" + outputCmd = "output" + pathCmd = "path" + stateCmd = "state" + + // https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings + multiLineFileDelim = "_GitHubActionsFileCommandDelimeter_" + multilineFileCmd = "%s<<" + multiLineFileDelim + EOF + "%s" + EOF + multiLineFileDelim // ${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter} + + addMatcherCmd = "add-matcher" + removeMatcherCmd = "remove-matcher" + + groupCmd = "group" + endGroupCmd = "endgroup" + + stepSummaryCmd = "step-summary" + + debugCmd = "debug" + noticeCmd = "notice" + warningCmd = "warning" + errorCmd = "error" + + errFileCmdFmt = "unable to write command to the environment file: %s" +) + +// New creates a new wrapper with helpers for outputting information in GitHub +// actions format. +func New(opts ...Option) *Action { + a := &Action{ + w: os.Stdout, + getenv: os.Getenv, + httpClient: &http.Client{ + Timeout: 10 * time.Second, + }, + } + + for _, opt := range opts { + if opt == nil { + continue + } + a = opt(a) + } + + return a +} + +// Action is an internal wrapper around GitHub Actions' output and magic +// strings. +type Action struct { + w io.Writer + fields CommandProperties + getenv GetenvFunc + httpClient *http.Client +} + +// IssueCommand issues a new GitHub actions Command. It panics if it cannot +// write to the output stream. +func (c *Action) IssueCommand(cmd *Command) { + if _, err := fmt.Fprint(c.w, cmd.String()+EOF); err != nil { + panic(fmt.Errorf("failed to issue command: %w", err)) + } +} + +// IssueFileCommand issues a new GitHub actions Command using environment files. +// It panics if writing to the file fails. +// +// https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#environment-files +// +// The TypeScript equivalent function: +// +// https://github.com/actions/toolkit/blob/4f7fb6513a355689f69f0849edeb369a4dc81729/packages/core/src/file-command.ts#L10-L23 +// +// IssueFileCommand currently ignores the 'CommandProperties' field provided +// with the 'Command' argument as it's scope is unclear in the current +// TypeScript implementation. +func (c *Action) IssueFileCommand(cmd *Command) { + if err := c.issueFileCommand(cmd); err != nil { + panic(err) + } +} + +// issueFileCommand is an internal-only helper that issues the command and +// returns an error to make testing easier. +func (c *Action) issueFileCommand(cmd *Command) (retErr error) { + e := strings.ReplaceAll(cmd.Name, "-", "_") + e = strings.ToUpper(e) + e = "GITHUB_" + e + + filepath := c.getenv(e) + msg := []byte(cmd.Message + EOF) + f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + retErr = fmt.Errorf(errFileCmdFmt, err) + return + } + + defer func() { + if err := f.Close(); err != nil && retErr == nil { + retErr = err + } + }() + + if _, err := f.Write(msg); err != nil { + retErr = fmt.Errorf(errFileCmdFmt, err) + return + } + return +} + +// AddMask adds a new field mask for the given string "p". After called, future +// attempts to log "p" will be replaced with "***" in log output. It panics if +// it cannot write to the output stream. +func (c *Action) AddMask(p string) { + // ::add-mask::

+ c.IssueCommand(&Command{ + Name: addMaskCmd, + Message: p, + }) +} + +// AddMatcher adds a new matcher with the given file path. It panics if it +// cannot write to the output stream. +func (c *Action) AddMatcher(p string) { + // ::add-matcher::

+ c.IssueCommand(&Command{ + Name: addMatcherCmd, + Message: p, + }) +} + +// RemoveMatcher removes a matcher with the given owner name. It panics if it +// cannot write to the output stream. +func (c *Action) RemoveMatcher(o string) { + // ::remove-matcher owner=:: + c.IssueCommand(&Command{ + Name: removeMatcherCmd, + Properties: CommandProperties{ + "owner": o, + }, + }) +} + +// AddPath adds the string "p" to the path for the invocation. It panics if it +// cannot write to the output file. +// +// https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#adding-a-system-path +// https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ +func (c *Action) AddPath(p string) { + c.IssueFileCommand(&Command{ + Name: pathCmd, + Message: p, + }) +} + +// SaveState saves state to be used in the "finally" post job entry point. It +// panics if it cannot write to the output stream. +// +// On 2022-10-11, GitHub deprecated "::save-state name=::" in favor of +// [environment files]. +// +// [environment files]: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ +func (c *Action) SaveState(k, v string) { + c.IssueFileCommand(&Command{ + Name: stateCmd, + Message: fmt.Sprintf(multilineFileCmd, k, v), + }) +} + +// GetInput gets the input by the given name. It returns the empty string if the +// input is not defined. +func (c *Action) GetInput(i string) string { + e := strings.ReplaceAll(i, " ", "_") + e = strings.ToUpper(e) + e = "INPUT_" + e + return strings.TrimSpace(c.getenv(e)) +} + +// Group starts a new collapsable region up to the next ungroup invocation. It +// panics if it cannot write to the output stream. +func (c *Action) Group(t string) { + // ::group:: + c.IssueCommand(&Command{ + Name: groupCmd, + Message: t, + }) +} + +// EndGroup ends the current group. It panics if it cannot write to the output +// stream. +func (c *Action) EndGroup() { + // ::endgroup:: + c.IssueCommand(&Command{ + Name: endGroupCmd, + }) +} + +// AddStepSummary writes the given markdown to the job summary. If a job summary +// already exists, this value is appended. +// +// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary +// https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/ +func (c *Action) AddStepSummary(markdown string) { + c.IssueFileCommand(&Command{ + Name: stepSummaryCmd, + Message: markdown, + }) +} + +// AddStepSummaryTemplate adds a summary template by parsing the given Go +// template using html/template with the given input data. See AddStepSummary +// for caveats. +// +// This primarily exists as a convenience function that renders a template. +func (c *Action) AddStepSummaryTemplate(tmpl string, data any) error { + t, err := template.New("").Parse(tmpl) + if err != nil { + return fmt.Errorf("failed to parse template: %w", err) + } + + var b bytes.Buffer + if err := t.Execute(&b, data); err != nil { + return fmt.Errorf("failed to execute template: %w", err) + } + + c.AddStepSummary(b.String()) + return nil +} + +// SetEnv sets an environment variable. It panics if it cannot write to the +// output file. +// +// https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable +// https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ +func (c *Action) SetEnv(k, v string) { + c.IssueFileCommand(&Command{ + Name: envCmd, + Message: fmt.Sprintf(multilineFileCmd, k, v), + }) +} + +// SetOutput sets an output parameter. It panics if it cannot write to the +// output stream. +// +// On 2022-10-11, GitHub deprecated "::set-output name=::" in favor of +// [environment files]. +// +// [environment files]: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ +func (c *Action) SetOutput(k, v string) { + c.IssueFileCommand(&Command{ + Name: outputCmd, + Message: fmt.Sprintf(multilineFileCmd, k, v), + }) +} + +// Debugf prints a debug-level message. It follows the standard fmt.Printf +// arguments, appending an OS-specific line break to the end of the message. It +// panics if it cannot write to the output stream. +func (c *Action) Debugf(msg string, args ...any) { + // ::debug :: + c.IssueCommand(&Command{ + Name: debugCmd, + Message: fmt.Sprintf(msg, args...), + Properties: c.fields, + }) +} + +// Noticef prints a notice-level message. It follows the standard fmt.Printf +// arguments, appending an OS-specific line break to the end of the message. It +// panics if it cannot write to the output stream. +func (c *Action) Noticef(msg string, args ...any) { + // ::notice :: + c.IssueCommand(&Command{ + Name: noticeCmd, + Message: fmt.Sprintf(msg, args...), + Properties: c.fields, + }) +} + +// Warningf prints a warning-level message. It follows the standard fmt.Printf +// arguments, appending an OS-specific line break to the end of the message. It +// panics if it cannot write to the output stream. +func (c *Action) Warningf(msg string, args ...any) { + // ::warning :: + c.IssueCommand(&Command{ + Name: warningCmd, + Message: fmt.Sprintf(msg, args...), + Properties: c.fields, + }) +} + +// Errorf prints a error-level message. It follows the standard fmt.Printf +// arguments, appending an OS-specific line break to the end of the message. It +// panics if it cannot write to the output stream. +func (c *Action) Errorf(msg string, args ...any) { + // ::error :: + c.IssueCommand(&Command{ + Name: errorCmd, + Message: fmt.Sprintf(msg, args...), + Properties: c.fields, + }) +} + +// Fatalf prints a error-level message and exits. This is equivalent to Errorf +// followed by os.Exit(1). +func (c *Action) Fatalf(msg string, args ...any) { + c.Errorf(msg, args...) + osExit(1) +} + +// Infof prints message to stdout without any level annotations. It follows the +// standard fmt.Printf arguments, appending an OS-specific line break to the end +// of the message. It panics if it cannot write to the output stream. +func (c *Action) Infof(msg string, args ...any) { + if _, err := fmt.Fprintf(c.w, msg+EOF, args...); err != nil { + panic(fmt.Errorf("failed to write info command: %w", err)) + } +} + +// WithFieldsSlice includes the provided fields in log output. "f" must be a +// slice of k=v pairs. The given slice will be sorted. It panics if any of the +// string in the given slice does not construct a valid 'key=value' pair. +func (c *Action) WithFieldsSlice(f []string) *Action { + m := make(CommandProperties) + for _, s := range f { + pair := strings.SplitN(s, "=", 2) + if len(pair) < 2 { + panic(fmt.Sprintf("%q is not a proper k=v pair!", s)) + } + + m[pair[0]] = pair[1] + } + + return c.WithFieldsMap(m) +} + +// WithFieldsMap includes the provided fields in log output. The fields in "m" +// are automatically converted to k=v pairs and sorted. +func (c *Action) WithFieldsMap(m map[string]string) *Action { + return &Action{ + w: c.w, + fields: m, + getenv: c.getenv, + httpClient: c.httpClient, + } +} + +// idTokenResponse is the response from minting an ID token. +type idTokenResponse struct { + Value string `json:"value,omitempty"` +} + +// GetIDToken returns the GitHub OIDC token from the GitHub Actions runtime. +func (c *Action) GetIDToken(ctx context.Context, audience string) (string, error) { + requestURL := c.getenv("ACTIONS_ID_TOKEN_REQUEST_URL") + if requestURL == "" { + return "", fmt.Errorf("missing ACTIONS_ID_TOKEN_REQUEST_URL in environment") + } + + requestToken := c.getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN") + if requestToken == "" { + return "", fmt.Errorf("missing ACTIONS_ID_TOKEN_REQUEST_TOKEN in environment") + } + + u, err := url.Parse(requestURL) + if err != nil { + return "", fmt.Errorf("failed to parse request URL: %w", err) + } + if audience != "" { + q := u.Query() + q.Set("audience", audience) + u.RawQuery = q.Encode() + } + + req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil) + if err != nil { + return "", fmt.Errorf("failed to create HTTP request: %w", err) + } + req.Header.Set("Authorization", "Bearer "+requestToken) + + resp, err := c.httpClient.Do(req) + if err != nil { + return "", fmt.Errorf("failed to make HTTP request: %w", err) + } + defer resp.Body.Close() + + // This has moved to the io package in Go 1.16, but we still support up to Go + // 1.13 for now. + body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1000)) + if err != nil { + return "", fmt.Errorf("failed to read response body: %w", err) + } + body = bytes.TrimSpace(body) + + if resp.StatusCode != 200 { + return "", fmt.Errorf("non-successful response from minting OIDC token: %s", body) + } + + var tokenResp idTokenResponse + if err := json.Unmarshal(body, &tokenResp); err != nil { + return "", fmt.Errorf("failed to process response as JSON: %w", err) + } + return tokenResp.Value, nil +} + +// Getenv retrieves the value of the environment variable named by the key. +// It uses an internal function that can be set with `WithGetenv`. +func (c *Action) Getenv(key string) string { + return c.getenv(key) +} + +// GetenvFunc is an abstraction to make tests feasible for commands that +// interact with environment variables. +type GetenvFunc func(key string) string + +// GitHubContext of current workflow. +// +// See: https://docs.github.com/en/actions/learn-github-actions/environment-variables +type GitHubContext struct { + Action string + ActionPath string + ActionRepository string + Actions bool + Actor string + ActorID string + APIURL string + BaseRef string + Env string + EventName string + EventPath string + GraphqlURL string + HeadRef string + Job string + Path string + Ref string + RefName string + RefProtected bool + RefType string + + // Repository is the owner and repository name. For example, octocat/Hello-World + // It is not recommended to use this field to acquire the repository name + // but to use the Repo method instead. + Repository string + + // RepositoryOwner is the repository owner. For example, octocat + // It is not recommended to use this field to acquire the repository owner + // but to use the Repo method instead. + RepositoryOwner string + + RetentionDays int64 + RunAttempt int64 + RunID int64 + RunNumber int64 + ServerURL string + SHA string + StepSummary string + TriggeringActor string + Workflow string + Workspace string + + // Event is populated by parsing the file at EventPath, if it exists. + Event map[string]any +} + +// Repo returns the username of the repository owner and repository name. +func (c *GitHubContext) Repo() (string, string) { + if c == nil { + return "", "" + } + + // Based on https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts + if c.Repository != "" { + parts := strings.SplitN(c.Repository, "/", 2) + if len(parts) == 1 { + return parts[0], "" + } + return parts[0], parts[1] + } + + // If c.Repository is empty attempt to get the repo from the Event data. + var repoName string + // NOTE: differs from context.ts. Fall back to GITHUB_REPOSITORY_OWNER + ownerName := c.RepositoryOwner + if c.Event != nil { + if repo, ok := c.Event["repository"].(map[string]any); ok { + if name, ok := repo["name"].(string); ok { + repoName = name + } + if owner, ok := repo["owner"].(map[string]any); ok { + if name, ok := owner["name"].(string); ok { + ownerName = name + } + } + } + } + return ownerName, repoName +} + +func parseBool(v string) (bool, error) { + if v == "" { + return false, nil + } + return strconv.ParseBool(v) +} + +func parseInt(v string) (int64, error) { + if v == "" { + return 0, nil + } + return strconv.ParseInt(v, 10, 64) +} + +// Context returns the context of current action with the payload object +// that triggered the workflow +func (c *Action) Context() (*GitHubContext, error) { + var merr error + githubContext := &GitHubContext{ + APIURL: "https://api.github.com", + GraphqlURL: "https://api.github.com/graphql", + ServerURL: "https://github.com", + } + + if v := c.getenv("GITHUB_ACTION"); v != "" { + githubContext.Action = v + } + if v := c.getenv("GITHUB_ACTION_PATH"); v != "" { + githubContext.ActionPath = v + } + if v := c.getenv("GITHUB_ACTION_REPOSITORY"); v != "" { + githubContext.ActionRepository = v + } + if v, err := parseBool(c.getenv("GITHUB_ACTIONS")); err == nil { + githubContext.Actions = v + } else { + merr = errors.Join(merr, err) + } + if v := c.getenv("GITHUB_ACTOR"); v != "" { + githubContext.Actor = v + } + if v := c.getenv("GITHUB_ACTOR_ID"); v != "" { + githubContext.ActorID = v + } + if v := c.getenv("GITHUB_API_URL"); v != "" { + githubContext.APIURL = v + } + if v := c.getenv("GITHUB_BASE_REF"); v != "" { + githubContext.BaseRef = v + } + if v := c.getenv("GITHUB_ENV"); v != "" { + githubContext.Env = v + } + if v := c.getenv("GITHUB_EVENT_NAME"); v != "" { + githubContext.EventName = v + } + if v := c.getenv("GITHUB_EVENT_PATH"); v != "" { + githubContext.EventPath = v + } + if v := c.getenv("GITHUB_GRAPHQL_URL"); v != "" { + githubContext.GraphqlURL = v + } + if v := c.getenv("GITHUB_HEAD_REF"); v != "" { + githubContext.HeadRef = v + } + if v := c.getenv("GITHUB_JOB"); v != "" { + githubContext.Job = v + } + if v := c.getenv("GITHUB_PATH"); v != "" { + githubContext.Path = v + } + if v := c.getenv("GITHUB_REF"); v != "" { + githubContext.Ref = v + } + if v := c.getenv("GITHUB_REF_NAME"); v != "" { + githubContext.RefName = v + } + if v, err := parseBool(c.getenv("GITHUB_REF_PROTECTED")); err == nil { + githubContext.RefProtected = v + } else { + merr = errors.Join(merr, err) + } + if v := c.getenv("GITHUB_REF_TYPE"); v != "" { + githubContext.RefType = v + } + + if v := c.getenv("GITHUB_REPOSITORY"); v != "" { + githubContext.Repository = v + } + if v := c.getenv("GITHUB_REPOSITORY_OWNER"); v != "" { + githubContext.RepositoryOwner = v + } + + if v, err := parseInt(c.getenv("GITHUB_RETENTION_DAYS")); err == nil { + githubContext.RetentionDays = v + } else { + merr = errors.Join(merr, err) + } + if v, err := parseInt(c.getenv("GITHUB_RUN_ATTEMPT")); err == nil { + githubContext.RunAttempt = v + } else { + merr = errors.Join(merr, err) + } + if v, err := parseInt(c.getenv("GITHUB_RUN_ID")); err == nil { + githubContext.RunID = v + } else { + merr = errors.Join(merr, err) + } + if v, err := parseInt(c.getenv("GITHUB_RUN_NUMBER")); err == nil { + githubContext.RunNumber = v + } else { + merr = errors.Join(merr, err) + } + if v := c.getenv("GITHUB_SERVER_URL"); v != "" { + githubContext.ServerURL = v + } + if v := c.getenv("GITHUB_SHA"); v != "" { + githubContext.SHA = v + } + if v := c.getenv("GITHUB_STEP_SUMMARY"); v != "" { + githubContext.StepSummary = v + } + if v := c.getenv("GITHUB_TRIGGERING_ACTOR"); v != "" { + githubContext.TriggeringActor = v + } + if v := c.getenv("GITHUB_WORKFLOW"); v != "" { + githubContext.Workflow = v + } + if v := c.getenv("GITHUB_WORKSPACE"); v != "" { + githubContext.Workspace = v + } + + if githubContext.EventPath != "" { + eventData, err := os.ReadFile(githubContext.EventPath) + if err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("could not read event file: %w", err) + } + if eventData != nil { + if err := json.Unmarshal(eventData, &githubContext.Event); err != nil { + return nil, fmt.Errorf("failed to unmarshal event payload: %w", err) + } + } + } + + return githubContext, merr +} diff --git a/vendor/github.com/sethvargo/go-githubactions/actions_root.go b/vendor/github.com/sethvargo/go-githubactions/actions_root.go new file mode 100644 index 0000000..f8c25a9 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/actions_root.go @@ -0,0 +1,156 @@ +// Copyright 2020 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package githubactions + +import ( + "context" +) + +var ( + defaultAction = New() +) + +// IssueCommand issues an arbitrary GitHub actions Command. +func IssueCommand(cmd *Command) { + defaultAction.IssueCommand(cmd) +} + +// IssueFileCommand issues a new GitHub actions Command using environment files. +func IssueFileCommand(cmd *Command) { + defaultAction.IssueFileCommand(cmd) +} + +// AddMask adds a new field mask for the given string "p". After called, future +// attempts to log "p" will be replaced with "***" in log output. +func AddMask(p string) { + defaultAction.AddMask(p) +} + +// AddMatcher adds a new matcher with the given file path. +func AddMatcher(p string) { + defaultAction.AddMatcher(p) +} + +// RemoveMatcher removes a matcher with the given owner name. +func RemoveMatcher(o string) { + defaultAction.RemoveMatcher(o) +} + +// AddPath adds the string "p" to the path for the invocation. +func AddPath(p string) { + defaultAction.AddPath(p) +} + +// SaveState saves state to be used in the "finally" post job entry point. +func SaveState(k, v string) { + defaultAction.SaveState(k, v) +} + +// GetInput gets the input by the given name. +func GetInput(i string) string { + return defaultAction.GetInput(i) +} + +// Group starts a new collapsable region up to the next ungroup invocation. +func Group(t string) { + defaultAction.Group(t) +} + +// EndGroup ends the current group. +func EndGroup() { + defaultAction.EndGroup() +} + +// AddStepSummary writes the given markdown to the job summary. If a job summary +// already exists, this value is appended. +func AddStepSummary(markdown string) { + defaultAction.AddStepSummary(markdown) +} + +// AddStepSummaryTemplate adds a summary template by parsing the given Go +// template using html/template with the given input data. See AddStepSummary +// for caveats. +// +// This primarily exists as a convenience function that renders a template. +func AddStepSummaryTemplate(tmpl string, data any) error { + return defaultAction.AddStepSummaryTemplate(tmpl, data) +} + +// SetEnv sets an environment variable. +func SetEnv(k, v string) { + defaultAction.SetEnv(k, v) +} + +// SetOutput sets an output parameter. +func SetOutput(k, v string) { + defaultAction.SetOutput(k, v) +} + +// Debugf prints a debug-level message. The arguments follow the standard Printf +// arguments. +func Debugf(msg string, args ...any) { + defaultAction.Debugf(msg, args...) +} + +// Noticef prints a notice-level message. The arguments follow the standard +// Printf arguments. +func Noticef(msg string, args ...any) { + defaultAction.Noticef(msg, args...) +} + +// Errorf prints a error-level message. The arguments follow the standard Printf +// arguments. +func Errorf(msg string, args ...any) { + defaultAction.Errorf(msg, args...) +} + +// Fatalf prints a error-level message and exits. This is equivalent to Errorf +// followed by os.Exit(1). +func Fatalf(msg string, args ...any) { + defaultAction.Fatalf(msg, args...) +} + +// Infof prints a info-level message. The arguments follow the standard Printf +// arguments. +func Infof(msg string, args ...any) { + defaultAction.Infof(msg, args...) +} + +// Warningf prints a warning-level message. The arguments follow the standard +// Printf arguments. +func Warningf(msg string, args ...any) { + defaultAction.Warningf(msg, args...) +} + +// WithFieldsSlice includes the provided fields in log output. "f" must be a +// slice of k=v pairs. The given slice will be sorted. +func WithFieldsSlice(f []string) *Action { + return defaultAction.WithFieldsSlice(f) +} + +// WithFieldsMap includes the provided fields in log output. The fields in "m" +// are automatically converted to k=v pairs and sorted. +func WithFieldsMap(m map[string]string) *Action { + return defaultAction.WithFieldsMap(m) +} + +// GetIDToken returns the GitHub OIDC token from the GitHub Actions runtime. +func GetIDToken(ctx context.Context, audience string) (string, error) { + return defaultAction.GetIDToken(ctx, audience) +} + +func Context() (*GitHubContext, error) { + return defaultAction.Context() +} diff --git a/vendor/github.com/sethvargo/go-githubactions/command.go b/vendor/github.com/sethvargo/go-githubactions/command.go new file mode 100644 index 0000000..24ef400 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/command.go @@ -0,0 +1,112 @@ +// Copyright 2020 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package githubactions + +import ( + "fmt" + "sort" + "strings" +) + +const ( + cmdSeparator = "::" + cmdPropertiesPrefix = " " +) + +// CommandProperties is a named "map[string]string" type to hold key-value pairs +// passed to an actions command. +type CommandProperties map[string]string + +// String encodes the CommandProperties to a string as comma separated +// 'key=value' pairs. The pairs are joined in a chronological order. +func (props *CommandProperties) String() string { + l := make([]string, 0, len(*props)) + for k, v := range *props { + l = append(l, fmt.Sprintf("%s=%s", k, escapeProperty(v))) + } + + sort.Strings(l) + return strings.Join(l, ",") +} + +// Command can be issued by a GitHub action by writing to `stdout` with +// following format. +// +// ::name key=value,key=value::message +// +// Examples: +// ::warning::This is the message +// ::set-env name=MY_VAR::some value +type Command struct { + Name string + Message string + Properties CommandProperties +} + +// String encodes the Command to a string in the following format: +// +// ::name key=value,key=value::message +func (cmd *Command) String() string { + // https://github.com/actions/toolkit/blob/9ad01e4fd30025e8858650d38e95cfe9193a3222/packages/core/src/command.ts#L43-L45 + if cmd.Name == "" { + cmd.Name = "missing.command" + } + + var builder strings.Builder + builder.WriteString(cmdSeparator) + builder.WriteString(cmd.Name) + if len(cmd.Properties) > 0 { + builder.WriteString(cmdPropertiesPrefix) + builder.WriteString(cmd.Properties.String()) + } + + builder.WriteString(cmdSeparator) + builder.WriteString(escapeData(cmd.Message)) + return builder.String() +} + +// escapeData escapes string values for presentation in the output of a command. +// This is a not-so-well-documented requirement of commands that define a +// message: +// +// https://github.com/actions/toolkit/blob/9ad01e4fd30025e8858650d38e95cfe9193a3222/packages/core/src/command.ts#L74 +// +// The equivalent toolkit function can be found here: +// +// https://github.com/actions/toolkit/blob/9ad01e4fd30025e8858650d38e95cfe9193a3222/packages/core/src/command.ts#L92 +// +func escapeData(v string) string { + v = strings.ReplaceAll(v, "%", "%25") + v = strings.ReplaceAll(v, "\r", "%0D") + v = strings.ReplaceAll(v, "\n", "%0A") + return v +} + +// escapeData escapes command property values for presentation in the output of +// a command. +// +// https://github.com/actions/toolkit/blob/9ad01e4fd30025e8858650d38e95cfe9193a3222/packages/core/src/command.ts#L68 +// +// The equivalent toolkit function can be found here: +// +// https://github.com/actions/toolkit/blob/1cc56db0ff126f4d65aeb83798852e02a2c180c3/packages/core/src/command.ts#L99-L106 +func escapeProperty(v string) string { + v = strings.ReplaceAll(v, "%", "%25") + v = strings.ReplaceAll(v, "\r", "%0D") + v = strings.ReplaceAll(v, "\n", "%0A") + v = strings.ReplaceAll(v, ":", "%3A") + v = strings.ReplaceAll(v, ",", "%2C") + return v +} diff --git a/vendor/github.com/sethvargo/go-githubactions/eof_crlf.go b/vendor/github.com/sethvargo/go-githubactions/eof_crlf.go new file mode 100644 index 0000000..12d0d31 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/eof_crlf.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build windows +// +build windows + +package githubactions + +const EOF = "\r\n" diff --git a/vendor/github.com/sethvargo/go-githubactions/eof_lf.go b/vendor/github.com/sethvargo/go-githubactions/eof_lf.go new file mode 100644 index 0000000..b2c3456 --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/eof_lf.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !windows +// +build !windows + +package githubactions + +const EOF = "\n" diff --git a/vendor/github.com/sethvargo/go-githubactions/options.go b/vendor/github.com/sethvargo/go-githubactions/options.go new file mode 100644 index 0000000..55727aa --- /dev/null +++ b/vendor/github.com/sethvargo/go-githubactions/options.go @@ -0,0 +1,59 @@ +// Copyright 2021 The Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package githubactions + +import ( + "io" + "net/http" +) + +// Option is a modifier for an Action. +type Option func(*Action) *Action + +// WithWriter sets the writer function on an Action. By default, this will +// be `os.Stdout` from the standard library. +func WithWriter(w io.Writer) Option { + return func(a *Action) *Action { + a.w = w + return a + } +} + +// WithFields sets the extra command field on an Action. +func WithFields(fields CommandProperties) Option { + return func(a *Action) *Action { + a.fields = fields + return a + } +} + +// WithGetenv sets the `Getenv` function on an Action. By default, this will +// be `os.Getenv` from the standard library. +func WithGetenv(getenv GetenvFunc) Option { + return func(a *Action) *Action { + a.getenv = getenv + return a + } +} + +// WithHTTPClient sets a custom HTTP client on the action. This is only used +// when the action makes output HTTP requests (such as generating an OIDC +// token). +func WithHTTPClient(c *http.Client) Option { + return func(a *Action) *Action { + a.httpClient = c + return a + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..cda6b3e --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/sethvargo/go-githubactions v1.3.1 +## explicit; go 1.21 +github.com/sethvargo/go-githubactions