Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wildcard matching for task names #1489

Merged
merged 5 commits into from
Feb 22, 2024
Merged

feat: wildcard matching for task names #1489

merged 5 commits into from
Feb 22, 2024

Conversation

pd93
Copy link
Member

@pd93 pd93 commented Jan 27, 2024

Fixes #836

Context

This PR is a proposal for a design that would allow basic wildcard matching for task names. At the moment, if users want to make a generic task that performs a common set of operations on many different services or directories (etc) they must either:

  1. Pass a variable into Task using environment variables
  2. Use .CLI_ARGS and the -- operator

Both of these methods can be clunky to type into a CLI and are not very intuitive. make has the option to do wildcard matching (using %). This PR is a proposal to do the equivalent in Task.

Proposal

When a Taskfile has a task with a name that contains a wildcard (*) and the user calls task with a task name that "matches" a wildcard task, that task will be run. Matches will occur by replacing any instances of * with the (.*) regex string and doing a regex match. The captured variables will be made available to the Task via the MATCH variable in the form of an array. For example:

version: 3

tasks:
  # A task that starts any service
  start-*:
    vars:
      SERVICE: "{{index .MATCH 0}}"
    cmds:
      - echo "Starting {{.SERVICE}}"
      - ...
> task start-foo
Starting foo
...

Implementation

The first commit updates all usages of ast.Call to be passed by reference. Doing this allows methods that accept a call to update the call's variables. This means we can add any wildcard matches into the call.Vars without have to propagate them and change a bunch of function signatures.

Other than this, the implementation is very simple, we are simply overriding the omap.Get() method by adding a Get() method directly on ast.Tasks and then adding a simple WildcardMatch() function to ast.Task which will work out if a Task matches the call and retrieve the wildcard variables

Notes

I haven't written any docs yet since I'd like some feedback on the design/implementation. I'll add this once we've decided we're happy with it. What do people think about the naming? i.e. * and MATCH. There were some other ideas discussed in #836, but I think starting with simple wildcard matching is probably a good start. I personally, would find this to be more than enough for my use cases.

Final note. The omap.Range() function doesn't support yielding right now so there is a bit of a hack to get around this (see TODO in code). There is an interesting iterator proposal that will be introduced to Golang 1.22 as an experiment. This would be a really good solution to this, but we probably won't be able to add this until it is added to the language permanently (probably 1.23) and we drop support for 1.22 (so around a year from now).

@vmaerten
Copy link
Member

vmaerten commented Feb 1, 2024

Thanks for this work.
It's a really cool feature and I have some use case of it!
I think MATCH is a good name

Copy link
Member

@andreynering andreynering left a comment

Choose a reason for hiding this comment

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

Great work! 🚀

I agree that a wildcard is probably enough. Regexp was suggested on the issue, but it'd be too complex IMO, specially when mixing in YAML.

Comment on lines 1252 to 1253
If multiple matching tasks are found, the first one listed in the Taskfile will
be used. If you are using included Taskfiles,
Copy link
Member

Choose a reason for hiding this comment

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

  1. Would return an error be better if multiple matches are found?
  2. Reminder: the second sentence is still incomplete 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

  1. I think that adding this restriction is fine for now. It keeps things simple. However, I can definitely see a world where someone with a complex setup asks for the current behaviour as it would allow you to do something like this:

    version: 3
    
    tasks:
      foo-*-*:
        ..
      foo-*:
        ..

    This allows Task to do something different depending on if the user does task foo-bar vs task foo-bar-baz. This would not be possible if we errored when multiple tasks match since task-bar-baz matches both tasks in the file.

    Happy to add the error for now and revisit this later though.

  2. Haha, I probably started writing this and then realised I didn't know what the behaviour with includes was, so went to check and never came back. I have completed the sentence now 😆

@pd93 pd93 merged commit 38a06da into main Feb 22, 2024
13 checks passed
@pd93 pd93 deleted the wildcards branch February 22, 2024 20:58
pd93 added a commit that referenced this pull request Feb 22, 2024
@pd93 pd93 added type: feature A new feature or functionality. and removed type: proposal labels Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature A new feature or functionality.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RFE: Allow use of wildcard or regex in task names (dynamic task names)
3 participants