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

Linux: functional test and XDG configuration support #422

Merged
merged 10 commits into from
Sep 14, 2020
12 changes: 9 additions & 3 deletions Scalar.Common/Platforms/Linux/LinuxPlatform.Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ public partial class LinuxPlatform
{
public static string GetDataRootForScalarImplementation()
{
// TODO(Linux): determine installation location and data path
string path = Environment.GetEnvironmentVariable("SCALAR_DATA_PATH");
return path ?? "/var/run/scalar";
string localDataRoot;
string localDataRootError;

if (!TryGetEnvironmentVariableBasePath(EnvironmentVariableBaseDataPaths, out localDataRoot, out localDataRootError))
{
throw new ArgumentException(localDataRootError);
}

return localDataRoot;
}

public static string GetDataRootForScalarComponentImplementation(string componentName)
Expand Down
35 changes: 34 additions & 1 deletion Scalar.Common/Platforms/Linux/LinuxPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ public partial class LinuxPlatform : POSIXPlatform
// TODO(Linux): determine installation location and upgrader path
private const string UpgradeProtectedDataDirectory = "/usr/local/scalar_upgrader";

// TODO(Linux): We should ideally consider any colon-separated paths
// in $XDG_CONFIG_DIRS and $XDG_DATA_DIRS, as well as their defaults
// (i.e., /etc/xdg and /usr/local/share:/usr/share).
// We should also ideally create any missing directories using a 0700
// permission mode via a native wrapper for mkdir(2) instead of
// relying on our caller's use of Directory.CreateDirectory().
private static readonly EnvironmentVariableBasePath[] EnvironmentVariableBaseCachePaths = new[] {
new EnvironmentVariableBasePath("XDG_CACHE_HOME", "scalar"),
new EnvironmentVariableBasePath("HOME", Path.Combine(".cache", "scalar")),
chrisd8088 marked this conversation as resolved.
Show resolved Hide resolved
};
protected static readonly EnvironmentVariableBasePath[] EnvironmentVariableBaseConfigPaths = new[] {
new EnvironmentVariableBasePath("XDG_CONFIG_HOME", "scalar"),
new EnvironmentVariableBasePath("HOME", Path.Combine(".config", "scalar")),
};
protected static readonly EnvironmentVariableBasePath[] EnvironmentVariableBaseDataPaths = new[] {
new EnvironmentVariableBasePath("XDG_DATA_HOME", "scalar"),
new EnvironmentVariableBasePath("HOME", Path.Combine(".local", "share", "scalar")),
};

public LinuxPlatform() : base(
underConstruction: new UnderConstructionFlags(
supportsScalarUpgrade: false,
Expand All @@ -36,7 +55,15 @@ public override string ScalarConfigPath
{
get
{
return Path.Combine(this.Constants.ScalarBinDirectoryPath, LocalScalarConfig.FileName);
string localConfigRoot;
string localConfigRootError;

if (!TryGetEnvironmentVariableBasePath(EnvironmentVariableBaseConfigPaths, out localConfigRoot, out localConfigRootError))
{
throw new ArgumentException(localConfigRootError);
}

return Path.Combine(localConfigRoot, LocalScalarConfig.FileName);
}
}

Expand Down Expand Up @@ -101,6 +128,11 @@ public override string GetUpgradeLogDirectoryParentDirectory()
return this.GetUpgradeNonProtectedDataDirectory();
}

public override bool TryGetDefaultLocalCacheRoot(string enlistmentRoot, out string localCacheRoot, out string localCacheRootError)
{
return TryGetEnvironmentVariableBasePath(EnvironmentVariableBaseCachePaths, out localCacheRoot, out localCacheRootError);
}

public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions(
PhysicalFileSystem fileSystem,
ITracer tracer)
Expand Down Expand Up @@ -138,6 +170,7 @@ public override string InstallerExtension
get { return ".deb"; }
}

// TODO(Linux): determine installation location
public override string ScalarBinDirectoryPath
{
get { return Path.Combine("/usr", "local", this.ScalarBinDirectoryName); }
Expand Down
14 changes: 9 additions & 5 deletions Scalar.Common/Platforms/Mac/MacPlatform.Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ public partial class MacPlatform
{
public static string GetDataRootForScalarImplementation()
{
return Path.Combine(
Environment.GetEnvironmentVariable("HOME"),
"Library",
"Application Support",
"Scalar");
string localDataRoot;
string localDataRootError;

if (!TryGetEnvironmentVariableBasePath(EnvironmentVariableBaseDataPaths, out localDataRoot, out localDataRootError))
{
throw new ArgumentException(localDataRootError);
}

return localDataRoot;
}

public static string GetDataRootForScalarComponentImplementation(string componentName)
Expand Down
11 changes: 11 additions & 0 deletions Scalar.Common/Platforms/Mac/MacPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ namespace Scalar.Platform.Mac
public partial class MacPlatform : POSIXPlatform
{
private const string UpgradeProtectedDataDirectory = "/usr/local/scalar_upgrader";
private static readonly EnvironmentVariableBasePath[] EnvironmentVariableBaseCachePaths = new[] {
new EnvironmentVariableBasePath("HOME", ScalarConstants.DefaultScalarCacheFolderName),
};
protected static readonly EnvironmentVariableBasePath[] EnvironmentVariableBaseDataPaths = new[] {
new EnvironmentVariableBasePath("HOME", Path.Combine("Library", "Application Support", "Scalar")),
};

public MacPlatform() : base(
underConstruction: new UnderConstructionFlags(
Expand Down Expand Up @@ -126,6 +132,11 @@ public override Dictionary<string, string> GetPhysicalDiskInfo(string path, bool
return result;
}

public override bool TryGetDefaultLocalCacheRoot(string enlistmentRoot, out string localCacheRoot, out string localCacheRootError)
{
return TryGetEnvironmentVariableBasePath(EnvironmentVariableBaseCachePaths, out localCacheRoot, out localCacheRootError);
}

public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions(
PhysicalFileSystem fileSystem,
ITracer tracer)
Expand Down
112 changes: 76 additions & 36 deletions Scalar.Common/Platforms/POSIX/POSIXPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,42 +182,6 @@ public override bool IsElevated()
return POSIXPlatform.IsElevatedImplementation();
}

public override bool TryGetDefaultLocalCacheRoot(string enlistmentRoot, out string localCacheRoot, out string localCacheRootError)
{
string homeDirectory;

try
{
homeDirectory = Environment.GetEnvironmentVariable("HOME");
}
catch (SecurityException e)
{
localCacheRoot = null;
localCacheRootError = $"Failed to read $HOME, insufficient permission: {e.Message}";
return false;
}

if (string.IsNullOrEmpty(homeDirectory))
{
localCacheRoot = null;
localCacheRootError = "$HOME empty or not found";
return false;
}

try
{
localCacheRoot = Path.Combine(homeDirectory, ScalarConstants.DefaultScalarCacheFolderName);
localCacheRootError = null;
return true;
}
catch (ArgumentException e)
{
localCacheRoot = null;
localCacheRootError = $"Failed to build local cache path using $HOME('{homeDirectory}'): {e.Message}";
return false;
}
}

public override bool TryKillProcessTree(int processId, out int exitCode, out string error)
{
ProcessResult result = ProcessHelper.Run("pkill", $"-P {processId}");
Expand Down Expand Up @@ -265,5 +229,81 @@ public override HashSet<string> UpgradeBlockingProcesses

public override bool SupportsUpgradeWhileRunning => true;
}

protected class EnvironmentVariableBasePath
{
private string environmentVariable;
private string subFolder;

public EnvironmentVariableBasePath (string environmentVariable, string subFolder)
{
this.environmentVariable = environmentVariable;
this.subFolder = subFolder;
}

public string EnvironmentVariable
{
get { return environmentVariable; }
}

public string SubFolder
{
get { return subFolder; }
}
}

protected static bool TryGetEnvironmentVariableBasePath(EnvironmentVariableBasePath[] environmentVariableBasePaths, out string path, out string error)
{
if (environmentVariableBasePaths == null || environmentVariableBasePaths.Length == 0)
{
path = null;
error = "Null or empty list of base path environment variables to read";
return false;
}

error = null;
foreach (EnvironmentVariableBasePath environmentVariableBasePath in environmentVariableBasePaths)
{
if (TryGetEnvironmentVariable(environmentVariableBasePath.EnvironmentVariable, out path, out error))
{
try
{
path = Path.Combine(path, environmentVariableBasePath.SubFolder);
return true;
}
catch (ArgumentException e)
{
error = $"Failed to build base path using ${environmentVariableBasePath.EnvironmentVariable}('{path}'), '{environmentVariableBasePath.SubFolder}': {e.Message}";
}
}
}

path = null;
return false;
}

private static bool TryGetEnvironmentVariable(string name, out string val, out string error)
{
try
{
val = Environment.GetEnvironmentVariable(name);
}
catch (SecurityException e)
{
val = null;
error = $"Failed to read ${name}, insufficient permission: {e.Message}";
return false;
}

if (string.IsNullOrEmpty(val))
{
val = null;
error = $"${name} empty or not found";
return false;
}

error = null;
return true;
}
}
}