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

Makesure can not be used to execute two goals with effects that counter each other #171

Open
koljakube opened this issue Aug 28, 2024 · 2 comments

Comments

@koljakube
Copy link

I sometimes need to implement update tasks.
A basic setup can look like this:

@goal 'x-created'
@reached_if test -f x
  touch x

@goal 'x-deleted'
@reached_if ! test -e x
  rm x

Now the update task would look like this:

@goal 'x-updated'
@depends_on 'x-deleted'
@depends_on 'x-created'

This does not work, since the reached-checks are checked during build graph construction, as far as I can tell.

Workarounds:

  • Calling ./makesure in x-updated instead of dependencies.
  • Simply executing ./makesure x-updated twice.
  • Removing checks (not very useful).

I could think of ways for makesure to support this better:

  • reached_if-checks could happen before goal execution (but I guess that would change too much of makesure's behavior).
  • A variable could be set for goals that were invoked directly from the CLI, and unset for dependencies.
  • More general, a variable that contains the "depenency depth level", à la $SHLVL.
  • Some "always execute" pseudo-parameter that could be passed to every goal.

I'm writing this primarily to start discussion, since I use makesure quite heavily at the moment. What do you think about this?

@xonixx
Copy link
Owner

xonixx commented Aug 28, 2024

This is interesting question indeed.

For sure changing the operational semantics can hurt the intended declarative nature of the tool. For example, now any goal runs at most one time, even if depended on multiple times.

I think the root of the issue is that dependency on a goal is not equivalent to a goal invocation.

What you could also try is something like:

@goal 'x-created'
@reached_if test -f x
@depends_on 'create-x'

@goal 'x-deleted'
@reached_if ! test -e x
@depends_on 'delete-x'

@goal 'create-x'
  touch x

@goal 'delete-x'
  rm -f x

@goal 'x-updated'
@depends_on 'delete-x'
@depends_on 'create-x'

I see how it’s bulky and repetitive, though.

Also the example with a file, though serves a good role of showing the problem, looks a bit abstract. If possible, I would like to see a more realistic example (from real project). This could help finding more elegant/appropriate solution.

@koljakube
Copy link
Author

A practical example would be the following, a chain of goals for task that is meant to be executed at regular intervals and that cleans up after itself:

  • A goal to build a docker image (reached_if the image exists)
  • A goal to save this image to a tar archive (reached_if the archive exists)
  • A goal to upload this image to a remote server (always performed)
  • A goal to remove the tar archive (reached_if the archive does not exist)
  • A goal to remove the docker image (reached_if the image does not exist)
  • A goal that depends_on all of the above, in the order listed

Given an empty directory and docker cache, the latter two goals would never be executed, leaving the files around.

In the end, I guess there is no right or wrong here, it's a matter of preference when to evaluate the reached_ifs. In the end, the same effect can be achieved by replacing @reached_if with shell if; then; fis. I just like the high visibility of the presence of a @reached_if.

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

No branches or pull requests

2 participants