Snag a directory#67
Merged
Merged
Conversation
Reviewer's GuideRefactored the Snaggle entry point to support directory inputs by extracting the file-handling logic into a private helper and added corresponding tests while cleaning up obsolete test data. Class diagram for refactored Snaggle entry point and helperclassDiagram
class Snaggle {
+Snaggle(path string, root string, opts ...option) error
}
class snaggle {
+snaggle(path string, binDir string, libDir string, options options) error
}
Snaggle --> snaggle : calls
class options {
<<struct>>
}
class option {
<<type>>
}
class elf {
+New(path string) (*elf, error)
}
snaggle --> elf : uses
Snaggle --> options : uses
Snaggle --> option : uses
snaggle --> options : uses
Flow diagram for directory handling in Snaggleflowchart TD
A["Snaggle(path, root, opts...)"] --> B["os.Stat(path)"]
B --> C{IsDir?}
C -- Yes --> D["os.ReadDir(path)"]
D --> E["for each file in directory"]
E --> F{file.IsDir?}
F -- No --> G["snaggle(filePath, binDir, libDir, options)"]
G --> H{error}
H -- nil or FormatError --> E
H -- other error --> I["return error"]
F -- Yes --> E
C -- No --> J["snaggle(path, binDir, libDir, options)"]
J --> K["return"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Closed
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #67 +/- ##
==========================================
+ Coverage 75.05% 75.42% +0.36%
==========================================
Files 8 8
Lines 449 472 +23
==========================================
+ Hits 337 356 +19
- Misses 80 83 +3
- Partials 32 33 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Hey there - I've reviewed your changes - here's some feedback:
- In the loop over directory entries, the local variable
pathshadows the outer parameter—consider renaming it (e.g.entryPath) to avoid confusion. - The implementation only scans the immediate directory and skips nested directories—if you need to support deeper hierarchies, consider recursing into subfolders or using filepath.WalkDir for a more robust traversal.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the loop over directory entries, the local variable `path` shadows the outer parameter—consider renaming it (e.g. `entryPath`) to avoid confusion.
- The implementation only scans the immediate directory and skips nested directories—if you need to support deeper hierarchies, consider recursing into subfolders or using filepath.WalkDir for a more robust traversal.
## Individual Comments
### Comment 1
<location> `snaggle_test.go:99` </location>
<code_context>
}
}
+
+func TestDirectory(t *testing.T) {
+ var stdout strings.Builder
+ log.SetOutput(&stdout)
</code_context>
<issue_to_address>
**suggestion (testing):** Missing test for error conditions when processing a directory.
Please add tests for cases like os.ReadDir failure, unreadable files, and snaggle errors to cover error handling in directory processing.
Suggested implementation:
```golang
func TestDirectory(t *testing.T) {
var stdout strings.Builder
log.SetOutput(&stdout)
t.Cleanup(func() { log.SetOutput(os.Stdout) })
Assert := assert.New(t)
tmp := WorkspaceTempDir(t)
contents := CommonBinaries(t)
dir := TestdataPath(".")
inplace := false
var expectedOut []string
// Test: os.ReadDir failure
t.Run("ReadDirError", func(t *testing.T) {
// Simulate ReadDir error by passing a non-existent directory
_, err := os.ReadDir("/nonexistent-directory-should-fail")
Assert.Error(err, "expected error when reading non-existent directory")
})
// Test: unreadable file in directory
t.Run("UnreadableFile", func(t *testing.T) {
unreadableFile := filepath.Join(tmp, "unreadable.txt")
err := os.WriteFile(unreadableFile, []byte("data"), 0000)
Assert.NoError(err)
defer os.Remove(unreadableFile)
f, err := os.Open(unreadableFile)
if f != nil {
f.Close()
}
Assert.Error(err, "expected error when opening unreadable file")
})
// Test: snaggle error simulation
t.Run("SnaggleError", func(t *testing.T) {
// Simulate a snaggle error by calling a function that returns an error
err := errors.New("snaggle error")
Assert.Error(err, "expected snaggle error")
})
}
```
- If your directory processing logic is encapsulated in a function (e.g., `ProcessDirectory`), you should call that function in the error tests and mock its dependencies to trigger the errors.
- You may need to adjust the error simulation to match your actual error handling and logging mechanisms.
- Ensure that the error cases are covered in your main directory processing function, not just in the test scaffolding.
</issue_to_address>
### Comment 2
<location> `snaggle.go:158` </location>
<code_context>
binDir := filepath.Join(root, "bin")
libDir := filepath.Join(root, "lib64")
+ stat, err := os.Stat(path)
+ if err != nil {
+ return err
</code_context>
<issue_to_address>
**issue (complexity):** Consider replacing manual directory traversal and recursion with filepath.WalkDir to simplify file iteration and error handling.
Consider replacing the custom recursion in Snaggle + snaggle with Go’s filepath.WalkDir. This flattens the logic, kills the helper, and handles directories/files in one place:
```go
import (
"debug/elf"
"errors"
"io/fs"
"path/filepath"
"golang.org/x/sync/errgroup"
)
// Snaggle walks src (file or dir), parses each ELF, skips non-ELF,
// and links executables into bin/ or libs into lib64/ under root.
func Snaggle(src, root string, opts ...option) error {
// 1. apply options as before
var options options
for _, o := range opts {
o(&options)
}
binDir := filepath.Join(root, "bin")
libDir := filepath.Join(root, "lib64")
g := new(errgroup.Group)
// 2. walk everything under src
err := filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
// 3. open ELF, skip non-ELF
f, err := elf.New(path)
if err != nil {
var fe *elf.FormatError
if errors.As(err, &fe) {
return nil
}
return err
}
// 4. no-op for inplace
if options.inplace {
return nil
}
// 5. enqueue link
dst := libDir
if f.IsExe() {
dst = binDir
}
p := path // capture
g.Go(func() error { return link(p, dst) })
return nil
})
if err != nil {
return err
}
// 6. wait for all links
return g.Wait()
}
```
Steps:
- Remove `os.Stat`/`os.ReadDir` and the separate `snaggle` helper.
- Use `filepath.WalkDir` to iterate files.
- Inline ELF-open + skip-FormatError logic.
- Use one errgroup for parallel links.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
b63459d to
65ae5be
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
Extend Snaggle to handle directories by iterating over contained files and apply the existing file processing logic via a new helper function
New Features:
Enhancements:
Tests: