Skip to content

follow symlinks#110

Merged
MusicalNinjaDad merged 15 commits into
mainfrom
follow_symlinks
Nov 18, 2025
Merged

follow symlinks#110
MusicalNinjaDad merged 15 commits into
mainfrom
follow_symlinks

Conversation

@MusicalNinjaDad

@MusicalNinjaDad MusicalNinjaDad commented Nov 18, 2025

Copy link
Copy Markdown
Owner

closes #93

Summary by Sourcery

Add support for following symbolic links when recursively snagging directories and files, including updates to the CLI usage, internal traversal logic, tests, and documentation.

New Features:

  • Enable symlink resolution for both files and directories via a new internal IsDir helper
  • Allow following symlinks during recursive directory traversal

Enhancements:

  • Restructure directory walking logic to use a custom recursive function instead of filepath.WalkDir to handle symlinks correctly
  • Rename internal callbacks (snagit to snagfile) for clarity
  • Introduce unique-file assertion logic to avoid duplicate entries in tests

Documentation:

  • Update README and in-app help to include symlink support and the new --copy flag in usage examples

Tests:

  • Add symlinked test fixtures and corresponding entries in internal testdata
  • Extend assertions to cover symlinked files and directories

Chores:

  • Bump version to 1.2.0

@sourcery-ai

sourcery-ai Bot commented Nov 18, 2025

Copy link
Copy Markdown

Reviewer's Guide

This PR implements recursive symlink-aware traversal by refactoring directory scanning, introduces an IsDir helper to follow symlinks, updates tests and test data for symlinked files, adjusts assertions to handle duplicate entries, updates CLI usage and docs to mention symlink support and adds a --copy flag, and bumps the version to 1.2.0.

Sequence diagram for symlink-aware recursive directory traversal

sequenceDiagram
    participant Caller
    participant "Snaggle()"
    participant "internal.IsDir()"
    participant "snagdir()"
    participant "snagfile()"
    Caller->>"Snaggle()": Call with path
    "Snaggle()"->>"internal.IsDir()": Check if path is directory (follows symlinks)
    alt IsDir returns true
        "Snaggle()"->>"snagdir()": Traverse directory
        loop For each file in directory
            "snagdir()"->>"internal.IsDir()": Check if file is directory (follows symlinks)
            alt IsDir returns true and recursive
                "snagdir()"->>"snagdir()": Recurse into subdirectory
            else IsDir returns false
                "snagdir()"->>"snagfile()": Process file
            end
        end
    else IsDir returns false
        "Snaggle()"->>"snagfile()": Process file
    end
Loading

Class diagram for new and updated directory traversal helpers

classDiagram
    class internal {
        +IsDir(path string) bool
    }
    class Snaggle {
        +Snaggle(path string, root string, opts ...Option) error
    }
    class snagdir {
        +snagdir(dir string) error
    }
    class snagfile {
        +snagfile(path string) error
    }
    Snaggle --> snagdir : uses
    Snaggle --> snagfile : uses
    snagdir --> internal : uses IsDir
    snagdir --> snagfile : uses
Loading

File-Level Changes

Change Details Files
Refactor directory traversal to follow symlinks
  • Rename snagit to snagfile
  • Implement snagdir recursive function with internal.IsDir checks
  • Replace filepath.WalkDir/os.Stat logic with custom recursion
  • Update error wrapping and initial path switch-case
snaggle.go
Introduce IsDir helper to follow symlinks
  • Add IsDir function using os.Stat and EvalSymlinks
  • Import fs and filepath for symlink handling
internal/comparisons.go
Expand test suite with symlink scenarios
  • Add symlinked file entries in testdata.go
  • Define new symlink path constants in testpaths.go
  • Include actual symlinked fixtures under internal/testdata
internal/testing/testdata.go
internal/testpaths.go
Adjust assertions to dedupe entries from symlinks
  • Collect unique file names before matching contents
  • Use map to filter duplicates in DirectoryContents
internal/testing/assertions.go
Update CLI flags and documentation
  • Add --copy option alongside --in-place
  • Note ‘Follows symlinks’ in help text and README
  • Update usage strings to reflect new flag
cmd/snaggle/main.go
README.md
Bump version to 1.2.0
  • Update Version constant from 1.1.2 to 1.2.0
version.go

Assessment against linked issues

Issue Objective Addressed Explanation
#93 Implement recursion into symlinked subdirectories when processing directories.
#93 Update documentation (README, help text) to indicate that symlinks are followed.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Nov 18, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.12500% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.27%. Comparing base (2723337) to head (46573d9).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
snaggle.go 76.47% 2 Missing and 2 partials ⚠️
internal/comparisons.go 72.72% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #110      +/-   ##
==========================================
- Coverage   76.59%   76.27%   -0.32%     
==========================================
  Files          13       13              
  Lines         752      763      +11     
==========================================
+ Hits          576      582       +6     
- Misses        119      123       +4     
- Partials       57       58       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Add guards against symlink cycles in snagdir to avoid infinite recursion when following circular symlinks.
  • internal.IsDir silently ignores errors from os.Stat and EvalSymlinks; propagate or handle errors explicitly to prevent panics or hidden failures.
  • Error handling in the new directory traversal is inconsistent (some errors are returned raw, others wrapped in SnaggleError/InvocationError); consider unifying error wrapping for consistency.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Add guards against symlink cycles in snagdir to avoid infinite recursion when following circular symlinks.
- internal.IsDir silently ignores errors from os.Stat and EvalSymlinks; propagate or handle errors explicitly to prevent panics or hidden failures.
- Error handling in the new directory traversal is inconsistent (some errors are returned raw, others wrapped in SnaggleError/InvocationError); consider unifying error wrapping for consistency.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@MusicalNinjaDad MusicalNinjaDad merged commit d7b3915 into main Nov 18, 2025
12 checks passed
@MusicalNinjaDad MusicalNinjaDad deleted the follow_symlinks branch November 18, 2025 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recurse into symlinked subdirectories

1 participant