diff --git a/cis/ReactOS.CustomRevisionAction/App.config b/cis/ReactOS.CustomRevisionAction/App.config new file mode 100644 index 00000000000..b697b94986f --- /dev/null +++ b/cis/ReactOS.CustomRevisionAction/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs b/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs new file mode 100644 index 00000000000..fa009e21841 --- /dev/null +++ b/cis/ReactOS.CustomRevisionAction/AssemblyInfo.cs @@ -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("")] diff --git a/cis/ReactOS.CustomRevisionAction/Default.build b/cis/ReactOS.CustomRevisionAction/Default.build new file mode 100644 index 00000000000..8bbc2ef20f7 --- /dev/null +++ b/cis/ReactOS.CustomRevisionAction/Default.build @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/cis/ReactOS.CustomRevisionAction/Main.cs b/cis/ReactOS.CustomRevisionAction/Main.cs new file mode 100644 index 00000000000..3535671c0a5 --- /dev/null +++ b/cis/ReactOS.CustomRevisionAction/Main.cs @@ -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 + { + /// + /// Path to store published binaries at. + /// + private static string publishPath; + + /// + /// 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); + } + + /// + /// 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) + { + 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); + } + + /// + /// 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; + } + } + } +} diff --git a/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs b/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs new file mode 100644 index 00000000000..e0acb7fac51 --- /dev/null +++ b/cis/ReactOS.CustomRevisionAction/RedirectableProcess.cs @@ -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/html/frlrfsystemdiagnosticsprocessstartinfoclassredirectstandardoutputtopic.asp + */ +using System; +using System.IO; +using System.Threading; +using System.Diagnostics; + +namespace ReactOS.CustomRevisionAction +{ + /// + /// Process that redirects standard output and standard error streams. + /// + public class RedirectableProcess + { + /// + /// Process. + /// + private Process process; + + /// + /// Redirected standard error stream. + /// + private string processError; + + /// + /// Redirected standard output stream. + /// + private string processOutput; + + /// + /// Exit code. + /// + private int exitCode; + + /// + /// Redirected standard error stream. + /// + public string ProcessError + { + get + { + return processError; + } + } + + /// + /// Redirected standard output stream. + /// + public string ProcessOutput + { + get + { + return processOutput; + } + } + + /// + /// Exit code. + /// + public int ExitCode + { + get + { + return exitCode; + } + } + + /// + /// Run an excutable and redirect standard error and/or standard output safely. + /// + public RedirectableProcess(ProcessStartInfo processStartInfo) + { + Run(processStartInfo, null); + } + + /// + /// Run an excutable and redirect standard error and/or standard output safely. + /// + 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; + } + + /// + /// Read standard error thread entry-point. + /// + private void ReadStandardError() + { + if (process != null) + { + processError = process.StandardError.ReadToEnd(); + } + } + + /// + /// Read standard output thread entry-point. + /// + private void ReadStandardOutput() + { + if (process != null) + { + processOutput = process.StandardOutput.ReadToEnd(); + } + } + } +}