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

Aliases #879

Merged
merged 11 commits into from
Oct 14, 2022
Merged

Aliases #879

merged 11 commits into from
Oct 14, 2022

Conversation

pd93
Copy link
Member

@pd93 pd93 commented Oct 2, 2022

Task aliases have been discussed a few times in #268, #340, #675. These discussions have ranged in scope and detail, but no design has yet to be established. This PR attempts to implement the simplest possible version of aliases as a proof of concept.

Aliases are alternative names for tasks. They can be used to make it easier and quicker to run tasks with long or hard-to-type names. You can use them on the command line, when calling sub-tasks in your Taskfile and when including tasks with aliases from another Taskfile.

There are 3 parts to this PR:

  1. Task aliases
  2. Namespace aliases
  3. --list / --list-all / --summary

Task Aliases

Task aliases allow users to set a list of alternative strings that can be used to call a task. In the example below, we have 2 tasks: foo and bar. We can call either of these tasks in the normal way with task foo or task bar respectively, but thanks to aliases, we are now also able to call task f and task b with the same behaviour.

Both tasks also have x aliased. If we were to run task x, Task will return an error as aliases are not allowed to collide.

Note that foo calls bar and that it is able to do this by using bar's alias: b.

# Taskfile.yml
version: "3"

tasks:
  foo:
    aliases:
      - f
      - x
    cmds:
      - echo "foo"
      - task: b

  bar:
    aliases:
      - b
      - x
    cmds:
      - echo "bar"

All of the above also works with included taskfiles. In the below example we can call task f and task included:b from the command-line with the same behaviour as the previous example.

# Taskfile.yml
version: "3"

includes:
  included:
    taskfile: Taskfile2.yml

tasks:
  foo:
    aliases:
      - f
    cmds:
      - echo "foo"
      - task: included:b
# Taskfile2.yml
version: "3"

tasks:
  bar:
    aliases:
      - b
    cmds:
      - echo "bar"

Namespace Aliases

Namespace aliases are a way to create alternative names for the namespace of an included taskfile. In the example below, we have adjusted the taskfile so that the included taskfile: included is now aliased to inc. This means that we can now call task inc:b and it will behave the same as task included:b:

# Taskfile.yml
version: "3"

includes:
  included:
    taskfile: Taskfile2.yml
    aliases:
      - inc

tasks:
  foo:
    aliases:
      - f
    cmds:
      - echo "foo"
      - task: inc:b
# Taskfile2.yml
version: "3"

tasks:
  bar:
    aliases:
      - b
    cmds:
      - echo "bar"

When a task with aliases is merged into a file with a namespace alias, these aliases form a matrix and they are all merged into the task. This means that all of the following are callable:

  • task foo
  • task f
  • task included:bar
  • task included:b
  • task inc:bar
  • task inc:b

--list / --list-all / --summary

These flags all print information about one or many tasks. Now that we have aliases, these need to be updated. Below are some examples of the new output for each flag when run against the taskfiles in the Namespace Aliases section above:

task --list / task --list-all:

task: Available tasks for this project:
* foo|f:                                prints foo
* included:bar|included:b|inc:b:        prints bar

Aliases are displayed after the main task name and separated by | characters. I'm slightly concerned about how long some of these lines might get with the matrix merging, so we may need to find a better way of displaying this.

task foo --summary:

task: foo (f)

prints foo

aliases:
 - f

commands:
 - echo "foo"
 - Task: inc:b

The output above show two options for how we could display aliases in the summary text. Either by having a | separated list in brackets next to the task name (similar to the --list/--list-all flags), or having a dedicated section with a bullet list like the commands - I think I prefer the latter.

Other noteworthy changes

  • There is a longstanding FIXME in the codebase about deep copying the tasks when merging an included taskfile. This caused some issues when implementing this change, so I have also added a couple of new utility functions in taskfile/copy.go called deepCopySlice and deepCopyMap. These leverage generics to create a deep copy of slices and maps respectively. These utils are then used in 3 new DeepCopy methods on the Task, Vars and IncludedTaskfile structs to create a full deep copy of a task when merging. Hopefully this will help to reduce bugs in the future.
  • I'm leveraging the golang.org/x/exp/slices package to do generic "slice contains element" lookups. This also means I was able to remove the non-generic version of this function in taskfile/slice.go. This package is expected to become a part of the standard library in the next few Go releases.
  • Updated usage.md and api_reference.md docs.
    • The API reference doc was actually missing a couple of other keys (like label), so I added these too.
  • Updated changelog.md doc.

@pd93 pd93 added the type: feature A new feature or functionality. label Oct 2, 2022
@pd93 pd93 marked this pull request as ready for review October 2, 2022 07:10
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.

Awesome work! 👏

I didn't test it yet, but the code is almost perfect. I did a couple of comments only.

Thanks a lot for your help 🙌

@@ -5,6 +5,10 @@ sidebar_position: 6

# Changelog

## Unreleased

