class Lita::Handlers::CapistranoRails

Public Instance Methods

define_routes(payload) click to toggle source
# File lib/lita/handlers/capistrano_rails.rb, line 26
def define_routes(payload)
  define_static_routes
  define_dinamic_routes
end
deploy_env_for_app(response) click to toggle source

command: “deploy #{env} for #{app}”

# File lib/lita/handlers/capistrano_rails.rb, line 42
def deploy_env_for_app(response)
  env = response.matches[0][0]
  app = response.matches[0][1]
  deploy_app(app, env, response)
end
deploy_list_apps(response) click to toggle source

command: “deploy list”

# File lib/lita/handlers/capistrano_rails.rb, line 32
def deploy_list_apps(response)
  response.reply_privately('Available apps:')
  apps = config.apps.map do |app, app_config|
    envs = app_config[:envs] ? app_config[:envs].keys.join(",") : 'production'
    "#{app}(#{envs})"
  end
  response.reply_privately(apps )
end
deploy_production_for_app(response) click to toggle source

command: “deploy #{app}”

# File lib/lita/handlers/capistrano_rails.rb, line 49
def deploy_production_for_app(response)
  env = 'production'
  app = response.matches[0][0]
  deploy_app(app, env, response)
end

Private Instance Methods

define_dinamic_routes() click to toggle source

define route for each rapporteur

