Skip to content

feat: support variables in include/exclude glob patterns#161

Merged
alirezanet merged 5 commits intomasterfrom
copilot/add-variables-in-includes-glob-pattern
Mar 11, 2026
Merged

feat: support variables in include/exclude glob patterns#161
alirezanet merged 5 commits intomasterfrom
copilot/add-variables-in-includes-glob-pattern

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

Variables like ${args} were only resolved in task args, not in include/exclude glob patterns. This prevented users from scoping file matching to a runtime-provided directory — a common need in multi-solution repos where dotnet format should only run against solutions containing staged files.

Example use case:

{
  "tasks": [
    {
      "name": "dotnet-format-staged-files",
      "command": "dotnet",
      "args": ["format", "${args}", "--include", "${staged}"],
      "include": ["${args}/**/*.cs"]
    }
  ]
}

Run with: dotnet husky run --args src/MySolution

Note: Only ${args} is supported in include/exclude glob patterns. Other variables (${staged}, ${all-files}, etc.) expand to file path lists at runtime, which are not suitable as glob pattern fragments. ${args} is the only variable that provides literal string values for pattern substitution.

Changes

  • ArgumentParser.GetPatternMatcher — accepts optional optionArguments; expands ${args} in each include/exclude pattern, yielding one pattern per argument value (supports multiple --args)
  • ArgumentParser.ParseAsync — forwards optionArguments to GetPatternMatcher
  • ExecutableTaskFactory.CheckIfWeShouldSkipTheTask — accepts and forwards optionArguments to GetPatternMatcher so skip-checks honour variable-based patterns
  • Issue113Tests.cs — 8 integration tests total:
    • 4 tests for new ${args} variable substitution in include and exclude patterns (match/skip cases for each)
    • 4 regression tests verifying old behavior is preserved:
      • ${staged} variable in args with static include pattern (match and no-match cases)
      • No variables at all, static args with static include pattern
      • Static include pattern unaffected when --args is supplied but pattern contains no ${args}

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation
  • I have commented my code, particularly in hard-to-understand areas
  • New and existing unit tests pass locally with my changes
  • I did test corresponding changes on Windows
  • I did test corresponding changes on Linux
  • I did test corresponding changes on Mac
Original prompt

This section details on the original issue you should resolve

<issue_title>Usage of variables in "includes" glob pattern</issue_title>
<issue_description>### Discussed in #113

Originally posted by ThijmenDam May 21, 2024
Hi @alirezanet and contributors, first of all I'd like to thank you for this very useful package.

So, our team would greatly benefit from the possibility of using variables in the "include" glob pattern. For example:

{
  "tasks": [
    {
      "name": "dotnet-format-staged-files",
      "group": "pre-commit-operations",
      "command": "dotnet",
      "args": [
        "format",
        "${args}",
        "--verbosity",
        "diagnostic",
        "--no-restore",
        "--include",
        "${staged}"
      ],
      "include": [
        "${args}/**/*.cs"   <---- this is what I refer to
      ]
    }
  ]
}

The reason this would be very useful for our team is performance. To elaborate, some of our repositories contain multiple solutions, and without variables in the "include" glob pattern, we are not able to execute dotnet format only for the solutions that contain staged files. This means that dotnet format is executed for every solution, even if there are no staged files from that solution (which slows down the pre-commit hook by a lot).

To illustrate, consider the following pre-commit hook:

echo "[PRE-COMMIT] Formatting staged .NET files..."

repository_root=$(dirname "$(dirname "$(dirname "$0")")")

# Loop through all subdirectories of the root folder of the repository
for dir in "$repository_root"/*; do

    # Ignore the directory if it is not a folder
    [ ! -d "$dir" ] && continue

    # Ignore directories that are not a .NET solution folder
    [ -z "$(find "$dir" -type f -name '*.sln')" ] && continue
    
    # Here we have confirmed that the directory contains a solution file
    solution_dir="$dir"

    # Get the path to the solution file in the .NET solution folder
    solution_file=$(find "$dir" -type f -name '*.sln')
    
    echo "[PRE-COMMIT] Formatting $solution_file"
    dotnet husky run --name dotnet-format-staged-files --args "$solution_dir" 
done

We pass the solution directory as argument, because dotnet format requires the workspace to be provided as command line argument whenever dotnet format is executed from a directory that does not contain a solution file (i.e. the root of our repository).

To conclude, if we could use the ${args} variable in the "include" glob pattern, dotnet format would only be executed for solutions that contain staged files. Which saves a lot of time for repositories with multiple solutions!

Thanks for reading, and I'd love to hear what you think of this.

Best,

Thijmen

</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Add support for variables in include glob pattern feat: support variables in include/exclude glob patterns Mar 11, 2026
@alirezanet alirezanet marked this pull request as ready for review March 11, 2026 18:09
Copy link
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

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

Build is failing right now, and make sure you have enough unit tests and some integration tests to fully make sure this feature working as expected.

{
foreach (var pattern in patterns)
{
if (pattern.Contains("${args}") && optionArguments is { Length: > 0 })
Copy link
Owner

Choose a reason for hiding this comment

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

does this check mean we only support ${args} variable? if not, we should support all possible variables as before

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently only ${args} is supported in include/exclude glob patterns. Other variables like ${staged}, ${all-files}, ${last-commit}, and ${git-files} expand to file path lists, not string fragments — embedding file paths as glob patterns doesn't make semantic sense. ${args} is the only variable that provides literal string values (e.g., a directory name) suitable for substitution into a glob pattern like ${args}/**/*.cs.

