shipitron 1.2.0 → 1.4.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +94 -0
  3. data/.dockerignore +1 -0
  4. data/.gitattributes +1 -0
  5. data/.gitignore +1 -0
  6. data/.rspec +1 -0
  7. data/Deskfile +15 -0
  8. data/Dockerfile +40 -15
  9. data/Dockerfile.release +29 -6
  10. data/Dockerfile.staging +70 -0
  11. data/Gemfile +2 -0
  12. data/README.md +53 -15
  13. data/docker-compose.yml +54 -0
  14. data/lib/shipitron.rb +19 -2
  15. data/lib/shipitron/cli.rb +42 -42
  16. data/lib/shipitron/client.rb +9 -0
  17. data/lib/shipitron/client/bootstrap_application.rb +1 -0
  18. data/lib/shipitron/client/create_ecs_services.rb +7 -7
  19. data/lib/shipitron/client/deploy_application.rb +2 -0
  20. data/lib/shipitron/client/ensure_deploy_not_running.rb +2 -1
  21. data/lib/shipitron/client/fetch_clusters.rb +1 -0
  22. data/lib/shipitron/client/force_deploy.rb +1 -0
  23. data/lib/shipitron/client/generate_deploy.rb +41 -0
  24. data/lib/shipitron/client/load_application_config.rb +17 -0
  25. data/lib/shipitron/client/load_templates.rb +1 -0
  26. data/lib/shipitron/client/register_ecs_task_definitions.rb +5 -5
  27. data/lib/shipitron/client/run_ecs_tasks.rb +101 -72
  28. data/lib/shipitron/docker_image.rb +4 -1
  29. data/lib/shipitron/find_docker_volume_name.rb +68 -0
  30. data/lib/shipitron/git_info.rb +57 -0
  31. data/lib/shipitron/mustache_yaml_parser.rb +12 -8
  32. data/lib/shipitron/s3_copy.rb +46 -0
  33. data/lib/shipitron/server/deploy_application.rb +2 -0
  34. data/lib/shipitron/server/docker/build_image.rb +4 -1
  35. data/lib/shipitron/server/docker/configure.rb +46 -10
  36. data/lib/shipitron/server/docker/push_image.rb +3 -0
  37. data/lib/shipitron/server/docker/run_build_script.rb +17 -10
  38. data/lib/shipitron/server/download_build_cache.rb +11 -3
  39. data/lib/shipitron/server/fetch_deploy.rb +50 -0
  40. data/lib/shipitron/server/git/clone_local_copy.rb +4 -5
  41. data/lib/shipitron/server/git/update_cache.rb +2 -1
  42. data/lib/shipitron/server/run_post_build.rb +40 -1
  43. data/lib/shipitron/server/transform_cli_args.rb +4 -0
  44. data/lib/shipitron/server/update_ecs_task_definitions.rb +2 -1
  45. data/lib/shipitron/server/upload_build_cache.rb +10 -9
  46. data/lib/shipitron/version.rb +1 -1
  47. data/scripts/docker-entrypoint.sh +37 -3
  48. data/scripts/release-entrypoint.sh +20 -0
  49. data/shipitron.gemspec +11 -8
  50. data/test.yml +5 -0
  51. metadata +78 -25
  52. data/build_dev.sh +0 -27
@@ -31,6 +31,8 @@ module Shipitron
31
31
  optional :build_script
32
32
  optional :post_builds
33
33
  optional :repository_branch
34
+ optional :skip_push, default: false
35
+ optional :registry
34
36
 
35
37
  around do |interactor|
36
38
  if ENV['CONSUL_HOST'].nil?
@@ -12,7 +12,10 @@ module Shipitron
12
12
 
13
13
  required :application
14
14
  required :docker_image
15
- required :git_sha
15
+ required :git_info
16
+ required :named_tag
17
+ required :region
18
+ optional :registry
16
19
 
