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

Add environment plugin #119

Merged
merged 7 commits into from Jun 11, 2017
Merged

Conversation

zsommers
Copy link
Contributor

Add environment plugin and documentation

This is built largely on the existing work of shanx in #39 and the ideas in #92 by adambreneck. This should address #92 and my manual testing was successful. Please let me know if there are any improvements that I can make.

Add environment plugin and documentation
@daisylb
Copy link
Collaborator

daisylb commented Apr 19, 2017

Hi Zach,

Thanks for this! Looks really good. I'll test and merge soon.

@zsommers
Copy link
Contributor Author

I totally forgot to activate the env vars after editing them, so here's an update that fixes that.

@justinmayer
Copy link
Owner

Glad this is making its way into Virtualfish. I'll review as well and provide some comments shortly.

@daisylb
Copy link
Collaborator

daisylb commented Apr 22, 2017

Okay, just had a look at it.

Everything I tested works great 👍 except for one issue: when I add blank lines to virtualfish-environment, I get the error message set: Variable name can not be the empty string, followed by usage info for set.

While I'm on it, something I just thought of now: it would also be good if comments starting with # could be supported; they currently produce a similar result to the above. Just testing if the first character of a line is # should do the trick, no need for anything more than that imo.

Oh, one last thing: The syntax of the virtualfish-environment file should probably be specified in the docs, too, maybe with an example file listing.

Add ability to parse empty lines and comments in the
`virtualfish-environment` file

Added description of `virtualfish-environment` file format to docs
@zsommers
Copy link
Contributor Author

@adambrenecki I added parsing for comments and empty lines along with updating the docs. Let me know if you see any other improvements that would be good. Thanks!

@zsommers
Copy link
Contributor Author

zsommers commented May 4, 2017

@adambrenecki Were there any other potential improvements that you spotted?

@justinmayer
Copy link
Owner

@zsommers: Sorry for the delay in reviewing, Zach. I've set aside time tomorrow to do some in-depth testing. Thanks for your patience!

@zsommers
Copy link
Contributor Author

zsommers commented May 5, 2017

@justinmayer No worries at all, just wanted to check in =)

@justinmayer
Copy link
Owner

After taking a look, I have to congratulate you, Zach, for some really nice work on this plugin. One of the main reasons I wanted to review this in detail is that I wrote my own implementation back in January but haven't had time to clean it up and submit it. The good news is that your pull request elegantly handles the following deficiencies in my implementation, which were on my to-do-someday list:

  • record existing values and restore them
  • expand variables found in values (instead of ending up with $FOO as a string)
  • easy way to edit the environment variables

Despite its deficiencies, my approach to the problem has a few benefits that I would like for us to consider adding to the excellent foundation established here in this pull request. Namely:

  • using the already well-established “dotenv” file convention
  • supporting Virtualfish’s Projects plugin

The .env convention

My team has found django-dotenv to be an excellent tool for setting environment varibles on a per-project basis, whereby key=value variables inside ~/projects/our-project/.env are automatically discovered and utilized by our Django project.

This convention is by no means relegated to Django or Python and is a very popular way of handling localized project-specific environment variables: https://encrypted.google.com/search?q=dotenv

Ergo, my suggestion would be to emulate what we already do in other places in Virtualfish, using .env as the default but allowing for the file name to be configurable:

if not set -q VIRTUALFISH_ENVIRONMENT_FILE
    set -g VIRTUALFISH_ENVIRONMENT_FILE .env
end

The key goal and benefit here is having a single environment variable file that can be used not only by Virtualfish, but also by other processes that can read the same standardized file name+format.

Projects plugin support

Related to the .env file convention is the fact that most supporting applications expect it to be in the project root — not inside in the virtual environment. My suggestion would be logic that assumes the project name is the same as the virtual environment name, looks first in the corresponding project root, and if no .env file is found there, then proceeds to look inside the virtual environment. Perhaps something like:

# Get virtualenv name and see if a corresponding project `.env` exists
set -l basename (basename $VIRTUAL_ENV)
if [ -f $PROJECT_HOME/$basename/$VIRTUALFISH_ENVIRONMENT_FILE ]
    set -l env_file_path $PROJECT_HOME/$basename/$VIRTUALFISH_ENVIRONMENT_FILE
else
    set -l env_file_path $VIRTUAL_ENV/$VIRTUALFISH_ENVIRONMENT_FILE
end

if [ -f $env_file_path ]
    [ ... logic that reads and sets the environment variables ... ]

