Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(337)

Side by Side Diff: Tools/BuildRelease/Program.cs

Issue 12767046: Issue 377: New build for releasing a new version (Closed) Base URL: https://google-api-dotnet-client.googlecode.com/hg/
Patch Set: david comments Created 10 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Tools/BuildRelease/BuildRelease.csproj ('k') | Tools/BuildRelease/Properties/AssemblyInfo.cs » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 Copyright 2011 Google Inc
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.IO;
21 using System.Linq;
22 using System.Runtime.InteropServices;
23 using System.Text;
24 using System.Text.RegularExpressions;
25
26 using BuildRelease.Wiki;
27 using Google.Apis.Samples.Helper;
28 using Google.Build.Utils;
29 using Google.Build.Utils.Build;
30 using Google.Build.Utils.Repositories;
31
32 namespace BuildRelease
33 {
34 /// <summary>
35 /// Release Builder.
36 /// Will automatically check out all repositories, run unit tests and create a release.
37 /// Can be re-run if the build should fail - Run this tool again, with the s ame dir argument after fixing the·
38 /// problem.
39 /// </summary>
40 public class Program
41 {
42 #region Command Line Arguments
43 public class CommandLineArguments
44 {
45 [Argument("version", ShortName = "v",
46 Description = "[Required] The version number of this release - < Major.Minor.Build> only.")]
47 public string Version { get; set; }
48
49 [Argument("local", ShortName = "l",
50 Description = "Uses the local repository instead of checking out a new one. Not releasable.")]
51 public bool UseLocalRepository { get; set; }
52
53 [Argument("tag", ShortName = "t",
54 Description = "Use this as the tag suffix for this build.")]
55 public string Tag { get; set; }
56
57 [Argument("dir", ShortName = "d",
58 Description = "Use this as the output directory for this build ( relative to current directory). "
59 "If not specified, dir will be set to current date.")]
60 public string OutputDirectory { get; set; }
61
62 public override string ToString()
63 {
64 return string.Format("CommandLineArguments "
65 "[UseLocalRepository: {0}; Tag: {1}; Dir: {2}; Version: {3}] ",
66 UseLocalRepository, Tag, OutputDirectory, Version);
67 }
68 }
69
70 /// <summary> Command line arguments. </summary>
71 public static CommandLineArguments Arguments { get; private set; }
72
73 #endregion
74
75 /// <summary> The "default" repository. </summary>
76 public static Hg Default { get; private set; }
77
78 /// <summary> The "samples" repository. </summary>
79 public static Hg Samples { get; private set; }
80
81 /// <summary> The "wiki" repository. </summary>
82 public static Hg Wiki { get; private set; }
83
84 /// <summary> The "contrib" repository. </summary>
85 public static Hg Contrib { get; private set; }
86
87 /// <summary> An array of all relevant mercurial repositories. </summary >
88 public static Hg[] AllRepositories { get; private set; }
89
90 /// <summary> The name of the directory containing the working copy. </s ummary>
91 public static string WorkingCopy { get; private set; }
92
93 private static int MajorVersion { get; set; }
94 private static int MinorVersion { get; set; }
95 private static int BuildVersion { get; set; }
96
97 private static IEnumerable<string> _excludeThirdParties = new[]·
98 {·
99 "Moq.dll", "Moq.LICENSE", "nunit.framework.dll", "nunit.framework.LI CENSE"·
100 };
101
102 /// <summary> Points to third party dependencies which will appear in th e bundle. </summary>
103 public static IEnumerable<string> ThirdPartyFiles
104 {
105 get
106 {
107 string dir = Default.Combine("ThirdParty");
108 return from file in Directory.GetFiles(dir, "*")
109 where !_excludeThirdParties.Contains(Path.GetFileName(fil e))
110 select file;
111 }
112 }
113
114 /// <summary>
115 /// The directory containing all the generated services.
116 /// </summary>
117 public static string ServiceDir
118 {
119 get { return Samples.Combine("Services"); }
120 }
121
122 [STAThread]
123 static void Main(string[] args)
124 {
125 // Try to enlarge the window.
126 try
127 {
128 Console.SetWindowSize(Console.LargestWindowWidth * 8 / 9, Consol e.LargestWindowHeight * 8 / 9);
129 }
130 catch (Exception) { }
131
132 CommandLine.DisplayGoogleSampleHeader("Build Release");
133 CommandLine.EnableExceptionHandling();
134
135 // Init arguments
136 if (!InitArguments(args))
137 {
138 CommandLine.PressAnyKeyToExit();
139 return;
140 }
141
142 // Clone repositories
143 CheckoutRepositories();
144
145 // Clean up the default/ repository by removing cache-files
146 CleanDefaultRepository();
147
148 // Check for incoming changes
149 foreach (Hg repository in AllRepositories)
150 {
151 if (repository.HasIncomingChanges)
152 {
153 CommandLine.WriteError(
154 "Repository [{0}] has incoming changes. Run hg pull & up date first!", repository.Name);
155 CommandLine.PressAnyKeyToExit();
156 return;
157 }
158 }
159
160 // Build projects
161 FileVersionInfo apiVersion;
162 Project[] allProjects;
163 Project[] baseLibraries = BuildProjects(out apiVersion, out allProje cts);
164
165 // Create tag
166 string tag = GetTagName(apiVersion);
167
168 // Update samples
169 UpdateSamples(baseLibraries);
170
171 // Update contrib
172 string notes = CreateChangelog(tag);
173 string zipDir;
174 notes = BuildContribRelease(tag, notes, baseLibraries, allProjects, out zipDir);
175
176 // Update wiki
177 UpdateWiki(notes, zipDir);
178
179 CommandLine.WriteLine("{{white}} =================================== ====");
180 CommandLine.WriteResult("Version: ", apiVersion.ProductVersion);
181 CommandLine.WriteLine();
182
183 if (Arguments.UseLocalRepository)
184 {
185 CommandLine.WriteAction("Local build done.");
186 CommandLine.PressAnyKeyToExit();
187 return;
188 }
189
190 // Ask the user whether he wants to continue the release.
191 string res = "no";
192 CommandLine.WriteLine(" {{gray}}In the next step all changes will be committed, tagged and pushed.");
193 CommandLine.WriteLine(" {{gray}}Only continue when you are sure th at you don't have to make "
194 "any new changes.");
195 CommandLine.RequestUserInput("Do you want to continue with the relea se? Type YES.", ref res);
196 CommandLine.WriteLine();
197
198 if (res != "YES")
199 {
200 Console.WriteLine("Done - NO CODE was committed, tagged or pushe d");
201 CommandLine.PressAnyKeyToExit();
202 return;
203 }
204
205 // Commit
206 CommitAndTagRelease(tag);
207
208 // Push
209 PushChanges();
210
211 // Create branch
212 PrintCreateBranch();
213
214 CommandLine.PressAnyKeyToExit();
215 }
216
217 /// <summary> Prints the user orders how to create a branch. </summary>
218 private static void PrintCreateBranch()
219 {
220 if (BuildVersion != 0)
221 {
222 // No need to branch in that case
223 return;
224 }
225
226 // TODO(peleyal): automate this as well
227 CommandLine.WriteAction("You should create a new branch for this rel ease now:");
228 CommandLine.WriteAction("cd " Default.WorkingDirectory);
229 var branchVersion = string.Format("{0}.{1}", MajorVersion, MinorVers ion);
230 CommandLine.WriteAction("hg branch " branchVersion);
231 CommandLine.WriteAction(string.Format("hg commit -m create {0} branc h", branchVersion));
232 CommandLine.WriteAction("hg push --new-branch");
233 }
234
235 /// <summary>·
236 /// Inits the Arguments for this release.·
237 /// Returns <code>true</code> if all arguments are valid.
238 /// </summary>
239 private static bool InitArguments(string[] args)
240 {
241 // TODO(peleyal): Add default value option on Argument (and then add those values to the definition above)
242 Arguments = new CommandLineArguments()
243 {
244 Tag = "beta",
245 UseLocalRepository = false
246 };
247
248 // Parse command line arguments.
249 CommandLineFlags.ParseArguments(Arguments, args);
250 if (string.IsNullOrEmpty(Arguments.Version))
251 {
252 CommandLine.WriteError("Version number can't be null");
253 return false;
254 }
255
256 var match = Regex.Match(Arguments.Version, @"^(\d )\.(\d )\.(\d) $") ;
257 if (!match.Success)
258 {
259 CommandLine.WriteError("Invalid version Number. Version should b e in <Major>.<Minor>.<Build> form.");
260 return false;
261 }
262
263 MajorVersion = int.Parse(match.Groups[1].Value);
264 MinorVersion = int.Parse(match.Groups[2].Value);
265 BuildVersion = int.Parse(match.Groups[3].Value);
266
267 // Create the name of the local working copy.
268 if (String.IsNullOrEmpty(Arguments.OutputDirectory))
269 {
270 Arguments.OutputDirectory = DateTime.UtcNow.ToString("yyyy-MM-dd -hh-mm-ss");
271 }
272
273 string fullPath = Path.GetFullPath(Arguments.OutputDirectory);
274 if (!Directory.Exists(fullPath))
275 {
276 Directory.CreateDirectory(fullPath);
277 }
278
279 Environment.CurrentDirectory = WorkingCopy = fullPath;
280 CommandLine.WriteLine(Arguments.ToString());
281
282 return true;
283 }
284
285 /// <summary> Cleans the default repository from user files ('resharper' , '.user', etc.). </summary>
286 private static void CleanDefaultRepository()
287 {
288 string toDelete = Default.Combine("_ReSharper.GoogleApisClient");
289 if (Directory.Exists(toDelete))
290 {
291 Directory.Delete(toDelete, true);
292 }
293 foreach (string pattern in new[] { "*.dotcover", "*.user", "*.suo" } )
294 {
295 foreach (string file in Directory.GetFiles(Default.WorkingDirect ory, pattern))
296 {
297 File.Delete(file);
298 }
299 }
300 }
301
302 /// <summary> Checks out all repositories. </summary>
303 private static void CheckoutRepositories()
304 {
305 CommandLine.WriteLine("{{white}} =================================== ====");
306 CommandLine.WriteLine("{{white}} Checking out repositories");
307 CommandLine.WriteLine("{{white}} =================================== ====");
308 const string URL = "https://code.google.com/p/google-api-dotnet-clie nt{0}/";
309
310 if (Arguments.UseLocalRepository)
311 {
312 CommandLine.WriteAction("Using local Default repository. This wo n't release!");
313 Default = Hg.Get("../../../../../", string.Format(URL, ""));
314 }
315 else
316 {
317 Default = Hg.Get("default", string.Format(URL, ""));
318 if (BuildVersion != 0)
319 {
320 Default.Update(string.Format("{0}.{1}", MajorVersion, MinorV ersion));
321 }
322 }
323
324 Samples = Hg.Get("samples", string.Format(URL, ".samples"));
325 Wiki = Hg.Get("wiki", string.Format(URL, ".wiki"));
326 Contrib = Hg.Get("contrib", string.Format(URL, ".contrib"));
327 AllRepositories = new[] { Default, Wiki, Contrib, Samples };
328
329 CommandLine.WriteLine();
330 }
331
332 /// <summary>·
333 /// Builds projects.
334 /// In addition runs UnitTest for testing projects.
335 /// </summary>
336 private static Project[] BuildProjects(out FileVersionInfo apiVersion, o ut Project[] allProjects)
337 {
338 CommandLine.WriteLine("{{white}} =================================== ====");
339 CommandLine.WriteLine("{{white}} Building the Projects");
340 CommandLine.WriteLine("{{white}} =================================== ====");
341
342 var projects = new List<Project>();
343 Project baseApi = new Project(Default.Combine("Src", "GoogleApis", " GoogleApis.csproj"));
344
345 Project fullProfileApi = new Project(Default.Combine("Src", "GoogleA pis.FullProfile",
346 "GoogleApis.FullProfile.csproj"));
347
348 Project oauth2 = new Project(Default.Combine("Src", "GoogleApis.Auth entication.OAuth2",
349 "GoogleApis.Authentication.OAuth2.csproj"));
350
351 var releaseProjects = new[] { baseApi, fullProfileApi, oauth2 };
352 projects.AddRange(releaseProjects);
353 projects.Add(new Project(Default.Combine("Src", "GoogleApis.Tests.Ut ility",
354 "GoogleApis.Tests.Utility.csproj")));
355 projects.Add(new Project(Default.Combine("Src", "GoogleApis.Tests", "GoogleApis.Tests.csproj")));
356 projects.Add(new Project(Default.Combine("Src", "GoogleApis.Authenti cation.OAuth2.Tests",
357 "GoogleApis.Authentication.OAuth2.Tests.csproj")));
358
359 foreach (Project proj in projects)
360 {
361 proj.ReplaceVersion(Arguments.Version);
362 proj.RunBuildTask();
363 if (!releaseProjects.Contains(proj)) // If this assembly may con tain tests, then run them.
364 {
365 RunUnitTest(proj.BinaryFile);
366 }
367 }
368
369 CommandLine.WriteLine();
370 apiVersion = FileVersionInfo.GetVersionInfo(baseApi.BinaryFile);
371 allProjects = projects.ToArray();
372 return releaseProjects;
373 }
374
375 /// <summary>
376 /// Runs the unit tester on the specified assembly.
377 /// </summary>
378 /// <remarks>Called from extern assemblies.</remarks>
379 public static void RunUnitTest(string assembly)
380 {
381 // Run the unit tester.
382 try
383 {
384 new Runner(Google.Build.Tester.Program.BinPath, assembly).Run();
385 }
386 catch (ExternalException ex)
387 {
388 CommandLine.WriteError(" {0} tests failed.", ex.ErrorCode < 0 ? "Loading" : ex.ErrorCode.ToString());
389 if (!CommandLine.RequestUserChoice("Do you want to continue anyw ay?"))
390 {
391 throw;
392 }
393 }
394 }
395
396 /// <summary> Gets tag name by the given release version. </summary>
397 private static string GetTagName(FileVersionInfo releaseVersion)
398 {
399 CommandLine.WriteLine("{{white}} =================================== ====");
400 CommandLine.WriteLine("{{white}} Creating Tag for the release");
401 CommandLine.WriteLine("{{white}} =================================== ====");
402
403 if (Arguments.UseLocalRepository)
404 {
405 return "date-version-local";
406 }
407
408 string tag = Arguments.Version;
409 if (!string.IsNullOrEmpty(Arguments.Tag))
410 {
411 tag = "-" Arguments.Tag;
412 }
413
414 CommandLine.WriteResult("Tag", tag);
415 CommandLine.WriteLine();
416 return tag;
417 }
418
419 /// <summary> Returns all changelist notes for this release. </summary>
420 private static string CreateChangelog(string tag)
421 {
422 StringBuilder log = new StringBuilder();
423 log.AppendLine("Google .NET Client Library");
424 log.AppendLine(string.Format("Stable Release '{0}'", tag));
425 log.AppendLine(DateTime.UtcNow.ToLongDateString());
426 log.AppendLine("===========================================");
427
428 log.AppendLine();
429 log.AppendLine("Changes:");
430 foreach (string line in Default.CreateChangelist())
431 {
432 log.AppendLine(" " line);
433 }
434
435 return log.ToString();
436 }
437
438 /// <summary> Udates the samples repository. </summary>
439 private static void UpdateSamples(IEnumerable<Project> releaseProjects)
440 {
441 CommandLine.WriteLine("{{white}} =================================== ====");
442 CommandLine.WriteLine("{{white}} Updating Samples");
443 CommandLine.WriteLine("{{white}} =================================== ====");
444
445 // Update all the dependencies.
446 string libDir = Samples.Combine("Lib");
447 DirUtils.ClearDir(libDir);
448
449 foreach (Project p in releaseProjects)
450 {
451 p.CopyTo(libDir);
452 }
453
454 string thirdpartyDir = Samples.Combine("Lib", "ThirdParty");
455 Directory.CreateDirectory(thirdpartyDir);
456 foreach (string file in ThirdPartyFiles)
457 {
458 DirUtils.CopyFile(file, thirdpartyDir);
459 }
460
461 DirUtils.ClearDir(ServiceDir);
462
463 // Generate all strongly typed services.
464 Console.WriteLine("Update the samples repository Services library \" {0}\" with the new generated services",
465 ServiceDir);
466 Console.WriteLine("Press any key to continue...");
467 Console.ReadKey();
468
469 // Build all the samples projects.
470 CommandLine.WriteAction("Building samples...");
471 foreach (string csproj in
472 Directory.GetFiles(Samples.WorkingDirectory, "*.csproj", SearchO ption.AllDirectories))
473 {
474 Project project = new Project(csproj);
475 project.RunBuildTask();
476 project.Clean();
477 }
478 CommandLine.WriteLine();
479 }
480
481 /// <summary>
482 /// Builds the Releases in the Contrib repository.
483 /// Depends on:·
484 /// - Compiled BaseLibrary
485 /// - Updated Sample repository
486 /// - Existing release tag name
487 /// </summary>
488 /// <returns>Edited changelog.</returns>
489 private static string BuildContribRelease(string tag,
490 string changelog,
491 IEnumerable<Project> baseLibra ry,
492 IEnumerable<Project> allProjec ts,
493 out string zipDir)
494 {
495 CommandLine.WriteLine("{{white}} =================================== ====");
496 CommandLine.WriteLine("{{white}} Building Contrib-Release");
497 CommandLine.WriteLine("{{white}} =================================== ====");
498
499 string releaseDir = Contrib.Combine(tag);
500 string currentDir = Contrib.Combine("Current");
501
502 // Clear existing directories.
503 DirUtils.ClearOrCreateDir(releaseDir);
504 // TODO(peleyal): remove currentDir eventually (after at least one o r two releases)
505 DirUtils.ClearOrCreateDir(currentDir);
506
507 // Create the <current> release
508 string genDir = Path.Combine(currentDir, "Generated");
509 Directory.CreateDirectory(genDir);
510
511 #region Current/Generated/Bin
512 string binDir = Path.Combine(genDir, "Bin");
513 CommandLine.WriteAction("Generating dir: " DirUtils.GetRelativePat h(binDir, Contrib.WorkingDirectory));
514 Directory.CreateDirectory(binDir);
515 {
516 // Copy all third party dlls into this directory.
517 foreach (string file in ThirdPartyFiles)
518 {
519 DirUtils.CopyFile(file, binDir);
520 }
521
522 // Copy all release dlls to this directory.
523 foreach (Project project in baseLibrary)
524 {
525 project.CopyTo(binDir);
526 }
527 }
528 #endregion
529
530 #region Current/ZipFiles
531 string zipFilesDir = Path.Combine(genDir, "ZipFiles");
532 CommandLine.WriteAction("Generating dir: "
533 DirUtils.GetRelativePath(zipFilesDir, Contrib.WorkingDirectory)) ;
534 Directory.CreateDirectory(zipFilesDir);
535 {
536 // clean all projects
537 foreach (Project project in allProjects)
538 {
539 project.Clean();
540 }
541
542 // Source.zip
543 using (Zip zip = new Zip(Path.Combine(zipFilesDir, "Source.zip") ))
544 {
545 zip.AddDirectory(Default.WorkingDirectory, "");
546 zip.RemoveDirectory(".hg");
547 zip.RemoveFile(".hgtags");
548 zip.RemoveFile(".hgignore");
549 }
550
551 // Binary.zip
552 using (Zip zip = new Zip(Path.Combine(zipFilesDir, "Binary.zip") ))
553 {
554 zip.AddDirectory(binDir, "");
555 }
556
557 // Samples.zip
558 using (Zip zip = new Zip(Path.Combine(zipFilesDir, "Samples.zip" )))
559 {
560 zip.AddDirectory(Samples.WorkingDirectory, "");
561 zip.RemoveDirectory(".hg");
562 zip.RemoveFile(".hgtags");
563 zip.RemoveFile(".hgignore");
564 }
565 }
566 #endregion
567
568 #region Current/ReleaseNotes.txt
569 CommandLine.WriteAction("Writing file...");
570 string changelogFile = Path.Combine(currentDir, "ReleaseNotes.txt");
571 using (var writer = new StreamWriter(changelogFile, false))
572 {
573 writer.WriteLine(changelog);
574 }
575 #endregion
576
577 // Open the created changelog.
578 CommandLine.WriteAction("Showing result...");
579 Process.Start(changelogFile).WaitForExit();
580
581 // Copy the content to the <tagname> release directory.
582 DirUtils.CopyFiles(currentDir, releaseDir);
583
584 // Rename the zips in the named release.
585 // Example: Binary.zip -> google-api-dotnet-client-1.0.0-beta.Binary .zip
586 string fileFormat = "google-api-dotnet-client-" tag ".{0}";
587 zipDir = zipFilesDir.Replace(currentDir, releaseDir);
588 foreach (string file in Directory.GetFiles(zipDir, "*.zip"))
589 {
590 string dir = Path.GetDirectoryName(file);
591 string newFile = string.Format(fileFormat, Path.GetFileName(file ).ToLower());
592 File.Move(file, Path.Combine(dir, newFile));
593 }
863
595 CommandLine.WriteLine();
596 return File.ReadAllText(changelogFile);
597 }
598
599 /// <summary> Updates wiki Downloads page. </summary>
600 private static void UpdateWiki(string releaseNotes, string zipDir)
601 {
602 CommandLine.WriteLine("{{white}} =================================== ====");
603 CommandLine.WriteLine("{{white}} Updating the Wiki");
604 CommandLine.WriteLine("{{white}} =================================== ====");
605
606 new DownloadsPage(releaseNotes, zipDir).UpdateWiki(Wiki.WorkingDirec tory);
607
608 CommandLine.WriteLine();
609 }
610
611 /// <summary> Commits and Tags this release with the given tag </summary >
612 private static void CommitAndTagRelease(string tag)
613 {
614 CommandLine.WriteLine("{{white}} =================================== ====");
615 CommandLine.WriteLine("{{white}} Tagging the release");
616 CommandLine.WriteLine("{{white}} =================================== ====");
617
618 foreach (Hg repository in AllRepositories)
619 {
620 repository.AddUnversionedFiles();
621 repository.RemoveDeletedFiles();
622 bool madeCommit = repository.Commit("Release " tag);
623 if (repository == Default || madeCommit)
624 {
625 try
626 {
627 repository.Tag(tag, false);
628 }
629 catch (Exception ex)
630 {
631 CommandLine.WriteError("Tagging Failed with message {0}" , ex.Message);
632 string response = "yes";
633 CommandLine.RequestUserInput("Do you want to force the l abel?", ref response);
634 if (response.ToLower() == "yes")
635 {
636 repository.Tag(tag, true);
637 }
638 else
639 {
640 throw;
641 }
642 }
643 }
644 }
645 CommandLine.WriteLine();
646 }
647
648 /// <summary> Pushes the changes in all repositories. </summary>
649 private static void PushChanges()
650 {
651 CommandLine.WriteLine("{{white}} =================================== ====");
652 CommandLine.WriteLine("{{white}} Pushing changes");
653 CommandLine.WriteLine("{{white}} =================================== ====");
654
655 foreach (Hg repository in AllRepositories)
656 {
657 repository.Push();
658 }
659
660 CommandLine.WriteLine();
661 }
662 }
663 }
OLDNEW
« no previous file with comments | « Tools/BuildRelease/BuildRelease.csproj ('k') | Tools/BuildRelease/Properties/AssemblyInfo.cs » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b