17
20
  organize [
18
21
  DownloadBuildCache,
@@ -1,5 +1,6 @@
1
1
  require 'shipitron'
2
2
  require 'shipitron/consul_keys'
3
+ require 'json'
3
4
 
4
5
  module Shipitron
5
6
  module Server
@@ -9,22 +10,48 @@ module Shipitron
9
10
  include ConsulKeys
10
11
 
11
12
  required :application
13
+ optional :registry
12
14
 
13
15
  before do
14
16
  configure_consul_client!
15
17
  end
16
18
 
17
19
  def call
18
- docker_auth = begin
19
- key = fetch_key(key: "shipitron/#{application}/docker_auth")
20
- key = fetch_key!(key: 'shipitron/docker_auth') if key.nil?
21
- key
22
- end
23
- auth_file = Pathname.new('/home/shipitron/.docker/config.json')
24
- auth_file.parent.mkpath
25
- auth_file.open('wb') do |file|
26
- file.puts(docker_auth.to_s)
27
- file.chmod(0600)
20
+ username = fetch_scoped_key('docker_user')
21
+ password = fetch_scoped_key('docker_password')
22
+
23
+ if username && password
24
+ Logger.info `docker login --username #{username} --password #{password}`
25
+ if $? != 0
26
+ fail_with_error!(message: 'Docker login failed.')
27
+ end
28
+ end
29
+
30
+ if registry
31
+ case registry
32
+ when /docker\.io/
33
+ # do nothing
34
+ when /\d+\.dkr\.ecr\.us-east-1\.amazonaws\.com/
35
+ # ECR
36
+ config_file = Pathname.new('/home/shipitron/.docker/config.json')
37
+ config_file.parent.mkpath
38
+
39
+ config_hash = {}
40
+ if config_file.file?
41
+ config_file.open('rb') do |file|
42
+ json = file.read
43
+ config_hash = JSON.parse(json) rescue {}
44
+ end
45
+ end
46
+
47
+ config_hash['credHelpers'] ||= {}
48
+ config_hash['credHelpers'][registry] = 'ecr-login'
49
+
50
+ config_file.open('wb') do |file|
51
+ file.puts(JSON.generate(config_hash))
52
+ file.chmod(0600)
53
+ end
54
+ end
28
55
  end
29
56
  end
30
57
 
@@ -33,6 +60,15 @@ module Shipitron
33
60
  context.application
34
61
  end
35
62
 
63
+ def registry
64
+ context.registry
65
+ end
66
+
67
+ def fetch_scoped_key(key)
68
+ value = fetch_key(key: "shipitron/#{application}/#{key}")
69
+ value = fetch_key(key: "shipitron/#{key}") if value.nil?
70
+ value
71
+ end
36
72
  end
37
73
  end
38
74
  end
@@ -8,8 +8,11 @@ module Shipitron
8
8
 
9
9
  required :docker_image
10
10
  required :named_tag
11
+ optional :skip_push, default: false
11
12
 
12
13
  def call
14
+ return if context.skip_push
15
+
13
16
  Logger.info "Pushing docker image #{docker_image} and #{docker_image.name_with_tag(named_tag)}"
14
17
 
15
18
  Logger.info `docker tag #{docker_image} #{docker_image.name_with_tag(named_tag)}`
@@ -9,17 +9,16 @@ module Shipitron
9
9
 
10
10
  required :application
11
11
  required :docker_image
12
- required :git_sha
13
- optional :build_script
14
-
15
- before do
16
- context.build_script ||= 'shipitron/build.sh'
17
- end
12
+ required :git_info
13
+ required :named_tag
14
+ optional :build_script, default: 'shipitron/build.sh'
15
+ optional :registry
18
16
 
19
17
  def call
20
18
  Logger.info 'Building docker image'
21
19
 
22
- docker_image.tag = git_sha
20
+ docker_image.registry = registry if registry != nil
21
+ docker_image.tag = git_info.short_sha
23
22
 
24
23
  FileUtils.cd("/home/shipitron/#{application}") do
25
24
  unless Pathname.new(build_script).exist?
@@ -27,7 +26,7 @@ module Shipitron
27
26
  end
28
27
 
29
28
  cmd = TTY::Command.new
30
- result = cmd.run!("#{build_script} #{docker_image}")
29
+ result = cmd.run!("#{build_script} #{docker_image} #{named_tag}")
31
30
 
32
31
  if result.failure?
33
32
  fail_with_error!(message: "build script exited with non-zero code: #{result.exit_status}")
@@ -44,13 +43,21 @@ module Shipitron
44
43
  context.docker_image
45
44
  end
46
45
 
47
- def git_sha
48
- context.git_sha
46
+ def git_info
47
+ context.git_info
48
+ end
49
+
50
+ def named_tag
51
+ context.named_tag
49
52
  end
50
53
 
51
54
  def build_script
52
55
  context.build_script
53
56
  end
57
+
58
+ def registry
59
+ context.registry
60
+ end
54
61
  end
55
62
  end
56
63
  end
@@ -1,5 +1,6 @@
1
1
  require 'shipitron'
2
2
  require 'shipitron/fetch_bucket'
3
+ require 'shipitron/s3_copy'
3
4
 
4
5
  module Shipitron
5
6
  module Server
@@ -9,11 +10,12 @@ module Shipitron
9
10
  required :application
10
11
  required :s3_cache_bucket
11
12
  required :build_cache_location
13
+ required :region
12
14
 
13
15
  def call
14
16
  Logger.info "Downloading build cache from bucket #{s3_cache_bucket}"
15
17
 
16
- s3_file = bucket.files.get("#{application}.build-cache.archive")
18
+ s3_file = bucket.files.head("#{application}.build-cache.archive")
17
19
  if s3_file.nil?
18
20
  Logger.warn 'Build cache not found.'
19
21
  return
@@ -21,8 +23,14 @@ module Shipitron
21
23
 
22
24
  build_cache = Pathname.new("/home/shipitron/#{application}/#{build_cache_location}")
23
25
  build_cache.parent.mkpath
24
- build_cache.open('wb') do |local_file|
25
- local_file.write(s3_file.body)
26
+
27
+ result = S3Copy.call(
28
+ source: "s3://#{s3_cache_bucket}/#{application}.build-cache.archive",
29
+ destination: build_cache.to_s,
30
+ region: context.region
31
+ )
32
+ if result.failure?
33
+ fail_with_error!(message: 'Failed to download build cache!')
26
34
  end
27
35
 
28
36
  Logger.info 'Download complete.'
@@ -0,0 +1,50 @@
1
+ require 'shipitron'
2
+ require 'aws-sdk-s3'
3
+ require 'json'
4
+
5
+ module Shipitron
6
+ module Server
7
+ class FetchDeploy
8
+ include Metaractor
9
+
10
+ required :deploy_bucket
11
+ required :deploy_bucket_region
12
+ required :deploy_id
13
+
14
+ def call
15
+ s3_key = "#{Shipitron::DEPLOY_BUCKET_PREFIX}#{context.deploy_id}"
16
+ Logger.info "Fetching deploy config from s3://#{deploy_bucket}/#{s3_key}"
17
+
18
+ response = client.get_object(
19
+ bucket: deploy_bucket,
20
+ key: s3_key
21
+ )
22
+
23
+ context.deploy_options =
24
+ JSON.parse(response.body.read)
25
+ .transform_keys {|k| k.to_sym }
26
+ end
27
+
28
+ private
29
+
30
+ def client
31
+ Aws::S3::Client.new(
32
+ region: deploy_bucket_region,
33
+ **client_opts
34
+ )
35
+ end
36
+
37
+ def client_opts
38
+ {}
39
+ end
40
+
41
+ def deploy_bucket
42
+ context.deploy_bucket
43
+ end
44
+
45
+ def deploy_bucket_region
46
+ context.deploy_bucket_region
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,6 @@
1
1
  require 'shipitron'
2
2
  require 'shellwords'
3
+ require 'shipitron/git_info'
3
4
 
4
5
  module Shipitron
5
6
  module Server
@@ -12,7 +13,7 @@ module Shipitron
12
13
  optional :repository_branch
13
14
 
14
15
  before do
15
- context.repository_branch ||= 'master'
16
+ context.repository_branch ||= 'main'
16
17
  end
17
18
 
18
19
  def call
@@ -22,10 +23,8 @@ module Shipitron
22
23
  end
23
24
 
24
25
  Logger.info 'Using this git commit:'
25
- FileUtils.cd("/home/shipitron/#{application}") do
26
- context.git_sha = `git rev-parse --short=12 HEAD`.chomp
27
- Logger.info `git --no-pager log --format='%aN (%h): %s' -n 1`.chomp
28
- end
26
+ context.git_info = GitInfo.from_path(path: "/home/shipitron/#{application}")
27
+ Logger.info context.git_info.one_liner
29
28
  end
30
29
 
31
30
  private
@@ -13,7 +13,7 @@ module Shipitron
13
13
  optional :repository_branch
14
14
 
15
15
  before do
16
- context.repository_branch ||= 'master'
16
+ context.repository_branch ||= 'main'
17
17
  end
18
18
 
19
19
  def call
@@ -25,6 +25,7 @@ module Shipitron
25
25
  else
26
26
  Logger.info 'Fetching new git commits'
27
27
  FileUtils.cd('/home/shipitron/git-cache') do
28
+ `git gc --auto`
28
29
  `git fetch -f #{Shellwords.escape repository_url} #{Shellwords.escape repository_branch}:#{Shellwords.escape repository_branch}`
29
30
  end
30
31
  end
@@ -9,6 +9,7 @@ module Shipitron
9
9
 
10
10
  required :region
11
11
  required :clusters
12
+ required :git_info
12
13
  optional :post_builds
13
14
 
14
15
  def call
@@ -26,7 +27,41 @@ module Shipitron
26
27
  container_overrides: [
27
28
  {
28
29
  name: post_build.container_name,
29
- command: post_build.command_ary
30
+ command: post_build.command_ary,
31
+ environment: [
32
+ {
33
+ name: "GIT_SHA",
34
+ value: git_info.sha
35
+ },
36
+ {
37
+ name: "GIT_SHORT_SHA",
38
+ value: git_info.short_sha
39
+ },
40
+ {
41
+ name: "GIT_EMAIL",
42
+ value: git_info.email
43
+ },
44
+ {
45
+ name: "GIT_NAME",
46
+ value: git_info.name
47
+ },
48
+ {
49
+ name: "GIT_MESSAGE",
50
+ value: git_info.summary
51
+ },
52
+ {
53
+ name: "GIT_TIMESTAMP",
54
+ value: git_info.timestamp
55
+ },
56
+ {
57
+ name: "GIT_BRANCH",
58
+ value: git_info.branch
59
+ },
60
+ {
61
+ name: "GIT_TAG",
62
+ value: git_info.tag
63
+ }
64
+ ]
30
65
  }
31
66
  ]
32
67
  },
