Skip to content

Commit 9ca0802

Browse files
cchristousclaude
andcommitted
Address code review feedback
- Cache isGNUTar() result using sync.Once to avoid repeated subprocess calls - Align internal naming: SkipExisting → IgnoreCollisions - Add assertion for restored file content in test Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 206f461 commit 9ca0802

5 files changed

Lines changed: 40 additions & 28 deletions

File tree

cache-cli/cmd/restore.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func RunRestore(cmd *cobra.Command, args []string) {
4242
utils.Check(err)
4343

4444
archiver := archive.NewArchiverWithOptions(metricsManager, archive.ArchiverOptions{
45-
SkipExisting: ignoreCollisions,
45+
IgnoreCollisions: ignoreCollisions,
4646
})
4747

4848
if len(args) == 0 {

cache-cli/pkg/archive/archiver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type Archiver interface {
1212
}
1313

1414
type ArchiverOptions struct {
15-
SkipExisting bool
15+
IgnoreCollisions bool
1616
}
1717

1818
func NewArchiver(metricsManager metrics.MetricsManager) Archiver {

cache-cli/pkg/archive/archiver_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,9 @@ func Test__Compress(t *testing.T) {
298298
// Delete only tempFile2, keep tempFile1 to simulate existing file
299299
assert.NoError(t, os.Remove(tempFile2.Name()))
300300

301-
// Create an archiver with SkipExisting enabled for decompression
301+
// Create an archiver with IgnoreCollisions enabled for decompression
302302
metricsManager := metrics.NewNoOpMetricsManager()
303-
opts := ArchiverOptions{SkipExisting: true}
303+
opts := ArchiverOptions{IgnoreCollisions: true}
304304
var skipArchiver Archiver
305305
switch archiverType {
306306
case "shell-out":
@@ -321,9 +321,10 @@ func Test__Compress(t *testing.T) {
321321
assert.NoError(t, err)
322322
assert.Equal(t, originalContent, content1)
323323

324-
// Verify tempFile2 was restored
325-
_, err = os.Stat(tempFile2.Name())
324+
// Verify tempFile2 was restored with correct content
325+
content2, err := ioutil.ReadFile(tempFile2.Name())
326326
assert.NoError(t, err)
327+
assert.Equal(t, cachedContent, content2)
327328

328329
assert.NoError(t, os.RemoveAll(tempDirBase))
329330
assert.NoError(t, os.Remove(compressedFileName))

cache-cli/pkg/archive/native_archiver.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import (
1717
)
1818

1919
type NativeArchiver struct {
20-
MetricsManager metrics.MetricsManager
21-
UseParallelism bool
22-
SkipExisting bool
20+
MetricsManager metrics.MetricsManager
21+
UseParallelism bool
22+
IgnoreCollisions bool
2323
}
2424

2525
func NewNativeArchiver(metricsManager metrics.MetricsManager, useParallelism bool) *NativeArchiver {
@@ -31,9 +31,9 @@ func NewNativeArchiver(metricsManager metrics.MetricsManager, useParallelism boo
3131

3232
func NewNativeArchiverWithOptions(metricsManager metrics.MetricsManager, useParallelism bool, opts ArchiverOptions) *NativeArchiver {
3333
return &NativeArchiver{
34-
MetricsManager: metricsManager,
35-
UseParallelism: useParallelism,
36-
SkipExisting: opts.SkipExisting,
34+
MetricsManager: metricsManager,
35+
UseParallelism: useParallelism,
36+
IgnoreCollisions: opts.IgnoreCollisions,
3737
}
3838
}
3939

@@ -215,7 +215,7 @@ func (a *NativeArchiver) Decompress(src string) (string, error) {
215215
continue
216216
}
217217

218-
// nil outFile means the file should be skipped (e.g., SkipExisting is enabled)
218+
// nil outFile means the file should be skipped (e.g., IgnoreCollisions is enabled)
219219
if outFile == nil {
220220
// Discard the file contents from the tar reader
221221
if _, err := io.Copy(io.Discard, tarReader); err != nil {
@@ -263,7 +263,7 @@ func (a *NativeArchiver) Decompress(src string) (string, error) {
263263
}
264264

265265
// openFile opens a file for writing during decompression.
266-
// Returns (nil, nil) if the file should be skipped (e.g., when SkipExisting is true and file exists).
266+
// Returns (nil, nil) if the file should be skipped (e.g., when IgnoreCollisions is true and file exists).
267267
func (a *NativeArchiver) openFile(header *tar.Header, tarReader *tar.Reader) (*os.File, error) {
268268
outFile, err := os.OpenFile(header.Name, os.O_RDWR|os.O_CREATE|os.O_EXCL, header.FileInfo().Mode())
269269

@@ -274,8 +274,8 @@ func (a *NativeArchiver) openFile(header *tar.Header, tarReader *tar.Reader) (*o
274274

275275
// Since we are using O_EXCL, this error could mean that the file already exists.
276276
if errors.Is(err, os.ErrExist) {
277-
// If SkipExisting is enabled, skip this file silently.
278-
if a.SkipExisting {
277+
// If IgnoreCollisions is enabled, skip this file silently.
278+
if a.IgnoreCollisions {
279279
return nil, nil
280280
}
281281
// Otherwise, attempt to remove it before opening again.

cache-cli/pkg/archive/shell_out_archiver.go

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"os/exec"
1010
"path/filepath"
1111
"strings"
12+
"sync"
1213

1314
"github.com/semaphoreci/toolbox/cache-cli/pkg/metrics"
1415
log "github.com/sirupsen/logrus"
1516
)
1617

1718
type ShellOutArchiver struct {
18-
metricsManager metrics.MetricsManager
19-
skipExisting bool
19+
metricsManager metrics.MetricsManager
20+
ignoreCollisions bool
2021
}
2122

2223
func NewShellOutArchiver(metricsManager metrics.MetricsManager) *ShellOutArchiver {
@@ -25,8 +26,8 @@ func NewShellOutArchiver(metricsManager metrics.MetricsManager) *ShellOutArchive
2526

2627
func NewShellOutArchiverWithOptions(metricsManager metrics.MetricsManager, opts ArchiverOptions) *ShellOutArchiver {
2728
return &ShellOutArchiver{
28-
metricsManager: metricsManager,
29-
skipExisting: opts.SkipExisting,
29+
metricsManager: metricsManager,
30+
ignoreCollisions: opts.IgnoreCollisions,
3031
}
3132
}
3233

@@ -84,10 +85,10 @@ func (a *ShellOutArchiver) decompressionCmd(dst, tempFile string) *exec.Cmd {
8485
args = append(args, "xzf", tempFile, "-C", ".")
8586
}
8687

87-
// When skipExisting is enabled, skip existing files without overwriting.
88+
// When ignoreCollisions is enabled, skip existing files without overwriting.
8889
// GNU tar uses --skip-old-files (silent, exit 0).
8990
// BSD tar uses -k (silent, exit 0).
90-
if a.skipExisting {
91+
if a.ignoreCollisions {
9192
if isGNUTar() {
9293
args = append(args, "--skip-old-files")
9394
} else {
@@ -98,15 +99,25 @@ func (a *ShellOutArchiver) decompressionCmd(dst, tempFile string) *exec.Cmd {
9899
return exec.Command("tar", args...)
99100
}
100101

102+
var (
103+
gnuTarOnce sync.Once
104+
gnuTarCached bool
105+
)
106+
101107
// isGNUTar returns true if the system tar is GNU tar.
102108
// GNU tar includes "GNU tar" in its --version output.
109+
// The result is cached to avoid repeated subprocess calls.
103110
func isGNUTar() bool {
104-
cmd := exec.Command("tar", "--version")
105-
output, err := cmd.Output()
106-
if err != nil {
107-
return false
108-
}
109-
return strings.Contains(string(output), "GNU tar")
111+
gnuTarOnce.Do(func() {
112+
cmd := exec.Command("tar", "--version")
113+
output, err := cmd.Output()
114+
if err != nil {
115+
gnuTarCached = false
116+
return
117+
}
118+
gnuTarCached = strings.Contains(string(output), "GNU tar")
119+
})
120+
return gnuTarCached
110121
}
111122

112123
func (a *ShellOutArchiver) findRestorationPath(src string) (string, error) {

0 commit comments

Comments
 (0)