From 3565d6e018aaef69bc1ad484ca851c06250abf70 Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Fri, 26 Apr 2024 13:07:15 +0200 Subject: [PATCH 1/4] Add support for clones created with git worktree --- internal/files/repository.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/internal/files/repository.go b/internal/files/repository.go index a6aa300ad4..b22123234e 100644 --- a/internal/files/repository.go +++ b/internal/files/repository.go @@ -5,11 +5,33 @@ package files import ( + "bytes" "fmt" "os" "path/filepath" + + "gopkg.in/yaml.v3" ) +func isGitWorktree(path string) error { + content, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read %s: %w", path, err) + } + type gitWorktree struct { + GitDir string `yaml:"gitdir"` + } + var worktree gitWorktree + dec := yaml.NewDecoder(bytes.NewBuffer(content)) + dec.KnownFields(true) + + if err := dec.Decode(&worktree); err != nil { + return fmt.Errorf("failed to decode %s: %w", path, err) + } + + return nil +} + func FindRepositoryRootDirectory() (string, error) { workDir, err := os.Getwd() if err != nil { @@ -24,6 +46,14 @@ func FindRepositoryRootDirectory() (string, error) { for dir != "." { path := filepath.Join(dir, ".git") fileInfo, err := os.Stat(path) + if err == nil && !fileInfo.IsDir() { + errWorktree := isGitWorktree(path) + if errWorktree != nil { + return "", errWorktree + } + + return dir, nil + } if err == nil && fileInfo.IsDir() { return dir, nil } From d6c3ea8953729b7f0eb46582ebb699f6b2de21d9 Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Fri, 26 Apr 2024 16:35:00 +0200 Subject: [PATCH 2/4] Add tests --- internal/files/repository_test.go | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 internal/files/repository_test.go diff --git a/internal/files/repository_test.go b/internal/files/repository_test.go new file mode 100644 index 0000000000..0244a637d2 --- /dev/null +++ b/internal/files/repository_test.go @@ -0,0 +1,103 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package files + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepositoryDirectory(t *testing.T) { + tempDir := t.TempDir() + + gitDir := filepath.Join(tempDir, ".git") + otherDir := filepath.Join(tempDir, "other") + + err := os.MkdirAll(gitDir, 0o755) + require.NoError(t, err) + err = os.MkdirAll(otherDir, 0o755) + require.NoError(t, err) + + err = os.Chdir(otherDir) + require.NoError(t, err) + + dir, err := FindRepositoryRootDirectory() + require.NoError(t, err) + assert.Equal(t, tempDir, dir) + + // test a non repository folder + nonGitDir := t.TempDir() + + err = os.Chdir(nonGitDir) + require.NoError(t, err) + + _, err = FindRepositoryRootDirectory() + assert.ErrorIs(t, err, os.ErrNotExist) +} + +func TestRepositoryGitWorktree(t *testing.T) { + cases := []struct { + name string + createGit bool + contents string + repoDir string + expectedError string + valid bool + }{ + { + name: "valid git worktree", + createGit: true, + contents: "gitdir: /path/to/repo/main", + repoDir: t.TempDir(), + expectedError: "", + valid: true, + }, + { + name: "invalid git worktree file", + createGit: true, + contents: "gitdir: /path/to/repo/main\nfoo: bar", + repoDir: t.TempDir(), + expectedError: "yaml: unmarshal errors:\n line 2: field foo not found in type files.gitWorktree", + valid: false, + }, + { + name: "invalid git worktree file", + createGit: false, + contents: "", + repoDir: t.TempDir(), + expectedError: "file does not exist", + valid: false, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + gitWorktreeFile := filepath.Join(c.repoDir, ".git") + otherDir := filepath.Join(c.repoDir, "other") + + if c.createGit { + err := os.WriteFile(gitWorktreeFile, []byte(c.contents), 0o644) + require.NoError(t, err) + } + err := os.MkdirAll(otherDir, 0o755) + require.NoError(t, err) + + err = os.Chdir(otherDir) + require.NoError(t, err) + + dir, err := FindRepositoryRootDirectory() + if c.valid { + require.NoError(t, err) + assert.Equal(t, c.repoDir, dir) + } else { + assert.ErrorContains(t, err, "yaml: unmarshal errors:\n line 2: field foo not found in type files.gitWorktree") + } + }) + } +} From be8738a3f9421e4a20a5a0bad5d66ae14de9bf5b Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Fri, 26 Apr 2024 20:40:39 +0200 Subject: [PATCH 3/4] Add a new isGitRootDir function --- internal/files/repository.go | 74 ++++++++++++++----------- internal/files/repository_test.go | 90 ++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 57 deletions(-) diff --git a/internal/files/repository.go b/internal/files/repository.go index b22123234e..65a10e2fdc 100644 --- a/internal/files/repository.go +++ b/internal/files/repository.go @@ -5,7 +5,7 @@ package files import ( - "bytes" + "errors" "fmt" "os" "path/filepath" @@ -13,25 +13,6 @@ import ( "gopkg.in/yaml.v3" ) -func isGitWorktree(path string) error { - content, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read %s: %w", path, err) - } - type gitWorktree struct { - GitDir string `yaml:"gitdir"` - } - var worktree gitWorktree - dec := yaml.NewDecoder(bytes.NewBuffer(content)) - dec.KnownFields(true) - - if err := dec.Decode(&worktree); err != nil { - return fmt.Errorf("failed to decode %s: %w", path, err) - } - - return nil -} - func FindRepositoryRootDirectory() (string, error) { workDir, err := os.Getwd() if err != nil { @@ -44,20 +25,13 @@ func FindRepositoryRootDirectory() (string, error) { dir := workDir for dir != "." { - path := filepath.Join(dir, ".git") - fileInfo, err := os.Stat(path) - if err == nil && !fileInfo.IsDir() { - errWorktree := isGitWorktree(path) - if errWorktree != nil { - return "", errWorktree - } - - return dir, nil + gitRepo, err := isGitRootDir(dir) + if err != nil { + return "", err } - if err == nil && fileInfo.IsDir() { + if gitRepo { return dir, nil } - if dir == rootDir { break } @@ -66,3 +40,41 @@ func FindRepositoryRootDirectory() (string, error) { return "", os.ErrNotExist } + +func isGitRootDir(dir string) (bool, error) { + path := filepath.Join(dir, ".git") + fileInfo, err := os.Stat(path) + if err != nil && errors.Is(err, os.ErrNotExist) { + return false, nil + } + if err != nil { + return false, err + } + + if fileInfo.IsDir() { + return true, nil + } + + worktree, err := isGitWorktree(path) + if err != nil { + return false, err + } + + return worktree, nil +} + +func isGitWorktree(path string) (bool, error) { + content, err := os.ReadFile(path) + if err != nil { + return false, fmt.Errorf("failed to read %s: %w", path, err) + } + type gitWorktree struct { + GitDir string `yaml:"gitdir"` + } + var worktree gitWorktree + if err := yaml.Unmarshal(content, &worktree); err != nil { + return false, fmt.Errorf("failed to unmarshall %s: %w", path, err) + } + + return worktree.GitDir != "", nil +} diff --git a/internal/files/repository_test.go b/internal/files/repository_test.go index 0244a637d2..fe2e9a637f 100644 --- a/internal/files/repository_test.go +++ b/internal/files/repository_test.go @@ -13,32 +13,54 @@ import ( "github.com/stretchr/testify/require" ) -func TestRepositoryDirectory(t *testing.T) { - tempDir := t.TempDir() - - gitDir := filepath.Join(tempDir, ".git") - otherDir := filepath.Join(tempDir, "other") - - err := os.MkdirAll(gitDir, 0o755) - require.NoError(t, err) - err = os.MkdirAll(otherDir, 0o755) - require.NoError(t, err) - - err = os.Chdir(otherDir) - require.NoError(t, err) +func TestRepositoryGitDirectory(t *testing.T) { + cases := []struct { + name string + createGit bool + repoDir string + expectedError string + valid bool + }{ + { + name: "git folder present", + createGit: true, + repoDir: t.TempDir(), + expectedError: "", + valid: true, + }, + { + name: "no git folder", + createGit: false, + repoDir: t.TempDir(), + expectedError: "file does not exist", + valid: false, + }, + } - dir, err := FindRepositoryRootDirectory() - require.NoError(t, err) - assert.Equal(t, tempDir, dir) + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + gitWorktreeFile := filepath.Join(c.repoDir, ".git") + otherDir := filepath.Join(c.repoDir, "other") - // test a non repository folder - nonGitDir := t.TempDir() + if c.createGit { + err := os.MkdirAll(gitWorktreeFile, 0o755) + require.NoError(t, err) + } + err := os.MkdirAll(otherDir, 0o755) + require.NoError(t, err) - err = os.Chdir(nonGitDir) - require.NoError(t, err) + err = os.Chdir(otherDir) + require.NoError(t, err) - _, err = FindRepositoryRootDirectory() - assert.ErrorIs(t, err, os.ErrNotExist) + dir, err := FindRepositoryRootDirectory() + if c.valid { + require.NoError(t, err) + assert.Equal(t, c.repoDir, dir) + } else { + assert.ErrorContains(t, err, c.expectedError) + } + }) + } } func TestRepositoryGitWorktree(t *testing.T) { @@ -59,15 +81,31 @@ func TestRepositoryGitWorktree(t *testing.T) { valid: true, }, { - name: "invalid git worktree file", + name: "git worktree file with more fields", createGit: true, contents: "gitdir: /path/to/repo/main\nfoo: bar", repoDir: t.TempDir(), - expectedError: "yaml: unmarshal errors:\n line 2: field foo not found in type files.gitWorktree", + expectedError: "", + valid: true, + }, + { + name: "no gitdir field", + createGit: true, + contents: "", + repoDir: t.TempDir(), + expectedError: "file does not exist", + valid: false, + }, + { + name: "empty gitdir field", + createGit: true, + contents: "gitdir: ''", + repoDir: t.TempDir(), + expectedError: "file does not exist", valid: false, }, { - name: "invalid git worktree file", + name: "no git file nor folder", createGit: false, contents: "", repoDir: t.TempDir(), @@ -96,7 +134,7 @@ func TestRepositoryGitWorktree(t *testing.T) { require.NoError(t, err) assert.Equal(t, c.repoDir, dir) } else { - assert.ErrorContains(t, err, "yaml: unmarshal errors:\n line 2: field foo not found in type files.gitWorktree") + assert.ErrorContains(t, err, c.expectedError) } }) } From 034997e1559e0bb68b25ebcad66bb2ebdc4646b9 Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Mon, 29 Apr 2024 10:55:38 +0200 Subject: [PATCH 4/4] Add internal function to find repository root directory --- internal/files/repository.go | 8 +++++++- internal/files/repository_test.go | 10 ++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/files/repository.go b/internal/files/repository.go index 65a10e2fdc..d281d4e022 100644 --- a/internal/files/repository.go +++ b/internal/files/repository.go @@ -11,6 +11,8 @@ import ( "path/filepath" "gopkg.in/yaml.v3" + + "github.com/elastic/elastic-package/internal/logger" ) func FindRepositoryRootDirectory() (string, error) { @@ -18,7 +20,10 @@ func FindRepositoryRootDirectory() (string, error) { if err != nil { return "", fmt.Errorf("locating working directory failed: %w", err) } + return findRepositoryRootDirectory(workDir) +} +func findRepositoryRootDirectory(workDir string) (string, error) { // VolumeName() will return something like "C:" in Windows, and "" in other OSs // rootDir will be something like "C:\" in Windows, and "/" everywhere else. rootDir := filepath.VolumeName(workDir) + string(filepath.Separator) @@ -30,6 +35,7 @@ func FindRepositoryRootDirectory() (string, error) { return "", err } if gitRepo { + logger.Debugf("Found git repository directory: %q", dir) return dir, nil } if dir == rootDir { @@ -44,7 +50,7 @@ func FindRepositoryRootDirectory() (string, error) { func isGitRootDir(dir string) (bool, error) { path := filepath.Join(dir, ".git") fileInfo, err := os.Stat(path) - if err != nil && errors.Is(err, os.ErrNotExist) { + if errors.Is(err, os.ErrNotExist) { return false, nil } if err != nil { diff --git a/internal/files/repository_test.go b/internal/files/repository_test.go index fe2e9a637f..ab3fa81cfa 100644 --- a/internal/files/repository_test.go +++ b/internal/files/repository_test.go @@ -49,10 +49,7 @@ func TestRepositoryGitDirectory(t *testing.T) { err := os.MkdirAll(otherDir, 0o755) require.NoError(t, err) - err = os.Chdir(otherDir) - require.NoError(t, err) - - dir, err := FindRepositoryRootDirectory() + dir, err := findRepositoryRootDirectory(otherDir) if c.valid { require.NoError(t, err) assert.Equal(t, c.repoDir, dir) @@ -126,10 +123,7 @@ func TestRepositoryGitWorktree(t *testing.T) { err := os.MkdirAll(otherDir, 0o755) require.NoError(t, err) - err = os.Chdir(otherDir) - require.NoError(t, err) - - dir, err := FindRepositoryRootDirectory() + dir, err := findRepositoryRootDirectory(otherDir) if c.valid { require.NoError(t, err) assert.Equal(t, c.repoDir, dir)