realityforge-buildr 1.5.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE +176 -0
  4. data/NOTICE +26 -0
  5. data/README.md +3 -0
  6. data/Rakefile +50 -0
  7. data/addon/buildr/checkstyle-report.xsl +104 -0
  8. data/addon/buildr/checkstyle.rb +254 -0
  9. data/addon/buildr/git_auto_version.rb +36 -0
  10. data/addon/buildr/gpg.rb +90 -0
  11. data/addon/buildr/gwt.rb +413 -0
  12. data/addon/buildr/jacoco.rb +161 -0
  13. data/addon/buildr/pmd.rb +185 -0
  14. data/addon/buildr/single_intermediate_layout.rb +71 -0
  15. data/addon/buildr/spotbugs.rb +265 -0
  16. data/addon/buildr/top_level_generate_dir.rb +37 -0
  17. data/addon/buildr/wsgen.rb +192 -0
  18. data/bin/buildr +20 -0
  19. data/buildr.gemspec +61 -0
  20. data/lib/buildr.rb +86 -0
  21. data/lib/buildr/core/application.rb +705 -0
  22. data/lib/buildr/core/assets.rb +96 -0
  23. data/lib/buildr/core/build.rb +587 -0
  24. data/lib/buildr/core/common.rb +167 -0
  25. data/lib/buildr/core/compile.rb +599 -0
  26. data/lib/buildr/core/console.rb +124 -0
  27. data/lib/buildr/core/doc.rb +275 -0
  28. data/lib/buildr/core/environment.rb +128 -0
  29. data/lib/buildr/core/filter.rb +405 -0
  30. data/lib/buildr/core/help.rb +114 -0
  31. data/lib/buildr/core/progressbar.rb +161 -0
  32. data/lib/buildr/core/project.rb +994 -0
  33. data/lib/buildr/core/test.rb +776 -0
  34. data/lib/buildr/core/transports.rb +456 -0
  35. data/lib/buildr/core/util.rb +77 -0
  36. data/lib/buildr/ide/idea.rb +1664 -0
  37. data/lib/buildr/java/commands.rb +230 -0
  38. data/lib/buildr/java/compiler.rb +85 -0
  39. data/lib/buildr/java/custom_pom.rb +300 -0
  40. data/lib/buildr/java/doc.rb +62 -0
  41. data/lib/buildr/java/packaging.rb +393 -0
  42. data/lib/buildr/java/pom.rb +191 -0
  43. data/lib/buildr/java/test_result.rb +54 -0
  44. data/lib/buildr/java/tests.rb +111 -0
  45. data/lib/buildr/packaging/archive.rb +586 -0
  46. data/lib/buildr/packaging/artifact.rb +1113 -0
  47. data/lib/buildr/packaging/artifact_namespace.rb +1010 -0
  48. data/lib/buildr/packaging/artifact_search.rb +138 -0
  49. data/lib/buildr/packaging/package.rb +237 -0
  50. data/lib/buildr/packaging/version_requirement.rb +189 -0
  51. data/lib/buildr/packaging/zip.rb +189 -0
  52. data/lib/buildr/packaging/ziptask.rb +387 -0
  53. data/lib/buildr/version.rb +18 -0
  54. data/rakelib/release.rake +99 -0
  55. data/spec/addon/checkstyle_spec.rb +58 -0
  56. data/spec/core/application_spec.rb +576 -0
  57. data/spec/core/build_spec.rb +922 -0
  58. data/spec/core/common_spec.rb +670 -0
  59. data/spec/core/compile_spec.rb +656 -0
  60. data/spec/core/console_spec.rb +65 -0
  61. data/spec/core/doc_spec.rb +194 -0
  62. data/spec/core/extension_spec.rb +200 -0
  63. data/spec/core/project_spec.rb +736 -0
  64. data/spec/core/test_spec.rb +1131 -0
  65. data/spec/core/transport_spec.rb +452 -0
  66. data/spec/core/util_spec.rb +154 -0
  67. data/spec/ide/idea_spec.rb +1952 -0
  68. data/spec/java/commands_spec.rb +79 -0
  69. data/spec/java/compiler_spec.rb +274 -0
  70. data/spec/java/custom_pom_spec.rb +165 -0
  71. data/spec/java/doc_spec.rb +55 -0
  72. data/spec/java/packaging_spec.rb +786 -0
  73. data/spec/java/pom_spec.rb +162 -0
  74. data/spec/java/test_coverage_helper.rb +257 -0
  75. data/spec/java/tests_spec.rb +224 -0
  76. data/spec/packaging/archive_spec.rb +686 -0
  77. data/spec/packaging/artifact_namespace_spec.rb +757 -0
  78. data/spec/packaging/artifact_spec.rb +1351 -0
  79. data/spec/packaging/packaging_helper.rb +63 -0
  80. data/spec/packaging/packaging_spec.rb +690 -0
  81. data/spec/sandbox.rb +166 -0
  82. data/spec/spec_helpers.rb +420 -0
  83. data/spec/version_requirement_spec.rb +145 -0
  84. data/spec/xpath_matchers.rb +123 -0
  85. metadata +295 -0
