Skip to content

Commit f44528b

Browse files
zlavclaude
andauthored
ANE-1036: Glob file matching for exclusion filters in .fossa.yml (#1703)
* ANE-1036: Support glob patterns in .fossa.yml path filters `paths.only` and `paths.exclude` entries that contain `*`, `?`, or `[` are now parsed as System.FilePattern globs via the existing Data.Glob wrapper. Entries without glob metacharacters keep their prior "directory and all children" semantics, so this change is backward-compatible. Adds a PathFilter sum type at the config layer, threads a parallel list of glob patterns through FilterCombination, and extends pathAllowed / applyComb to include-or-exclude directories whose relative path matches a glob. Matching normalizes the trailing slash that Path.toString appends to Dir paths so patterns like `node_modules/*` match as users expect. Docs and fossa-yml.v3.schema.json updated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix Windows glob path matching and link changelog entry Normalize backslashes to forward slashes before glob matching so user-supplied patterns like `node_modules/*` match the backslash- separated paths produced by `Path Rel Dir` on Windows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add glob filter test coverage for ?, character classes, root-level globs Cover '?' wildcards, '[...]' character classes, root-anchored single-segment globs, an explicit trailing-slash normalization regression guard, and a four-way mix of include/exclude globs and concrete paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Document System.FilePattern '?'/'[...]' literal-match limitation in tests System.FilePattern only implements `*` and `**`; `?` and `[...]` are matched literally rather than as wildcards/character classes. The two new tests asserted wildcard semantics and were red on CI. Flip the expectations so they document the actual behavior and serve as a regression guard if the engine ever gains those features. * Normalize backslashes in glob patterns; correct ?/[] doc semantics Extend the Windows portability fix from db3921b (which normalized the path side) to also normalize the user-supplied pattern side. A Windows user typing `node_modules\*` in `.fossa.yml` now gets the same glob as `node_modules/*`. The shared normalization is lifted into a top-level `normalizeSlashes` helper used by both `FromJSON PathFilter` and `globMatchesDir`. Also correct the glob-pattern documentation: it previously claimed `?` matches a single character, but `System.FilePattern` only implements `*` and `**` (test/Discovery/FiltersSpec.hs already asserts this). The doc now says `?` and `[...]` are matched as literals and notes that backslashes in patterns are normalized. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Limit glob trigger to `*`; drop unreachable `?`/`[]` literal tests System.FilePattern only implements `*` and `**` — `?` and `[...]` are matched as literal characters, not single-character wildcards or character classes (per commit 51f4f66). Routing strings containing `?` or `[` to the glob branch was therefore a no-op for matching, and on Windows it silently swallowed `parseRelDir` errors for `?` (a reserved NTFS character) by producing a glob that could never match a real path. Shrink the trigger to a simple `*`-in-string check inline. Strings with `?` or `[` now go through `parseRelDir` like any other concrete path. The two `FiltersSpec` tests that documented FilePattern's literal handling of `?`/`[]` go away — they tested an internal-engine quirk that is no longer reachable through the user-facing config. Doc + JSON schema updated to match (and the `*` description is fixed: "any sequence of characters within a single path segment", not the inaccurate "a single path segment"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Doc: show concrete example directories for each glob pattern Add a short bulleted list under the YAML example illustrating what each pattern actually matches in a real tree (deep Go vendoring, a scoped npm package's transitive plugin tree, and a generated-proto subtree). The list also calls out that `build/generated/*` is anchored at the root and that the walker prunes the matched directory's whole subtree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Echo active path filters at analyze startup Walker-level prunes from `paths.only`/`paths.exclude` short-circuit discovery before any strategy sees the excluded directory, so the user gets no log line and no "Skipping ..." trail telling them why a project they expected didn't appear in the analyze summary. The existing post-summary note even points at `fossa list-targets` as a workaround, but that command deliberately ignores all filters. Add a small `logActivePathFilters` helper invoked once at the start of `analyze` that prints the configured include/exclude paths and globs (skipping empty kinds). Output for a `.fossa.yml` containing `paths.exclude: ["**/zip/**"]` is now: [INFO] Active exclude glob filters: **/zip/** Per-prune logging would be more direct but requires propagating `Has Logger sig m` through `walkWithFilters'`, `simpleDiscover`, and every strategy's `findProjects`/`discover` (~35 files). Saving that for a follow-up; this gives the user the single piece of information needed to map a missing project back to a configured filter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Surface walker-pruned subtrees and add walker filter test coverage Walker-level path-filter prunes were previously silent: pathFilterIntercept short-circuits before any strategy reaches the directory, and there was no log trail explaining why a project the user expected didn't appear in the analyze summary. The post-summary note even pointed at `fossa list-targets` as a workaround, which deliberately ignores all filters. Wire `Has Logger sig m` through `walkWithFilters'` and `pathFilterIntercept` so the walker can speak. Per-prune log lines fire at debug level (one per strategy, ~28 strategies = noisy at info). Add `enumeratePrunedSubtrees`, a one-shot pre-discovery walk that returns the list of subtrees the filter will reject; analyze invokes it once before strategies run and logs each pruned path at info level. Result for a `.fossa.yml` with `paths.exclude: ["**/zip/**"]`: Active exclude glob filters: **/zip/** Skipping path "zip/" (excluded by paths filter) The Has Logger ripple touches every strategy that uses walkWithFilters' (~32 single-line constraint sites, ~7 multi-line). Each carrier already provides Logger via DiscoverTaskEffs, so the change is purely a constraint propagation — no new effects, no runtime cost. Add three Walker spec tests (test/Discovery/WalkSpec.hs): - include-path filter: mirror of the existing exclude test, asserts the walker accepts ancestors + included subtree and prunes siblings. - WalkSkipSome merge: strategy returns WalkSkipSome ["a"], filter excludes "b", both should be pruned. Catches the `pathFilterIntercept`/`skipDisallowed` merge logic. - YAML-to-walker end-to-end: parses a YAML config string with a glob exclude, runs it through `collectConfigFileFilters`, executes the walker, asserts pruning. Catches "globs parse but never reach the walker" wiring regressions — the exact class of bug we hit earlier. Cannot exercise the new tests locally because the test binary's startup reads test/Container/testdata/emptypath.tar (a git-LFS pointer not materialized in this environment); CI's Linux/macOS/Windows jobs will validate. Library and test-binary builds pass with no warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert Has Logger walker propagation; gate prune-enumeration walk Per-prune `logDebug` events from `pathFilterIntercept` were the only thing the 39-file `Has Logger sig m` ripple bought us. The user-facing value — "each pruned subtree shows up once at info" — comes from `enumeratePrunedSubtrees` + `logPrunedSubtrees` in `App.Fossa.Analyze`, and that path doesn't need the constraint propagated: `walk'` works without `Logger`, and the logging happens in `analyze` where Logger was always in scope. Revert the propagation in `walkWithFilters'` and `pathFilterIntercept` back to the prior `Applicative m, Monoid o` shape, and revert all 39 strategy files (and their `Effect.Logger` import additions) to master. Keep `enumeratePrunedSubtrees`, `logPrunedSubtrees`, and the "Active filters" startup line — they're the user-visible win. Add a no-filters short-circuit to `logPrunedSubtrees` so we don't pay for an extra walk when the user has no `paths.only`/`paths.exclude` entries configured. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix hlint hits in FiltersSpec backslash-normalization test CI's hlint job flagged two restricted patterns I introduced: - Text.pack should be toText (project-wide convention). - bare error is a restricted function in this codebase. Replace Text.pack with toText, and change the failure branch from error to Nothing so parse returns Maybe PathFilter. The shouldBe assertion still works: it now compares two Maybe PathFilter values, both of which are Just _ for valid input. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix duplicate viaShow import in Analyze.hs CI's -Werror=unused-imports flagged the existing `Prettyprinter.viaShow` as unused because the same name was also imported (and used) from Effect.Logger when I added it for logPrunedSubtrees. Drop my Effect.Logger.viaShow addition; the Prettyprinter one was already in scope and is what's actually used. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Condense changelog entry for 3.17.6 to one line Drop the long-form description of glob semantics and the analyze visibility entry; "now accept glob patterns" plus the PR link gives readers everything they need, and full docs live in docs/references/files/fossa-yml.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address CodeRabbit review: include-glob reachability + JSON shape + filter scope Three issues from CodeRabbit: 1. Startup logs used `filters`, but discovery uses `discoveryFilters` (which is `mempty` under `--no-discovery-exclusion`). Compute `discoveryFilters` once near the top of `analyze` and use it for `logActivePathFilters` / `logPrunedSubtrees` so the log line agrees with what discovery actually applies. 2. `FilterCombination`'s ToJSON used `genericToEncoding defaultOptions`, which emits the new `_combinedPathGlobs` field as `[]` even when no globs are configured — so every serialized payload changes shape vs pre-glob-support runs. Replace with a hand-written `toEncoding` that omits the field when the list is empty. 3. Include globs only matched the directory itself; ancestors-on-the-way to a match and descendants-after-a-match weren't allowed. So `paths.only: ["apps/*"]` rejected `apps/`, the walker never descended, and every project under `apps/` was silently dropped. Add `isParentOfIncludedGlob` (path's segments are a prefix of the glob's literal directory prefix — segments before the first `*`) and `isChildOfIncludedGlob` (any proper ancestor of `path` matches a glob) to `pathAllowed`. Cover both with new tests in `FiltersSpec.hs`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Allow any path as ancestor when include-glob's literal prefix is empty The new "treats a leading '**' include glob as accepting any ancestor" test was failing on CI. With include `**/service/**`, the walker has no idea which subtree contains a `service/` directory and must descend everywhere on the way down. My initial pathSegmentsPrefixOf check returned False whenever the path was non-empty and the glob's literal directory prefix was empty, so the walker refused to descend at all. Fix: when the literal prefix is empty (because the glob's first segment is wildcarded — `**`, `*`, `*foo`, etc.), accept any path as a candidate ancestor. The actual match still has to fire via isIncludedByGlob when the walker reaches a real match. Costs extra walking when the user writes a leading-wildcard include, but that's the only correct behavior — there's no way to know up front where a match lives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address CodeRabbit nitpicks: stdlib isPrefixOf + drop list comprehension Two style fixes from CodeRabbit's second review: 1. `pathSegmentsPrefixOf`'s local `isPrefixOfList` was a hand-rolled reimplementation of `Data.List.isPrefixOf`. Drop the where-clause and import the standard one (Data.List was already imported). 2. `properAncestors` used a list comprehension to build prefix lists, which the project's coding guidelines disallow ("avoid partial functions, list comprehensions, and match guards"). Replace with `map (`take` segs) [1 .. length segs - 1]`. Both behavior-equivalent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop logActivePathFilters per review feedback Reviewer flagged the startup "Active <kind> filters: ..." log lines as redundant with the user's own .fossa.yml — and at info level, more noise than signal. The actually-useful info is "what got pruned in this run," which logPrunedSubtrees already provides. Remove logActivePathFilters and its call site, plus the imports it was the only consumer of (Data.Glob.unGlob, Data.Text, Text.intercalate). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Use unsnoc instead of double-reverse in trimTrailingSlash Per review feedback. The original `case reverse s of '/' : rest -> reverse rest` walks the list twice and allocates an intermediate reversed copy. Replacing with an unsnoc-based version traverses once. Project supports `base >= 4.15` and `Data.List.unsnoc` is base-4.19+ (GHC 9.8), so I can't import it directly. Inline a tiny helper that matches the stdlib implementation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Rename glob ancestor-reachability test + clarify the comment Match the existing concrete-path "should include all parents" naming convention, and rewrite the comment so it's clear pathAllowed is the walker's traversal predicate (not a "this path matches the filter" check). The previous wording made it look like the assertions were about glob matches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 56cc319 commit f44528b

10 files changed

Lines changed: 558 additions & 28 deletions

File tree

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
## 3.17.6
44

5+
- Config: `paths.only` and `paths.exclude` in `.fossa.yml` now accept glob patterns. ([#1703](https://github.com/fossas/fossa-cli/pull/1703))
56
- Licensing - Fix two bad GPL matches [No PR]
67

8+
79
## 3.17.5
810

911
- Vendetta: Debug bundles now include per-file component match data from Vendetta scans, making it easier to diagnose why a vendored dependency was or wasn't detected. ([#1706](https://github.com/fossas/fossa-cli/pull/1706))

docs/references/files/fossa-yml.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,28 @@ The list of paths to exclude from scanning in your directory.
334334

335335
This section is intended to be used as the inverse to `paths.only`. If you have a certain directory such as `development` you wish to exclude, `paths.exclude` enables you to do this.
336336

337+
#### Glob patterns
338+
339+
Entries in `paths.only` and `paths.exclude` may also be glob patterns. An entry is treated as a glob if it contains `*`; other entries keep their existing semantics (match the directory and all of its children). Glob matching follows [`System.FilePattern`][filepattern] semantics: `*` matches any sequence of characters within a single path segment, and `**` matches any number of segments.
340+
341+
Patterns use forward slashes (`/`) as path separators; backslashes are normalized so Windows-native patterns also work.
342+
343+
```yaml
344+
paths:
345+
exclude:
346+
- "**/vendor/**"
347+
- "**/node_modules/**"
348+
- "build/generated/*"
349+
```
350+
351+
Each example above excludes a different shape of directory:
352+
353+
- `**/vendor/**` skips Go-style vendored trees at any depth, e.g. `services/billing/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/`.
354+
- `**/node_modules/**` skips installed npm packages wherever they appear, e.g. `apps/web-frontend/node_modules/@babel/preset-env/lib/plugins/syntax-dynamic-import/`.
355+
- `build/generated/*` is anchored at the repo root and matches *direct* children of `build/generated/` only. `build/generated/proto-go/` matches; the walker then prunes its entire subtree (e.g. `build/generated/proto-go/v1/messagepb/`).
356+
357+
[filepattern]: https://hackage.haskell.org/package/filepattern
358+
337359
### Analysis target configuration
338360
Analysis target configuration allows you to select a very specific subset of your directory for scanning. The `targets` and `paths` sections allow users to configure which targets and directories should be scanned. This is useful if you have a custom test directory or development projects within the root project.
339361

docs/references/files/fossa-yml.v3.schema.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,18 +528,18 @@
528528
},
529529
"paths": {
530530
"type": "object",
531-
"description": "The paths filtering section allows you to specify which paths should be scanned and which should not. The paths should be listed as their location from the root of your project.",
531+
"description": "The paths filtering section allows you to specify which paths should be scanned and which should not. Entries may be concrete directory paths (which match the directory and all of its children) or glob patterns. An entry is treated as a glob if it contains `*`. Glob syntax follows System.FilePattern: `*` matches any sequence of characters within a single path segment, and `**` matches any number of segments.",
532532
"properties": {
533533
"only": {
534534
"type": "array",
535-
"description": "The list of paths to only allow scanning within.",
535+
"description": "The list of paths or glob patterns to only allow scanning within.",
536536
"items": {
537537
"type": "string"
538538
}
539539
},
540540
"exclude": {
541541
"type": "array",
542-
"description": "The list of paths to exclude from scanning in your directory.",
542+
"description": "The list of paths or glob patterns to exclude from scanning in your directory.",
543543
"items": {
544544
"type": "string"
545545
}

src/App/Fossa/Analyze.hs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ import Data.Aeson qualified as Aeson
121121
import Data.ByteString.Lazy qualified as BL
122122
import Data.Error (createBody)
123123
import Data.Flag (Flag, fromFlag)
124-
import Data.Foldable (traverse_)
124+
import Data.Foldable (for_, traverse_)
125125
import Data.Functor (($>))
126126
import Data.List.NonEmpty qualified as NE
127127
import Data.Map qualified as Map
@@ -132,8 +132,9 @@ import Data.Traversable (for)
132132
import Diag.Diagnostic as DI
133133
import Diag.Result (resultToMaybe)
134134
import Discovery.Archive qualified as Archive
135-
import Discovery.Filters (AllFilters, MavenScopeFilters, applyFilters, filterIsVSIOnly, ignoredPaths, isDefaultNonProductionPath)
135+
import Discovery.Filters (AllFilters (..), MavenScopeFilters, applyFilters, combinedPathGlobs, combinedPaths, filterIsVSIOnly, ignoredPaths, isDefaultNonProductionPath)
136136
import Discovery.Projects (withDiscoveredProjects)
137+
import Discovery.Walk (enumeratePrunedSubtrees)
137138
import Effect.Exec (Exec)
138139
import Effect.Logger (
139140
Logger,
@@ -297,6 +298,32 @@ runAnalyzers allowedTactics filters withoutDefaultFilters basedir pathPrefix = d
297298
where
298299
single (DiscoverFunc f) = withDiscoveredProjects f basedir (runDependencyAnalysis basedir filters withoutDefaultFilters pathPrefix allowedTactics)
299300

301+
-- | Walk the tree once at startup and surface every directory the path
302+
-- filters will prune. Each prune is logged once at info level here, instead
303+
-- of emitting per-strategy duplicates from inside the walker (~28 strategies
304+
-- would otherwise each report the same prune). Short-circuits when no path
305+
-- filters are configured so the extra walk is only paid for when it can
306+
-- produce output.
307+
logPrunedSubtrees ::
308+
( Has Logger sig m
309+
, Has ReadFS sig m
310+
, Has Diag.Diagnostics sig m
311+
) =>
312+
AllFilters ->
313+
Path Abs Dir ->
314+
m ()
315+
logPrunedSubtrees filters basedir =
316+
unless (noPathFilters filters) $ do
317+
pruned <- enumeratePrunedSubtrees filters basedir
318+
for_ pruned $ \p ->
319+
logInfo $ "Skipping path " <> viaShow p <> " (excluded by paths filter)"
320+
where
321+
noPathFilters AllFilters{includeFilters = i, excludeFilters = e} =
322+
null (combinedPaths i)
323+
&& null (combinedPathGlobs i)
324+
&& null (combinedPaths e)
325+
&& null (combinedPathGlobs e)
326+
300327
analyze ::
301328
( Has Debug sig m
302329
, Has Diag.Diagnostics sig m
@@ -331,6 +358,12 @@ analyze cfg = Diag.context "fossa-analyze" $ do
331358
withoutDefaultFilters = Config.withoutDefaultFilters cfg
332359
enableSnippetScan = Config.snippetScan cfg
333360
enableVendetta = Config.xVendetta cfg
361+
-- Discovery runs with `mempty` when `--no-discovery-exclusion` is set
362+
-- (see definition further down). Log against the same filter set so the
363+
-- startup output matches what discovery actually applies.
364+
discoveryFilters = if fromFlag NoDiscoveryExclusion noDiscoveryExclusion then mempty else filters
365+
366+
logPrunedSubtrees discoveryFilters basedir
334367

335368
manualDepsResult <-
336369
Diag.errorBoundaryIO . diagToDebug $
@@ -434,7 +467,6 @@ analyze cfg = Diag.context "fossa-analyze" $ do
434467
pure Nothing
435468
else Diag.context "first-party-scans" . runStickyLogger SevInfo $ runFirstPartyScan basedir maybeApiOpts cfg
436469
let firstPartyScanResults = join . resultToMaybe $ maybeFirstPartyScanResults
437-
let discoveryFilters = if fromFlag NoDiscoveryExclusion noDiscoveryExclusion then mempty else filters
438470
let strategyCfg =
439471
(Config.strategyConfig cfg)
440472
{ Config.useGitBackedCargoLocators = Config.UseGitBackedCargoLocators $ maybe True orgSupportsGitBackedCargoLocators orgInfo

src/App/Fossa/Config/Common.hs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ import Data.String (IsString)
120120
import Data.String.Conversion (ToText (toText))
121121
import Data.Text (Text, null, strip, toLower)
122122
import Diag.Result (Result (Failure, Success), renderFailure)
123-
import Discovery.Filters (AllFilters (AllFilters), MavenScopeFilters (..), comboExclude, comboInclude, setExclude, setInclude, targetFilterParser)
123+
import Discovery.Filters (AllFilters (AllFilters), MavenScopeFilters (..), comboExcludeWithGlobs, comboIncludeWithGlobs, partitionPathFilters, setExclude, setInclude, targetFilterParser)
124124
import Effect.Exec (Exec)
125125
import Effect.Logger (Logger, logDebug, logInfo, renderIt, vsep)
126126
import Effect.ReadFS (ReadFS, doesDirExist, doesFileExist)
@@ -624,11 +624,13 @@ collectConfigFileFilters configFile = do
624624
let pullFromFile :: (a -> [b]) -> (ConfigFile -> Maybe a) -> [b]
625625
pullFromFile field section = maybe [] field (section configFile)
626626
onlyT = pullFromFile targetsOnly configTargets
627-
onlyP = pullFromFile pathsOnly configPaths
627+
(onlyP, onlyG) = partitionPathFilters $ pullFromFile pathsOnly configPaths
628628
excludeT = pullFromFile targetsExclude configTargets
629-
excludeP = pullFromFile pathsExclude configPaths
629+
(excludeP, excludeG) = partitionPathFilters $ pullFromFile pathsExclude configPaths
630630

631-
AllFilters (comboInclude onlyT onlyP) (comboExclude excludeT excludeP)
631+
AllFilters
632+
(comboIncludeWithGlobs onlyT onlyP onlyG)
633+
(comboExcludeWithGlobs excludeT excludeP excludeG)
632634

633635
collectConfigMavenScopeFilters :: ConfigFile -> MavenScopeFilters
634636
collectConfigMavenScopeFilters configFile = do

src/App/Fossa/Config/ConfigFile.hs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module App.Fossa.Config.ConfigFile (
1111
ConfigRevision (..),
1212
ConfigTargets (..),
1313
ConfigPaths (..),
14+
PathFilter (..),
1415
ConfigTelemetry (..),
1516
ConfigTelemetryScope (..),
1617
ExperimentalConfigs (..),
@@ -57,6 +58,7 @@ import Data.Set qualified as Set
5758
import Data.String.Conversion (ToString (toString), ToText (toText))
5859
import Data.Text (Text, strip, toLower)
5960
import Diag.Diagnostic (ToDiagnostic (..))
61+
import Discovery.Filters (PathFilter (..))
6062
import Effect.Logger (
6163
Logger,
6264
logDebug,
@@ -257,8 +259,8 @@ data ConfigTargets = ConfigTargets
257259
deriving (Eq, Ord, Show)
258260

259261
data ConfigPaths = ConfigPaths
260-
{ pathsOnly :: [Path Rel Dir]
261-
, pathsExclude :: [Path Rel Dir]
262+
{ pathsOnly :: [PathFilter]
263+
, pathsExclude :: [PathFilter]
262264
}
263265
deriving (Eq, Ord, Show)
264266

0 commit comments

Comments
 (0)