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

Allow finer control over execution of tasks #240

Open
pawamoy opened this issue Jul 29, 2020 · 29 comments
Open

Allow finer control over execution of tasks #240

pawamoy opened this issue Jul 29, 2020 · 29 comments
Labels
good first issue Easy things for newbies
Milestone

Comments

@pawamoy
Copy link
Contributor

pawamoy commented Jul 29, 2020

I would like to discuss about a way to improve the usability of tasks. In short, I think it would be great to have control over when (and if) a task is executed.

Related to #196 and #238.

@yajo
Copy link
Member

yajo commented Aug 11, 2020

Yes, I think that it should work similar to migrations: pre and post. We could even have pre-copy, pre-update, post-copy, post-update. That should be enough, right?

@yajo yajo added this to the v5.0.0 milestone Aug 11, 2020
@yajo yajo added good first issue Easy things for newbies Status:Planned labels Aug 11, 2020
@yajo
Copy link
Member

yajo commented Aug 11, 2020

@baurmatt had a good idea at #238 (comment): add $OPERATION={copy,update} to the environment. With that, you can simply include any needed if in your task code and perform it or not, without needing to overcomplicate the copier.yml file. Also it'd be fully backwards compatible.

IMHO the path to follow.

@pawamoy
Copy link
Contributor Author

pawamoy commented Aug 11, 2020

pre-copy, pre-update, post-copy, post-update

I'd love to have those indeed. Would this be accomplished with both STAGE and OPERATION env vars then? The four combinations would be mutually exclusive, meaning each one is only executed when the conditions are met.

STAGE / OPERATION copy update
before
after

@yajo
Copy link
Member

yajo commented Aug 11, 2020

Yes, well... definitely this couldn't be backwards-compatible after all.

@pawamoy
Copy link
Contributor Author

pawamoy commented Aug 11, 2020

I'm OK with implementing this incrementally if you'd like to make it backwards-compatible first, then add functionality like more stages.

@yajo yajo modified the milestones: v5.0.0, v6.0.0 Aug 13, 2020
@pawamoy
Copy link
Contributor Author

pawamoy commented Oct 6, 2020

Hey @yajo, I'd like to start tackling this. Basically, I want to add an OPERATION environment variable when running tasks, as was discussed previously.

Do you already have an idea of how this could be implemented?
I was wondering if it would make sense to set the environment variable with os.environ["OPERATION"] = "update" to later on pass it in the extra_env argument:

run_tasks(
        conf, render, [{"task": t, "extra_env": {"STAGE": "task", "OPERATION": os.environ["OPERATION"]}} for t in conf.tasks]
    )

...but I'm not sure because it's like using a global variable 😅
Also, maybe we would not even need to pass it in the extra_env argument since we set it with os.environ?

What do you think?

@yajo
Copy link
Member

yajo commented Jan 17, 2021

What if we apply a logic like the one explained at #326 but for tasks too?

@pawamoy
Copy link
Contributor Author

pawamoy commented Jan 17, 2021

Isn't it exactly like what is discussed here 🙂 ? Passing everything needed (env vars) to tasks when executing them?

@yajo
Copy link
Member

yajo commented Jan 17, 2021

Lol yes...

I've got pretty clear now that copier 6 will break all things. It's gonna be fun 😆

@KristianTanggaard
Copy link

Is there a preferred way of handling things in terms running commands only on creation of a new project vs. updating them now we're up on copier 7?

For example: Wanting to just run git init and doing an initial commit on creation, but not on any updates afterwards?

@yajo
Copy link
Member

yajo commented Nov 25, 2022

FWIW if you git init on an existing repo, it does nothing.

Other than that, right now there's no official way to distinguish it. Thus, this issue exists. 😁

You have to use your imagination. One workaround for you would be to check if .git exists. If it doesn't you know you're copying. If it does, you're updating.

@KristianTanggaard
Copy link

That is true. I just gave it as an example. Considering I might then want to do an initial commit, and add a second or third branch, it seems like something that might get a bit iffy.

I will probably do it with a python script that checks the state before running anything.

@yajo
Copy link
Member

yajo commented Dec 14, 2022

You may consider also pushing a PR here to enhance this feature and close the issue. 😃

@lyz-code
Copy link

lyz-code commented Jul 4, 2023

Hi all, first thank you for making and maintaining this awesome project <3.

I'm having this same question:

Is there a preferred way of handling things in terms running commands only on creation of a new project vs. updating them now we're up on copier 7?

For example: Wanting to just run git init and doing an initial commit on creation, but not on any updates afterwards?

And I've thought of creating a tag 0.0.0 on the first valid commit of the template and then have an initial migration script for version 0.1.0. That way instead of doing copier copy src dest you can do:

copier copy -r 0.0.0 src dest
copier update

It will run over all the migrations steps you make in the future. A way to tackle this is to eventually release a 1.0.0 and move the 0.1.0 migration script to 1.1.0 using copier copy -r 1.0.0 src dest.

As I'm new to the tool I was wondering if you think this is a good idea, or if it can backfire

Thanks!

PS: Our paths cross again @pawamoy :)

@pawamoy
Copy link
Contributor Author

pawamoy commented Jul 5, 2023

Hey @lyz-code, happy to cross paths again 😄
I think this is a good idea, however this can indeed backfire, for this very simple reason: all the versions of your template will not be backward compatible with 0.0.0. If they are now, they probably won't be in the future. This might be because of the template itself, or because of extensions it uses, or because of the version of Copier it required at the time of each version release. So I think this is doomed to fail at some point 😕 This will be OK for existing projects, but not when trying to generate new ones.

