diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index d981d01..e796c1d 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -48,19 +48,10 @@ jobs:
runs-on: '${{ matrix.os }}'
steps:
- - uses: 'actions/checkout@v2'
+ - uses: 'actions/checkout@v3'
- - uses: 'actions/setup-go@v2'
+ - uses: 'actions/setup-go@v3'
with:
go-version: '${{ matrix.go }}'
- - uses: 'actions/cache@v2'
- with:
- path: '~/go/pkg/mod'
- key: '${{ matrix.os }}-go-${{ matrix.go }}-${{ hashFiles(''**/go.sum'') }}'
- restore-keys: |
- ${{ matrix.os }}-go-${{ matrix.go }}
- ${{ matrix.os }}-go-
-
- - name: 'Test'
- run: 'make test-acc'
+ - run: 'make test-acc'
diff --git a/actions.go b/actions.go
index c74cebe..fe64af0 100644
--- a/actions.go
+++ b/actions.go
@@ -84,15 +84,6 @@ func New(opts ...Option) *Action {
return a
}
-// NewWithWriter creates a wrapper using the given writer. This is useful for
-// tests. The given writer cannot add any prefixes to the string, since GitHub
-// requires these special strings to match a very particular format.
-//
-// Deprecated: Use New() with WithWriter instead.
-func NewWithWriter(w io.Writer) *Action {
- return New(WithWriter(w))
-}
-
// Action is an internal wrapper around GitHub Actions' output and magic
// strings.
type Action struct {
@@ -102,12 +93,16 @@ type Action struct {
httpClient *http.Client
}
-// IssueCommand issues a new GitHub actions Command.
+// IssueCommand issues a new GitHub actions Command. It panics if it cannot
+// write to the output stream.
func (c *Action) IssueCommand(cmd *Command) {
- fmt.Fprint(c.w, cmd.String()+EOF)
+ 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
//
@@ -118,7 +113,15 @@ func (c *Action) IssueCommand(cmd *Command) {
// 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) error {
+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
@@ -127,17 +130,26 @@ func (c *Action) IssueFileCommand(cmd *Command) error {
msg := []byte(cmd.Message + EOF)
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
- return fmt.Errorf(errFileCmdFmt, err)
+ retErr = fmt.Errorf(errFileCmdFmt, err)
+ return
}
- defer f.Close()
+
+ defer func() {
+ if err := f.Close(); err != nil && retErr == nil {
+ retErr = err
+ }
+ }()
+
if _, err := f.Write(msg); err != nil {
- return fmt.Errorf(errFileCmdFmt, err)
+ retErr = fmt.Errorf(errFileCmdFmt, err)
+ return
}
- return nil
+ 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.
+// 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{
@@ -146,7 +158,8 @@ func (c *Action) AddMask(p string) {
})
}
-// AddMatcher adds a new matcher with the given file path.
+// 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{
@@ -155,7 +168,8 @@ func (c *Action) AddMatcher(p string) {
})
}
-// RemoveMatcher removes a matcher with the given owner name.
+// 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{
@@ -166,30 +180,20 @@ func (c *Action) RemoveMatcher(o string) {
})
}
-// AddPath adds the string "p" to the path for the invocation. It attempts to
-// issue a file command at first. If that fails, it falls back to the regular
-// (now deprecated) 'add-path' command, which may stop working in the future.
-// The deprecated fallback may be useful for users running an older version of
-// GitHub runner.
+// 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) {
- err := c.IssueFileCommand(&Command{
+ c.IssueFileCommand(&Command{
Name: pathCmd,
Message: p,
})
-
- if err != nil { // use regular command as fallback
- // ::add-path::
- c.IssueCommand(&Command{
- Name: addPathCmd,
- Message: p,
- })
- }
}
-// SaveState saves state to be used in the "finally" post job entry point.
+// SaveState saves state to be used in the "finally" post job entry point. It
+// panics if it cannot write to the output stream.
func (c *Action) SaveState(k, v string) {
// ::save-state name=::
c.IssueCommand(&Command{
@@ -201,7 +205,8 @@ func (c *Action) SaveState(k, v string) {
})
}
-// GetInput gets the input by the given name.
+// 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)
@@ -209,7 +214,8 @@ func (c *Action) GetInput(i string) string {
return strings.TrimSpace(c.getenv(e))
}
-// Group starts a new collapsable region up to the next ungroup invocation.
+// 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{
@@ -218,7 +224,8 @@ func (c *Action) Group(t string) {
})
}
-// EndGroup ends the current group.
+// EndGroup ends the current group. It panics if it cannot write to the output
+// stream.
func (c *Action) EndGroup() {
// ::endgroup::
c.IssueCommand(&Command{
@@ -226,32 +233,20 @@ func (c *Action) EndGroup() {
})
}
-// SetEnv sets an environment variable. It attempts to issue a file command at
-// first. If that fails, it falls back to the regular (now deprecated) 'set-env'
-// command, which may stop working in the future. The deprecated fallback may be
-// useful for users running an older version of GitHub runner.
+// 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) {
- err := c.IssueFileCommand(&Command{
+ c.IssueFileCommand(&Command{
Name: envCmd,
Message: fmt.Sprintf(envCmdMsgFmt, k, envCmdDelimiter, v, envCmdDelimiter),
})
-
- if err != nil { // use regular command as fallback
- // ::set-env name=::
- c.IssueCommand(&Command{
- Name: setEnvCmd,
- Message: v,
- Properties: CommandProperties{
- "name": k,
- },
- })
- }
}
-// SetOutput sets an output parameter.
+// SetOutput sets an output parameter. It panics if it cannot write to the
+// output stream.
func (c *Action) SetOutput(k, v string) {
// ::set-output name=::
c.IssueCommand(&Command{
@@ -264,7 +259,8 @@ func (c *Action) SetOutput(k, v string) {
}
// 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.
+// 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 ...interface{}) {
// ::debug ::
c.IssueCommand(&Command{
@@ -275,7 +271,8 @@ func (c *Action) Debugf(msg string, args ...interface{}) {
}
// 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.
+// 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 ...interface{}) {
// ::notice ::
c.IssueCommand(&Command{
@@ -286,7 +283,8 @@ func (c *Action) Noticef(msg string, args ...interface{}) {
}
// 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.
+// 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 ...interface{}) {
// ::warning ::
c.IssueCommand(&Command{
@@ -297,7 +295,8 @@ func (c *Action) Warningf(msg string, args ...interface{}) {
}
// 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.
+// 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 ...interface{}) {
// ::error ::
c.IssueCommand(&Command{
@@ -314,10 +313,13 @@ func (c *Action) Fatalf(msg string, args ...interface{}) {
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.
+// 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 ...interface{}) {
- fmt.Fprintf(c.w, msg+EOF, args...)
+ 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
diff --git a/actions_root.go b/actions_root.go
index 88d9ba4..f1e29bd 100644
--- a/actions_root.go
+++ b/actions_root.go
@@ -26,8 +26,8 @@ func IssueCommand(cmd *Command) {
}
// IssueFileCommand issues a new GitHub actions Command using environment files.
-func IssueFileCommand(cmd *Command) error {
- return defaultAction.IssueFileCommand(cmd)
+func IssueFileCommand(cmd *Command) {
+ defaultAction.IssueFileCommand(cmd)
}
// AddMask adds a new field mask for the given string "p". After called, future
diff --git a/actions_test.go b/actions_test.go
index 60c2a70..37b1272 100644
--- a/actions_test.go
+++ b/actions_test.go
@@ -53,22 +53,6 @@ func TestNew(t *testing.T) {
}
}
-func TestNewWithWriter(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
- a := NewWithWriter(&b)
-
- a.IssueCommand(&Command{
- Name: "foo",
- Message: "bar",
- })
-
- if got, want := b.String(), "::foo::bar"+EOF; got != want {
- t.Errorf("expected %q to be %q", got, want)
- }
-}
-
func TestAction_IssueCommand(t *testing.T) {
t.Parallel()
@@ -87,7 +71,7 @@ func TestAction_IssueCommand(t *testing.T) {
func TestAction_IssueFileCommand(t *testing.T) {
t.Parallel()
- file, err := ioutil.TempFile(".", ".issue_file_cmd_test_")
+ file, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("unable to create a temp env file: %s", err)
}
@@ -98,12 +82,10 @@ func TestAction_IssueFileCommand(t *testing.T) {
var b bytes.Buffer
a := New(WithWriter(&b), WithGetenv(fakeGetenvFunc))
- err = a.IssueFileCommand(&Command{
+ if err := a.issueFileCommand(&Command{
Name: "foo",
Message: "bar",
- })
-
- if err != nil {
+ }); err != nil {
t.Errorf("expected nil error, got: %s", err)
}
@@ -164,27 +146,16 @@ func TestAction_AddPath(t *testing.T) {
const envGitHubPath = "GITHUB_PATH"
- // expect a regular command to be issued when env file is not set.
- fakeGetenvFunc := newFakeGetenvFunc(t, envGitHubPath, "")
- var b bytes.Buffer
- a := New(WithWriter(&b), WithGetenv(fakeGetenvFunc))
-
- a.AddPath("/custom/bin")
- if got, want := b.String(), "::add-path::/custom/bin"+EOF; got != want {
- t.Errorf("expected %q to be %q", got, want)
- }
-
- b.Reset()
-
// expect a file command to be issued when env file is set.
- file, err := ioutil.TempFile(".", ".add_path_test_")
+ file, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("unable to create a temp env file: %s", err)
}
-
defer os.Remove(file.Name())
- fakeGetenvFunc = newFakeGetenvFunc(t, envGitHubPath, file.Name())
- WithGetenv(fakeGetenvFunc)(a)
+
+ fakeGetenvFunc := newFakeGetenvFunc(t, envGitHubPath, file.Name())
+ var b bytes.Buffer
+ a := New(WithWriter(&b), WithGetenv(fakeGetenvFunc))
a.AddPath("/custom/bin")
@@ -261,27 +232,9 @@ func TestAction_SetEnv(t *testing.T) {
const envGitHubEnv = "GITHUB_ENV"
- // expectations for regular set-env commands
- checks := []struct {
- key, value, want string
- }{
- {"key", "value", "::set-env name=key::value" + EOF},
- {"key", "this is 100% a special\n\r value!", "::set-env name=key::this is 100%25 a special%0A%0D value!" + EOF},
- }
-
- for _, check := range checks {
- fakeGetenvFunc := newFakeGetenvFunc(t, envGitHubEnv, "")
- var b bytes.Buffer
- a := New(WithWriter(&b), WithGetenv(fakeGetenvFunc))
- a.SetEnv(check.key, check.value)
- if got, want := b.String(), check.want; got != want {
- t.Errorf("SetEnv(%q, %q): expected %q; got %q", check.key, check.value, want, got)
- }
- }
-
// expectations for env file env commands
var b bytes.Buffer
- file, err := ioutil.TempFile(".", ".set_env_test_")
+ file, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("unable to create a temp env file: %s", err)
}