jakub holý

building the right thing, building it right, fast

How to patch Travis CI''s deployment tool for your needs

2018-01-09Tools

Travis CI is a pretty good software-as-a-service Continuous Integration server. It can deploy to many targets, including AWS BeanStalk, S3, and CodeDeploy.

However it might happen that the deploy tool (dpl) has a missing feature or doesn't do exactly what you need. Fortunately it is easy to fix and run a modified version of the tool, and I will show you how to do that.



My particular problem is that dpl allows me to upload an archive to S3 and to create a CodeDeploy deployment, but it lacks the intermediary and required step of registering a new revision based on the uploaded archive. (See #732)

To fix this:

  1. Subclass and fix the deployer
  2. Send a pull request to Travis CI :-)
  3. Run it as a Ruby script


Here is an example of fixing the code_deploy.rb to fetch S3 version and etag and register a revision before proceeding with creating the deployment:
# Modified Travis-CI deployment tool (dpl) provider for AWS CodeDeploy
# that does also correctly register the revision
require 'dpl/cli'
require 'dpl/provider'
require 'dpl/provider/code_deploy'
require 'time'
module DPL
class Provider
class CodeDeployWithRegister < DPL::Provider::CodeDeploy
experimental 'AWS Code Deploy With Register'
def revision_version_info
s3api = ::Aws::S3::Client.new(code_deploy_options) # region, credentials same for all services
s3obj = s3api.get_object({
bucket: option(:bucket),
key: s3_key,
range: "bytes=0-1"
})
end
def s3_revision
s3info = revision_version_info
{
revision_type: 'S3',
s3_location: {
bucket: option(:bucket),
bundle_type: bundle_type,
key: s3_key,
version: s3info[:version_id],
e_tag: s3info[:etag]
}
}
end
def push_app
rev = revision()
if rev[:s3_location]
revInfo = rev[:s3_location]
log "Registering app revision with version=#{revInfo[:version]}, etag=#{revInfo[:e_tag]}"
end
code_deploy.register_application_revision({
revision: rev,
application_name: options[:application] || option(:application_name),
description: options[:description] || default_description
})
super
end
end
end
end
# EXPECTED ARGUMENTS:
# --bucket=..,
# key, application, deployment_group,
# optionally --region etc
args = [
"--provider=codedeploywithregister"].concat(ARGV)
DPL::CLI.run(args)
# To test this from the command line:
# ruby --bucket=my-bucket --key=myapp.zip --application=my-cd-app --deployment_group=my-cd-group-staging \
# --region=eu-west-1 --skip_cleanup
view raw deploy_codedeploy.rb hosted with ❤ by GitHub


As shown in the code, you can run it from the command line. To run it from your Travis CI build:

# excerpt from .travis.yml
deploy:
  - provider: s3
    local_dir: dpl_cd_upload
    skip_cleanup: true
    bucket: my-bucket 
    region: eu-west-1
  - provider: script
    script: ruby ./deploy_codedeploy.rb
        --bucket=my-bucket 
        --key=myapp.zip
        --application=my-cd-app
        --deployment_group=my-cd-group-staging
        --region=eu-west-1


Enjoy!

PS: Or you can fork dpl and use your forked one, as described in Testing dpl in the context of Travis CI builds. This builds the whole dpl gem on the build VM.