file-folder-helper/ADO2024/PI4/Helper-2024-12-17.cs
2025-05-17 07:51:53 -07:00

308 lines
14 KiB
C#

using DiscUtils.Iso9660;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241217
{
private record SecureShell(
);
private record ServerMessageBlock(string Path,
bool Required);
private record Target(SecureShell? SecureShell,
ServerMessageBlock? ServerMessageBlock);
private record File(long LastWriteTicks,
long Length,
string RelativePath);
private record Record(string Directory,
Job Job,
string Path);
private record Job(string? AlternatePath,
string Directory,
string Extension,
File[] Files,
int FilesCount,
double FilesTotalLength,
int Keep,
Target[] Targets);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Job))]
private partial class JobSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(File[]))]
private partial class FilesSourceGenerationContext : JsonSerializerContext
{
}
internal static void Backup(ILogger<Worker> logger, List<string> args)
{
Job jobNew;
string path;
string? json;
bool areTheyTheSame;
string directoryName;
ReadOnlyCollection<File> files;
string searchPattern = args[2];
string[] ignoreFileNames = args[3].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
char destinationDriveLetter = args[4].Split(':')[0][0];
logger.LogInformation("Searching <{sourceDirectory}> with search pattern {searchPattern}", args[0], searchPattern);
if (Debugger.IsAttached)
Verify(searchPattern, ignoreFileNames);
IEnumerable<Record> records = GetRecords(sourceDirectory, searchPattern);
foreach (Record record in records)
{
if (record.Job is null || string.IsNullOrEmpty(record.Job.Extension))
continue;
logger.LogInformation("Searching <{directory}>", record.Directory);
files = GetFiles(searchPattern, ignoreFileNames, record);
jobNew = GetJob(searchPattern, ignoreFileNames, record, files);
json = JsonSerializer.Serialize(jobNew, JobSourceGenerationContext.Default.Job);
areTheyTheSame = GetAreTheyTheSame(logger, searchPattern, ignoreFileNames, record, jobNew);
if (areTheyTheSame)
{
WriteAllText(record.Path, json);
continue;
}
directoryName = Path.GetFileName(record.Directory);
path = Path.Combine(record.Directory, $"{directoryName}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}{record.Job.Extension}");
logger.LogInformation("Writing <{directory}> extension", record.Directory);
WritePassedExtension(destinationDriveLetter, record, files, path);
WriteAllText(record.Path, json);
}
if (Debugger.IsAttached && records.Count() == 0)
{
files = GetFiles(sourceDirectory, searchPattern, ignoreFileNames);
json = JsonSerializer.Serialize(files.ToArray(), FilesSourceGenerationContext.Default.FileArray);
WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json);
}
}
private static void Verify(string searchPattern, string[] ignoreFileNames)
{
List<Target> targets = [
new(new SecureShell(), null),
new(null, new ServerMessageBlock("\\\\mesfs.infineon.com\\EC_APC\\DEV", true))
];
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
string path = Path.Combine(directory, "verify.json");
ReadOnlyCollection<File> files = GetFiles(directory, searchPattern, ignoreFileNames);
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, files);
double filesTotalLength = collection.Select(l => l.Length).Sum();
Job job = new(AlternatePath: "C:/Users/phares",
Directory: directory,
Extension: ".iso",
Files: collection.ToArray(),
FilesCount: files.Count,
FilesTotalLength: filesTotalLength,
Keep: 3,
Targets: targets.ToArray());
string json = JsonSerializer.Serialize(job, JobSourceGenerationContext.Default.Job);
WriteAllText(path, json);
}
private static ReadOnlyCollection<File> GetFilteredFiles(string searchPattern, string[] ignoreFileNames, ReadOnlyCollection<File> files)
{
List<File> results = [];
string fileName;
foreach (File file in files)
{
if (file.RelativePath == searchPattern)
continue;
fileName = Path.GetFileName(file.RelativePath);
if (fileName == searchPattern)
throw new Exception("Found nested file!");
if (ignoreFileNames.Any(l => l == fileName))
continue;
if (file.Length == 0)
continue;
results.Add(file);
}
return results.AsReadOnly();
}
private static IEnumerable<Record> GetRecords(string directory, string searchPattern)
{
Job? job;
string json;
Record record;
string fileName;
string directoryName;
IEnumerable<string> files = Directory.EnumerateFiles(directory, searchPattern, new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
foreach (string file in files)
{
fileName = Path.GetFileName(file);
directoryName = Path.GetDirectoryName(file) ?? throw new Exception();
if (!fileName.StartsWith('.'))
{
System.IO.File.Move(file, Path.Combine(directoryName, $".{fileName}"));
continue;
}
json = System.IO.File.ReadAllText(file);
if (string.IsNullOrEmpty(json) || json is "{}" or "[]")
job = null;
else
job = JsonSerializer.Deserialize(json, JobSourceGenerationContext.Default.Job);
job ??= new(AlternatePath: null,
Directory: directory,
Extension: ".iso",
Files: [],
FilesCount: 0,
FilesTotalLength: 0,
Keep: 3,
Targets: []);
record = new(Directory: directoryName, Job: job, Path: file);
yield return record;
}
}
private static ReadOnlyCollection<File> GetFiles(string directory, string searchPattern, string[] ignoreFileNames)
{
List<File> results = [];
File file;
string relativePath;
string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
FileInfo[] fileInfoCollection = files.Select(l => new FileInfo(l)).ToArray();
foreach (FileInfo fileInfo in fileInfoCollection)
{
if (fileInfo.Name == searchPattern)
continue;
if (ignoreFileNames.Any(l => l == fileInfo.Name))
continue;
if (!string.IsNullOrEmpty(fileInfo.LinkTarget))
continue;
relativePath = Path.GetRelativePath(directory, fileInfo.FullName).Replace(';', '_');
if (relativePath.StartsWith(".."))
relativePath = relativePath[3..];
file = new(LastWriteTicks: fileInfo.LastWriteTime.Ticks, Length: fileInfo.Length, RelativePath: relativePath);
results.Add(file);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<File> GetFiles(string searchPattern, string[] ignoreFileNames, Record record) =>
GetFiles(record.Directory, searchPattern, ignoreFileNames);
private static Job GetJob(string searchPattern, string[] ignoreFileNames, Record record, ReadOnlyCollection<File> files)
{
Job result;
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, files);
double filesTotalLengthNew = collection.Select(l => l.Length).Sum();
result = new(AlternatePath: record.Job.AlternatePath,
Directory: record.Directory,
Extension: record.Job.Extension,
Files: collection.ToArray(),
FilesCount: collection.Count,
FilesTotalLength: filesTotalLengthNew,
Keep: record.Job.Keep,
Targets: record.Job.Targets);
return result;
}
private static bool GetAreTheyTheSame(ILogger<Worker> logger, string searchPattern, string[] ignoreFileNames, Record record, Job jobNew)
{
bool result;
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, record.Job.Files.AsReadOnly());
int filesCountOld = collection.Count;
int filesCountNew = jobNew.Files.Length;
if (filesCountNew != filesCountOld)
{
result = false;
logger.LogInformation("<{directory}> file count has changed {filesCountNew} != {filesCountOld}", record.Directory, filesCountNew, filesCountOld);
}
else
{
double filesTotalLengthOld = collection.Select(l => l.Length).Sum();
double filesTotalLengthNew = jobNew.Files.Select(l => l.Length).Sum();
if (filesTotalLengthNew != filesTotalLengthOld)
{
result = false;
logger.LogInformation("<{directory}> file length has changed {filesTotalLengthNew} != {filesTotalLengthOld}", record.Directory, filesTotalLengthNew, filesTotalLengthOld);
}
else
{
string jsonNew = JsonSerializer.Serialize(jobNew.Files, FilesSourceGenerationContext.Default.FileArray);
string jsonOld = JsonSerializer.Serialize(collection.ToArray(), FilesSourceGenerationContext.Default.FileArray);
if (jsonNew == jsonOld)
result = true;
else
{
result = false;
WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "old.json"), jsonOld);
WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "new.json"), jsonNew);
logger.LogInformation("<{directory}> file serialized are different {filesTotalLengthNew} != {filesTotalLengthOld}", record.Directory, filesTotalLengthNew, filesTotalLengthOld);
}
}
}
return result;
}
private static void WriteAllText(string path, string text)
{
string check = !System.IO.File.Exists(path) ? string.Empty : System.IO.File.ReadAllText(path);
if (check != text)
System.IO.File.WriteAllText(path, text);
}
private static void WritePassedExtension(char destinationDriveLetter, Record record, ReadOnlyCollection<File> files, string path)
{
string directoryName = Path.GetFileName(record.Directory);
if (record.Job.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase))
WriteISO(destinationDriveLetter, record, files, path, directoryName);
else if (record.Job.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase))
WriteZIP(destinationDriveLetter, record, files, path);
else
throw new NotImplementedException();
}
private static void WriteISO(char destinationDriveLetter, Record record, ReadOnlyCollection<File> files, string path, string directoryName)
{
string checkFile = $"{destinationDriveLetter}{path[1..]}";
string checkDirectory = Path.GetDirectoryName(checkFile) ?? throw new Exception();
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
CDBuilder builder = new() { UseJoliet = true, VolumeIdentifier = directoryName.Length < 25 ? directoryName : directoryName[..25] };
foreach (File file in files)
_ = builder.AddFile(file.RelativePath, Path.Combine(record.Directory, file.RelativePath));
builder.Build(checkFile);
}
private static void WriteZIP(char destinationDriveLetter, Record record, ReadOnlyCollection<File> files, string path)
{
string checkFile = $"{destinationDriveLetter}{path[1..]}";
string checkDirectory = Path.GetDirectoryName(checkFile) ?? throw new Exception();
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
using ZipArchive zip = ZipFile.Open(checkFile, ZipArchiveMode.Create);
string directoryEntry;
List<string> directoryEntries = [];
foreach (File file in files)
{
directoryEntry = Path.GetDirectoryName(file.RelativePath) ?? throw new Exception();
if (!directoryEntries.Contains(directoryEntry))
continue;
directoryEntries.Add(directoryEntry);
_ = zip.CreateEntry(file.RelativePath);
}
foreach (File file in files)
_ = zip.CreateEntryFromFile(Path.Combine(record.Directory, file.RelativePath), file.RelativePath);
}
}