- Add ability to set `aliases` for tasks and namespaces (#879).
Copy link
Member

Choose a reason for hiding this comment

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

  • This may be confusing, but the right flow at the moment is to write changes to /CHANGELOG.md only, and on release I call task docs:changelog which will actually update this file according to this one.
  • Unfortunately linking issues/PR with just #num won't always work (on the website, for example). We need to make a proper link as ([#num](url)) as the others below.
  • Mention Task identifier shorthand notation / aliases #268 and Feature: aliases #340 as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops, yeah that makes sense. I missed that there are two copies of the file. Should be fixed now

Comment on lines 243 to 244
aliases:
- gen
Copy link
Member

Choose a reason for hiding this comment

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

What about using one line arrays for aliases mentions on documentation? This would be consistent with deps and is shorter.

Suggested change
aliases:
- gen
aliases: [gen]

@@ -936,6 +950,27 @@ If the task does not have a summary or a description, a warning is printed.

Please note: *showing the summary will not execute the command*.

## Task aliases

Aliases are alternative names for tasks. They can be used to make it easier and quicker to run tasks with long or hard-to-type names. You can use them on the command line, when [calling sub-tasks](#calling-another-task) in your Taskfile and when [including tasks](#including-other-taskfiles) with aliases from another Taskfile. They can also be used together with [namespace aliases](#namespace-aliases).
Copy link
Member

Choose a reason for hiding this comment

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

As a convention, we try to wrap paragraphs in 80 chars when possible.

help.go Outdated
Comment on lines 47 to 48
taskNames := append([]string{task.Name()}, task.Aliases...)
fmt.Fprintf(w, "* %s: \t%s\n", strings.Join(taskNames, "|"), task.Desc)
Copy link
Member

Choose a reason for hiding this comment

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

I believe this may possible break the auto-completion scripts that use --list to parse the list of available tasks.

Because of this, I think we may need to move the somewhere after. Not sure about the format, but maybe something like this?:

task: Available tasks for this project:
* foo:                prints foo (aliases: f)
* included:bar        prints bar (aliases: included:b, inc:b)

Copy link
Member Author

@pd93 pd93 Oct 7, 2022

Choose a reason for hiding this comment

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

Fine with this. Do we want to print them in a new column? or just inline next to the description. A column might make this info easier to parse.

Also, same question as --summary flag below - Should we colour the aliases?

Example below:

image

Copy link
Member

Choose a reason for hiding this comment

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

I end up changing that myself, taking your suggestion of printing in a separate column, and colored.

l.Outf(logger.Default, "aliases:")
for _, alias := range t.Aliases {
l.Outf(logger.Default, " - %s", alias)
}
Copy link
Member

Choose a reason for hiding this comment

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

I liked the format choice this this 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated the PR and removed the alternative syntax in favour of this. I've also set the color of aliases to Cyan since the colored output is now merged. Wdyt?

task.go Outdated
}
if t.Internal {
// If the task is internal
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this comment is needed. The code is self-descriptive.

Suggested change
// If the task is internal

task.go Outdated
// Search for a matching task
matchingTask, ok := e.Taskfile.Tasks[call.Task]
// If didn't find one, search for a task with a matching alias
if !ok {
Copy link
Member

Choose a reason for hiding this comment

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

Early returns are preferred. Check for if ok { return } and move the current block to after it.

if ok {
  return matchingTask, nil
}

// Rest of the code

@postlund
Copy link

postlund commented Oct 7, 2022

I like this a lot 👍 One thing I feel is missing though is aliases across namespaces. In my particular use case (a mono repo), I for instance have various tools under the tools directory and each tools have set of generic tasks, e.g. build, run, clean, etc. to make it look like a build system. So running for instance the "foobar" tool, I would call tools:foobar:run and potentially pass some arguments. If "foobar" is a tool that I run often, then typing that would be tedious. So I would much rather have just foobar on top-level map to that task. Pseudo-something like this:

version: "3"

includes:
  tools: ./tools

tasks:
  foobar:
    alias: tools:foobar:run

Could something like this fit within the alias concept somehow?

@pd93
Copy link
Member Author

pd93 commented Oct 7, 2022

@postlund "flattening" included files is something I considered, but I decided against it for a few reasons:

  • Firstly, if this were to be a feature, I think it fits a flatten keyword in includes better than aliases.
  • Secondly, there has been some discussion regarding this in Add option to 'flatten' included taskfiles #273, and the general consensus was against it.
  • Lastly, I wanted the first version of aliases to be as simple as possible. Adding flattening would increase the complexity quite a bit as we'd have to decide how to deal with conflicting task names.

I would suggest adding to the conversation in #273 if you're interested in moving this idea forwards.

Copy link
Contributor

@ssbarnea ssbarnea left a comment

Choose a reason for hiding this comment

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

That is cool, I do often like to have 1-2 letter aliases, like with git.

@andreynering andreynering merged commit 6d90c78 into master Oct 14, 2022
@andreynering
Copy link
Member

Awesome work @pd93!

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.

4 participants