# File lib/lita/handlers/capistrano_rails.rb, line 206
def define_dinamic_routes
  config.apps.each do |app, app_config|
    # define command "deploy APP"
    self.class.route(
      %r{deploy (#{app})$},
      :deploy_production_for_app,
      command: true,
      # restrict_to: [:admins, value[:deploy_group]],
      help: { "deploy #{app}" => "deploy produciton for #{app}"}
    )

    # define command "deploy ENV for APP"
    # puts "define route: ^deploy\s+(#{app})\s+(#{area})\s+(.+)\s+(.+)"
    envs = app_config[:envs] ? app_config[:envs].keys : ['production']
    self.class.route(
      %r{deploy +(\w+) +for +(#{app})$},
      :deploy_env_for_app,
      command: true,
      # restrict_to: [:admins, value[:deploy_group]],
      help: { "deploy #{envs.join("|")} for #{app}" => "deploy ENV for #{app}"}
    )
  end
end
define_static_routes() click to toggle source
# File lib/lita/handlers/capistrano_rails.rb, line 196
def define_static_routes
  self.class.route(
    %r{deploy\s+list},
    :deploy_list_apps,
    command: true,
    help: { "deploy list" => "List available apps for deploy"}
  )
end
deploy_app(app, env, response) click to toggle source
# File lib/lita/handlers/capistrano_rails.rb, line 57
def deploy_app(app, env, response)
  app_config = config.apps[app]

  # check env
  branch = \
  step :validate_deploy_request, :first_step do
    app_envs = app_config[:envs] ? app_config[:envs].keys : ['production'] # use "production" as default env
    if !app_envs.include?(env)
      return response.reply(%{"#{env}" is not available env for #{app}, available env is: #{app_envs.join("|")}})
    end

    branch = app_config[:envs] ? app_config[:envs][env] : 'master' # use "master" as default branch

    response.reply("I'm deploying #{env} using #{branch} branch for #{app} ...")
    branch
  end

  app_path, app_source_path, bundle_path = \
  step :prepare_tmp_dirs do
    # prepare the dirs
    app_path    = File.expand_path("tmp/capistrano_rails/apps/#{app}")
    app_source_path = File.expand_path("#{app_path}/source")
    bundle_path     = File.expand_path("tmp/capistrano_rails/bundle") # sharing bundle to reduce space usage and reuse gems
    FileUtils.mkdir_p(app_path)
    [app_path, app_source_path, bundle_path]
  end

  step :get_source_code do
    # if dir ".git" exists, use `git pull`
    if File.exists?("#{app_source_path}/.git")
      step_log 'Found ".git" exists, run `git pull`'
      # run_in_dir("git checkout #{branch} && git pull", app_source_path)
      revision = "origin/#{branch}"
      # since we're in a local branch already, just reset to specified revision rather than merge
      run_in_dir("git fetch #{verbose} && git reset #{verbose} --hard #{revision}", app_source_path)
    else
      step_log 'not found ".git", run `git clone`'
      run_in_dir("git clone -b #{branch} #{app_config[:git]} source", app_path)
    end

    # TODO: if ".git" exists but repo url changed, clean up the dir then run `git clone`
  end

  step :hack_gemfile do
    # disable "ruby 'x.y.z'" config in Gemfile
    # to avoid errors like "Your Ruby version is 2.2.4, but your Gemfile specified 2.1.2 (Bundler::RubyVersionMismatch)"
    gemfile_path = "#{app_source_path}/Gemfile"
    gemfile_content = File.read gemfile_path
    if gemfile_content.match(/^ruby .+/)
      gemfile_content.sub!(/(^ruby .+)/, '# \1 # hacked by Lita::Handlers::CapistranoRails to avoid "Bundler::RubyVersionMismatch" problem')
      File.open(gemfile_path, 'w') { |gemfile| gemfile.write(gemfile_content) }
    end
  end

  step :run_bundle_install do
    # run_in_dir("bundle install --quiet", app_source_path)
    run_in_dir("bundle install --gemfile #{app_source_path}/Gemfile --path #{bundle_path} --deployment --without darwin test", app_source_path)
    # TODO: check result, reply failure message
    # TODO: check result, auto retry 1 time if timeout as "Gem::RemoteFetcher::FetchError: Errno::ETIMEDOUT: Operation timed out"
  end

  step :run_cap_deploy do
    # TODO: check "Capfile"
    # if "Capfile" not exits, reply "not a capistrano project"
    result = run_in_dir("bundle exec cap #{env} deploy", app_source_path)
    if result[:exit_status] != 0 # could be 256
      failure = %{errrors while executing: "#{result[:cmd]}"\n}
      failure << result[:stderr]
      step_fails(response, failure)
    end
  end

  step :reply_deploy_success, :last_step do
    deploy_success = "deploy #{env} for #{app} finished!"

    # TODO: get "app_url"
    app_url = ""
    deploy_success += ", please visit #{app_url}" if !app_url.empty?

    response.reply(deploy_success)
  end
end
execute_command(cmd) click to toggle source

execute commandline and catch stdout and stderr

# File lib/lita/handlers/capistrano_rails.rb, line 153
def execute_command(cmd)
  log.debug "execute: #{cmd}"
  full_stdout = String.new
  full_stderr = String.new
  started_at  = Time.now

  Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
    stdout_thread = Thread.new do
      while (line = stdout.gets) do
        full_stdout += line
        # printf line
      end
    end

    stderr_thread = Thread.new do
      while (line = stderr.gets) do
        full_stderr += line
      end
    end

    stdout_thread.join
    stderr_thread.join

    exit_status = wait_thr.value.to_i
    finished_at = Time.now
    runtime     = finished_at - started_at

    {
      cmd:          cmd,
      started_at:   started_at,
      finished_at:  finished_at,
      exit_status:  exit_status,
      runtime:      runtime,
      stdout:       full_stdout,
      stderr:       full_stderr,
    }
  end
end
run_in_dir(cmd, dir) click to toggle source
# File lib/lita/handlers/capistrano_rails.rb, line 140
def run_in_dir(cmd, dir)
  lita_mark = "LITA=#{Lita::VERSION}"
  _cmd = "cd #{dir} && #{lita_mark} #{cmd}"
  # log.info _cmd
  # running bundle install inside a bundle-managed shell to avoid "RuntimeError: Specs already loaded"
  # see https://github.com/bundler/bundler/issues/1981#issuecomment-6292686
  Bundler.with_clean_env do
    # system(_cmd) # can't catch output
    execute_command(_cmd)
  end
end
verbose() click to toggle source
# File lib/lita/handlers/capistrano_rails.rb, line 192
def verbose
  # config.scm_verbose ? nil : "-q"
end