pyinvoke / invoke

Pythonic task management & command execution.
http://pyinvoke.org
BSD 2-Clause "Simplified" License
4.32k stars 365 forks source link

How to handle paths in invoke commands running from any subdirectory? #831

Open shimonacarvalho opened 2 years ago

shimonacarvalho commented 2 years ago

Invoke seems to be clever enough that I can be in any subdirectory and call an invoke task. However I'm wondering how to ensure that the tasks run the same way without a lot of path massaging.

So for example, my structure is

code/
code/server/<django files>
code/tasks/build.py

I usually run inv build.test from the code/ directory which then does the following:

    with c.cd('server/'):
        c.run('python manage.py test')

However if I'm in code/server/ I can also run inv build.run and while it's nice that it finds the task definition, obviously it fails because it can't cd server/. Not a big deal in this case but in a more complicated task it could fail halfway through a sequence of commands.

Is there a clean way to handle this other than splicing paths together? If not, is there a function that will return the absolute path of the invoke root of /users/xxx/repo/code/ to me so I can accurately splice the paths?

I've tried __file__ which returns code/server/build.py and os.cwd returns code/server/ neither of which is the invoke root.

shimonacarvalho commented 2 years ago

I ended up writing this function in a known location so that anywhere in the code I can retrieve the "root directory".

def root_dir() -> str:
    """
    Returns the invoke root directory (aka the project root)
    so that tasks can use it and be directory agnostic 
    """
    return path.dirname(path.dirname(__file__))

Would love to hear of a better way.