A have a few other thoughts regarding some of the nuts and bolts of the implementation, but I first wanted to discuss these two higher-level topics, both of which I think would be very useful additions to what is shaping up to be an excellent new plugin for Virtualfish. 😃

What do you think?

@zsommers
Copy link
Contributor Author

zsommers commented May 5, 2017

You make some great points, Justin. I'm a fan of .env files generally and I think it makes sense to keep that sort of project configuration in the project root for the sake of explicitness. I'll take a crack at those changes over this weekend, hopefully.

I'm eager to hear your thoughts about the nuts and bolts. I'm pretty new to Fish, so any advice is welcome.

Thanks!

@justinmayer
Copy link
Owner

I never would have guessed that you are new to Fish. This is some really great work. Bravo!

I'll add some comments on the implementation, all of which are quite minor. 😄

Copy link
Owner

@justinmayer justinmayer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some very minor comments. Let me know what you think.

@@ -0,0 +1,56 @@
function __vfext_environment_activate --on-event virtualenv_did_activate
if test -f $VIRTUAL_ENV/virtualfish-environment
for line in (cat $VIRTUAL_ENV/virtualfish-environment)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything wrong with getting the file contents via cat, but Fish actually has a read command built-in. Here's the method I used:

function __vfext_environment_activate --on-event virtualenv_did_activate
    if test -r $env_file_path
        while read line;
            [ ... logic that sets the environment variables ... ]
        ; end < $env_file_path
    end
end

Again, I haven't done any performance profiling to compare how the two might differ in speed, but at least wanted to mention this method in case you decide it has merit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was a little on the fence about this just because this version reads a little more clearly to me from a code comprehension standpoint.

for line in (foo)
    # Do work
end

However, I also like using built-ins and the prevailing language constructs. Based on what you said and what I read here, the method you recommend is the way to go.


# Preserve existing env var with shared name
if set -q $key
set -xg __VF_ENVIRONMENT_OLD_VALUE_$key $$key
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use set -gx [...] in some places and set -xg [...] in others. Perhaps standardize on set -gx [...] for consistency? (I told you my comments would be minor!) 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call - consistency is important!


function __vfext_environment_deactivate --on-event virtualenv_will_deactivate
if test -f $VIRTUAL_ENV/virtualfish-environment
for line in (cat $VIRTUAL_ENV/virtualfish-environment)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here with regards to perhaps using Fish's read built-in command. Also, as above, it might be better to use test -r here, which ensures the file exists and is readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm definitely onboard with -r 😄

end

function __vf_environment --description "Edit the environment variables for the active virtual environment"
if set -q 'VIRTUAL_ENV'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure VIRTUAL_ENV neeeds to be in quotation marks here, but I'm not sure it's doing any harm, either. 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this and you're totally right - no quotes needed.

Add configurable filename
Add support function to find correct env file path
Update activate and deactivate with support function
Update edit env file with support function and to actually remove
  deleted vars from current env
Use `read` instead of `cat`
Standardize `set -gx`
@zsommers
Copy link
Contributor Author

zsommers commented May 8, 2017

@justinmayer I made the updates you suggested. I'm interested in your feedback on my implementation of the Project support. I'm not sure dirtying the environment with an extra variable was the best way to handle it, but the logic for finding the correct environment file path was messy enough that I didn't want to replicate it everywhere it was needed. Thanks!

@justinmayer
Copy link
Owner

Hey Zach. Thanks for the follow-up enhancements. I agree that trying to keep the environment clean is a worthy goal, while recognizing that your supporting function to return the correct environment file path helps to keep things DRY.

