Skip to content

feat: support custom variable references in include/exclude patterns#167

Draft
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-use-of-variables-in-filtering-rules
Draft

feat: support custom variable references in include/exclude patterns#167
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-use-of-variables-in-filtering-rules

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 12, 2026

Tasks like dotnet test or npm run test don't accept file paths as args, so there was no way to conditionally skip them based on which files were changed — without dropping into an OS-specific shell script.

Description

Custom variables from the variables section can now be referenced directly inside include and exclude patterns using the same ${variable-name} syntax already used in args. When a variable reference in include produces no output, the task is automatically skipped — no separate property needed.

Example

{
  "variables": [
    {
      "name": "cs-to-be-pushed",
      "command": "git",
      "args": ["diff", "origin/main..HEAD", "--name-only"]
    }
  ],
  "tasks": [
    {
      "name": "dotnet-test",
      "group": "pre-push",
      "command": "dotnet",
      "args": ["test"],
      "include": ["${cs-to-be-pushed}"]
    }
  ]
}

If cs-to-be-pushed returns no files, the task is skipped. Works cross-platform — no shell hackery required.

Mixed patterns (e.g. ["**/*.cs", "${my-var}"]) behave correctly: the task is only skipped when all include entries are custom-variable references that returned nothing; a plain glob alongside an empty variable still allows the task to run.

Changes

  • ArgumentParser — new async GetPatternMatcherAsync on IArgumentParser; resolves ${variable-name} references in include/exclude patterns by executing the named variable's command and using its output as patterns. Returns null when all include entries are custom-variable references that produced no output (skip signal). The static GetPatternMatcher is kept for backward compatibility.
  • ExecutableTaskFactoryCheckIfWeShouldSkipTheTask checks GetPatternMatcherAsync == null for the Variable rule; also uses it for the Staged rule so that variable references in include/exclude are resolved there too.
  • schema.jsoninclude/exclude descriptions updated to document ${variable-name} support.
  • Tests — 5 unit tests for GetPatternMatcherAsync; 4 integration tests covering matching, no-match, missing-variable, and mixed-pattern scenarios.

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>Use of variables in filtering rules</issue_title>
<issue_description>### Details

I believe this is similar to #114 but slightly different.

I have a monorepo with a mix of dotnet and typescript based projects.

I only want to run dotnet test on push if the commits about to be pushed container *.cs files.

My current configuration is:

{
   "$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
   "tasks": [
      {
         "name": "dotnet-build-test",
         "group": "pre-push",
         "cwd": "./nnotes-backend",
         "command": "sh",
         "args": [
            "-c",
            "git diff --cached --name-only origin/main | grep -E '\\.cs$' && dotnet build | dotnet test || echo 'No .cs file changes detected, skipping build and test.'"
         ],
         "include": ["**/*.cs"]
      },
      {
         "name": "angular-test",
         "group": "pre-push",
         "cwd": "./nnotes-client",
         "include": [
            "**/*.ts"
         ],
         "command": "npm",
         "args": [
            "run",
            "test"
         ]
      }

   ]
}

I have 2 problems I'd like to get around:

  • dotnet-build-test uses sh as the command to execute the git checks, which prevents users from using these hooks on Windows.
  • angular-test runs on every push even if there are no *.ts files in the commits about to be pushed. I could do the same hack as dotnet-build-test to resolve that too but at the same expense of not being able to use the hook in a Windows environment.

Proposed Solution

This solution should not break backward compatibility.
Add a new filteringRuleVariable to the configuration options. The rule should be:

  1. filteringRule is set to Variable
  2. If the filteringRuleVariable is unspecified or an empty string, use the existing behavior.
  3. If the filteringRuleVariable is specified and returns an empty string, skip processing the task; otherwise, run the task.

Sample configuration using the proposed solution

{
   "$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
   "variables": [
      {
         "name": "cs-to-be-pushed",
         "command": "git",
         "args": [
            "diff",
            "--cached",
            "--name-only", 
            "origin/main",
            "|",
            "rg",
            "\\.cs$"
         ]
      },
      {
         "name": "ts-to-be-pushed",
         "command": "git",
         "args": [
            "diff",
            "--cached",
            "--name-only", 
            "origin/main",
            "|",
            "rg",
            "\\.ts$"
         ]
      }
   ],
   "tasks": [
      {
         "name": "dotnet-build-test",
         "group": "pre-push",
         "cwd": "./nnotes-backend",
         "command": "dotnet",
         "args": [
            "test'"
         ],
         "include": ["**/*.cs"],
         "filteringRule": "variable",
         "filteringRuleVariable": "cs-to-be-pushed"
      },
      {
         "name": "angular-test",
         "group": "pre-push",
         "cwd": "./nnotes-client",
         "include": [
            "**/*.ts"
         ],
         "command": "npm",
         "args": [
            "run",
            "test"
         ]
         "filteringRule": "variable",
         "filteringRuleVariable": "ts-to-be-pushed"
      }
   ]
}

Benefits of this approach

  • I can use the git hackery to check if the pre-pushed commits directly against the git command, which should work across platforms.
  • I can configure my filtering logic in interesting ways without dropping into an OS-specific shell.

I think this suggestion would be cool for this tool.
I am sure other potential solutions can provide the desired flexibility.</issue_description>

<agent_instructions>TTD style. first add few unit/integration tests to prove this is actually an issue,
if you convinced the user story is correct, try to explore the husky features and find a way to solve it.

only if currently there is no way of dealing with these cases, implement it.
(user suggestion can be considered but aim for the best way of api design to expand husky.net features, don't limit yourself to the user suggestion) </agent_instructions>

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: alirezanet <7004080+alirezanet@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix use of variables in filtering rules feat: add filteringRuleVariable to filter tasks by variable output Mar 12, 2026
Copilot AI requested a review from alirezanet March 12, 2026 18:19
alirezanet

This comment was marked as duplicate.

Copy link
Copy Markdown
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.

instead of using filteringRuleVariable isn't it possible to use the custom variables in the include and exclude glob patterns like what we do for ${args} so people can adjust it properly? I don't want to create two separate way of handling one similar issue

Co-authored-by: alirezanet <7004080+alirezanet@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 12, 2026

instead of using filteringRuleVariable isn't it possible to use the custom variables in the include and exclude glob patterns like what we do for ${args} so people can adjust it properly? I don't want...

Done in commit 169bce9. The filteringRuleVariable property has been removed. Custom variables from the variables section can now be referenced directly in include/exclude patterns using the same ${variable-name} syntax already used in args.

{
  "variables": [
    { "name": "cs-to-be-pushed", "command": "git", "args": ["diff", "origin/main..HEAD", "--name-only"] }
  ],
  "tasks": [
    {
      "name": "dotnet-test",
      "group": "pre-push",
      "command": "dotnet",
      "args": ["test"],
      "include": ["${cs-to-be-pushed}"]
    }
  ]
}

When the variable produces no output, the task is skipped. If include mixes a glob pattern and a variable (e.g. ["**/*.cs", "${extra-files}"]), the task is only skipped when all include entries are custom-variable references that returned nothing — so existing glob patterns are unaffected.

Copilot AI changed the title feat: add filteringRuleVariable to filter tasks by variable output feat: support custom variable references in include/exclude patterns Mar 12, 2026
Copilot AI requested a review from alirezanet March 12, 2026 19: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.

Use of variables in filtering rules

2 participants