escobar 0.3.22 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/escobar.gemspec +1 -0
- data/lib/escobar.rb +4 -0
- data/lib/escobar/github/client.rb +1 -1
- data/lib/escobar/heroku/app.rb +5 -1
- data/lib/escobar/heroku/build_request.rb +1 -6
- data/lib/escobar/heroku/client.rb +4 -1
- data/lib/escobar/heroku/pipeline.rb +17 -0
- data/lib/escobar/heroku/pipeline_promotion.rb +56 -0
- data/lib/escobar/heroku/pipeline_promotion_request.rb +138 -0
- data/lib/escobar/heroku/pipeline_promotion_targets.rb +62 -0
- data/lib/escobar/heroku/release.rb +9 -1
- data/lib/escobar/heroku/slug.rb +24 -0
- data/lib/escobar/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9319e5ced60dbfb825a5d97d8c39ddc69a847d7a
|
4
|
+
data.tar.gz: 5fa1d246746ad8385b7ec4d07dff3615f1f5e09c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 807b6c257647c7841612393639816fefb4805a73fa86453dd52fb00afc260de468057e9b078be6022a85340e2fecc6f72793e4c2a31d91aa162f79a328952ba0
|
7
|
+
data.tar.gz: 61c0411e0c87a88153c3024ffbb3eecff890452f862f04444fec51057b5d0c96cacce459c8991c682071530fe6df5d580de72542ba8854463f0617db93c17f3b
|
data/escobar.gemspec
CHANGED
data/lib/escobar.rb
CHANGED
@@ -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] || {},
|
data/lib/escobar/heroku/app.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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:
|
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
|
data/lib/escobar/version.rb
CHANGED
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.
|
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-
|
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:
|