Skip to content

Commit 723a0c3

Browse files
Listener430ostermanaknyshcoderabbitai[bot]autofix-ci[bot]
authored
Update dry run for atmos vendor pull to support ssh + detailed SCP urls alignment (#1076)
* ssh and scp support for pulling * updates * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * updates as per the feedback * mask for basic auth fileds + tests * vendored files commited by mistake removal * ATMOS_GITHUB_TOKEN is prioritized * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * atmos.yaml cleanup * update dry run for generic and component vendoring * vendor utils excess verbose removal + tests fixed so ssh ones are run only in dry mode * updates * updated snapshot plus removal of ssh agent missing warning causing different error message on MacOS * Detect function refactoring to use helper functions and become shorter * comments cleanup * symantic logs * symantic logs + tests snapshots updates * golangci refactoring to make Detect function shorter * updated snapshots * golgangci new feedback after new rules were introduced * futher code alignment as per the new rules * nested code blocks feedback * more lint refactoring * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Andriy Knysh <[email protected]> * Update tests/test-cases/demo-vendoring.yaml Co-authored-by: Andriy Knysh <[email protected]> * Update tests/test-cases/demo-vendoring.yaml Co-authored-by: Andriy Knysh <[email protected]> * Update tests/test-cases/demo-vendoring.yaml Co-authored-by: Andriy Knysh <[email protected]> * Update tests/test-cases/demo-vendoring.yaml Co-authored-by: Andriy Knysh <[email protected]> * Update internal/exec/vendor_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update internal/exec/vendor_model_component.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * updated bitbucket tokens docs + fixed order tokens are handled + the same for gitlab token * Update internal/exec/go_getter_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update internal/exec/vendor_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * tests update and vendoring logs alignment to the way it used to be before charmbracelet * fixtures clean up * Update tests/test-cases/demo-vendoring.yaml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * bring back charmbracelet log output format for vendoring plus update the snapshot * tests alignment * vendor pull ssh is cleaned up to contain only ssh link for vendoring * rollback broken token priorities and cleaned up tests * linter feedback fix * ssh vendoring test update * updated tests with correct !not statement and more linter feedback fixes * component test url fix * updated client * client update * tests alignment * fixing tests * initial docs text * broken link fix * doc wording update * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update tests/fixtures/scenarios/vendor-pulls-ssh/atmos.yaml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * whitespaces removed and unit tests added for go getter utils * [autofix.ci] apply automated fixes * test fix for Linux plus linter feedback for tests implemented * [autofix.ci] apply automated fixes * furhter linter feedback fixes * more linter fixes * more linter fixes * error wrapping ignore * trailing whitespaces removal * linter feedback update er113 ignore * Refactored error handling as per coderabbit suggestions in go_getter_utils.go * Linter feedback ignore * Cognitive complexity up * Loosen linter rules * Cyclomatic up * Cognitive up * Cognitive up - second parameter * Linter - Function length up to 85 * Linter - Function length - second parameter * atmosConfig should not be passed by value - ignoring this as it was adddressed in PR984 Globs * Update internal/exec/vendor_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/configuration/configuration.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * MaskedSecret constant added to url utils * Added definition lists to the vendor pull mdx * Update .golangci.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update .golangci.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update .golangci.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update .golangci.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * Update .golangci.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * snahshot update * vendoring messages to stderr * taking out severity settings from golangci.yaml * Removing excessing nolint flags * CodeQL feedback fix * [autofix.ci] apply automated fixes * Apply suggestions from code review * Update website/docs/cli/commands/vendor/vendor-pull.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> * docs update from the childen PR * vendoring to stderr fix * updated snapshot * initial commit of PR1061 code * removing whitespaces from snapshots * loosen codecov thresholds temporarily * change go getter signature to align the approach used in main * snapshots update * missing head commit fix for test coverage * updated snapshots * restoring codecov values * remaining snapshot * Fix after linter changes rollback * temporarily taking out the unit tests for go getter * updated snapshots * updated unit tests * taking out tests that modify global env settings that cause vendoring issues with unit tests * workflow/test back to original state * reverting back to standard logger for files vendoring * updated snapshot after getting back to standard logger * mergeDefaultImports method messages from load.go added to the snapshots * Ensure ATMOS_BITBUCKET_TOKEN and ATMOS_GITLAB_TOKEN take precedence over BITBUCKET_TOKEN and GITLAB_TOKEN. * reverting golangci-linter params back * More linter fixes * More linter feedback fixes * schemeSeparator conts and updated snapshot * Thread safety for custom detectors registration * Removing broken file * Adding the snapshot back * Detect to skip local file path fix * added 2 more unit tests to cover local file vendoring and host being not the one from the list * additional tests for Gitlab and Bitbucket in dry-run mode + code alignment * updates --------- Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]> Co-authored-by: Andriy Knysh <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 6f5959c commit 723a0c3

