using System.Text.Json; using SwOfflineApply; // Offline Rollout Package (ORP) local apply tool — tracker §53, Phase 4. // // Usage: // SwOfflineApply // // Run entirely by the on-prem/BYOC operator, on their own machine, against their own local SQL // Server. Nothing here calls back to any GB5-hosted service — the private key file never leaves // this machine, and the only network access this tool makes is the local SQL Server connection // the operator themselves supplies. if (args.Length != 3) { Console.Error.WriteLine("Usage: SwOfflineApply "); return 1; } var packagePath = args[0]; var privateKeyPath = args[1]; var connectionString = args[2]; if (!File.Exists(packagePath)) { Console.Error.WriteLine($"Package file not found: {packagePath}"); return 1; } if (!File.Exists(privateKeyPath)) { Console.Error.WriteLine($"Private key file not found: {privateKeyPath}"); return 1; } try { var encryptedPackage = await File.ReadAllBytesAsync(packagePath); var privateKeyPem = await File.ReadAllTextAsync(privateKeyPath); Console.WriteLine("Decrypting package..."); var plaintext = OfflinePackageDecryptor.Decrypt(encryptedPackage, privateKeyPem); var bundle = JsonSerializer.Deserialize(plaintext) ?? throw new InvalidOperationException("Package decrypted but content could not be parsed."); Console.WriteLine($"Package: {bundle.PackageCode} (PackageId={bundle.PackageId}, {bundle.Scripts.Count} script(s))"); var logFilePath = Path.Combine( Path.GetDirectoryName(Path.GetFullPath(packagePath)) ?? ".", $"{Path.GetFileNameWithoutExtension(packagePath)}.applylog.jsonl"); var results = await LocalScriptApplier.ApplyAsync(bundle, connectionString, logFilePath, CancellationToken.None); int succeeded = results.Count(r => r.Success); int failed = results.Count - succeeded; Console.WriteLine($"Applied {succeeded}/{results.Count} script(s) successfully."); if (failed > 0) { Console.WriteLine($"{failed} script(s) failed:"); foreach (var r in results.Where(r => !r.Success)) Console.WriteLine($" - {r.ObjectName}: {r.Error}"); } Console.WriteLine($"Local execution log written to: {logFilePath}"); return failed > 0 ? 2 : 0; } catch (Exception ex) { Console.Error.WriteLine($"Failed: {ex.Message}"); return 1; }