Skip to content

Commit 8ec80a1

Browse files
New command-line APIs (#1068)
1 parent 12c1566 commit 8ec80a1

File tree

5 files changed

+114
-74
lines changed

5 files changed

+114
-74
lines changed

NuGet.config

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
88
<add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />
99
<add key="dotnet8-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json" />
10+
<!-- Feeds for command-line-api -->
11+
<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
1012
<!-- Feeds for source-build command-line-api intermediate -->
1113
<add key="dotnet-libraries-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries-transport/nuget/v3/index.json" />
1214
</packageSources>

eng/Version.Details.xml

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Dependencies>
33
<ProductDependencies>
4-
<Dependency Name="System.CommandLine" Version="2.0.0-beta4.22272.1">
4+
<Dependency Name="System.CommandLine" Version="2.0.0-beta4.23307.1">
55
<Uri>https://github.com/dotnet/command-line-api</Uri>
6-
<Sha>209b724a3c843253d3071e8348c353b297b0b8b5</Sha>
6+
<Sha>02fe27cd6a9b001c8feb7938e6ef4b3799745759</Sha>
77
</Dependency>
8-
<Dependency Name="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1">
8+
<Dependency Name="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.23307.1">
99
<Uri>https://github.com/dotnet/command-line-api</Uri>
10-
<Sha>209b724a3c843253d3071e8348c353b297b0b8b5</Sha>
10+
<Sha>02fe27cd6a9b001c8feb7938e6ef4b3799745759</Sha>
1111
</Dependency>
12-
<Dependency Name="System.CommandLine.Rendering" Version="0.4.0-alpha.22272.1">
12+
<Dependency Name="System.CommandLine.Rendering" Version="0.4.0-alpha.23307.1">
1313
<Uri>https://github.com/dotnet/command-line-api</Uri>
14-
<Sha>209b724a3c843253d3071e8348c353b297b0b8b5</Sha>
14+
<Sha>02fe27cd6a9b001c8feb7938e6ef4b3799745759</Sha>
1515
</Dependency>
16-
<Dependency Name="Microsoft.SourceBuild.Intermediate.command-line-api" Version="0.1.327201">
16+
<Dependency Name="Microsoft.SourceBuild.Intermediate.command-line-api" Version="0.1.430701">
1717
<Uri>https://github.com/dotnet/command-line-api</Uri>
18-
<Sha>209b724a3c843253d3071e8348c353b297b0b8b5</Sha>
18+
<Sha>02fe27cd6a9b001c8feb7938e6ef4b3799745759</Sha>
1919
<SourceBuild RepoName="command-line-api" ManagedOnly="true" />
2020
</Dependency>
2121
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="8.0.0-alpha.1.23371.1">

eng/Versions.props

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
</PropertyGroup>
1111
<PropertyGroup>
1212
<!-- commandline -->
13-
<SystemCommandLineVersion>2.0.0-beta4.22272.1</SystemCommandLineVersion>
14-
<SystemCommandLineNamingConventionBinderVersion>2.0.0-beta4.22272.1</SystemCommandLineNamingConventionBinderVersion>
15-
<SystemCommandLineRenderingVersion>0.4.0-alpha.22272.1</SystemCommandLineRenderingVersion>
13+
<SystemCommandLineVersion>2.0.0-beta4.23307.1</SystemCommandLineVersion>
14+
<SystemCommandLineNamingConventionBinderVersion>2.0.0-beta4.23307.1</SystemCommandLineNamingConventionBinderVersion>
15+
<SystemCommandLineRenderingVersion>0.4.0-alpha.23307.1</SystemCommandLineRenderingVersion>
1616
<!-- msbuild -->
1717
<MicrosoftBuildVersion>17.3.2</MicrosoftBuildVersion>
1818
<MicrosoftBuildTasksCoreVersion>17.3.2</MicrosoftBuildTasksCoreVersion>

src/dotnet-sourcelink/Program.cs

+100-60
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,18 @@ private record DocumentInfo(
4242
ImmutableArray<byte> Hash,
4343
Guid HashAlgorithm);
4444

45-
private readonly IConsole _console;
45+
private readonly ParseResult _parseResult;
4646
private bool _errorReported;
4747

48-
public Program(IConsole console)
48+
public Program(ParseResult parseResult)
4949
{
50-
_console = console;
50+
_parseResult = parseResult;
5151
}
5252

5353
public static async Task<int> Main(string[] args)
5454
{
5555
var rootCommand = GetRootCommand();
56-
return await rootCommand.InvokeAsync(args);
56+
return await rootCommand.Parse(args).InvokeAsync();
5757
}
5858

5959
private static string GetSourceLinkVersion()
@@ -62,41 +62,77 @@ private static string GetSourceLinkVersion()
6262
return attribute.InformationalVersion.Split('+').First();
6363
}
6464

