Skip to content

Commit

Permalink
Update set outputs to latest specs
Browse files Browse the repository at this point in the history
  • Loading branch information
tjamet committed Sep 14, 2023
1 parent 07e4214 commit bf91240
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 24 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
id: gofmt
run: |
gofmt -w .
echo ::set-output name=formatted_files::$(git status -s -uno | wc -l)
echo "formatted_files=$(git status -s -uno | wc -l)" >> $GITHUB_OUTPUT
- name: push formatting fixes
if: steps.gofmt.outputs.formatted_files > 0
Expand Down Expand Up @@ -62,8 +62,18 @@ jobs:
run: go mod download

- name: run tests
id: tests
if: ${{ matrix.os }} != "windows-latest"
env:
# unauthenticated calls have lower limits than authenticated ones
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -v -race ${{ matrix.coverageArgs }} ./...

- name: run tests with inputs
if: ${{ matrix.os }} != "windows-latest"
env:
ACTIONS_OUTPUT_SET: "true"
# unauthenticated calls have lower limits than authenticated ones
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
my_output: "${{ steps.tests.outputs.my-output }}"
run: go test -v -run TestOutputTasks ./...
4 changes: 2 additions & 2 deletions core/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func IssueCommand(kind string, properties map[string]string, message string) {
// issueFileCommand implements stores the command in a file
// see https://github.com/actions/toolkit/pull/571/files#diff-9ce6eb99f5fb5529e795254801e03ae56d67d3d5fcbec635f91e9a8a61ad8b64R10
func issueFileCommand(command string, message string) error {
path, ok := os.LookupEnv("GITHUB_" + command)
path, ok := os.LookupEnv(command)
if ok {
fd, err := os.OpenFile(path, os.O_RDWR, 0)
if err != nil {
Expand All @@ -57,7 +57,7 @@ func issueFileCommand(command string, message string) error {
fmt.Fprintln(fd, message)
return nil
}
return fmt.Errorf("unable to find command file GITHUB_%s", command)
return fmt.Errorf("unable to find command file %s", command)
}

type command struct {
Expand Down
48 changes: 43 additions & 5 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,60 @@ package core

import (
"fmt"
"io"
"os"
"strings"
"sync"
)

const (
delimiter = "_GitHubActionsGoFileCommandDelimeter_"

// StatusFailed is returned by Status() in case this action has been marked as failed
StatusFailed = 1
// StatusSuccess is returned by Status() in case this action has not been marked as failed. By default an action is claimed as successful
StatusSuccess = 0

GitHubOutputFilePathEnvName = "GITHUB_OUTPUT"
GitHubStateFilePathEnvName = "GITHUB_STATE"
GitHubExportEnvFilePathEnvName = "GITHUB_ENV"
GitHubPathFilePathEnvName = "GITHUB_PATH"
)

var (
status = StatusSuccess
statusAccess = &sync.Mutex{}
lookupEnv = os.LookupEnv
open = func(path string, flag int, perm os.FileMode) (File, error) {
fd, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
return fd, nil
}
)

type File interface {
io.Reader
io.Writer
io.Closer
}

func formatOutput(name, value string) string {
return strings.Join(
[]string{
fmt.Sprintf("%s<<%s", name, delimiter),
value,
delimiter,
"",
},
EOF,
)
}

// ExportVariable sets the environment varaible name (for this action and future actions)
func ExportVariable(name, value string) {
const delimiter = "_GitHubActionsFileCommandDelimeter_"
if err := issueFileCommand("ENV", fmt.Sprintf("%s<<%s%s%s%s%s", name, delimiter, EOF, value, delimiter, EOF)); err != nil {
if err := issueFileCommand(GitHubExportEnvFilePathEnvName, formatOutput(name, value)); err != nil {
IssueCommand("set-env", map[string]string{"name": name}, value)
}
os.Setenv(name, value)
Expand All @@ -36,7 +68,7 @@ func SetSecret(secret string) {

// AddPath prepends inputPath to the PATH (for this action and future actions)
func AddPath(path string) {
if err := issueFileCommand("PATH", path); err != nil {
if err := issueFileCommand(GitHubPathFilePathEnvName, path); err != nil {
Issue("add-path", path)
}
// TODO js: process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`
Expand Down Expand Up @@ -65,7 +97,10 @@ func GetInputOrDefault(name, dflt string) string {

// SetOutput sets the value of an output for future actions
func SetOutput(name, value string) {
IssueCommand("set-output", map[string]string{"name": name}, value)
if err := issueFileCommand(GitHubOutputFilePathEnvName, formatOutput(name, value)); err != nil {
Warningf("did not find output file from environment variable %s, falling back to the deprecated command implementation", GitHubOutputFilePathEnvName)
IssueCommand("set-output", map[string]string{"name": name}, value)
}
}

// SetFailedf sets the action status to failed and sets an error message
Expand Down Expand Up @@ -142,7 +177,10 @@ func Group(name string, f func()) func() {

// SaveState saves state for current action, the state can only be retrieved by this action's post job execution.
func SaveState(name, value string) {
IssueCommand("save-state", map[string]string{"name": name}, value)
if err := issueFileCommand(GitHubStateFilePathEnvName, formatOutput(name, value)); err != nil {
Warningf("did not find state file from environment variable %s, falling back to the deprecated command implementation", GitHubStateFilePathEnvName)
IssueCommand("save-state", map[string]string{"name": name}, value)
}
}

// GetState gets the value of an state set by this action's main execution.
Expand Down
22 changes: 22 additions & 0 deletions core/core_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
package core

import (
"os"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
)

func TestFormatOutput(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("This test only runs on unix with \\n line separator")
}
assert.Equal(t, "my-name<<_GitHubActionsGoFileCommandDelimeter_\nmy-value\n_GitHubActionsGoFileCommandDelimeter_\n", formatOutput("my-name", "my-value"))
}

func TestOutputTasks(t *testing.T) {
if _, ok := os.LookupEnv("ACTIONS_OUTPUT_SET"); ok {
// state is only available in pre and post actions:
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions
// assert.Equal(t, "my-state-value", GetState("my-state"))
assert.Equal(t, "my-output-value", os.Getenv("my_output"))
assert.Equal(t, "my-env-value", os.Getenv("my_env"))
}
SaveState("my-state", "my-state-value")
ExportVariable("my_env", "my-env-value")
SetOutput("my-output", "my-output-value")
}

func TestGetInput(t *testing.T) {
t.Run("when environment variable is not net", func(t *testing.T) {
lookupEnv = func(name string) (string, bool) {
Expand Down
39 changes: 23 additions & 16 deletions github/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

"github.com/actions-go/toolkit/core"
"github.com/google/go-github/v42/github"
)

Expand Down Expand Up @@ -79,15 +80,18 @@ type ActionRepo struct {

// ActionContext contains details on the workflow execution
type ActionContext struct {
Payload WebhookPayload
EventName string
SHA string
Ref string
Workflow string
Action string
Actor string
Issue ActionIssue
Repo ActionRepo
Payload WebhookPayload
EventName string
SHA string
Ref string
Workflow string
Action string
Actor string
Issue ActionIssue
Repo ActionRepo
OutputFilePath string
StateFilePath string
ExportEnvFilePath string
}

func noGitHubEvent(path string) {
Expand All @@ -109,13 +113,16 @@ func ParseActionEnv() ActionContext {
Repo: getIndex(r, 1),
}
ctx := ActionContext{
EventName: os.Getenv("GITHUB_EVENT_NAME"),
SHA: os.Getenv("GITHUB_SHA"),
Ref: os.Getenv("GITHUB_REF"),
Workflow: os.Getenv("GITHUB_WORKFLOW"),
Action: os.Getenv("GITHUB_ACTION"),
Actor: os.Getenv("GITHUB_ACTOR"),
Repo: repo,
EventName: os.Getenv("GITHUB_EVENT_NAME"),
SHA: os.Getenv("GITHUB_SHA"),
Ref: os.Getenv("GITHUB_REF"),
Workflow: os.Getenv("GITHUB_WORKFLOW"),
Action: os.Getenv("GITHUB_ACTION"),
Actor: os.Getenv("GITHUB_ACTOR"),
OutputFilePath: os.Getenv(core.GitHubOutputFilePathEnvName),
StateFilePath: os.Getenv(core.GitHubStateFilePathEnvName),
ExportEnvFilePath: os.Getenv(core.GitHubExportEnvFilePathEnvName),
Repo: repo,
Issue: ActionIssue{
Owner: repo.Owner,
Repo: repo.Repo,
Expand Down

0 comments on commit bf91240

Please sign in to comment.