From 655c7d8db76d1040f93105fa0ff1ba712a68cf6a Mon Sep 17 00:00:00 2001 From: Daniel Cazzulino Date: Fri, 29 Aug 2025 23:38:14 -0300 Subject: [PATCH] Unify implementation across gist/runcs The `gist` command is just a default added before the ref format by prepending `gist.github.com` to the generic runcs. This means that you can also use plain runcs to run gists, obviously. --- readme.md | 31 ++++-- src/Core/Core.csproj | 1 + src/Core/DirectoryExtensions.cs | 25 +++++ src/Core/DownloadManager.cs | 50 --------- src/Core/DownloadProvider.cs | 1 + src/Core/Http/RedirectingHttpHandler.cs | 5 +- src/Core/HttpExtensions.cs | 4 +- src/Core/RemoteRefExtensions.cs | 27 +++++ src/Core/RemoteRunner.cs | 99 +++++++++++++++++ src/gist/Program.cs | 130 +++++------------------ src/runcs/Program.cs | 130 +++++------------------ src/runcs/Properties/launchSettings.json | 4 + src/runcs/runcs.csproj | 2 - 13 files changed, 234 insertions(+), 275 deletions(-) create mode 100644 src/Core/DirectoryExtensions.cs delete mode 100644 src/Core/DownloadManager.cs create mode 100644 src/Core/RemoteRefExtensions.cs create mode 100644 src/Core/RemoteRunner.cs diff --git a/readme.md b/readme.md index c2121ac..24abf91 100644 --- a/readme.md +++ b/readme.md @@ -42,20 +42,29 @@ View [source gist](https://gist.github.com/kzu/52b115ce24c7978ddc33245d4ff840f5) Run C# code programs from git repos on GitHub, GitLab, Bitbucket and Azure DevOps. ``` -Usage: [dnx] runcs REPO_REF [args] - REPO_REF Reference to remote file to run, with format [host/]owner/repo[@ref][:path] - host optional host name (default: github.com) - @ref optional branch, tag, or commit (default: default branch) - :path optional path to file in repo (default: program.cs at repo root) +Usage: + [dnx] runcs [...] - Examples: - * kzu/sandbox@v1.0.0:run.cs (implied host github.com, explicit tag and file path) - * gitlab.com/kzu/sandbox@main:run.cs (all explicit parts) - * bitbucket.org/kzu/sandbox (implied ref as default branch and path as program.cs) - * kzu/sandbox (implied host github.com, ref and path defaults) +Arguments: + Reference to remote file to run, with format [host/]owner/repo[@ref][:path] + host optional host name ([gist.]github.com|gitlab.com|dev.azure.com, default: github.com) + @ref optional branch, tag, or commit (default: default branch) + :path optional path to file in repo (default: program.cs at repo root) + + Examples: + * kzu/sandbox@v1.0.0:run.cs (implied host github.com, explicit tag and file path) + * gitlab.com/kzu/sandbox@main:run.cs (all explicit parts) + * kzu/sandbox (implied host github.com, ref and path defaults) + + Arguments passed to the C# program that is being run. +``` + +Example: - args Arguments to pass to the C# program ``` +dnx kzu/runcs@v1:run.cs dotnet rocks +``` + diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index bfe03d1..6db7afb 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Core/DirectoryExtensions.cs b/src/Core/DirectoryExtensions.cs new file mode 100644 index 0000000..8b2f07d --- /dev/null +++ b/src/Core/DirectoryExtensions.cs @@ -0,0 +1,25 @@ +namespace Devlooped; + +static class DirectoryExtensions +{ + extension(Directory) + { + /// Creates a temporary user-owned subdirectory for file-based apps. + public static string CreateUserDirectory(string path) + { + if (OperatingSystem.IsWindows()) + { + Directory.CreateDirectory(path); + } + else + { + // Ensure only the current user has access to the directory to avoid leaking the program to other users. + // We don't mind that permissions might be different if the directory already exists, + // since it's under user's local directory and its path should be unique. + Directory.CreateDirectory(path, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); + } + + return path; + } + } +} diff --git a/src/Core/DownloadManager.cs b/src/Core/DownloadManager.cs deleted file mode 100644 index f5790b3..0000000 --- a/src/Core/DownloadManager.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Devlooped; - -public static class DownloadManager -{ - extension(RemoteRef location) - { - public string TempPath => Path.Join(GetTempRoot(), location.Host ?? "github.com", location.Owner, location.Project ?? "", location.Repo, location.Ref ?? "main"); - } - - /// - /// Obtains the temporary directory root, e.g., /tmp/dotnet/runcs/. - /// - public static string GetTempRoot() - { - // We want a location where permissions are expected to be restricted to the current user. - string directory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Path.GetTempPath() - : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - - return CreateTempPath(Path.Join(directory, "dotnet", "runcs")); - } - - /// - /// Obtains a specific temporary path in a subdirectory of the temp root, e.g., /tmp/dotnet/runcs/{name}. - /// - public static string GetTempSubpath(params string[] name) => CreateTempPath(Path.Join([GetTempRoot(), .. name])); - - /// - /// Creates a temporary subdirectory for file-based apps. - /// Use to obtain the path. - /// - static string CreateTempPath(string path) - { - if (OperatingSystem.IsWindows()) - { - Directory.CreateDirectory(path); - } - else - { - // Ensure only the current user has access to the directory to avoid leaking the program to other users. - // We don't mind that permissions might be different if the directory already exists, - // since it's under user's local directory and its path should be unique. - Directory.CreateDirectory(path, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); - } - - return path; - } -} diff --git a/src/Core/DownloadProvider.cs b/src/Core/DownloadProvider.cs index cf67419..0ccfe77 100644 --- a/src/Core/DownloadProvider.cs +++ b/src/Core/DownloadProvider.cs @@ -12,6 +12,7 @@ public abstract class DownloadProvider "gitlab.com" => new GitLabDownloadProvider(), "dev.azure.com" => new AzureDevOpsDownloadProvider(), //"bitbucket.org" => new BitbucketDownloadProvider(), + "gist.github.com" => new GitHubDownloadProvider(gist: true), _ => new GitHubDownloadProvider(), }; diff --git a/src/Core/Http/RedirectingHttpHandler.cs b/src/Core/Http/RedirectingHttpHandler.cs index ba50d32..8046e00 100644 --- a/src/Core/Http/RedirectingHttpHandler.cs +++ b/src/Core/Http/RedirectingHttpHandler.cs @@ -22,7 +22,10 @@ protected override async Task SendAsync(HttpRequestMessage var response = await base.SendAsync(currentRequest, cancellationToken).ConfigureAwait(false); if (!response.StatusCode.IsRedirect() || response.Headers.Location is null) + { + response.Headers.TryAddWithoutValidation("X-Original-URI", originalUri.AbsoluteUri); return response; + } var location = response.Headers.Location; var nextUri = location.IsAbsoluteUri ? location : new Uri(currentRequest.RequestUri!, location); @@ -76,8 +79,6 @@ protected override async Task SendAsync(HttpRequestMessage // Copy headers (preserve conditional ones like If-None-Match) CopyHeaders(currentRequest, next); - next.Headers.TryAddWithoutValidation("X-Original-URI", originalUri.AbsoluteUri); - // We're not going to read this 3xx body; free the socket. response.Dispose(); diff --git a/src/Core/HttpExtensions.cs b/src/Core/HttpExtensions.cs index 8451950..1d5440a 100644 --- a/src/Core/HttpExtensions.cs +++ b/src/Core/HttpExtensions.cs @@ -11,7 +11,7 @@ public static async Task ExtractToAsync(this HttpResponseMessage content, Remote if (Directory.Exists(location.TempPath)) Directory.Delete(location.TempPath, true); - Directory.CreateDirectory(location.TempPath); + location.EnsureTempPath(); // Extract files while skipping the top-level directory and preserving structure from that point onwards // This matches the behavior of github/gitlab archive downloads. @@ -43,7 +43,7 @@ public static async Task ExtractToAsync(this HttpResponseMessage content, Remote // Ensure the directory exists var directoryPath = Path.GetDirectoryName(destinationPath); if (!string.IsNullOrEmpty(directoryPath)) - Directory.CreateDirectory(directoryPath); + Directory.CreateUserDirectory(directoryPath); entry.ExtractToFile(destinationPath); } diff --git a/src/Core/RemoteRefExtensions.cs b/src/Core/RemoteRefExtensions.cs new file mode 100644 index 0000000..c0ad69f --- /dev/null +++ b/src/Core/RemoteRefExtensions.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; + +namespace Devlooped; + +public static class RemoteRefExtensions +{ + extension(RemoteRef location) + { + public string TempPath => Path.Join(GetTempRoot(), location.Host ?? "github.com", location.Owner, location.Project ?? "", location.Repo, location.Ref ?? "main"); + + public string EnsureTempPath() => Directory.CreateUserDirectory(location.TempPath); + } + + /// Obtains the temporary directory root, e.g., /tmp/dotnet/runcs/. + static string GetTempRoot() + { + // We want a location where permissions are expected to be restricted to the current user. + string directory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Path.GetTempPath() + : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + + return Directory.CreateUserDirectory(Path.Join(directory, "dotnet", "runcs")); + } + + /// Obtains a specific temporary path in a subdirectory of the temp root, e.g., /tmp/dotnet/runcs/{name}. + public static string GetTempSubpath(params string[] name) => Directory.CreateUserDirectory(Path.Join([GetTempRoot(), .. name])); +} diff --git a/src/Core/RemoteRunner.cs b/src/Core/RemoteRunner.cs new file mode 100644 index 0000000..bee26b8 --- /dev/null +++ b/src/Core/RemoteRunner.cs @@ -0,0 +1,99 @@ +using System.Diagnostics; +using System.Net; +using DotNetConfig; +using Spectre.Console; + +namespace Devlooped; + +public class RemoteRunner(RemoteRef location, string toolName) +{ + public async Task RunAsync(string[] args) + { + var config = Config.Build(Config.GlobalLocation); + var etag = config.GetString(toolName, location.ToString(), "etag"); + if (etag != null && Directory.Exists(location.TempPath)) + { + if (etag.StartsWith("W/\"", StringComparison.OrdinalIgnoreCase) && !etag.EndsWith('"')) + etag += '"'; + + location = location with { ETag = etag }; + } + if (config.TryGetString(toolName, location.ToString(), "uri", out var url) && + Uri.TryCreate(url, UriKind.Absolute, out var uri)) + location = location with { ResolvedUri = uri }; + + if (DotnetMuxer.Path is null) + { + AnsiConsole.MarkupLine($":cross_mark: Unable to locate the .NET SDK."); + return 1; + } + + var provider = DownloadProvider.Create(location); + var contents = await provider.GetAsync(location); + var updated = false; + // We consider a not modified as successful too + var success = contents.IsSuccessStatusCode || contents.StatusCode == HttpStatusCode.NotModified; + + if (!success) + { + AnsiConsole.MarkupLine($":cross_mark: Reference [yellow]{location}[/] not found."); + return 1; + } + + if (contents.StatusCode != HttpStatusCode.NotModified) + { +#if DEBUG + await AnsiConsole.Status().StartAsync($":open_file_folder: {location} :backhand_index_pointing_right: {location.TempPath}", async ctx => + { + await contents.ExtractToAsync(location); + }); +#else + await contents.ExtractToAsync(location); +#endif + + if (contents.Headers.ETag?.ToString() is { } newEtag) + config = config.SetString(toolName, location.ToString(), "etag", newEtag); + + if (contents.Headers.TryGetValues("X-Original-URI", out var urls) && urls.Any()) + config = config.SetString(toolName, location.ToString(), "uri", urls.First()); + else + config = config.SetString(toolName, location.ToString(), "uri", contents.RequestMessage!.RequestUri!.AbsoluteUri); + + updated = true; + } + + var program = Path.Combine(location.TempPath, location.Path ?? "program.cs"); + if (!File.Exists(program)) + { + if (location.Path is not null) + { + AnsiConsole.MarkupLine($":cross_mark: File reference not found in {location}."); + return 1; + } + + var first = Directory.EnumerateFiles(location.TempPath, "*.cs", SearchOption.TopDirectoryOnly).FirstOrDefault(); + if (first is null) + { + AnsiConsole.MarkupLine($":cross_mark: No .cs files found in {location}."); + return 1; + } + program = first; + } + + if (updated) + { + // Clean since otherwise we sometimes get stale build outputs? :/ + Process.Start(DotnetMuxer.Path.FullName, ["clean", "-v:q", program]).WaitForExit(); + } + +#if DEBUG + AnsiConsole.MarkupLine($":rocket: {DotnetMuxer.Path.FullName} run -v:q {program} {string.Join(' ', args)}"); +#endif + + var start = new ProcessStartInfo(DotnetMuxer.Path.FullName, ["run", "-v:q", program, .. args]); + var process = Process.Start(start); + process?.WaitForExit(); + + return process?.ExitCode ?? 1; + } +} diff --git a/src/gist/Program.cs b/src/gist/Program.cs index 46251dd..119de13 100644 --- a/src/gist/Program.cs +++ b/src/gist/Program.cs @@ -1,28 +1,29 @@ -using System.Diagnostics; -using System.Net; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Text; using Devlooped; -using DotNetConfig; using GitCredentialManager.UI; using Spectre.Console; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Console.InputEncoding = Console.OutputEncoding = Encoding.UTF8; -if (args.Length == 0 || !RemoteRef.TryParse(args[0], out var location)) +if (args.Length == 0 || !RemoteRef.TryParse("gist.github.com/" + args[0], out var location)) { - AnsiConsole.MarkupLine($"Usage: [grey][[dnx]][/] [lime]{ThisAssembly.Project.ToolCommandName}[/] [italic]GIST_REF[/] [grey][[args]][/]"); - AnsiConsole.MarkupLine(""" - [bold]GIST_REF[/] Reference to gist file to run, with format [yellow]owner/gist[[@commit]][[:path]][/] - [italic][yellow]@commit[/][/] optional gist commit (default: default branch) - [italic][yellow]:path[/][/] optional path to file in gist (default: program.cs or first .cs file) - - Examples: - * kzu/0ac826dc7de666546aaedd38e5965381 (tip commit and program.cs or first .cs file) - * kzu/0ac826dc7de666546aaedd38e5965381@d8079cf:run.cs (explicit commit and file path) - - [bold]args[/] Arguments to pass to the C# gist program + AnsiConsole.MarkupLine( + $""" + Usage: + [grey][[dnx]][/] [lime]{ThisAssembly.Project.ToolCommandName}[/] [bold][/] [grey italic][[...]][/] + + Arguments: + [bold][/] Reference to gist file to run, with format [yellow]owner/gist[[@commit]][[:path]][/] + [italic][yellow]@commit[/][/] optional gist commit (default: latest) + [italic][yellow]:path[/][/] optional path to file in gist (default: program.cs or first .cs file) + + Examples: + * kzu/0ac826dc7de666546aaedd38e5965381 (tip commit and program.cs or first .cs file) + * kzu/0ac826dc7de666546aaedd38e5965381@d8079cf:run.cs (explicit commit and file path) + + [bold][/] Arguments passed to the C# program that is being run. """); return; } @@ -35,7 +36,14 @@ // Run AppMain in a new thread and keep the main thread free // to process the dispatcher's job queue. -var main = Task.Run(() => Main(location, args[1..])); +var main = Task + .Run(() => new RemoteRunner(location, ThisAssembly.Project.ToolCommandName) + .RunAsync(args[1..])) + .ContinueWith(t => + { + Dispatcher.MainThread.Shutdown(); + return t.Result; + }); // Process the dispatcher job queue (aka: message pump, run-loop, etc...) // We must ensure to run this on the same thread that it was created on @@ -44,90 +52,4 @@ Dispatcher.MainThread.Run(); // Dispatcher was shutdown -Environment.Exit(await main); - -static async Task Main(RemoteRef location, string[] args) -{ - var config = Config.Build(Config.GlobalLocation); - var etag = config.GetString(ThisAssembly.Project.ToolCommandName, $"{location.Owner}/{location.Repo}", location.Ref ?? "main"); - if (etag != null && Directory.Exists(location.TempPath)) - { - if (etag.StartsWith("W/\"", StringComparison.OrdinalIgnoreCase) && !etag.EndsWith('"')) - etag += '"'; - - location = location with { ETag = etag }; - } - - if (DotnetMuxer.Path is null) - { - AnsiConsole.MarkupLine($":cross_mark: Unable to locate the .NET SDK."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - var provider = new GitHubDownloadProvider(gist: true); - var contents = await provider.GetAsync(location); - var updated = false; - - if (!contents.IsSuccessStatusCode) - { - AnsiConsole.MarkupLine($":cross_mark: Reference [yellow]{location}[/] not found."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - if (contents.StatusCode != HttpStatusCode.NotModified) - { -#if DEBUG - await AnsiConsole.Status().StartAsync($":open_file_folder: {location} :backhand_index_pointing_right: {location.TempPath}", async ctx => - { - await contents.ExtractToAsync(location); - }); -#else - await contents.ExtractToAsync(location); -#endif - - if (contents.Headers.ETag?.ToString() is { } newEtag) - config.SetString(ThisAssembly.Project.ToolCommandName, $"{location.Owner}/{location.Repo}", location.Ref ?? "main", newEtag); - - updated = true; - } - - var program = Path.Combine(location.TempPath, location.Path ?? "program.cs"); - if (!File.Exists(program)) - { - if (location.Path is not null) - { - AnsiConsole.MarkupLine($":cross_mark: File reference not found in gist {location}."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - var first = Directory.EnumerateFiles(location.TempPath, "*.cs", SearchOption.TopDirectoryOnly).FirstOrDefault(); - if (first is null) - { - AnsiConsole.MarkupLine($":cross_mark: No .cs files found in gist {location}."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - program = first; - } - - if (updated) - { - // Clean since it otherwise we get stale build outputs :/ - Process.Start(DotnetMuxer.Path.FullName, ["clean", "-v:q", program]).WaitForExit(); - } - -#if DEBUG - AnsiConsole.MarkupLine($":rocket: {DotnetMuxer.Path.FullName} run -v:q {program} {string.Join(' ', args)}"); -#endif - - var start = new ProcessStartInfo(DotnetMuxer.Path.FullName, ["run", "-v:q", program, .. args]); - var process = Process.Start(start); - process?.WaitForExit(); - - Dispatcher.MainThread.Shutdown(); - - return process?.ExitCode ?? 1; -} \ No newline at end of file +Environment.Exit(await main); \ No newline at end of file diff --git a/src/runcs/Program.cs b/src/runcs/Program.cs index 61b28ae..f34702c 100644 --- a/src/runcs/Program.cs +++ b/src/runcs/Program.cs @@ -1,9 +1,6 @@ -using System.Diagnostics; -using System.Net; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Text; using Devlooped; -using DotNetConfig; using GitCredentialManager.UI; using Spectre.Console; @@ -12,19 +9,23 @@ if (args.Length == 0 || !RemoteRef.TryParse(args[0], out var location)) { - AnsiConsole.MarkupLine($"Usage: [grey][[dnx]][/] [lime]{ThisAssembly.Project.ToolCommandName}[/] [italic]REPO_REF[/] [grey][[args]][/]"); - AnsiConsole.MarkupLine(""" - [bold]REPO_REF[/] Reference to remote file to run, with format [yellow][[host/]]owner/repo[[@ref]][[:path]][/] - [italic][yellow]host[/][/] optional host name (default: github.com) - [italic][yellow]@ref[/][/] optional branch, tag, or commit (default: default branch) - [italic][yellow]:path[/][/] optional path to file in repo (default: program.cs at repo root) - - Examples: - * kzu/sandbox@v1.0.0:run.cs (implied host github.com, explicit tag and file path) - * gitlab.com/kzu/sandbox@main:run.cs (all explicit parts) - * kzu/sandbox (implied host github.com, ref and path defaults) - - [bold]args[/] Arguments to pass to the C# program + AnsiConsole.MarkupLine( + $""" + Usage: + [grey][[dnx]][/] [lime]{ThisAssembly.Project.ToolCommandName}[/] [bold][/] [grey italic][[...]][/] + + Arguments: + [bold][/] Reference to remote file to run, with format [yellow][[host/]]owner/repo[[@ref]][[:path]][/] + [italic][yellow]host[/][/] optional host name ([grey][[gist.]][/]github.com|gitlab.com|dev.azure.com, default: github.com) + [italic][yellow]@ref[/][/] optional branch, tag, or commit (default: default branch) + [italic][yellow]:path[/][/] optional path to file in repo (default: program.cs at repo root) + + Examples: + * kzu/sandbox@v1.0.0:run.cs (implied host github.com, explicit tag and file path) + * gitlab.com/kzu/sandbox@main:run.cs (all explicit parts) + * kzu/sandbox (implied host github.com, ref and path defaults) + + [bold][/] Arguments passed to the C# program that is being run. """); return; } @@ -37,7 +38,14 @@ // Run AppMain in a new thread and keep the main thread free // to process the dispatcher's job queue. -var main = Task.Run(() => Main(location, args[1..])); +var main = Task + .Run(() => new RemoteRunner(location, ThisAssembly.Project.ToolCommandName) + .RunAsync(args[1..])) + .ContinueWith(t => + { + Dispatcher.MainThread.Shutdown(); + return t.Result; + }); // Process the dispatcher job queue (aka: message pump, run-loop, etc...) // We must ensure to run this on the same thread that it was created on @@ -47,89 +55,3 @@ // Dispatcher was shutdown Environment.Exit(await main); - -static async Task Main(RemoteRef location, string[] args) -{ - var config = Config.Build(Config.GlobalLocation); - var etag = config.GetString(ThisAssembly.Project.ToolCommandName, $"{location.Owner}/{location.Repo}", location.Ref ?? "main"); - if (etag != null && Directory.Exists(location.TempPath)) - { - if (etag.StartsWith("W/\"", StringComparison.OrdinalIgnoreCase) && !etag.EndsWith('"')) - etag += '"'; - - location = location with { ETag = etag }; - } - - if (DotnetMuxer.Path is null) - { - AnsiConsole.MarkupLine($":cross_mark: Unable to locate the .NET SDK."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - var provider = DownloadProvider.Create(location); - var contents = await provider.GetAsync(location); - var updated = false; - - if (!contents.IsSuccessStatusCode) - { - AnsiConsole.MarkupLine($":cross_mark: Reference [yellow]{location}[/] not found."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - if (contents.StatusCode != HttpStatusCode.NotModified) - { -#if DEBUG - await AnsiConsole.Status().StartAsync($":open_file_folder: {location} :backhand_index_pointing_right: {location.TempPath}", async ctx => - { - await contents.ExtractToAsync(location); - }); -#else - await contents.ExtractToAsync(location); -#endif - - if (contents.Headers.ETag?.ToString() is { } newEtag) - config.SetString(ThisAssembly.Project.ToolCommandName, $"{location.Owner}/{location.Repo}", location.Ref ?? "main", newEtag); - - updated = true; - } - - var program = Path.Combine(location.TempPath, location.Path ?? "program.cs"); - if (!File.Exists(program)) - { - if (location.Path is not null) - { - AnsiConsole.MarkupLine($":cross_mark: File reference not found in gist {location}."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - - var first = Directory.EnumerateFiles(location.TempPath, "*.cs", SearchOption.TopDirectoryOnly).FirstOrDefault(); - if (first is null) - { - AnsiConsole.MarkupLine($":cross_mark: No .cs files found in gist {location}."); - Dispatcher.MainThread.Shutdown(); - return 1; - } - program = first; - } - - if (updated) - { - // Clean since it otherwise we get stale build outputs :/ - Process.Start(DotnetMuxer.Path.FullName, ["clean", "-v:q", program]).WaitForExit(); - } - -#if DEBUG - AnsiConsole.MarkupLine($":rocket: {DotnetMuxer.Path.FullName} run -v:q {program} {string.Join(' ', args)}"); -#endif - - var start = new ProcessStartInfo(DotnetMuxer.Path.FullName, ["run", "-v:q", program, .. args]); - var process = Process.Start(start); - process?.WaitForExit(); - - Dispatcher.MainThread.Shutdown(); - - return process?.ExitCode ?? 1; -} \ No newline at end of file diff --git a/src/runcs/Properties/launchSettings.json b/src/runcs/Properties/launchSettings.json index e84fb79..58fc9d7 100644 --- a/src/runcs/Properties/launchSettings.json +++ b/src/runcs/Properties/launchSettings.json @@ -1,5 +1,9 @@ { "profiles": { + "args": { + "commandName": "Project", + "commandLineArgs": "kzu/runcs@v0.1.0 dotnet rocks" + }, "github": { "commandName": "Project", "commandLineArgs": "kzu/runcs@main:program.cs" diff --git a/src/runcs/runcs.csproj b/src/runcs/runcs.csproj index 28428ac..5f9a114 100644 --- a/src/runcs/runcs.csproj +++ b/src/runcs/runcs.csproj @@ -6,8 +6,6 @@ runcs Devlooped - - false runcs runcs true