LEFT | RIGHT |
1 using System; | 1 /* |
| 2 Copyright 2013 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; |
2 using System.Collections.Generic; | 18 using System.Collections.Generic; |
3 using System.Diagnostics; | 19 using System.Diagnostics; |
4 using System.IO; | 20 using System.IO; |
5 using System.Linq; | 21 using System.Linq; |
6 using System.Reflection; | 22 using System.Reflection; |
7 using System.Text; | 23 using System.Text; |
8 using System.Text.RegularExpressions; | 24 using System.Text.RegularExpressions; |
| 25 using Microsoft.Build.Evaluation; |
| 26 using Microsoft.Build.Framework; |
| 27 using Microsoft.Build.Logging; |
9 | 28 |
10 using CommandLine; | 29 using CommandLine; |
11 using CommandLine.Text; | 30 using CommandLine.Text; |
12 using Ionic.Zip; | 31 using Ionic.Zip; |
13 using Microsoft.Build.Evaluation; | |
14 using Microsoft.Build.Framework; | |
15 using Microsoft.Build.Logging; | |
16 | 32 |
17 using Google.Apis.Release.Repositories; | 33 using Google.Apis.Release.Repositories; |
| 34 using Google.Apis.Release.Wiki; |
18 using Google.Apis.Utils; | 35 using Google.Apis.Utils; |
19 using Google.Apis.Release.Wiki; | 36 using Google.Apis.Utils.Trace; |
20 | 37 |
21 namespace Google.Apis.Release | 38 namespace Google.Apis.Release |
22 { | 39 { |
| 40 /// <summary>The main program for creating a new Google.Apis release.</summa
ry> |
23 class Program | 41 class Program |
24 { | 42 { |
| 43 /// <summary>The options class which contains the different options to t
his publish release utility.</summary> |
25 public class Options | 44 public class Options |
26 { | 45 { |
27 [Option('v', "version", Required = true, | 46 #region HelpText |
28 HelpText = "The version number of this release - <Major.Minor.Bu
ild> only.")] | 47 |
29 public string Version { get; set; } | 48 const string VersionHelpText = "The version number of this release -
<Major.Minor.Build> only."; |
30 | 49 const string OutputHelpText = "Define the output directory for this
build. " + |
31 [Option('d', "dir", Required = true, HelpText = "Define the output d
irectory for this build. " + | 50 "Notice that it's relative to current directory."; |
32 "Notice that it's relative to current directory.")] | 51 const string StepHelpText = "Two options: " + |
33 public string OutputDirectory { get; set; } | 52 "'1' for building the core library. \n" + |
34 | 53 "'2' for compiling samples, updating wiki and push to contrib th
e new version."; |
35 [Option('s', "step", Required = true, HelpText = "Two options: '1' f
or building the core library" + | 54 const string IsBetaHelpText = "Is this release beta?"; |
36 "'2' for compiling samples, updating wiki and push to contrib th
e new version")] | 55 const string NuGetApiKeyHelpText = "Define the NuGet API key to publ
ish to NuGet main repository."; |
37 public int Step { get; set; } | |
38 | |
39 [Option('b', "beta", DefaultValue = true, HelpText = "Is this releas
e is beta?")] | |
40 public bool IsBeta { get; set; } | |
41 | 56 |
42 [HelpOption] | 57 [HelpOption] |
43 public string GetHelp() | 58 public string GetHelp() |
44 { | 59 { |
45 return HelpText.AutoBuild(this, c => HelpText.DefaultParsingErro
rsHandler(this, c)); | 60 return HelpText.AutoBuild(this, c => HelpText.DefaultParsingErro
rsHandler(this, c)); |
46 } | 61 } |
47 } | 62 |
48 | 63 #endregion |
49 static readonly TraceSource TraceSource = new TraceSource("Google.Apis")
; | 64 |
50 | 65 [Option('v', "version", Required = true, HelpText = VersionHelpText)
] |
51 /// <summary> Command line arguments. </summary> | 66 public string Version { get; set; } |
| 67 |
| 68 [Option('d', "dir", Required = true, HelpText = OutputHelpText)] |
| 69 public string OutputDirectory { get; set; } |
| 70 |
| 71 [Option('s', "step", Required = true, HelpText = StepHelpText)] |
| 72 public int Step { get; set; } |
| 73 |
| 74 [Option('b', "beta", DefaultValue = true, HelpText = IsBetaHelpText)
] |
| 75 public bool IsBeta { get; set; } |
| 76 |
| 77 [Option('k', "nuget_key", HelpText = NuGetApiKeyHelpText)] |
| 78 public string NuGetApiKey { get; set; } |
| 79 } |
| 80 |
| 81 private static readonly TraceSource TraceSource = new TraceSource("Googl
e.Apis"); |
| 82 |
| 83 /// <summary>Command line arguments.</summary> |
52 private readonly Options options; | 84 private readonly Options options; |
53 | 85 |
54 private int MajorVersion { get; set; } | 86 private int MajorVersion { get; set; } |
55 private int MinorVersion { get; set; } | 87 private int MinorVersion { get; set; } |
56 private int BuildVersion { get; set; } | 88 private int BuildVersion { get; set; } |
57 | 89 |
58 private string Tag | 90 private string Tag |
59 { | 91 { |
60 get { return options.Version + (options.IsBeta ? "-beta" : ""); } | 92 get { return options.Version + (options.IsBeta ? "-beta" : ""); } |
61 } | 93 } |
62 | 94 |
63 /// <summary>The "default" repository.</summary> | 95 /// <summary>Gets or sets the "default" repository.</summary> |
64 private Hg DefaultRepository { get; set; } | 96 private Hg DefaultRepository { get; set; } |
65 | 97 |
66 /// <summary>The "samples" repository.</summary> | 98 /// <summary>Gets or sets the "samples" repository.</summary> |
67 private Hg SamplesRepository { get; set; } | 99 private Hg SamplesRepository { get; set; } |
68 | 100 |
69 /// <summary>The "wiki" repository.</summary> | 101 /// <summary>Gets or sets the "wiki" repository.</summary> |
70 private Hg WikiRepository { get; set; } | 102 private Hg WikiRepository { get; set; } |
71 | 103 |
72 /// <summary>The "contrib" repository.</summary> | 104 /// <summary>Gets or sets the "contrib" repository.</summary> |
73 private Hg ContribRepository { get; set; } | 105 private Hg ContribRepository { get; set; } |
74 | 106 |
| 107 /// <summary>Gets all four repositories.</summary> |
75 private IEnumerable<Hg> AllRepositories | 108 private IEnumerable<Hg> AllRepositories |
76 { | 109 { |
77 get { return new List<Hg> { DefaultRepository, SamplesRepository, Wi
kiRepository, ContribRepository }; } | 110 get { return new List<Hg> { DefaultRepository, SamplesRepository, Wi
kiRepository, ContribRepository }; } |
78 } | 111 } |
79 | 112 |
| 113 /// <summary> |
| 114 /// Clones URL format which expects one parameter of the repository name
(the default repository should be· |
| 115 /// empty). |
| 116 /// </summary> |
| 117 private const string CloneUrlFormat = "https://code.google.com/p/google-
api-dotnet-client{0}/"; |
| 118 |
80 static void Main(string[] args) | 119 static void Main(string[] args) |
81 { | 120 { |
| 121 bool valid = true; |
| 122 |
82 var options = new Options(); | 123 var options = new Options(); |
83 if (!CommandLine.Parser.Default.ParseArguments(args, options)) | 124 if (!CommandLine.Parser.Default.ParseArguments(args, options)) |
84 { | 125 { |
85 Console.ReadKey(); | 126 Console.ReadKey(); |
86 return; | 127 return; |
87 } | 128 } |
88 | 129 |
| 130 if (options.Step > 2 || options.Step < 1) |
| 131 { |
| 132 TraceSource.TraceEvent(TraceEventType.Error, "Invalid Step. Vali
d step is '1' or '2'."); |
| 133 valid = false; |
| 134 } |
| 135 |
89 var match = Regex.Match(options.Version, @"^(\d+)\.(\d+)\.(\d)+$"); | 136 var match = Regex.Match(options.Version, @"^(\d+)\.(\d+)\.(\d)+$"); |
90 if (!match.Success) | 137 if (!match.Success) |
91 { | 138 { |
92 TraceSource.TraceEvent(TraceEventType.Error, | 139 TraceSource.TraceEvent(TraceEventType.Error, |
93 "Invalid version Number. Version should be in <Major>.<Minor
>.<Build> form."); | 140 "Invalid version Number. Version should be in <Major>.<Minor
>.<Build> form."); |
94 } | 141 valid = false; |
95 | 142 } |
96 var p = new Program(options) | 143 |
| 144 var program = new Program(options) |
97 { | 145 { |
98 MajorVersion = int.Parse(match.Groups[1].Value), | 146 MajorVersion = int.Parse(match.Groups[1].Value), |
99 MinorVersion = int.Parse(match.Groups[2].Value), | 147 MinorVersion = int.Parse(match.Groups[2].Value), |
100 BuildVersion = int.Parse(match.Groups[3].Value), | 148 BuildVersion = int.Parse(match.Groups[3].Value), |
101 }; | 149 }; |
102 | 150 |
103 try | 151 if (valid) |
104 { | 152 { |
105 p.Run(); | 153 try |
106 } | 154 { |
107 catch (Exception ex) | 155 program.Run(); |
108 { | 156 } |
109 TraceSource.TraceEvent(TraceEventType.Error, "Exception occurred
while running. Exception is {0}", | 157 catch (Exception ex) |
110 ex.Message); | 158 { |
111 } | 159 TraceSource.TraceEvent(TraceEventType.Error, "Exception occu
rred while running. Exception is: {0}", |
112 | 160 ex.Message); |
| 161 } |
| 162 } |
| 163 |
| 164 Console.WriteLine("Press any key to continue..."); |
113 Console.ReadKey(); | 165 Console.ReadKey(); |
114 } | 166 } |
115 | 167 |
| 168 /// <summary>The main release logic for creating a new release of Google
.Apis.</summary> |
116 private void Run() | 169 private void Run() |
117 { | 170 { |
118 const string CloneUrl = "https://code.google.com/p/google-api-dotnet
-client{0}/"; | 171 DefaultRepository = new Hg(new Uri(string.Format(CloneUrlFormat, "")
), "default"); |
119 | 172 |
120 DefaultRepository = Hg.Get("default", string.Format(CloneUrl, "")); | 173 // Step 1 is only for creating Google.Apis and Google.Apis.Authentic
ation packages |
121 | |
122 if (options.Step == 1) | 174 if (options.Step == 1) |
123 { | 175 { |
124 if (BuildVersion != 0) | 176 DoStep1(); |
125 { | 177 } |
126 DefaultRepository.Update(string.Format("{0}.{1}", MajorVersi
on, MinorVersion)); | 178 // Step 2 should be done after the NuGet publisher generated all the
APIs and the samples repository was· |
127 } | 179 // updated with the new packages |
128 | |
129 if (!HasIncomingChanges(new List<Hg> { DefaultRepository })) | |
130 { | |
131 if (BuildDefaultRepository()) | |
132 { | |
133 CreateNuGetPackage(); | |
134 } | |
135 } | |
136 } | |
137 | |
138 else if (options.Step == 2) | 180 else if (options.Step == 2) |
139 { | 181 { |
140 Console.WriteLine(); | 182 DoStep2(); |
141 Console.WriteLine(); | 183 } |
| 184 } |
| 185 |
| 186 /// <summary>Creates Google.Apis and Google.Apis.Authentication packages
.</summary> |
| 187 private void DoStep1() |
| 188 { |
| 189 if (BuildVersion != 0) |
| 190 { |
| 191 DefaultRepository.Update(string.Format("{0}.{1}", MajorVersion,
MinorVersion)); |
| 192 } |
| 193 |
| 194 // if there are incoming changes those changes will be printed, othe
rwise we can continue in the process |
| 195 if (!HasIncomingChanges(new List<Hg> { DefaultRepository })) |
| 196 { |
| 197 // in case build fails the method will print its failures |
| 198 if (BuildDefaultRepository()) |
| 199 { |
| 200 CreateCoreNuGetPackages(); |
| 201 // TODO(peleyal): release notes should be part of the packag
e |
| 202 } |
| 203 } |
| 204 } |
| 205 |
| 206 /// <summary> |
| 207 /// Doing the following: |
| 208 /// <list type="number"> |
| 209 /// <item><description>Builds samples</description></item> |
| 210 /// <item><description>Creates a release notes</description></item> |
| 211 /// <item><description>Update wiki download page</description></item> |
| 212 /// <item><description>Create a new release in the contrib repository</d
escription></item> |
| 213 /// <item><description>Commits, Tags and Pushes</description></item> |
| 214 /// </list> |
| 215 /// </summary> |
| 216 private void DoStep2() |
| 217 { |
| 218 Console.WriteLine(); |
| 219 Console.WriteLine(); |
| 220 Console.WriteLine("========================="); |
| 221 Console.WriteLine("Prerequisites for Step 2:"); |
| 222 Console.WriteLine("You ran Step 1."); |
| 223 Console.WriteLine("You upgraded the Google.Apis NuGet packages for e
ach sample in the samples " + |
| 224 "repository and pushed that change."); |
| 225 Console.WriteLine("========================="); |
| 226 if (!CanContinue()) |
| 227 { |
| 228 return; |
| 229 } |
| 230 |
| 231 SamplesRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".s
amples")), "samples"); |
| 232 WikiRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".wiki
")), "wiki"); |
| 233 ContribRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".c
ontrib")), "contrib"); |
| 234 |
| 235 // if there are incoming changes those changes will be printed, othe
rwise we can continue in the· |
| 236 // process |
| 237 if (!HasIncomingChanges(AllRepositories)) |
| 238 { |
| 239 BuildSamples(); |
| 240 var notes = CreateContribNewRelease(); |
| 241 UpdateWiki(notes); |
| 242 |
| 243 foreach (var repository in AllRepositories) |
| 244 { |
| 245 repository.AddRemoveFiles(); |
| 246 } |
| 247 |
142 Console.WriteLine("========================="); | 248 Console.WriteLine("========================="); |
143 Console.WriteLine("Prerequisites for Step 2:"); | 249 Console.WriteLine("Commit, Tag and Push"); |
144 Console.WriteLine("You ran Step 1."); | 250 Console.WriteLine("========================="); |
145 Console.WriteLine("You upgrade the libraries in the samples repo
sitory and pushed that change."); | 251 if (!CanContinue()) |
| 252 { |
| 253 return; |
| 254 } |
| 255 |
| 256 // commit |
| 257 CommitAndTag(); |
| 258 |
| 259 // push |
| 260 foreach (Hg repository in AllRepositories) |
| 261 { |
| 262 repository.Push(); |
| 263 } |
| 264 |
| 265 // create branch |
| 266 PrintCreateBranch(); |
| 267 |
| 268 // publish core components to NuGet |
| 269 if (!string.IsNullOrEmpty(options.NuGetApiKey)) |
| 270 { |
| 271 PublishPackagesToNuGet(); |
| 272 Console.WriteLine("Now... you should run the NuGet publisher
to publish a new PCL " |
| 273 + "for each generated Google API. Run: " + |
| 274 "Google.Apis.NuGet.Publisher --all_apis true -m publishe
r -k [NUGET_KEY]"); |
| 275 } |
| 276 else |
| 277 { |
| 278 TraceSource.TraceEvent(TraceEventType.Error, "NuGet API key
is empty!"); |
| 279 } |
| 280 } |
| 281 } |
| 282 |
| 283 /// <summary>Asks the user if he wants to continue in the process.</summ
ary> |
| 284 /// <returns><c>true</c> if the user to press 'y' or 'yes' to continue</
returns> |
| 285 private bool CanContinue() |
| 286 { |
| 287 var yesOptions = new[] { "y", "yes" }; |
| 288 var noOptions = new[] { "n", "no" }; |
| 289 |
| 290 string input; |
| 291 do |
| 292 { |
146 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n' | 'no'
to stop"); | 293 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n' | 'no'
to stop"); |
147 Console.WriteLine("========================="); | 294 input = Console.ReadLine().ToLower(); |
148 var input = Console.ReadLine(); | 295 } while (!yesOptions.Contains(input) && !noOptions.Contains(input)); |
149 var yesOptions = new[] { "y", "yes" }; | 296 |
150 var noOptions = new[] { "n", "no" }; | 297 return yesOptions.Contains(input); |
151 while (!yesOptions.Contains(input) && !noOptions.Contains(input)
) | 298 } |
152 { | 299 |
153 Console.WriteLine("please press 'y' | 'yes' | 'n' | 'no' onl
y"); | 300 /// <summary>Publishes the core packages to NuGet main repository.</summ
ary> |
154 input = Console.ReadLine(); | 301 private void PublishPackagesToNuGet() |
155 } | 302 { |
156 | 303 var apiNupkgPath = Path.Combine(NuGetUtilities.LocalNuGetPackageFold
er, |
157 if (yesOptions.Contains(input)) | 304 string.Format("Google.Apis.{0}.nupkg", Tag)); |
158 { | 305 NuGetUtilities.PublishToNuget(apiNupkgPath, options.NuGetApiKey); |
159 SamplesRepository = Hg.Get("samples", string.Format(CloneUrl
, ".samples")); | 306 |
160 WikiRepository = Hg.Get("wiki", string.Format(CloneUrl, ".wi
ki")); | 307 var authenticationNupkgPath = Path.Combine(NuGetUtilities.LocalNuGet
PackageFolder, |
161 ContribRepository = Hg.Get("contrib", string.Format(CloneUrl
, ".contrib")); | 308 string.Format("Google.Apis.Authentication.{0}.nupkg", Tag)); |
162 | 309 NuGetUtilities.PublishToNuget(authenticationNupkgPath, options.NuGet
ApiKey); |
163 if (!HasIncomingChanges(new List<Hg> {· | 310 } |
164 DefaultRepository, SamplesRepository, WikiRepository, Co
ntribRepository })) | 311 |
165 { | 312 /// <summary> |
166 // BuildSamples(); | 313 /// Displays the user orders how to create a branch (only in case it's a
new major or minor release. |
167 var notes = BuildContribNewRelease(); | 314 /// </summary> |
168 UpdateWiki(notes); | |
169 | |
170 AddRemoveFiles(); | |
171 | |
172 Console.WriteLine("========================="); | |
173 Console.WriteLine("Commit, Tag and Push"); | |
174 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n'
| 'no' to stop"); | |
175 Console.WriteLine("========================="); | |
176 input = Console.ReadLine(); | |
177 while (!yesOptions.Contains(input) && !noOptions.Contain
s(input)) | |
178 { | |
179 Console.WriteLine("please press 'y' | 'yes' | 'n' |
'no' only"); | |
180 input = Console.ReadLine(); | |
181 } | |
182 | |
183 if (yesOptions.Contains(input)) | |
184 { | |
185 // commit | |
186 CommitAndTag(); | |
187 | |
188 // push | |
189 foreach (Hg repository in AllRepositories) | |
190 { | |
191 repository.Push(); | |
192 } | |
193 | |
194 // create branch | |
195 PrintCreateBranch(); | |
196 } | |
197 } | |
198 } | |
199 } | |
200 else | |
201 { | |
202 TraceSource.TraceEvent(TraceEventType.Error, "Invalid step optio
n!"); | |
203 } | |
204 | |
205 Console.WriteLine("Done..."); | |
206 Console.ReadKey(); | |
207 return; | |
208 } | |
209 | |
210 private void AddRemoveFiles() | |
211 { | |
212 foreach (var repository in AllRepositories) | |
213 { | |
214 repository.AddUnversionedFiles(); | |
215 repository.RemoveDeletedFiles(); | |
216 } | |
217 } | |
218 | |
219 /// <summary>Displays the user orders how to create a branch.</summary> | |
220 private void PrintCreateBranch() | 315 private void PrintCreateBranch() |
221 { | 316 { |
222 if (BuildVersion != 0) | 317 if (BuildVersion != 0) |
223 { | 318 { |
224 // No need to branch in that case | 319 // No need to branch in that case |
225 return; | 320 return; |
226 } | 321 } |
227 | 322 |
228 // TODO(peleyal): automate this as well | 323 // TODO(peleyal): consider automate this as well |
229 Console.WriteLine("You should create a new branch for this release n
ow:"); | 324 Console.WriteLine("You should create a new branch for this release n
ow:"); |
230 Console.WriteLine("cd " + DefaultRepository.WorkingDirectory); | 325 Console.WriteLine("cd " + DefaultRepository.WorkingDirectory); |
231 var branchVersion = string.Format("{0}.{1}", MajorVersion, MinorVers
ion); | 326 var branchVersion = string.Format("{0}.{1}", MajorVersion, MinorVers
ion); |
232 Console.WriteLine("hg branch " + branchVersion); | 327 Console.WriteLine("hg branch " + branchVersion); |
233 Console.WriteLine(string.Format("hg commit -m create {0} branch", br
anchVersion)); | 328 Console.WriteLine(string.Format("hg commit -m create {0} branch", br
anchVersion)); |
234 Console.WriteLine("hg push --new-branch"); | 329 Console.WriteLine("hg push --new-branch"); |
235 } | 330 } |
236 | 331 |
| 332 /// <summary>Commits all changes in all repositories and tags the "defau
lt" repository.</summary> |
237 private void CommitAndTag() | 333 private void CommitAndTag() |
238 { | 334 { |
239 foreach (var repository in AllRepositories) | 335 foreach (var repository in AllRepositories) |
240 { | 336 { |
241 repository.AddUnversionedFiles(); | |
242 repository.RemoveDeletedFiles(); | |
243 | |
244 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ting", repository.Name); | 337 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ting", repository.Name); |
245 | 338 |
246 bool committed = repository.Commit("Release " + Tag); | 339 bool committed = repository.Commit("Release " + Tag); |
247 | 340 |
| 341 // TODO(peleyal): think to remove this if from this function. I
don't like a if inside a loop statement |
248 if (repository.Equals(DefaultRepository) && committed) | 342 if (repository.Equals(DefaultRepository) && committed) |
249 { | 343 { |
250 try | 344 try |
251 { | 345 { |
252 repository.Tag(Tag, false); | 346 repository.Tag(Tag, false); |
253 TraceSource.TraceEvent(TraceEventType.Information, "{0}
- Was tagged \"{1}\"", | 347 TraceSource.TraceEvent(TraceEventType.Information, "{0}
- Was tagged \"{1}\"", |
254 repository.Name, Tag); | 348 repository.Name, Tag); |
255 } | 349 } |
256 catch (Exception ex) | 350 catch (Exception ex) |
257 { | 351 { |
258 TraceSource.TraceEvent(TraceEventType.Error, "{0} - Tagg
ing Failed. Exception is: {1}", | 352 TraceSource.TraceEvent(TraceEventType.Error, "{0} - Tagg
ing Failed. Exception is: {1}", |
259 repository.Name, ex.Message); | 353 repository.Name, ex.Message); |
260 } | 354 } |
261 } | 355 } |
262 | 356 |
263 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ted", repository.Name); | 357 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ted", repository.Name); |
264 } | 358 } |
265 } | 359 } |
266 | 360 |
| 361 /// <summary>Updates the "Downloads" wiki page with the new release note
s.</summary> |
267 private void UpdateWiki(string notes) | 362 private void UpdateWiki(string notes) |
268 { | 363 { |
269 TraceSource.TraceEvent(TraceEventType.Information, "Updating wiki do
wnloads page"); | 364 TraceSource.TraceEvent(TraceEventType.Information, "Updating wiki do
wnloads page"); |
270 | 365 |
271 // TODO(peleyal): improve. Currently we count on that old release of
X.Y.Z is X.Y-1.0 | 366 // TODO(peleyal): improve. Currently we count on that old release of
X.Y.Z is X.Y-1.0 |
272 var oldVersion = string.Format("{0}.{1}.{2}", MajorVersion, MinorVer
sion - 1, 0); | 367 var oldVersion = string.Format("{0}.{1}.{2}", MajorVersion, MinorVer
sion - 1, 0); |
273 DownloadsPageUpdater.UpdateWiki(WikiRepository.WorkingDirectory, not
es, oldVersion, options.Version); | 368 DownloadsPageUpdater.UpdateWiki(WikiRepository.WorkingDirectory, not
es, oldVersion, options.Version); |
274 | 369 |
275 TraceSource.TraceEvent(TraceEventType.Information, "wiki downloads p
age was updated"); | 370 TraceSource.TraceEvent(TraceEventType.Information, "wiki downloads p
age was updated"); |
276 } | 371 } |
277 | 372 |
278 private string BuildContribNewRelease() | 373 /// <summary>Creates a new release in the "contrib" repository.</summary
> |
| 374 /// <returns>The release notes of this version</returns> |
| 375 private string CreateContribNewRelease() |
279 { | 376 { |
280 TraceSource.TraceEvent(TraceEventType.Information, "Building Contrib
release"); | 377 TraceSource.TraceEvent(TraceEventType.Information, "Building Contrib
release"); |
281 | 378 |
282 string releaseDir = ContribRepository.Combine(Tag); | 379 string releaseDir = ContribRepository.Combine(Tag); |
283 | 380 |
284 // Clear existing directories. | 381 // Clear existing directories. |
285 DirectoryUtils.ClearOrCreateDirectory(releaseDir); | 382 DirectoryUtilities.ClearOrCreateDirectory(releaseDir); |
286 string genDir = Path.Combine(releaseDir, "Generated"); | 383 string genDir = Path.Combine(releaseDir, "Generated"); |
287 Directory.CreateDirectory(genDir); | 384 Directory.CreateDirectory(genDir); |
288 | 385 |
289 #region [RELEASE_VERSION]/Generated/Bin | 386 #region [RELEASE_VERSION]/Generated/Bin |
290 | 387 |
291 string binDir = Path.Combine(genDir, "Bin"); | 388 string binDir = Path.Combine(genDir, "Bin"); |
292 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", | 389 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", |
293 DirectoryUtils.GetRelativePath(binDir, ContribRepository.Working
Directory)); | 390 DirectoryUtilities.GetRelativePath(binDir, ContribRepository.Wor
kingDirectory)); |
| 391 |
294 Directory.CreateDirectory(binDir); | 392 Directory.CreateDirectory(binDir); |
295 | 393 foreach (var project in ReleaseProjects) |
296 { | 394 { |
297 foreach (var project in ReleaseProjects) | 395 var releasePath = Path.Combine(project.DirectoryPath, "Bin", "Re
lease"); |
298 { | 396 foreach (var filePath in Directory.GetFiles(releasePath, "Google
.Apis.*")) |
299 var releasePath = Path.Combine(project.DirectoryPath, "Bin",
"Release"); | 397 { |
300 foreach (var filePath in Directory.GetFiles(releasePath, "Go
ogle.Apis.*")) | 398 File.Copy(filePath, |
301 { | 399 Path.Combine(binDir, filePath.Substring(filePath.LastInd
exOf("\\") + 1)), true); |
302 File.Copy(filePath, | 400 } |
303 Path.Combine(binDir, filePath.Substring(filePath.Las
tIndexOf("\\") + 1)), true); | 401 } |
304 } | 402 |
305 } | 403 // TODO(peleyal): Put also the nuspec and nupkg |
306 | |
307 // Put also the nuspec and nupkg | |
308 } | |
309 | 404 |
310 #endregion | 405 #endregion |
311 | 406 |
312 #region [RELEASE_VERSION]/ZipFiles | 407 #region [RELEASE_VERSION]/ZipFiles |
313 | 408 |
314 string zipFilesDir = Path.Combine(genDir, "ZipFiles"); | 409 string zipFilesDir = Path.Combine(genDir, "ZipFiles"); |
315 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", | 410 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", |
316 DirectoryUtils.GetRelativePath(zipFilesDir, ContribRepository.Wo
rkingDirectory)); | 411 DirectoryUtilities.GetRelativePath(zipFilesDir, ContribRepositor
y.WorkingDirectory)); |
317 | 412 |
318 Directory.CreateDirectory(zipFilesDir); | 413 Directory.CreateDirectory(zipFilesDir); |
319 { | 414 foreach (var project in ReleaseProjects) |
320 foreach (var project in ReleaseProjects) | 415 { |
321 { | 416 project.Build("Clean"); |
322 project.Build("Clean"); | 417 } |
323 } | 418 |
324 | 419 TraceSource.TraceEvent(TraceEventType.Information, "Release projects
were cleaned"); |
325 TraceSource.TraceEvent(TraceEventType.Information, "Release proj
ects were cleaned"); | 420 |
326 | 421 // source.zip |
327 // source.zip | 422 var fileNameFormat = "google-api-dotnet-client-{0}.{1}.zip"; |
328 var fileNameFormat = "google-api-dotnet-client-{0}.{1}.zip"; | 423 var sourceZipName = string.Format(fileNameFormat, Tag, "source"); |
329 var sourceZipName = string.Format(fileNameFormat, Tag, "source")
; | 424 using (var zip = new ZipFile(Path.Combine(zipFilesDir, sourceZipName
))) |
330 using (var zip = new ZipFile(Path.Combine(zipFilesDir, sourceZip
Name))) | 425 { |
331 { | 426 zip.AddDirectory(Path.Combine(DefaultRepository.WorkingDirectory
, "Src"), "Src"); |
332 zip.AddDirectory(Path.Combine(DefaultRepository.WorkingDirec
tory, "Src"), "Src"); | 427 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory, "Go
ogleApisClient.sln"), ""); |
333 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory,
"GoogleApisClient.sln"), ""); | 428 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory, "LI
CENSE"), ""); |
334 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory,
"LICENSE"), ""); | 429 zip.Save(); |
335 zip.Save(); | 430 } |
336 } | 431 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, sourceZipName); |
337 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", sourceZipName); | 432 |
338 | 433 // binary.zip |
339 // binary.zip | 434 var binaryZipName = string.Format(fileNameFormat, Tag, "binary"); |
340 var binaryZipName = string.Format(fileNameFormat, Tag, "binary")
; | 435 using (var zip = new ZipFile(Path.Combine(zipFilesDir, binaryZipName
))) |
341 using (var zip = new ZipFile(Path.Combine(zipFilesDir, binaryZip
Name))) | 436 { |
342 { | 437 Directory.GetFiles(binDir).ToList().ForEach(f => zip.AddFile(Pat
h.Combine(binDir, f), "")); |
343 Directory.GetFiles(binDir).ToList().ForEach(f => zip.AddFile
(Path.Combine(binDir, f), "")); | 438 zip.Save(); |
344 zip.Save(); | 439 } |
345 } | 440 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, binaryZipName); |
346 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", binaryZipName); | 441 |
347 | 442 |
348 | 443 // samples.zip |
349 // samples.zip | 444 var samplesZipName = string.Format(fileNameFormat, Tag, "samples"); |
350 var samplesZipName = string.Format(fileNameFormat, Tag, "samples
"); | 445 using (var zip = new ZipFile(Path.Combine(zipFilesDir, samplesZipNam
e))) |
351 using (var zip = new ZipFile(Path.Combine(zipFilesDir, samplesZi
pName))) | 446 { |
352 { | 447 foreach (var d in Directory.GetDirectories(SamplesRepository.Wor
kingDirectory)) |
353 foreach (var d in Directory.GetDirectories(SamplesRepository
.WorkingDirectory)) | 448 { |
| 449 if (!d.EndsWith(".hg")) |
354 { | 450 { |
355 if (!d.EndsWith(".hg")) | 451 var directoryName = d.Substring(d.LastIndexOf("\\") + 1)
; |
356 { | 452 zip.AddDirectory(Path.Combine(SamplesRepository.WorkingD
irectory, d), directoryName); |
357 var directoryName = d.Substring(d.LastIndexOf("\\")
+ 1); | |
358 zip.AddDirectory(Path.Combine(SamplesRepository.Work
ingDirectory, d), directoryName); | |
359 } | |
360 } | 453 } |
361 foreach (var f in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.sln")) | 454 } |
362 { | 455 foreach (var f in Directory.GetFiles(SamplesRepository.WorkingDi
rectory, "*.sln")) |
363 zip.AddFile(Path.Combine(SamplesRepository.WorkingDirect
ory, f), ""); | 456 { |
364 } | 457 zip.AddFile(Path.Combine(SamplesRepository.WorkingDirectory,
f), ""); |
365 zip.Save(); | 458 } |
366 } | 459 zip.Save(); |
367 | 460 } |
368 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", samplesZipName); | 461 |
369 } | 462 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, samplesZipName); |
370 | 463 |
371 #endregion | 464 #endregion |
372 | 465 |
373 #region [RELEASE_VERSION]/ReleaseNotes.txt | 466 #region [RELEASE_VERSION]/ReleaseNotes.txt |
374 | 467 |
375 var notes = CreateChangelog(); | 468 var notes = GetChangelog(); |
376 TraceSource.TraceEvent(TraceEventType.Information, "Creating Release
Notes file"); | 469 TraceSource.TraceEvent(TraceEventType.Information, "Creating Release
Notes file"); |
377 var noteFilePath = Path.Combine(genDir, "ReleaseNotes.txt"); | 470 var noteFilePath = Path.Combine(genDir, "ReleaseNotes.txt"); |
378 File.WriteAllText(noteFilePath, notes); | 471 File.WriteAllText(noteFilePath, notes); |
379 | 472 |
380 #endregion | 473 #endregion |
381 | 474 |
382 // open the created change-log and read again the notes (in case the
user had modified the file) | 475 // open the created change-log and read again the notes (in case the
user had modified the file) |
383 Process.Start(noteFilePath).WaitForExit(); | 476 Process.Start(noteFilePath).WaitForExit(); |
384 notes = File.ReadAllText(noteFilePath); | 477 notes = File.ReadAllText(noteFilePath); |
385 | 478 |
386 return notes; | 479 return notes; |
387 } | 480 } |
388 | 481 |
389 private string CreateChangelog() | 482 /// <summary> |
| 483 /// Returns the notes list of this release. It contains the change log o
f all pushes to the "default"· |
| 484 /// repository. |
| 485 /// </summary> |
| 486 private string GetChangelog() |
390 { | 487 { |
391 StringBuilder log = new StringBuilder(); | 488 StringBuilder log = new StringBuilder(); |
392 log.AppendLine("Google .NET Client Library"); | 489 log.AppendLine("Google .NET Client Library"); |
393 log.AppendLine(string.Format("Stable Release '{0}'", Tag)); | 490 log.AppendLine(string.Format("Stable Release '{0}'", Tag)); |
394 log.AppendLine(DateTime.UtcNow.ToLongDateString()); | 491 log.AppendLine(DateTime.UtcNow.ToLongDateString()); |
395 log.AppendLine("==========================================="); | 492 log.AppendLine("==========================================="); |
396 | 493 |
397 log.AppendLine().AppendLine("Changes:"); | 494 log.AppendLine().AppendLine("Changes:"); |
398 foreach (string line in DefaultRepository.CreateChangelist()) | 495 foreach (string line in DefaultRepository.CreateChangelist()) |
399 { | 496 { |
400 log.AppendLine(" " + line); | 497 log.AppendLine(" " + line); |
401 } | 498 } |
402 | 499 |
403 return log.ToString(); | 500 return log.ToString(); |
404 } | 501 } |
405 | 502 |
| 503 /// <summary>Builds all projects in the "samples" repository.</summary> |
406 private void BuildSamples() | 504 private void BuildSamples() |
407 { | 505 { |
408 // Build all the samples projects. | 506 // build all the samples projects. |
409 TraceSource.TraceEvent(TraceEventType.Information, "Building the sam
ples"); | 507 TraceSource.TraceEvent(TraceEventType.Information, "Building the sam
ples"); |
410 foreach (string csproj in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.csproj", | 508 foreach (string csproj in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.csproj", |
411 SearchOption.AllDirectories)) | 509 SearchOption.AllDirectories)) |
412 { | 510 { |
413 Project project = new Project(csproj); | 511 Project project = new Project(csproj); |
414 project.SetProperty("Configuration", "Release"); | 512 project.SetProperty("Configuration", "Release"); |
415 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
", project.GetName()); | 513 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
", project.GetName()); |
416 bool success = project.Build("Build", new[] { new ConsoleLogger(
LoggerVerbosity.Quiet) }); | 514 bool success = project.Build("Build", new[] { new ConsoleLogger(
LoggerVerbosity.Quiet) }); |
417 if (!success) | 515 if (!success) |
418 { | 516 { |
419 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} F
AILED", project.GetName()); | 517 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} F
AILED", project.GetName()); |
420 } | 518 } |
421 project.Build("Clean"); | 519 project.Build("Clean"); |
422 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
succeeded", project.GetName()); | 520 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
succeeded", project.GetName()); |
423 } | 521 } |
424 } | 522 } |
425 | 523 |
| 524 /// <summary>Returns <c>true</c> if one of the repositories has incoming
changes.</summary> |
426 private bool HasIncomingChanges(IEnumerable<Hg> repositories) | 525 private bool HasIncomingChanges(IEnumerable<Hg> repositories) |
427 { | 526 { |
428 foreach (var repository in repositories) | 527 foreach (var repository in repositories) |
429 { | 528 { |
430 if (repository.HasIncomingChanges) | 529 if (repository.HasIncomingChanges) |
431 { | 530 { |
432 TraceSource.TraceEvent(TraceEventType.Error, | 531 TraceSource.TraceEvent(TraceEventType.Error, |
433 "[{0}] has incoming changes. Run hg pull & update first!
", repository.Name); | 532 "[{0}] has incoming changes. Run hg pull & update first!
", repository.Name); |
434 return true; | 533 return true; |
435 } | 534 } |
436 } | 535 } |
437 return false; | 536 return false; |
438 } | 537 } |
439 | 538 |
440 | 539 /// <summary>Creates the Google.Apis and Google.Apis.Authentication NuGe
t packages.</summary> |
441 private void CreateNuGetPackage() | 540 private void CreateCoreNuGetPackages() |
442 { | 541 { |
443 // create a resource dir in the working folder | 542 // create a resource dir in the working folder |
444 var destDirectory = Path.Combine(Environment.CurrentDirectory, "Reso
urces"); | 543 var destDirectory = Path.Combine(Environment.CurrentDirectory, "Reso
urces"); |
445 if (Directory.Exists(destDirectory)) | 544 if (Directory.Exists(destDirectory)) |
446 { | 545 { |
447 Directory.Delete(destDirectory, true); | 546 Directory.Delete(destDirectory, true); |
448 } | 547 } |
449 | 548 |
450 FileInfo info = new FileInfo(Assembly.GetEntryAssembly().Location); | 549 FileInfo info = new FileInfo(Assembly.GetEntryAssembly().Location); |
451 DirectoryUtils.CopyDirectory(Path.Combine(info.Directory.FullName, "
Resources"), destDirectory); | 550 DirectoryUtilities.CopyDirectory(Path.Combine(info.Directory.FullNam
e, "Resources"), destDirectory); |
452 | 551 |
453 var newVersion = options.Version + "-beta"; | 552 var newVersion = options.Version + "-beta"; |
454 | 553 |
| 554 // get the Google.Apis and Google.Apis.Authentication nuspec files a
nd replace the version in it |
455 var apiNuspec = Path.Combine(destDirectory, "Google.Apis.VERSION.nus
pec"); | 555 var apiNuspec = Path.Combine(destDirectory, "Google.Apis.VERSION.nus
pec"); |
456 var newApiNuspec = apiNuspec.Replace("VERSION", newVersion); | 556 var newApiNuspec = apiNuspec.Replace("VERSION", newVersion); |
457 var authenticationNuspec = Path.Combine(destDirectory, "Google.Apis.
Authentication.VERSION.nuspec"); | 557 var authenticationNuspec = Path.Combine(destDirectory, "Google.Apis.
Authentication.VERSION.nuspec"); |
458 var newAuthenticationNuspec = authenticationNuspec.Replace("VERSION"
, newVersion); | 558 var newAuthenticationNuspec = authenticationNuspec.Replace("VERSION"
, newVersion); |
459 | 559 |
460 File.Move(apiNuspec, newApiNuspec); | 560 File.Move(apiNuspec, newApiNuspec); |
461 File.Move(authenticationNuspec, newAuthenticationNuspec); | 561 File.Move(authenticationNuspec, newAuthenticationNuspec); |
462 | 562 |
463 var allLines = File.ReadAllText(newApiNuspec).Replace("VERSION", new
Version); | 563 var allLines = File.ReadAllText(newApiNuspec).Replace("VERSION", new
Version); |
464 File.WriteAllText(newApiNuspec, allLines); | 564 File.WriteAllText(newApiNuspec, allLines); |
465 | 565 |
466 allLines = File.ReadAllText(newAuthenticationNuspec).Replace("VERSIO
N", newVersion); | 566 allLines = File.ReadAllText(newAuthenticationNuspec).Replace("VERSIO
N", newVersion); |
467 File.WriteAllText(newAuthenticationNuspec, allLines); | 567 File.WriteAllText(newAuthenticationNuspec, allLines); |
468 | 568 |
469 Utilities.CreateLocalNupkgFile(newApiNuspec); | 569 NuGetUtilities.CreateLocalNupkgFile(newApiNuspec, Environment.Curren
tDirectory); |
470 Utilities.CreateLocalNupkgFile(newAuthenticationNuspec); | 570 NuGetUtilities.CreateLocalNupkgFile(newAuthenticationNuspec, Environ
ment.CurrentDirectory); |
471 } | 571 } |
472 | 572 |
473 private IEnumerable<Project> releaseProjects; | 573 private IEnumerable<Project> releaseProjects; |
| 574 /// <summary>Gets the release projects of from the "default" repository.
</summary> |
474 private IEnumerable<Project> ReleaseProjects | 575 private IEnumerable<Project> ReleaseProjects |
475 { | 576 { |
476 get | 577 get |
477 { | 578 { |
478 if (releaseProjects != null) | 579 if (releaseProjects != null) |
479 { | 580 { |
480 return releaseProjects; | 581 return releaseProjects; |
481 } | 582 } |
482 | 583 |
483 var releasePaths = new[]· | 584 var releasePaths = new[]· |
484 { | 585 { |
485 DefaultRepository.Combine("Src", "GoogleApis", "GoogleApis.c
sproj"), | 586 DefaultRepository.Combine("Src", "GoogleApis", "GoogleApis.c
sproj"), |
486 DefaultRepository.Combine("Src", "GoogleApis.DotNet4", "Goog
leApis.DotNet4.csproj"), | 587 DefaultRepository.Combine("Src", "GoogleApis.DotNet4", "Goog
leApis.DotNet4.csproj"), |
487 DefaultRepository.Combine("Src", "GoogleApis.Authentication.
OAuth2",· | 588 DefaultRepository.Combine("Src", "GoogleApis.Authentication.
OAuth2",· |
488 "GoogleApis.Authentication.OAuth2.csproj") | 589 "GoogleApis.Authentication.OAuth2.csproj") |
489 }; | 590 }; |
490 releaseProjects = (from path in releasePaths | 591 return releaseProjects = (from path in releasePaths |
491 select new Project(path)).ToList(); | 592 select new Project(path)).ToList(); |
492 return releaseProjects; | 593 } |
493 } | 594 } |
494 } | 595 |
495 | 596 /// <summary>Builds the "default" repository projects.</summary> |
| 597 /// <returns><c>true</c> if the default repository was build successfull
y</returns> |
496 private bool BuildDefaultRepository() | 598 private bool BuildDefaultRepository() |
497 { | 599 { |
498 TraceSource.TraceEvent(TraceEventType.Information, "Building project
s...."); | 600 TraceSource.TraceEvent(TraceEventType.Information, "Building project
s...."); |
499 | 601 |
500 var allProjects = new List<Project>(); | 602 var allProjects = new List<Project>(); |
501 allProjects.AddRange(ReleaseProjects); | 603 allProjects.AddRange(ReleaseProjects); |
502 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Tests", | 604 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Tests", |
503 "GoogleApis.Tests.csproj"))); | 605 "GoogleApis.Tests.csproj"))); |
504 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Authentication.OAuth2.Tests", | 606 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Authentication.OAuth2.Tests", |
505 "GoogleApis.Authentication.OAuth2.Tests.csproj"))); | 607 "GoogleApis.Authentication.OAuth2.Tests.csproj"))); |
(...skipping 13 matching lines...) Expand all Loading... |
519 } | 621 } |
520 else | 622 else |
521 { | 623 { |
522 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} f
ailed!", name); | 624 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} f
ailed!", name); |
523 return false; | 625 return false; |
524 } | 626 } |
525 | 627 |
526 if (!ReleaseProjects.Contains(project)) | 628 if (!ReleaseProjects.Contains(project)) |
527 { | 629 { |
528 // TODO(peleyal): run unit tests for all test projects | 630 // TODO(peleyal): run unit tests for all test projects |
529 project.Build("Clean"); | 631 } |
530 } | |
531 | |
532 | |
533 } | 632 } |
534 | 633 |
535 return true; | 634 return true; |
536 } | 635 } |
537 | 636 |
| 637 /// <summary>Constructs a new program with the given options.</summary> |
538 public Program(Options options) | 638 public Program(Options options) |
539 { | 639 { |
540 this.options = options; | 640 this.options = options; |
541 | 641 |
542 string path = Path.GetFullPath(options.OutputDirectory); | 642 string path = Path.GetFullPath(options.OutputDirectory); |
543 if (!Directory.Exists(path)) | 643 if (!Directory.Exists(path)) |
544 { | 644 { |
545 Directory.CreateDirectory(path); | 645 Directory.CreateDirectory(path); |
546 } | 646 } |
547 | 647 |
548 Environment.CurrentDirectory = path; | 648 Environment.CurrentDirectory = path; |
549 } | 649 } |
550 } | 650 } |
551 } | 651 } |
LEFT | RIGHT |