diff --git a/atlasaction/action.go b/atlasaction/action.go index 957d98bd..873eff97 100644 --- a/atlasaction/action.go +++ b/atlasaction/action.go @@ -1240,26 +1240,35 @@ func RenderTemplate(name string, data any) (string, error) { return buf.String(), nil } -// toEnvVar converts the given string to an environment variable name. -func toEnvVar(s string) string { +// toEnvName converts the given string to an environment variable name. +func toEnvName(s string) string { return strings.ToUpper(strings.NewReplacer( " ", "_", "-", "_", "/", "_", ).Replace(s)) } -// writeBashEnv writes the given name and value to the bash environment file. -func writeBashEnv(path, name, value string) error { +// toInputVarName converts the given string to an input variable name. +func toInputVarName(input string) string { + return fmt.Sprintf("ATLAS_INPUT_%s", toEnvName(input)) +} + +// toOutputVar converts the given values to an output variable. +// The action and output are used to create the output variable name with the format: +// ATLAS_OUTPUT__="" +func toOutputVar(action, output, value string) string { + return fmt.Sprintf("ATLAS_OUTPUT_%s=%q", toEnvName(action+"_"+output), value) +} + +// fprintln writes the given values to the file using fmt.Fprintln. +func fprintln(name string, val ...any) error { // Write the output to a file. - f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + f, err := os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer f.Close() - _, err = fmt.Fprintf(f, "export %s=%q\n", name, value) - if err != nil { - return err - } - return nil + _, err = fmt.Fprintln(f, val...) + return err } // commentMarker creates a hidden marker to identify the comment as one created by this action. diff --git a/atlasaction/bitbucket.go b/atlasaction/bitbucket.go index 7026a91e..d2add496 100644 --- a/atlasaction/bitbucket.go +++ b/atlasaction/bitbucket.go @@ -80,7 +80,7 @@ func (a *bbPipe) GetTriggerContext(context.Context) (*TriggerContext, error) { // GetInput implements the Action interface. func (a *bbPipe) GetInput(name string) string { - return strings.TrimSpace(a.getenv("ATLAS_INPUT_" + toEnvVar(name))) + return strings.TrimSpace(a.getenv(toInputVarName(name))) } // SetOutput implements Action. @@ -104,11 +104,11 @@ func (a *bbPipe) SetOutput(name, value string) { a.Errorf("failed to create output directory %s: %v", dir, err) return } - cmd := a.getenv("ATLAS_ACTION_COMMAND") - err := writeBashEnv(filepath.Join(dir, "outputs.sh"), toEnvVar( - fmt.Sprintf("ATLAS_OUTPUT_%s_%s", cmd, name)), value) + outputs := filepath.Join(dir, "outputs.sh") + err := fprintln(outputs, + "export", toOutputVar(a.getenv("ATLAS_ACTION_COMMAND"), name, value)) if err != nil { - a.Errorf("failed to write output to file %s: %v", dir, err) + a.Errorf("failed to write output to file %s: %v", outputs, err) } } diff --git a/atlasaction/circleci_action.go b/atlasaction/circleci_action.go index ae3e5d55..5184474a 100644 --- a/atlasaction/circleci_action.go +++ b/atlasaction/circleci_action.go @@ -38,19 +38,22 @@ func (a *circleCIOrb) Getenv(key string) string { // GetInput implements the Action interface. func (a *circleCIOrb) GetInput(name string) string { - return strings.TrimSpace(a.getenv(toEnvVar("INPUT_" + name))) + v := a.getenv(toInputVarName(name)) + if v == "" { + // TODO: Remove this fallback once all the actions are updated. + v = a.getenv(toEnvName("INPUT_" + name)) + } + return strings.TrimSpace(v) } // SetOutput implements the Action interface. func (a *circleCIOrb) SetOutput(name, value string) { if bashEnv := a.getenv("BASH_ENV"); bashEnv != "" { - cmd := a.getenv("ATLAS_ACTION_COMMAND") - err := writeBashEnv(bashEnv, toEnvVar( - fmt.Sprintf("ATLAS_OUTPUT_%s_%s", cmd, name)), value) + err := fprintln(bashEnv, + "export", toOutputVar(a.getenv("ATLAS_ACTION_COMMAND"), name, value)) if err != nil { - a.Fatalf("failed to write env to file %s: %v", bashEnv, err) + a.Fatalf("failed to write output to file %s: %v", bashEnv, err) } - return } } diff --git a/atlasaction/circleci_action_test.go b/atlasaction/circleci_action_test.go index 0775993b..a7c0efc6 100644 --- a/atlasaction/circleci_action_test.go +++ b/atlasaction/circleci_action_test.go @@ -60,7 +60,7 @@ func Test_circleCIOrb_GetTriggerContext(t *testing.T) { func TestCircleCI(t *testing.T) { var ( actions = "actions" - output = filepath.Join(actions, "output.txt") + output = filepath.Join(actions, "output.sh") ) wd, err := os.Getwd() require.NoError(t, err) @@ -75,7 +75,7 @@ func TestCircleCI(t *testing.T) { e.Setenv("CIRCLECI", "true") e.Setenv("CIRCLE_PROJECT_REPONAME", "atlas-orb") e.Setenv("CIRCLE_SHA1", "1234567890") - e.Setenv("BASH_ENV", filepath.Join(dir, "output.txt")) + e.Setenv("BASH_ENV", filepath.Join(dir, "output.sh")) return nil }, Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ diff --git a/atlasaction/gitlab_ci.go b/atlasaction/gitlab_ci.go index 78dd4c5b..d490cd9d 100644 --- a/atlasaction/gitlab_ci.go +++ b/atlasaction/gitlab_ci.go @@ -8,7 +8,6 @@ import ( "context" "fmt" "io" - "os" "slices" "strconv" "strings" @@ -40,17 +39,15 @@ func (a *gitlabCI) Getenv(key string) string { // GetInput implements the Action interface. func (a *gitlabCI) GetInput(name string) string { - return strings.TrimSpace(a.getenv(toEnvVar("ATLAS_INPUT_" + name))) + return strings.TrimSpace(a.getenv(toInputVarName(name))) } // SetOutput implements the Action interface. func (a *gitlabCI) SetOutput(name, value string) { - f, err := os.OpenFile(".env", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + err := fprintln(".env", toOutputVar(a.getenv("ATLAS_ACTION_COMMAND"), name, value)) if err != nil { - return + a.Errorf("failed to write output to file .env: %v", err) } - defer f.Close() - fmt.Fprintf(f, "%s=%s\n", name, value) } // GetTriggerContext implements the Action interface. diff --git a/atlasaction/testdata/circleci/migrate-lint.txtar b/atlasaction/testdata/circleci/migrate-lint.txtar index a6041936..11abcb39 100644 --- a/atlasaction/testdata/circleci/migrate-lint.txtar +++ b/atlasaction/testdata/circleci/migrate-lint.txtar @@ -1,26 +1,26 @@ # Mock the atlas command outputs env ATLAS_PATH=$MOCK_ATLAS TEST_BATCH=./migrate-lint # Setup the action input variables -env INPUT_CONFIG=file://testdata/config/atlas.hcl -env INPUT_ENV=test -env INPUT_DIR_NAME=pupisu -env INPUT_TAG=staging -env INPUT_VARS='{"var1":"value1","var2":"value2"}' -env INPUT_DIR=file://testdata/migrations -env INPUT_DEV_URL=sqlite://file?mode=memory -env INPUT_RUN=example +env ATLAS_INPUT_CONFIG=file://testdata/config/atlas.hcl +env ATLAS_INPUT_ENV=test +env ATLAS_INPUT_DIR_NAME=pupisu +env ATLAS_INPUT_TAG=staging +env ATLAS_INPUT_VARS='{"var1":"value1","var2":"value2"}' +env ATLAS_INPUT_DIR=file://testdata/migrations +env ATLAS_INPUT_DEV_URL=sqlite://file?mode=memory +env ATLAS_INPUT_RUN=example # The action's output should append the existing outputs -cp output-pre.txt actions/output.txt +cp output-pre.sh actions/output.sh atlas-action --action=migrate/lint -output output.txt +output output.sh -- migrate-lint/1/args -- migrate lint -w --context {"repo":"atlas-orb","path":"file://testdata/migrations","commit":"1234567890"} --env test --config file://testdata/config/atlas.hcl --dev-url sqlite://file?mode=memory --dir file://testdata/migrations --base atlas://pupisu?tag=staging --var var1=value1 --var var2=value2 --format {{ json . }} -- migrate-lint/1/stdout -- {"URL":"https://migration-lint-report-url"} --- output-pre.txt -- +-- output-pre.sh -- export FOO=bar --- output.txt -- +-- output.sh -- export FOO=bar export ATLAS_OUTPUT_MIGRATE_LINT_REPORT_URL="https://migration-lint-report-url" diff --git a/atlasaction/testdata/gitlab/schema-plan-approve.txtar b/atlasaction/testdata/gitlab/schema-plan-approve.txtar index 7741ce1c..7d6318a4 100644 --- a/atlasaction/testdata/gitlab/schema-plan-approve.txtar +++ b/atlasaction/testdata/gitlab/schema-plan-approve.txtar @@ -7,7 +7,7 @@ stdout 'No schema plan found' # One pending plan. atlas-action --action=schema/plan/approve -output expected-output.env +output .env.expected-output # Multiple pending plans. ! atlas-action --action=schema/plan/approve @@ -15,12 +15,12 @@ stdout 'No plan URL provided, searching for the pending plan' stdout 'Found schema plan: atlas://plans/1234' stdout 'Found schema plan: atlas://plans/5678' stdout 'found multiple schema plans, please approve or delete the existing plans' -output expected-output.env +output .env.expected-output --- expected-output.env -- -link=https://test.atlasgo.cloud/schemas/123/plans/456 -plan=atlas://plans/1234 -status= +-- .env.expected-output -- +ATLAS_OUTPUT_SCHEMA_PLAN_APPROVE_LINK="https://test.atlasgo.cloud/schemas/123/plans/456" +ATLAS_OUTPUT_SCHEMA_PLAN_APPROVE_PLAN="atlas://plans/1234" +ATLAS_OUTPUT_SCHEMA_PLAN_APPROVE_STATUS="" -- schema-plan-approve/1/args -- schema plan list --format {{ json . }} --context {"scmType":"GITLAB"} --pending --auto-approve diff --git a/atlasaction/testdata/gitlab/schema-plan.txtar b/atlasaction/testdata/gitlab/schema-plan.txtar index 8d72fad2..fb8fdc81 100644 --- a/atlasaction/testdata/gitlab/schema-plan.txtar +++ b/atlasaction/testdata/gitlab/schema-plan.txtar @@ -15,12 +15,12 @@ atlas-action --action=schema/plan stdout 'Schema plan does not exist, creating a new one with name "pr-1-3RRRcLHF"' cmp comments-expected/1 comments/1 -output expected-output.env +output .env.expected-output --- expected-output.env -- -link=http://test.atlasgo.cloud/schemas/141733920769/plans/210453397511 -plan=atlas://app/plans/20241010143904 -status=PENDING +-- .env.expected-output -- +ATLAS_OUTPUT_SCHEMA_PLAN_LINK="http://test.atlasgo.cloud/schemas/141733920769/plans/210453397511" +ATLAS_OUTPUT_SCHEMA_PLAN_PLAN="atlas://app/plans/20241010143904" +ATLAS_OUTPUT_SCHEMA_PLAN_STATUS="PENDING" -- schema-plan/1/args -- schema plan list --format {{ json . }} --context {"repo":"my-project","branch":"test-branch","commit":"123","url":"https://gitlab.com/projects/my-project/merge_requests/1","username":"user","userID":"123","scmType":"GITLAB"} --pending --auto-approve