Skip to content

Commit

Permalink
Mirror actions/toolkit on newline handling, emit \r\n on Windows (#33)
Browse files Browse the repository at this point in the history
* Add Noticef.

* Make Infof behave like other commands.

Add trailing \n automatically if needed.

* Mirror actions/toolkit on newline handling, emit \r\n on Windows

Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
  • Loading branch information
sethvargo and AlekSi committed Dec 29, 2021
1 parent 91ea68e commit e5e41f9
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 66 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/test.yml → .github/workflows/unit.yml
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

name: 'Test'
name: 'unit'

on:
push:
Expand All @@ -23,9 +23,10 @@ on:
pull_request:
branches:
- 'main'
workflow_dispatch:

concurrency:
group: '${{ github.head_ref || github.ref }}'
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
Expand Down Expand Up @@ -62,4 +63,4 @@ jobs:
${{ matrix.os }}-go-
- name: 'Test'
run: make test-acc
run: 'make test-acc'
65 changes: 39 additions & 26 deletions actions.go
Expand Up @@ -44,9 +44,9 @@ const (
addPathCmd = "add-path" // used when issuing the regular command
pathCmd = "path" // used when issuing the file command

setEnvCmd = "set-env" // used when issuing the regular command
envCmd = "env" // used when issuing the file command
envCmdMsgFmt = "%s<<%s\n%s\n%s" // ${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}
setEnvCmd = "set-env" // used when issuing the regular command
envCmd = "env" // used when issuing the file command
envCmdMsgFmt = "%s<<%s" + EOF + "%s" + EOF + "%s" // ${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}
envCmdDelimiter = "_GitHubActionsFileCommandDelimeter_"

addMatcherCmd = "add-matcher"
Expand All @@ -56,8 +56,9 @@ const (
endGroupCmd = "endgroup"

debugCmd = "debug"
errorCmd = "error"
noticeCmd = "notice"
warningCmd = "warning"
errorCmd = "error"

errFileCmdFmt = "unable to write command to the environment file: %s"
)
Expand Down Expand Up @@ -103,7 +104,7 @@ type Action struct {

// IssueCommand issues a new GitHub actions Command.
func (c *Action) IssueCommand(cmd *Command) {
fmt.Fprintln(c.w, cmd.String())
fmt.Fprint(c.w, cmd.String()+EOF)
}

// IssueFileCommand issues a new GitHub actions Command using environment files.
Expand All @@ -122,8 +123,9 @@ func (c *Action) IssueFileCommand(cmd *Command) error {
e = strings.ToUpper(e)
e = "GITHUB_" + e

err := ioutil.WriteFile(c.getenv(e), []byte(cmd.Message+"\n"), os.ModeAppend)
if err != nil {
filepath := c.getenv(e)
msg := []byte(cmd.Message + EOF)
if err := ioutil.WriteFile(filepath, msg, os.ModeAppend); err != nil {
return fmt.Errorf(errFileCmdFmt, err)
}

Expand Down Expand Up @@ -257,8 +259,8 @@ func (c *Action) SetOutput(k, v string) {
})
}

// Debugf prints a debug-level message. The arguments follow the standard Printf
// arguments.
// Debugf prints a debug-level message. It follows the standard fmt.Printf
// arguments, appending an operating-system to the end of the message.
func (c *Action) Debugf(msg string, args ...interface{}) {
// ::debug <c.fields>::<msg, args>
c.IssueCommand(&Command{
Expand All @@ -268,8 +270,30 @@ func (c *Action) Debugf(msg string, args ...interface{}) {
})
}

// Errorf prints a error-level message. The arguments follow the standard Printf
// arguments.
// Noticef prints a notice-level message. It follows the standard fmt.Printf
// arguments, appending an operating-system to the end of the message.
func (c *Action) Noticef(msg string, args ...interface{}) {
// ::notice <c.fields>::<msg, args>
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 operating-system to the end of the message.
func (c *Action) Warningf(msg string, args ...interface{}) {
// ::warning <c.fields>::<msg, args>
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 operating-system to the end of the message.
func (c *Action) Errorf(msg string, args ...interface{}) {
// ::error <c.fields>::<msg, args>
c.IssueCommand(&Command{
Expand All @@ -286,22 +310,11 @@ func (c *Action) Fatalf(msg string, args ...interface{}) {
osExit(1)
}

// Infof prints a info-level message. The arguments follow the standard Printf
// arguments.
// Infof prints message to stdout without any level annotations. It follows the
// standard fmt.Printf arguments, appending an operating-system to the end of
// the message.
func (c *Action) Infof(msg string, args ...interface{}) {
// ::info <c.fields>::<msg, args>
fmt.Fprintf(c.w, msg, args...)
}

// Warningf prints a warning-level message. The arguments follow the standard
// Printf arguments.
func (c *Action) Warningf(msg string, args ...interface{}) {
// ::warning <c.fields>::<msg, args>
c.IssueCommand(&Command{
Name: warningCmd,
Message: fmt.Sprintf(msg, args...),
Properties: c.fields,
})
fmt.Fprintf(c.w, msg+EOF, args...)
}

// WithFieldsSlice includes the provided fields in log output. "f" must be a
Expand Down
82 changes: 47 additions & 35 deletions actions_test.go
Expand Up @@ -44,7 +44,7 @@ func TestNew(t *testing.T) {
Message: "bar",
})

if got, want := b.String(), "::foo::bar\n"; got != want {
if got, want := b.String(), "::foo::bar"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}

Expand All @@ -64,7 +64,7 @@ func TestNewWithWriter(t *testing.T) {
Message: "bar",
})

if got, want := b.String(), "::foo::bar\n"; got != want {
if got, want := b.String(), "::foo::bar"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -79,7 +79,7 @@ func TestAction_IssueCommand(t *testing.T) {
Message: "bar",
})

if got, want := b.String(), "::foo::bar\n"; got != want {
if got, want := b.String(), "::foo::bar"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestAction_IssueFileCommand(t *testing.T) {
t.Errorf("unable to read temp env file: %s", err)
}

if got, want := string(data), "bar\n"; got != want {
if got, want := string(data), "bar"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -130,7 +130,7 @@ func TestAction_AddMask(t *testing.T) {
a := New(WithWriter(&b))
a.AddMask("foobar")

if got, want := b.String(), "::add-mask::foobar\n"; got != want {
if got, want := b.String(), "::add-mask::foobar"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -142,7 +142,7 @@ func TestAction_AddMatcher(t *testing.T) {
a := New(WithWriter(&b))
a.AddMatcher("foobar.json")

if got, want := b.String(), "::add-matcher::foobar.json\n"; got != want {
if got, want := b.String(), "::add-matcher::foobar.json"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -154,7 +154,7 @@ func TestAction_RemoveMatcher(t *testing.T) {
a := New(WithWriter(&b))
a.RemoveMatcher("foobar")

if got, want := b.String(), "::remove-matcher owner=foobar::\n"; got != want {
if got, want := b.String(), "::remove-matcher owner=foobar::"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -170,7 +170,7 @@ func TestAction_AddPath(t *testing.T) {
a := New(WithWriter(&b), WithGetenv(fakeGetenvFunc))

a.AddPath("/custom/bin")
if got, want := b.String(), "::add-path::/custom/bin\n"; got != want {
if got, want := b.String(), "::add-path::/custom/bin"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}

Expand Down Expand Up @@ -203,7 +203,7 @@ func TestAction_AddPath(t *testing.T) {
t.Errorf("unable to read temp env file: %s", err)
}

if got, want := string(data), "/custom/bin\n"; got != want {
if got, want := string(data), "/custom/bin"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -215,7 +215,7 @@ func TestAction_SaveState(t *testing.T) {
a := New(WithWriter(&b))
a.SaveState("key", "value")

if got, want := b.String(), "::save-state name=key::value\n"; got != want {
if got, want := b.String(), "::save-state name=key::value"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -239,7 +239,7 @@ func TestAction_Group(t *testing.T) {
a := New(WithWriter(&b))
a.Group("mygroup")

if got, want := b.String(), "::group::mygroup\n"; got != want {
if got, want := b.String(), "::group::mygroup"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -251,7 +251,7 @@ func TestAction_EndGroup(t *testing.T) {
a := New(WithWriter(&b))
a.EndGroup()

if got, want := b.String(), "::endgroup::\n"; got != want {
if got, want := b.String(), "::endgroup::"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -265,8 +265,8 @@ func TestAction_SetEnv(t *testing.T) {
checks := []struct {
key, value, want string
}{
{"key", "value", "::set-env name=key::value\n"},
{"key", "this is 100% a special\n\r value!", "::set-env name=key::this is 100%25 a special%0A%0D value!\n"},
{"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 {
Expand Down Expand Up @@ -302,7 +302,7 @@ func TestAction_SetEnv(t *testing.T) {
t.Errorf("unable to read temp env file: %s", err)
}

want := "key<<_GitHubActionsFileCommandDelimeter_\nvalue\n_GitHubActionsFileCommandDelimeter_\n"
want := "key<<_GitHubActionsFileCommandDelimeter_" + EOF + "value" + EOF + "_GitHubActionsFileCommandDelimeter_" + EOF
if got := string(data); got != want {
t.Errorf("expected %q to be %q", got, want)
}
Expand All @@ -315,7 +315,7 @@ func TestAction_SetOutput(t *testing.T) {
a := New(WithWriter(&b))
a.SetOutput("key", "value")

if got, want := b.String(), "::set-output name=key::value\n"; got != want {
if got, want := b.String(), "::set-output name=key::value"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -327,7 +327,31 @@ func TestAction_Debugf(t *testing.T) {
a := New(WithWriter(&b))
a.Debugf("fail: %s", "thing")

if got, want := b.String(), "::debug::fail: thing\n"; got != want {
if got, want := b.String(), "::debug::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}

func TestAction_Noticef(t *testing.T) {
t.Parallel()

var b bytes.Buffer
a := New(WithWriter(&b))
a.Noticef("fail: %s", "thing")

if got, want := b.String(), "::notice::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}

func TestAction_Warningf(t *testing.T) {
t.Parallel()

var b bytes.Buffer
a := New(WithWriter(&b))
a.Warningf("fail: %s", "thing")

if got, want := b.String(), "::warning::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -339,7 +363,7 @@ func TestAction_Errorf(t *testing.T) {
a := New(WithWriter(&b))
a.Errorf("fail: %s", "thing")

if got, want := b.String(), "::error::fail: thing\n"; got != want {
if got, want := b.String(), "::error::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -355,7 +379,7 @@ func TestAction_Fatalf(t *testing.T) {
a := New(WithWriter(&b))
a.Fatalf("fail: %s", "bring")

if got, want := b.String(), "::error::fail: bring\n"; got != want {
if got, want := b.String(), "::error::fail: bring"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}

Expand All @@ -364,26 +388,14 @@ func TestAction_Fatalf(t *testing.T) {
}
}

func TestAction_Warningf(t *testing.T) {
t.Parallel()

var b bytes.Buffer
a := New(WithWriter(&b))
a.Warningf("fail: %s", "thing")

if got, want := b.String(), "::warning::fail: thing\n"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}

func TestAction_Infof(t *testing.T) {
t.Parallel()

var b bytes.Buffer
a := New(WithWriter(&b))
a.Infof("info: %s\n", "thing")
a.Infof("info: %s", "thing")

if got, want := b.String(), "info: thing\n"; got != want {
if got, want := b.String(), "info: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand All @@ -396,7 +408,7 @@ func TestAction_WithFieldsSlice(t *testing.T) {
a = a.WithFieldsSlice([]string{"line=100", "file=app.js"})
a.Debugf("fail: %s", "thing")

if got, want := b.String(), "::debug file=app.js,line=100::fail: thing\n"; got != want {
if got, want := b.String(), "::debug file=app.js,line=100::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand Down Expand Up @@ -425,7 +437,7 @@ func TestAction_WithFieldsMap(t *testing.T) {
a = a.WithFieldsMap(map[string]string{"line": "100", "file": "app.js"})
a.Debugf("fail: %s", "thing")

if got, want := b.String(), "::debug file=app.js,line=100::fail: thing\n"; got != want {
if got, want := b.String(), "::debug file=app.js,line=100::fail: thing"+EOF; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}
Expand Down
20 changes: 20 additions & 0 deletions 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"

0 comments on commit e5e41f9

Please sign in to comment.