19 files changed

+412
-28
lines changed

internal/exec/go_getter_utils.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,21 @@ func (d *CustomGitDetector) Detect(src, _ string) (string, bool, error) {
9595
return "", false, fmt.Errorf("failed to parse URL %q: %w", maskedSrc, err)
9696
}
9797

98+
// If no host is detected, this is likely a local file path.
99+
// Skip custom processing so that go getter handles it as is.
100+
if parsedURL.Host == "" {
101+
log.Debug("No host detected in URL, skipping custom git detection", keyURL, src)
102+
return "", false, nil
103+
}
104+
98105
// Normalize the path.
99106
d.normalizePath(parsedURL)
100107

101108
// Adjust host check to support GitHub, Bitbucket, GitLab, etc.
102109
host := strings.ToLower(parsedURL.Host)
103110
if host != hostGitHub && host != hostBitbucket && host != hostGitLab {
104-
log.Debug("Skipping token injection for a unsupported host", "host", parsedURL.Host)
111+
log.Debug("Skipping token injection for an unsupported host", "host", parsedURL.Host)
112+
return "", false, nil
105113
}
106114

107115
log.Debug("Reading config param", "InjectGithubToken", d.AtmosConfig.Settings.InjectGithubToken)
@@ -263,7 +271,6 @@ func getDefaultUsername(host string) string {
263271
return "x-token-auth"
264272
}
265273
}
266-
log.Debug("Using Bitbucket username", "username", defaultUsername)
267274
return defaultUsername
268275
default:
269276
return "x-access-token"

internal/exec/go_getter_utils_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,40 @@ func TestNormalizePath_ErrorHandling(t *testing.T) {
267267
}
268268
}
269269

