Execute valet from process
This commit is contained in:
Родитель
e1305cdce5
Коммит
e0a0bfbfe7
|
@ -9,6 +9,9 @@
|
||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
.DS_STORE
|
||||||
|
output/
|
||||||
|
dist/
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
|
@ -41,9 +41,7 @@ public class App
|
||||||
|
|
||||||
public async Task<int> ExecuteValetAsync(string[] args)
|
public async Task<int> ExecuteValetAsync(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(string.Join(' ', args));
|
var result = await _dockerService.ExecuteCommandAsync($"{ValetContainerRegistry}/{ValetImage}:latest", args);
|
||||||
|
return result ? 0 : 1;
|
||||||
await _dockerService.ExecuteCommandAsync($"{ValetContainerRegistry}/{ValetImage}:latest", args);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Valet.Interfaces;
|
||||||
|
|
||||||
|
public interface IProcessService
|
||||||
|
{
|
||||||
|
Task<bool> RunAsync(string filename, string arguments, string? cwd = null, IEnumerable<(string, string)>? environmentVariables = null);
|
||||||
|
}
|
|
@ -1,27 +1,43 @@
|
||||||
// See https://aka.ms/new-console-template for more information
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
|
using CommandLine.Text;
|
||||||
using Valet;
|
using Valet;
|
||||||
using Valet.Models;
|
using Valet.Models;
|
||||||
using Valet.Services;
|
using Valet.Services;
|
||||||
|
|
||||||
|
var processService = new ProcessService();
|
||||||
|
|
||||||
var app = new App(
|
var app = new App(
|
||||||
new DockerService(),
|
new DockerService(processService),
|
||||||
new AuthenticationService()
|
new AuthenticationService()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
var parser = new Parser(with => with.HelpWriter = null);
|
||||||
|
var parserResult = parser.ParseArguments<UpdateOptions, ExecuteOptions>(args);
|
||||||
|
|
||||||
// TODO: Utilize help menu from Valet itself
|
// TODO: Utilize help menu from Valet itself
|
||||||
await Parser.Default.ParseArguments<UpdateOptions, ExecuteOptions>(args)
|
await parserResult.WithNotParsedAsync(errs =>
|
||||||
.MapResult(
|
{
|
||||||
(UpdateOptions opts) =>
|
return app.ExecuteValetAsync(args);
|
||||||
{
|
});
|
||||||
return app.UpdateValetAsync(opts.Username, opts.Password);
|
|
||||||
},
|
await parserResult.WithParsedAsync<UpdateOptions>(options => app.UpdateValetAsync(options.Username, options.Password));
|
||||||
(ExecuteOptions opts) =>
|
|
||||||
{
|
|
||||||
return Task.FromResult(1);
|
// (UpdateOptions opts) => app.UpdateValetAsync(opts.Username, opts.Password),
|
||||||
},
|
// (ExecuteOptions opts) => Task.FromResult(1),
|
||||||
_ =>
|
// _ =>
|
||||||
{
|
// {
|
||||||
return app.ExecuteValetAsync(args);
|
// var helpText = new HelpText
|
||||||
});
|
// {
|
||||||
|
// Heading = "Tool tool usage",
|
||||||
|
// AdditionalNewLineAfterOption = false,
|
||||||
|
// AddDashesToOption = true
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// Console.Error.WriteLine(helpText);
|
||||||
|
//
|
||||||
|
// return app.ExecuteValetAsync(args);
|
||||||
|
// });
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
using Docker.DotNet;
|
using Docker.DotNet;
|
||||||
using Docker.DotNet.Models;
|
using Docker.DotNet.Models;
|
||||||
using Valet.Interfaces;
|
using Valet.Interfaces;
|
||||||
|
@ -8,8 +10,10 @@ namespace Valet.Services;
|
||||||
public class DockerService : IDockerService
|
public class DockerService : IDockerService
|
||||||
{
|
{
|
||||||
private readonly DockerClient _client;
|
private readonly DockerClient _client;
|
||||||
|
private readonly IProcessService _processService;
|
||||||
|
|
||||||
private readonly string[] _valetEnvVars = {
|
private readonly string[] _valetEnvVars =
|
||||||
|
{
|
||||||
"GH_ACCESS_TOKEN", "GH_INSTANCE_URL", "GITHUB_ACCESS_TOKEN", "GITHUB_INSTANCE_URL",
|
"GH_ACCESS_TOKEN", "GH_INSTANCE_URL", "GITHUB_ACCESS_TOKEN", "GITHUB_INSTANCE_URL",
|
||||||
"JENKINSFILE_ACCESS_TOKEN", "JENKINS_USERNAME", "JENKINS_ACCESS_TOKEN", "JENKINS_INSTANCE_URL",
|
"JENKINSFILE_ACCESS_TOKEN", "JENKINS_USERNAME", "JENKINS_ACCESS_TOKEN", "JENKINS_INSTANCE_URL",
|
||||||
"TRAVIS_CI_ACCESS_TOKEN", "TRAVIS_CI_INSTANCE_URL", "TRAVIS_CI_SOURCE_GITHUB_ACCESS_TOKEN", "TRAVIS_CI_SOURCE_GITHUB_INSTANCE_URL", "TRAVIS_CI_ORGANIZATION",
|
"TRAVIS_CI_ACCESS_TOKEN", "TRAVIS_CI_INSTANCE_URL", "TRAVIS_CI_SOURCE_GITHUB_ACCESS_TOKEN", "TRAVIS_CI_SOURCE_GITHUB_INSTANCE_URL", "TRAVIS_CI_ORGANIZATION",
|
||||||
|
@ -18,9 +22,11 @@ public class DockerService : IDockerService
|
||||||
"AZURE_DEVOPS_ACCESS_TOKEN", "AZURE_DEVOPS_PROJECT", "AZURE_DEVOPS_ORGANIZATION", "AZURE_DEVOPS_INSTANCE_URL",
|
"AZURE_DEVOPS_ACCESS_TOKEN", "AZURE_DEVOPS_PROJECT", "AZURE_DEVOPS_ORGANIZATION", "AZURE_DEVOPS_INSTANCE_URL",
|
||||||
"YAML_VERBOSITY", "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "OCTOKIT_PROXY", "OCTOKIT_SSL_VERIFY_MODE",
|
"YAML_VERBOSITY", "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "OCTOKIT_PROXY", "OCTOKIT_SSL_VERIFY_MODE",
|
||||||
};
|
};
|
||||||
|
|
||||||
public DockerService()
|
public DockerService(IProcessService processService)
|
||||||
{
|
{
|
||||||
|
_processService = processService;
|
||||||
|
|
||||||
// TODO: Raise error if docker daemon not started
|
// TODO: Raise error if docker daemon not started
|
||||||
_client = new DockerClientConfiguration()
|
_client = new DockerClientConfiguration()
|
||||||
.CreateClient();
|
.CreateClient();
|
||||||
|
@ -47,65 +53,47 @@ public class DockerService : IDockerService
|
||||||
|
|
||||||
public async Task<bool> ExecuteCommandAsync(string image, params string[] arguments)
|
public async Task<bool> ExecuteCommandAsync(string image, params string[] arguments)
|
||||||
{
|
{
|
||||||
// MSYS_NO_PATHCONV=1 docker run --rm $dockerArgs --env INSTALLATION_ID="$INSTALLATION_ID" $DOCKER_OPTIONS -v "$VALET_LOCAL_FOLDER"":/data" "$VALET_IMAGE":"$VALET_IMAGE_VERSION" "$@"
|
var valetArguments = new List<string>();
|
||||||
var container = await _client.Containers.CreateContainerAsync(
|
valetArguments.Add("run --rm");
|
||||||
new CreateContainerParameters
|
valetArguments.AddRange(GetEnvironmentVariableArguments());
|
||||||
{
|
valetArguments.Add($"-v \"{Directory.GetCurrentDirectory()}\":/data");
|
||||||
Image = image,
|
valetArguments.Add(image);
|
||||||
HostConfig = new HostConfig
|
valetArguments.AddRange(arguments);
|
||||||
{
|
|
||||||
Binds = new[] { $"{Directory.GetCurrentDirectory()}:/data" },
|
|
||||||
AutoRemove = true,
|
|
||||||
},
|
|
||||||
Env = GetEnvironmentVariables().ToArray()
|
|
||||||
}
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
|
|
||||||
await _client.Containers.StartContainerAsync(
|
Debug.WriteLine(string.Join(' ', valetArguments));
|
||||||
container.ID,
|
|
||||||
new ContainerStartParameters()
|
var result = await _processService.RunAsync(
|
||||||
|
"docker",
|
||||||
|
string.Join(' ', valetArguments),
|
||||||
|
Directory.GetCurrentDirectory(),
|
||||||
|
new[] { ("MSYS_NO_PATHCONV", "1") }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<string> GetEnvironmentVariables()
|
private IEnumerable<string> GetEnvironmentVariableArguments()
|
||||||
{
|
{
|
||||||
if (File.Exists(".env.local"))
|
if (File.Exists(".env.local"))
|
||||||
{
|
{
|
||||||
foreach (var line in File.ReadAllLines(".env.local"))
|
yield return "--env-file .env.local";
|
||||||
{
|
|
||||||
var parts = line.Split('=', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (parts.Length != 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
yield return $"{parts[0]}={parts[1]}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var installationId = Environment.GetEnvironmentVariable("INSTALLATION_ID") ?? "get_from_client";
|
||||||
|
yield return $"--env INSTALLATION_ID={installationId}";
|
||||||
|
|
||||||
foreach (var env in _valetEnvVars)
|
foreach (var env in _valetEnvVars)
|
||||||
{
|
{
|
||||||
var value = Environment.GetEnvironmentVariable(env);
|
var value = Environment.GetEnvironmentVariable(env);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value)) continue;
|
||||||
{
|
|
||||||
var key = env;
|
|
||||||
// TODO: This can probably be cleaner
|
|
||||||
if (key.StartsWith("GH_"))
|
|
||||||
key = key.Replace("GH_", "GITHUB_");
|
|
||||||
|
|
||||||
yield return $"{key}={value}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var installationId = Environment.GetEnvironmentVariable("INSTALLATION_ID");
|
var key = env;
|
||||||
if (installationId == null)
|
// TODO: This can probably be cleaner
|
||||||
{
|
if (key.StartsWith("GH_"))
|
||||||
installationId = "get_from_client";
|
key = key.Replace("GH_", "GITHUB_");
|
||||||
}
|
|
||||||
|
|
||||||
yield return $"INSTALLATION_ID={installationId}";
|
yield return $"--env {key}={value}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using Valet.Interfaces;
|
||||||
|
|
||||||
|
namespace Valet.Services;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class ProcessService : IProcessService
|
||||||
|
{
|
||||||
|
public Task<bool> RunAsync(string filename, string arguments, string? cwd = null, IEnumerable<(string, string)>? environmentVariables = null)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = filename,
|
||||||
|
Arguments = arguments,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
WorkingDirectory = cwd
|
||||||
|
};
|
||||||
|
|
||||||
|
if (environmentVariables != null)
|
||||||
|
{
|
||||||
|
foreach (var (key, value) in environmentVariables)
|
||||||
|
{
|
||||||
|
startInfo.EnvironmentVariables.Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = startInfo,
|
||||||
|
EnableRaisingEvents = true
|
||||||
|
};
|
||||||
|
|
||||||
|
void OnProcessExited(object? sender, EventArgs args)
|
||||||
|
{
|
||||||
|
process.Exited -= OnProcessExited;
|
||||||
|
process.OutputDataReceived -= OnOutputDataReceived;
|
||||||
|
|
||||||
|
if (process.ExitCode == 0)
|
||||||
|
{
|
||||||
|
tcs.TrySetResult(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Verify works
|
||||||
|
var error = process.StandardError.ReadToEnd();
|
||||||
|
tcs.TrySetException(new Exception(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
process.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.OutputDataReceived += OnOutputDataReceived;
|
||||||
|
process.Exited += OnProcessExited;
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче