--- /dev/null
+using System;\r
+using System.IO;\r
+using System.Diagnostics;\r
+using System.Configuration;\r
+using System.Web.Mail;\r
+\r
+namespace ReactOS.CustomRevisionAction\r
+{\r
+ public class MainClass\r
+ {\r
+ /// <summary>\r
+ /// Path to store published binaries at.\r
+ /// </summary>\r
+ private static string publishPath;\r
+\r
+ /// <summary>\r
+ /// Run the application.\r
+ /// </summary>\r
+ /// <param name="script">Script to run.</param>\r
+ /// <param name="args">Arguments to pass to script.</param>\r
+ /// <param name="workingDirectory">Working directory.</param>\r
+ /// <param name="standardOutput">Receives standard output.</param>\r
+ /// <param name="standardError">Receives standard error.</param>\r
+ /// <returns>\r
+ /// Exit code.\r
+ /// </returns>\r
+ private static int RunScript(string script,\r
+ string args,\r
+ string workingDirectory,\r
+ out string standardOutput,\r
+ out string standardError)\r
+ {\r
+ ProcessStartInfo scriptProcessStartInfo = new ProcessStartInfo(script,\r
+ args);\r
+ scriptProcessStartInfo.CreateNoWindow = true;\r
+ /*\r
+ * All standard streams must be redirected.\r
+ * Otherwise DuplicateHandle() will fail.\r
+ */\r
+ scriptProcessStartInfo.RedirectStandardInput = true;\r
+ scriptProcessStartInfo.RedirectStandardError = true;\r
+ scriptProcessStartInfo.RedirectStandardOutput = true;\r
+ scriptProcessStartInfo.UseShellExecute = false;\r
+ scriptProcessStartInfo.WorkingDirectory = workingDirectory;\r
+ RedirectableProcess redirectableProcess = new RedirectableProcess(scriptProcessStartInfo);\r
+ standardOutput = redirectableProcess.ProcessOutput;\r
+ standardError = redirectableProcess.ProcessError;\r
+ return redirectableProcess.ExitCode;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Retrieve value of configuration from configuration file.\r
+ /// </summary>\r
+ /// <param name="name">Name of configuration option.</param>\r
+ /// <param name="defaultValue">\r
+ /// Default value to be returned if the option does not exist.\r
+ /// </param>\r
+ /// <returns>\r
+ /// Value of configuration option or null if the option does not\r
+ /// exist and no default value is provided.\r
+ /// </returns>\r
+ private static string GetConfigurationOption(string name,\r
+ string defaultValue)\r
+ {\r
+ if (ConfigurationSettings.AppSettings[name] != null)\r
+ return ConfigurationSettings.AppSettings[name];\r
+ else\r
+ return defaultValue;\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Send an email.\r
+ /// </summary>\r
+ /// <param name="subject">Subject of the email.</param>\r
+ /// <param name="body">Content of the email.</param>\r
+ private static void SendErrorMail(string subject, string body)\r
+ {\r
+ try\r
+ {\r
+ string smtpServer = GetConfigurationOption("smtpServer", "localhost");\r
+ string toEmail = GetConfigurationOption("errorEmail", null);\r
+ if (toEmail == null)\r
+ return;\r
+ string fromEmail = GetConfigurationOption("fromEmail", null);\r
+ if (fromEmail == null)\r
+ fromEmail = toEmail;\r
+ MailMessage mm = new MailMessage();\r
+ mm.Priority = MailPriority.Normal;\r
+ mm.From = toEmail;\r
+ mm.To = toEmail;\r
+ mm.Subject = subject;\r
+ mm.Body += body;\r
+ mm.Body += "<br>";\r
+ mm.BodyFormat = MailFormat.Html;\r
+ SmtpMail.SmtpServer = smtpServer;\r
+ SmtpMail.Send(mm);\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ Console.Error.WriteLine(ex.Message);\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Fail with an error message.\r
+ /// </summary>\r
+ /// <param name="revision">Repository revision.</param>\r
+ /// <param name="text">Error message.</param>\r
+ private static void Fail(int revision,\r
+ string text)\r
+ {\r
+ Console.WriteLine(text);\r
+ Console.Error.WriteLine(text);\r
+ SendErrorMail(String.Format("[{0}] ReactOS Publish Error", revision), text);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Fail with an error message.\r
+ /// </summary>\r
+ /// <param name="text">Error message.</param>\r
+ private static void Fail(string text)\r
+ {\r
+ Console.WriteLine(text);\r
+ Console.Error.WriteLine(text);\r
+ SendErrorMail("ReactOS Publish Error", text);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Generate filename of distribution.\r
+ /// </summary>\r
+ /// <param name="branch">Branch.</param>\r
+ /// <param name="revision">Revision.</param>\r
+ private static string GetDistributionFilename(string branch,\r
+ int revision)\r
+ {\r
+ return String.Format("ReactOS-{0}-r{1}.iso",\r
+ branch,\r
+ revision);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Copy ISO to the destination.\r
+ /// </summary>\r
+ /// <param name="sourceFilename">Name of source ISO file to copy.</param>\r
+ /// <param name="branch">Branch.</param>\r
+ /// <param name="revision">Revision.</param>\r
+ /// <remarks>\r
+ /// Structure is <branch>\ReactOS-<branch>-r<revision>.iso.\r
+ /// </remarks>\r
+ private static void CopyISOToDestination(string sourceFilename,\r
+ string branch,\r
+ int revision)\r
+ {\r
+ string distributionFilename = GetDistributionFilename(branch,\r
+ revision);\r
+ string destinationDirectory = Path.Combine(publishPath,\r
+ branch);\r
+ string destinationFilename = Path.Combine(destinationDirectory,\r
+ distributionFilename);\r
+ if (!Directory.Exists(destinationDirectory))\r
+ Directory.CreateDirectory(destinationDirectory);\r
+ File.Copy(sourceFilename,\r
+ destinationFilename);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Publish a revision of ReactOS.\r
+ /// </summary>\r
+ /// <param name="text">Error message.</param>\r
+ private static int Publish(string branch,\r
+ int revision,\r
+ string workingDirectory)\r
+ {\r
+ string make = "mingw32-make";\r
+ string makeParameters = GetConfigurationOption("makeParameters", "");\r
+ string reactosDirectory = Path.Combine(workingDirectory,\r
+ "reactos");\r
+ Console.WriteLine(String.Format("ReactOS directory is {0}",\r
+ reactosDirectory));\r
+ string standardOutput;\r
+ string standardError;\r
+ int exitCode = RunScript(make,\r
+ makeParameters + " bootcd",\r
+ reactosDirectory,\r
+ out standardOutput,\r
+ out standardError);\r
+ if (exitCode != 0)\r
+ {\r
+ Fail(revision,\r
+ String.Format("make bootcd failed: (error: {0}) {1}",\r
+ standardError,\r
+ standardOutput));\r
+ return exitCode;\r
+ }\r
+\r
+ string sourceFilename = Path.Combine(reactosDirectory,\r
+ "ReactOS.iso");\r
+ if (File.Exists(sourceFilename))\r
+ CopyISOToDestination(sourceFilename,\r
+ branch,\r
+ revision);\r
+ else\r
+ {\r
+ Fail(revision,\r
+ "make bootcd produced no ReactOS.iso");\r
+ return exitCode;\r
+ }\r
+\r
+ return exitCode;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Program entry point.\r
+ /// </summary>\r
+ /// <param name="args">Arguments from command line.</param>\r
+ /// <remarks>\r
+ /// If exit code is 0, then the commit was processed successfully.\r
+ /// If exit code is 1, then the commit was not processed successfully.\r
+ /// </remarks>\r
+ public static void Main(string[] args)\r
+ {\r
+ try\r
+ {\r
+ System.Environment.ExitCode = 1;\r
+\r
+ publishPath = ConfigurationSettings.AppSettings["publishPath"];\r
+ if (publishPath == null)\r
+ {\r
+ Fail("PublishPath option not set.");\r
+ return;\r
+ }\r
+\r
+ if (args.Length < 3)\r
+ {\r
+ Fail("Usage: ReactOS.CustomRevisionAction action branch revision");\r
+ return;\r
+ }\r
+\r
+ string action = args[0]; /* bootcd */\r
+ string branch = args[1];\r
+ int revision = Int32.Parse(args[2]);\r
+ \r
+ System.Environment.ExitCode = Publish(branch,\r
+ revision,\r
+ System.Environment.CurrentDirectory);\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ Fail(String.Format("Exception: {0}", ex));\r
+ System.Environment.ExitCode = 1;\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * When using the ProcessStartInfo.RedirectStandardXxx properties there is a chance of\r
+ * the parent and child process blocking due to a race condition. This class handles the\r
+ * problem.\r
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticsprocessstartinfoclassredirectstandardoutputtopic.asp\r
+ */\r
+using System;\r
+using System.IO;\r
+using System.Threading;\r
+using System.Diagnostics;\r
+\r
+namespace ReactOS.CustomRevisionAction\r
+{\r
+ /// <summary>\r
+ /// Process that redirects standard output and standard error streams.\r
+ /// </summary>\r
+ public class RedirectableProcess\r
+ {\r
+ /// <summary>\r
+ /// Process.\r
+ /// </summary>\r
+ private Process process;\r
+\r
+ /// <summary>\r
+ /// Redirected standard error stream.\r
+ /// </summary>\r
+ private string processError;\r
+\r
+ /// <summary>\r
+ /// Redirected standard output stream.\r
+ /// </summary>\r
+ private string processOutput;\r
+\r
+ /// <summary>\r
+ /// Exit code.\r
+ /// </summary>\r
+ private int exitCode;\r
+\r
+ /// <summary>\r
+ /// Redirected standard error stream.\r
+ /// </summary>\r
+ public string ProcessError\r
+ {\r
+ get\r
+ {\r
+ return processError;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Redirected standard output stream.\r
+ /// </summary>\r
+ public string ProcessOutput\r
+ {\r
+ get\r
+ {\r
+ return processOutput;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Exit code.\r
+ /// </summary>\r
+ public int ExitCode\r
+ {\r
+ get\r
+ {\r
+ return exitCode;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Run an excutable and redirect standard error and/or standard output safely.\r
+ /// </summary>\r
+ public RedirectableProcess(ProcessStartInfo processStartInfo)\r
+ {\r
+ Run(processStartInfo, null);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Run an excutable and redirect standard error and/or standard output safely.\r
+ /// </summary>\r
+ public RedirectableProcess(ProcessStartInfo processStartInfo, string input)\r
+ {\r
+ Run(processStartInfo, input);\r
+ }\r
+ \r
+ private void Run(ProcessStartInfo processStartInfo, string input)\r
+ {\r
+ process = new Process();\r
+ process.StartInfo = processStartInfo;\r
+ process.Start();\r
+ if (processStartInfo.RedirectStandardInput && input != null)\r
+ {\r
+ process.StandardInput.AutoFlush = true;\r
+ process.StandardInput.WriteLine(input);\r
+ }\r
+ Thread readStandardError = null;\r
+ if (processStartInfo.RedirectStandardError)\r
+ {\r
+ readStandardError = new Thread(new ThreadStart(ReadStandardError));\r
+ readStandardError.Start();\r
+ }\r
+ Thread readStandardOutput = null;\r
+ if (processStartInfo.RedirectStandardOutput)\r
+ {\r
+ readStandardOutput = new Thread(new ThreadStart(ReadStandardOutput));\r
+ readStandardOutput.Start();\r
+ }\r
+ if (processStartInfo.RedirectStandardError)\r
+ {\r
+ readStandardError.Join();\r
+ }\r
+ if (processStartInfo.RedirectStandardOutput)\r
+ {\r
+ readStandardOutput.Join();\r
+ }\r
+ process.WaitForExit();\r
+ exitCode = process.ExitCode;\r
+ process = null;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Read standard error thread entry-point.\r
+ /// </summary> \r
+ private void ReadStandardError()\r
+ {\r
+ if (process != null)\r
+ {\r
+ processError = process.StandardError.ReadToEnd();\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Read standard output thread entry-point.\r
+ /// </summary> \r
+ private void ReadStandardOutput()\r
+ {\r
+ if (process != null)\r
+ {\r
+ processOutput = process.StandardOutput.ReadToEnd();\r
+ }\r
+ }\r
+ }\r
+}\r