270+
func TestDetect_LocalFilePath(t *testing.T) {
271+
// This tests the branch when the input is a local file path (no host).
272+
config := fakeAtmosConfig(false)
273+
detector := &CustomGitDetector{AtmosConfig: config, source: "/home/user/repo"}
274+
localFile := "/home/user/repo/README.md"
275+
result, ok, err := detector.Detect(localFile, "")
276+
if err != nil {
277+
t.Fatalf("Expected no error for local file path, got: %v", err)
278+
}
279+
if ok != false {
280+
t.Errorf("Expected ok to be false for local file path, got true")
281+
}
282+
if result != "" {
283+
t.Errorf("Expected result to be empty for local file path, got: %s", result)
284+
}
285+
}
286+
287+
func TestDetect_UnsupportedHost(t *testing.T) {
288+
// This tests the branch when the URL host is not supported (not GitHub, GitLab, or Bitbucket)
289+
config := fakeAtmosConfig(false)
290+
detector := &CustomGitDetector{AtmosConfig: config, source: "repo.git"}
291+
unsupportedURL := "https://example.com/repo.git"
292+
result, ok, err := detector.Detect(unsupportedURL, "")
293+
if err != nil {
294+
t.Fatalf("Expected no error for unsupported host, got: %v", err)
295+
}
296+
if ok != false {
297+
t.Errorf("Expected ok to be false for unsupported host, got true")
298+
}
299+
if result != "" {
300+
t.Errorf("Expected result to be empty for unsupported host, got: %s", result)
301+
}
302+
}
303+
270304
func TestMain(m *testing.M) {
271305
code := m.Run()
272306
getter.Detectors = originalDetectors

internal/exec/vendor_component_utils.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -386,13 +386,26 @@ func parseMixinURI(mixin *schema.VendorComponentMixins) (string, error) {
386386
func downloadComponentAndInstall(p *pkgComponentVendor, dryRun bool, atmosConfig *schema.AtmosConfiguration) tea.Cmd {
387387
return func() tea.Msg {
388388
if dryRun {
389-
// Simulate the action
389+
if needsCustomDetection(p.uri) {
390+
log.Debug("Dry-run mode: custom detection required for component (or mixin) URI", "component", p.name, "uri", p.uri)
391+
detector := &CustomGitDetector{AtmosConfig: *atmosConfig, source: ""}
392+
_, _, err := detector.Detect(p.uri, "")
393+
if err != nil {
394+
return installedPkgMsg{
395+
err: fmt.Errorf("dry-run: detection failed for component %s: %w", p.name, err),
396+
name: p.name,
397+
}
398+
}
399+
} else {
400+
log.Debug("Dry-run mode: skipping custom detection; URI already supported by go-getter", "component", p.name, "uri", p.uri)
401+
}
390402
time.Sleep(100 * time.Millisecond)
391403
return installedPkgMsg{
392404
err: nil,
393405
name: p.name,
394406
}
395407
}
408+
396409
if p.IsComponent {
397410
err := installComponent(p, atmosConfig)
398411
if err != nil {

internal/exec/vendor_model.go

+68-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package exec
22

33
import (
44
"fmt"
5+
"net/url"
56
"os"
67
"path/filepath"
78
"strings"
@@ -18,6 +19,7 @@ import (
1819
"github.com/cloudposse/atmos/internal/tui/templates/term"
1920
"github.com/cloudposse/atmos/pkg/schema"
2021
"github.com/cloudposse/atmos/pkg/ui/theme"
22+
u "github.com/cloudposse/atmos/pkg/utils"
2123
)
2224

2325
type pkgType int
@@ -330,8 +332,9 @@ func max(a, b int) int {
330332

331333
func downloadAndInstall(p *pkgAtmosVendor, dryRun bool, atmosConfig *schema.AtmosConfiguration) tea.Cmd {
332334
return func() tea.Msg {
335+
log.Debug("Downloading and installing package", "package", p.name)
333336
if dryRun {
334-
return handleDryRunInstall(p)
337+
return handleDryRunInstall(p, atmosConfig)
335338
}
336339
tempDir, err := createTempDir()
337340
if err != nil {
@@ -385,15 +388,77 @@ func (p *pkgAtmosVendor) installer(tempDir *string, atmosConfig *schema.AtmosCon
385388
return nil
386389
}
387390

388-
func handleDryRunInstall(p *pkgAtmosVendor) tea.Msg {
389-
// Simulate the action
391+
func handleDryRunInstall(p *pkgAtmosVendor, atmosConfig *schema.AtmosConfiguration) tea.Msg {
392+
log.Debug("Entering dry-run flow for generic (non component/mixin) vendoring ", "package", p.name)
393+
394+
if needsCustomDetection(p.uri) {
395+
log.Debug("Custom detection required for URI", "uri", p.uri)
396+
detector := &CustomGitDetector{AtmosConfig: *atmosConfig, source: ""}
397+
_, _, err := detector.Detect(p.uri, "")
398+
if err != nil {
399+
return installedPkgMsg{
400+
err: fmt.Errorf("dry-run: detection failed: %w", err),
401+
name: p.name,
402+
}
403+
}
404+
} else {
405+
log.Debug("Skipping custom detection; URI already supported by go getter", "uri", p.uri)
406+
}
407+
390408
time.Sleep(500 * time.Millisecond)
391409
return installedPkgMsg{
392410
err: nil,
393411
name: p.name,
394412
}
395413
}
396414

415+
// Thie is a replica of getForce method from go getter library, had to make it as it is not exported.
416+
// The idea is to call Detect method in dry run only for those links where go getter does this.
417+
// Otherwise, Detect is run for every link being vendored which isn't correct.
418+
func needsCustomDetection(src string) bool {
419+
_, getSrc := "", src
420+
if idx := strings.Index(src, "::"); idx >= 0 {
421+
_, getSrc = src[:idx], src[idx+2:]
422+
}
423+
424+
getSrc, _ = getter.SourceDirSubdir(getSrc)
425+
426+
if absPath, err := filepath.Abs(getSrc); err == nil {
427+
if u.FileExists(absPath) {
428+
return false
429+
}
430+
isDir, err := u.IsDirectory(absPath)
431+
if err == nil && isDir {
432+
return false
433+
}
434+
}
435+
436+
parsed, err := url.Parse(getSrc)
437+
if err != nil || parsed.Scheme == "" {
438+
return true
439+
}
440+
441+
supportedSchemes := map[string]bool{
442+
"http": true,
443+
"https": true,
444+
"git": true,
445+
"hg": true,
446+
"s3": true,
447+
"gcs": true,
448+
"file": true,
449+
"oci": true,
450+
"ssh": true,
451+
"git+ssh": true,
452+
"git+https": true,
453+
}
454+
455+
if _, ok := supportedSchemes[parsed.Scheme]; ok {
456+
return false
457+
}
458+
459+
return true
460+
}
461+
397462
func createTempDir() (string, error) {
398463
// Create temp directory
399464
tempDir, err := os.MkdirTemp("", "atmos-vendor")

tests/fixtures/scenarios/vendor-creds-sanitize/atmos.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
base_path: "./"
32
settings:
43
inject_github_token: true
@@ -17,4 +16,4 @@ stacks:
1716
- "deploy/**/*"
1817
excluded_paths:
1918
- "**/_defaults.yaml"
20-
name_pattern: "{stage}"
19+
name_pattern: "{stage}"

tests/fixtures/scenarios/vendor-creds-sanitize/vendor.yaml

+23-5
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ spec:
77
imports: []
88

99
sources:
10-
# Basic HTTPS default (token injection expected)
10+
# GitHub - Basic HTTPS default (token injection expected)
1111
- component: "terraform-null-label-basic"
1212
source: "github.com/cloudposse/terraform-null-label.git?ref={{ .Version }}"
1313
version: "0.25.0"
1414
targets:
1515
- "library/basic/{{ .Component }}"
1616
tags:
1717
- demo
18-
19-
# Direct credentials provided in the URL (token injection should be skipped)
18+
19+
# GitHub - Direct credentials provided in the URL (token injection should be skipped)
2020
- component: "terraform-null-label-direct"
2121
source: "https://myuser:[email protected]/cloudposse/terraform-null-label.git?ref={{ .Version }}"
2222
version: "0.25.0"
@@ -25,11 +25,29 @@ spec:
2525
tags:
2626
- demo
2727

28-
# HTTPS with pre-existing credentials (token injection skipped)
28+
# GitHub - HTTPS with pre-existing credentials (token injection skipped)
2929
- component: "terraform-null-label-cred"
3030
source: "https://[email protected]/cloudposse/terraform-null-label.git?ref={{ .Version }}"
3131
version: "0.25.0"
3232
targets:
3333
- "library/cred/{{ .Component }}"
3434
tags:
35-
- demo
35+
- demo
36+
37+
# GitLab - Public Terraform module (token injection expected)
38+
- component: "terraform-gitlab-vpc"
39+
source: "gitlab.com/gitlab-org/ci-cd/deploy-stage/environments-group/examples/gitlab-terraform-aws.git?ref={{ .Version }}"
40+
version: "master"
41+
targets:
42+
- "library/gitlab/{{ .Component }}"
43+
tags:
44+
- demo
45+
46+
# Bitbucket - Public DevOps module (token injection expected)
47+
- component: "terraform-bitbucket-deploy-jenkins"
48+
source: "bitbucket.org/blainethemono/terraform-deploy_jenkins_to_k8s_using_terraform.git?ref=master"
49+
version: "master"
50+
targets:
51+
- "library/bitbucket/{{ .Component }}"
52+
tags:
53+
- demo

tests/fixtures/scenarios/vendor-pulls-ssh/atmos.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ stacks:
1717
name_pattern: "{stage}"
1818
logs:
1919
file: "/dev/stderr"
20-
level: Info
20+
level: Info

tests/fixtures/scenarios/vendor-pulls-ssh/vendor.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ spec:
2121
targets:
2222
- "library/basic/{{ .Component }}"
2323
tags:
24-
- demo
24+
- demo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
base_path: "./"
2+
settings:
3+
inject_github_token: true
4+
components:
5+
terraform:
6+
base_path: "components/terraform"
7+
apply_auto_approve: false
8+
deploy_run_init: true
9+
init_run_reconfigure: true
10+
auto_generate_backend_file: false
11+
stacks:
12+
base_path: "stacks"
13+
included_paths:
14+
- "deploy/**/*"
15+
excluded_paths:
16+
- "**/_defaults.yaml"
17+
name_pattern: "{stage}"
18+
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: atmos/v1
2+
kind: ComponentVendorConfig
3+
metadata:
4+
name: ipinfo
5+
description: Vendor configuration for the ipinfo component
6+
spec:
7+
source:
8+
uri: "git::[email protected]:cloudposse/atmos.git//examples/demo-library/ipinfo?ref=main"
9+
version: "main"
10+
included_paths:
11+
- "**/*.tf"
12+
- "**/*.tfvars"
13+
- "**/*.md"
14+
targets:
15+
- "components/terraform/ipinfo/main"
16+
tags:
17+
- demo

tests/snapshots/TestCLICommands_atmos_vendor_pull_component_using_SSH.stderr.golden

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/snapshots/TestCLICommands_atmos_vendor_pull_component_using_SSH.stdout.golden

Whitespace-only changes.

0 commit comments

Comments
 (0)