using GB5DailyBuild; using Microsoft.Extensions.Configuration; using System.IO.Compression; using System.Linq; // Repo root = five levels up from bin\Debug\net10.0\ (Tools\GB5DailyBuild\bin\Debug\net10.0) var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "..")); var buildFileDir = Path.Combine(repoRoot, "GB5BuildFile"); var config = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddJsonFile("appsettings.json", optional: false) .Build(); var deployFlags = config.GetSection("Deploy").Get() ?? new DeployFlags(); var windowsSettings = config.GetSection("WindowsDeploy").Get() ?? new WindowsDeploySettings(); var linuxSettings = config.GetSection("LinuxDeploy").Get() ?? new LinuxDeploySettings(); var dockerSettings = config.GetSection("DockerDeploy").Get() ?? new DockerDeploySettings(); Console.WriteLine("================================================================"); Console.WriteLine(" GB5 DAILY BUILD TOOL - local equivalent of the CI/CD pipeline"); Console.WriteLine($" Repo root: {repoRoot}"); Console.WriteLine("================================================================"); // ------------------------------------------------------------------ // Stage 1: GB5Shared // ------------------------------------------------------------------ Console.WriteLine(); Console.WriteLine("=== Stage 1: GB5Shared ==="); var sharedDir = Path.Combine(repoRoot, "GB5Shared"); if (Run("dotnet", "restore GB5Shared.sln", sharedDir) != 0) return Fail("GB5Shared restore"); if (Run("dotnet", "build GB5Shared.sln --configuration Debug", sharedDir) != 0) return Fail("GB5Shared build"); // ------------------------------------------------------------------ // Stage 2-4: GB5Build -> FrameworkSL -> Hosts (publish, same order as .gitlab-ci.yml) // ------------------------------------------------------------------ var stages = new[] { new BuildStage("GB5Build", "GB5Solution/GB5Build/GB5Build/GB5Build.csproj", "GB5Build"), new BuildStage("FrameworkSL", "GB5Framework/FrameworkSL/FrameworkSL.csproj", "FrameworkSL"), new BuildStage("PlatformHost", "GB5Solution/Hosts/Platform/PlatformHost/PlatformHost.csproj", "PlatformHost"), new BuildStage("BusinessHost", "GB5Solution/Hosts/Business/BusinessHost/BusinessHost.csproj", "BusinessHost"), new BuildStage("EngagementHost", "GB5Solution/Hosts/Engagement/EngagementHost/EngagementHost.csproj", "EngagementHost"), new BuildStage("HRFinanceHost", "GB5Solution/Hosts/HRFinance/HRFinanceHost/HRFinanceHost.csproj", "HRFinanceHost"), }; foreach (var stage in stages) { Console.WriteLine(); Console.WriteLine($"=== Stage: {stage.Name} ==="); var projectFullPath = Path.Combine(repoRoot, stage.ProjectPath.Replace('/', Path.DirectorySeparatorChar)); var outputDir = Path.Combine(buildFileDir, stage.OutputName); if (Run("dotnet", $"restore \"{projectFullPath}\"", repoRoot) != 0) return Fail($"{stage.Name} restore"); if (Run("dotnet", $"publish \"{projectFullPath}\" --configuration Release --output \"{outputDir}\"", repoRoot) != 0) return Fail($"{stage.Name} publish"); } // ------------------------------------------------------------------ // Stage 5: Merge // ------------------------------------------------------------------ Console.WriteLine(); Console.WriteLine("=== Stage: Merge ==="); var version = args.Length > 0 ? args[0] : null; var mergeName = string.IsNullOrWhiteSpace(version) ? "merged" : $"GB5 Build - {version}"; var mergeDir = Path.Combine(buildFileDir, mergeName); Directory.CreateDirectory(mergeDir); foreach (var stage in stages) { var source = Path.Combine(buildFileDir, stage.OutputName); var dest = Path.Combine(mergeDir, stage.OutputName); if (Directory.Exists(source)) { CopyDirectory(source, dest); } } Console.WriteLine($"Merge output folder: {mergeDir}"); // ------------------------------------------------------------------ // Zip the merged output - this is what gets pushed to Windows/Linux // ------------------------------------------------------------------ var zipPath = Path.Combine(buildFileDir, $"{mergeName}.zip"); Console.WriteLine($"Zipping {mergeDir} -> {zipPath}"); if (File.Exists(zipPath)) { File.Delete(zipPath); } ZipFile.CreateFromDirectory(mergeDir, zipPath); Console.WriteLine(); Console.WriteLine("================================================================"); Console.WriteLine(" BUILD COMPLETE"); Console.WriteLine($" Output: {mergeDir}"); Console.WriteLine($" Zip: {zipPath}"); Console.WriteLine("================================================================"); // ------------------------------------------------------------------ // Stage 6: Deploy (Windows / Linux / Docker) - each individually // skippable via appsettings.json -> Deploy.Windows/Linux/Docker = "Y" or "N" // ------------------------------------------------------------------ var deployVersion = string.IsNullOrWhiteSpace(version) ? "dev" : version; Console.WriteLine(); Console.WriteLine("=== Stage: Deploy ==="); DeployResult? windowsResult = null; DeployResult? linuxResult = null; DeployResult? dockerResult = null; if (IsEnabled(deployFlags.Windows)) { windowsResult = DeployRunner.DeployWindows(zipPath, mergeName, windowsSettings); } else { Console.WriteLine("Windows deploy skipped (Deploy:Windows = N in appsettings.json)."); } if (IsEnabled(deployFlags.Linux)) { linuxResult = DeployRunner.DeployLinux(zipPath, mergeName, linuxSettings); } else { Console.WriteLine("Linux deploy skipped (Deploy:Linux = N in appsettings.json)."); } if (IsEnabled(deployFlags.Docker)) { dockerResult = DeployRunner.DeployDocker(repoRoot, deployVersion, dockerSettings); } else { Console.WriteLine("Docker deploy skipped (Deploy:Docker = N in appsettings.json)."); } // ------------------------------------------------------------------ // Keep only the 7 most recent builds locally (both the merge folders // and their zips) so GB5BuildFile doesn't grow unbounded over time. // ------------------------------------------------------------------ PruneLocalBuilds(buildFileDir); // ------------------------------------------------------------------ // Final Summary - one clear block, easy to copy/paste and read back // ------------------------------------------------------------------ Console.WriteLine(); Console.WriteLine("================================================================"); Console.WriteLine(" FINAL SUMMARY"); Console.WriteLine("================================================================"); Console.WriteLine($" Build (Shared, GB5Build, FrameworkSL, 4 Hosts) : SUCCESS"); Console.WriteLine($" Merge + Zip : SUCCESS ({zipPath})"); Console.WriteLine($" Windows Deploy : {Describe(deployFlags.Windows, windowsResult)}"); Console.WriteLine($" Linux Deploy : {Describe(deployFlags.Linux, linuxResult)}"); Console.WriteLine($" Docker Deploy : {Describe(deployFlags.Docker, dockerResult)}"); Console.WriteLine("================================================================"); return 0; static string Describe(string flag, DeployResult? result) { if (!IsEnabled(flag)) return "SKIPPED (disabled in appsettings.json)"; if (result is null) return "NOT RUN"; return result.Success ? $"SUCCESS ({result.Message})" : $"FAILED - {result.Message}"; } static bool IsEnabled(string flag) => string.Equals(flag, "Y", StringComparison.OrdinalIgnoreCase); static int Run(string fileName, string arguments, string workingDirectory) => ProcessRunner.Run(fileName, arguments, workingDirectory); static int Fail(string stageName) { Console.WriteLine(); Console.WriteLine("================================================================"); Console.WriteLine($" BUILD FAILED at: {stageName}"); Console.WriteLine("================================================================"); return 1; } static void CopyDirectory(string sourceDir, string destDir) { Directory.CreateDirectory(destDir); foreach (var file in Directory.GetFiles(sourceDir)) { File.Copy(file, Path.Combine(destDir, Path.GetFileName(file)), overwrite: true); } foreach (var subDir in Directory.GetDirectories(sourceDir)) { CopyDirectory(subDir, Path.Combine(destDir, Path.GetFileName(subDir))); } } static void PruneLocalBuilds(string buildFileDir) { const int MaxRetainedBuilds = 7; try { var oldZips = new DirectoryInfo(buildFileDir) .GetFiles("GB5 Build - *.zip") .OrderByDescending(f => f.LastWriteTimeUtc) .Skip(MaxRetainedBuilds); foreach (var file in oldZips) { Console.WriteLine($"Pruning old local zip: {file.FullName}"); file.Delete(); } var oldFolders = new DirectoryInfo(buildFileDir) .GetDirectories("GB5 Build - *") .OrderByDescending(d => d.LastWriteTimeUtc) .Skip(MaxRetainedBuilds); foreach (var dir in oldFolders) { Console.WriteLine($"Pruning old local build folder: {dir.FullName}"); dir.Delete(recursive: true); } } catch (Exception ex) { Console.WriteLine($"Warning: could not prune local builds in {buildFileDir}: {ex.Message}"); } }