Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions go/internal/importer/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ func handleReconcileGit(ctx context.Context, ch chan<- WorkItem, config Config,
Branch: branch,
Force: true,
})
if err != nil {
return nil, err
}

err = wt.Clean(&git.CleanOptions{
Dir: true,
})

return sharedRepo{
Repository: repo,
Expand Down
115 changes: 115 additions & 0 deletions go/internal/importer/git_test.go
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we should even have this test.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package importer

import (
"crypto/sha256"
"encoding/hex"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -253,3 +255,116 @@ func TestHandleImportGit_Deletion(t *testing.T) {
t.Errorf("Expected LastSyncedCommit %s, got %s", commitB.String(), sourceRepo.Git.LastSyncedCommit)
}
}

func TestHandleReconcileGit_CleanUntracked(t *testing.T) {
// Setup a temporary git repo acting as the remote source
remoteDir := t.TempDir()
remoteRepo, err := git.PlainInit(remoteDir, false)
if err != nil {
t.Fatalf("Failed to init remote repo: %v", err)
}
remoteWt, err := remoteRepo.Worktree()
if err != nil {
t.Fatalf("Failed to get remote worktree: %v", err)
}

// Initial commit: one tracked file
if err := os.WriteFile(filepath.Join(remoteDir, "CVE-A.json"), []byte("{}"), 0600); err != nil {
t.Fatalf("Failed to write file: %v", err)
}
if _, err := remoteWt.Add("CVE-A.json"); err != nil {
t.Fatalf("Failed to add file: %v", err)
}
_, err = remoteWt.Commit("Initial", &git.CommitOptions{
Author: &object.Signature{Name: "Test", Email: "test@example.com", When: time.Now()},
})
if err != nil {
t.Fatalf("Failed to commit: %v", err)
}

mockStore := &mockSourceRepositoryStore{
updates: make(map[string]any),
}
mockVulnStore := &mockVulnerabilityStore{
Entries: make(map[string][]*models.VulnSourceRef),
}
workDir := t.TempDir()

config := Config{
SourceRepoStore: mockStore,
VulnerabilityStore: mockVulnStore,
GitWorkDir: workDir,
}

sourceRepo := &models.SourceRepository{
Name: "test-git-repo",
Type: models.SourceRepositoryTypeGit,
Extension: ".json",
Git: &models.SourceRepoGit{
URL: remoteDir,
},
}

// 1. Run reconcile first time to clone the repo
ch := make(chan WorkItem, 10)
err = handleReconcileGit(t.Context(), ch, config, sourceRepo)
if err != nil {
t.Fatalf("handleReconcileGit failed: %v", err)
}
close(ch)

// Consume channel to avoid blocking
for range ch {
continue
}

// Calculate local clone path
sha := sha256.Sum256([]byte(sourceRepo.Git.URL))
localCloneDir := filepath.Join(workDir, hex.EncodeToString(sha[:]))

// Verify the local clone exists and has the tracked file
trackedFilePath := filepath.Join(localCloneDir, "CVE-A.json")
if _, err := os.Stat(trackedFilePath); err != nil {
t.Fatalf("Expected tracked file to exist at %s, got error: %v", trackedFilePath, err)
}

// 2. Create an untracked file in the local clone
untrackedFilePath := filepath.Join(localCloneDir, "untracked.json")
if err := os.WriteFile(untrackedFilePath, []byte("untracked"), 0600); err != nil {
t.Fatalf("Failed to write untracked file: %v", err)
}

// Create an untracked directory with a file
untrackedSubDir := filepath.Join(localCloneDir, "untracked_dir")
if err := os.Mkdir(untrackedSubDir, 0755); err != nil {
t.Fatalf("Failed to create untracked dir: %v", err)
}
untrackedFileInDir := filepath.Join(untrackedSubDir, "file.json")
if err := os.WriteFile(untrackedFileInDir, []byte("untracked"), 0600); err != nil {
t.Fatalf("Failed to write untracked file in dir: %v", err)
}

// 3. Run reconcile again. It should clean up untracked files/dirs.
ch2 := make(chan WorkItem, 10)
err = handleReconcileGit(t.Context(), ch2, config, sourceRepo)
if err != nil {
t.Fatalf("handleReconcileGit failed second time: %v", err)
}
close(ch2)
for range ch2 {
continue
}

// 4. Verify untracked file and dir are GONE
if _, err := os.Stat(untrackedFilePath); !os.IsNotExist(err) {
t.Errorf("Expected untracked file to be deleted, but it still exists (or got error: %v)", err)
}
if _, err := os.Stat(untrackedSubDir); !os.IsNotExist(err) {
t.Errorf("Expected untracked directory to be deleted, but it still exists (or got error: %v)", err)
}

// Verify tracked file still exists
if _, err := os.Stat(trackedFilePath); err != nil {
t.Errorf("Expected tracked file to still exist, got error: %v", err)
}
}
Loading