Files
UnrealEngine/Engine/Extras/P4VUtils/Commands/DescribeCommand.cs
2025-05-18 13:04:45 +08:00

179 lines
5.4 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using EpicGames.Perforce;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace P4VUtils.Commands
{
[Command("describe", CommandCategory.Root, 0)]
class DescribeCommand : Command
{
public override string Description => "Prints out a description of the given changelist";
public override CustomToolInfo CustomTool => new CustomToolInfo("Describe", "%c") { ShowConsole = true, RefreshUI = false};
public override async Task<int> Execute(string[] Args, IReadOnlyDictionary<string, string> ConfigValues, ILogger Logger)
{
int Change;
if (Args.Length < 2)
{
Logger.LogError("Missing changelist number");
return 1;
}
else if (!int.TryParse(Args[1], out Change))
{
Logger.LogError("'{Argument}' is not a numbered changelist", Args[1]);
return 1;
}
using PerforceConnection Perforce = new PerforceConnection(null, null, Logger);
DescribeRecord DescribeRecord = await Perforce.DescribeAsync(Change, CancellationToken.None);
foreach (string Line in DescribeRecord.Description.Split('\n'))
{
Console.WriteLine(Line);
}
return 0;
}
}
[Command("describedirectory", CommandCategory.Toolbox, 8)]
class DescribeDirectoryCommand : Command
{
public override string Description => "Prints out a description of last 30 days of CL's in the given directory to txt and csv";
public override CustomToolInfo CustomTool => new CustomToolInfo("Describe Directory", "%D") { ShowConsole = true, RefreshUI = false };
private int MaxDaysHistory = 30;
private int MaxChangeListCount = 10000;
public override async Task<int> Execute(string[] Args, IReadOnlyDictionary<string, string> ConfigValues, ILogger Logger)
{
if (Args.Length < 2)
{
Logger.LogError("Missing directory path(s)");
return 1;
}
List<string> DescribePathsStrings = new List<string>();
// %D is 'selected files or folders', ensure we only use folders
for (int i = 1; i < Args.Length; i++)
{
Logger.LogInformation("params {Idx} - {Arg}", i, Args[i]);
if (Args[i].EndsWith("...", StringComparison.Ordinal))
{
DescribePathsStrings.Add(Args[i]);
}
else
{
Logger.LogError(" ignoring individial selected file {File}", Args[i]);
}
}
using PerforceConnection Perforce = new PerforceConnection(null, null, Logger);
// grab a list of the changes with a large max count
List<ChangesRecord> ChangeRecords = await Perforce.GetChangesAsync(ChangesOptions.LongOutput, MaxChangeListCount, ChangeStatus.Submitted, DescribePathsStrings, CancellationToken.None);
if (ChangeRecords.Count == 0)
{
Logger.LogWarning("Could not find any p4 change record for {File}", DescribePathsStrings);
return 1;
}
StringBuilder CSVFormat = new StringBuilder();
StringBuilder PlainTextFormat = new StringBuilder();
DateTime TimeNow = DateTime.Now;
// iterate over the changes, filter to within the last x days
foreach (ChangesRecord ChangeRecord in ChangeRecords)
{
TimeSpan Delta = TimeNow - ChangeRecord.Time;
if (Delta.TotalDays < MaxDaysHistory)
{
// grab the description string - split it and remove lines that start with # or trim lines that have # within them.
string[] Lines = ChangeRecord.Description.Split('\n');
StringBuilder FilteredOutput = new StringBuilder();
foreach(string line in Lines)
{
string[] subLines = line.Split('#');
if (subLines.Length > 0)
{
FilteredOutput.Append(subLines[0]);
}
}
// now remove all newlines for the plain text output, truncate to 100 chars for text size reasons
string Single = FilteredOutput.ToString().
Replace("\n", "", System.StringComparison.Ordinal).
Replace("\r", "", System.StringComparison.Ordinal);
string FinalSingleString;
if (Single.Length > 100)
{
FinalSingleString = Single.Substring(0, 100);
FinalSingleString += "(...)";
}
else
{
FinalSingleString = Single;
}
if(FinalSingleString.Length < 2)
{
FinalSingleString = Lines[0];
}
PlainTextFormat.AppendLine("{0} - {1} - {2} - {3}", ChangeRecord.Number, ChangeRecord.User, FinalSingleString, ChangeRecord.Time.ToString());
string CSVString = FilteredOutput.ToString().Replace("\"", "\"\"",StringComparison.Ordinal);
if (CSVString.Length < 2)
{
CSVString = Lines[0];
}
CSVFormat.AppendLine("{0},{1},\"{2}\",{3}", ChangeRecord.Number, ChangeRecord.User, CSVString, ChangeRecord.Time.ToString());
}
}
string GuidString = Guid.NewGuid().ToString();
string CsvPath = Path.Combine(Path.GetTempPath(), GuidString + ".csv");
string PlainTextPath = Path.Combine(Path.GetTempPath(), GuidString + ".txt");
using (StreamWriter Writer = new StreamWriter(PlainTextPath))
{
await Writer.WriteLineAsync(PlainTextFormat.ToString());
}
using (StreamWriter Writer = new StreamWriter(CsvPath))
{
await Writer.WriteLineAsync(CSVFormat.ToString());
}
ProcessUtils.OpenInNewProcess(PlainTextPath);
Logger.LogInformation("Complete");
Logger.LogInformation("PlainText: {PlainText}", PlainTextPath);
Logger.LogInformation("CSV Form : {CsvPath}", CsvPath);
return 0;
}
}
}