@@ -75,6 +110,10 @@ module Shipitron
75
110
  def clusters
76
111
  context.clusters
77
112
  end
113
+
114
+ def git_info
115
+ context.git_info
116
+ end
78
117
  end
79
118
  end
80
119
  end
@@ -12,6 +12,7 @@ module Shipitron
12
12
  required :application
13
13
  required :repository_url
14
14
  optional :repository_branch
15
+ optional :registry
15
16
  required :s3_cache_bucket
16
17
  required :build_cache_location
17
18
  required :image_name
@@ -23,6 +24,7 @@ module Shipitron
23
24
  optional :ecs_services
24
25
  optional :ecs_service_templates
25
26
  optional :build_script
27
+ optional :skip_push, default: false
26
28
  optional :post_builds
27
29
 
28
30
  before do
@@ -37,6 +39,7 @@ module Shipitron
37
39
  application
38
40
  repository_url
39
41
  repository_branch
42
+ registry
40
43
  s3_cache_bucket
41
44
  build_cache_location
42
45
  named_tag
@@ -44,6 +47,7 @@ module Shipitron
44
47
  clusters
45
48
  ecs_services
46
49
  build_script
50
+ skip_push
47
51
  ].each_with_object(cli_args) { |k, args| args[k] = context[k] }
48
52
 
