- Fix crashes in calls to ScmAssignNewTag.
[reactos.git] / cis / ReactOS.CustomRevisionAction / Main.cs
1 using System;
2 using System.IO;
3 using System.Diagnostics;
4 using System.Configuration;
5 using System.Web.Mail;
6
7 namespace ReactOS.CustomRevisionAction
8 {
9 public class MainClass
10 {
11 /// <summary>
12 /// Path to store published binaries at.
13 /// </summary>
14 private static string publishPath;
15
16 /// <summary>
17 /// Whether or not to publish ISOs to a remote destination via FTP.
18 /// </summary>
19 private static bool PublishToRemoteFtpLocation
20 {
21 get
22 {
23 return publishPath.StartsWith("ftp://");
24 }
25 }
26
27 /// <summary>
28 /// Run the application.
29 /// </summary>
30 /// <param name="script">Script to run.</param>
31 /// <param name="args">Arguments to pass to script.</param>
32 /// <param name="workingDirectory">Working directory.</param>
33 /// <param name="standardOutput">Receives standard output.</param>
34 /// <param name="standardError">Receives standard error.</param>
35 /// <returns>
36 /// Exit code.
37 /// </returns>
38 private static int RunScript(string script,
39 string args,
40 string workingDirectory,
41 out string standardOutput,
42 out string standardError)
43 {
44 ProcessStartInfo scriptProcessStartInfo = new ProcessStartInfo(script,
45 args);
46 scriptProcessStartInfo.CreateNoWindow = true;
47 /*
48 * All standard streams must be redirected.
49 * Otherwise DuplicateHandle() will fail.
50 */
51 scriptProcessStartInfo.RedirectStandardInput = true;
52 scriptProcessStartInfo.RedirectStandardError = true;
53 scriptProcessStartInfo.RedirectStandardOutput = true;
54 scriptProcessStartInfo.UseShellExecute = false;
55 scriptProcessStartInfo.WorkingDirectory = workingDirectory;
56 RedirectableProcess redirectableProcess = new RedirectableProcess(scriptProcessStartInfo);
57 standardOutput = redirectableProcess.ProcessOutput;
58 standardError = redirectableProcess.ProcessError;
59 return redirectableProcess.ExitCode;
60 }
61
62 /// <summary>
63 /// Retrieve value of configuration from configuration file.
64 /// </summary>
65 /// <param name="name">Name of configuration option.</param>
66 /// <param name="defaultValue">
67 /// Default value to be returned if the option does not exist.
68 /// </param>
69 /// <returns>
70 /// Value of configuration option or null if the option does not
71 /// exist and no default value is provided.
72 /// </returns>
73 private static string GetConfigurationOption(string name,
74 string defaultValue)
75 {
76 if (ConfigurationSettings.AppSettings[name] != null)
77 return ConfigurationSettings.AppSettings[name];
78 else
79 return defaultValue;
80 }
81
82 /// <summary>
83 /// Send an email.
84 /// </summary>
85 /// <param name="subject">Subject of the email.</param>
86 /// <param name="body">Content of the email.</param>
87 private static void SendErrorMail(string subject, string body)
88 {
89 try
90 {
91 string smtpServer = GetConfigurationOption("smtpServer", "localhost");
92 string toEmail = GetConfigurationOption("errorEmail", null);
93 if (toEmail == null)
94 return;
95 string fromEmail = GetConfigurationOption("fromEmail", null);
96 if (fromEmail == null)
97 fromEmail = toEmail;
98 MailMessage mm = new MailMessage();
99 mm.Priority = MailPriority.Normal;
100 mm.From = toEmail;
101 mm.To = toEmail;
102 mm.Subject = subject;
103 mm.Body += body;
104 mm.Body += "<br>";
105 mm.BodyFormat = MailFormat.Html;
106 SmtpMail.SmtpServer = smtpServer;
107 SmtpMail.Send(mm);
108 }
109 catch (Exception ex)
110 {
111 Console.Error.WriteLine(ex.Message);
112 }
113 }
114
115 /// <summary>
116 /// Fail with an error message.
117 /// </summary>
118 /// <param name="revision">Repository revision.</param>
119 /// <param name="text">Error message.</param>
120 private static void Fail(int revision,
121 string text)
122 {
123 Console.WriteLine(text);
124 Console.Error.WriteLine(text);
125 SendErrorMail(String.Format("[{0}] ReactOS Publish Error", revision), text);
126 }
127
128 /// <summary>
129 /// Fail with an error message.
130 /// </summary>
131 /// <param name="text">Error message.</param>
132 private static void Fail(string text)
133 {
134 Console.WriteLine(text);
135 Console.Error.WriteLine(text);
136 SendErrorMail("ReactOS Publish Error", text);
137 }
138
139 /// <summary>
140 /// Generate filename of distribution.
141 /// </summary>
142 /// <param name="branch">Branch.</param>
143 /// <param name="revision">Revision.</param>
144 private static string GetDistributionFilename(string branch,
145 int revision)
146 {
147 return String.Format("ReactOS-{0}-r{1}.iso",
148 branch,
149 revision);
150 }
151
152 private static void SplitRemotePublishPath(string publishPath,
153 out string server,
154 out string directory)
155 {
156 string searchString = "://";
157 int index = publishPath.IndexOf(searchString);
158 if (index == -1)
159 throw new InvalidOperationException();
160 int endOfProtocolIndex = index + searchString.Length;
161 string withoutProtocol = publishPath.Remove(0, endOfProtocolIndex);
162 index = withoutProtocol.IndexOf("/");
163 if (index == -1)
164 {
165 server = withoutProtocol;
166 directory = "";
167 }
168 else
169 {
170 server = withoutProtocol.Substring(0, index);
171 directory = withoutProtocol.Remove(0, index + 1);
172 }
173 }
174
175 /// <summary>
176 /// Copy ISO to the (remote) destination.
177 /// </summary>
178 /// <param name="sourceFilename">Name of source ISO file to copy.</param>
179 /// <param name="branch">Branch.</param>
180 /// <param name="revision">Revision.</param>
181 /// <remarks>
182 /// Structure is ftp://ftp.server.com/whereever/<branch>/ReactOS-<branch>-r<revision>.iso.
183 /// </remarks>
184 private static void CopyISOToRemoteFtpDestination(string sourceFilename,
185 string branch,
186 int revision)
187 {
188 string distributionFilename = GetDistributionFilename(branch,
189 revision);
190 string destinationFilename = Path.Combine(Path.GetDirectoryName(sourceFilename),
191 distributionFilename);
192 File.Move(sourceFilename, destinationFilename);
193 string server;
194 string directory;
195 SplitRemotePublishPath(publishPath, out server, out directory);
196 FtpClient ftpClient = new FtpClient(server, "anonymous", "sin@svn.reactos.com");
197 ftpClient.Login();
198 if (directory != "")
199 ftpClient.ChangeDir(directory);
200 /* Create destination directory if it does not already exist */
201 if (!ftpClient.DirectoryExists(branch))
202 ftpClient.MakeDir(branch);
203 ftpClient.ChangeDir(branch);
204 ftpClient.Upload(destinationFilename);
205 ftpClient.Close();
206 }
207
208 /// <summary>
209 /// Copy ISO to the (local) destination.
210 /// </summary>
211 /// <param name="sourceFilename">Name of source ISO file to copy.</param>
212 /// <param name="branch">Branch.</param>
213 /// <param name="revision">Revision.</param>
214 /// <remarks>
215 /// Structure is <branch>\ReactOS-<branch>-r<revision>.iso.
216 /// </remarks>
217 private static void CopyISOToLocalDestination(string sourceFilename,
218 string branch,
219 int revision)
220 {
221 string distributionFilename = GetDistributionFilename(branch,
222 revision);
223 string destinationDirectory = Path.Combine(publishPath,
224 branch);
225 string destinationFilename = Path.Combine(destinationDirectory,
226 distributionFilename);
227 if (!Directory.Exists(destinationDirectory))
228 Directory.CreateDirectory(destinationDirectory);
229 File.Copy(sourceFilename,
230 destinationFilename);
231 }
232
233 /// <summary>
234 /// Copy ISO to the destination.
235 /// </summary>
236 /// <param name="sourceFilename">Name of source ISO file to copy.</param>
237 /// <param name="branch">Branch.</param>
238 /// <param name="revision">Revision.</param>
239 /// <remarks>
240 /// Structure is <branch>\ReactOS-<branch>-r<revision>.iso.
241 /// </remarks>
242 private static void CopyISOToDestination(string sourceFilename,
243 string branch,
244 int revision)
245 {
246 if (PublishToRemoteFtpLocation)
247 CopyISOToRemoteFtpDestination(sourceFilename, branch, revision);
248 else
249 CopyISOToLocalDestination(sourceFilename, branch, revision);
250 }
251
252 /// <summary>
253 /// Publish a revision of ReactOS.
254 /// </summary>
255 /// <param name="text">Error message.</param>
256 private static int Publish(string branch,
257 int revision,
258 string workingDirectory)
259 {
260 string make = "mingw32-make";
261 string makeParameters = GetConfigurationOption("makeParameters", "");
262 string reactosDirectory = Path.Combine(workingDirectory,
263 "reactos");
264 Console.WriteLine(String.Format("ReactOS directory is {0}",
265 reactosDirectory));
266 string standardOutput;
267 string standardError;
268 int exitCode = RunScript(make,
269 makeParameters + " bootcd",
270 reactosDirectory,
271 out standardOutput,
272 out standardError);
273 if (exitCode != 0)
274 {
275 Fail(revision,
276 String.Format("make bootcd failed: (error: {0}) {1}",
277 standardError,
278 standardOutput));
279 return exitCode;
280 }
281
282 string sourceFilename = Path.Combine(reactosDirectory,
283 "ReactOS.iso");
284 if (File.Exists(sourceFilename))
285 CopyISOToDestination(sourceFilename,
286 branch,
287 revision);
288 else
289 {
290 Fail(revision,
291 "make bootcd produced no ReactOS.iso");
292 return exitCode;
293 }
294
295 return exitCode;
296 }
297
298 /// <summary>
299 /// Program entry point.
300 /// </summary>
301 /// <param name="args">Arguments from command line.</param>
302 /// <remarks>
303 /// If exit code is 0, then the commit was processed successfully.
304 /// If exit code is 1, then the commit was not processed successfully.
305 /// </remarks>
306 public static void Main(string[] args)
307 {
308 try
309 {
310 System.Environment.ExitCode = 1;
311
312 publishPath = ConfigurationSettings.AppSettings["publishPath"];
313 if (publishPath == null)
314 {
315 Fail("PublishPath option not set.");
316 return;
317 }
318
319 if (args.Length < 3)
320 {
321 Fail("Usage: ReactOS.CustomRevisionAction action branch revision");
322 return;
323 }
324
325 string action = args[0]; /* bootcd */
326 string branch = args[1];
327 int revision = Int32.Parse(args[2]);
328
329 System.Environment.ExitCode = Publish(branch,
330 revision,
331 System.Environment.CurrentDirectory);
332 }
333 catch (Exception ex)
334 {
335 Fail(String.Format("Exception: {0}", ex));
336 System.Environment.ExitCode = 1;
337 }
338 }
339 }
340 }