Skip to content

Commit

Permalink
Automatic detect default shell on Mac and Linux
Browse files Browse the repository at this point in the history
Implements #32.
  • Loading branch information
edassis committed Dec 16, 2023
1 parent cbcf37a commit 4407851
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 39 deletions.
99 changes: 67 additions & 32 deletions GodotEnv/src/common/clients/EnvironmentVariableClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,37 @@ namespace Chickensoft.GodotEnv.Common.Clients;
using Chickensoft.GodotEnv.Common.Models;
using Chickensoft.GodotEnv.Common.Utilities;

public interface IEnvironmentVariableClient {
public interface IEnvironmentVariableClient
{
string GetUserEnv(string name);
Task SetUserEnv(string name, string value);
Task AppendToUserEnv(string name, string value);
string GetDefaultShell();
bool IsShellSupported(string shellName);
}

public class EnvironmentVariableClient :
IEnvironmentVariableClient {
IEnvironmentVariableClient
{
public IProcessRunner ProcessRunner { get; }
public IFileClient FileClient { get; }
public IComputer Computer { get; }

public EnvironmentVariableClient(
IProcessRunner processRunner, IFileClient fileClient, IComputer computer
) {
)
{
ProcessRunner = processRunner;
FileClient = fileClient;
Computer = computer;
}

public async Task SetUserEnv(string name, string value) {
switch (FileClient.OS) {
public async Task SetUserEnv(string name, string value)
{
var defaultShellName = GetDefaultShell();

switch (FileClient.OS)
{
case OSType.Windows:
var currentValue = GetUserEnv(name);
Environment.SetEnvironmentVariable(
Expand All @@ -37,29 +46,29 @@ public async Task SetUserEnv(string name, string value) {
currentValue = GetUserEnv(name);
break;
case OSType.MacOS:
var zshRcPath = FileClient.Combine(FileClient.UserDirectory, ".zshrc");
FileClient.AddLinesToFileIfNotPresent(
zshRcPath, $"export {name}=\"{value}\""
);
break;
case OSType.Linux:
var bashRcPath = FileClient.Combine(FileClient.UserDirectory, ".bashrc");
FileClient.AddLinesToFileIfNotPresent(
bashRcPath, $"export {name}=\"{value}\""
);
break;
{
var rcPath = FileClient.Combine(FileClient.UserDirectory, $".{defaultShellName}rc");
FileClient.AddLinesToFileIfNotPresent(
rcPath, $"export {name}=\"{value}\""
);
break;
}
case OSType.Unknown:
default:
break;
}
}

public async Task AppendToUserEnv(string name, string value) {
public async Task AppendToUserEnv(string name, string value)
{
// Set a user environment variable.

var shell = Computer.CreateShell(FileClient.AppDataDirectory);
var defaultShellName = GetDefaultShell();

switch (FileClient.OS) {
switch (FileClient.OS)
{
case OSType.Windows:
var currentValue = Environment.GetEnvironmentVariable(
name, EnvironmentVariableTarget.User
Expand All @@ -81,15 +90,10 @@ public async Task AppendToUserEnv(string name, string value) {
break;
// In case the path assigned to variable changes, the previous one will remain in the file but with lower priority.
case OSType.MacOS:
var zshRcPath = FileClient.Combine(FileClient.UserDirectory, ".zshrc");
FileClient.AddLinesToFileIfNotPresent(
zshRcPath, $"export {name}=\"{value}:${name}\""
);
break;
case OSType.Linux:
var bashRcPath = FileClient.Combine(FileClient.UserDirectory, ".bashrc");
var rcPath = FileClient.Combine(FileClient.UserDirectory, $".{defaultShellName}rc");
FileClient.AddLinesToFileIfNotPresent(
bashRcPath, $"export {name}=\"{value}:${name}\""
rcPath, $"export {name}=\"{value}:${name}\""
);
break;
case OSType.Unknown:
Expand All @@ -98,33 +102,64 @@ public async Task AppendToUserEnv(string name, string value) {
}
}

public string GetUserEnv(string name) {
public string GetUserEnv(string name)
{
var shell = Computer.CreateShell(FileClient.AppDataDirectory);
var defaultShellName = GetDefaultShell();

switch (FileClient.OS) {
switch (FileClient.OS)
{
case OSType.Windows:
return Environment.GetEnvironmentVariable(
name, EnvironmentVariableTarget.User
) ?? "";
// It's important to use the user's default shell to get the env-var value here.
// Note the use of the '-i' flag to initialize an interactive shell. Properly loading '<shell>'rc file.
case OSType.MacOS: {
case OSType.MacOS:
case OSType.Linux:
{
var task = shell.Run(
"zsh", ["-ic", $"echo ${name}"]
$"{defaultShellName}", ["-ic", $"echo ${name}"]
);
task.Wait();
return task.Result.StandardOutput;
}
case OSType.Linux: {
case OSType.Unknown:
default:
return "";
}
}

public string GetDefaultShell()
{
var shell = Computer.CreateShell(FileClient.AppDataDirectory);

switch (FileClient.OS)
{
case OSType.MacOS:
{
var task = shell.Run(
"sh", ["-c", "dscl . -read /Users/$USER UserShell | awk -F/ '{ print $NF }'"]
);
task.Wait();
return task.Result.StandardOutput.TrimEnd(Environment.NewLine.ToCharArray());
}
case OSType.Linux:
{
var task = shell.Run(
"bash", ["-ic", $"echo ${name}"]
"sh", ["-c", "getent passwd $USER | awk -F/ '{ print $NF }'"]
);
task.Wait();
return task.Result.StandardOutput;
return task.Result.StandardOutput.TrimEnd(Environment.NewLine.ToCharArray());
}
case OSType.Windows:
case OSType.Unknown:
default:
return "";
return string.Empty;
}
}

// Should be called on CLI initialization when making environment validation.
// Can be used to guarantee that the user's default shell is supported.
public bool IsShellSupported(string shellName) => new[] { "bash", "zsh" }.Contains(shellName.ToLower());
}
15 changes: 10 additions & 5 deletions GodotEnv/src/features/godot/domain/GodotRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,16 @@ public async Task AddOrUpdateGodotEnvVariable(ILog log) {
log.Warn("You may need to restart your shell or run the following ");
log.Warn("to get the updated environment variable value.");
log.Print("");
if (Platform is MacOS) {
log.Info(" source ~/.zshrc");
}
else if (Platform is Linux) {
log.Info(" source ~/.bashrc");

switch (FileClient.OS) {
case OSType.MacOS:
case OSType.Linux:
log.Info($" source ~/.{EnvironmentVariableClient.GetDefaultShell()}rc");
break;
case OSType.Windows:
case OSType.Unknown:
default:
break;
}
log.Print("");
}
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ godotenv godot env get

> On Windows, this adds the `GODOT` environment variable to the current user's environment variable.
>
> On macOS, this adds the `GODOT` environment variable to the current user's `.zshrc` file.
> On macOS, this adds the `GODOT` environment variable to the current user's default shell configuration file.
>
> On Linux, this adds the `GODOT` environment variable to the current user's `.bashrc` file.
> On Linux, this adds the `GODOT` environment variable to the current user's default shell configuration file.
>
> After making changes to environment variables on any system, be sure to close any open terminals and open a new one to ensure the changes are picked up. If changes are not picked up across other applications, you may have to log out and log back in. Fortunately, since the environment variable points to a symlink which points to the active Godot version, you only have to do this once! Afterwards, you are free to switch Godot versions without any further headache as often as you like.
Expand Down

0 comments on commit 4407851

Please sign in to comment.