escobar 0.3.22 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 331accfe3f9ff2cca530fa1037f115088e806288
4
- data.tar.gz: 7557209c606e88607b5e9e43ca04eae53563bccd
3
+ metadata.gz: 9319e5ced60dbfb825a5d97d8c39ddc69a847d7a
4
+ data.tar.gz: 5fa1d246746ad8385b7ec4d07dff3615f1f5e09c
5
5
  SHA512:
6
- metadata.gz: 20b094047b014575d3749ffb4f51715462f10954e2c8df1852a8b4259ff4e0033ce9cddf7bd158090c4243f3018fe1fb1e98fa2e48e78b15f4b9b933fb95c410
7
- data.tar.gz: 1d6598813bfeabcc5d1ef95b263fadce6bbcc4316eb3843cc2657e4eac735723bcc4ff2328399a2b4defa45a058d081bf867f1e9997a8b70c69b7f461737b474
6
+ metadata.gz: 807b6c257647c7841612393639816fefb4805a73fa86453dd52fb00afc260de468057e9b078be6022a85340e2fecc6f72793e4c2a31d91aa162f79a328952ba0
7
+ data.tar.gz: 61c0411e0c87a88153c3024ffbb3eecff890452f862f04444fec51057b5d0c96cacce459c8991c682071530fe6df5d580de72542ba8854463f0617db93c17f3b
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+
2
3
  lib = File.expand_path("../lib", __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "escobar/version"
@@ -70,3 +70,7 @@ require_relative "./escobar/heroku/release"
70
70
  require_relative "./escobar/heroku/coupling"
71
71
  require_relative "./escobar/heroku/pipeline"
72
72
  require_relative "./escobar/heroku/config_vars"
73
+ require_relative "./escobar/heroku/slug"
74
+ require_relative "./escobar/heroku/pipeline_promotion"
75
+ require_relative "./escobar/heroku/pipeline_promotion_request"
76
+ require_relative "./escobar/heroku/pipeline_promotion_targets"
@@ -56,7 +56,7 @@ module Escobar
56
56
  def create_deployment(options)
57
57
  body = {
58
58
  ref: options[:ref] || "master",
59
- task: "deploy",
59
+ task: options[:task] || "deploy",
60
60
  auto_merge: false,
61
61
  required_contexts: options[:required_contexts] || [],
62
62
  payload: options[:payload] || {},
@@ -28,6 +28,10 @@ module Escobar
28
28
  end
29
29
  end
30
30
 
31
+ def current_release_ref
32
+ releases.first.ref
33
+ end
34
+
31
35
  def dynos
32
36
  @dynos ||= Escobar::Heroku::Dynos.new(client, id)
33
37
  end
@@ -50,7 +54,7 @@ module Escobar
50
54
 
51
55
  # Accepts either google authenticator or yubikey second_factor formatting
52
56
  def preauth(second_factor)
53
- !client.heroku.put("/apps/#{id}/pre-authorizations", second_factor).any?
57
+ client.heroku.put("/apps/#{id}/pre-authorizations", second_factor).none?
54
58
  end
55
59
 
56
60
  def locked?
@@ -165,12 +165,7 @@ module Escobar
165
165
 
166
166
  def required_commit_contexts
167
167
  return [] if forced || environment != "production"
168
- github_client.required_contexts.map do |context|
169
- if context == "continuous-integration/travis-ci"
170
- context = "continuous-integration/travis-ci/push"
171
- end
172
- context
173
- end
168
+ pipeline.required_commit_contexts(false)
174
169
  end
175
170
 
176
171
  def github_client
@@ -42,11 +42,14 @@ module Escobar
42
42
  end
43
43
  end
44
44
 
45
- def post(path, body)
45
+ def post(path, body, second_factor = nil)
46
46
  with_error_handling do
47
47
  response = client.post do |request|
48
48
  request.url path
49
49
  request_defaults(request)
50
+ if second_factor
51
+ request.headers["Heroku-Two-Factor-Code"] = second_factor.to_s
52
+ end
50
53
  request.body = body.to_json
51
54
  end
52
55
 
@@ -130,6 +130,23 @@ module Escobar
130
130
  heroku_build
131
131
  end
132
132
 
133
+ # source: A Escobar::Heroku::App to promote from
134
+ # targets: An array of Escobar::Heroku::App to promote to
135
+ # environment: The pipeline stage applying to the promotion
136
+ # force: true if github commit status checks shouldn't be verified
137
+ # payload: Extra info to insert into the github deployment API
138
+ # second_factor: an OTP credential for protected resources
139
+ # rubocop:disable Metrics/ParameterLists
140
+ # rubocop:disable Metrics/LineLength
141
+ def promote(source, targets, environment, force = false, payload = {}, second_factor = nil)
142
+ promotion_request = Escobar::Heroku::PipelinePromotionRequest.new(
143
+ client, self, source, targets, second_factor
144
+ )
145
+ promotion_request.create(environment, force, payload)
146
+ end
147
+ # rubocop:enable Metrics/ParameterLists
148
+ # rubocop:enable Metrics/LineLength
149
+
133
150
  def get(path)
134
151
  response = kolkrabbi_client.get do |request|
135
152
  request.url path
@@ -0,0 +1,56 @@
1
+ module Escobar
2
+ module Heroku
3
+ # Class reperesenting a Heroku Pipeline Promotion
4
+ class PipelinePromotion
5
+ attr_reader :client, :id, :name, :pipeline,
6
+ :source, :targets, :second_factor
7
+ def initialize(client, pipeline, source, targets, second_factor)
8
+ @id = pipeline.id
9
+ @client = client
10
+ @source = source
11
+ @targets = targets
12
+ @pipeline = pipeline
13
+ @second_factor = second_factor
14
+ end
15
+
16
+ def promotion_path
17
+ "/pipeline-promotions"
18
+ end
19
+
20
+ def create
21
+ response = client.heroku.post(promotion_path, body, second_factor)
22
+ case response["id"]
23
+ when Escobar::UUID_REGEX
24
+ results = Escobar::Heroku::PipelinePromotionTargets.new(
25
+ self, response
26
+ )
27
+ results.releases
28
+ when "two_factor"
29
+ raise_2fa_error
30
+ else
31
+ raise ArgumentError, response.to_json
32
+ end
33
+ end
34
+
35
+ class RequiresTwoFactorError < ArgumentError
36
+ end
37
+
38
+ class MissingContextsError < \
39
+ Escobar::Heroku::BuildRequest::MissingContextsError
40
+ end
41
+
42
+ def raise_2fa_error
43
+ message = "Pipeline requires second factor: #{pipeline.name}"
44
+ raise RequiresTwoFactorError, message
45
+ end
46
+
47
+ def body
48
+ {
49
+ pipeline: { id: id },
50
+ source: { app: { id: source.id } },
51
+ targets: targets.map { |t| { app: { id: t.id } } }
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,138 @@
1
+ module Escobar
2
+ module Heroku
3
+ # Class reperesenting a request for Pipeline Promotion
4
+ class PipelinePromotionRequest
5
+ attr_reader :client, :id, :name, :pipeline, :source, :targets
6
+ attr_reader :github_deployment_url, :sha
7
+
8
+ attr_accessor :environment, :forced, :custom_payload, :second_factor
9
+
10
+ def initialize(client, pipeline, source, targets, second_factor)
11
+ @id = pipeline.id
12
+ @client = client
13
+ @pipeline = pipeline
14
+ @source = source
15
+ @targets = targets || []
16
+ @second_factor = second_factor
17
+ end
18
+
19
+ def create(environment, forced, custom_payload)
20
+ raise ArgumentError, "No target applications" if targets.empty?
21
+ @environment = environment
22
+ @forced = forced
23
+ @custom_payload = custom_payload
24
+
25
+ fill_promotion_target_urls
26
+ create_in_api
27
+ end
28
+
29
+ def create_in_api
30
+ promotion = Escobar::Heroku::PipelinePromotion.new(
31
+ client, pipeline, source, targets, second_factor
32
+ )
33
+
34
+ releases = promotion.create
35
+ handle_github_deployment_statuses_for(releases)
36
+ releases
37
+ end
38
+
39
+ def handle_github_deployment_statuses_for(releases)
40
+ releases.each do |release|
41
+ release.sha = release.ref
42
+ release.github_url = target_urls[release.app.id]
43
+ release.pipeline_name = pipeline.name
44
+
45
+ create_github_deployment_status(
46
+ release.github_url,
47
+ release.dashboard_build_output_url,
48
+ "pending",
49
+ "Promotion releasing.."
50
+ )
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def target_urls
57
+ @target_urls ||= {}
58
+ end
59
+
60
+ def fill_promotion_target_urls
61
+ targets.each do |target|
62
+ custom_payload_for_app = custom_payload.merge(
63
+ app_id: target.id, name: target.name
64
+ )
65
+ create_github_deployment(environment, custom_payload_for_app)
66
+ target_urls[target.id] = github_deployment_url
67
+ end
68
+ end
69
+
70
+ def create_github_deployment_status(url, target_url, state, description)
71
+ payload = {
72
+ state: state,
73
+ target_url: target_url,
74
+ description: description
75
+ }
76
+ create_deployment_status(url, payload)
77
+ end
78
+
79
+ def create_deployment_status(url, payload)
80
+ github_client.create_deployment_status(url, payload)
81
+ end
82
+
83
+ def create_github_deployment(environment, custom_payload)
84
+ options = {
85
+ ref: source.current_release_ref,
86
+ task: "promote",
87
+ auto_merge: false,
88
+ payload: custom_payload.merge(custom_deployment_payload),
89
+ environment: environment,
90
+ required_contexts: required_commit_contexts
91
+ }
92
+ response = github_client.create_deployment(options)
93
+ handle_github_deployment_response(response)
94
+ end
95
+
96
+ def custom_deployment_payload
97
+ {
98
+ source: source.id,
99
+ pipeline: pipeline.to_hash,
100
+ provider: "slash-heroku"
101
+ }
102
+ end
103
+
104
+ def github_client
105
+ @github_client ||= Escobar::GitHub::Client.new(
106
+ client.github_token,
107
+ pipeline.github_repository
108
+ )
109
+ end
110
+
111
+ def required_commit_contexts
112
+ pipeline.required_commit_contexts(false)
113
+ end
114
+
115
+ def handle_github_deployment_response(response)
116
+ unless response["sha"]
117
+ handle_github_deployment_error(response)
118
+ end
119
+
120
+ @sha = response["sha"]
121
+ @github_deployment_url = response["url"]
122
+ response
123
+ end
124
+
125
+ class MissingContextsError < \
126
+ Escobar::Heroku::PipelinePromotion::MissingContextsError
127
+ end
128
+
129
+ def handle_github_deployment_error(response)
130
+ error = Escobar::GitHub::DeploymentError.new(
131
+ pipeline.github_repository, response, required_commit_contexts
132
+ )
133
+ raise error_for(error.default_message) unless error.missing_contexts?
134
+ raise MissingContextsError.new_from_build_request_and_error(self, error)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,62 @@
1
+ module Escobar
2
+ module Heroku
3
+ # Class reperesenting a Heroku Pipeline Promotion Targets
4
+ class PipelinePromotionTargets
5
+ attr_reader :client, :id, :name, :pipeline, :promotion
6
+
7
+ def initialize(pipeline, promotion)
8
+ @id = promotion["id"]
9
+ @name = pipeline.name
10
+ @client = pipeline.client
11
+ @pipeline = pipeline
12
+ @retries = 30
13
+ end
14
+
15
+ def release_id
16
+ promotion["source"]["release"]["id"]
17
+ end
18
+
19
+ def app_id
20
+ promotion["source"]["app"]["id"]
21
+ end
22
+
23
+ def source_release
24
+ @release ||= Escobar::Heroku::Release.new(
25
+ client, app_id, nil, release_id
26
+ )
27
+ end
28
+
29
+ def ref
30
+ @ref ||= source_release.ref
31
+ end
32
+
33
+ def targets_path
34
+ "/pipeline-promotions/#{id}/promotion-targets"
35
+ end
36
+
37
+ def info
38
+ @info ||= client.heroku.get(targets_path)
39
+ end
40
+
41
+ def releases
42
+ info.map do |target|
43
+ target_app_id = target["app"]["id"]
44
+ target_release_id = target["release"]["id"]
45
+ Escobar::Heroku::Release.new(
46
+ client, target_app_id, nil, target_release_id
47
+ )
48
+ end
49
+ rescue NoMethodError
50
+ raise(ArgumentError, info.to_json) unless retry?
51
+ sleep 0.5
52
+ @retries -= 1
53
+ @info = nil
54
+ retry
55
+ end
56
+
57
+ def retry?
58
+ @retries.positive?
59
+ end
60
+ end
61
+ end
62
+ end
@@ -28,6 +28,14 @@ module Escobar
28
28
  @build ||= Escobar::Heroku::Build.new(client, app.id, build_id)
29
29
  end
30
30
 
31
+ def slug
32
+ @slug ||= Escobar::Heroku::Slug.new(client, app.id, info["slug"]["id"])
33
+ end
34
+
35
+ def ref
36
+ slug && slug.ref
37
+ end
38
+
31
39
  def dashboard_build_output_url
32
40
  "https://dashboard.heroku.com/apps/#{app.name}/activity/builds/#{id}"
33
41
  end
@@ -50,7 +58,7 @@ module Escobar
50
58
  name: pipeline_name,
51
59
  repo: repository,
52
60
  app_id: app_id,
53
- app_name: app_name,
61
+ app_name: app.name,
54
62
  build_id: build_id,
55
63
  release_id: id,
56
64
  command_id: command_id,
@@ -0,0 +1,24 @@
1
+ module Escobar
2
+ module Heroku
3
+ # Class representing a heroku slug
4
+ class Slug
5
+ attr_reader :app_id, :app_name, :client, :id
6
+
7
+ attr_accessor :sha
8
+
9
+ def initialize(client, app_id, id)
10
+ @id = id
11
+ @app_id = app_id
12
+ @client = client
13
+ end
14
+
15
+ def info
16
+ @info ||= client.heroku.get("/apps/#{app_id}/slugs/#{id}")
17
+ end
18
+
19
+ def ref
20
+ info["commit"]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Escobar
2
- VERSION = "0.3.22".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: escobar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.22
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Corey Donohoe
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-23 00:00:00.000000000 Z
11
+ date: 2017-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -199,7 +199,11 @@ files:
199
199
  - lib/escobar/heroku/coupling.rb
200
200
  - lib/escobar/heroku/dynos.rb
201
201
  - lib/escobar/heroku/pipeline.rb
202
+ - lib/escobar/heroku/pipeline_promotion.rb
203
+ - lib/escobar/heroku/pipeline_promotion_request.rb
204
+ - lib/escobar/heroku/pipeline_promotion_targets.rb
202
205
  - lib/escobar/heroku/release.rb
206
+ - lib/escobar/heroku/slug.rb
203
207
  - lib/escobar/version.rb
204
208
  homepage: https://github.com/atmos/escobar
205
209
  licenses: