using System; using System.IO; using System.Diagnostics; using System.Configuration; using System.Web.Mail; namespace ReactOS.CustomRevisionAction { public class MainClass { /// /// Path to store published binaries at. /// private static string publishPath; /// /// Whether or not to publish ISOs to a remote destination via FTP. /// private static bool PublishToRemoteFtpLocation { get { return publishPath.StartsWith("ftp://"); } } /// /// Run the application. /// /// Script to run. /// Arguments to pass to script. /// Working directory. /// Receives standard output. /// Receives standard error. /// /// Exit code. /// private static int RunScript(string script, string args, string workingDirectory, out string standardOutput, out string standardError) { ProcessStartInfo scriptProcessStartInfo = new ProcessStartInfo(script, args); scriptProcessStartInfo.CreateNoWindow = true; /* * All standard streams must be redirected. * Otherwise DuplicateHandle() will fail. */ scriptProcessStartInfo.RedirectStandardInput = true; scriptProcessStartInfo.RedirectStandardError = true; scriptProcessStartInfo.RedirectStandardOutput = true; scriptProcessStartInfo.UseShellExecute = false; scriptProcessStartInfo.WorkingDirectory = workingDirectory; RedirectableProcess redirectableProcess = new RedirectableProcess(scriptProcessStartInfo); standardOutput = redirectableProcess.ProcessOutput; standardError = redirectableProcess.ProcessError; return redirectableProcess.ExitCode; } /// /// Retrieve value of configuration from configuration file. /// /// Name of configuration option. /// /// Default value to be returned if the option does not exist. /// /// /// Value of configuration option or null if the option does not /// exist and no default value is provided. /// private static string GetConfigurationOption(string name, string defaultValue) { if (ConfigurationSettings.AppSettings[name] != null) return ConfigurationSettings.AppSettings[name]; else return defaultValue; } /// /// Send an email. /// /// Subject of the email. /// Content of the email. private static void SendErrorMail(string subject, string body) { try { string smtpServer = GetConfigurationOption("smtpServer", "localhost"); string toEmail = GetConfigurationOption("errorEmail", null); if (toEmail == null) return; string fromEmail = GetConfigurationOption("fromEmail", null); if (fromEmail == null) fromEmail = toEmail; MailMessage mm = new MailMessage(); mm.Priority = MailPriority.Normal; mm.From = toEmail; mm.To = toEmail; mm.Subject = subject; mm.Body += body; mm.Body += "
"; mm.BodyFormat = MailFormat.Html; SmtpMail.SmtpServer = smtpServer; SmtpMail.Send(mm); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } } /// /// Fail with an error message. /// /// Repository revision. /// Error message. private static void Fail(int revision, string text) { Console.WriteLine(text); Console.Error.WriteLine(text); SendErrorMail(String.Format("[{0}] ReactOS Publish Error", revision), text); } /// /// Fail with an error message. /// /// Error message. private static void Fail(string text) { Console.WriteLine(text); Console.Error.WriteLine(text); SendErrorMail("ReactOS Publish Error", text); } /// /// Generate filename of distribution. /// /// Branch. /// Revision. private static string GetDistributionFilename(string branch, int revision) { return String.Format("ReactOS-{0}-r{1}.iso", branch, revision); } private static void SplitRemotePublishPath(string publishPath, out string server, out string directory) { string searchString = "://"; int index = publishPath.IndexOf(searchString); if (index == -1) throw new InvalidOperationException(); int endOfProtocolIndex = index + searchString.Length; string withoutProtocol = publishPath.Remove(0, endOfProtocolIndex); index = withoutProtocol.IndexOf("/"); if (index == -1) { server = withoutProtocol; directory = ""; } else { server = withoutProtocol.Substring(0, index); directory = withoutProtocol.Remove(0, index + 1); } } /// /// Copy ISO to the (remote) destination. /// /// Name of source ISO file to copy. /// Branch. /// Revision. /// /// Structure is ftp://ftp.server.com/whereever//ReactOS--r.iso. /// private static void CopyISOToRemoteFtpDestination(string sourceFilename, string branch, int revision) { string distributionFilename = GetDistributionFilename(branch, revision); string destinationFilename = Path.Combine(Path.GetDirectoryName(sourceFilename), distributionFilename); File.Move(sourceFilename, destinationFilename); string server; string directory; SplitRemotePublishPath(publishPath, out server, out directory); FtpClient ftpClient = new FtpClient(server, "anonymous", "sin@svn.reactos.com"); ftpClient.Login(); if (directory != "") ftpClient.ChangeDir(directory); /* Create destination directory if it does not already exist */ if (!ftpClient.DirectoryExists(branch)) ftpClient.MakeDir(branch); ftpClient.ChangeDir(branch); ftpClient.Upload(destinationFilename); ftpClient.Close(); } /// /// Copy ISO to the (local) destination. /// /// Name of source ISO file to copy. /// Branch. /// Revision. /// /// Structure is \ReactOS--r.iso. /// private static void CopyISOToLocalDestination(string sourceFilename, string branch, int revision) { string distributionFilename = GetDistributionFilename(branch, revision); string destinationDirectory = Path.Combine(publishPath, branch); string destinationFilename = Path.Combine(destinationDirectory, distributionFilename); if (!Directory.Exists(destinationDirectory)) Directory.CreateDirectory(destinationDirectory); File.Copy(sourceFilename, destinationFilename); } /// /// Copy ISO to the destination. /// /// Name of source ISO file to copy. /// Branch. /// Revision. /// /// Structure is \ReactOS--r.iso. /// private static void CopyISOToDestination(string sourceFilename, string branch, int revision) { if (PublishToRemoteFtpLocation) CopyISOToRemoteFtpDestination(sourceFilename, branch, revision); else CopyISOToLocalDestination(sourceFilename, branch, revision); } /// /// Publish a revision of ReactOS. /// /// Error message. private static int Publish(string branch, int revision, string workingDirectory) { string make = "mingw32-make"; string makeParameters = GetConfigurationOption("makeParameters", ""); string reactosDirectory = Path.Combine(workingDirectory, "reactos"); Console.WriteLine(String.Format("ReactOS directory is {0}", reactosDirectory)); string standardOutput; string standardError; int exitCode = RunScript(make, makeParameters + " bootcd", reactosDirectory, out standardOutput, out standardError); if (exitCode != 0) { Fail(revision, String.Format("make bootcd failed: (error: {0}) {1}", standardError, standardOutput)); return exitCode; } string sourceFilename = Path.Combine(reactosDirectory, "ReactOS.iso"); if (File.Exists(sourceFilename)) CopyISOToDestination(sourceFilename, branch, revision); else { Fail(revision, "make bootcd produced no ReactOS.iso"); return exitCode; } return exitCode; } /// /// Program entry point. /// /// Arguments from command line. /// /// If exit code is 0, then the commit was processed successfully. /// If exit code is 1, then the commit was not processed successfully. /// public static void Main(string[] args) { try { System.Environment.ExitCode = 1; publishPath = ConfigurationSettings.AppSettings["publishPath"]; if (publishPath == null) { Fail("PublishPath option not set."); return; } if (args.Length < 3) { Fail("Usage: ReactOS.CustomRevisionAction action branch revision"); return; } string action = args[0]; /* bootcd */ string branch = args[1]; int revision = Int32.Parse(args[2]); System.Environment.ExitCode = Publish(branch, revision, System.Environment.CurrentDirectory); } catch (Exception ex) { Fail(String.Format("Exception: {0}", ex)); System.Environment.ExitCode = 1; } } } }