namespace HSchool.AppHost.Tests; /// /// Same rules as tools/apphost-uptodate.ps1. Keep the two in sync. /// internal static class LaunchBuildStamp { public const string TargetFramework = "net10.0"; public static readonly string[] InputExtensions = [".cs", ".csproj", ".props"]; public static readonly string[] NamedInputs = [ "global.json", "Directory.Build.props", "Directory.Packages.props", ]; public static readonly string[] SkipDirectoryNames = ["bin", "obj", "node_modules"]; public static readonly string[] OutputProjects = ["HSchool.AppHost", "HSchool.Server"]; public static bool NeedsBuild(string repoRoot, string configuration = "Debug") { ArgumentException.ThrowIfNullOrWhiteSpace(repoRoot); ArgumentException.ThrowIfNullOrWhiteSpace(configuration); DateTime? oldestOutput = null; foreach (var project in OutputProjects) { var dll = Path.Combine( repoRoot, "src", project, "bin", configuration, TargetFramework, project + ".dll"); if (!File.Exists(dll)) { return true; } var written = File.GetLastWriteTimeUtc(dll); if (oldestOutput is null || written < oldestOutput.Value) { oldestOutput = written; } } if (oldestOutput is null) { return true; } var cutoff = oldestOutput.Value; foreach (var name in NamedInputs) { var path = Path.Combine(repoRoot, name); if (File.Exists(path) && File.GetLastWriteTimeUtc(path) > cutoff) { return true; } } var src = Path.Combine(repoRoot, "src"); return !Directory.Exists(src) || HasNewerInput(src, cutoff); } private static bool HasNewerInput(string directory, DateTime cutoff) { foreach (var file in Directory.EnumerateFiles(directory)) { if (!IsInputExtension(Path.GetExtension(file))) { continue; } if (File.GetLastWriteTimeUtc(file) > cutoff) { return true; } } foreach (var child in Directory.EnumerateDirectories(directory)) { var leaf = Path.GetFileName(child); if (SkipDirectoryNames.Contains(leaf, StringComparer.OrdinalIgnoreCase)) { continue; } if (HasNewerInput(child, cutoff)) { return true; } } return false; } private static bool IsInputExtension(string extension) => InputExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); }