diff --git a/internal/git_clone_test.go b/internal/git_clone_test.go new file mode 100644 index 000000000..a4b662779 --- /dev/null +++ b/internal/git_clone_test.go @@ -0,0 +1,21 @@ +package internal + +import ( + "path/filepath" + "testing" + + "github.com/databricks/cli/libs/git" + "github.com/stretchr/testify/assert" +) + +func TestAccGitClonePrivateRepository(t *testing.T) { + tmpDir := t.TempDir() + + // This is a private repository only accessible to databricks employees + err := git.Clone("databricks", "bundle-samples-internal", tmpDir) + assert.NoError(t, err) + + // assert examples from the private repository + assert.DirExists(t, filepath.Join(tmpDir, "shark_sightings")) + assert.DirExists(t, filepath.Join(tmpDir, "wikipedia_clickstream")) +} diff --git a/libs/git/clone.go b/libs/git/clone.go index 419832592..7c191bdab 100644 --- a/libs/git/clone.go +++ b/libs/git/clone.go @@ -1,19 +1,40 @@ package git import ( + "errors" "fmt" "io" "net/http" "os" + "os/exec" "path/filepath" "github.com/databricks/cli/libs/zip" ) +var errNotFound = errors.New("not found") + func githubZipUrl(org string, name string, ref string) string { - return fmt.Sprintf(`https://github.com/%s/%s/archive/%s.zip`, org, name, ref) + return fmt.Sprintf(`%s/archive/%s.zip`, githubUrl(org, name), ref) } +func githubUrl(org string, name string) string { + return fmt.Sprintf(`https://github.com/%s/%s`, org, name) +} + +type RepositoryNotFoundError struct { + url string +} + +func (err RepositoryNotFoundError) Error() string { + return fmt.Sprintf("repository not found: %s", err.url) +} + +func (err RepositoryNotFoundError) Is(other error) bool { + return other == errNotFound +} + +// TODO: pass context to these the get requests func download(url string, dest string) error { resp, err := http.Get(url) if err != nil { @@ -22,7 +43,7 @@ func download(url string, dest string) error { defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("repository not found: %s", url) + return RepositoryNotFoundError{url} } if resp.StatusCode != http.StatusOK { @@ -38,7 +59,18 @@ func download(url string, dest string) error { return err } -func Clone(org string, repoName string, targetDir string) error { +func clonePrivate(org string, repoName string, targetDir string) error { + zipUrl := githubUrl(org, repoName) + + // We append repoName-main to targetDir to be symmetric to clonePublic + targetDir = filepath.Join(targetDir, repoName+"-main") + + // TODO: pass context to the command execution + cmd := exec.Command("git", "clone", zipUrl, targetDir, "--branch", "main") + return cmd.Run() +} + +func clonePublic(org string, repoName string, targetDir string) error { zipDst := filepath.Join(targetDir, repoName+".zip") zipUrl := githubZipUrl(org, repoName, "main") @@ -57,3 +89,15 @@ func Clone(org string, repoName string, targetDir string) error { // Remove the ZIP file return os.Remove(zipDst) } + +func Clone(org, repoName string, targetDir string) error { + // First we try to clone the repository as a public URL, as that does not + // require the git CLI + err := clonePublic(org, repoName, targetDir) + if err != nil && !errors.Is(err, errNotFound) { + return err + } + + // Since a public repository was not found, we defer to the git CLI + return clonePrivate(org, repoName, targetDir) +} diff --git a/libs/git/clone_test.go b/libs/git/clone_test.go index b655d2ec9..a28e43110 100644 --- a/libs/git/clone_test.go +++ b/libs/git/clone_test.go @@ -12,7 +12,7 @@ func TestGitClonePublicRepository(t *testing.T) { tmpDir := t.TempDir() var err error - err = Clone("databricks", "cli", tmpDir) + err = clonePublic("databricks", "cli", tmpDir) assert.NoError(t, err) assert.DirExists(t, filepath.Join(tmpDir, "cli-main"))