diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f5d7731..0f9354d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,16 +25,16 @@ jobs: uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..93d5ad5 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + $(MSBuildThisFileDirectory)artifacts + + diff --git a/README.md b/README.md index ab7118b..246bb32 100644 --- a/README.md +++ b/README.md @@ -33,18 +33,19 @@ _**NOTE**_: The option `-p:PublishTrimmed=true` may produce some *warnings*. If ```sh USAGE: - scrl [OPTIONS] + scrl [path] [OPTIONS] ARGUMENTS: - Starting directory (Default: .) + [path] Starting directory (Default: .) OPTIONS: - -h, --help Prints help information - -x, --extensions Comma separated list of script extensions - -d, --depth Search depth - -e, --elevated Run with elevated privileges - -g, --group Group scripts by folder - -b, --brief Show brief information + DEFAULT + -h, --help Prints help information + -x, --extensions List of script extensions to search for + -d, --depth 3 Search depth + -e, --elevated Run with elevated privileges + -g, --group Group scripts by folder + -b, --brief Show brief information ``` [CLI]: https://docs.microsoft.com/en-us/dotnet/core/tools/ ".NET CLI Docs" diff --git a/src/Commands.cs b/src/Commands.cs index 4be0a59..ed0b950 100644 --- a/src/Commands.cs +++ b/src/Commands.cs @@ -1,5 +1,5 @@ using System.ComponentModel; - +using System.Diagnostics.CodeAnalysis; using Spectre.Console; using Spectre.Console.Cli; @@ -76,27 +76,30 @@ public sealed class RootCommand : AsyncCommand public sealed class RootCommandSettings : CommandSettings { - [Description("Comma separated list of script extensions")] - [CommandOption("-x|--extensions")] - public string? Extensions { get; init; } + [Description("List of script extensions to search for")] + [CommandOption("-x|--extensions ")] + [SuppressMessage("Performance", "CA1819:Properties should not return arrays")] + public string[] Extensions { get; init; } = [".ps1", ".*sh", ".bat", ".cmd", ".nu"]; [Description("Search depth")] [CommandOption("-d|--depth")] - public int Depth { get; init; } = 1; + [DefaultValue(3)] + public int Depth { get; init; } [Description("Run with elevated privileges")] [CommandOption("-e|--elevated")] - public bool Elevated { get; init; } = false; + public bool Elevated { get; init; } [Description("Group scripts by folder")] [CommandOption("-g|--group")] - public bool Group { get; init; } = false; + public bool Group { get; init; } [Description("Show brief information")] [CommandOption("-b|--brief")] - public bool Brief { get; init; } = false; + public bool Brief { get; init; } [Description("Starting directory (Default: .)")] - [CommandArgument(0, "")] - public string Directory { get; init; } = "."; -} \ No newline at end of file + [CommandArgument(0, "[path]")] + [DefaultValue(".")] + public string Directory { get; init; } = string.Empty; +} diff --git a/src/Program.cs b/src/Program.cs index f53f375..836c56b 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -2,5 +2,6 @@ using ScriptLauncher; using Spectre.Console.Cli; var app = new CommandApp(); -app.Configure(x => x.SetApplicationName("scrl")); +app.Configure(static x => x.SetApplicationName("scrl")); + return app.Run(args); diff --git a/src/ScriptExecutor.cs b/src/ScriptExecutor.cs index 77e1fe0..a1d14aa 100644 --- a/src/ScriptExecutor.cs +++ b/src/ScriptExecutor.cs @@ -26,36 +26,55 @@ internal static class ScriptExecutor ).ConfigureAwait(ConfigureAwaitOptions.None); } - private static ProcessStartInfo? GetExecutableProcessInfo(FileInfo file, bool elevated) => - file.Extension switch + private static string GetElevationVerb(bool elevated) + { + if (!elevated) { - ".bat" - or ".cmd" - => new ProcessStartInfo - { - FileName = "cmd", - Arguments = $"/Q /C .\\{file.Name}", - Verb = elevated ? "runas /user:Administrator" : string.Empty, - WorkingDirectory = file.DirectoryName - }, - ".ps1" - => new ProcessStartInfo - { - FileName = "powershell.exe", - Arguments = $"-NoProfile -ExecutionPolicy Bypass -File .\\{file.Name}", - Verb = elevated ? "runas /user:Administrator" : string.Empty, - WorkingDirectory = file.DirectoryName - }, - ".sh" - or ".zsh" - or ".fish" - => new ProcessStartInfo - { - FileName = "sh", - Arguments = $"-c ./{file.Name}", - Verb = elevated ? "sudo" : string.Empty, - WorkingDirectory = file.DirectoryName - }, - _ => null + return string.Empty; + } + + var platform = Environment.OSVersion.Platform; + return platform switch + { + PlatformID.Win32NT => "runas /user:Administrator", + PlatformID.Unix or PlatformID.MacOSX => "sudo", + _ => string.Empty, }; + } + + private static ProcessStartInfo? GetExecutableProcessInfo(FileInfo file, bool elevated) + { + var verb = GetElevationVerb(elevated); + return file.Extension switch + { + ".bat" or ".cmd" => new ProcessStartInfo + { + FileName = "cmd", + Arguments = $"/Q /C ./{file.Name}", + Verb = verb, + WorkingDirectory = file.DirectoryName, + }, + ".ps1" => new ProcessStartInfo + { + FileName = "powershell.exe", + Arguments = $"-NoProfile -ExecutionPolicy Bypass -File ./{file.Name}", + Verb = verb, + WorkingDirectory = file.DirectoryName, + }, + ".nu" => new ProcessStartInfo + { + FileName = "nu", + Arguments = $"--no-config-file ./{file.Name}", + Verb = verb, + WorkingDirectory = file.DirectoryName, + }, + _ => new ProcessStartInfo + { + FileName = "sh", + Arguments = $"-c ./{file.Name}", + Verb = verb, + WorkingDirectory = file.DirectoryName, + }, + }; + } } diff --git a/src/ScriptFinder.cs b/src/ScriptFinder.cs index 3ea2f4a..e7040f4 100644 --- a/src/ScriptFinder.cs +++ b/src/ScriptFinder.cs @@ -2,23 +2,15 @@ namespace ScriptLauncher; internal readonly struct ScriptFinder { - private static readonly string[] DefaultExtensions = new[] { ".ps1", ".*sh", ".bat", ".cmd" }; - private static readonly char[] DefaultSeparators = new[] { ',', ' ' }; - - public string[] Extensions { get; } + public IEnumerable Extensions { get; } public string RootDirectory { get; } - public int Depth { get; } + private int Depth { get; } private readonly EnumerationOptions _options; - public ScriptFinder(string? extensions, string directory, int depth) + public ScriptFinder(IEnumerable extensions, string directory, int depth) { - Extensions = - extensions - ?.Split(DefaultSeparators, StringSplitOptions.RemoveEmptyEntries) - .ToHashSet() - .Select(x => $".{x.TrimStart('.')}") - .ToArray() ?? DefaultExtensions; + Extensions = extensions.ToHashSet().Select(static x => $".{x.TrimStart('.')}"); Depth = depth; RootDirectory = directory; @@ -36,7 +28,7 @@ internal readonly struct ScriptFinder try { var filenames = Directory.GetFiles(RootDirectory, $"*{extension}", _options); - return filenames.Select(x => new FileInfo(x)); + return filenames.Select(static x => new FileInfo(x)); } catch (UnauthorizedAccessException) { @@ -45,13 +37,13 @@ internal readonly struct ScriptFinder } public FileInfo[] GetScripts() => - Extensions.Select(GetScriptFilesWithExtension).SelectMany(x => x).ToArray(); + Extensions.Select(GetScriptFilesWithExtension).SelectMany(static x => x).ToArray(); public IDictionary GetScriptsByDirectory() => Extensions .Select(GetScriptFilesWithExtension) - .SelectMany(x => x) - .GroupBy(x => x.DirectoryName!) - .OrderBy(x => x.Key) - .ToDictionary(x => new DirectoryInfo(x.Key), x => x.ToArray()); + .SelectMany(static x => x) + .GroupBy(static x => x.DirectoryName!) + .OrderBy(static x => x.Key) + .ToDictionary(static x => new DirectoryInfo(x.Key), static x => x.ToArray()); } diff --git a/src/ScriptLauncher.csproj b/src/ScriptLauncher.csproj index 7784893..985e1e3 100644 --- a/src/ScriptLauncher.csproj +++ b/src/ScriptLauncher.csproj @@ -1,12 +1,12 @@ - + Exe - net8.0 + net8.0 enable enable All - 0.1.5 + 0.1.7 true Tool to find and exec shell scripts README.md @@ -14,8 +14,8 @@ - - + +