| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| lib/buildr/packaging/artifact.rb | 905 | 463 | 26.74%
|
38.88%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
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 |
17 require 'buildr/core/project' |
18 require 'buildr/core/transports' |
19 require 'buildr/packaging/artifact_namespace' |
20 require 'fileutils' |
21 |
22 |
23 module Buildr |
24 |
25 desc 'Download all artifacts' |
26 task 'artifacts' |
27 |
28 desc "Download all artifacts' sources" |
29 task 'artifacts:sources' |
30 |
31 desc "Download all artifacts' javadoc" |
32 task 'artifacts:javadoc' |
33 |
34 # Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks. |
35 # |
36 # An artifact has an identifier, group identifier, type, version number and |
37 # optional classifier. All can be used to locate it in the local repository, |
38 # download from or upload to a remote repository. |
39 # |
40 # The #to_spec and #to_hash methods allow it to be used everywhere an artifact is |
41 # accepted. |
42 module ActsAsArtifact |
43 |
44 ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version] |
45 |
46 class << self |
47 private |
48 |
49 # :stopdoc: |
50 def included(mod) |
51 mod.extend self |
52 end |
53 |
54 def extend_object(base) |
55 base.instance_eval { alias :install_old :install } if base.respond_to? :install |
56 base.instance_eval { alias :uninstall_old :uninstall } if base.respond_to? :uninstall |
57 base.instance_eval { alias :upload_old :upload } if base.respond_to? :upload |
58 super |
59 end |
60 |
61 def extended(base) |
62 #We try to keep the previous instance methods defined on the base instance if there were ones. |
63 base.instance_eval { alias :install :install_old } if base.respond_to? :install_old |
64 base.instance_eval { alias :uninstall :uninstall_old } if base.respond_to? :uninstall_old |
65 base.instance_eval { alias :upload :upload_old } if base.respond_to? :upload_old |
66 end |
67 |
68 # :startdoc: |
69 end |
70 |
71 # The artifact identifier. |
72 attr_reader :id |
73 # The group identifier. |
74 attr_reader :group |
75 # The file type. (Symbol) |
76 attr_reader :type |
77 # The version number. |
78 attr_reader :version |
79 # Optional artifact classifier. |
80 attr_reader :classifier |
81 |
82 def snapshot? |
83 version =~ /-SNAPSHOT$/ |
84 end |
85 |
86 # :call-seq: |
87 # to_spec_hash => Hash |
88 # |
89 # Returns the artifact specification as a hash. For example: |
90 # com.example:app:jar:1.2 |
91 # becomes: |
92 # { :group=>'com.example', |
93 # :id=>'app', |
94 # :type=>:jar, |
95 # :version=>'1.2' } |
96 def to_spec_hash |
97 base = { :group=>group, :id=>id, :type=>type, :version=>version } |
98 classifier ? base.merge(:classifier=>classifier) : base |
99 end |
100 alias_method :to_hash, :to_spec_hash |
101 |
102 # :call-seq: |
103 # to_spec => String |
104 # |
105 # Returns the artifact specification, in the structure: |
106 # <group>:<artifact>:<type>:<version> |
107 # or |
108 # <group>:<artifact>:<type>:<classifier>:<version> |
109 def to_spec |
110 classifier ? "#{group}:#{id}:#{type}:#{classifier}:#{version}" : "#{group}:#{id}:#{type}:#{version}" |
111 end |
112 |
113 # :call-seq: |
114 # pom => Artifact |
115 # |
116 # Convenience method that returns a POM artifact. |
117 def pom |
118 return self if type == :pom |
119 Buildr.artifact(:group=>group, :id=>id, :version=>version, :type=>:pom) |
120 end |
121 |
122 # :call-seq: |
123 # sources_artifact => Artifact |
124 # |
125 # Convenience method that returns a sources artifact. |
126 def sources_artifact |
127 sources_spec = to_spec_hash.merge(:classifier=>'sources') |
128 sources_task = OptionalArtifact.define_task(Buildr.repositories.locate(sources_spec)) |
129 sources_task.send :apply_spec, sources_spec |
130 sources_task |
131 end |
132 |
133 # :call-seq: |
134 # javadoc_artifact => Artifact |
135 # |
136 # Convenience method that returns the associated javadoc artifact |
137 def javadoc_artifact |
138 javadoc_spec = to_spec_hash.merge(:classifier=>'javadoc') |
139 javadoc_task = OptionalArtifact.define_task(Buildr.repositories.locate(javadoc_spec)) |
140 javadoc_task.send :apply_spec, javadoc_spec |
141 javadoc_task |
142 end |
143 |
144 # :call-seq: |
145 # pom_xml => string |
146 # |
147 # Creates POM XML for this artifact. |
148 def pom_xml |
149 xml = Builder::XmlMarkup.new(:indent=>2) |
150 xml.instruct! |
151 xml.project do |
152 xml.modelVersion '4.0.0' |
153 xml.groupId group |
154 xml.artifactId id |
155 xml.version version |
156 xml.classifier classifier if classifier |
157 end |
158 end |
159 |
160 def install |
161 invoke |
162 in_local_repository = Buildr.repositories.locate(self) |
163 if pom && pom != self && classifier.nil? |
164 pom.invoke |
165 pom.install |
166 end |
167 if name != in_local_repository |
168 mkpath File.dirname(in_local_repository) |
169 cp name, in_local_repository, :preserve => false |
170 info "Installed #{name} to #{in_local_repository}" |
171 end |
172 end |
173 |
174 def uninstall |
175 installed = Buildr.repositories.locate(self) |
176 rm installed if File.exist?(installed) |
177 pom.uninstall if pom && pom != self && classifier.nil? |
178 end |
179 |
180 # :call-seq: |
181 # upload |
182 # upload(url) |
183 # upload(options) |
184 # |
185 # Uploads the artifact, its POM and digital signatures to remote server. |
186 # |
187 # In the first form, uses the upload options specified by repositories.release_to. |
188 # In the second form, uses a URL that includes all the relevant information. |
189 # In the third form, uses a hash with the options :url, :username, :password, |
190 # and :permissions. All but :url are optional. |
191 def upload(upload_to = nil) |
192 upload_task(upload_to).invoke |
193 end |
194 |
195 def upload_task(upload_to = nil) |
196 upload_to ||= Buildr.repositories.release_to |
197 upload_to = { :url=>upload_to } unless Hash === upload_to |
198 raise ArgumentError, 'Don\'t know where to upload, perhaps you forgot to set repositories.release_to' unless upload_to[:url] |
199 |
200 # Set the upload URI, including mandatory slash (we expect it to be the base directory). |
201 # Username/password may be part of URI, or separate entities. |
202 uri = URI.parse(upload_to[:url].clone) |
203 uri.path = uri.path + '/' unless uri.path[-1] == '/' |
204 uri.user = upload_to[:username] if upload_to[:username] |
205 uri.password = upload_to[:password] if upload_to[:password] |
206 |
207 path = group.gsub('.', '/') + "/#{id}/#{version}/#{File.basename(name)}" |
208 |
209 unless task = Buildr.application.lookup(uri+path) |
210 deps = [self] |
211 deps << pom.upload_task( upload_to ) if pom && pom != self && classifier.nil? |
212 |
213 task = Rake::Task.define_task uri + path => deps do |
214 # Upload artifact relative to base URL, need to create path before uploading. |
215 options = upload_to[:options] || {:permissions => upload_to[:permissions]} |
216 info "Deploying #{to_spec}" |
217 URI.upload uri + path, name, options |
218 end |
219 end |
220 task |
221 end |
222 |
223 protected |
224 |
225 # Apply specification to this artifact. |
226 def apply_spec(spec) |
227 spec = Artifact.to_hash(spec) |
228 ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) } |
229 self |
230 end |
231 |
232 def group_path |
233 group.gsub('.', '/') |
234 end |
235 |
236 end |
237 |
238 |
239 # A file task referencing an artifact in the local repository. |
240 # |
241 # This task includes all the artifact attributes (group, id, version, etc). It points |
242 # to the artifact's path in the local repository. When invoked, it will download the |
243 # artifact into the local repository if the artifact does not already exist. |
244 # |
245 # Note: You can enhance this task to create the artifact yourself, e.g. download it from |
246 # a site that doesn't have a remote repository structure, copy it from a different disk, etc. |
247 class Artifact < Rake::FileTask |
248 |
249 # The default artifact type. |
250 DEFAULT_TYPE = :jar |
251 |
252 include ActsAsArtifact, Buildr |
253 |
254 class << self |
255 |
256 # :call-seq: |
257 # lookup(spec) => Artifact |
258 # |
259 # Lookup a previously registered artifact task based on its specification (String or Hash). |
260 def lookup(spec) |
261 @artifacts ||= {} |
262 @artifacts[to_spec(spec)] |
263 end |
264 |
265 # :call-seq: |
266 # list => specs |
267 # |
268 # Returns an array of specs for all the registered artifacts. (Anything created from artifact, or package). |
269 def list |
270 @artifacts ||= {} |
271 @artifacts.keys |
272 end |
273 |
274 # :call-seq: |
275 # register(artifacts) => artifacts |
276 # |
277 # Register an artifact task(s) for later lookup (see #lookup). |
278 def register(*tasks) |
279 @artifacts ||= {} |
280 fail 'You can only register an artifact task, one of the arguments is not a Task that responds to to_spec' unless |
281 tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) } |
282 tasks.each { |task| @artifacts[task.to_spec] = task } |
283 tasks |
284 end |
285 |
286 # :call-seq: |
287 # to_hash(spec_hash) => spec_hash |
288 # to_hash(spec_string) => spec_hash |
289 # to_hash(artifact) => spec_hash |
290 # |
291 # Turn a spec into a hash. This method accepts a String, Hash or any object that responds to |
292 # the method to_spec. There are several reasons to use this method: |
293 # * You can pass anything that could possibly be a spec, and get a hash. |
294 # * It will check that the spec includes the group identifier, artifact |
295 # identifier and version number and set the file type, if missing. |
296 # * It will always return a new specs hash. |
297 def to_hash(spec) |
298 if spec.respond_to?(:to_spec) |
299 to_hash spec.to_spec |
300 elsif Hash === spec |
301 rake_check_options spec, :id, :group, :type, :classifier, :version |
302 # Sanitize the hash and check it's valid. |
303 spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k].to_s if spec[k] ; h } |
304 fail "Missing group identifier for #{spec.inspect}" unless spec[:group] |
305 fail "Missing artifact identifier for #{spec.inspect}" unless spec[:id] |
306 fail "Missing version for #{spec.inspect}" unless spec[:version] |
307 spec[:type] = (spec[:type] || DEFAULT_TYPE).to_sym |
308 spec |
309 elsif String === spec |
310 group, id, type, version, *rest = spec.split(':').map { |part| part.empty? ? nil : part } |
311 unless rest.empty? |
312 # Optional classifier comes before version. |
313 classifier, version = version, rest.shift |
314 fail "Expecting <group:id:type:version> or <group:id:type:classifier:version>, found <#{spec}>" unless rest.empty? |
315 end |
316 to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier |
317 else |
318 fail 'Expecting a String, Hash or object that responds to to_spec' |
319 end |
320 end |
321 |
322 # :call-seq: |
323 # to_spec(spec_hash) => spec_string |
324 # |
325 # Convert a hash back to a spec string. This method accepts |
326 # a string, hash or any object that responds to to_spec. |
327 def to_spec(hash) |
328 hash = to_hash(hash) unless Hash === hash |
329 version = ":#{hash[:version]}" if hash[:version] |
330 classifier = ":#{hash[:classifier]}" if hash[:classifier] |
331 "#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_TYPE}#{classifier}#{version}" |
332 end |
333 |
334 # :call-seq: |
335 # hash_to_file_name(spec_hash) => file_name |
336 # |
337 # Convert a hash spec to a file name. |
338 def hash_to_file_name(hash) |
339 version = "-#{hash[:version]}" if hash[:version] |
340 classifier = "-#{hash[:classifier]}" if hash[:classifier] |
341 "#{hash[:id]}#{version}#{classifier}.#{hash[:type] || DEFAULT_TYPE}" |
342 end |
343 |
344 end |
345 |
346 def initialize(*args) #:nodoc: |
347 super |
348 enhance do |task| |
349 # Default behavior: download the artifact from one of the remote repositories |
350 # if the file does not exist. But this default behavior is counter productive |
351 # if the artifact knows how to build itself (e.g. download from a different location), |
352 # so don't perform it if the task found a different way to create the artifact. |
353 task.enhance do |
354 if download_needed? task |
355 info "Downloading #{to_spec}" |
356 download |
357 pom.invoke rescue nil if pom && pom != self && classifier.nil? |
358 end |
359 end |
360 end |
361 end |
362 |
363 # :call-seq: |
364 # from(path) => self |
365 # |
366 # Use this when you want to install or upload an artifact from a given file, for example: |
367 # test = artifact('group:id:jar:1.0').from('test.jar') |
368 # install test |
369 # See also Buildr#install and Buildr#upload. |
370 def from(path) |
371 @from = path.is_a?(Rake::Task) ? path : File.expand_path(path.to_s) |
372 enhance [@from] do |
373 mkpath File.dirname(name) |
374 cp @from.to_s, name |
375 end |
376 pom.content pom_xml unless pom == self || pom.has_content? |
377 self |
378 end |
379 |
380 # :call-seq: |
381 # content(string) => self |
382 # |
383 # Use this when you want to install or upload an artifact from a given content, for example: |
384 # readme = artifact('com.example:readme:txt:1.0').content(<<-EOF |
385 # Please visit our website at http://example.com/readme |
386 # <<EOF |
387 # install readme |
388 # |
389 # If the argument is not a string, it will be converted to a string using to_s |
390 def content(string = nil) |
391 return @content unless string |
392 |
393 unless @content |
394 enhance do |
395 write name, @content |
396 end |
397 |
398 class << self |
399 # Force overwriting target since we don't have source file |
400 # to check for timestamp modification |
401 def needed? |
402 true |
403 end |
404 end |
405 end |
406 @content = string |
407 pom.content pom_xml unless pom == self || pom.has_content? |
408 self |
409 end |
410 |
411 protected |
412 |
413 def has_content? |
414 @from || @content |
415 end |
416 |
417 # :call-seq: |
418 # download |
419 # |
420 # Downloads an artifact from one of the remote repositories, and stores it in the local |
421 # repository. Raises an exception if the artifact is not found. |
422 # |
423 # This method attempts to download the artifact from each repository in the order in |
424 # which they are returned from #remote, until successful. |
425 def download |
426 trace "Downloading #{to_spec}" |
427 remote = Buildr.repositories.remote.map { |repo_url| URI === repo_url ? repo_url : URI.parse(repo_url) } |
428 remote = remote.each { |repo_url| repo_url.path += '/' unless repo_url.path[-1] == '/' } |
429 fail "Unable to download #{to_spec}. No remote repositories defined." if remote.empty? |
430 exact_success = remote.find do |repo_url| |
431 begin |
432 path = "#{group_path}/#{id}/#{version}/#{File.basename(name)}" |
433 download_artifact(repo_url + path) |
434 true |
435 rescue URI::NotFoundError |
436 false |
437 rescue Exception=>error |
438 info error |
439 trace error.backtrace.join("\n") |
440 false |
441 end |
442 end |
443 |
444 if exact_success |
445 return |
446 elsif snapshot? |
447 download_m2_snapshot(remote) |
448 else |
449 fail_download(remote) |
450 end |
451 end |
452 |
453 def download_m2_snapshot(remote_uris) |
454 remote_uris.find do |repo_url| |
455 snapshot_url = current_snapshot_repo_url(repo_url) |
456 if snapshot_url |
457 begin |
458 download_artifact snapshot_url |
459 true |
460 rescue URI::NotFoundError |
461 false |
462 end |
463 else |
464 false |
465 end |
466 end or fail_download(remote_uris) |
467 end |
468 |
469 def current_snapshot_repo_url(repo_url) |
470 begin |
471 metadata_path = "#{group_path}/#{id}/#{version}/maven-metadata.xml" |
472 metadata_xml = StringIO.new |
473 URI.download repo_url + metadata_path, metadata_xml |
474 metadata = REXML::Document.new(metadata_xml.string).root |
475 timestamp = REXML::XPath.first(metadata, '//timestamp') |
476 build_number = REXML::XPath.first(metadata, '//buildNumber') |
477 error "No timestamp provided for the snapshot #{to_spec}" if timestamp.nil? |
478 error "No build number provided for the snapshot #{to_spec}" if build_number.nil? |
479 return nil if timestamp.nil? || build_number.nil? |
480 snapshot_of = version[0, version.size - 9] |
481 classifier_snippet = (classifier != nil) ? "-#{classifier}" : "" |
482 repo_url + "#{group_path}/#{id}/#{version}/#{id}-#{snapshot_of}-#{timestamp.text}-#{build_number.text}#{classifier_snippet}.#{type}" |
483 rescue URI::NotFoundError |
484 nil |
485 end |
486 end |
487 |
488 def fail_download(remote_uris) |
489 fail "Failed to download #{to_spec}, tried the following repositories:\n#{remote_uris.join("\n")}" |
490 end |
491 |
492 protected |
493 |
494 # :call-seq: |
495 # needed? |
496 # |
497 # Validates whether artifact is required to be downloaded from repository |
498 def needed? |
499 return true if snapshot? && File.exist?(name) && (update_snapshot? || old?) |
500 super |
501 end |
502 |
503 private |
504 |
505 # :call-seq: |
506 # download_artifact |
507 # |
508 # Downloads artifact from given repository, |
509 # supports downloading snapshot artifact with relocation on succeed to local repository |
510 def download_artifact(path) |
511 download_file = "#{name}.#{Time.new.to_i}" |
512 begin |
513 URI.download path, download_file |
514 if File.exist?(download_file) |
515 FileUtils.mkdir_p(File.dirname(name)) |
516 FileUtils.mv(download_file, name) |
517 end |
518 ensure |
519 File.delete(download_file) if File.exist?(download_file) |
520 end |
521 end |
522 |
523 # :call-seq: |
524 # :download_needed? |
525 # |
526 # Validates whether artifact is required to be downloaded from repository |
527 def download_needed?(task) |
528 return true if !File.exist?(name) |
529 |
530 if snapshot? |
531 return false if offline? && File.exist?(name) |
532 return true if update_snapshot? || old? |
533 end |
534 |
535 return false |
536 end |
537 |
538 def update_snapshot? |
539 Buildr.application.options.update_snapshots |
540 end |
541 |
542 def offline? |
543 Buildr.application.options.work_offline |
544 end |
545 |
546 # :call-seq: |
547 # old? |
548 # |
549 # Checks whether existing artifact is older than period from build settings or one day |
550 def old? |
551 settings = Buildr.application.settings |
552 time_to_be_old = settings.user[:expire_time] || settings.build[:expire_time] || 60 * 60 * 24 |
553 File.mtime(name).to_i < (Time.new.to_i - time_to_be_old) |
554 end |
555 |
556 end |
557 |
558 |
559 # An artifact that is optional. |
560 # If downloading fails, the user will be informed but it will not raise an exception. |
561 class OptionalArtifact < Artifact |
562 |
563 protected |
564 |
565 # If downloading fails, the user will be informed but it will not raise an exception. |
566 def download |
567 super |
568 rescue |
569 info "Failed to download #{to_spec}. Skipping it." |
570 end |
571 |
572 end |
573 |
574 |
575 # Holds the path to the local repository, URLs for remote repositories, and settings for release server. |
576 # |
577 # You can access this object from the #repositories method. For example: |
578 # puts repositories.local |
579 # repositories.remote << 'http://example.com/repo' |
580 # repositories.release_to = 'sftp://example.com/var/www/public/repo' |
581 class Repositories |
582 include Singleton |
583 |
584 # :call-seq: |
585 # local => path |
586 # |
587 # Returns the path to the local repository. |
588 # |
589 # The default path is .m2/repository relative to the home directory. |
590 # You can set this using the M2_REPO environment variable or the repositories/local |
591 # value in your settings.yaml file. |
592 def local |
593 @local ||= File.expand_path(ENV['M2_REPO'] || ENV['local_repo'] || |
594 (Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['local']) || |
595 File.join(ENV['HOME'], '.m2/repository')) |
596 end |
597 |
598 # :call-seq: |
599 # local = path |
600 # |
601 # Sets the path to the local repository. |
602 # |
603 # The best place to set the local repository path is from a buildr.rb file |
604 # located in the .buildr directory under your home directory. That way all |
605 # your projects will share the same path, without affecting other developers |
606 # collaborating on these projects. |
607 def local=(dir) |
608 @local = dir ? File.expand_path(dir) : nil |
609 end |
610 |
611 # :call-seq: |
612 # locate(spec) => path |
613 # |
614 # Locates an artifact in the local repository based on its specification, and returns |
615 # a file path. |
616 # |
617 # For example: |
618 # locate :group=>'log4j', :id=>'log4j', :version=>'1.1' |
619 # => ~/.m2/repository/log4j/log4j/1.1/log4j-1.1.jar |
620 def locate(spec) |
621 spec = Artifact.to_hash(spec) |
622 File.join(local, spec[:group].split('.'), spec[:id], spec[:version], Artifact.hash_to_file_name(spec)) |
623 end |
624 |
625 # :call-seq: |
626 # remote => Array |
627 # |
628 # Returns an array of all the remote repository URLs. |
629 # |
630 # When downloading artifacts, repositories are accessed in the order in which they appear here. |
631 # The best way is to add repositories individually, for example: |
632 # repositories.remote << 'http://example.com/repo' |
633 # |
634 # You can also specify remote repositories in the settings.yaml (per user) and build.yaml (per build) |
635 # files. Both sets of URLs are loaded by default into this array, URLs from the personal setting |
636 # showing first. |
637 # |
638 # For example: |
639 # repositories: |
640 # remote: |
641 # - http://example.com/repo |
642 # - http://elsewhere.com/repo |
643 def remote |
644 unless @remote |
645 @remote = [Buildr.settings.user, Buildr.settings.build].inject([]) { |repos, hash| |
646 repos | Array(hash['repositories'] && hash['repositories']['remote']) |
647 } |
648 end |
649 @remote |
650 end |
651 |
652 # :call-seq: |
653 # remote = Array |
654 # remote = url |
655 # remote = nil |
656 # |
657 # With a String argument, clears the array and set it to that single URL. |
658 # |
659 # With an Array argument, clears the array and set it to these specific URLs. |
660 # |
661 # With nil, clears the array. |
662 def remote=(urls) |
663 case urls |
664 when nil then @remote = nil |
665 when Array then @remote = urls.dup |
666 else @remote = [urls.to_s] |
667 end |
668 end |
669 |
670 # :call-seq: |
671 # release_to = url |
672 # release_to = hash |
673 # |
674 # Specifies the release server. Accepts a Hash with different repository settings |
675 # (e.g. url, username, password), or a String to only set the repository URL. |
676 # |
677 # Besides the URL, all other settings depend on the transport protocol in use. |
678 # |
679 # For example: |
680 # repositories.release_to = 'sftp://john:secret@example.com/var/www/repo/' |
681 # |
682 # repositories.release_to = { :url=>'sftp://example.com/var/www/repo/', |
683 # :username='john', :password=>'secret' } |
684 # Or in the settings.yaml file: |
685 # repositories: |
686 # release_to: sftp://john:secret@example.com/var/www/repo/ |
687 # |
688 # repositories: |
689 # release_to: |
690 # url: sftp://example.com/var/www/repo/ |
691 # username: john |
692 # password: secret |
693 def release_to=(options) |
694 options = { :url=>options } unless Hash === options |
695 @release_to = options |
696 end |
697 |
698 # :call-seq: |
699 # release_to => hash |
700 # |
701 # Returns the current release server setting as a Hash. This is a more convenient way to |
702 # configure the settings, as it allows you to specify the settings progressively. |
703 # |
704 # For example, the Buildfile will contain the repository URL used by all developers: |
705 # repositories.release_to[:url] ||= 'sftp://example.com/var/www/repo' |
706 # Your private buildr.rb will contain your credentials: |
707 # repositories.release_to[:username] = 'john' |
708 # repositories.release_to[:password] = 'secret' |
709 def release_to |
710 unless @release_to |
711 value = (Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['release_to']) \ |
712 || (Buildr.settings.build['repositories'] && Buildr.settings.build['repositories']['release_to']) |
713 @release_to = Hash === value ? value.inject({}) { |hash, (key, value)| hash.update(key.to_sym=>value) } : { :url=>Array(value).first } |
714 end |
715 @release_to |
716 end |
717 |
718 end |
719 |
720 # :call-seq: |
721 # repositories => Repositories |
722 # |
723 # Returns an object you can use for setting the local repository path, remote repositories |
724 # URL and release server settings. |
725 # |
726 # See Repositories. |
727 def repositories |
728 Repositories.instance |
729 end |
730 |
731 # :call-seq: |
732 # artifact(spec) => Artifact |
733 # artifact(spec) { |task| ... } => Artifact |
734 # |
735 # Creates a file task to download and install the specified artifact in the local repository. |
736 # |
737 # You can use a String or a Hash for the artifact specification. The file task will point at |
738 # the artifact's path inside the local repository. You can then use this tasks as a prerequisite |
739 # for other tasks. |
740 # |
741 # This task will download and install the artifact only once. In fact, it will download and |
742 # install the artifact if the artifact does not already exist. You can enhance it if you have |
743 # a different way of creating the artifact in the local repository. See Artifact for more details. |
744 # |
745 # For example, to specify an artifact: |
746 # artifact('log4j:log4j:jar:1.1') |
747 # |
748 # To use the artifact in a task: |
749 # compile.with artifact('log4j:log4j:jar:1.1') |
750 # |
751 # To specify an artifact and the means for creating it: |
752 # download(artifact('dojo:dojo-widget:zip:2.0')=> |
753 # 'http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip') |
754 def artifact(spec, path = nil, &block) #:yields:task |
755 spec = artifact_ns.fetch(spec) if spec.kind_of?(Symbol) |
756 spec = Artifact.to_hash(spec) |
757 unless task = Artifact.lookup(spec) |
758 task = Artifact.define_task(path || repositories.locate(spec)) |
759 task.send :apply_spec, spec |
760 Rake::Task['rake:artifacts'].enhance [task] |
761 Artifact.register(task) |
762 unless spec[:type] == :pom |
763 Rake::Task['artifacts:sources'].enhance [task.sources_artifact] |
764 Rake::Task['artifacts:javadoc'].enhance [task.javadoc_artifact] |
765 end |
766 end |
767 task.enhance &block |
768 end |
769 |
770 # :call-seq: |
771 # artifacts(*spec) => artifacts |
772 # |
773 # Handles multiple artifacts at a time. This method is the plural equivalent of |
774 # #artifact, but can do more things. |
775 # |
776 # Returns an array of artifacts built using the supplied |
777 # specifications, each of which can be: |
778 # * An artifact specification (String or Hash). Returns the appropriate Artifact task. |
779 # * An artifact of any other task. Returns the task as is. |
780 # * A project. Returns all artifacts created (packaged) by that project. |
781 # * A string. Returns that string, assumed to be a file name. |
782 # * An array of artifacts or a Struct. |
783 # * A symbol. Returns the named artifact from the current ArtifactNamespace |
784 # |
785 # For example, handling a collection of artifacts: |
786 # xml = [ xerces, xalan, jaxp ] |
787 # ws = [ axis, jax-ws, jaxb ] |
788 # db = [ jpa, mysql, sqltools ] |
789 # artifacts(xml, ws, db) |
790 # |
791 # Using artifacts created by a project: |
792 # artifacts project('my-app') # All packages |
793 # artifacts project('my-app').package(:war) # Only the WAR |
794 def artifacts(*specs, &block) |
795 specs.flatten.inject([]) do |set, spec| |
796 case spec |
797 when ArtifactNamespace |
798 set |= spec.artifacts |
799 when Symbol, Hash |
800 set |= [artifact(spec)] |
801 when /([^:]+:){2,4}/ # A spec as opposed to a file name. |
802 set |= [artifact(spec)] |
803 when String # Must always expand path. |
804 set |= [File.expand_path(spec)] |
805 when Project |
806 set |= artifacts(spec.packages) |
807 when Rake::Task |
808 set |= [spec] |
809 when Struct |
810 set |= artifacts(spec.values) |
811 else |
812 fail "Invalid artifact specification in #{specs.inspect}" |
813 end |
814 end |
815 end |
816 |
817 def transitive(*specs) |
818 specs.flatten.inject([]) do |set, spec| |
819 case spec |
820 when /([^:]+:){2,4}/ # A spec as opposed to a file name. |
821 artifact = artifact(spec) |
822 set |= [artifact] unless artifact.type == :pom |
823 set |= POM.load(artifact.pom).dependencies.map { |spec| artifact(spec) } |
824 when Hash |
825 set |= [transitive(spec)] |
826 when String # Must always expand path. |
827 set |= transitive(file(File.expand_path(spec))) |
828 when Project |
829 set |= transitive(spec.packages) |
830 when Rake::Task |
831 set |= spec.respond_to?(:to_spec) ? transitive(spec.to_spec) : [spec] |
832 when Struct |
833 set |= transitive(spec.values) |
834 else |
835 fail "Invalid artifact specification in: #{specs.to_s}" |
836 end |
837 end |
838 end |
839 |
840 # :call-seq: |
841 # group(ids, :under=>group_name, :version=>number) => artifacts |
842 # |
843 # Convenience method for defining multiple artifacts that belong to the same group, type and version. |
844 # Accepts multiple artifact identifiers followed by two or three hash values: |
845 # * :under -- The group identifier |
846 # * :version -- The version number |
847 # * :type -- The artifact type (optional) |
848 # * :classifier -- The artifact classifier (optional) |
849 # |
850 # For example: |
851 # group 'xbean', 'xbean_xpath', 'xmlpublic', :under=>'xmlbeans', :version=>'2.1.0' |
852 # Or: |
853 # group %w{xbean xbean_xpath xmlpublic}, :under=>'xmlbeans', :version=>'2.1.0' |
854 def group(*args) |
855 hash = args.pop |
856 args.flatten.map do |id| |
857 artifact :group => hash[:under], |
858 :type => hash[:type], |
859 :version => hash[:version], |
860 :classifier => hash[:classifier], |
861 :id => id |
862 end |
863 end |
864 |
865 # :call-seq: |
866 # install(artifacts) => install_task |
867 # |
868 # Installs the specified artifacts in the local repository as part of the install task. |
869 # |
870 # You can use this to install various files in the local repository, for example: |
871 # install artifact('group:id:jar:1.0').from('some_jar.jar') |
872 # $ buildr install |
873 def install(*args, &block) |
874 artifacts = artifacts(args).uniq |
875 raise ArgumentError, 'This method can only install artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) } |
876 task('install').tap do |install| |
877 install.enhance(artifacts) do |
878 artifacts.each(&:install) |
879 end |
880 install.enhance &block if block |
881 task('uninstall') do |
882 artifacts.map(&:to_s ).each { |file| rm file if File.exist?(file) } |
883 end |
884 end |
885 end |
886 |
887 # :call-seq: |
888 # upload(artifacts) |
889 # |
890 # Uploads the specified artifacts to the release server as part of the upload task. |
891 # |
892 # You can use this to upload various files to the release server, for example: |
893 # upload artifact('group:id:jar:1.0').from('some_jar.jar') |
894 # $ buildr upload |
895 def upload(*args, &block) |
896 artifacts = artifacts(args) |
897 raise ArgumentError, 'This method can only upload artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) } |
898 upload_artifacts_tasks = artifacts.map { |artifact| artifact.upload_task } |
899 task('upload').tap do |task| |
900 task.enhance &block if block |
901 task.enhance upload_artifacts_tasks |
902 end |
903 end |
904 |
905 end |
Generated on 2011-07-06 23:35:38 -0700 with rcov 0.9.8