49
53
  cli_args.docker_image = DockerImage.new(name: context.image_name)
@@ -14,11 +14,12 @@ module Shipitron
14
14
  required :docker_image
15
15
  required :ecs_task_defs
16
16
  optional :ecs_task_def_templates
17
+ optional :registry
17
18
 
18
19
  before do
19
20
  context.ecs_task_def_templates ||= []
20
21
  context.templates = context.ecs_task_def_templates
21
- context.template_context = { tag: docker_image.tag }
22
+ context.template_context = { tag: docker_image.tag, registry: context.registry }
22
23
  end
23
24
 
24
25
  organize [
@@ -9,6 +9,7 @@ module Shipitron
9
9
  required :application
10
10
  required :s3_cache_bucket
11
11
  required :build_cache_location
12
+ required :region
12
13
 
13
14
  def call
14
15
  Logger.info "Uploading build cache to bucket #{s3_cache_bucket}"
@@ -19,11 +20,15 @@ module Shipitron
19
20
  return
20
21
  end
21
22
 
22
- build_cache.open('rb') do |local_file|
23
- bucket.files.create(
24
- key: "#{application}.build-cache.archive",
25
- body: local_file.read
26
- )
23
+ result = S3Copy.call(
24
+ source: build_cache.to_s,
25
+ destination: "s3://#{s3_cache_bucket}/#{application}.build-cache.archive",
26
+ region: context.region
27
+ )
28
+ if result.failure?
29
+ Logger.warn 'Failed to upload build cache!'
30
+ else
31
+ Logger.info 'Upload complete.'
27
32
  end
28
33
  end
29
34
 
@@ -39,10 +44,6 @@ module Shipitron
39
44
  def build_cache_location
40
45
  context.build_cache_location
41
46
  end
42
-
43
- def bucket
44
- @bucket ||= FetchBucket.call!(name: s3_cache_bucket).bucket
45
- end
46
47
  end
47
48
  end
48
49
  end