65-
private static RootCommand GetRootCommand()
65+
private static CliRootCommand GetRootCommand()
6666
{
67-
var authArg = new Option<string>(new[] { "--auth", "-a" }, "Authentication method").FromAmong(AuthenticationMethod.Basic);
68-
var userArg = new Option<string>(new[] { "--user", "-u" }, "Username to use to authenticate") { Arity = ArgumentArity.ExactlyOne };
69-
var passwordArg = new Option<string>(new[] { "--password", "-p" }, "Password to use to authenticate") { Arity = ArgumentArity.ExactlyOne };
67+
var authArg = new CliOption<string>("--auth", "-a")
68+
{
69+
Description = "Authentication method"
70+
};
71+
authArg.AcceptOnlyFromAmong(AuthenticationMethod.Basic);
72+
73+
var userArg = new CliOption<string>("--user", "-u")
74+
{
75+
Description = "Username to use to authenticate",
76+
Arity = ArgumentArity.ExactlyOne
77+
};
78+
79+
var passwordArg = new CliOption<string>("--password", "-p")
80+
{
81+
Description = "Password to use to authenticate",
82+
Arity = ArgumentArity.ExactlyOne
83+
};
7084

71-
var test = new Command("test", "TODO")
85+
var offlineArg = new CliOption<bool>("--offline")
7286
{
73-
new Argument<string>("path", "Path to an assembly or .pdb"),
87+
Description = "Offline mode - skip validation of sourcelink URL targets"
88+
};
89+
90+
var test = new CliCommand("test", "TODO")
91+
{
92+
new CliArgument<string>("path")
93+
{
94+
Description = "Path to an assembly or .pdb"
95+
},
7496
authArg,
75-
new Option<Encoding>(new[] { "--auth-encoding", "-e" }, (arg) => Encoding.GetEncoding(arg.Tokens.Single().Value), false, "Encoding to use for authentication value"),
97+
new CliOption<Encoding>("--auth-encoding", "-e")
98+
{
99+
CustomParser = arg => Encoding.GetEncoding(arg.Tokens.Single().Value),
100+
Description = "Encoding to use for authentication value"
101+
},
76102
userArg,
77103
passwordArg,
104+
offlineArg,
78105
};
79-
test.Handler = CommandHandler.Create<string, string?, Encoding?, string?, string?, IConsole>(TestAsync);
106+
test.Action = CommandHandler.Create<string, string?, Encoding?, string?, string?, bool, ParseResult>(TestAsync);
80107

81-
var printJson = new Command("print-json", "Print Source Link JSON stored in the PDB")
108+
var printJson = new CliCommand("print-json", "Print Source Link JSON stored in the PDB")
82109
{
83-
new Argument<string>("path", "Path to an assembly or .pdb"),
110+
new CliArgument<string>("path")
111+
{
112+
Description = "Path to an assembly or .pdb"
113+
}
84114
};
85-
printJson.Handler = CommandHandler.Create<string, IConsole>(PrintJsonAsync);
115+
printJson.Action = CommandHandler.Create<string, ParseResult>(PrintJsonAsync);
86116

87-
var printDocuments = new Command("print-documents", "TODO")
117+
var printDocuments = new CliCommand("print-documents", "TODO")
88118
{
89-
new Argument<string>("path", "Path to an assembly or .pdb"),
119+
new CliArgument<string>("path")
120+
{
121+
Description = "Path to an assembly or .pdb"
122+
}
90123
};
91-
printDocuments.Handler = CommandHandler.Create<string, IConsole>(PrintDocumentsAsync);
124+
printDocuments.Action = CommandHandler.Create<string, ParseResult>(PrintDocumentsAsync);
92125

93-
var printUrls = new Command("print-urls", "TODO")
126+
var printUrls = new CliCommand("print-urls", "TODO")
94127
{
95-
new Argument<string>("path", "Path to an assembly or .pdb"),
128+
new CliArgument<string>("path")
129+
{
130+
Description = "Path to an assembly or .pdb"
131+
}
96132
};
97-
printUrls.Handler = CommandHandler.Create<string, IConsole>(PrintUrlsAsync);
133+
printUrls.Action = CommandHandler.Create<string, ParseResult>(PrintUrlsAsync);
98134

99-
var root = new RootCommand()
135+
var root = new CliRootCommand()
100136
{
101137
test,
102138
printJson,
@@ -106,13 +142,13 @@ private static RootCommand GetRootCommand()
106142

107143
root.Description = "dotnet-sourcelink";
108144

109-
root.AddValidator(commandResult =>
145+
root.Validators.Add(commandResult =>
110146
{
111-
if (commandResult.FindResultFor(authArg) != null)
147+
if (commandResult.GetResult(authArg) != null)
112148
{
113-
if (commandResult.FindResultFor(userArg) == null || commandResult.FindResultFor(passwordArg) == null)
149+
if (commandResult.GetResult(userArg) == null || commandResult.GetResult(passwordArg) == null)
114150
{
115-
commandResult.ErrorMessage = "Specify --user and --password options";
151+
commandResult.AddError("Specify --user and --password options");
116152
}
117153
}
118154
});
@@ -122,15 +158,15 @@ private static RootCommand GetRootCommand()
122158

123159
private void ReportError(string message)
124160
{
125-
_console.Error.Write(message);
126-
_console.Error.Write(Environment.NewLine);
161+
_parseResult.Configuration.Error.Write(message);
162+
_parseResult.Configuration.Error.Write(Environment.NewLine);
127163
_errorReported = true;
128164
}
129165

130166
private void WriteOutputLine(string message)
131167
{
132-
_console.Out.Write(message);
133-
_console.Out.Write(Environment.NewLine);
168+
_parseResult.Configuration.Output.Write(message);
169+
_parseResult.Configuration.Output.Write(Environment.NewLine);
134170
}
135171

136172
private static async Task<int> TestAsync(
@@ -139,7 +175,8 @@ private static async Task<int> TestAsync(
139175
Encoding? authEncoding,
140176
string? user,
141177
string? password,
142-
IConsole console)
178+
bool offline,
179+
ParseResult parseResult)
143180
{
144181
var authenticationHeader = (authMethod != null) ? GetAuthenticationHeader(authMethod, authEncoding ?? Encoding.ASCII, user!, password!) : null;
145182

@@ -152,17 +189,17 @@ private static async Task<int> TestAsync(
152189

153190
try
154191
{
155-
return await new Program(console).TestAsync(path, authenticationHeader, cancellationSource.Token).ConfigureAwait(false);
192+
return await new Program(parseResult).TestAsync(path, authenticationHeader, offline, cancellationSource.Token).ConfigureAwait(false);
156193
}
157194
catch (OperationCanceledException)
158195
{
159-
console.Error.Write("Operation canceled.");
160-
console.Error.Write(Environment.NewLine);
196+
parseResult.Configuration.Error.Write("Operation canceled.");
197+
parseResult.Configuration.Error.Write(Environment.NewLine);
161198
return -1;
162199
}
163200
}
164201

165-
private async Task<int> TestAsync(string path, AuthenticationHeaderValue? authenticationHeader, CancellationToken cancellationToken)
202+
private async Task<int> TestAsync(string path, AuthenticationHeaderValue? authenticationHeader, bool offline, CancellationToken cancellationToken)
166203
{
167204
var documents = new List<DocumentInfo>();
168205
ReadAndResolveDocuments(path, documents);
@@ -172,31 +209,34 @@ private async Task<int> TestAsync(string path, AuthenticationHeaderValue? authen
172209
return _errorReported ? 1 : 0;
173210
}
174211

175-
var handler = new HttpClientHandler();
176-
if (handler.SupportsAutomaticDecompression)
177-
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
212+
if (!offline)
213+
{
214+
var handler = new HttpClientHandler();
215+
if (handler.SupportsAutomaticDecompression)
216+
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
178217

179-
using var client = new HttpClient(handler);
180-
client.DefaultRequestHeaders.UserAgent.Add(s_sourceLinkProductHeaderValue);
181-
client.DefaultRequestHeaders.Authorization = authenticationHeader;
218+
using var client = new HttpClient(handler);
219+
client.DefaultRequestHeaders.UserAgent.Add(s_sourceLinkProductHeaderValue);
220+
client.DefaultRequestHeaders.Authorization = authenticationHeader;
182221

183-
var outputLock = new object();
222+
var outputLock = new object();
184223

185-
var errorReporter = new Action<string>(message =>
186-
{
187-
lock (outputLock)
224+
var errorReporter = new Action<string>(message =>
188225
{
189-
ReportError(message);
190-
}
191-
});
226+
lock (outputLock)
227+
{
228+
ReportError(message);
229+
}
230+
});
192231

193-
var tasks = documents.Where(document => document.Uri != null).Select(document => DownloadAndValidateDocumentAsync(client, document, errorReporter, cancellationToken));
194-
195-
_ = await Task.WhenAll(tasks).ConfigureAwait(false);
232+
var tasks = documents.Where(document => document.Uri != null).Select(document => DownloadAndValidateDocumentAsync(client, document, errorReporter, cancellationToken));
196233

197-
if (_errorReported)
198-
{
199-
return 1;
234+
_ = await Task.WhenAll(tasks).ConfigureAwait(false);
235+
236+
if (_errorReported)
237+
{
238+
return 1;
239+
}
200240
}
201241

202242
WriteOutputLine($"File '{path}' validated.");
@@ -277,8 +317,8 @@ private static async Task<bool> DownloadAndValidateDocumentAsync(HttpClient clie
277317
}
278318
}
279319

280-
private static Task<int> PrintJsonAsync(string path, IConsole console)
281-
=> Task.FromResult(new Program(console).PrintJson(path));
320+
private static Task<int> PrintJsonAsync(string path, ParseResult parseResult)
321+
=> Task.FromResult(new Program(parseResult).PrintJson(path));
282322

283323
private int PrintJson(string path)
284324
{
@@ -299,8 +339,8 @@ private int PrintJson(string path)
299339
return _errorReported ? 1 : 0;
300340
}
301341

302-
private static Task<int> PrintDocumentsAsync(string path, IConsole console)
303-
=> Task.FromResult(new Program(console).PrintDocuments(path));
342+
private static Task<int> PrintDocumentsAsync(string path, ParseResult parseResult)
343+
=> Task.FromResult(new Program(parseResult).PrintDocuments(path));
304344

305345
public static string ToHex(byte[] bytes)
306346
=> BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
@@ -324,8 +364,8 @@ private int PrintDocuments(string path)
324364
return _errorReported ? 1 : 0;
325365
}
326366

327-
private static Task<int> PrintUrlsAsync(string path,IConsole console)
328-
=> Task.FromResult(new Program(console).PrintUrls(path));
367+
private static Task<int> PrintUrlsAsync(string path, ParseResult parseResult)
368+
=> Task.FromResult(new Program(parseResult).PrintUrls(path));
329369

330370
private int PrintUrls(string path)
331371
{

src/dotnet-sourcelink/dotnet-sourcelink.csproj

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44
<TargetFrameworks>$(NetCurrent)</TargetFrameworks>
55
<!-- Allow tool to roll forward to a newer major version. -->
66
<RollForward>Major</RollForward>
7-
<!-- Will be removed by https://github.com/dotnet/sourcelink/issues/1028 -->
8-
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
97

108
<!-- NuGet -->
119
<IsPackable>true</IsPackable>
1210
<PackAsTool>True</PackAsTool>
1311
<ToolCommandName>sourcelink</ToolCommandName>
1412
<Description>Command line tool for SourceLink testing.</Description>
1513
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
16-
<PackAsToolShimRuntimeIdentifiers>win-x64;win-x86;osx-x64</PackAsToolShimRuntimeIdentifiers>
14+
<PackAsToolShimRuntimeIdentifiers Condition="'$(DotNetBuildFromSource)' != 'true'">win-x64;win-x86;osx-x64</PackAsToolShimRuntimeIdentifiers>
1715
</PropertyGroup>
1816

1917
<ItemGroup>

0 commit comments

Comments
 (0)