protected async Task <TfsVCPorcelainCommandResult> TryRunPorcelainCommandAsync(FormatFlags formatFlags, params string[] args) { // Validation. ArgUtil.NotNull(args, nameof(args)); ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext)); // Invoke tf. var processInvoker = new ProcessInvoker(ExecutionContext); var result = new TfsVCPorcelainCommandResult(); var outputLock = new object(); processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { lock (outputLock) { ExecutionContext.Debug(e.Data); result.Output.Add(e.Data); } }; processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { lock (outputLock) { ExecutionContext.Debug(e.Data); result.Output.Add(e.Data); } }; string arguments = FormatArguments(formatFlags, args); ExecutionContext.Debug($@"tf {arguments}"); // TODO: Test whether the output encoding needs to be specified on a non-Latin OS. try { await processInvoker.ExecuteAsync( workingDirectory : SourcesDirectory, fileName : "tf", arguments : arguments, environment : AdditionalEnvironmentVariables, requireExitCodeZero : true, outputEncoding : OutputEncoding, cancellationToken : CancellationToken); } catch (ProcessExitCodeException ex) { result.Exception = ex; } return(result); }
public async Task <ITfsVCWorkspace[]> WorkspacesAsync(bool matchWorkspaceNameOnAnyComputer = false) { // Build the args. var args = new List <string>(); args.Add("workspaces"); if (matchWorkspaceNameOnAnyComputer) { args.Add(WorkspaceName); args.Add($"-computer:*"); } args.Add("-format:xml"); // Run the command. TfsVCPorcelainCommandResult result = await TryRunPorcelainCommandAsync(FormatFlags.None, RetriesOnFailure, args.ToArray()); ArgUtil.NotNull(result, nameof(result)); if (result.Exception != null) { // Check if the workspace name was specified and the command returned exit code 1. if (matchWorkspaceNameOnAnyComputer && result.Exception.ExitCode == 1) { // Ignore the error. This condition can indicate the workspace was not found. return(new ITfsVCWorkspace[0]); } // Dump the output and throw. result.Output?.ForEach(x => ExecutionContext.Output(x ?? string.Empty)); throw result.Exception; } // Note, string.join gracefully handles a null element within the IEnumerable<string>. string output = string.Join(Environment.NewLine, result.Output ?? new List <string>()) ?? string.Empty; string xml = ExtractXml(output); // Deserialize the XML. var serializer = new XmlSerializer(typeof(TeeWorkspaces)); using (var reader = new StringReader(xml)) { return((serializer.Deserialize(reader) as TeeWorkspaces) ?.Workspaces ?.Cast <ITfsVCWorkspace>() .ToArray()); } }
protected async Task <string> RunPorcelainCommandAsync(FormatFlags formatFlags, params string[] args) { // Run the command. TfsVCPorcelainCommandResult result = await TryRunPorcelainCommandAsync(formatFlags, args); ArgUtil.NotNull(result, nameof(result)); if (result.Exception != null) { // The command failed. Dump the output and throw. result.Output?.ForEach(x => ExecutionContext.Output(x ?? string.Empty)); throw result.Exception; } // Return the output. // Note, string.join gracefully handles a null element within the IEnumerable<string>. return(string.Join(Environment.NewLine, result.Output ?? new List <string>())); }
protected async Task <TfsVCPorcelainCommandResult> TryRunPorcelainCommandAsync(FormatFlags formatFlags, int retriesOnFailure, params string[] args) { // Validation. ArgUtil.NotNull(args, nameof(args)); ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext)); // Invoke tf. using (var processInvoker = new ProcessInvoker(ExecutionContext)) { var result = new TfsVCPorcelainCommandResult(); var outputLock = new object(); processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { lock (outputLock) { ExecutionContext.Debug(e.Data); result.Output.Add(e.Data); } }; processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { lock (outputLock) { ExecutionContext.Debug(e.Data); result.Output.Add(e.Data); } }; string arguments = FormatArguments(formatFlags, args); ExecutionContext.Debug($@"tf {arguments}"); // TODO: Test whether the output encoding needs to be specified on a non-Latin OS. try { for (int attempt = 0; attempt < retriesOnFailure; attempt++) { int exitCode = await processInvoker.ExecuteAsync( workingDirectory : SourcesDirectory, fileName : "tf", arguments : arguments, environment : AdditionalEnvironmentVariables, requireExitCodeZero : false, outputEncoding : OutputEncoding, cancellationToken : CancellationToken); if (exitCode == 0) { return(result); } int sleep = Math.Min(200 * (int)Math.Pow(5, attempt), 30000); ExecutionContext.Output($"Sleeping for {sleep} ms"); Thread.Sleep(sleep); // Use attempt+2 since we're using 0 based indexing and we're displaying this for the next attempt. ExecutionContext.Output($@"Retrying. Attempt ${attempt + 2}/${retriesOnFailure}"); } // Perform one last try and fail on non-zero exit code await processInvoker.ExecuteAsync( workingDirectory : SourcesDirectory, fileName : "tf", arguments : arguments, environment : AdditionalEnvironmentVariables, requireExitCodeZero : true, outputEncoding : OutputEncoding, cancellationToken : CancellationToken); } catch (ProcessExitCodeException ex) { result.Exception = ex; } return(result); } }