Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Vendoring Issues with Globs and Symlinks #984

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 58 additions & 5 deletions internal/exec/go_getter_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func IsValidScheme(scheme string) bool {
// do a git-based clone with a token.
type CustomGitHubDetector struct {
AtmosConfig schema.AtmosConfiguration
source string
}

// Detect implements the getter.Detector interface for go-getter v1.
Expand Down Expand Up @@ -99,6 +100,14 @@ func (d *CustomGitHubDetector) Detect(src, _ string) (string, bool, error) {
return "", false, fmt.Errorf("invalid GitHub URL %q", parsedURL.Path)
}

if !strings.Contains(d.source, "//") {
// means user typed something like "github.com/org/repo.git" with NO subdir
if strings.HasSuffix(parsedURL.Path, ".git") || len(parts) == 3 {
u.LogDebug(d.AtmosConfig, "Detected top-level repo with no subdir: appending '//.'\n")
parsedURL.Path = parsedURL.Path + "//."
}
}

atmosGitHubToken := os.Getenv("ATMOS_GITHUB_TOKEN")
gitHubToken := os.Getenv("GITHUB_TOKEN")

Expand Down Expand Up @@ -139,10 +148,10 @@ func (d *CustomGitHubDetector) Detect(src, _ string) (string, bool, error) {

// RegisterCustomDetectors prepends the custom detector so it runs before
// the built-in ones. Any code that calls go-getter should invoke this.
func RegisterCustomDetectors(atmosConfig schema.AtmosConfiguration) {
func RegisterCustomDetectors(atmosConfig schema.AtmosConfiguration, source string) {
getter.Detectors = append(
[]getter.Detector{
&CustomGitHubDetector{AtmosConfig: atmosConfig},
&CustomGitHubDetector{AtmosConfig: atmosConfig, source: source},
},
getter.Detectors...,
)
Expand All @@ -159,24 +168,68 @@ func GoGetterGet(
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

// Register custom detectors
RegisterCustomDetectors(atmosConfig)
// Register custom detectors, passing the original `src` to the CustomGitHubDetector.
// go-getter typically strips subdirectories before calling the detector, so the
// unaltered source is needed to identify whether a top-level repository or a
// subdirectory was specified (e.g., for appending "//." only when no subdir is present).
RegisterCustomDetectors(atmosConfig, src)

client := &getter.Client{
Ctx: ctx,
Src: src,
// Destination where the files will be stored. This will create the directory if it doesn't exist
Dst: dest,
Mode: clientMode,
}
Getters: map[string]getter.Getter{
// Overriding 'git'
"git": &CustomGitGetter{},
"file": &getter.FileGetter{},
"hg": &getter.HgGetter{},
"http": &getter.HttpGetter{},
"https": &getter.HttpGetter{},
// "s3": &getter.S3Getter{}, // add as needed
// "gcs": &getter.GCSGetter{},

},
}
if err := client.Get(); err != nil {
return err
}

return nil
}

// CustomGitGetter is a custom getter for git (git::) that removes symlinks
type CustomGitGetter struct {
getter.GitGetter
}

// Implements the custom getter logic removing symlinks
func (c *CustomGitGetter) Get(dst string, url *url.URL) error {
// Normal clone
if err := c.GitGetter.Get(dst, url); err != nil {
return err
}
// Remove symlinks
return removeSymlinks(dst)
}

// removeSymlinks walks the destination directory and removes any symlinks
Listener430 marked this conversation as resolved.
Show resolved Hide resolved
// it encounters.
func removeSymlinks(root string) error {
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
u.LogWarning(schema.AtmosConfiguration{}, fmt.Sprintf("Removing symlink: %s", path))
// It's a symlink, remove it
return os.Remove(path)
}
return nil
})
}

// DownloadDetectFormatAndParseFile downloads a remote file, detects the format of the file (JSON, YAML, HCL) and parses the file into a Go type
func DownloadDetectFormatAndParseFile(atmosConfig schema.AtmosConfiguration, file string) (any, error) {
tempDir := os.TempDir()
Expand Down