@lyz-code
Copy link

lyz-code commented Jul 5, 2023

Thanks @pawamoy for your quick answer. The next "solution" I can think of is to embed inside the generated project's Makefile an init target that runs the init script. The user will then need to:

copier copy src dest
cd dest
make init

Not ideal but it can be a workaround until we have the pre-copy tasks

@bswck
Copy link

bswck commented Nov 17, 2023

What is the status of this issue? Any news so far?

@bswck
Copy link

bswck commented Nov 19, 2023

Also, I would honestly call it TASK_STAGE or EVENT, not OPERATION.

@bswck
Copy link

bswck commented Nov 19, 2023

For those who need a workaround, you might look into my template—https://github.com/bswck/skeleton/blob/master/handle-task-event.sh.

I made a template shell script that is rendered and run every time copier operates. It's included in copier.yml with {% include handle-task-event.sh %} as a single task.

It has some caveats and requires Redis to communicate between task stages aka "operations", but maybe that works for you at the moment.

Not sure if my shell script interacts well with existing projects, but I will be fixing it to incorporate the template into all of my Python projects.

@yajo
Copy link
Member

yajo commented Nov 20, 2023

Thanks for providing that workaround. For the time being, the only progress is that now we require the --trust flag to execute any tasks or migrations.

@hahuang65
Copy link

hahuang65 commented Jan 28, 2024

Newcomer here, wondering if this suggestion is naive or preposterous...

But in the interest of preserving backwards-compatibility... Could we simply add the new operations/stages as new categories?

We could add a pre-run, pre-update, post-run, post-update... and then simply alias tasks to post-run and post-update?

This would maintain backwards compatibility and have the benefit of not having overly complex logic branches in the tasks yaml, given many combinations of stage and operation.

It would allow the yaml document to flow nicely as well, allowing users to order the headers as they want.

For what it's worth, I'd be extremely eager to work on this. It's the only thing preventing me from migrating to copier. I need a pre-check that will prevent the project from being generated if the user doesn't have a certain tool installed.

@bswck
Copy link

bswck commented Jan 29, 2024

My favorite approach would be a single declarative Python script that would allow to register callbacks for each event; with overall appearance similar to noxfile.py.
Something more or less like:

# hooks.py
from copier import after_copy, after_update, session

@after_update
def teardown_update() -> None:
    session.run("somecmd")

@after_copy
def create_repo() -> None:
    should_create_repo: bool = ...
    if should_create_repo:
        session.run("gh", "repo", "create", ...)

@sisp
Copy link
Member

sisp commented Jan 29, 2024

@bswck That would only work for Python scripts, but tasks can use executables written in any language.

@bswck
Copy link

bswck commented Jan 29, 2024

@bswck That would only work for Python scripts, but tasks can use executables written in any language.

Why? I just showed an example of invoking the GitHub CLI.

If you're referring to rendering scripts and then running them, session.run could provide facilities for it.

A Python layer for registering callbacks would not only make things easier, but also extend the overall ability to control tasks.

If you are, however, opposing the idea of having to define a Python script, I would argue then that the potential benefit justifies the trade-off.

@pawamoy
Copy link
Contributor Author

pawamoy commented Jan 29, 2024

@bswck, @sisp meant that providing this feature through Python decorators like after_copy would make the feature, well, Python-only. It would require to write a Python script to run a Bash task or run any other executable that is not Python. A language-agnostic implementation would probably use environment variables instead of Python decorators, since you check env vars in any language and act accordingly. Then only, Copier could maybe provide such decorators to facilitate writing tasks in Python 🙂 So your idea is not incompatible with the feature 👍

@yajo
Copy link
Member

yajo commented Jan 30, 2024

FWIW I was scared away from yeoman because I didn't want to write js just for a template. In the same fashion I don't want to scare away js devs by requiring writing hooks in Python. The truth is that we can just use env variables and command calls, and that just gives the dev all possibilities. Copier has remained as lang-agnostic as possible and I wouldn't like to loose that.

@pescobar
Copy link

in case it's helpful for others, I am testing this workaround which seems to work to only execute the tasks during the first execution of copier to initialize my project (create the required gitlab repos and extra stuff I don't want to repeat when doing copier update)

in copier.yml :

_tasks:
  - bash .project_init.sh

and in .project_init.sh :

#!/bin/bash

if [[ $PWD == /tmp* ]]; then
    echo "We are /tmp/. This is copier update. Exiting"
    exit 0
fi

if [ -d .git/ ]; then
  echo "This project is already initialized. Exiting"
  exit 0
fi

git init
git add .
git commit -a -m 'First commit by copier'

# here more initilization stuff

Script .project_init.sh` is another template in my copier project.

I need to do more testing but so far it seems to work for me

@sevdesk-ryanp
Copy link

I just saw the 'good for newbies' label and could potentially set aside some time to work on this. Has there been any progress yet, or would it pretty much be a clean slate to work from

@pawamoy
Copy link
Contributor Author

pawamoy commented Jun 5, 2024

@sevdesk-ryanp thanks for offering your help! There is an unfinished PR here: #1511, if you want to take a look (and maybe take over).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Easy things for newbies
Projects
None yet
Development

No branches or pull requests

9 participants