Jump to content

"Global" Environment Variables for workflows?


Recommended Posts

  • Alfred 4.5 build 1249
  • macOS 11.5

 

When Alfred executes Bash scripts with the standard shebang of `#!/usr/bin/env bash`, it runs a non-login, non-interactive shell (`/bin/bash foo.sh`). This means that ~/.bashrc, ~/.bash_profile, /etc/bashrc etc are not sourced. This makes it difficult to set certain environment parameters, notably $PATH, $HOMEBREW_PREFIX etc.

 

I found that setting $BASH_ENV to ~/.bashrc is a workaround. As per the Bash manpage, that var causes bash to source the specified file on noninteractive shells.

 

TL;DR— is there any way to globally set a variable to apply to all workflows? I can definitely set it for each individual wf, but this would be a nice feature...

 

Link to comment

Well here's the low hanging fruit @deanishe ... 

 

I just got my first M1 Mac (a Mini), and am now syncing my Alfred configs between it and my trusty old Intel MacBook Air.
 

I use Homebrew on both, and a handful of my workflows require commands from it.

 

I learned the hard way that on arm64, Homebrew likes to live in /opt/homebrew -- vs /usr/local/bin on x86. So I can't hardcode paths... and $PATH must be different on each Mac.  This was my first task: unwinding all those hardcoded /usr/local/bin/foo... references.

 

The recommended best practice is to export a $HOMEBREW_PREFIX env var and add it to your PATH. This is done in my ~/.bashrc currently. This also ensures that the standard shebang of #!/usr/bin/env bash results in Bash5 instead of the ancient Bash3 that is rotting away in the default OS.

 

This all works great in a normal login shell, but not in Alfred.

 

I found the cleanest solution to be setting $BASH_ENV which causes my .bashrc to be sourced inside Alfred.

 

For me, it would be very useful to be able to set this globally to apply it to all scripts -- I had to do a lot of hunting and pecking through workflows to figure out where it was needed and why certain things were broken. If I ever need to source a different file (e.g. if I switch to zsh) it would be a chore to update all those workflows again.

Link to comment
1 hour ago, luckman212 said:

I learned the hard way that on arm64, Homebrew likes to live in /opt/homebrew -- vs /usr/local/bin on x86.

 

Oh yeah. So crappy of Apple to require that change.

 

1 hour ago, luckman212 said:

The recommended best practice is to export a $HOMEBREW_PREFIX env var and add it to your PATH.

 

For an interactive shell, yes. Requiring users to fiddle with $PATH to run something like an Alfred workflow would be poor practice, and a recipe for lots of support requests from users who don't know what the hell that is. I don't see Andrew adding the ability to do that to Alfred itself because then he and Vero would get the support requests.

 

The best solution – which kinda sucks for the developer – is probably to stick with bash/zsh as your workflow language and dynamically set the path to the executable:

 

prog=/usr/local/bin/prog
[[ "$( /usr/bin/arch )" = "i386" ]] || prog=/opt/homebrew/blah/prog
$prog "$1"

 

Or in the case of programs that need to call other programs: export PATH=/usr/local/bin:/opt/homebrew/whatever:$PATH

Edited by deanishe
Link to comment
7 hours ago, deanishe said:

Requiring users to fiddle with $PATH to run something like an Alfred workflow would be poor practice, and a recipe for lots of support requests from users who don't know what the hell that is

 

The whole point of this is so the users DON'T have to faff around with PATH or any other vars to make scripts that work in Alfred the same way they do "outside of Alfred". This setting would be tucked away somewhere (along with the myriad other settings in Alfred) and default to {null} so it would not do anything until & unless needed. Not sure why it should cause any additional support requests.

 

Quote

Or in the case of programs that need to call other programs: export PATH=/usr/local/bin:/opt/homebrew/whatever:$PATH

 

I thought about overloading PATH like this too. But, we'd be back to having to manually set the PATH variable for each individual workflow, which is what I'm trying to avoid in the first place. Also, on the ARM machine /usr/local/bin also exists. So there's potential for the wrong binary to be executed.

 

$HOMEBREW_PREFIX/bin/my_prog is faster (no need to traverse the whole path) and also predictable.

Link to comment
1 hour ago, luckman212 said:

The whole point of this is so the users DON'T have to faff around with PATH 

 

Indeed they shouldn't. It's something the workflow developer should take care of.

 

1 hour ago, luckman212 said:

Not sure why it should cause any additional support requests.

 

