lib/capistrano-maven.rb in capistrano-maven-0.0.7 vs lib/capistrano-maven.rb in capistrano-maven-0.1.0

- old
+ new

@@ -7,279 +7,341 @@ module Capistrano module Maven def self.extended(configuration) configuration.load { namespace(:mvn) { - _cset(:mvn_version, '3.0.4') - _cset(:mvn_major_version) { - mvn_version.split('.').first.to_i - } + _cset(:mvn_roles, [:app]) + _cset(:mvn_version, "3.0.5") + _cset(:mvn_major_version) { mvn_version.split(".").first.to_i } _cset(:mvn_archive_url) { "http://www.apache.org/dist/maven/maven-#{mvn_major_version}/#{mvn_version}/binaries/apache-maven-#{mvn_version}-bin.tar.gz" } - _cset(:mvn_archive_file) { - File.join(shared_path, 'tools', 'mvn', File.basename(URI.parse(mvn_archive_url).path)) - } - _cset(:mvn_archive_file_local) { - File.join(File.expand_path('.'), 'tools', 'mvn', File.basename(URI.parse(mvn_archive_url).path)) - } - _cset(:mvn_checksum_url) { - "#{mvn_archive_url}.md5" - } - _cset(:mvn_checksum_file) { - File.join(shared_path, 'tools', 'mvn', File.basename(URI.parse(mvn_checksum_url).path)) - } - _cset(:mvn_checksum_file_local) { - File.join(File.expand_path('.'), 'tools', 'mvn', File.basename(URI.parse(mvn_checksum_url).path)) - } - _cset(:mvn_checksum_cmd) { - case File.extname(File.basename(URI.parse(mvn_checksum_url).path)) - when '.md5' then 'md5sum' - when '.sha1' then 'sha1sum' + _cset(:mvn_tools_path) { File.join(shared_path, "tools", "mvn") } + _cset(:mvn_tools_path_local) { File.expand_path("tools/mvn") } + _cset(:mvn_archive_path) { mvn_tools_path } + _cset(:mvn_archive_path_local) { mvn_tools_path_local } + _cset(:mvn_archive_file) { File.join(mvn_archive_path, File.basename(URI.parse(mvn_archive_url).path)) } + _cset(:mvn_archive_file_local) { File.join(mvn_archive_path_local, File.basename(URI.parse(mvn_archive_url).path)) } + _cset(:mvn_path) { File.join(mvn_tools_path, File.basename(URI.parse(mvn_archive_url).path, "-bin.tar.gz")) } + _cset(:mvn_path_local) { File.join(mvn_tools_path_local, File.basename(URI.parse(mvn_archive_url).path, "-bin.tar.gz")) } + _cset(:mvn_bin_path) { File.join(mvn_path, "bin") } + _cset(:mvn_bin_path_local) { File.join(mvn_path_local, "bin") } + _cset(:mvn_bin) { File.join(mvn_bin_path, "mvn") } + _cset(:mvn_bin_local) { File.join(mvn_bin_path_local, "mvn") } + _cset(:mvn_project_path) { release_path } + _cset(:mvn_project_path_local) { File.expand_path(".") } + _cset(:mvn_template_path) { File.expand_path("config/templates") } + + ## Maven environment + _cset(:mvn_common_environment, {}) + _cset(:mvn_default_environment) { + environment = {} + environment["JAVA_HOME"] = fetch(:mvn_java_home) if exists?(:mvn_java_home) + if exists?(:mvn_java_options) + environment["MAVEN_OPTS"] = [ fetch(:mvn_java_options, []) ].flatten.join(" ") end + environment["PATH"] = [ mvn_bin_path, "$PATH" ].join(":") if mvn_setup_remotely + _merge_environment(mvn_common_environment, environment) } - _cset(:mvn_path) { - File.join(shared_path, 'tools', 'mvn', File.basename(URI.parse(mvn_archive_url).path, "-bin.tar.gz")) - } - _cset(:mvn_path_local) { - File.join(File.expand_path('.'), 'tools', 'mvn', File.basename(URI.parse(mvn_archive_url).path, "-bin.tar.gz")) - } - _cset(:mvn_bin) { - File.join(mvn_path, 'bin', 'mvn') - } - _cset(:mvn_bin_local) { - File.join(mvn_path_local, 'bin', 'mvn') - } - _cset(:mvn_cmd) { - if fetch(:mvn_java_home, nil) - "env JAVA_HOME=#{mvn_java_home} #{mvn_bin} #{mvn_options.join(' ')}" - else - "#{mvn_bin} #{mvn_options.join(' ')}" + _cset(:mvn_default_environment_local) { + environment = {} + environment["JAVA_HOME"] = fetch(:mvn_java_home_local) if exists?(:mvn_java_home_local) + if exists?(:mvn_java_options_local) + environment["MAVEN_OPTS"] = [ fetch(:mvn_java_options_local, []) ].flatten.join(" ") end + environment["PATH"] = [ mvn_bin_path_local, "$PATH" ].join(":") if mvn_setup_locally + _merge_environment(mvn_common_environment, environment) } - _cset(:mvn_cmd_local) { - if fetch(:mvn_java_home_local, nil) - "env JAVA_HOME=#{mvn_java_home_local} #{mvn_bin_local} #{mvn_options_local.join(' ')}" + _cset(:mvn_environment) { _merge_environment(mvn_default_environment, fetch(:mvn_extra_environment, {})) } + _cset(:mvn_environment_local) { _merge_environment(mvn_default_environment_local, fetch(:mvn_extra_environment_local, {})) } + def _command(cmdline, options={}) + environment = options.fetch(:env, {}) + if environment.empty? + cmdline else - "#{mvn_bin_local} #{mvn_options_local.join(' ')}" + env = (["env"] + environment.map { |k, v| "#{k}=#{v.dump}" }).join(" ") + "#{env} #{cmdline}" end - } - _cset(:mvn_project_path) { - release_path - } - _cset(:mvn_project_path_local) { - Dir.pwd - } - _cset(:mvn_target_path) { - File.join(mvn_project_path, 'target') - } - _cset(:mvn_target_path_local) { - File.join(mvn_project_path_local, File.basename(mvn_target_path)) - } - _cset(:mvn_template_path, File.join(File.dirname(__FILE__), 'templates')) - _cset(:mvn_update_settings, false) - _cset(:mvn_update_settings_locally, false) - _cset(:mvn_settings_path) { mvn_project_path } - _cset(:mvn_settings_path_local) { mvn_project_path_local } - _cset(:mvn_settings, %w(settings.xml)) - _cset(:mvn_settings_local, %w(settings.xml)) - _cset(:mvn_cleanup_settings, []) - _cset(:mvn_cleanup_settings_local, []) - _cset(:mvn_compile_locally, false) # perform precompilation on localhost + end + def command(cmdline, options={}) + _command(cmdline, :env => mvn_environment.merge(options.fetch(:env, {}))) + end + def command_local(cmdline, options={}) + _command(cmdline, :env => mvn_environment_local.merge(options.fetch(:env, {}))) + end + _cset(:mvn_cmd) { command("#{mvn_bin.dump} #{mvn_options.map { |x| x.dump }.join(" ")}") } + _cset(:mvn_cmd_local) { command_local("#{mvn_bin_local.dump} #{mvn_options_local.map { |x| x.dump }.join(" ")}") } _cset(:mvn_goals, %w(clean package)) _cset(:mvn_common_options) { options = [] - options << "-P#{mvn_profiles.join(',')}" unless fetch(:mvn_profiles, []).empty? + options << "-P#{mvn_profiles.join(",")}" unless fetch(:mvn_profiles, []).empty? options << "-Dmaven.test.skip=true" if fetch(:mvn_skip_tests, false) options << "-U" if fetch(:mvn_update_snapshots, false) options << "-B" options } - _cset(:mvn_options) { - options = mvn_common_options + fetch(:mvn_extra_options, []) - if mvn_update_settings - settings = File.join(mvn_settings_path, mvn_settings.first) - options << "--settings=#{settings}" - end + _cset(:mvn_default_options) { + options = mvn_common_options.dup + options += mvn_settings.map { |s| "--settings=#{File.join(mvn_settings_path, s).dump}" } if mvn_update_settings options } - _cset(:mvn_options_local) { - options = mvn_common_options + fetch(:mvn_extra_options_local, []) - if mvn_update_settings_locally - settings = File.join(mvn_settings_path_local, mvn_settings_local.first) - options << "--settings=#{settings}" - end + _cset(:mvn_default_options_local) { + options = mvn_common_options.dup + options += mvn_settings_local.map { |s| "--settings=#{File.join(mvn_settings_path_local, s).dump}" } if mvn_update_settings_locally options } + _cset(:mvn_options) { mvn_default_options + fetch(:mvn_extra_options, []) } + _cset(:mvn_options_local) { mvn_default_options_local + fetch(:mvn_extra_options_local, []) } - desc("Setup maven.") - task(:setup, :roles => :app, :except => { :no_release => true }) { - transaction { - install - update_settings if mvn_update_settings - setup_locally if mvn_compile_locally - } + _cset(:mvn_setup_remotely) { mvn_update_remotely } + _cset(:mvn_setup_locally) { mvn_update_locally } + _cset(:mvn_update_remotely) { not(mvn_update_locally) } + _cset(:mvn_update_locally) { # perform update on localhost + if exists?(:mvn_compile_locally) + logger.info(":mvn_compile_locally has been deprecated. use :mvn_update_locally instead.") + fetch(:mvn_compile_locally, false) + else + false + end } - after 'deploy:setup', 'mvn:setup' - desc("Setup maven locally.") - task(:setup_locally, :except => { :no_release => true }) { - transaction { - install_locally - update_settings_locally if mvn_update_settings_locally + if top.namespaces.key?(:multistage) + after "multistage:ensure", "mvn:setup_default_environment" + else + on :start do + if top.namespaces.key?(:multistage) + after "multistage:ensure", "mvn:setup_default_environment" + else + setup_default_environment + end + end + end + + _cset(:mvn_environment_join_keys, %w(DYLD_LIBRARY_PATH LD_LIBRARY_PATH MANPATH PATH)) + def _merge_environment(x, y) + x.merge(y) { |key, x_val, y_val| + if mvn_environment_join_keys.include?(key) + ( y_val.split(":") + x_val.split(":") ).uniq.join(":") + else + y_val + end } + end + + task(:setup_default_environment, :roles => mvn_roles, :except => { :no_release => true }) { + if fetch(:mvn_setup_default_environment, true) + set(:default_environment, _merge_environment(default_environment, mvn_environment)) + end } - def _validate_archive(archive_file, checksum_file) - if cmd = fetch(:mvn_checksum_cmd, nil) - "test `#{cmd} #{archive_file} | cut -d' ' -f1` = `cat #{checksum_file}`" + def _invoke_command(cmdline, options={}) + if options[:via] == :run_locally + run_locally(cmdline) else - "true" + invoke_command(cmdline, options) end end - def _install(options={}) - path = options.delete(:path) - bin = options.delete(:bin) - checksum_file = options.delete(:checksum_file) - checksum_url = options.delete(:checksum_url) - archive_file = options.delete(:archive_file) - archive_url = options.delete(:archive_url) - dirs = [ File.dirname(checksum_file), File.dirname(archive_file), File.dirname(path) ].uniq() + def _download(uri, filename, options={}) + options = fetch(:mvn_download_options, {}).merge(options) + if FileTest.exist?(filename) + logger.info("Found downloaded archive: #{filename}") + else + dirs = [ File.dirname(filename) ] + execute = [] + execute << "mkdir -p #{dirs.uniq.map { |x| x.dump }.join(" ")}" + execute << "wget --no-verbose -O #{filename.dump} #{uri.dump}" + _invoke_command(execute.join(" && "), options) + end + end + + def _upload(filename, remote_filename, options={}) + _invoke_command("mkdir -p #{File.dirname(remote_filename).dump}", options) + transfer_if_modified(:up, filename, remote_filename, fetch(:mvn_upload_options, {}).merge(options)) + end + + def _install(filename, destination, options={}) execute = [] - execute << "mkdir -p #{dirs.join(' ')}" - execute << (<<-EOS).gsub(/\s+/, ' ').strip - if ! test -f #{archive_file}; then - ( rm -f #{checksum_file}; wget --no-verbose -O #{checksum_file} #{checksum_url} ) && - wget --no-verbose -O #{archive_file} #{archive_url} && - #{_validate_archive(archive_file, checksum_file)} || ( rm -f #{archive_file}; false ) && - test -f #{archive_file}; - fi - EOS - execute << (<<-EOS).gsub(/\s+/, ' ').strip - if ! test -x #{bin}; then - ( test -d #{path} || tar xf #{archive_file} -C #{File.dirname(path)} ) && - test -x #{bin}; - fi - EOS - execute.join(' && ') + execute << "mkdir -p #{File.dirname(destination).dump}" + execute << "tar xf #{filename.dump} -C #{File.dirname(destination).dump}" + _invoke_command(execute.join(" && "), options) end - task(:install, :roles => :app, :except => { :no_release => true }) { - run(_install(:path => mvn_path, :bin => mvn_bin, - :checksum_file => mvn_checksum_file, :checksum_url => mvn_checksum_url, - :archive_file => mvn_archive_file, :archive_url => mvn_archive_url)) - run("#{mvn_cmd} --version") + def _installed?(destination, options={}) + mvn = File.join(destination, "bin", "mvn") + cmdline = "test -d #{destination.dump} && test -x #{mvn.dump}" + _invoke_command(cmdline, options) + true + rescue + false + end + + ## setup + desc("Setup maven.") + task(:setup, :roles => mvn_roles, :except => { :no_release => true }) { + transaction { + setup_remotely if mvn_setup_remotely + setup_locally if mvn_setup_locally + } } + after "deploy:setup", "mvn:setup" - task(:install_locally, :except => { :no_release => true }) { - run_locally(_install(:path => mvn_path_local, :bin => mvn_bin_local, - :checksum_file => mvn_checksum_file_local, :checksum_url => mvn_checksum_url, - :archive_file => mvn_archive_file_local, :archive_url => mvn_archive_url)) - run_locally("#{mvn_cmd_local} --version") + task(:setup_remotely, :roles => mvn_roles, :except => { :no_release => true }) { + _download(mvn_archive_url, mvn_archive_file_local, :via => :run_locally) + _upload(mvn_archive_file_local, mvn_archive_file) + unless _installed?(mvn_path) + _install(mvn_archive_file, mvn_path) + _installed?(mvn_path) + end + update_settings if mvn_update_settings } - task(:update_settings, :roles => :app, :except => { :no_release => true }) { - mvn_settings.each do |f| - safe_put(template(f, :path => mvn_template_path), File.join(mvn_settings_path, f)) + desc("Setup maven locally.") + task(:setup_locally, :roles => mvn_roles, :except => { :no_release => true }) { + _download(mvn_archive_url, mvn_archive_file_local, :via => :run_locally) + unless _installed?(mvn_path_local, :via => :run_locally) + _install(mvn_archive_file_local, mvn_path_local, :via => :run_locally) + _installed?(mvn_path_local, :via => :run_locally) end - run("rm -f #{mvn_cleanup_settings.map { |x| x.dump }.join(' ')}") unless mvn_cleanup_settings.empty? + update_settings_locally if mvn_update_settings_locally } - task(:update_settings_locally, :except => { :no_release => true }) { - mvn_settings_local.each do |f| - File.write(File.join(mvn_settings_path_local, f), template(f, :path => mvn_template_path)) + _cset(:mvn_update_settings) { mvn_setup_remotely and not(mvn_settings.empty?) } + _cset(:mvn_update_settings_locally) { mvn_setup_locally and not(mvn_settings_local.empty?) } + _cset(:mvn_settings_path) { mvn_tools_path } + _cset(:mvn_settings_path_local) { mvn_tools_path_local } + _cset(:mvn_settings, []) + _cset(:mvn_settings_local) { mvn_settings } + task(:update_settings, :roles => mvn_roles, :except => { :no_release => true }) { + mvn_settings.each do |file| + safe_put(template(file, :path => mvn_template_path), File.join(mvn_settings_path, file)) end - run_locally("rm -f #{mvn_cleanup_settings_local.map { |x| x.dump }.join(' ')}") unless mvn_cleanup_settings_local.empty? } + task(:update_settings_locally, :roles => mvn_roles, :except => { :no_release => true }) { + mvn_settings_local.each do |file| + destination = File.join(mvn_settings_path_local, file) + run_locally("mkdir -p #{File.dirname(destination).dump}") + File.write(destination, template(file, :path => mvn_template_path)) + end + } + + ## update desc("Update maven build.") - task(:update, :roles => :app, :except => { :no_release => true }) { + task(:update, :roles => mvn_roles, :except => { :no_release => true }) { transaction { - if mvn_compile_locally - update_locally - else - execute - end + update_remotely if mvn_update_remotely + update_locally if mvn_update_locally } } - after 'deploy:finalize_update', 'mvn:update' + _cset(:mvn_update_hook_type, :after) + _cset(:mvn_update_hook, "deploy:finalize_update") + on(:start) do + [ mvn_update_hook ].flatten.each do |hook| + send(mvn_update_hook_type, hook, "mvn:update") if hook + end + end + task(:update_remotely, :roles => mvn_roles, :except => { :no_release => true }) { + execute_remotely + } + desc("Update maven build locally.") - task(:update_locally, :except => { :no_release => true }) { - transaction { - execute_locally - upload_locally - } + task(:update_locally, :roles => mvn_roles, :except => { :no_release => true }) { + execute_locally + upload_locally } - def _mvn(cmd, path, goals=[]) - "cd #{path.dump} && #{cmd} #{goals.map { |s| s.dump }.join(' ')}" - end - - def _mvn_parse_version(s) + def _parse_project_version(s) # FIXME: is there any better way to get project version? s.split(/(?:\r?\n)+/).reject { |line| /^\[[A-Z]+\]/ =~ line }.last end - _cset(:mvn_release_build, false) - _cset(:mvn_snapshot_pattern, /-SNAPSHOT$/i) _cset(:mvn_project_version) { - _mvn_parse_version(capture(_mvn(mvn_cmd, mvn_project_path, %w(-Dexpression=project.version help:evaluate)))) + _parse_project_version(mvn.exec(%w(-Dexpression=project.version help:evaluate), :via => :capture)) } _cset(:mvn_project_version_local) { - _mvn_parse_version(run_locally(_mvn(mvn_cmd_local, mvn_project_path_local, %w(-Dexpression=project.version help:evaluate)))) + _parse_project_version(mvn.exec_locally(%w(-Dexpression=project.version help:evaluate), :via => :capture_locally)) } - - def _validate_project_version(version_key) - if mvn_release_build - version = fetch(version_key) - if mvn_snapshot_pattern === version - abort("Skip to build project since \`#{version}' is a SNAPSHOT version.") - end + _cset(:mvn_snapshot_pattern, /-SNAPSHOT$/i) + def _validate_project_version(key) + if fetch(:mvn_release_build, false) + version = fetch(key) + abort("Skip to build project since \`#{version}' is a SNAPSHOT version.") if mvn_snapshot_pattern === version end end desc("Perform maven build.") - task(:execute, :roles => :app, :except => { :no_release => true }) { - on_rollback { - run(_mvn(mvn_cmd, mvn_project_path, %w(clean))) - } + task(:execute, :roles => mvn_roles, :except => { :no_release => true }) { + execute_remotely + } + task(:execute_remotely, :roles => mvn_roles, :except => { :no_release => true }) { + on_rollback do + mvn.exec("clean") + end _validate_project_version(:mvn_project_version) - run(_mvn(mvn_cmd, mvn_project_path, mvn_goals)) + mvn.exec(mvn_goals) } desc("Perform maven build locally.") - task(:execute_locally, :roles => :app, :except => { :no_release => true }) { - on_rollback { - run_locally(_mvn(mvn_cmd_local, mvn_project_path_local, %w(clean))) - } + task(:execute_locally, :roles => mvn_roles, :except => { :no_release => true }) { + on_rollback do + mvn.exec_locally("clean") + end _validate_project_version(:mvn_project_version_local) - cmdline = _mvn(mvn_cmd_local, mvn_project_path_local, mvn_goals) - logger.info(cmdline) - abort("execution failure") unless system(cmdline) + mvn.exec_locally(mvn_goals) } - _cset(:mvn_tar, 'tar') - _cset(:mvn_tar_local, 'tar') - _cset(:mvn_target_archive) { - "#{mvn_target_path}.tar.gz" - } - _cset(:mvn_target_archive_local) { - "#{mvn_target_path_local}.tar.gz" - } - task(:upload_locally, :roles => :app, :except => { :no_release => true }) { - on_rollback { - run("rm -rf #{mvn_target_path} #{mvn_target_archive}") - } + _cset(:mvn_target_path) { File.join(mvn_project_path, "target") } + _cset(:mvn_target_path_local) { File.join(mvn_project_path_local, "target") } + task(:upload_locally, :roles => mvn_roles, :except => { :no_release => true }) { + on_rollback do + run("rm -rf #{mvn_target_path.dump}") + end + filename = "#{mvn_target_path_local}.tar.gz" + remote_filename = "#{mvn_target_path}.tar.gz" begin - run_locally("cd #{File.dirname(mvn_target_path_local)} && #{mvn_tar_local} chzf #{mvn_target_archive_local} #{File.basename(mvn_target_path_local)}") - upload(mvn_target_archive_local, mvn_target_archive) - run("cd #{File.dirname(mvn_target_path)} && #{mvn_tar} xzf #{mvn_target_archive} && rm -f #{mvn_target_archive}") + run_locally("cd #{File.dirname(mvn_target_path_local).dump} && tar chzf #{filename.dump} #{File.basename(mvn_target_path_local).dump}") + run("mkdir -p #{File.dirname(mvn_target_path).dump}") + top.upload(filename, remote_filename) + run("cd #{File.dirname(mvn_target_path).dump} && tar xzf #{remote_filename.dump}") ensure - run_locally("rm -f #{mvn_target_archive_local}") + run("rm -f #{remote_filename.dump}") rescue nil + run_locally("rm -f #{filename.dump}") rescue nil end } + + def _exec_command(args=[], options={}) + args = [ args ].flatten + mvn = options.fetch(:mvn, "mvn") + execute = [] + execute << "cd #{options[:path].dump}" if options.key?(:path) + execute << "#{mvn} #{args.map { |x| x.dump }.join(" ")}" + execute.join(" && ") + end + + ## public methods + def exec(args=[], options={}) + cmdline = _exec_command(args, { :path => mvn_project_path, :mvn => mvn_cmd, :via => :run }.merge(options)) + _invoke_command(cmdline, options) + end + + def exec_locally(args=[], options={}) + via = options.delete(:via) + cmdline = _exec_command(args, { :path => mvn_project_path_local, :mvn => mvn_cmd_local, :via => :run_locally }.merge(options)) + if via == :capture_locally + _invoke_command(cmdline, options.merge(:via => :run_locally)) + else + logger.trace("executing locally: #{cmdline.dump}") + elapsed = Benchmark.realtime do + system(cmdline) + end + if $?.to_i > 0 # $? is command exit code (posix style) + raise Capistrano::LocalArgumentError, "Command #{cmd} returned status code #{$?}" + end + logger.trace "command finished in #{(elapsed * 1000).round}ms" + end + end } } end end end