Skip to content

improvement : Able to get environment variables from shell #7301

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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

ajafri2001
Copy link

Co-authored-by : Tomasz Godzik [email protected]
Co-authored-by : Marcin Sobejko [email protected]

Related to: Issue-7252

I want to finish this, what would the next steps look like?

@tgodzik
Copy link
Contributor

tgodzik commented Mar 14, 2025

First step would be to try and set it up for yourself to see if it works, I've also been thinking about how to test it and I think we can do a test in https://github.com/scalameta/metals/blob/main/tests/slow/src/test/scala/tests/sbt/SbtBloopLspSuite.scala#L39

You could copy one of the basic tests and add something that would fail like:

sys.env.get("MY_ENV_VAR").get

to build.sbt file in the test.

Later we can create a fake bash, which would add an export for MY_ENV_VAR and invoke normal bash. We the use the new defaultShell setting like in

_ <- server.didChangeConfiguration(userConfig.toString())

but we would do copy on the default userConfig.copy(defaultShell = Some(fakepath))

After that it should not fail to import the sbt definition into bloop.

Let me know if you get stuck!

@ajafri2001
Copy link
Author

I have not deserted this 😄.

Co-authored-by : Tomasz Godzik <[email protected]>
Co-authored-by : Marcin Sobejko <[email protected]>
@ajafri2001 ajafri2001 changed the title improvement : Able to get environment variables from vscode improvement : Able to get environment variables from shell Mar 27, 2025
@@ -87,15 +92,22 @@ class ShellRunner(time: Time, workDoneProvider: WorkDoneProgress)(implicit
): Future[Int] = {
val elapsed = new Timer(time)
val env = additionalEnv ++ JdkSources.envVariables(javaHome)

val shellArguments = userConfiguration().defaultShell match {
case Some(shell) => List(shell, "-i", "-l", "-c", args.mkString(" "))
Copy link
Contributor

Choose a reason for hiding this comment

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

would all shells have the same options though?

Copy link
Author

Choose a reason for hiding this comment

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

I really do think so. I've tried bash, zsh and fish so far, and they all seem to work with the above flags. If some obscure shell doesn't work with some of the flags, we should be able to adjust them like this.

val shellArguments = userConfiguration().defaultShell match {
  case Some(shell) =>
    shell match {
      case "/usr/bin/fish" => List(shell, "-c", args.mkString(" "))
      case _               => List(shell, "-i", "-l", "-c", args.mkString(" "))
    }
  case None => args
}

what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

People might have the fish shell located somewhere else, so this wouldn't work. Do we need the additional options though?

Suggested change
case Some(shell) => List(shell, "-i", "-l", "-c", args.mkString(" "))
case Some(shell) => shell :: args

should work, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

OR you could do Some(shell) if shell.contains("fish") to be on the safe side.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should also pack it with Try() and return an empty map if there is an exception

|/build.sbt
|scalaVersion := "${V.scala213}"
|sys.env.get("MY_ENV_VAR").get
|""".stripMargin
Copy link
Contributor

Choose a reason for hiding this comment

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

You can create a bash script here and provide that as different defaultShell

Copy link
Author

Choose a reason for hiding this comment

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

I think I'm stuck here and need help 😓. I'm not sure how the fake-shell test is supposed to work, but I pushed a draft test I put together for you to take a look at whenever you have the time.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good, though you might need to add executable flag to fake-shell.sh

We should add:
_ <- server.executeCommand(ServerCommands.ImportBuild) at the end and it it doesn't fail we should be good.

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

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

Sorry for getting back so late to it!

@@ -87,15 +92,22 @@ class ShellRunner(time: Time, workDoneProvider: WorkDoneProgress)(implicit
): Future[Int] = {
val elapsed = new Timer(time)
val env = additionalEnv ++ JdkSources.envVariables(javaHome)

val shellArguments = userConfiguration().defaultShell match {
case Some(shell) => List(shell, "-i", "-l", "-c", args.mkString(" "))
Copy link
Contributor

Choose a reason for hiding this comment

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

People might have the fish shell located somewhere else, so this wouldn't work. Do we need the additional options though?

Suggested change
case Some(shell) => List(shell, "-i", "-l", "-c", args.mkString(" "))
case Some(shell) => shell :: args

should work, no?

@@ -455,6 +456,7 @@ object UserConfiguration {
|default to using it instead of Bloop.
|""".stripMargin,
),
UserConfigurationOption("default-shell", "", "", "", ""),
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's make sure we explain the option properly

|/build.sbt
|scalaVersion := "${V.scala213}"
|sys.env.get("MY_ENV_VAR").get
|""".stripMargin
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good, though you might need to add executable flag to fake-shell.sh

We should add:
_ <- server.executeCommand(ServerCommands.ImportBuild) at the end and it it doesn't fail we should be good.

workspace.resolve("sbt").createDirectories()

for {
_ <- initialize(
Copy link
Contributor

Choose a reason for hiding this comment

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

Ach and initialize itself will fail, but you can use recover{ case t => } to make sure the correct exception is being thrown. That's why the tests now fail, it tries to import automatically and it does fail, which is expected.

@ajafri2001
Copy link
Author

ajafri2001 commented Apr 17, 2025

Sorry, for the late reply, I was traveling after wrapping up my coursework for the semester 😵.

 - case Some(shell) => List(shell, "-i", "-l", "-c", args.mkString(" "))
 + case Some(shell) => shell :: args

I did this because of the following observation, if I have an env variable in my ~/.bashrc and my current shell is zsh, If I type /usr/bin/bash env | grep ARTIFACTORY_USERNAME | wc -l to grep for the env variable, it doesn't work, I'd have to change it and add flags like /usr/bin/bash -i -l -c "env | grep ARTIFACTORY_USERNAME | wc -l" for it to work.

An example using fish shell, for which I placed the env variable under config.fish

❯ /usr/bin/fish env | grep ARTIFACTORY_USERNAME | wc -l
error: Error reading script file 'env':
No such file or directory (os error 2)
0
❯ /usr/bin/fish -i -l -c "env | grep ARTIFACTORY_USERNAME | wc -l"                                                                                                                                                  
1

Note - This is all done via heuristics and informal reasoning 😄 and there actually might be a simpler solution, For me, the code change wasn't working until I added all the flags.

@ajafri2001 ajafri2001 marked this pull request as ready for review April 18, 2025 15:31
@ajafri2001
Copy link
Author

Hi @tgodzik, whenever you have some time, would you mind taking a look at this? 🙂 I was also wondering where it might make sense to include a try block. 🤔 The metals.log file already provides very helpful error messages if the user messes up, so I'm not sure if it's too necessary. Thanks a lot! 🥳

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.

2 participants