Because if it's in Alfred's preferences, the Alfred team will get support requests for it. When the configuration is within the workflow, most users will also understand they need to speak to the workflow's developer, not Alfred's, and the Alfred team has a legitimate reason to decline the requests from those who don't.

 

1 hour ago, luckman212 said:

But, we'd be back to having to manually set the PATH variable for each individual workflow, which is what I'm trying to avoid in the first place.

 

Well, yes. If your workflow requires a non-standard PATH to work, it seems to me it should be your workflow’s responsibility to modify it.

 

Remember, you’re talking about everybody’s workflows here, not just your own. What about all the breakages and headaches changing their workflows’ PATH would cause other people?

 

1 hour ago, luckman212 said:

Also, on the ARM machine /usr/local/bin also exists.

 

That's why I suggested a method based on CPU architecture, not the existence of the directories.

Edited by deanishe
Link to comment
4 hours ago, luckman212 said:

So there's potential for the wrong binary to be executed.

 

I view that as an argument against the requested feature. A Workflow developer expects a certain tool to be available at a certain path, but because the user has overridden some global environment variable in Alfred, the wrong thing is executed and the Workflow doesn’t work.


Different macOS or Alfred versions breaking a Workflow is understandable. A single setting doing the breakage inconsistently, not so much.

Edited by vitor
Link to comment
11 minutes ago, vitor said:

A Workflow developer expects a certain tool to be available at a certain path, but because the user has overridden some global environment variable in Alfred, the wrong thing is executed and the Workflow doesn’t work.

 

I think we're on the same side here. My suggestion would make it precisely so the workflow could explicitly target the right version of a tool, instead of leaving it up to whatever the user has in their  $PATH  which could be incorrect.

 

Hypothetical example:

I create a workflow that requires the  jq  binary, which is available in Homebrew, but can also be installed in other ways. My workflow tests for the existence of jq and throws an error if it's not found, prompting the user to put it in their  $PATH .

 

One way to do this would be

if [[ ! $(command -v jq) ]]; then
  ..throw error..
fi

 

A few possible solutions would be:

 

Method #1

Bundle a copy of jq with your workflow (not sure if licenses will allow this) and hardcode your scripts to use that version with dot-slash:

#!/usr/bin/env bash

blah blah
./jq '.foo[] | .bar'
blah

 

Drawbacks:

- Might run into problems with license

- Which version of the tool to include? Intel? ARM? What if there's no universal binary? Now we need 2 versions of the tool, and extra code like  if [[ $(arch) == "i386" ]]; then foo...  etc.

- Tool can't be shared among workflows, leads to redundancy and mismatched versions.

 

Method #2

Ask the user or have steps in the workflow itself that set the environment variables correctly. E.g. prompt them to set the  $PATH  or some var called  $JQ_PATH  in the workflow itself. And then in your scripts, have someting like this

 

#!/usr/bin/env bash

if [[ -z $JQ_PATH ]]; then
  ..prompt user to set JQ_PATH..
  exit 1
fi
if [[ ! -x $JQ_PATH ]]; then
  ..tell user they have specified an invalid binary path..
  exit 1
fi

blah blah | $JQ_PATH '{ foo: bar }'

 

Drawbacks:

- May be frustrating and error-prone for the user to do these steps

- Very little guarantee that they will install the tool correctly

- If workflow is sync'ed across multiple machines, no guarantee all machines will have the same architecture, PATH etc.

 

Method #3

Use Homebrew, MacPorts etc.

 

This is in my opinion a better option. It provides a consistent way to share tools across machines and workflows, and a reasonable guarantee that those tools will be up-to-date and in a consistent state. It would allow the workflow-embedded scripts to simply reference unversioned, unqualified versions of the tools, e.g.  jq '.foo'  or even better  $HOMEBREW_PREFIX/bin/jq '.foo' . This would work across machines & architectures without any fuss. All that would be required would be a simple global environment variable:  $HOMEBREW_PREFIX  which could be defined directly in Alfred, or via  ~/.bashrc  or any other env file if the  $BASH_ENV  var is set.

 

Drawbacks:

