Quick fixes: Make `dotnet-getdocument` more reliable (#8716)

- use `WaitAny(...)` in inside man
- call `Process.WaitForExit()` twice
- `Flush()` all output `FileStream`s before disposal
- catch `UnauthorizedAccessException` when calling `File.Delete(...)` in case file's in use
- add `/nr:false` to `dotnet msbuild` command line
This commit is contained in:
Doug Bunting 2018-11-12 11:26:02 -08:00 committed by GitHub
parent 195a22d92c
commit 8308d94e39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 16 deletions

View File

@ -109,8 +109,8 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
return false; return false;
} }
var finished = Task.WhenAny(resultTask, Task.Delay(TimeSpan.FromMinutes(1))); var finishedIndex = Task.WaitAny(resultTask, Task.Delay(TimeSpan.FromMinutes(1)));
if (!ReferenceEquals(resultTask, finished)) if (finishedIndex != 0)
{ {
Reporter.WriteWarning(Resources.FormatMethodTimedOut(methodName, serviceName, 1)); Reporter.WriteWarning(Resources.FormatMethodTimedOut(methodName, serviceName, 1));
return false; return false;
@ -121,6 +121,8 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
using (var outStream = File.Create(context.OutputPath)) using (var outStream = File.Create(context.OutputPath))
{ {
stream.CopyTo(outStream); stream.CopyTo(outStream);
outStream.Flush();
} }
} }

View File

@ -177,6 +177,8 @@ namespace Microsoft.Extensions.ApiDescription.Tasks
using (var outStream = File.Create(destinationPath)) using (var outStream = File.Create(destinationPath))
{ {
await responseStream.CopyToAsync(outStream); await responseStream.CopyToAsync(outStream);
await outStream.FlushAsync();
} }
} }
} }

View File

@ -186,8 +186,22 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
{ {
if (cleanupExecutable && !string.IsNullOrEmpty(executable)) if (cleanupExecutable && !string.IsNullOrEmpty(executable))
{ {
File.Delete(executable); // Ignore errors about in-use files. Should still be marked for delete after process cleanup.
File.Delete(executable + ".config"); try
{
File.Delete(executable);
}
catch (UnauthorizedAccessException)
{
}
try
{
File.Delete(executable + ".config");
}
catch (UnauthorizedAccessException)
{
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
@ -31,20 +32,32 @@ namespace Microsoft.Extensions.ApiDescription.Tool
startInfo.WorkingDirectory = workingDirectory; startInfo.WorkingDirectory = workingDirectory;
} }
var process = Process.Start(startInfo); using (var process = Process.Start(startInfo))
if (interceptOutput)
{ {
string line; if (interceptOutput)
while ((line = process.StandardOutput.ReadLine()) != null)
{ {
Reporter.WriteVerbose(line); string line;
while ((line = process.StandardOutput.ReadLine()) != null)
{
Reporter.WriteVerbose(line);
}
} }
// Follow precedent set in Razor integration tests and ensure process events and output are complete.
// https://github.com/aspnet/Razor/blob/d719920fdcc7d1db3a6f74cd5404d66fa098f057/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildProcessManager.cs#L91-L102
// Timeout is double how long the inside man waits for the IDocumentProcessor to wrap up.
if (!process.WaitForExit((int)(TimeSpan.FromMinutes(2).TotalMilliseconds)))
{
process.Kill();
// Should be unreachable in almost every case.
throw new TimeoutException($"Process {executable} timed out after 2 minutes.");
}
process.WaitForExit();
return process.ExitCode;
} }
process.WaitForExit();
return process.ExitCode;
} }
private static string ToArguments(IReadOnlyList<string> args) private static string ToArguments(IReadOnlyList<string> args)

View File

@ -89,6 +89,8 @@ namespace Microsoft.Extensions.ApiDescription.Tool
// NB: Copy always in case it changes // NB: Copy always in case it changes
Reporter.WriteVerbose(Resources.FormatWritingFile(targetsPath)); Reporter.WriteVerbose(Resources.FormatWritingFile(targetsPath));
input.CopyTo(output); input.CopyTo(output);
output.Flush();
} }
} }
@ -102,6 +104,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool
"/target:WriteServiceProjectReferenceMetadata", "/target:WriteServiceProjectReferenceMetadata",
"/verbosity:quiet", "/verbosity:quiet",
"/nologo", "/nologo",
"/nodeReuse:false",
$"/property:ServiceProjectReferenceMetadataPath={metadataPath}", $"/property:ServiceProjectReferenceMetadataPath={metadataPath}",
projectFile, projectFile,
}; };
@ -134,8 +137,22 @@ namespace Microsoft.Extensions.ApiDescription.Tool
} }
finally finally
{ {
File.Delete(metadataPath); // Ignore errors about in-use files. Should still be marked for delete after process cleanup.
File.Delete(targetsPath); try
{
File.Delete(metadataPath);
}
catch (UnauthorizedAccessException)
{
}
try
{
File.Delete(targetsPath);
}
catch (UnauthorizedAccessException)
{
}
} }
var project = new Project var project = new Project