Allow `if` on tasks
See original GitHub issueI have an enhancement to propose: Allowing an if
property (which would work just like the link
option) on the top-level tasks, alongside each directive. If the executed command is not successful, the whole task would be skipped.
The idea is that you could define sets of link
s, or shell
commands, or whatever task, for an environment/profile/whatever. My use case is that I’d like to have a task with link
s that are always created, and then another set just for personal machines, and a third just for work machines. I have a different .gitconfig
file between the two, plus a .pairs
file for work, plus more…Since I can’t put two different entries for the same file under a single link
task, I need to have different task for these cases, anyway. So, it would be wonderful if I didn’t have to repeat the same if
option again and again and again.
For exmaple:
- link:
# all my common links
- if: [[ ! $PROFILE ]]
link:
~/.gitconfig:
- if: [[ $PROFILE = work ]]
link:
~/.gitconfig: gitconfig.work
~/.pairs
# other work-only links
Plus, I could imagine this being useful with other directives – definitely for shell
and create
, and probably for many plugins, too.
I think this small enhancement would make dotbot so much more flexible and powerful.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:6 (2 by maintainers)
Top GitHub Comments
After I finished my PR and comment last night, I took a look at issue #81 based on the mention above, and that expanded my thinking a bit more and maybe helped me to see some of the disconnects.
I do think what folks are asking for there is closely related to what I’m asking for here. To put a finer point on what I was trying to say in my previous comment: In both cases, I think it’s not control flow, but rather more powerful composition. It’s the ability to group some related actions and identity and describe them as a unit. And as I see it, composition is very much a declarative concept.
The main difference between what’s been going there vs. what I’m proposing here is that I see the existing concept of a task as that unit of composition. It already can, itself, compose multiple actions. All it’s missing to be super useful is a name and a way of declaring in which situations it applies (and my suggestion was to reuse the existing
if
mechanism for that).What they’re talking about over there is a unit of composition that’s bigger than tasks. But what’s not clear to me is if they’re thinking about it that way because they don’t know that tasks can already compose multiple actions, or if they need something more than that. In fact, it’s not even clear to me if tasks are intended to compose multiple actions. The only reason I know they can is from looking at the code (
dispatcher.py:24
). There aren’t any tasks with multiple actions shown in the docs.So, I wonder if that might be the source of discomfort and disconnect with these sorts of suggestions – the question of what tasks are actually meant to be and whether a mechanism for composition (rather than just a list of actions) is desirable…
As I see it, yes, a mechanism for composition would be highly beneficial in a lot of cases, and tasks are very well suited to be that mechanism. It wouldn’t complicate the model at all for users who don’t need them, and the changes to the code would be minor.
With all this in mind, I’ve come up with minor revision to my idea that’s a little more explicit and unambiguous:
I’m adding a
task
property that allows you to identify the task. When that’s present, you can also use anif
property to declare the task’s applicability, and the task’s actions move under an explicitactions
property. This way, there’s no ambiguity and you don’t have to worry about task properties stepping on action names from plugins.Of course, all of this is optional. I could have declared the common task without a name just like before:
Users who just want a flat list of actions and no composition can continue using this format.
What would you think of this approach? If you think it might be worth pursuing, I’d be happy to update my PR to implement it.
Yeah, this is a good point. I think Python is among the more accessible languages (even though I love Racket, I don’t think I’d release a Dotbot 2.0 implemented in it), but yeah, YAML is probably better understood.
This is the thing that “true programming languages” handle pretty well, but data serialization languages don’t do quite as well. Have a generic
if
construct, and it applies anywhere (can have anif
at the top level, can nest it deep inside somewhere, …).Heh, that’s a good question. Yes, in practice, each task ends up having exactly one action. This is a bit of an artificial distinction that arose from the need to have actions be named (so we know whether to
link
orshell
or whatever) but also the desire to have actions be executed in the order written, and also to allow multiple actions with the same name. This is another weird thing that’s a result of using a pretty rigid data serialization format (YAML/JSON) as oppoosed to something more customized for the use case at hand. What I wanted was a (ordered) list of pairs (task name, arguments), but YAML dictionaries are unordered, so we couldn’t use just a dictionary at the top level. So it looks like everything is “indented” one level in, where we have a list of dictionoaries where each dictionary has a single key/value pair. If someone didn’t care about ordering of task execution, and also didn’t list any key twice (e.g. no multiplelink
s), then it would be fine if a task had multiple actions.That looks pretty clean, though perhaps a bit verbose for my tastes (I think I wouldn’t personally need that kind of complexity, so I’d have a single task). Again, a challenge with the YAML format is that
actions:
is unordered, and any given action can only appear once.In the strictest sense, this is not backwards compatible, because technically “task” and “if” are legal plugin names. But nobody is using those silly plugin names in practice, so I don’t think it’s a problem.
(more specific comments in #229)