@@ -0,0 +1,96 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); 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, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ module Buildr #:nodoc:
17
+
18
+ module Assets #:nodoc:
19
+
20
+ # The base assets task that is responsible for
21
+ # collecting all of the assets into a single output
22
+ # directory
23
+ class AssetsTask < Rake::FileTask
24
+ attr_reader :project
25
+
26
+ def project=(project)
27
+ @project = project
28
+ end
29
+
30
+ # The list of input paths to add to output directory
31
+ def paths
32
+ unless @paths
33
+ @paths = []
34
+ @paths << project._(:source, :main, :assets) if File.exist?(project._(:source, :main, :assets))
35
+ end
36
+ @paths
37
+ end
38
+
39
+ protected
40
+
41
+ def initialize(*args) #:nodoc:
42
+ super
43
+ enhance do
44
+ paths = self.paths.flatten.compact
45
+ if paths.size > 0
46
+ mkdir_p name
47
+ paths.collect do |a|
48
+ a.is_a?(String) ? project.file(a) : a
49
+ end.each do |a|
50
+ a.invoke if a.respond_to?(:invoke)
51
+ end.each do |asset|
52
+ cp_r Dir["#{asset}/*"], "#{name}/"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def out_of_date?(stamp)
61
+ super ||
62
+ self.paths.any? { |n| n.respond_to?(:needed?) && n.needed? }
63
+ end
64
+
65
+ end
66
+
67
+ module ProjectExtension
68
+ include Extension
69
+
70
+ first_time do
71
+ desc "Prepare the assets"
72
+ Project.local_task("assets")
73
+ end
74
+
75
+ before_define do |project|
76
+ # Force the construction of the assets task
77
+ project.assets.paths
78
+ end
79
+
80
+ # Access the asset task
81
+ def assets
82
+ if @assets.nil?
83
+ @assets = AssetsTask.define_task(project._(:target, :main, :assets) => [])
84
+ @assets.project = self
85
+ project.task('assets').enhance([@assets])
86
+ project.build.enhance([@assets])
87
+ end
88
+ @assets
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ class Buildr::Project #:nodoc:
95
+ include ::Buildr::Assets::ProjectExtension
96
+ end
@@ -0,0 +1,587 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); 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, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ module Buildr #:nodoc:
17
+
18
+ class Options
19
+
20
+ # Runs the build in parallel when true (defaults to false). You can force a parallel build by
21
+ # setting this option directly, or by running the parallel task ahead of the build task.
22
+ #
23
+ # This option only affects recursive tasks. For example:
24
+ # buildr parallel package
25
+ # will run all package tasks (from the sub-projects) in parallel, but each sub-project's package
26
+ # task runs its child tasks (prepare, compile, resources, etc) in sequence.
27
+ attr_accessor :parallel
28
+
29
+ end
30
+
31
+ task('parallel') { Buildr.options.parallel = true }
32
+
33
+
34
+ module Build
35
+
36
+ include Extension
37
+
38
+ first_time do
39
+ desc 'Build the project'
40
+ Project.local_task('build') { |name| "Building #{name}" }
41
+ desc 'Clean files generated during a build'
42
+ Project.local_task('clean') { |name| "Cleaning #{name}" }
43
+
44
+ desc 'The default task is build'
45
+ task 'default'=>'build'
46
+ end
47
+
48
+ before_define(:build => [:compile, :test]) do |project|
49
+ project.recursive_task 'build'
50
+ project.recursive_task 'clean'
51
+ project.clean do
52
+ rm_rf project.path_to(:target)
53
+ rm_rf project.path_to(:reports)
54
+ end
55
+ end
56
+
57
+ after_define(:build)
58
+
59
+ # :call-seq:
60
+ # build(*prereqs) => task
61
+ # build { |task| .. } => task
62
+ #
63
+ # Returns the project's build task. With arguments or block, also enhances that task.
64
+ def build(*prereqs, &block)
65
+ task('build').enhance prereqs, &block
66
+ end
67
+
68
+ # :call-seq:
69
+ # clean(*prereqs) => task
70
+ # clean { |task| .. } => task
71
+ #
72
+ # Returns the project's clean task. With arguments or block, also enhances that task.
73
+ def clean(*prereqs, &block)
74
+ task('clean').enhance prereqs, &block
75
+ end
76
+
77
+ end
78
+
79
+ module Hg #:nodoc:
80
+ module_function
81
+
82
+ # :call-seq:
83
+ # hg(*args)
84
+ #
85
+ # Executes a Mercurial (hg) command passing through the args and returns the output.
86
+ # Throws exception if the exit status is not zero. For example:
87
+ # hg 'commit'
88
+ # hg 'update', 'default'
89
+ def hg(*args)
90
+ cmd = "hg #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}"
91
+ output = `#{cmd}`
92
+ fail "Mercurial command \"#{cmd}\" failed with status #{$?.exitstatus}\n#{output}" unless $?.exitstatus == 0
93
+ return output
94
+ end
95
+
96
+ # Return a list of uncommitted / untracked files as reported by hg status
97
+ # The codes used to show the status of files are:
98
+ # M = modified
99
+ # A = added
100
+ # R = removed
101
+ # C = clean
102
+ # ! = missing (deleted by non-hg command, but still tracked)
103
+ # ? = not tracked
104
+ # I = ignored
105
+ # = origin of the previous file listed as A (added)
106
+ def uncommitted_files
107
+ `hg status`.scan(/^(A|M|R|!|\?) (\S.*)$/).map{ |match| match.last.split.last }
108
+ end
109
+
110
+ # Commit the given file with a message. The file should already be added to the Mercurial index.
111
+ def commit(file, message)
112
+ hg 'commit', '-m', message, file
113
+ end
114
+
115
+ # Update the remote branch with the local commits
116
+ # This will push the current remote destination and current branch.
117
+ def push
118
+ hg 'push'
119
+ end
120
+
121
+ # Return the name of the current local branch or nil if none.
122
+ def current_branch
123
+ hg('branch').to_s.strip
124
+ end
125
+
126
+ # Return the aliases (if any) of any remote repositories which the current local branch tracks
127
+ def remote
128
+ hg('paths').scan(/^(?:default|default-push)\s+=\s+(\S.*)/).map{ |match| match.last }
129
+ end
130
+ end
131
+
132
+ module Git #:nodoc:
133
+ module_function
134
+
135
+ # :call-seq:
136
+ # git(*args)
137
+ #
138
+ # Executes a Git command and returns the output. Throws exception if the exit status
139
+ # is not zero. For example:
140
+ # git 'commit'
141
+ # git 'remote', 'show', 'origin'
142
+ def git(*args)
143
+ cmd = "git #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}"
144
+ output = `#{cmd}`
145
+ fail "GIT command \"#{cmd}\" failed with status #{$?.exitstatus}\n#{output}" unless $?.exitstatus == 0
146
+ return output
147
+ end
148
+
149
+ # Returns list of uncommited/untracked files as reported by git status.
150
+ def uncommitted_files
151
+ `git status`.scan(/^#(\t|\s{7})(\S.*)$/).map { |match| match.last.split.last }
152
+ end
153
+
154
+ # Commit the given file with a message.
155
+ # The file has to be known to Git meaning that it has either to have been already committed in the past
156
+ # or freshly added to the index. Otherwise it will fail.
157
+ def commit(file, message)
158
+ git 'commit', '-m', message, file
159
+ end
160
+
161
+ # Update the remote refs using local refs
162
+ #
163
+ # By default, the "remote" destination of the push is the the remote repo linked to the current branch.
164
+ # The default remote branch is the current local branch.
165
+ def push(remote_repo = remote, remote_branch = current_branch)
166
+ git 'push', remote, current_branch
167
+ end
168
+
169
+ # Return the name of the remote repository whose branch the current local branch tracks,
170
+ # or nil if none.
171
+ def remote(branch = current_branch)
172
+ remote = git('config', '--get', "branch.#{branch}.remote").to_s.strip
173
+ remote if !remote.empty? && git('remote').include?(remote)
174
+ end
175
+
176
+ # Return the name of the current branch
177
+ def current_branch
178
+ git('branch')[/^\* (.*)$/, 1]
179
+ end
180
+ end
181
+
182
+
183
+ module Svn #:nodoc:
184
+ module_function
185
+
186
+ # :call-seq:
187
+ # svn(*args)
188
+ #
189
+ # Executes a SVN command and returns the output. Throws exception if the exit status
190
+ # is not zero. For example:
191
+ # svn 'commit'
192
+ def svn(*args)
193
+ output = `svn #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}`
194
+ fail "SVN command failed with status #{$?.exitstatus}" unless $?.exitstatus == 0
195
+ return output
196
+ end
197
+
198
+ def tag(tag_name)
199
+ url = tag_url repo_url, tag_name
200
+ remove url, 'Removing old copy' rescue nil
201
+ copy Dir.pwd, url, "Release #{tag_name}"
202
+ end
203
+
204
+ # Status check reveals modified files, but also SVN externals which we can safely ignore.
205
+ def uncommitted_files
206
+ svn('status', '--ignore-externals').split("\n").reject { |line| line =~ /^X\s/ }
207
+ end
208
+
209
+ def commit(file, message)
210
+ svn 'commit', '-m', message, file
211
+ end
212
+
213
+ # :call-seq:
214
+ # tag_url(svn_url, version) => tag_url
215
+ #
216
+ # Returns the SVN url for the tag.
217
+ # Can tag from the trunk or from branches.
218
+ # Can handle the two standard repository layouts.
219
+ # - http://my.repo/foo/trunk => http://my.repo/foo/tags/1.0.0
220
+ # - http://my.repo/trunk/foo => http://my.repo/tags/foo/1.0.0
221
+ def tag_url(svn_url, tag)
222
+ trunk_or_branches = Regexp.union(%r{^(.*)/trunk(.*)$}, %r{^(.*)/branches(.*)/([^/]*)$})
223
+ match = trunk_or_branches.match(svn_url)
224
+ prefix = match[1] || match[3]
225
+ suffix = match[2] || match[4]
226
+ prefix + '/tags' + suffix + '/' + tag
227
+ end
228
+
229
+ # Return the current SVN URL
230
+ def repo_url
231
+ svn('info', '--xml')[/<url>(.*?)<\/url>/, 1].strip
232
+ end
233
+
234
+ def copy(dir, url, message)
235
+ svn 'copy', '--parents', dir, url, '-m', message
236
+ end
237
+
238
+ def remove(url, message)
239
+ svn 'remove', url, '-m', message
240
+ end
241
+
242
+ end
243
+
244
+
245
+ class Release #:nodoc:
246
+
247
+ THIS_VERSION_PATTERN = /(THIS_VERSION|VERSION_NUMBER)\s*=\s*(["'])(.*)\2/
248
+
249
+ class << self
250
+
251
+ # Use this to specify a different tag name for tagging the release in source control.
252
+ # You can set the tag name or a proc that will be called with the version number,
253
+ # for example:
254
+ # Release.tag_name = lambda { |ver| "foo-#{ver}" }
255
+ attr_accessor :tag_name
256
+
257
+ # Use this to specify a different commit message to commit the buildfile with the next version in source control.
258
+ # You can set the commit message or a proc that will be called with the next version number,
259
+ # for example:
260
+ # Release.commit_message = lambda { |ver| "Changed version number to #{ver}" }
261
+ attr_accessor :commit_message
262
+
263
+ # Use this to specify the next version number to replace VERSION_NUMBER with in the buildfile.
264
+ # You can set the next version or a proc that will be called with the current version number.
265
+ # For example, with the following buildfile:
266
+ # THIS_VERSION = "1.0.0-rc1"
267
+ # Release.next_version = lambda { |version|
268
+ # version[-1] = version[-1].to_i + 1
269
+ # version
270
+ # }
271
+ #
272
+ # Release.next_version will return "1.0.0-rc2", so at the end of the release, the buildfile will contain VERSION_NUMBER = "1.0.0-rc2"
273
+ #
274
+ attr_accessor :next_version
275
+
276
+ # :call-seq:
277
+ # add(MyReleaseClass)
278
+ #
279
+ # Add a Release implementation to the list of available Release classes.
280
+ def add(release)
281
+ @list ||= []
282
+ @list |= [release]
283
+ end
284
+ alias :<< :add
285
+
286
+ # The list of supported Release implementations
287
+ def list
288
+ @list ||= []
289
+ end
290
+
291
+ # Finds and returns the Release instance for this project.
292
+ def find
293
+ unless @release
294
+ klass = list.detect { |impl| impl.applies_to? }
295
+ @release = klass.new if klass
296
+ end
297
+ @release
298
+ end
299
+
300
+ end
301
+
302
+ # :call-seq:
303
+ # make()
304
+ #
305
+ # Make a release.
306
+ def make
307
+ @this_version = extract_version
308
+ check
309
+ with_release_candidate_version do |release_candidate_buildfile|
310
+ args = []
311
+ args << 'buildr' << '--buildfile' << release_candidate_buildfile
312
+ args << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty?
313
+ args << 'clean' << 'upload' << 'DEBUG=no'
314
+ sh *args
315
+ end
316
+ tag_release resolve_tag
317
+ update_version_to_next if this_version != resolve_next_version(this_version)
318
+ end
319
+
320
+ def check
321
+ if this_version == resolve_next_version(this_version) && this_version.match(/-SNAPSHOT$/)
322
+ fail "The next version can't be equal to the current version #{this_version}.\nUpdate THIS_VERSION/VERSION_NUMBER, specify Release.next_version or use NEXT_VERSION env var"
323
+ end
324
+ end
325
+
326
+ # :call-seq:
327
+ # extract_version() => this_version
328
+ #
329
+ # Extract the current version number from the buildfile.
330
+ # Raise an error if not found.
331
+ def extract_version
332
+ buildfile = File.read(version_file)
333
+ buildfile.scan(THIS_VERSION_PATTERN)[0][2]
334
+ rescue
335
+ fail 'Looking for THIS_VERSION = "..." in your Buildfile, none found'
336
+ end
337
+
338
+ protected
339
+
340
+ # the initial value of THIS_VERSION
341
+ attr_accessor :this_version
342
+
343
+ # :call-seq:
344
+ # version_file()
345
+ # Provides the file containing the version of the project.
346
+ # If the project contains a version.rb file next to the Buildr build file,
347
+ # it is used. Otherwise, always use the buildfile.
348
+ def version_file
349
+ version_rb_file = File.dirname(Buildr.application.buildfile.to_s) + '/version.rb'
350
+ return version_rb_file if File.exists?(version_rb_file)
351
+ return Buildr.application.buildfile.to_s
352
+ end
353
+
354
+ # :call-seq:
355
+ # with_release_candidate_version() { |filename| ... }
356
+ #
357
+ # Yields to block with release candidate buildfile, before committing to use it.
358
+ #
359
+ # We need a Buildfile with upgraded version numbers to run the build, but we don't want the
360
+ # Buildfile modified unless the build succeeds. So this method updates the version number in
361
+ # a separate (Buildfile.next) file, yields to the block with that filename, and if successful
362
+ # copies the new file over the existing one.
363
+ #
364
+ # The release version is the current version without '-SNAPSHOT'. So:
365
+ # THIS_VERSION = 1.1.0-SNAPSHOT
366
+ # becomes:
367
+ # THIS_VERSION = 1.1.0
368
+ # for the release buildfile.
369
+ def with_release_candidate_version
370
+ release_candidate_buildfile = version_file + '.next'
371
+
372
+ release_candidate_buildfile_contents = change_version { |version|
373
+ version.gsub(/-SNAPSHOT$/, "")
374
+ }
375
+ File.open(release_candidate_buildfile, 'w') { |file| file.write release_candidate_buildfile_contents }
376
+ begin
377
+ yield release_candidate_buildfile
378
+ mv release_candidate_buildfile, version_file
379
+ ensure
380
+ rm release_candidate_buildfile rescue nil
381
+ end
382
+ end
383
+
384
+ # :call-seq:
385
+ # change_version() { |this_version| ... } => buildfile
386
+ #
387
+ # Change version number in the current Buildfile, but without writing a new file (yet).
388
+ # Returns the contents of the Buildfile with the modified version number.
389
+ #
390
+ # This method yields to the block with the current (this) version number and expects
391
+ # the block to return the updated version.
392
+ def change_version
393
+ current_version = extract_version
394
+ new_version = yield(current_version)
395
+ buildfile = File.read(version_file)
396
+ buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{new_version}"}) }
397
+ end
398
+
399
+ # Return the name of the tag to tag the release with.
400
+ def resolve_tag
401
+ version = extract_version
402
+ tag = Release.tag_name || version
403
+ tag = tag.call(version) if Proc === tag
404
+ tag
405
+ end
406
+
407
+ # Return the new value of THIS_VERSION based on the version passed.
408
+ #
409
+ # This method receives the existing value of THIS_VERSION
410
+ def resolve_next_version(current_version)
411
+ next_version = Release.next_version
412
+ next_version ||= lambda { |v|
413
+ snapshot = v.match(/-SNAPSHOT$/)
414
+ version = v.gsub(/-SNAPSHOT$/, "").split(/\./)
415
+ if snapshot
416
+ version[-1] = sprintf("%0#{version[-1].size}d", version[-1].to_i + 1) + '-SNAPSHOT'
417
+ end
418
+ version.join('.')
419
+ }
420
+ next_version = ENV['NEXT_VERSION'] if ENV['NEXT_VERSION']
421
+ next_version = ENV['next_version'] if ENV['next_version']
422
+ next_version = next_version.call(current_version) if Proc === next_version
423
+ next_version
424
+ end
425
+
426
+ # Move the version to next and save the updated buildfile
427
+ def update_buildfile
428
+ buildfile = change_version { |version| # THIS_VERSION minus SNAPSHOT
429
+ resolve_next_version(this_version) # THIS_VERSION
430
+ }
431
+ File.open(version_file, 'w') { |file| file.write buildfile }
432
+ end
433
+
434
+ # Return the message to use to commit the buildfile with the next version
435
+ def message
436
+ version = extract_version
437
+ msg = Release.commit_message || "Changed version number to #{version}"
438
+ msg = msg.call(version) if Proc === msg
439
+ msg
440
+ end
441
+
442
+ def update_version_to_next
443
+ update_buildfile
444
+ end
445
+ end
446
+
447
+
448
+ class HgRelease < Release
449
+ class << self
450
+ def applies_to?
451
+ if File.exist? '.hg/requires'
452
+ true
453
+ else
454
+ curr_pwd = Dir.pwd
455
+ Dir.chdir('..') do
456
+ return false if curr_pwd == Dir.pwd # Means going up one level is not possible.
457
+ applies_to?
458
+ end
459
+ end
460
+ end
461
+ end
462
+
463
+ # Fails if one of these 2 conditions are not met:
464
+ # 1. The reository is not 'clean'; no content staged or unstaged
465
+ # 2. The repository is only a local repository and has no remote refs
466
+ def check
467
+ super
468
+ info "Working in branch '#{Hg.current_branch}'"
469
+ uncommitted = Hg.uncommitted_files
470
+ fail "Uncommitted files violate the First Principle Of Release!\n#{uncommitted.join("\n")}" unless uncommitted.empty?
471
+ fail "You are releasing from a local branch that does not track a remote!" if Hg.remote.empty?
472
+ end
473
+
474
+ # Tag this release in Mercurial
475
+ def tag_release(tag)
476
+ unless this_version == extract_version
477
+ info "Committing buildfile with version number #{extract_version}"
478
+ Hg.commit File.basename(version_file), message
479
+ Hg.push if Hg.remote
480
+ end
481
+ info "Tagging release #{tag}"
482
+ Hg.hg 'tag', tag, '-m', "[buildr] Cutting release #{tag}"
483
+ Hg.push if Hg.remote
484
+ end
485
+
486
+ # Update buildfile with next version number
487
+ def update_version_to_next
488
+ super
489
+ info "Current version is now #{extract_version}"
490
+ Hg.commit File.basename(version_file), message
491
+ Hg.push if Hg.remote
492
+ end
493
+ end
494
+
495
+
496
+ class GitRelease < Release
497
+ class << self
498
+ def applies_to?
499
+ if File.exist? '.git/config'
500
+ true
501
+ else
502
+ curr_pwd = Dir.pwd
503
+ Dir.chdir('..') do
504
+ return false if curr_pwd == Dir.pwd # Means going up one level is not possible.
505
+ applies_to?
506
+ end
507
+ end
508
+ end
509
+ end
510
+
511
+ # Fails if one of these 2 conditions are not met:
512
+ # 1. the repository is clean: no content staged or unstaged
513
+ # 2. some remote repositories are defined but the current branch does not track any
514
+ def check
515
+ super
516
+ uncommitted = Git.uncommitted_files
517
+ fail "Uncommitted files violate the First Principle Of Release!\n#{uncommitted.join("\n")}" unless uncommitted.empty?
518
+ fail "You are releasing from a local branch that does not track a remote!" unless Git.remote
519
+ end
520
+
521
+ # Add a tag reference in .git/refs/tags and push it to the remote if any.
522
+ # If a tag with the same name already exists it will get deleted (in both local and remote repositories).
523
+ def tag_release(tag)
524
+ unless this_version == extract_version
525
+ info "Committing buildfile with version number #{extract_version}"
526
+ Git.commit File.basename(version_file), message
527
+ Git.push if Git.remote
528
+ end
529
+ info "Tagging release #{tag}"
530
+ Git.git 'tag', '-d', tag rescue nil
531
+ Git.git 'push', Git.remote, ":refs/tags/#{tag}" rescue nil if Git.remote
532
+ Git.git 'tag', '-a', tag, '-m', "[buildr] Cutting release #{tag}"
533
+ Git.git 'push', Git.remote, 'tag', tag if Git.remote
534
+ end
535
+
536
+ def update_version_to_next
537
+ super
538
+ info "Current version is now #{extract_version}"
539
+ Git.commit File.basename(version_file), message
540
+ Git.push if Git.remote
541
+ end
542
+ end
543
+
544
+
545
+ class SvnRelease < Release
546
+ class << self
547
+ def applies_to?
548
+ File.exist?('.svn')
549
+ end
550
+ end
551
+
552
+ def check
553
+ super
554
+ fail "Uncommitted files violate the First Principle Of Release!\n"+Svn.uncommitted_files.join("\n") unless Svn.uncommitted_files.empty?
555
+ fail "SVN URL must contain 'trunk' or 'branches/...'" unless Svn.repo_url =~ /(trunk)|(branches.*)$/
556
+ end
557
+
558
+ def tag_release(tag)
559
+ # Unlike Git, committing the buildfile with the released version is not necessary.
560
+ # svn tag does commit & tag.
561
+ info "Tagging release #{tag}"
562
+ Svn.tag tag
563
+ end
564
+
565
+ def update_version_to_next
566
+ super
567
+ info "Current version is now #{extract_version}"
568
+ Svn.commit version_file, message
569
+ end
570
+ end
571
+
572
+ Release.add HgRelease
573
+ Release.add SvnRelease
574
+ Release.add GitRelease
575
+
576
+ desc 'Make a release'
577
+ task 'release' do |task|
578
+ release = Release.find
579
+ fail 'Unable to detect the Version Control System.' unless release
580
+ release.make
581
+ end
582
+
583
+ end
584
+
585
+ class Buildr::Project #:nodoc:
586
+ include Buildr::Build
587
+ end