From 5f5410977da7841094ea38e3c28047919b6f029c Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 11:43:28 -0600 Subject: [PATCH 01/23] add conditionals based on flavor --- .github/workflows/test.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 516b80097..f0869a31e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -286,6 +286,7 @@ jobs: path: /usr/local/bin - name: Set execute permissions on atmos + if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: chmod +x /usr/local/bin/atmos - name: Check out code into the Go module directory @@ -296,11 +297,18 @@ jobs: terraform_version: ${{ env.TERRAFORM_VERSION }} terraform_wrapper: false - - name: Run tests for ${{ matrix.demo-folder }} + - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} + working-directory: examples/${{ matrix.demo-folder }} + if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: | - cd examples/${{ matrix.demo-folder }} atmos test + - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} + working-directory: examples/${{ matrix.demo-folder }} + if: matrix.flavor.target == 'windows' + run: | + atmos.exe test + # run other demo tests lint: name: "[lint] ${{ matrix.demo-folder }}" From f548f8b08c6fcd4177ff01d9854780ae724bb308 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 11:58:19 -0600 Subject: [PATCH 02/23] add conditionals based on flavor --- .github/workflows/test.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0869a31e..c643d4a7f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -279,13 +279,17 @@ jobs: timeout-minutes: 20 steps: - - name: Download build artifacts + - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 with: name: build-artifacts-${{ matrix.flavor.target }} - path: /usr/local/bin + path: ${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }} - - name: Set execute permissions on atmos + - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} + run: | + echo "${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }}" >> $GITHUB_PATH + + - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: chmod +x /usr/local/bin/atmos From 26d7fcb0657d08e6a4975eea9f1698711ed654fb Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 12:01:30 -0600 Subject: [PATCH 03/23] add conditionals based on flavor --- .github/workflows/test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c643d4a7f..329c5be2c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -279,15 +279,19 @@ jobs: timeout-minutes: 20 steps: + - name: Determine build artifacts path + id: determine-path + run: echo "artifact_path=${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }}" >> $GITHUB_ENV + - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 with: name: build-artifacts-${{ matrix.flavor.target }} - path: ${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }} + path: ${{ env.artifact_path }} - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} run: | - echo "${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }}" >> $GITHUB_PATH + echo "${{ env.artifact_path }}" >> $GITHUB_PATH - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' From c5511c365a0ce7d626da9fdcee84fb7657bad456 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 12:17:45 -0600 Subject: [PATCH 04/23] add conditionals based on flavor --- .github/workflows/test.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 329c5be2c..e9c325075 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -279,26 +279,23 @@ jobs: timeout-minutes: 20 steps: - - name: Determine build artifacts path - id: determine-path - run: echo "artifact_path=${{ matrix.flavor.target == 'windows' && 'D:\\a\\_temp' || '/usr/local/bin' }}" >> $GITHUB_ENV + - name: Check out code into the Go module directory + uses: actions/checkout@v4 - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 with: name: build-artifacts-${{ matrix.flavor.target }} - path: ${{ env.artifact_path }} + path: ${{ github.workspace }} - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} run: | - echo "${{ env.artifact_path }}" >> $GITHUB_PATH + ls "${{ github.workspace }}" + echo "${{ github.workspace }}" >> $GITHUB_PATH - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' - run: chmod +x /usr/local/bin/atmos - - - name: Check out code into the Go module directory - uses: actions/checkout@v4 + run: chmod +x "${{ github.workspace }}/atmos" - uses: hashicorp/setup-terraform@v3 with: From 3ccedca3cd7ff9e98727cdc2774389ad9790ca05 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 12:21:42 -0600 Subject: [PATCH 05/23] add conditionals based on flavor --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e9c325075..51bf944ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -282,6 +282,10 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v4 + - name: Add GNU tar to PATH (significantly faster than windows tar) + if: matrix.flavor.target == 'windows' + run: echo "C:\Program Files\Git\usr\bin" >> $GITHUB_PATH + - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 with: From 71fea78e07b6ec6e6e78453271a3d63428a6753b Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Fri, 20 Dec 2024 12:24:23 -0600 Subject: [PATCH 06/23] add conditionals based on flavor --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51bf944ea..d80a4209e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,6 +40,10 @@ jobs: - name: Build run: echo "Building on ${{ matrix.os }}" + - name: Add GNU tar to PATH (significantly faster than windows tar) + if: matrix.flavor.target == 'windows' + run: echo "C:\Program Files\Git\usr\bin" >> $GITHUB_PATH + - name: Check out code into the Go module directory uses: actions/checkout@v4 From 1995290469b4a499eeb080a704797eb864818108 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 20:33:18 -0600 Subject: [PATCH 07/23] add tests to verify we can run atmos correctly --- tests/cli_test.go | 184 ++++++++++++++++++++++++++++++++++++++++++ tests/test_cases.yaml | 142 ++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 tests/cli_test.go create mode 100644 tests/test_cases.yaml diff --git a/tests/cli_test.go b/tests/cli_test.go new file mode 100644 index 000000000..0e0dd2e31 --- /dev/null +++ b/tests/cli_test.go @@ -0,0 +1,184 @@ +package tests + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "testing" + + "gopkg.in/yaml.v3" +) + +type Expectation struct { + Stdout []string `yaml:"stdout"` + Stderr []string `yaml:"stderr"` + ExitCode int `yaml:"exit_code"` + FileExists []string `yaml:"file_exists"` + FileContains map[string][]string `yaml:"file_contains"` +} + +type TestCase struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Enabled bool `yaml:"enabled"` + Workdir string `yaml:"workdir"` + Command string `yaml:"command"` + Args []string `yaml:"args"` + Env map[string]string `yaml:"env"` + Expect Expectation `yaml:"expect"` +} + +type TestSuite struct { + Tests []TestCase `yaml:"tests"` +} + +func loadTestSuite(filePath string) (*TestSuite, error) { + data, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, err + } + + var suite TestSuite + err = yaml.Unmarshal(data, &suite) + if err != nil { + return nil, err + } + + return &suite, nil +} + +func TestCLICommands(t *testing.T) { + // Capture the starting working directory + startingDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get the current working directory: %v", err) + } + + testSuite, err := loadTestSuite("test_cases.yaml") + if err != nil { + t.Fatalf("Failed to load test suite: %v", err) + } + + for _, tc := range testSuite.Tests { + + if !tc.Enabled { + t.Logf("Skipping disabled test: %s", tc.Name) + continue + } + + t.Run(tc.Name, func(t *testing.T) { + defer func() { + // Change back to the original working directory after the test + if err := os.Chdir(startingDir); err != nil { + t.Fatalf("Failed to change back to the starting directory: %v", err) + } + }() + + // Change to the specified working directory + if tc.Workdir != "" { + err := os.Chdir(tc.Workdir) + if err != nil { + t.Fatalf("Failed to change directory to %q: %v", tc.Workdir, err) + } + } + + // Prepare the command + cmd := exec.Command(tc.Command, tc.Args...) + + // Set environment variables + envVars := os.Environ() + for key, value := range tc.Env { + envVars = append(envVars, fmt.Sprintf("%s=%s", key, value)) + } + cmd.Env = envVars + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // Run the command + err := cmd.Run() + + // Validate exit code + exitCode := 0 + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitCode = exitErr.ExitCode() + } + } + if exitCode != tc.Expect.ExitCode { + t.Errorf("Description: %s", tc.Description) + t.Errorf("Reason: Expected exit code %d, got %d", tc.Expect.ExitCode, exitCode) + } + + // Validate stdout + if !verifyOutput(stdout.String(), tc.Expect.Stdout) { + t.Errorf("Description: %s", tc.Description) + t.Errorf("Reason: Stdout did not match expected patterns.") + t.Errorf("Output: %q", stdout.String()) + } + + // Validate stderr + if !verifyOutput(stderr.String(), tc.Expect.Stderr) { + t.Errorf("Description: %s", tc.Description) + t.Errorf("Reason: Stderr did not match expected patterns.") + t.Errorf("Output: %q", stderr.String()) + } + + // Validate file existence + if !verifyFileExists(tc.Expect.FileExists) { + t.Errorf("Description: %s", tc.Description) + } + + // Validate file contents + if !verifyFileContains(tc.Expect.FileContains) { + t.Errorf("Description: %s", tc.Description) + } + }) + } +} + +func verifyOutput(output string, patterns []string) bool { + for _, pattern := range patterns { + re, err := regexp.Compile(pattern) + if err != nil { + return false + } + if !re.MatchString(output) { + return false + } + } + return true +} + +func verifyFileExists(files []string) bool { + for _, file := range files { + if _, err := os.Stat(file); errors.Is(err, os.ErrNotExist) { + return false + } + } + return true +} + +func verifyFileContains(filePatterns map[string][]string) bool { + for file, patterns := range filePatterns { + content, err := ioutil.ReadFile(file) + if err != nil { + return false + } + for _, pattern := range patterns { + re, err := regexp.Compile(pattern) + if err != nil { + return false + } + if !re.Match(content) { + return false + } + } + } + return true +} diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml new file mode 100644 index 000000000..6ec917cde --- /dev/null +++ b/tests/test_cases.yaml @@ -0,0 +1,142 @@ +tests: + - name: "atmos" + enabled: true + description: "Verify atmos CLI reports missing stacks directory." + workdir: "../" + command: "atmos" + expect: + stdout: + - "atmos.yaml CLI config file specifies the directory for Atmos stacks as stacks," + - "but the directory does not exist." + exit: 0 + + - name: atmos --help + enabled: true + description: "Ensure atmos CLI help command lists available commands." + workdir: "../examples/demo-stacks" + command: "atmos" + args: + - "--help" + expect: + stdout: + - "Available Commands:" + - "\\batlantis\\b" + - "\\baws\\b" + - "\\bcompletion\\b" + - "\\bdescribe\\b" + - "\\bdocs\\b" + - "\\bhelmfile\\b" + - "\\bhelp\\b" + - "\\blist\\b" + - "\\bpro\\b" + - "\\bterraform\\b" + - "\\bvalidate\\b" + - "\\bvendor\\b" + - "\\bversion\\b" + - "\\bworkflow\\b" + - "Flags:" + - "for more information about a command" + exit_code: 0 + + - name: atmos version + enabled: true + description: "Check that atmos version command outputs version details." + workdir: "../examples/demo-stacks" + command: "atmos" + args: + - "version" + expect: + stdout: + - '👽 Atmos \d+\.\d+\.\d+ on [a-z]+/[a-z0-9]+' + stderr: [] + exit_code: 0 + + - name: atmos version --check + enabled: true + description: "Verify atmos version --check command functions correctly." + workdir: "../examples/demo-stacks" + command: "atmos" + args: + - "version" + - "--check" + expect: + stdout: + - '👽 Atmos \d+\.\d+\.\d+ on [a-z]+/[a-z0-9]+' + stderr: [] + exit_code: 0 + + - name: atmos docs + enabled: false + description: "Ensure atmos docs command executes without errors." + workdir: "../" + command: "atmos" + args: + - "docs" + expect: + exit_code: 0 + + - name: atmos docs myapp + enabled: true + description: "Validate atmos docs command outputs documentation for a specific component." + workdir: "../examples/demo-stacks/" + command: "atmos" + args: + - "docs" + - "myapp" + expect: + stdout: + - "Example Terraform Weather Component" + exit_code: 0 + + - name: atmos non-existent + enabled: true + description: "Ensure atmos CLI returns an error for a non-existent command." + workdir: "../" + command: "atmos" + args: + - "non-existent" + expect: + stderr: + - 'unknown command "non-existent" for "atmos"' + exit_code: 1 + + - name: atmos terraform non-existent + enabled: false + description: "Ensure atmos CLI returns an error for a non-existent command." + workdir: "../" + command: "atmos" + args: + - "terraform" + - "non-existent" + expect: + stderr: + - 'unknown command "non-existent" for "atmos"' + exit_code: 1 + + - name: atmos describe config -f yaml + enabled: true + description: "Ensure atmos CLI outputs the Atmos configuration in YAML." + workdir: "../examples/demo-stacks/" + command: "atmos" + args: + - "describe" + - "config" + - "-f" + - "yaml" + expect: + stdout: + - 'append_user_agent: Atmos/\d+\.\d+\.\d+ \(Cloud Posse; \+https:\/\/atmos\.tools\)' + exit_code: 0 + + - name: atmos describe config + enabled: true + description: "Ensure atmos CLI outputs the Atmos configuration in JSON." + workdir: "../examples/demo-stacks/" + command: "atmos" + args: + - "describe" + - "config" + expect: + stdout: + - '"append_user_agent": "Atmos/\d+\.\d+\.\d+ \(Cloud Posse; \+https:\/\/atmos\.tools\)"' + exit_code: 0 From 4ce6a598b6cfd22b0dca90aa424ac4c9a5956f7c Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Sun, 22 Dec 2024 20:46:22 -0600 Subject: [PATCH 08/23] Update .github/workflows/test.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d80a4209e..02cc27a18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -299,7 +299,7 @@ jobs: - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} run: | ls "${{ github.workspace }}" - echo "${{ github.workspace }}" >> $GITHUB_PATH + echo "${{ github.workspace }}" >> "$GITHUB_PATH" - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' From f02bb978ac885cc506a605554ea5648f1c742802 Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Sun, 22 Dec 2024 20:46:32 -0600 Subject: [PATCH 09/23] Update .github/workflows/test.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02cc27a18..550529acc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,8 +41,8 @@ jobs: run: echo "Building on ${{ matrix.os }}" - name: Add GNU tar to PATH (significantly faster than windows tar) - if: matrix.flavor.target == 'windows' - run: echo "C:\Program Files\Git\usr\bin" >> $GITHUB_PATH + if: matrix.target == 'windows' + run: echo "C:\Program Files\Git\usr\bin" >> "$GITHUB_PATH" - name: Check out code into the Go module directory uses: actions/checkout@v4 From 89b8ca8e3ea47452c7903e0bcd7dc6da8b69c5a1 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 20:47:49 -0600 Subject: [PATCH 10/23] fix exit code --- tests/test_cases.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 6ec917cde..68edfb10d 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -8,7 +8,7 @@ tests: stdout: - "atmos.yaml CLI config file specifies the directory for Atmos stacks as stacks," - "but the directory does not exist." - exit: 0 + exit_code: 0 - name: atmos --help enabled: true From a0b7a140cec0e8543e658fb90d148355fc4417b7 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 20:58:34 -0600 Subject: [PATCH 11/23] test atmos integrity on windows --- .github/workflows/test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 550529acc..5c90b76a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -316,11 +316,18 @@ jobs: run: | atmos test + - name: Check atmos.exe integrity + if: matrix.flavor.target == 'windows' + run: | + echo $PATH + dir "${{ github.workspace }}" + Get-Command "${{ github.workspace }}\atmos.exe" + - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} if: matrix.flavor.target == 'windows' run: | - atmos.exe test + "${{ github.workspace }}\atmos.exe" test # run other demo tests lint: From d150c3d5548be81b8caf6c8c6b513ca2981de080 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 21:27:21 -0600 Subject: [PATCH 12/23] test atmos integrity on windows --- .github/workflows/test.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c90b76a8..b433820bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - name: Add GNU tar to PATH (significantly faster than windows tar) if: matrix.target == 'windows' - run: echo "C:\Program Files\Git\usr\bin" >> "$GITHUB_PATH" + run: echo "C:\Program Files\Git\usr\bin" >> "${{env.GITHUB_PATH}}" - name: Check out code into the Go module directory uses: actions/checkout@v4 @@ -288,7 +288,7 @@ jobs: - name: Add GNU tar to PATH (significantly faster than windows tar) if: matrix.flavor.target == 'windows' - run: echo "C:\Program Files\Git\usr\bin" >> $GITHUB_PATH + run: echo "C:\Program Files\Git\usr\bin" >> "${{env.GITHUB_PATH}}" - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 @@ -299,7 +299,7 @@ jobs: - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} run: | ls "${{ github.workspace }}" - echo "${{ github.workspace }}" >> "$GITHUB_PATH" + echo "${{ github.workspace }}" >> "${{env.GITHUB_PATH}}" - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' @@ -318,10 +318,13 @@ jobs: - name: Check atmos.exe integrity if: matrix.flavor.target == 'windows' + shell: pwsh run: | - echo $PATH - dir "${{ github.workspace }}" + Write-Output "PATH=$Env:PATH" + Write-Output "PATHEXT=$Env:PATHEXT" + Get-ChildItem "${{ github.workspace }}" Get-Command "${{ github.workspace }}\atmos.exe" + .\atmos.exe version - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} From 14c893d3bd27888602f40e7f7be1d42888296454 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 21:42:07 -0600 Subject: [PATCH 13/23] test atmos integrity on windows --- .github/workflows/test.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b433820bd..8163d4b45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - name: Add GNU tar to PATH (significantly faster than windows tar) if: matrix.target == 'windows' - run: echo "C:\Program Files\Git\usr\bin" >> "${{env.GITHUB_PATH}}" + run: echo "C:\Program Files\Git\usr\bin" >> $Env:GITHUB_PATH - name: Check out code into the Go module directory uses: actions/checkout@v4 @@ -288,7 +288,7 @@ jobs: - name: Add GNU tar to PATH (significantly faster than windows tar) if: matrix.flavor.target == 'windows' - run: echo "C:\Program Files\Git\usr\bin" >> "${{env.GITHUB_PATH}}" + run: echo "C:\Program Files\Git\usr\bin" >> $Env:GITHUB_PATH - name: Download build artifacts for ${{ matrix.flavor.target }} uses: actions/download-artifact@v4 @@ -297,13 +297,15 @@ jobs: path: ${{ github.workspace }} - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} + if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: | - ls "${{ github.workspace }}" - echo "${{ github.workspace }}" >> "${{env.GITHUB_PATH}}" + echo "${{ github.workspace }}" >> $GITHUB_PATH + chmod +x "${{ github.workspace }}/atmos - - name: Set execute permissions on atmos for ${{ matrix.flavor.target }} - if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' - run: chmod +x "${{ github.workspace }}/atmos" + - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} + if: matrix.flavor.target == 'windows' + run: | + echo "${{ github.workspace }}" >> $Env:GITHUB_PATH - uses: hashicorp/setup-terraform@v3 with: @@ -329,8 +331,9 @@ jobs: - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} if: matrix.flavor.target == 'windows' + shell: pwsh run: | - "${{ github.workspace }}\atmos.exe" test + ${{ github.workspace }}\atmos.exe test # run other demo tests lint: From a73e8c4b01b19087203cefe2c66f29e49eb6f458 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 21:48:14 -0600 Subject: [PATCH 14/23] test atmos integrity on windows --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8163d4b45..db065d678 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -300,7 +300,7 @@ jobs: if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: | echo "${{ github.workspace }}" >> $GITHUB_PATH - chmod +x "${{ github.workspace }}/atmos + chmod +x "${{ github.workspace }}/atmos" - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} if: matrix.flavor.target == 'windows' From 854957740efdd15897c1e2a3d20be579c6d3d86b Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 23:04:42 -0600 Subject: [PATCH 15/23] fix deprecation --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db065d678..1b6245cf1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -386,7 +386,7 @@ jobs: --minimum-failure-severity=warning --recursive --config=${{ github.workspace }}/examples/.tflint.hcl - fail_on_error: true + fail_level: error # run other demo tests validate: From 0cf97f17850aab9d3bd785f97a5e7ae7500914f6 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 23:11:54 -0600 Subject: [PATCH 16/23] try to use search paths on windows --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b6245cf1..eda1ba4eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -326,14 +326,14 @@ jobs: Write-Output "PATHEXT=$Env:PATHEXT" Get-ChildItem "${{ github.workspace }}" Get-Command "${{ github.workspace }}\atmos.exe" - .\atmos.exe version + atmos version - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} if: matrix.flavor.target == 'windows' shell: pwsh run: | - ${{ github.workspace }}\atmos.exe test + atmos test # run other demo tests lint: From d6d994f829dd76d876711a4792ab38971c549548 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 23:21:56 -0600 Subject: [PATCH 17/23] download atmos artifact for acceptance tests --- .github/workflows/test.yml | 44 ++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eda1ba4eb..a94642034 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,6 +92,42 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v4 + - name: Add GNU tar to PATH (significantly faster than windows tar) + if: matrix.flavor.target == 'windows' + run: echo "C:\Program Files\Git\usr\bin" >> $Env:GITHUB_PATH + + - name: Download build artifacts for ${{ matrix.flavor.target }} + uses: actions/download-artifact@v4 + with: + name: build-artifacts-${{ matrix.flavor.target }} + path: ${{ github.workspace }} + + - name: Add build artifacts directory to PATH for linux or macos + if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' + run: | + echo "${{ github.workspace }}" >> $GITHUB_PATH + chmod +x "${{ github.workspace }}/atmos" + + - name: Add build artifacts directory to PATH for windows + if: matrix.flavor.target == 'windows' + run: | + echo "${{ github.workspace }}" >> $Env:GITHUB_PATH + + - uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ env.TERRAFORM_VERSION }} + terraform_wrapper: false + + - name: Check atmos.exe integrity + if: matrix.flavor.target == 'windows' + shell: pwsh + run: | + Write-Output "PATH=$Env:PATH" + Write-Output "PATHEXT=$Env:PATHEXT" + Get-ChildItem "${{ github.workspace }}" + Get-Command "${{ github.workspace }}\atmos.exe" + atmos version + - name: Set up Go uses: actions/setup-go@v5 with: @@ -296,13 +332,13 @@ jobs: name: build-artifacts-${{ matrix.flavor.target }} path: ${{ github.workspace }} - - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} + - name: Add build artifacts directory to PATH for linux or macos if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: | echo "${{ github.workspace }}" >> $GITHUB_PATH chmod +x "${{ github.workspace }}/atmos" - - name: Add build artifacts directory to PATH for ${{ matrix.flavor.target }} + - name: Add build artifacts directory to PATH for windows if: matrix.flavor.target == 'windows' run: | echo "${{ github.workspace }}" >> $Env:GITHUB_PATH @@ -312,7 +348,7 @@ jobs: terraform_version: ${{ env.TERRAFORM_VERSION }} terraform_wrapper: false - - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} + - name: Run tests in ${{ matrix.demo-folder }} for ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} if: matrix.flavor.target == 'linux' || matrix.flavor.target == 'macos' run: | @@ -328,7 +364,7 @@ jobs: Get-Command "${{ github.workspace }}\atmos.exe" atmos version - - name: Run tests for ${{ matrix.demo-folder }} with ${{ matrix.flavor.target }} + - name: Run tests in ${{ matrix.demo-folder }} for ${{ matrix.flavor.target }} working-directory: examples/${{ matrix.demo-folder }} if: matrix.flavor.target == 'windows' shell: pwsh From 7c1b7d6dc128f7b9f61a23808fd10af412cc47af Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 23:28:49 -0600 Subject: [PATCH 18/23] download atmos artifact for acceptance tests --- .github/workflows/test.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a94642034..1cdcc0360 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,13 +79,10 @@ jobs: strategy: fail-fast: false matrix: - include: - - os: ubuntu-latest - target: linux - - os: windows-latest - target: windows - - os: macos-latest - target: macos + flavor: + - { os: ubuntu-latest, target: linux } + - { os: windows-latest, target: windows } + - { os: macos-latest, target: macos } timeout-minutes: 15 runs-on: ${{ matrix.os }} steps: From f256196692e8398af53291107059a9ddaaa92728 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Sun, 22 Dec 2024 23:34:03 -0600 Subject: [PATCH 19/23] download atmos artifact for acceptance tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1cdcc0360..876a208fd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,7 +84,7 @@ jobs: - { os: windows-latest, target: windows } - { os: macos-latest, target: macos } timeout-minutes: 15 - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.flavor.os }} steps: - name: Check out code into the Go module directory uses: actions/checkout@v4 From 41ef594145e1093fa5cb9e9e084123d7801a6b80 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Mon, 23 Dec 2024 07:55:30 -0600 Subject: [PATCH 20/23] use the atmos that was built --- tests/cli_test.go | 85 +++++++++++++++++++++++++++++++++---------- tests/test_cases.yaml | 20 ++++++++-- 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/tests/cli_test.go b/tests/cli_test.go index 0e0dd2e31..0ae6a3b76 100644 --- a/tests/cli_test.go +++ b/tests/cli_test.go @@ -7,7 +7,9 @@ import ( "io/ioutil" "os" "os/exec" + "path/filepath" // For resolving absolute paths "regexp" + "strings" "testing" "gopkg.in/yaml.v3" @@ -51,6 +53,38 @@ func loadTestSuite(filePath string) (*TestSuite, error) { return &suite, nil } +func prependPath(envVars []string, dirs ...string) []string { + // Resolve absolute paths for the directories + var resolvedPaths []string + for _, dir := range dirs { + absPath, err := filepath.Abs(dir) + if err != nil { + fmt.Printf("Failed to resolve absolute path for %q: %v\n", dir, err) + continue + } + resolvedPaths = append(resolvedPaths, absPath) + } + + // Create the new PATH variable with precedence order + currentPath := os.Getenv("PATH") + newPath := fmt.Sprintf("%s%c%s", + strings.Join(resolvedPaths, string(os.PathListSeparator)), + os.PathListSeparator, + currentPath, + ) + + // Replace the PATH in envVars + for i, env := range envVars { + if len(env) > 5 && env[:5] == "PATH=" { + envVars[i] = fmt.Sprintf("PATH=%s", newPath) + return envVars + } + } + + // If PATH is not found, add it + return append(envVars, fmt.Sprintf("PATH=%s", newPath)) +} + func TestCLICommands(t *testing.T) { // Capture the starting working directory startingDir, err := os.Getwd() @@ -91,6 +125,7 @@ func TestCLICommands(t *testing.T) { // Set environment variables envVars := os.Environ() + envVars = prependPath(envVars, "./build", ".") // Add ./build and . to PATH in precedence order for key, value := range tc.Env { envVars = append(envVars, fmt.Sprintf("%s=%s", key, value)) } @@ -116,69 +151,79 @@ func TestCLICommands(t *testing.T) { } // Validate stdout - if !verifyOutput(stdout.String(), tc.Expect.Stdout) { + if !verifyOutput(t, "stdout", stdout.String(), tc.Expect.Stdout) { t.Errorf("Description: %s", tc.Description) - t.Errorf("Reason: Stdout did not match expected patterns.") - t.Errorf("Output: %q", stdout.String()) } // Validate stderr - if !verifyOutput(stderr.String(), tc.Expect.Stderr) { + if !verifyOutput(t, "stderr", stderr.String(), tc.Expect.Stderr) { t.Errorf("Description: %s", tc.Description) - t.Errorf("Reason: Stderr did not match expected patterns.") - t.Errorf("Output: %q", stderr.String()) } // Validate file existence - if !verifyFileExists(tc.Expect.FileExists) { + if !verifyFileExists(t, tc.Expect.FileExists) { t.Errorf("Description: %s", tc.Description) } // Validate file contents - if !verifyFileContains(tc.Expect.FileContains) { + if !verifyFileContains(t, tc.Expect.FileContains) { t.Errorf("Description: %s", tc.Description) } }) } } -func verifyOutput(output string, patterns []string) bool { +func verifyOutput(t *testing.T, outputType, output string, patterns []string) bool { + success := true for _, pattern := range patterns { re, err := regexp.Compile(pattern) if err != nil { - return false + t.Errorf("Invalid %s regex: %q, error: %v", outputType, pattern, err) + success = false + continue } if !re.MatchString(output) { - return false + t.Errorf("Reason: %s did not match pattern %q.", outputType, pattern) + t.Errorf("Output: %q", output) + success = false } } - return true + return success } -func verifyFileExists(files []string) bool { +func verifyFileExists(t *testing.T, files []string) bool { + success := true for _, file := range files { if _, err := os.Stat(file); errors.Is(err, os.ErrNotExist) { - return false + t.Errorf("Reason: Expected file does not exist: %q", file) + success = false } } - return true + return success } -func verifyFileContains(filePatterns map[string][]string) bool { +func verifyFileContains(t *testing.T, filePatterns map[string][]string) bool { + success := true for file, patterns := range filePatterns { content, err := ioutil.ReadFile(file) if err != nil { - return false + t.Errorf("Reason: Failed to read file %q: %v", file, err) + success = false + continue } for _, pattern := range patterns { re, err := regexp.Compile(pattern) if err != nil { - return false + t.Errorf("Invalid regex for file %q: %q, error: %v", file, pattern, err) + success = false + continue } if !re.Match(content) { - return false + t.Errorf("Reason: File %q did not match pattern %q.", file, pattern) + t.Errorf("Content: %q", string(content)) + success = false } } } - return true + return success } diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 68edfb10d..254bfec98 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -1,4 +1,16 @@ tests: + - name: "which atmos" + enabled: true + description: "Ensure atmos CLI is installed and we're using the one that was built." + workdir: "../" + command: "which" + args: + - "atmos" + expect: + stdout: + - '(build[/\\]atmos|atmos[/\\]atmos)' + exit_code: 0 + - name: "atmos" enabled: true description: "Verify atmos CLI reports missing stacks directory." @@ -47,7 +59,7 @@ tests: - "version" expect: stdout: - - '👽 Atmos \d+\.\d+\.\d+ on [a-z]+/[a-z0-9]+' + - '👽 Atmos (\d+\.\d+\.\d+|test) on [a-z]+/[a-z0-9]+' stderr: [] exit_code: 0 @@ -61,7 +73,7 @@ tests: - "--check" expect: stdout: - - '👽 Atmos \d+\.\d+\.\d+ on [a-z]+/[a-z0-9]+' + - '👽 Atmos (\d+\.\d+\.\d+|test) on [a-z]+/[a-z0-9]+' stderr: [] exit_code: 0 @@ -125,7 +137,7 @@ tests: - "yaml" expect: stdout: - - 'append_user_agent: Atmos/\d+\.\d+\.\d+ \(Cloud Posse; \+https:\/\/atmos\.tools\)' + - 'append_user_agent: Atmos/(\d+\.\d+\.\d+|test) \(Cloud Posse; \+https:\/\/atmos\.tools\)' exit_code: 0 - name: atmos describe config @@ -138,5 +150,5 @@ tests: - "config" expect: stdout: - - '"append_user_agent": "Atmos/\d+\.\d+\.\d+ \(Cloud Posse; \+https:\/\/atmos\.tools\)"' + - '"append_user_agent": "Atmos/(\d+\.\d+\.\d+|test) \(Cloud Posse; \+https:\/\/atmos\.tools\)"' exit_code: 0 From 3fffbc1538711d9b1186ef4a713121c0db23f7d2 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Mon, 23 Dec 2024 09:01:31 -0600 Subject: [PATCH 21/23] improve path handling --- tests/cli_test.go | 65 +++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/tests/cli_test.go b/tests/cli_test.go index 0ae6a3b76..255503cc8 100644 --- a/tests/cli_test.go +++ b/tests/cli_test.go @@ -53,36 +53,43 @@ func loadTestSuite(filePath string) (*TestSuite, error) { return &suite, nil } -func prependPath(envVars []string, dirs ...string) []string { - // Resolve absolute paths for the directories - var resolvedPaths []string +type PathManager struct { + OriginalPath string + Prepended []string +} + +// NewPathManager initializes a PathManager with the current PATH. +func NewPathManager() *PathManager { + return &PathManager{ + OriginalPath: os.Getenv("PATH"), + Prepended: []string{}, + } +} + +// Prepend adds directories to the PATH with precedence. +func (pm *PathManager) Prepend(dirs ...string) { for _, dir := range dirs { absPath, err := filepath.Abs(dir) if err != nil { fmt.Printf("Failed to resolve absolute path for %q: %v\n", dir, err) continue } - resolvedPaths = append(resolvedPaths, absPath) + pm.Prepended = append(pm.Prepended, absPath) } +} - // Create the new PATH variable with precedence order - currentPath := os.Getenv("PATH") - newPath := fmt.Sprintf("%s%c%s", - strings.Join(resolvedPaths, string(os.PathListSeparator)), +// GetPath returns the updated PATH. +func (pm *PathManager) GetPath() string { + return fmt.Sprintf("%s%c%s", + strings.Join(pm.Prepended, string(os.PathListSeparator)), os.PathListSeparator, - currentPath, + pm.OriginalPath, ) +} - // Replace the PATH in envVars - for i, env := range envVars { - if len(env) > 5 && env[:5] == "PATH=" { - envVars[i] = fmt.Sprintf("PATH=%s", newPath) - return envVars - } - } - - // If PATH is not found, add it - return append(envVars, fmt.Sprintf("PATH=%s", newPath)) +// Apply updates the PATH environment variable globally. +func (pm *PathManager) Apply() error { + return os.Setenv("PATH", pm.GetPath()) } func TestCLICommands(t *testing.T) { @@ -92,6 +99,15 @@ func TestCLICommands(t *testing.T) { t.Fatalf("Failed to get the current working directory: %v", err) } + // Initialize PathManager and update PATH + pathManager := NewPathManager() + pathManager.Prepend("../build", "..") + err = pathManager.Apply() + if err != nil { + t.Fatalf("Failed to apply updated PATH: %v", err) + } + fmt.Printf("Updated PATH: %s\n", pathManager.GetPath()) + testSuite, err := loadTestSuite("test_cases.yaml") if err != nil { t.Fatalf("Failed to load test suite: %v", err) @@ -120,12 +136,17 @@ func TestCLICommands(t *testing.T) { } } + // Check if the binary exists + binaryPath, err := exec.LookPath(tc.Command) + if err != nil { + t.Fatalf("Binary not found: %s. Current PATH: %s", tc.Command, pathManager.GetPath()) + } + // Prepare the command - cmd := exec.Command(tc.Command, tc.Args...) + cmd := exec.Command(binaryPath, tc.Args...) // Set environment variables envVars := os.Environ() - envVars = prependPath(envVars, "./build", ".") // Add ./build and . to PATH in precedence order for key, value := range tc.Env { envVars = append(envVars, fmt.Sprintf("%s=%s", key, value)) } @@ -136,7 +157,7 @@ func TestCLICommands(t *testing.T) { cmd.Stderr = &stderr // Run the command - err := cmd.Run() + err = cmd.Run() // Validate exit code exitCode := 0 From c628f8bbe9d6b015320a2a245f1e5d3a35cece59 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Mon, 23 Dec 2024 09:08:55 -0600 Subject: [PATCH 22/23] improve tests --- tests/test_cases.yaml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 254bfec98..9ebb6641f 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -9,6 +9,8 @@ tests: expect: stdout: - '(build[/\\]atmos|atmos[/\\]atmos)' + stderr: + - "^$" exit_code: 0 - name: "atmos" @@ -20,6 +22,8 @@ tests: stdout: - "atmos.yaml CLI config file specifies the directory for Atmos stacks as stacks," - "but the directory does not exist." + stderr: + - "^$" exit_code: 0 - name: atmos --help @@ -48,6 +52,8 @@ tests: - "\\bworkflow\\b" - "Flags:" - "for more information about a command" + stderr: + - "^$" exit_code: 0 - name: atmos version @@ -60,7 +66,8 @@ tests: expect: stdout: - '👽 Atmos (\d+\.\d+\.\d+|test) on [a-z]+/[a-z0-9]+' - stderr: [] + stderr: + - "^$" exit_code: 0 - name: atmos version --check @@ -74,7 +81,8 @@ tests: expect: stdout: - '👽 Atmos (\d+\.\d+\.\d+|test) on [a-z]+/[a-z0-9]+' - stderr: [] + stderr: + - "^$" exit_code: 0 - name: atmos docs @@ -86,6 +94,8 @@ tests: - "docs" expect: exit_code: 0 + stderr: + - "^$" - name: atmos docs myapp enabled: true @@ -98,6 +108,8 @@ tests: expect: stdout: - "Example Terraform Weather Component" + stderr: + - "^$" exit_code: 0 - name: atmos non-existent @@ -138,6 +150,8 @@ tests: expect: stdout: - 'append_user_agent: Atmos/(\d+\.\d+\.\d+|test) \(Cloud Posse; \+https:\/\/atmos\.tools\)' + stderr: + - "^$" exit_code: 0 - name: atmos describe config @@ -151,4 +165,6 @@ tests: expect: stdout: - '"append_user_agent": "Atmos/(\d+\.\d+\.\d+|test) \(Cloud Posse; \+https:\/\/atmos\.tools\)"' + stderr: + - "^$" exit_code: 0 From a81d40015272e3ab6f33c608ff24729211dbe854 Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Mon, 23 Dec 2024 11:13:11 -0600 Subject: [PATCH 23/23] Update .github/workflows/test.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 876a208fd..3c589351e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -107,7 +107,12 @@ jobs: - name: Add build artifacts directory to PATH for windows if: matrix.flavor.target == 'windows' + shell: pwsh run: | + $atmosPath = Join-Path ${{ github.workspace }} "atmos.exe" + if (-not (Test-Path $atmosPath)) { + throw "atmos.exe not found at: $atmosPath" + } echo "${{ github.workspace }}" >> $Env:GITHUB_PATH - uses: hashicorp/setup-terraform@v3