Skip to content

Commit

Permalink
🐛 Handle errors for .idea cache and add logging (QD-9927)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiulpin authored and qodana-bot committed Sep 19, 2024
1 parent 3ee6745 commit 17a0c34
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 56 deletions.
140 changes: 98 additions & 42 deletions core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,51 +382,107 @@ func Test_createUser(t *testing.T) {

func Test_syncIdeaCache(t *testing.T) {
tmpDir := filepath.Join(os.TempDir(), "cache")
tc := "NotExist"
syncIdeaCache(filepath.Join(tmpDir, "1"), filepath.Join(tmpDir, "2"), true)
if _, err := os.Stat(filepath.Join(tmpDir, "2")); err == nil {
t.Errorf("Case: %s: Folder dst created, when it should not", tc)
}

tc = "NoOverwrite"
err := os.MkdirAll(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.MkdirAll(filepath.Join(tmpDir, "2", ".idea", "dir1"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "file1"), []byte("test1"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"), []byte("test2"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"), []byte("test!"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
syncIdeaCache(filepath.Join(tmpDir, "1"), filepath.Join(tmpDir, "2"), false)
if _, err := os.Stat(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2")); os.IsNotExist(err) {
t.Errorf("Case: %s: Resulting folder .idea/dir1/dir2 not found", tc)
}
got, err := os.ReadFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"))
if err != nil || string(got) != "test!" {
t.Errorf("Case: %s: Got: %s\n Expected: test!", tc, got)
}
t.Run("NotExist", func(t *testing.T) {
err := syncIdeaCache(filepath.Join(tmpDir, "1"), filepath.Join(tmpDir, "2"), true)
if err == nil {
t.Errorf("Expected error when source folder does not exist")
}
if _, err := os.Stat(filepath.Join(tmpDir, "2")); err == nil {
t.Errorf("Folder dst created, when it should not")
}
})

tc = "Overwrite"
syncIdeaCache(filepath.Join(tmpDir, "2"), filepath.Join(tmpDir, "1"), true)
got, err = os.ReadFile(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"))
if err != nil || string(got) != "test!" {
t.Errorf("Case: %s: Got: %s\n Expected: test!", tc, got)
}
t.Run("NoOverwrite", func(t *testing.T) {
err := os.MkdirAll(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.MkdirAll(filepath.Join(tmpDir, "2", ".idea", "dir1"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "file1"), []byte("test1"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"), []byte("test2"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"), []byte("test!"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}

err = os.RemoveAll(tmpDir)
if err != nil {
err = syncIdeaCache(filepath.Join(tmpDir, "1"), filepath.Join(tmpDir, "2"), false)
if err != nil {
t.Fatalf("syncIdeaCache failed: %v", err)
}

if _, err := os.Stat(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2")); os.IsNotExist(err) {
t.Errorf("Resulting folder .idea/dir1/dir2 not found")
}
got, err := os.ReadFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"))
if err != nil || string(got) != "test!" {
t.Errorf("Got: %s\n Expected: test!", string(got))
}
})

t.Run("Overwrite", func(t *testing.T) {
err := syncIdeaCache(filepath.Join(tmpDir, "2"), filepath.Join(tmpDir, "1"), true)
if err != nil {
t.Fatalf("syncIdeaCache failed: %v", err)
}

got, err := os.ReadFile(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"))
if err != nil || string(got) != "test!" {
t.Errorf("Got: %s\n Expected: test!", string(got))
}
})

t.Run("HasSymlinksAndFileExistsInDst", func(t *testing.T) {
err := os.MkdirAll(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.MkdirAll(filepath.Join(tmpDir, "2", ".idea", "dir1"), os.FileMode(0o755))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "file1"), []byte("test1"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"), []byte("test2"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"), []byte("test!"), os.FileMode(0o600))
if err != nil {
t.Fatal(err)
}

err = os.Symlink(filepath.Join(tmpDir, "1", ".idea", "dir1", "file2"), filepath.Join(tmpDir, "1", ".idea", "dir1", "symlink"))
if err != nil {
t.Fatal(err)
}

err = syncIdeaCache(filepath.Join(tmpDir, "1"), filepath.Join(tmpDir, "2"), false)
if err != nil {
t.Fatalf("syncIdeaCache failed: %v", err)
}

if _, err := os.Stat(filepath.Join(tmpDir, "1", ".idea", "dir1", "dir2")); os.IsNotExist(err) {
t.Errorf("Resulting folder .idea/dir1/dir2 not found")
}
got, err := os.ReadFile(filepath.Join(tmpDir, "2", ".idea", "dir1", "file2"))
if err != nil || string(got) != "test!" {
t.Errorf("Got: %s\n Expected: test!", string(got))
}
})

if err := os.RemoveAll(tmpDir); err != nil {
t.Fatal(err)
}
}
Expand Down
37 changes: 23 additions & 14 deletions core/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ func GetIdeArgs(opts *QodanaOptions) []string {

// postAnalysis post-analysis stage: wait for FUS stats to upload
func postAnalysis(opts *QodanaOptions) {
syncIdeaCache(opts.ProjectDir, opts.CacheDir, true)
err := syncIdeaCache(opts.ProjectDir, opts.CacheDir, true)
if err != nil {
log.Warnf("failed to sync .idea directory: %v", err)
}
syncConfigCache(opts, false)
for i := 1; i <= 600; i++ {
if findProcess("statistics-uploader") {
Expand Down Expand Up @@ -307,7 +310,10 @@ func prepareLocalIdeSettings(opts *QodanaOptions) {
writeProperties(opts)

if platform.IsContainer() {
syncIdeaCache(opts.CacheDir, opts.ProjectDir, false)
err := syncIdeaCache(opts.CacheDir, opts.ProjectDir, false)
if err != nil {
log.Warnf("failed to sync .idea directory: %v", err)
}
syncConfigCache(opts, true)
createUser("/etc/passwd")
}
Expand Down Expand Up @@ -434,26 +440,29 @@ func syncConfigCache(opts *QodanaOptions, fromCache bool) {
}

// syncIdeaCache sync .idea/ content from cache and back.
func syncIdeaCache(from string, to string, overwrite bool) {
opt := cp.Options{}
if overwrite {
opt.OnDirExists = func(src, dest string) cp.DirExistsAction {
return cp.Merge
}
} else {
opt.OnDirExists = func(src, dest string) cp.DirExistsAction {
func syncIdeaCache(from string, to string, overwrite bool) error {
copyOptions := cp.Options{
OnDirExists: func(src, dest string) cp.DirExistsAction {
if overwrite {
return cp.Merge
}
return cp.Untouchable
}
},
OnSymlink: func(src string) cp.SymlinkAction {
return cp.Skip
},
}
src := filepath.Join(from, ".idea")
if _, err := os.Stat(src); os.IsNotExist(err) {
return
return fmt.Errorf("source .idea directory does not exist: %s", src)
}
dst := filepath.Join(to, ".idea")
log.Printf("Sync IDE cache from: %s to: %s", src, dst)
if err := cp.Copy(src, dst, opt); err != nil {
log.Fatal(err)
if err := cp.Copy(src, dst, copyOptions); err != nil {
return fmt.Errorf("failed to sync .idea directory: %w", err)
}

return nil
}

//goland:noinspection GoBoolExpressions
Expand Down

0 comments on commit 17a0c34

Please sign in to comment.