- requires one-time setup (user must put in their Homebrew path once in the global vars area. This could be made easier if it had a fallback value (/usr/local/bin on Intel and /opt/homebrew on arm64) since 99.5% of people would use the defaults.

 

Advantages:

- One or two clicks is all it takes to enable this for the entire set of workflows

- Simple to override (workflows can still hardcode paths, embed their own versions of tools, or override PATH variable etc) as before

- Simple to change/update (e.g. if switching shells, moving from Homebrew to MacPorts, NIX etc. (again, one place to go instead of hunting through dozens of individual workflows)

- Cross platform, well-suited for users who use multiple machines and sync workflows

 

I'm sure there are other methods and ways, these are just some that come to mind. I'd like to know @Andrew's thoughts too if he cares to read.

Link to comment

Method 3 isn’t an argument for having a global environment variable setting, but for Alfred to understand Homebrew by default. Those aren’t the same thing and I can see a stronger case for the latter (limited scope; greater control by Alfred; harder for users to screw up). The list of drawbacks is too short for that one; it wouldn’t require setup “once”, but (at most) once per Workflow to install the necessary tools.


Either way, it’s not that different from telling a user to brew install whatever then having PATH set inside the Workflow with both Homebrew directories (/opt first). I remain unconvinced that a global environment variable is a better solution than that, given the drawbacks already listed by @deanishe.


There’s a fourth method. It’s similar to method 1 with none of your listed drawbacks. That method is live in about a dozen of my Workflows and I’ve implemented it in other people’s with success.

Edited by vitor
Link to comment
2 hours ago, vitor said:

Those aren’t the same thing and I can see a stronger case for the latter (limited scope; greater control by Alfred; harder for users to screw up).


Agree 100%. Alfred setting a variable like HOMEBREW_PREFIX isn't going to screw other things up the way messing with PATH would.

 

2 hours ago, luckman212 said:

requires one-time setup (user must put in their Homebrew path once in the global vars area.


Wrong approach, imo. Alfred should set that variable itself automatically (based on CPU arch), with the possibility for users to override it if they've used a non-standard location.

 

2 hours ago, luckman212 said:

I'd like to know @Andrew's thoughts too if he cares to read.


I'm sure he's reading the thread, and I'm equally certain he'd say something similar to what Vitor and I have been saying.

 

Andrew goes out of his way to preserve backwards compatibility with older workflows, and I've never seen him accept a feature request that is likely to break stuff or to produce support requests in large numbers, both of which an option to mess with the global PATH in Alfred would do.

 

One way or another, if you add that option to Alfred, a bunch of workflow developers are going to require users to set it to different, mutually-incompatible values because so many developers think their software is more important than everyone else's.

Edited by deanishe
Link to comment
8 minutes ago, deanishe said:

an option to mess with the global PATH in Alfred

 

Just want to clarify—I never was asking for a "global PATH" setting. Just a way to set global variables (e.g. $MY_COOL_API_KEY ). There could (should) even be a warning about not setting a global PATH variable right in the dialog box...

Link to comment
48 minutes ago, luckman212 said:

Just a way to set global variables (e.g. $MY_COOL_API_KEY )


Why? Wouldn't a single HOMEBREW_PATH variable managed by Alfred itself be a better solution to the problem you're trying to solve?


General global variables is a bit of a can of worms, imo.

 

I mean, how would global variables interact with syncing? What would be the point in putting $MY_COOL_API_KEY in a global variable if it won't be synced between machines? Because you obviously can't have architecture-specific variables syncing between machines.

 

Does every variable get a "sync this" checkbox? And if so, who's going to deal with all the support requests from people who don't understand they're syncing a setting that's only compatible with one of their devices?

 

48 minutes ago, luckman212 said:

There could (should) even be a warning about not setting a global PATH variable right in the dialog box...


I think Alfred already prevents you from messing with PATH in its variable configuration sheet.

Edited by deanishe
Link to comment
1 hour ago, deanishe said:

Alfred should set that variable itself automatically (based on CPU arch), with the possibility for users to override it if they've used a non-standard location.


Homebrew itself doesn’t support non-standard locations. You can do it, but if you choose to you are on your own against unexpected behaviour (though PRs that would fix it are accepted). So Alfred wouldn’t even need the override setting and could point to Homebrew policy as the reason.

Link to comment
42 minutes ago, deanishe said:

Wouldn't a single HOMEBREW_PATH variable managed by Alfred itself be a better solution to the problem you're trying to solve?

 

Yes that would definitely solve most of the cases! I suggest the variable be $HOMEBREW_PREFIX however, just to align with what they currently use [https://docs.brew.sh/Manpage#shellenv].

 

Quote

I think Alfred already prevents you from messing with PATH in its variable configuration sheet.

 

Not anywhere that I can see, but that'd probably be a good addition.

Thanks @deanishe

Link to comment

@vitor I like the idea of using brew shellenv to get the values. But, isn't there a chicken vs. egg problem? How to know which brew to invoke without first having the prefix? I suppose it could just go with the defaults as mentioned farther up, or...

 

How about a field under Advanced that reads:

 

Path to `brew` command:  [______________________]  ( Default for this system: /opt/homebrew/bin/ )

 

If users simply do nothing, then it uses the default. But if some lunatic has their brew at /users/john/stuff/brew, then they can simply fill that into the field.

Edited by luckman212
pronoun
Link to comment

As mentioned above:

 

1 hour ago, vitor said:

Homebrew itself doesn’t support non-standard locations. You can do it, but if you choose to you are on your own against unexpected behaviour (though PRs that would fix it are accepted). So Alfred wouldn’t even need the override setting and could point to Homebrew policy as the reason.

 

It wouldn’t be a controversial decision (context: I’m a Homebrew developer).

Link to comment
9 hours ago, luckman212 said:

How about a field under Advanced that reads

 

Should be fully automatic, imo. If it's even possible to override, you should need to run defaults in a shell to do so.

 

This is not a knob the typical user needs to know exists: anything but the default is almost certainly a bad idea.

 

11 hours ago, luckman212 said:

Just want to clarify—I never was asking for a "global PATH" setting.

 

I forgot to mention earlier: Changing PATH is frequently your only option. Setting HOMEBREW_PREFIX isn’t going to work if the program you’re running runs other Homebrew programs.

Edited by deanishe
Link to comment
27 minutes ago, deanishe said:

Setting

HOMEBREW_PREFIX isn’t going to work if the program you’re running runs other Homebrew programs

 

Not sure what you mean here... if Alfred exports HOMEBREW_PREFIX then any programs forked from it should inherit that value, no? Maybe I'm misunderstanding.

Link to comment
7 hours ago, luckman212 said:

then any programs forked from it should inherit that value, no?

 

Sure, the variable will still be set, but nothing will actually pay any attention to it, will it?

 

Let’s say you run $HOMEBREW_PREFIX/bin/youtube-dl. When YTDL wants to call FFMPEG, it doesn't consider $HOMEBREW_PREFIX or its own path. It just runs the command ffmpeg, so if you haven’t altered PATH to include $HOMEBREW_PREFIX/bin, where ffmpeg is, the call will fail.

 

In situations like this, you will need to do export PATH=$HOMEBREW_PREFIX/bin:$PATH

Edited by deanishe
Link to comment
3 minutes ago, Andrew said:

if it leads to a change / improvement in Alfred

 

If the goal is making using Homebrew programs easier, an option in each workflow's configuration to activate Homebrew support might be the best solution. Alfred could then alter the PATH passed to the workflow to include Homebrew’s bin directory.

 

That should make Homebrew Just Work for those who need it, without any unwanted side-effects for those that don't.

Link to comment
3 hours ago, deanishe said:

an option in each workflow's configuration to activate Homebrew support might be the best solution. Alfred could then alter the PATH passed to the workflow to include Homebrew’s bin directory.


It could go one further and allow specifying the dependency names. On Workflow install it could check for the existence of those packages and show a message if they’re not installed:
 

“You’re missing dependencies for this Workflow. Install them with:

 

brew install pkg-name”

 

If the user does not have Homebrew, an extra sentence:

 

”To learn more about Homebrew, visit https://brew.sh” (or an Alfred documentation link).


On the message there might be a way to disable the Homebrew requirement and continue using the Workflow, for advanced users. Still have to think of the advantages and drawbacks of this one.

 

I though of the possibility of the requirements being binary names instead of Homebrew package names. The advantage of the former is that a user doesn’t truly need Homebrew installed, just the binary, but Homebrew package names don’t always map exactly to package names (making the automatic dependency message harder). Specifying the package name, on the other hand, allows for specific versions and taps to be set.

Edited by vitor
Link to comment
8 minutes ago, vitor said:

If the user does not have Homebrew, an extra sentence:

 

”To learn more about Homebrew, visit https://brew.sh” (or an Alfred documentation link

 

Agree with this, but I don’t think I’d go as far as having Alfred bother with specific dependencies. It’s a lot of work just to show “Run brew install blah blah blah in Terminal.app” when the workflow author can simply write that in the workflow description.

Edited by deanishe
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...