Instead of setting a global variable, an alternate solution might be for the function to "return" the environment file path via echo. (I've seen this done before in other places in Fish-land.) I'll add some comments to show what I'm talking about. Following is an alternate version I whipped up, along with some other minor suggested changes: https://dpaste.de/tnEF

Let me know what you think. 😄

Copy link
Owner

@justinmayer justinmayer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some comments here to try to elaborate on my suggested changes. If my feedback here is confusing in any way (and I fear it might be), please don't hesitate to let me know! 😅

else
set -g VIRTUALFISH_ENVIRONMENT_FILE_PATH $vf_path
end
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably use echo instead of setting a global variable. I also realized the basename call doesn't need to be made if $PROJECT_HOME is not defined, so that should probably be moved further below. I also (very slightly) simplified the environment file path determination logic:

function __vfsupport_env_file_path --description "Return full path to environment file"
    set -l vf_env_file_path $VIRTUAL_ENV/$VIRTUALFISH_ENVIRONMENT_FILE
    # Check if Projects plugin is used
    if set -q PROJECT_HOME
        set -l vf_name (basename $VIRTUAL_ENV)
        set -l proj_env $PROJECT_HOME/$vf_name/$VIRTUALFISH_ENVIRONMENT_FILE
        if test -r $proj_env
            echo $proj_env
        else
            echo $vf_env_file_path
        end
    else
        echo $vf_env_file_path
    end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your echo suggestion a lot - that does exactly what I was searching for. The reason I used more complicated logic when the Projects plugin is present was for creating a new env file. Here's how my thinking goes:

  1. If we aren't using Projects, place the .env file in the virtualenv directory
  2. If we are using Projects and there is a .env in the project directory, use that file
  3. If we are using Projects and there is not a .env file in the project directory and there is and .env file in the virtualenv directory use that.
  4. (This is why it got a little more complicated) If we are using Projects and there isn't a .env file in either the project or virtualenv directories, create a new file in the project directory.
    Case 4 is why I have the logic like this:
if set -q PROJECT_HOME
    if not test -r $proj_env
        and test -r $vf_env
        # Use the virtualenv only when that's been established with a file there
    else
        # Prefer project directory
    end
else
    # Not using Projects
end

I think adding some comments and perhaps reordering the logic might make it a bit more clear.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I hadn't considered that the logic was taking environment file creation into account. Now your logic makes more sense. I agree some comments might help clarify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't until I was working on the environment command that I realized that was an issue, just because it opens the file in the $EDITOR regardless of whether it exists. Kind of an edge case, but it caught me. 😄

for line in (cat $VIRTUAL_ENV/virtualfish-environment)
__vfsupport_set_env_file_path

if test -r $VIRTUALFISH_ENVIRONMENT_FILE_PATH
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section would become:

set -l env_file_path (__vfsupport_env_file_path)

if test -r $env_file_path

end

# Eval to allow for expanding variables, e.g. PATH=$PATH foo
set -gx $key (eval echo $value)
end
end < $VIRTUALFISH_ENVIRONMENT_FILE_PATH
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end < $env_file_path

if set -q 'VIRTUAL_ENV'
eval $EDITOR $VIRTUAL_ENV/virtualfish-environment
if set -q VIRTUAL_ENV
__vfsupport_set_env_file_path
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set -l env_file_path (__vfsupport_env_file_path)

# Deactivate before applying new env vars to avoid stomping stashed values
__vfext_environment_deactivate
eval $EDITOR $VIRTUALFISH_ENVIRONMENT_FILE_PATH
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eval $EDITOR $env_file_path

Add comments for additional clarity
Reorder some logic for legibility
@justinmayer
Copy link
Owner

Latest code changes look good to me, Zach. Perhaps update the docs to match the revised default .env environment file name?

Once the docs have been updated, I think we are good to merge.

@zsommers
Copy link
Contributor Author

Alright, @justinmayer, sorry for the delay! I've updated the docs and I hope they're pretty clear. Let me know what you think. Thanks!

Copy link
Owner

@justinmayer justinmayer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me. Let's get this merged! 👍

@zsommers
Copy link
Contributor Author

Sounds like a plan! Does everything look shipshape to you, @adambrenecki ?

@justinmayer
Copy link
Owner

Hey @adambrenecki. Just checking in. Any thoughts on this PR?

@daisylb
Copy link
Collaborator

daisylb commented Jun 11, 2017

Sorry, everyone! I've been flat out at Uni—and I still am :(

I've had a quick flick through the docs and I'm happy with them and the design of the feature, so I'm going to trust @justinmayer's judgement w.r.t. reviewing the actual code, and blindly merge this :)

@daisylb daisylb merged commit 5c5203c into justinmayer:master Jun 11, 2017
@zsommers
Copy link
Contributor Author

Thanks @adambrenecki ! Good luck at Uni and let me know if you come across any issues with this. Thanks for all your help on this, @justinmayer

justinmayer added a commit to justinmayer/tackle that referenced this pull request Jun 13, 2017
Vendors plugin, which automatically sets/restores environment variables
upon virtual environment activation/deactivation. For more detail, see:
justinmayer/virtualfish#119
@justinmayer
Copy link
Owner

Thanks, @adambrenecki. The good news is that if you ever come back to the code later and decide it could be improved, it's totally mutable! 😉

@zsommers: Thanks again for the contribution. Excited to have it merged.

simotek pushed a commit to simotek/tackle that referenced this pull request Aug 12, 2019
Vendors plugin, which automatically sets/restores environment variables
upon virtual environment activation/deactivation. For more detail, see:
justinmayer/virtualfish#119
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

Successfully merging this pull request may close these issues.

None yet

3 participants