Copy link
Contributor Author

Copilot AI commented Mar 11, 2026

Build is failing right now, and make sure you have enough unit tests and some integration tests to fully make sure this feature working as expected.

Fixed in 6efed18. The CI failure was because the integration tests were checking result.Stderr for the "Skipped" message, but when running dotnet husky run directly (not via git hook), husky writes all output to stdout. In Issue106Tests, the skipped message appears in stderr because it runs through a git commit hook — git captures the hook's stdout/stderr and surfaces it as the commit command's stderr.

Also expanded integration test coverage to 4 tests:

  • ${args} in include: match case (task runs) and no-match case (task skips)
  • ${args} in exclude: excluded case (task skips) and not-excluded case (task runs)

Copy link
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

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

add a couple of tests to make sure the old behavior is still working,
1- old behavior with a variable
2- old behavior without any variable
3- also new behavior without any variable and just with some glob patterns

Copy link
Contributor Author

Copilot AI commented Mar 11, 2026

add a couple of tests to make sure the old behavior is still working,
1- old behavior with a variable
2- old behavior without any variable...

Added 4 regression tests in fb03f67:

  1. Old behavior with variable (${staged} in args + static **/*.cs include) → task runs when staged .cs files match
  2. Old behavior with variable, no match (${staged} in args + static **/*.ts include) → task skips when no staged .ts files
  3. Old behavior without any variable (static args + static **/*.cs include) → task runs when staged .cs files match
  4. Static patterns unaffected by --args (static **/*.cs include, no ${args} in pattern, run with --args tests) → pattern remains unchanged and still matches staged .cs files

github-actions bot pushed a commit to ptr727/LanguageTags that referenced this pull request Mar 13, 2026
Updated [husky](https://github.com/alirezanet/husky.net) from 0.8.0 to
0.9.0.

<details>
<summary>Release notes</summary>

_Sourced from [husky's
releases](https://github.com/alirezanet/husky.net/releases)._

## 0.9.0

## What's Changed
* remove net6.0/net7.0 since out of support by @​WeihanLi in
alirezanet/Husky.Net#135
* fix: update regex pattern to allow breaking commits by @​joaoopereira
in alirezanet/Husky.Net#153
* Fix parallel install mutex bug and support incremental build by
@​MattKotsenas in alirezanet/Husky.Net#159
* fix: handle file paths with spaces in git commands in
alirezanet/Husky.Net#156
* feat: add `staged` property to custom variables for re-staging support
in alirezanet/Husky.Net#163
* feat: support variables in include/exclude glob patterns in
alirezanet/Husky.Net#161
* fix: use AfterTargets="Restore" to support NuGet credential helpers
for private feeds in alirezanet/Husky.Net#162

## New Contributors
* @​joaoopereira made their first contribution in
alirezanet/Husky.Net#153
* @​MattKotsenas made their first contribution in
alirezanet/Husky.Net#159

**Full Changelog**:
alirezanet/Husky.Net@v0.8.0...v0.9.0

Commits viewable in [compare
view](alirezanet/Husky.Net@v0.8.0...v0.9.0).
</details>

Updated
[Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/dotnet)
from 10.0.4 to 10.0.5.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Logging.Abstractions's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated [Microsoft.SourceLink.GitHub](https://github.com/dotnet/dotnet)
from 10.0.200 to 10.0.201.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.SourceLink.GitHub's
releases](https://github.com/dotnet/dotnet/releases)._

## 10.0.201

You can build .NET 10.0 from the repository by cloning the release tag
`v10.0.201` and following the build instructions in the [main
README.md](https://github.com/dotnet/dotnet/blob/v10.0.201/README.md#building).

Alternatively, you can build from the sources attached to this release
directly.
More information on this process can be found in the [dotnet/dotnet
repository](https://github.com/dotnet/dotnet/blob/v10.0.201/README.md#building-from-released-sources).

Attached are PGP signatures for the GitHub generated tarball and
zipball. You can find the public key at https://dot.net/release-key-2023

Commits viewable in [compare
view](dotnet/dotnet@v10.0.200...v10.0.201).
</details>

Updated [System.CommandLine](https://github.com/dotnet/dotnet) from
2.0.4 to 2.0.5.

<details>
<summary>Release notes</summary>

_Sourced from [System.CommandLine's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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.

Usage of variables in "includes" glob pattern

2 participants