Files
runner/src/Runner.Common/CommandLineParser.cs
2022-09-15 12:14:10 +02:00

128 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using GitHub.DistributedTask.Logging;
using GitHub.Runner.Sdk;
//
// Pattern:
// cmd1 cmd2 --arg1 arg1val --aflag --arg2 arg2val
//
namespace GitHub.Runner.Common
{
public sealed class CommandLineParser
{
private ISecretMasker _secretMasker;
private Tracing _trace;
public List<string> Commands { get; }
public HashSet<string> Flags { get; }
public Dictionary<string, string> Args { get; }
public HashSet<string> SecretArgNames { get; }
private bool HasArgs { get; set; }
public CommandLineParser(IHostContext hostContext, string[] secretArgNames)
{
_secretMasker = hostContext.SecretMasker;
_trace = hostContext.GetTrace(nameof(CommandLineParser));
Commands = new List<string>();
Flags = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Args = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
SecretArgNames = new HashSet<string>(secretArgNames ?? new string[0], StringComparer.OrdinalIgnoreCase);
}
public bool IsCommand(string name)
{
bool result = false;
if (Commands.Count > 0)
{
result = String.Equals(name, Commands[0], StringComparison.CurrentCultureIgnoreCase);
}
return result;
}
public void Parse(string[] args)
{
_trace.Info(nameof(Parse));
ArgUtil.NotNull(args, nameof(args));
_trace.Info("Parsing {0} args", args.Length);
string argScope = null;
foreach (string arg in args)
{
_trace.Info("parsing argument");
HasArgs = HasArgs || arg.StartsWith("--");
_trace.Info("HasArgs: {0}", HasArgs);
if (string.Equals(arg, "/?", StringComparison.Ordinal))
{
Flags.Add("help");
}
else if (!HasArgs)
{
_trace.Info("Adding Command: {0}", arg);
Commands.Add(arg.Trim());
}
else
{
// it's either an arg, an arg value or a flag
if (arg.StartsWith("--") && arg.Length > 2)
{
string argVal = arg.Substring(2);
_trace.Info("arg: {0}", argVal);
// this means two --args in a row which means previous was a flag
if (argScope != null)
{
_trace.Info("Adding flag: {0}", argScope);
Flags.Add(argScope.Trim());
}
argScope = argVal;
}
else if (!arg.StartsWith("-"))
{
// we found a value - check if we're in scope of an arg
if (argScope != null && !Args.ContainsKey(argScope = argScope.Trim()))
{
if (SecretArgNames.Contains(argScope))
{
_secretMasker.AddValue(arg);
}
_trace.Info("Adding option '{0}': '{1}'", argScope, arg);
// ignore duplicates - first wins - below will be val1
// --arg1 val1 --arg1 val1
Args.Add(argScope, arg);
argScope = null;
}
}
else
{
//
// ignoring the second value for an arg (val2 below)
// --arg val1 val2
// ignoring invalid things like empty - and --
// --arg val1 -- --flag
_trace.Info("Ignoring arg");
}
}
}
_trace.Verbose("done parsing arguments");
// handle last arg being a flag
if (argScope != null)
{
Flags.Add(argScope);
}
_trace.Verbose("Exiting parse");
}
}
}