mottosso / bleeding-rez

Rez - Reproducible software environments for Windows, Linux and MacOS
GNU Lesser General Public License v3.0
72 stars 10 forks source link

rez env --isolated #36

Open mottosso opened 5 years ago

mottosso commented 5 years ago

Goal

Prevent parent environment from affecting a resolved context.

Motivation

Currently, if you have Python available on your PATH at the time of entering a rez env, then that Python will remain available inside.

That's really convenient but can be unexpected if you also have a Rez package providing a Python of its own.

$ rez env python-3
> $ python
# Which Python did I get?

You would expect to see the Python you asked for and fingers crossed that's the case. However, to make things worse, let's say you've got a project that depends on Python.

$ rez env myproject six requests -- python -m myproject

With this, your project successfully runs. You clear every test, polish each knob and finally submit your creation to PyPI. Only to find that while it works On My Machine (tm) it doesn't work for anyone else. Why?

Turns out, your project depends on Python and worked because Python was available on your system and your system alone. What should have happened is for the above command to say..

$ rez env myproject six requests -- python -m myproject
python : The term 'python' is not recognized as the name of a cmdlet, function, script file, or operable program.

At which point you put your palm to your forehead and append python to the request.

$ rez env python myproject six requests -- python -m myproject

Implementation

To the novice user, retaining access to Python and rez itself is an expected attribute of entering rez env. Any executable already on a users PATH is expected to still be there from within a context; Rez adds features to your command-line session, it doesn't take them away.

Only once you've committed and gotten used to the Rez semantics do you start looking for safety and consistency, at which point you'll discover --isolated.

$ rez env --isolated myproject

With --isolated, the resulting environment is no longer inherited from the parent, but is instead created from scratch using an OS-specific set of members, and those are:

# Windows
USERNAME
SYSTEMROOT
COMSPEC
WINDIR
PROMPT
PATHEXT
OS
TMP
TEMP

# Linux
DISPLAY

Most of these are forwarded as-is from the parent environment, except PATH which is re-created based off of the location of a few key OS-specific executables.

# Windows
cmd
powershell

# Linux
bash

# MacOS
bash

The absolute path is looked up with which and stripped to its parent directory, which is the added to the isolated PATH.

All other members of the environment are ignored.

rez build

So how does this affect other commands, like build? For the same reasons as rez env, a build should also be unaffected by what's provided by the parent environment, so odds are it's a good fit for having --isolated be the norm.

rez config

For advanced users --isolated is likely the norm, and for such cases there should be a global configuration option to make it default.

# Do not let subshell inherit parent environment
isolated_subshell = True

Which is then automatically made available as an environment option.

$ set REZ_ISOLATED_SUBSHELL=True

By making --isolated opt-in, rather than an installation default, we've enhanced the new-user experience and simplified introductory tutorials.

mottosso commented 5 years ago

Currently, the PATH is determined by which("cmd") on Windows and similarly on Linux. But, there's no reason PATH can be fixed on each platform. E.g. on Windows, the PATH on a newly installed system is small, and we could replicate that. On CentOS, the same applies. It would (1) enhance the predictability and reliability of the environment and (2) reduce IO calls, as the above currently has to query the OS for whether cmd and friends exists, to find their absolute paths.

To accommodate for systems or environment that aren't following the norm or have special requirements, provide a REZ_ENVIRONMENT and REZ_ENVIRONMENT_FILE to explicitly provide an environment for Rez.

$env:REZ_ENVIRONMENT = "{'PATH': 'c:\windows\system32', 'USERNAME': '$env:USERNAME'}"

The REZ_ENVIRONMENT would be a JSON-qualified string, whereas REZ_ENVIRONMENT_FILE would be an actual JSON file, to accommodate for large environments.

mottosso commented 5 years ago

At the moment, in order to call the Rez Python package, without having it present in either site-packages or PYTHONPATH, the current working directory is set to its parent directory. That way, it's visible for as long as that's the directory we're in.

Since we want to preserve the working directory upon entering a context, the original working directory is stored in an environment variable and later passed to the resulting subshell. That's working well.

However, the second subshell currently picks this up as well, which means it will return to this directory each time. That's a bug.

$ cd peaches
$ rez env --isolated
> $ cd ..\notpeaches
> $ rez env --patch
>> $ pwd
peaches