summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2023-08-23 10:15:56 +0200
committerMartin Polden <mpolden@mpolden.no>2023-08-23 10:16:49 +0200
commit14dc0c331f8d607019f60e745e22137d0865cfd9 (patch)
tree8d7a768bb102f333933264a2913f092f650dc78a /client
parent30758a13a310f5088dcf3bdfcd7e5688872afa7b (diff)
Support cloning into existing and empty directory
Diffstat (limited to 'client')
-rw-r--r--client/go/internal/cli/cmd/clone.go26
-rw-r--r--client/go/internal/cli/cmd/clone_test.go42
2 files changed, 54 insertions, 14 deletions
diff --git a/client/go/internal/cli/cmd/clone.go b/client/go/internal/cli/cmd/clone.go
index 6fb97581ea3..a835892990b 100644
--- a/client/go/internal/cli/cmd/clone.go
+++ b/client/go/internal/cli/cmd/clone.go
@@ -6,8 +6,10 @@ package cmd
import (
"archive/zip"
+ "errors"
"fmt"
"io"
+ "io/fs"
"log"
"net/http"
"os"
@@ -76,6 +78,23 @@ type zipFile struct {
modTime time.Time
}
+func (c *cloner) createDirectory(path string) error {
+ if err := os.Mkdir(path, 0755); err != nil {
+ if errors.Is(err, fs.ErrExist) {
+ entries, err := os.ReadDir(path)
+ if err != nil {
+ return err
+ }
+ if len(entries) > 0 {
+ return fmt.Errorf("%s already exists and is not empty", path)
+ }
+ } else {
+ return err
+ }
+ }
+ return nil
+}
+
// Clone copies the application identified by applicationName into given path. If the cached copy of sample applications
// has expired (as determined by its entity tag), a current copy will be downloaded from GitHub automatically.
func (c *cloner) Clone(applicationName, path string) error {
@@ -95,9 +114,8 @@ func (c *cloner) Clone(applicationName, path string) error {
dirPrefix := "sample-apps-master/" + applicationName + "/"
if strings.HasPrefix(f.Name, dirPrefix) {
if !found { // Create destination directory lazily when source is found
- createErr := os.Mkdir(path, 0755)
- if createErr != nil {
- return fmt.Errorf("could not create directory '%s': %w", color.CyanString(path), createErr)
+ if err := c.createDirectory(path); err != nil {
+ return fmt.Errorf("could not create directory: %w", err)
}
}
found = true
@@ -111,7 +129,7 @@ func (c *cloner) Clone(applicationName, path string) error {
if !found {
return errHint(fmt.Errorf("could not find source application '%s'", color.CyanString(applicationName)), "Use -f to ignore the cache")
} else {
- log.Print("Created ", color.CyanString(path))
+ log.Print("Cloned into ", color.CyanString(path))
}
return nil
}
diff --git a/client/go/internal/cli/cmd/clone_test.go b/client/go/internal/cli/cmd/clone_test.go
index f136299db85..3d7250cc760 100644
--- a/client/go/internal/cli/cmd/clone_test.go
+++ b/client/go/internal/cli/cmd/clone_test.go
@@ -18,14 +18,16 @@ import (
)
func TestClone(t *testing.T) {
- assertCreated("text-search", "mytestapp", t)
-}
-
-func assertCreated(sampleAppName string, app string, t *testing.T) {
+ origWd, err := os.Getwd()
+ require.Nil(t, err)
+ sampleAppName := "text-search"
+ app := "mytestapp"
tempDir := t.TempDir()
app1 := filepath.Join(tempDir, "app1")
- defer os.RemoveAll(app)
-
+ t.Cleanup(func() {
+ os.Chdir(origWd)
+ os.RemoveAll(app)
+ })
httpClient := &mock.HTTPClient{}
cli, stdout, stderr := newTestCLI(t)
cli.httpClient = httpClient
@@ -35,7 +37,7 @@ func assertCreated(sampleAppName string, app string, t *testing.T) {
// Initial cloning. GitHub includes the ETag header, but we don't require it
httpClient.NextResponseBytes(200, testdata)
require.Nil(t, cli.Run("clone", sampleAppName, app1))
- assert.Equal(t, "Created "+app1+"\n", stdout.String())
+ assert.Equal(t, "Cloned into "+app1+"\n", stdout.String())
assertFiles(t, app1)
// Clone with cache hit
@@ -43,8 +45,27 @@ func assertCreated(sampleAppName string, app string, t *testing.T) {
stdout.Reset()
app2 := filepath.Join(tempDir, "app2")
require.Nil(t, cli.Run("clone", sampleAppName, app2))
- assert.Equal(t, "Using cached sample apps ...\nCreated "+app2+"\n", stdout.String())
+ assert.Equal(t, "Using cached sample apps ...\nCloned into "+app2+"\n", stdout.String())
assertFiles(t, app2)
+ stdout.Reset()
+
+ // Clone to current directory (dot)
+ emptyDir := filepath.Join(tempDir, "mypath1")
+ require.Nil(t, os.Mkdir(emptyDir, 0755))
+ require.Nil(t, os.Chdir(emptyDir))
+ httpClient.NextStatus(http.StatusNotModified)
+ require.Nil(t, cli.Run("clone", sampleAppName, "."))
+ assert.Equal(t, "Using cached sample apps ...\nCloned into .\n", stdout.String())
+ assertFiles(t, ".")
+ stdout.Reset()
+
+ // Clone to non-empty directory
+ httpClient.NextStatus(http.StatusNotModified)
+ nonEmptyDir := filepath.Join(tempDir, "mypath2")
+ require.Nil(t, os.MkdirAll(filepath.Join(nonEmptyDir, "more"), 0755))
+ require.NotNil(t, cli.Run("clone", sampleAppName, nonEmptyDir))
+ assert.Equal(t, "Error: could not create directory: "+nonEmptyDir+" already exists and is not empty\n", stderr.String())
+ stderr.Reset()
// Clone while ignoring cache
headers := make(http.Header)
@@ -53,7 +74,7 @@ func assertCreated(sampleAppName string, app string, t *testing.T) {
stdout.Reset()
app3 := filepath.Join(tempDir, "app3")
require.Nil(t, cli.Run("clone", "-f", sampleAppName, app3))
- assert.Equal(t, "Created "+app3+"\n", stdout.String())
+ assert.Equal(t, "Cloned into "+app3+"\n", stdout.String())
assertFiles(t, app3)
// Cloning falls back to cached copy if GitHub is unavailable
@@ -62,7 +83,7 @@ func assertCreated(sampleAppName string, app string, t *testing.T) {
app4 := filepath.Join(tempDir, "app4")
require.Nil(t, cli.Run("clone", "-f=false", sampleAppName, app4))
assert.Equal(t, "Warning: could not download sample apps: github returned status 500\n", stderr.String())
- assert.Equal(t, "Using cached sample apps ...\nCreated "+app4+"\n", stdout.String())
+ assert.Equal(t, "Using cached sample apps ...\nCloned into "+app4+"\n", stdout.String())
assertFiles(t, app4)
// The only cached file is the latest one
@@ -79,6 +100,7 @@ func assertCreated(sampleAppName string, app string, t *testing.T) {
}
func assertFiles(t *testing.T, app string) {
+ t.Helper()
assert.True(t, util.PathExists(filepath.Join(app, "README.md")))
assert.True(t, util.PathExists(filepath.Join(app, "src", "main", "application")))
assert.True(t, util.IsDirectory(filepath.Join(app, "src", "main", "application")))