diff --git a/.github/workflows/workflow-executor.yaml b/.github/workflows/workflow-executor.yaml index d22364ee..c2f5203d 100644 --- a/.github/workflows/workflow-executor.yaml +++ b/.github/workflows/workflow-executor.yaml @@ -48,6 +48,22 @@ on: description: "Version to manually set for SDK generation" required: false type: string + environment: + description: | + Any required environment variables to set for the speakeasy run execution. Set as a string with each entry of the form "key=value". + + Example: + environment: | + SPECIFICATION_URL=https://api.example.com/swagger.json + Where .speakeasy/workflow.yaml contains: + sources: + my-source: + inputs: + - location: $SPECIFICATION_URL + + Note: This should not be used for secrets as inputs will *not* be masked. Something akin to $npm_token should be explicitly passed as a secret. + required: false + type: string dotnet_version: description: "The version of dotnet to use when compiling the C# SDK" required: false @@ -160,6 +176,7 @@ jobs: registry_tags: ${{ inputs.registry_tags }} push_code_samples_only: ${{ inputs.push_code_samples }} set_version: ${{ inputs.set_version }} + cli_environment_variables: ${{ inputs.environment }} - uses: ravsamhq/notify-slack-action@v2 if: always() && env.SLACK_WEBHOOK_URL != '' with: diff --git a/action.yml b/action.yml index 66eed275..5afac8aa 100644 --- a/action.yml +++ b/action.yml @@ -1,6 +1,6 @@ # action.yml -name: Speakeasy SDK Workflow Runner Action (alpha) -description: The Speakeasy Generation Action isto be run via the workflows provided in this repo and is not intended to be run directly. +name: Speakeasy SDK Workflow Runner Action +description: The Speakeasy Generation Action is to be run via the workflows provided in this repo and is not intended to be run directly. inputs: speakeasy_version: description: The version of the Speakeasy CLI to use or "latest" @@ -87,6 +87,10 @@ inputs: set_version: description: "Version to manually set for SDK generation" required: false + cli_environment_variables: + description: | + Extra environment variables to set for the speakeasy run execution. + required: false outputs: publish_python: description: "Whether the Python SDK will be published to PyPi" @@ -190,3 +194,4 @@ runs: - ${{ inputs.sources }} - ${{ inputs.code_samples }} - ${{ inputs.set_version }} + - ${{ inputs.cli_environment_variables }} diff --git a/go.mod b/go.mod index 92b352af..2de38de7 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.22.4 require ( github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.11.0 - github.com/google/go-github/v54 v54.0.0 github.com/google/go-github/v63 v63.0.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.6.0 + github.com/joho/godotenv v1.5.1 github.com/pb33f/libopenapi v0.14.0 github.com/speakeasy-api/git-diff-parser v0.0.3 github.com/speakeasy-api/sdk-gen-config v1.7.4 diff --git a/go.sum b/go.sum index c90f8e94..31c35c44 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v54 v54.0.0 h1:OZdXwow4EAD5jEo5qg+dGFH2DpkyZvVsAehjvJuUL/c= -github.com/google/go-github/v54 v54.0.0/go.mod h1:Sw1LXWHhXRZtzJ9LI5fyJg9wbQzYvFhW8W5P2yaAQ7s= github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE= github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -89,6 +87,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= diff --git a/internal/cli/run_test.go b/internal/cli/run_test.go index 72569df4..6f7b6373 100644 --- a/internal/cli/run_test.go +++ b/internal/cli/run_test.go @@ -1,6 +1,8 @@ package cli import ( + "github.com/speakeasy-api/sdk-generation-action/internal/environment" + "os" "testing" "github.com/stretchr/testify/assert" @@ -37,3 +39,63 @@ OpenAPI spec validation complete. 0 errors, 1 warnings, 0 hints`, }) } } + +func Test_environmentVariables(t *testing.T) { + tests := []struct { + name string + input string + expected []string + }{ + { + name: "simple case", + input: "FOO=bar", + expected: []string{"FOO=bar"}, + }, + { + name: "trimmed", + input: " FOO=bar", + expected: []string{"FOO=bar"}, + }, + { + name: "quotes", + input: `FOO="bar baz"`, + expected: []string{`FOO=bar baz`}, + }, + { + name: "multi input", + input: "FOO=bar\nBAR=qux", + expected: []string{"BAR=qux", "FOO=bar"}, + }, + { + name: "quoted input with newline", + input: " FOO=\"foo\nbar\n\"\nother_input=value", + expected: []string{"FOO=foo\nbar\n", "other_input=value"}, + }, + { + name: "windows newlines", + input: "FOO=bar\r\nBAR=qux", + expected: []string{"BAR=qux", "FOO=bar"}, + }, + { + name: "empty input", + input: "", + expected: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the environment variable + os.Setenv("INPUT_CLI_ENVIRONMENT_VARIABLES", tt.input) + + // Call the function + result := environment.SpeakeasyEnvVars() + + // Validate the result + assert.Equal(t, tt.expected, result, "The result should match the expected output") + + // Clean up + os.Unsetenv("INPUT_CLI_ENVIRONMENT_VARIABLES") + }) + } +} diff --git a/internal/cli/speakeasy.go b/internal/cli/speakeasy.go index 9aa9bc82..0231bc51 100644 --- a/internal/cli/speakeasy.go +++ b/internal/cli/speakeasy.go @@ -76,6 +76,7 @@ func Download(pinnedVersion string, g Git) (string, error) { func runSpeakeasyCommand(args ...string) (string, error) { baseDir := environment.GetBaseDir() + extraRunEnvVars := environment.SpeakeasyEnvVars() cmdPath := filepath.Join(baseDir, "bin", "speakeasy") @@ -84,6 +85,7 @@ func runSpeakeasyCommand(args ...string) (string, error) { cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "SPEAKEASY_RUN_LOCATION=action") cmd.Env = append(cmd.Env, "SPEAKEASY_ENVIRONMENT=github") + cmd.Env = append(cmd.Env, extraRunEnvVars...) output, err := cmd.CombinedOutput() if err != nil { diff --git a/internal/environment/environment.go b/internal/environment/environment.go index b70d21e7..0523d4d2 100644 --- a/internal/environment/environment.go +++ b/internal/environment/environment.go @@ -2,7 +2,9 @@ package environment import ( "fmt" + "github.com/joho/godotenv" "os" + "sort" "strconv" "strings" "time" @@ -54,6 +56,25 @@ func IsDebugMode() bool { return os.Getenv("INPUT_DEBUG") == "true" || os.Getenv("RUNNER_DEBUG") == "1" } +func SpeakeasyEnvVars() []string { + rawEnv := os.Getenv("INPUT_CLI_ENVIRONMENT_VARIABLES") + if len(rawEnv) == 0 { + return []string{} + } + src, err := godotenv.Unmarshal(rawEnv) + if err != nil { + fmt.Printf("Error: Failed to parse env vars from %s: %s\n", rawEnv, err) + return []string{} + } + + var result []string + for k, v := range src { + result = append(result, fmt.Sprintf("%s=%s", k, v)) + } + sort.Strings(result) + return result +} + func ForceGeneration() bool { return os.Getenv("INPUT_FORCE") == "true" }