mirror of
https://github.com/actions/runner.git
synced 2025-12-11 12:57:05 +00:00
GitHub Actions Runner
This commit is contained in:
467
src/Runner.Listener/CommandSettings.cs
Normal file
467
src/Runner.Listener/CommandSettings.cs
Normal file
@@ -0,0 +1,467 @@
|
||||
using GitHub.Runner.Listener.Configuration;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GitHub.DistributedTask.Logging;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Listener
|
||||
{
|
||||
public sealed class CommandSettings
|
||||
{
|
||||
private readonly Dictionary<string, string> _envArgs = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly CommandLineParser _parser;
|
||||
private readonly IPromptManager _promptManager;
|
||||
private readonly Tracing _trace;
|
||||
|
||||
private readonly string[] validCommands =
|
||||
{
|
||||
Constants.Runner.CommandLine.Commands.Configure,
|
||||
Constants.Runner.CommandLine.Commands.Remove,
|
||||
Constants.Runner.CommandLine.Commands.Run,
|
||||
Constants.Runner.CommandLine.Commands.Warmup,
|
||||
};
|
||||
|
||||
private readonly string[] validFlags =
|
||||
{
|
||||
Constants.Runner.CommandLine.Flags.Commit,
|
||||
#if OS_WINDOWS
|
||||
Constants.Runner.CommandLine.Flags.GitUseSChannel,
|
||||
#endif
|
||||
Constants.Runner.CommandLine.Flags.Help,
|
||||
Constants.Runner.CommandLine.Flags.Replace,
|
||||
Constants.Runner.CommandLine.Flags.RunAsService,
|
||||
Constants.Runner.CommandLine.Flags.Once,
|
||||
Constants.Runner.CommandLine.Flags.SslSkipCertValidation,
|
||||
Constants.Runner.CommandLine.Flags.Unattended,
|
||||
Constants.Runner.CommandLine.Flags.Version
|
||||
};
|
||||
|
||||
private readonly string[] validArgs =
|
||||
{
|
||||
Constants.Runner.CommandLine.Args.Agent,
|
||||
Constants.Runner.CommandLine.Args.Auth,
|
||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||
Constants.Runner.CommandLine.Args.NotificationPipeName,
|
||||
Constants.Runner.CommandLine.Args.Password,
|
||||
Constants.Runner.CommandLine.Args.Pool,
|
||||
Constants.Runner.CommandLine.Args.ProxyPassword,
|
||||
Constants.Runner.CommandLine.Args.ProxyUrl,
|
||||
Constants.Runner.CommandLine.Args.ProxyUserName,
|
||||
Constants.Runner.CommandLine.Args.SslCACert,
|
||||
Constants.Runner.CommandLine.Args.SslClientCert,
|
||||
Constants.Runner.CommandLine.Args.SslClientCertKey,
|
||||
Constants.Runner.CommandLine.Args.SslClientCertArchive,
|
||||
Constants.Runner.CommandLine.Args.SslClientCertPassword,
|
||||
Constants.Runner.CommandLine.Args.StartupType,
|
||||
Constants.Runner.CommandLine.Args.Token,
|
||||
Constants.Runner.CommandLine.Args.Url,
|
||||
Constants.Runner.CommandLine.Args.UserName,
|
||||
Constants.Runner.CommandLine.Args.WindowsLogonAccount,
|
||||
Constants.Runner.CommandLine.Args.WindowsLogonPassword,
|
||||
Constants.Runner.CommandLine.Args.Work
|
||||
};
|
||||
|
||||
// Commands.
|
||||
public bool Configure => TestCommand(Constants.Runner.CommandLine.Commands.Configure);
|
||||
public bool Remove => TestCommand(Constants.Runner.CommandLine.Commands.Remove);
|
||||
public bool Run => TestCommand(Constants.Runner.CommandLine.Commands.Run);
|
||||
public bool Warmup => TestCommand(Constants.Runner.CommandLine.Commands.Warmup);
|
||||
|
||||
// Flags.
|
||||
public bool Commit => TestFlag(Constants.Runner.CommandLine.Flags.Commit);
|
||||
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
||||
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||
|
||||
#if OS_WINDOWS
|
||||
public bool GitUseSChannel => TestFlag(Constants.Runner.CommandLine.Flags.GitUseSChannel);
|
||||
#endif
|
||||
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
||||
|
||||
// Constructor.
|
||||
public CommandSettings(IHostContext context, string[] args)
|
||||
{
|
||||
ArgUtil.NotNull(context, nameof(context));
|
||||
_promptManager = context.GetService<IPromptManager>();
|
||||
_trace = context.GetTrace(nameof(CommandSettings));
|
||||
|
||||
// Parse the command line args.
|
||||
_parser = new CommandLineParser(
|
||||
hostContext: context,
|
||||
secretArgNames: Constants.Runner.CommandLine.Args.Secrets);
|
||||
_parser.Parse(args);
|
||||
|
||||
// Store and remove any args passed via environment variables.
|
||||
IDictionary environment = Environment.GetEnvironmentVariables();
|
||||
string envPrefix = "ACTIONS_RUNNER_INPUT_";
|
||||
foreach (DictionaryEntry entry in environment)
|
||||
{
|
||||
// Test if starts with ACTIONS_RUNNER_INPUT_.
|
||||
string fullKey = entry.Key as string ?? string.Empty;
|
||||
if (fullKey.StartsWith(envPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string val = (entry.Value as string ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(val))
|
||||
{
|
||||
// Extract the name.
|
||||
string name = fullKey.Substring(envPrefix.Length);
|
||||
|
||||
// Mask secrets.
|
||||
bool secret = Constants.Runner.CommandLine.Args.Secrets.Any(x => string.Equals(x, name, StringComparison.OrdinalIgnoreCase));
|
||||
if (secret)
|
||||
{
|
||||
context.SecretMasker.AddValue(val);
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
_envArgs[name] = val;
|
||||
}
|
||||
|
||||
// Remove from the environment block.
|
||||
_trace.Info($"Removing env var: '{fullKey}'");
|
||||
Environment.SetEnvironmentVariable(fullKey, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate commandline parser result
|
||||
public List<string> Validate()
|
||||
{
|
||||
List<string> unknowns = new List<string>();
|
||||
|
||||
// detect unknown commands
|
||||
unknowns.AddRange(_parser.Commands.Where(x => !validCommands.Contains(x, StringComparer.OrdinalIgnoreCase)));
|
||||
|
||||
// detect unknown flags
|
||||
unknowns.AddRange(_parser.Flags.Where(x => !validFlags.Contains(x, StringComparer.OrdinalIgnoreCase)));
|
||||
|
||||
// detect unknown args
|
||||
unknowns.AddRange(_parser.Args.Keys.Where(x => !validArgs.Contains(x, StringComparer.OrdinalIgnoreCase)));
|
||||
|
||||
return unknowns;
|
||||
}
|
||||
|
||||
//
|
||||
// Interactive flags.
|
||||
//
|
||||
public bool GetReplace()
|
||||
{
|
||||
return TestFlagOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Flags.Replace,
|
||||
description: "Would you like to replace the existing runner? (Y/N)",
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
public bool GetRunAsService()
|
||||
{
|
||||
return TestFlagOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Flags.RunAsService,
|
||||
description: "Would you like to run the runner as service? (Y/N)",
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
public bool GetAutoLaunchBrowser()
|
||||
{
|
||||
return TestFlagOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Flags.LaunchBrowser,
|
||||
description: "Would you like to launch your browser for AAD Device Code Flow? (Y/N)",
|
||||
defaultValue: true);
|
||||
}
|
||||
//
|
||||
// Args.
|
||||
//
|
||||
public string GetAgentName()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Agent,
|
||||
description: "Enter the name of runner:",
|
||||
defaultValue: Environment.MachineName ?? "myagent",
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetAuth(string defaultValue)
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Auth,
|
||||
description: "How would you like to authenticate?",
|
||||
defaultValue: defaultValue,
|
||||
validator: Validators.AuthSchemeValidator);
|
||||
}
|
||||
|
||||
public string GetPassword()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Password,
|
||||
description: "What is your GitHub password?",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetPool()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Pool,
|
||||
description: "Enter the name of your runner pool:",
|
||||
defaultValue: "default",
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetToken()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Token,
|
||||
description: "Enter your personal access token:",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetRunnerRegisterToken()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Token,
|
||||
description: "Enter runner register token:",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetUrl(bool suppressPromptIfEmpty = false)
|
||||
{
|
||||
// Note, GetArg does not consume the arg (like GetArgOrPrompt does).
|
||||
if (suppressPromptIfEmpty &&
|
||||
string.IsNullOrEmpty(GetArg(Constants.Runner.CommandLine.Args.Url)))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Url,
|
||||
description: "What is the URL of your repository?",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.ServerUrlValidator);
|
||||
}
|
||||
|
||||
public string GetUserName()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.UserName,
|
||||
description: "What is your GitHub username?",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetWindowsLogonAccount(string defaultValue, string descriptionMsg)
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.WindowsLogonAccount,
|
||||
description: descriptionMsg,
|
||||
defaultValue: defaultValue,
|
||||
validator: Validators.NTAccountValidator);
|
||||
}
|
||||
|
||||
public string GetWindowsLogonPassword(string accountName)
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.WindowsLogonPassword,
|
||||
description: $"Password for the account {accountName}",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetWork()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Work,
|
||||
description: "Enter name of work folder:",
|
||||
defaultValue: Constants.Path.WorkDirectory,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetMonitorSocketAddress()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.MonitorSocketAddress);
|
||||
}
|
||||
|
||||
public string GetNotificationPipeName()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.NotificationPipeName);
|
||||
}
|
||||
|
||||
public string GetNotificationSocketAddress()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.NotificationSocketAddress);
|
||||
}
|
||||
|
||||
// This is used to find out the source from where the Runner.Listener.exe was launched at the time of run
|
||||
public string GetStartupType()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.StartupType);
|
||||
}
|
||||
|
||||
public string GetProxyUrl()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyUrl);
|
||||
}
|
||||
|
||||
public string GetProxyUserName()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyUserName);
|
||||
}
|
||||
|
||||
public string GetProxyPassword()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyPassword);
|
||||
}
|
||||
|
||||
public bool GetSkipCertificateValidation()
|
||||
{
|
||||
return TestFlag(Constants.Runner.CommandLine.Flags.SslSkipCertValidation);
|
||||
}
|
||||
|
||||
public string GetCACertificate()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.SslCACert);
|
||||
}
|
||||
|
||||
public string GetClientCertificate()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.SslClientCert);
|
||||
}
|
||||
|
||||
public string GetClientCertificatePrivateKey()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.SslClientCertKey);
|
||||
}
|
||||
|
||||
public string GetClientCertificateArchrive()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.SslClientCertArchive);
|
||||
}
|
||||
|
||||
public string GetClientCertificatePassword()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.SslClientCertPassword);
|
||||
}
|
||||
|
||||
//
|
||||
// Private helpers.
|
||||
//
|
||||
private string GetArg(string name)
|
||||
{
|
||||
string result;
|
||||
if (!_parser.Args.TryGetValue(name, out result))
|
||||
{
|
||||
result = GetEnvArg(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void RemoveArg(string name)
|
||||
{
|
||||
if (_parser.Args.ContainsKey(name))
|
||||
{
|
||||
_parser.Args.Remove(name);
|
||||
}
|
||||
|
||||
if (_envArgs.ContainsKey(name))
|
||||
{
|
||||
_envArgs.Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetArgOrPrompt(
|
||||
string name,
|
||||
string description,
|
||||
string defaultValue,
|
||||
Func<string, bool> validator)
|
||||
{
|
||||
// Check for the arg in the command line parser.
|
||||
ArgUtil.NotNull(validator, nameof(validator));
|
||||
string result = GetArg(name);
|
||||
|
||||
// Return the arg if it is not empty and is valid.
|
||||
_trace.Info($"Arg '{name}': '{result}'");
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
// After read the arg from input commandline args, remove it from Arg dictionary,
|
||||
// This will help if bad arg value passed through CommandLine arg, when ConfigurationManager ask CommandSetting the second time,
|
||||
// It will prompt for input instead of continue use the bad input.
|
||||
_trace.Info($"Remove {name} from Arg dictionary.");
|
||||
RemoveArg(name);
|
||||
|
||||
if (validator(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_trace.Info("Arg is invalid.");
|
||||
}
|
||||
|
||||
// Otherwise prompt for the arg.
|
||||
return _promptManager.ReadValue(
|
||||
argName: name,
|
||||
description: description,
|
||||
secret: Constants.Runner.CommandLine.Args.Secrets.Any(x => string.Equals(x, name, StringComparison.OrdinalIgnoreCase)),
|
||||
defaultValue: defaultValue,
|
||||
validator: validator,
|
||||
unattended: Unattended);
|
||||
}
|
||||
|
||||
private string GetEnvArg(string name)
|
||||
{
|
||||
string val;
|
||||
if (_envArgs.TryGetValue(name, out val) && !string.IsNullOrEmpty(val))
|
||||
{
|
||||
_trace.Info($"Env arg '{name}': '{val}'");
|
||||
return val;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool TestCommand(string name)
|
||||
{
|
||||
bool result = _parser.IsCommand(name);
|
||||
_trace.Info($"Command '{name}': '{result}'");
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TestFlag(string name)
|
||||
{
|
||||
bool result = _parser.Flags.Contains(name);
|
||||
if (!result)
|
||||
{
|
||||
string envStr = GetEnvArg(name);
|
||||
if (!bool.TryParse(envStr, out result))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
_trace.Info($"Flag '{name}': '{result}'");
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TestFlagOrPrompt(
|
||||
string name,
|
||||
string description,
|
||||
bool defaultValue)
|
||||
{
|
||||
bool result = TestFlag(name);
|
||||
if (!result)
|
||||
{
|
||||
result = _promptManager.ReadBool(
|
||||
argName: name,
|
||||
description: description,
|
||||
defaultValue: defaultValue,
|
||||
unattended: Unattended);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user