diff --git a/internal/cmd/cli/apply.go b/internal/cmd/cli/apply.go index 35d3530f2c..f03ff58f6f 100644 --- a/internal/cmd/cli/apply.go +++ b/internal/cmd/cli/apply.go @@ -4,9 +4,10 @@ import ( "bytes" "fmt" "os" - "os/exec" "strings" + gogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ctrl "sigs.k8s.io/controller-runtime" @@ -99,7 +100,7 @@ func (a *Apply) Run(cmd *cobra.Command, args []string) error { func (a *Apply) run(cmd *cobra.Command, args []string) error { labels := a.Label if a.Commit == "" { - a.Commit = currentCommit() + a.Commit = currentCommit(".") } if a.Commit != "" { if labels == nil { @@ -115,7 +116,7 @@ func (a *Apply) run(cmd *cobra.Command, args []string) error { Output: writer.NewDefaultNone(a.Output), Compress: a.Compress, ServiceAccount: a.ServiceAccount, - Labels: a.Label, + Labels: labels, TargetsFile: a.TargetsFile, TargetNamespace: a.TargetNamespace, Paused: a.Paused, @@ -243,15 +244,18 @@ func (a *Apply) addAuthToOpts(opts *apply.Options, readFile readFile, helmBasicH return nil } -func currentCommit() string { - cmd := exec.Command("git", "rev-parse", "HEAD") //nolint:noctx // TODO: refactor to use go-git's ResolveRevision - buf := &bytes.Buffer{} - cmd.Stdout = buf - err := cmd.Run() - if err == nil { - return strings.TrimSpace(buf.String()) +// currentCommit returns the HEAD commit SHA of the git repository +// containing dir, or "" if dir is not inside a git repository. +func currentCommit(dir string) string { + repo, err := gogit.PlainOpenWithOptions(dir, &gogit.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + return "" + } + hash, err := repo.ResolveRevision(plumbing.Revision("HEAD")) + if err != nil { + return "" } - return "" + return hash.String() } // writeTmpKnownHosts creates a temporary file and writes known_hosts data to it, if such data is available from diff --git a/internal/cmd/cli/apply_test.go b/internal/cmd/cli/apply_test.go index 7977213afa..f6eab52448 100644 --- a/internal/cmd/cli/apply_test.go +++ b/internal/cmd/cli/apply_test.go @@ -3,8 +3,13 @@ package cli import ( "errors" "os" + "path/filepath" + "regexp" "testing" + "time" + gogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/google/go-cmp/cmp" "sigs.k8s.io/yaml" @@ -226,3 +231,86 @@ func mockReadFile(name string) ([]byte, error) { return nil, errorNotFound } + +func TestCurrentCommit(t *testing.T) { + t.Run("returns empty string for non-git directory", func(t *testing.T) { + dir := t.TempDir() + got := currentCommit(dir) + if got != "" { + t.Errorf("expected empty string for non-git dir, got %q", got) + } + }) + + t.Run("returns HEAD commit SHA for git repository", func(t *testing.T) { + dir := t.TempDir() + + repo, err := gogit.PlainInit(dir, false) + if err != nil { + t.Fatalf("PlainInit: %v", err) + } + wt, err := repo.Worktree() + if err != nil { + t.Fatalf("Worktree: %v", err) + } + + if err := os.WriteFile(filepath.Join(dir, "README"), []byte("test"), 0600); err != nil { + t.Fatalf("WriteFile: %v", err) + } + if _, err = wt.Add("README"); err != nil { + t.Fatalf("Add: %v", err) + } + expected, err := wt.Commit("initial commit", &gogit.CommitOptions{ + Author: &object.Signature{Name: "test", Email: "test@example.com", When: time.Now()}, + }) + if err != nil { + t.Fatalf("Commit: %v", err) + } + + got := currentCommit(dir) + if got != expected.String() { + t.Errorf("expected SHA %q, got %q", expected.String(), got) + } + if matched, _ := regexp.MatchString(`^[0-9a-f]{40}$`, got); !matched { + t.Errorf("expected a 40-char hex SHA, got %q", got) + } + }) + + t.Run("returns HEAD commit SHA from nested subdirectory", func(t *testing.T) { + dir := t.TempDir() + + repo, err := gogit.PlainInit(dir, false) + if err != nil { + t.Fatalf("PlainInit: %v", err) + } + wt, err := repo.Worktree() + if err != nil { + t.Fatalf("Worktree: %v", err) + } + + if err := os.WriteFile(filepath.Join(dir, "README"), []byte("test"), 0600); err != nil { + t.Fatalf("WriteFile: %v", err) + } + if _, err = wt.Add("README"); err != nil { + t.Fatalf("Add: %v", err) + } + expected, err := wt.Commit("initial commit", &gogit.CommitOptions{ + Author: &object.Signature{Name: "test", Email: "test@example.com", When: time.Now()}, + }) + if err != nil { + t.Fatalf("Commit: %v", err) + } + + subdir := filepath.Join(dir, "some", "nested") + if err := os.MkdirAll(subdir, 0o755); err != nil { + t.Fatalf("MkdirAll: %v", err) + } + + got := currentCommit(subdir) + if got != expected.String() { + t.Errorf("expected SHA %q from nested subdir, got %q", expected.String(), got) + } + if matched, _ := regexp.MatchString(`^[0-9a-f]{40}$`, got); !matched { + t.Errorf("expected a 40-char hex SHA from nested subdir, got %q", got) + } + }) +}