ReactOS Sin Custom Revision Action (Revision ISO Creator) Added: trunk/cis/ReactOS.CustomRevisionAction/ Added: trunk/cis/ReactOS.CustomRevisionAction/App.config Added: trunk/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs Added: trunk/cis/ReactOS.CustomRevisionAction/Default.build Added: trunk/cis/ReactOS.CustomRevisionAction/Main.cs Added: trunk/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs _____
Added: trunk/cis/ReactOS.CustomRevisionAction/App.config --- trunk/cis/ReactOS.CustomRevisionAction/App.config 2005-11-23 17:25:55 UTC (rev 19490) +++ trunk/cis/ReactOS.CustomRevisionAction/App.config 2005-11-23 17:31:28 UTC (rev 19491) @@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <appSettings> + <add key="publishPath" value="c:\iso" /> + <add key="smtpServer" value="localhost" /> + <add key="errorEmail" value="mailbox@somewhere-on-the-net" /> + <add key="makeParameters" value="" /> + <add key="fastDisk" value="" /> + </appSettings> +</configuration> _____
Added: trunk/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs --- trunk/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs 2005-11-23 17:25:55 UTC (rev 19490) +++ trunk/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs 2005-11-23 17:31:28 UTC (rev 19491) @@ -0,0 +1,14 @@
+using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("ReactOS Sin Custom Revision Action")] +[assembly: AssemblyDescription("ReactOS Sin Custom Revision Action")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ReactOS Project")] +[assembly: AssemblyProduct("React Operating System")] +[assembly: AssemblyCopyright("Copyright 2005 ReactOS Project")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] _____
Added: trunk/cis/ReactOS.CustomRevisionAction/Default.build --- trunk/cis/ReactOS.CustomRevisionAction/Default.build 2005-11-23 17:25:55 UTC (rev 19490) +++ trunk/cis/ReactOS.CustomRevisionAction/Default.build 2005-11-23 17:31:28 UTC (rev 19491) @@ -0,0 +1,20 @@
+<?xml version="1.0"?> +<project name="ReactOS.CustomRevisionAction" default="build"> + + <property name="output.dir" value="bin" /> + + <target name="build" description="Build component"> + <mkdir dir="${output.dir}" /> + <csc target="exe" + output="${output.dir}\ReactOS.CustomRevisionAction.exe" + optimize="true" + debug="true" + doc="${output.dir}\ReactOS.CustomRevisionAction.xml" + warninglevel="0"> + <sources> + <include name="*.cs" /> + </sources> + </csc> + </target> + +</project> _____
Added: trunk/cis/ReactOS.CustomRevisionAction/Main.cs --- trunk/cis/ReactOS.CustomRevisionAction/Main.cs 2005-11-23 17:25:55 UTC (rev 19490) +++ trunk/cis/ReactOS.CustomRevisionAction/Main.cs 2005-11-23 17:31:28 UTC (rev 19491) @@ -0,0 +1,254 @@
+using System; +using System.IO; +using System.Diagnostics; +using System.Configuration; +using System.Web.Mail; + +namespace ReactOS.CustomRevisionAction +{ + public class MainClass + { + /// <summary> + /// Path to store published binaries at. + /// </summary> + private static string publishPath; + + /// <summary> + /// Run the application. + /// </summary> + /// <param name="script">Script to run.</param> + /// <param name="args">Arguments to pass to script.</param> + /// <param name="workingDirectory">Working directory.</param> + /// <param name="standardOutput">Receives standard output.</param> + /// <param name="standardError">Receives standard error.</param> + /// <returns> + /// Exit code. + /// </returns> + 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; + } + + /// <summary> + /// Retrieve value of configuration from configuration file. + /// </summary> + /// <param name="name">Name of configuration option.</param> + /// <param name="defaultValue"> + /// Default value to be returned if the option does not exist. + /// </param> + /// <returns> + /// Value of configuration option or null if the option does not + /// exist and no default value is provided. + /// </returns> + private static string GetConfigurationOption(string name, + string defaultValue) + { + if (ConfigurationSettings.AppSettings[name] != null) + return ConfigurationSettings.AppSettings[name]; + else + return defaultValue; + } + + /// <summary> + /// Send an email. + /// </summary> + /// <param name="subject">Subject of the email.</param> + /// <param name="body">Content of the email.</param> + 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 += "<br>"; + mm.BodyFormat = MailFormat.Html; + SmtpMail.SmtpServer = smtpServer; + SmtpMail.Send(mm); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + } + } + + /// <summary> + /// Fail with an error message. + /// </summary> + /// <param name="revision">Repository revision.</param> + /// <param name="text">Error message.</param> + private static void Fail(int revision, + string text) + { + Console.WriteLine(text); + Console.Error.WriteLine(text); + SendErrorMail(String.Format("[{0}] ReactOS Publish Error", revision), text); + } + + /// <summary> + /// Fail with an error message. + /// </summary> + /// <param name="text">Error message.</param> + private static void Fail(string text) + { + Console.WriteLine(text); + Console.Error.WriteLine(text); + SendErrorMail("ReactOS Publish Error", text); + } + + /// <summary> + /// Generate filename of distribution. + /// </summary> + /// <param name="branch">Branch.</param> + /// <param name="revision">Revision.</param> + private static string GetDistributionFilename(string branch, + int revision) + { + return String.Format("ReactOS-{0}-r{1}.iso", + branch, + revision); + } + + /// <summary> + /// Copy ISO to the destination. + /// </summary> + /// <param name="sourceFilename">Name of source ISO file to copy.</param> + /// <param name="branch">Branch.</param> + /// <param name="revision">Revision.</param> + /// <remarks> + /// Structure is <branch>\ReactOS-<branch>-r<revision>.iso. + /// </remarks> + private static void CopyISOToDestination(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); + } + + /// <summary> + /// Publish a revision of ReactOS. + /// </summary> + /// <param name="text">Error message.</param> + 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; + } + + /// <summary> + /// Program entry point. + /// </summary> + /// <param name="args">Arguments from command line.</param> + /// <remarks> + /// If exit code is 0, then the commit was processed successfully. + /// If exit code is 1, then the commit was not processed successfully. + /// </remarks> + 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; + } + } + } +} _____
Added: trunk/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs --- trunk/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs 2005-11-23 17:25:55 UTC (rev 19490) +++ trunk/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs 2005-11-23 17:31:28 UTC (rev 19491) @@ -0,0 +1,145 @@
+/* + * When using the ProcessStartInfo.RedirectStandardXxx properties there is a chance of + * the parent and child process blocking due to a race condition. This class handles the + * problem. + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/h tml/frlrfsystemdiagnosticsprocessstartinfoclassredirectstandardoutputtop ic.asp + */ +using System; +using System.IO; +using System.Threading; +using System.Diagnostics; + +namespace ReactOS.CustomRevisionAction +{ + /// <summary> + /// Process that redirects standard output and standard error streams. + /// </summary> + public class RedirectableProcess + { + /// <summary> + /// Process. + /// </summary> + private Process process; + + /// <summary> + /// Redirected standard error stream. + /// </summary> + private string processError; + + /// <summary> + /// Redirected standard output stream. + /// </summary> + private string processOutput; + + /// <summary> + /// Exit code. + /// </summary> + private int exitCode; + + /// <summary> + /// Redirected standard error stream. + /// </summary> + public string ProcessError + { + get + { + return processError; + } + } + + /// <summary> + /// Redirected standard output stream. + /// </summary> + public string ProcessOutput + { + get + { + return processOutput; + } + } + + /// <summary> + /// Exit code. + /// </summary> + public int ExitCode + { + get + { + return exitCode; + } + } + + /// <summary> + /// Run an excutable and redirect standard error and/or standard output safely. + /// </summary> + public RedirectableProcess(ProcessStartInfo processStartInfo) + { + Run(processStartInfo, null); + } + + /// <summary> + /// Run an excutable and redirect standard error and/or standard output safely. + /// </summary> + public RedirectableProcess(ProcessStartInfo processStartInfo, string input) + { + Run(processStartInfo, input); + } + + private void Run(ProcessStartInfo processStartInfo, string input) + { + process = new Process(); + process.StartInfo = processStartInfo; + process.Start(); + if (processStartInfo.RedirectStandardInput && input != null) + { + process.StandardInput.AutoFlush = true; + process.StandardInput.WriteLine(input); + } + Thread readStandardError = null; + if (processStartInfo.RedirectStandardError) + { + readStandardError = new Thread(new ThreadStart(ReadStandardError)); + readStandardError.Start(); + } + Thread readStandardOutput = null; + if (processStartInfo.RedirectStandardOutput) + { + readStandardOutput = new Thread(new ThreadStart(ReadStandardOutput)); + readStandardOutput.Start(); + } + if (processStartInfo.RedirectStandardError) + { + readStandardError.Join(); + } + if (processStartInfo.RedirectStandardOutput) + { + readStandardOutput.Join(); + } + process.WaitForExit(); + exitCode = process.ExitCode; + process = null; + } + + /// <summary> + /// Read standard error thread entry-point. + /// </summary> + private void ReadStandardError() + { + if (process != null) + { + processError = process.StandardError.ReadToEnd(); + } + } + + /// <summary> + /// Read standard output thread entry-point. + /// </summary> + private void ReadStandardOutput() + { + if (process != null) + { + processOutput = process.StandardOutput.ReadToEnd(); + } + } + } +}