realityforge-buildr 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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