shipit-engine 0.24.0 → 0.25.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 +4 -4
- data/app/assets/javascripts/shipit/stacks.js.coffee +15 -1
- data/app/assets/stylesheets/_base/_icons.scss +18 -0
- data/app/assets/stylesheets/_base/_status-items.scss +28 -0
- data/app/assets/stylesheets/_pages/_commits.scss +1 -5
- data/app/assets/stylesheets/_pages/_deploy.scss +60 -3
- data/app/controllers/concerns/shipit/authentication.rb +1 -1
- data/app/controllers/shipit/merge_status_controller.rb +2 -0
- data/app/controllers/shipit/release_statuses_controller.rb +36 -0
- data/app/jobs/shipit/append_delayed_release_status_job.rb +17 -0
- data/app/jobs/shipit/cache_deploy_spec_job.rb +2 -0
- data/app/jobs/shipit/clear_git_cache_job.rb +1 -1
- data/app/jobs/shipit/create_release_statuses_job.rb +11 -0
- data/app/jobs/shipit/deferred_touch_job.rb +2 -0
- data/app/jobs/shipit/deliver_hook_job.rb +1 -0
- data/app/jobs/shipit/merge_pull_requests_job.rb +2 -0
- data/app/jobs/shipit/perform_commit_checks_job.rb +2 -0
- data/app/jobs/shipit/perform_task_job.rb +2 -4
- data/app/jobs/shipit/refresh_pull_request_job.rb +2 -0
- data/app/models/shipit/anonymous_user.rb +1 -1
- data/app/models/shipit/commit.rb +36 -3
- data/app/models/shipit/deploy.rb +43 -0
- data/app/models/shipit/deploy_spec.rb +16 -2
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +5 -1
- data/app/models/shipit/deploy_spec/file_system.rb +4 -0
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -1
- data/app/models/shipit/ephemeral_commit_checks.rb +2 -4
- data/app/models/shipit/hook.rb +36 -3
- data/app/models/shipit/pull_request.rb +4 -2
- data/app/models/shipit/release_status.rb +41 -0
- data/app/models/shipit/rollback.rb +9 -0
- data/app/models/shipit/stack.rb +4 -9
- data/app/models/shipit/status/common.rb +4 -0
- data/app/models/shipit/status/group.rb +2 -1
- data/app/models/shipit/status/missing.rb +4 -0
- data/app/models/shipit/status/unknown.rb +15 -0
- data/app/models/shipit/task.rb +4 -0
- data/app/models/shipit/user.rb +16 -3
- data/app/serializers/shipit/stack_serializer.rb +1 -1
- data/app/views/shipit/deploys/_deploy.html.erb +18 -2
- data/config/locales/en.yml +3 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20180802172632_allow_commit_without_author.rb +6 -0
- data/db/migrate/20180906083930_create_release_statuses.rb +21 -0
- data/lib/shipit.rb +5 -0
- data/lib/shipit/command.rb +14 -18
- data/lib/shipit/deploy_commands.rb +0 -4
- data/lib/shipit/engine.rb +1 -1
- data/lib/shipit/first_parent_commits_iterator.rb +1 -1
- data/lib/shipit/flock.rb +43 -0
- data/lib/shipit/github_app.rb +5 -3
- data/lib/shipit/rollback_commands.rb +6 -0
- data/lib/shipit/task_commands.rb +1 -5
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/release_statuses_controller_test.rb +23 -0
- data/test/dummy/db/schema.rb +18 -3
- data/test/dummy/db/seeds.rb +4 -0
- data/test/fixtures/shipit/commits.yml +13 -0
- data/test/fixtures/shipit/release_statuses.yml +16 -0
- data/test/fixtures/shipit/stacks.yml +4 -0
- data/test/jobs/append_delayed_release_status_job_test.rb +25 -0
- data/test/jobs/cache_deploy_spec_job_test.rb +1 -2
- data/test/jobs/emit_event_job_test.rb +1 -1
- data/test/jobs/github_sync_job_test.rb +1 -0
- data/test/models/commits_test.rb +54 -1
- data/test/models/deploy_spec_test.rb +83 -11
- data/test/models/deploys_test.rb +52 -0
- data/test/models/hook_test.rb +1 -28
- data/test/models/pull_request_test.rb +19 -0
- data/test/models/release_statuses_test.rb +28 -0
- data/test/models/rollbacks_test.rb +2 -0
- data/test/models/stacks_test.rb +1 -1
- data/test/test_helper.rb +5 -0
- data/test/unit/rollback_commands_test.rb +35 -0
- metadata +121 -104
data/app/models/shipit/deploy.rb
CHANGED
@@ -9,6 +9,7 @@ module Shipit
|
|
9
9
|
after_transition to: :success, do: :schedule_merges
|
10
10
|
after_transition to: :success, do: :update_undeployed_commits_count
|
11
11
|
after_transition to: :aborted, do: :trigger_revert_if_required
|
12
|
+
after_transition any => any, do: :update_release_status
|
12
13
|
after_transition any => any, do: :update_commit_deployments
|
13
14
|
after_transition any => any, do: :update_last_deploy_time
|
14
15
|
end
|
@@ -33,6 +34,7 @@ module Shipit
|
|
33
34
|
|
34
35
|
before_create :denormalize_commit_stats
|
35
36
|
after_create :create_commit_deployments
|
37
|
+
after_create :update_release_status
|
36
38
|
after_commit :broadcast_update
|
37
39
|
|
38
40
|
delegate :broadcast_update, :filter_deploy_envs, to: :stack
|
@@ -143,6 +145,21 @@ module Shipit
|
|
143
145
|
confirmations.abs >= CONFIRMATIONS_REQUIRED
|
144
146
|
end
|
145
147
|
|
148
|
+
delegate :last_release_status, to: :until_commit
|
149
|
+
def append_release_status(state, description, user: self.user)
|
150
|
+
status = until_commit.create_release_status!(
|
151
|
+
state,
|
152
|
+
user: user.presence,
|
153
|
+
target_url: permalink,
|
154
|
+
description: description,
|
155
|
+
)
|
156
|
+
status
|
157
|
+
end
|
158
|
+
|
159
|
+
def permalink
|
160
|
+
Shipit::Engine.routes.url_helpers.stack_deploy_url(stack, self)
|
161
|
+
end
|
162
|
+
|
146
163
|
private
|
147
164
|
|
148
165
|
def create_commit_deployments
|
@@ -151,6 +168,32 @@ module Shipit
|
|
151
168
|
end
|
152
169
|
end
|
153
170
|
|
171
|
+
def update_release_status
|
172
|
+
return unless stack.release_status?
|
173
|
+
|
174
|
+
case status
|
175
|
+
when 'pending'
|
176
|
+
append_release_status('pending', "A deploy was triggered on #{stack.environment}")
|
177
|
+
when 'failed', 'error', 'timedout'
|
178
|
+
append_release_status('error', "The deploy on #{stack.environment} did not succeed (#{status})")
|
179
|
+
when 'aborted', 'aborting'
|
180
|
+
append_release_status('failure', "The deploy on #{stack.environment} was canceled")
|
181
|
+
when 'success'
|
182
|
+
delay = stack.release_status_delay
|
183
|
+
if delay.zero?
|
184
|
+
append_release_status('success', "The deploy on #{stack.environment} succeeded")
|
185
|
+
elsif delay.positive?
|
186
|
+
append_release_status('pending', "The deploy on #{stack.environment} succeeded")
|
187
|
+
AppendDelayedReleaseStatusJob.set(wait: delay).perform_later(
|
188
|
+
self,
|
189
|
+
cursor: until_commit.release_statuses.last,
|
190
|
+
status: 'success',
|
191
|
+
description: "No issue were signaled after #{delay}",
|
192
|
+
)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
154
197
|
def update_commit_deployments
|
155
198
|
commit_deployments.append_status(status)
|
156
199
|
end
|
@@ -67,6 +67,20 @@ module Shipit
|
|
67
67
|
config('deploy', 'max_commits') { 8 }
|
68
68
|
end
|
69
69
|
|
70
|
+
def release_status?
|
71
|
+
!!release_status_context
|
72
|
+
end
|
73
|
+
|
74
|
+
def release_status_context
|
75
|
+
config('status', 'context')
|
76
|
+
end
|
77
|
+
|
78
|
+
def release_status_delay
|
79
|
+
if delay = config('status', 'delay') { config('deploy', 'interval') }
|
80
|
+
Duration.parse(delay)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
70
84
|
def pause_between_deploys
|
71
85
|
Duration.parse(config('deploy', 'interval') { 0 })
|
72
86
|
end
|
@@ -131,7 +145,7 @@ module Shipit
|
|
131
145
|
end
|
132
146
|
|
133
147
|
def hidden_statuses
|
134
|
-
Array.wrap(config('ci', 'hide'))
|
148
|
+
Array.wrap(config('ci', 'hide')) + [release_status_context].compact
|
135
149
|
end
|
136
150
|
|
137
151
|
def required_statuses
|
@@ -161,7 +175,7 @@ module Shipit
|
|
161
175
|
|
162
176
|
def pull_request_ignored_statuses
|
163
177
|
if config('merge', 'require') || config('merge', 'ignore')
|
164
|
-
Array.wrap(config('merge', 'ignore'))
|
178
|
+
Array.wrap(config('merge', 'ignore')) + [release_status_context].compact
|
165
179
|
else
|
166
180
|
soft_failing_statuses | hidden_statuses
|
167
181
|
end
|
@@ -19,8 +19,12 @@ module Shipit
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def discover_machine_env
|
23
|
+
super.merge('BUNDLE_PATH' => bundle_path.to_s)
|
24
|
+
end
|
25
|
+
|
22
26
|
def bundle_install
|
23
|
-
bundle = %(bundle
|
27
|
+
bundle = %(bundle install #{frozen_flag} --jobs 4 --path #{bundle_path} --retry 2)
|
24
28
|
bundle += " --without=#{bundler_without.join(':')}" unless bundler_without.empty?
|
25
29
|
[remove_ruby_version_from_gemfile, bundle]
|
26
30
|
end
|
@@ -57,6 +57,10 @@ module Shipit
|
|
57
57
|
'checks' => review_checks,
|
58
58
|
},
|
59
59
|
'plugins' => plugins,
|
60
|
+
'status' => {
|
61
|
+
'context' => release_status_context,
|
62
|
+
'delay' => release_status_delay,
|
63
|
+
},
|
60
64
|
'dependencies' => {'override' => dependencies_steps},
|
61
65
|
'deploy' => {
|
62
66
|
'override' => deploy_steps,
|
@@ -14,10 +14,8 @@ module Shipit
|
|
14
14
|
commands = StackCommands.new(stack)
|
15
15
|
commands.with_temporary_working_directory(commit: commit) do |directory|
|
16
16
|
deploy_spec = DeploySpec::FileSystem.new(directory, stack.environment)
|
17
|
-
|
18
|
-
|
19
|
-
capture_all(build_commands(deploy_spec.review_checks, chdir: directory))
|
20
|
-
end
|
17
|
+
capture_all(build_commands(deploy_spec.dependencies_steps, chdir: directory))
|
18
|
+
capture_all(build_commands(deploy_spec.review_checks, chdir: directory))
|
21
19
|
end
|
22
20
|
self
|
23
21
|
rescue Command::Error
|
data/app/models/shipit/hook.rb
CHANGED
@@ -1,5 +1,38 @@
|
|
1
1
|
module Shipit
|
2
2
|
class Hook < ActiveRecord::Base
|
3
|
+
class DeliverySpec
|
4
|
+
def initialize(event:, url:, content_type:, payload:)
|
5
|
+
@event = event
|
6
|
+
@url = url
|
7
|
+
@content_type = content_type
|
8
|
+
@payload = payload
|
9
|
+
end
|
10
|
+
|
11
|
+
def send!
|
12
|
+
http.post(url, payload)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :event, :url, :content_type, :payload
|
18
|
+
|
19
|
+
def http
|
20
|
+
Faraday::Connection.new do |connection|
|
21
|
+
connection.headers = headers
|
22
|
+
connection.adapter Faraday.default_adapter
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def headers
|
27
|
+
{
|
28
|
+
'User-Agent' => 'Shipit Webhook',
|
29
|
+
'Content-Type' => content_type,
|
30
|
+
'X-Shipit-Event' => event,
|
31
|
+
'Accept' => '*/*',
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
3
36
|
default_scope { order :id }
|
4
37
|
|
5
38
|
DELIVERIES_LOG_SIZE = 500
|
@@ -71,12 +104,12 @@ module Shipit
|
|
71
104
|
end
|
72
105
|
|
73
106
|
def deliver!(event, payload)
|
74
|
-
|
75
|
-
event: event,
|
107
|
+
DeliverHookJob.perform_later(
|
108
|
+
event: event.to_s,
|
76
109
|
url: delivery_url,
|
77
110
|
content_type: CONTENT_TYPES[content_type],
|
78
111
|
payload: serialize_payload(payload),
|
79
|
-
)
|
112
|
+
)
|
80
113
|
end
|
81
114
|
|
82
115
|
def purge_old_deliveries!(keep: DELIVERIES_LOG_SIZE)
|
@@ -2,6 +2,8 @@ module Shipit
|
|
2
2
|
class PullRequest < ApplicationRecord
|
3
3
|
include DeferredTouch
|
4
4
|
|
5
|
+
MERGE_REQUEST_FIELD = 'Merge-Requested-By'.freeze
|
6
|
+
|
5
7
|
WAITING_STATUSES = %w(fetching pending).freeze
|
6
8
|
QUEUED_STATUSES = %w(pending revalidating).freeze
|
7
9
|
REJECTION_REASONS = %w(ci_failing merge_conflict requires_rebase).freeze
|
@@ -188,7 +190,7 @@ module Shipit
|
|
188
190
|
|
189
191
|
def any_status_checks_failed?
|
190
192
|
status = StatusChecker.new(head, head.statuses, stack.cached_deploy_spec)
|
191
|
-
status.failure? || status.error?
|
193
|
+
status.failure? || status.error? || status.missing?
|
192
194
|
end
|
193
195
|
|
194
196
|
def waiting?
|
@@ -245,7 +247,7 @@ module Shipit
|
|
245
247
|
|
246
248
|
def merge_message
|
247
249
|
return title unless merge_requested_by
|
248
|
-
"#{title}\n\
|
250
|
+
"#{title}\n\n#{MERGE_REQUEST_FIELD}: #{merge_requested_by.login}\n"
|
249
251
|
end
|
250
252
|
|
251
253
|
def stale?
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shipit
|
2
|
+
class ReleaseStatus < ActiveRecord::Base
|
3
|
+
MAX_DESCRIPTION_LENGTH = 140
|
4
|
+
include DeferredTouch
|
5
|
+
|
6
|
+
belongs_to :stack
|
7
|
+
belongs_to :commit
|
8
|
+
belongs_to :user, optional: true
|
9
|
+
|
10
|
+
deferred_touch stack: :updated_at, commit: :updated_at
|
11
|
+
after_commit :schedule_create_release_statuses, on: :create
|
12
|
+
|
13
|
+
scope :to_be_created, -> { where(github_id: nil).order(id: :asc) }
|
14
|
+
|
15
|
+
STATES = %w(pending success failure error).freeze
|
16
|
+
validates :state, presence: true, inclusion: {in: STATES}
|
17
|
+
|
18
|
+
def create_status_on_github!
|
19
|
+
return true if github_id?
|
20
|
+
|
21
|
+
update!(github_id: create_status_on_github.id)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def create_status_on_github
|
27
|
+
Shipit.github.api.create_status(
|
28
|
+
stack.github_repo_name,
|
29
|
+
commit.sha,
|
30
|
+
state,
|
31
|
+
context: stack.release_status_context,
|
32
|
+
target_url: target_url,
|
33
|
+
description: description&.truncate(MAX_DESCRIPTION_LENGTH),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def schedule_create_release_statuses
|
38
|
+
CreateReleaseStatusesJob.perform_later(commit)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -34,6 +34,15 @@ module Shipit
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
+
def update_release_status
|
38
|
+
return unless stack.release_status?
|
39
|
+
|
40
|
+
case status
|
41
|
+
when 'pending'
|
42
|
+
deploy.append_release_status('failure', "A rollback #{stack.to_param} was triggered")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
37
46
|
def lock_reverted_commits
|
38
47
|
stack.lock_reverted_commits!
|
39
48
|
end
|
data/app/models/shipit/stack.rb
CHANGED
@@ -73,8 +73,8 @@ module Shipit
|
|
73
73
|
validates :lock_reason, length: {maximum: 4096}
|
74
74
|
|
75
75
|
serialize :cached_deploy_spec, DeploySpec
|
76
|
-
delegate :find_task_definition, :supports_rollback?, :links,
|
77
|
-
:supports_fetch_deployed_revision?, to: :cached_deploy_spec, allow_nil: true
|
76
|
+
delegate :find_task_definition, :supports_rollback?, :links, :release_status?, :release_status_delay,
|
77
|
+
:release_status_context, :supports_fetch_deployed_revision?, to: :cached_deploy_spec, allow_nil: true
|
78
78
|
|
79
79
|
def self.refresh_deployed_revisions
|
80
80
|
find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
|
@@ -307,13 +307,8 @@ module Shipit
|
|
307
307
|
File.join(base_path, "git")
|
308
308
|
end
|
309
309
|
|
310
|
-
def acquire_git_cache_lock(timeout: 15,
|
311
|
-
|
312
|
-
"stack:#{id}:git-cache-lock",
|
313
|
-
Shipit.redis,
|
314
|
-
timeout: timeout,
|
315
|
-
expiration: expiration,
|
316
|
-
).lock(&block)
|
310
|
+
def acquire_git_cache_lock(timeout: 15, &block)
|
311
|
+
Flock.new(git_path.to_s + '.lock').lock(timeout: timeout, &block)
|
317
312
|
end
|
318
313
|
|
319
314
|
def clear_git_cache!
|
@@ -29,7 +29,8 @@ module Shipit
|
|
29
29
|
@statuses = visible_statuses.sort_by!(&:context)
|
30
30
|
end
|
31
31
|
|
32
|
-
delegate :pending?, :success?, :error?, :failure?, :unknown?, :state, :simple_state,
|
32
|
+
delegate :pending?, :success?, :error?, :failure?, :unknown?, :missing?, :state, :simple_state,
|
33
|
+
to: :significant_status
|
33
34
|
delegate :each, :size, :map, to: :statuses
|
34
35
|
delegate :required_statuses, to: :commit
|
35
36
|
|
@@ -1,18 +1,33 @@
|
|
1
1
|
module Shipit
|
2
2
|
class Status
|
3
3
|
class Unknown
|
4
|
+
include GlobalID::Identification
|
4
5
|
include Common
|
5
6
|
|
7
|
+
class << self
|
8
|
+
def find(id)
|
9
|
+
new(Commit.find(id))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
6
13
|
attr_reader :commit
|
7
14
|
|
8
15
|
def initialize(commit)
|
9
16
|
@commit = commit
|
10
17
|
end
|
11
18
|
|
19
|
+
def id
|
20
|
+
commit.id
|
21
|
+
end
|
22
|
+
|
12
23
|
def state
|
13
24
|
'unknown'.freeze
|
14
25
|
end
|
15
26
|
|
27
|
+
def missing?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
16
31
|
def target_url
|
17
32
|
nil
|
18
33
|
end
|
data/app/models/shipit/task.rb
CHANGED
data/app/models/shipit/user.rb
CHANGED
@@ -8,6 +8,8 @@ module Shipit
|
|
8
8
|
has_many :commits, foreign_key: :committer_id, inverse_of: :committer
|
9
9
|
has_many :tasks
|
10
10
|
|
11
|
+
validates :name, presence: true
|
12
|
+
|
11
13
|
attr_encrypted :github_access_token, key: Shipit.user_access_tokens_key
|
12
14
|
|
13
15
|
def self.find_or_create_by_login!(login)
|
@@ -16,8 +18,19 @@ module Shipit
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
21
|
+
def self.find_or_create_committer_from_github_commit(github_commit)
|
22
|
+
find_or_create_from_github(github_commit.committer.presence || github_commit.commit.committer.presence)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_or_create_author_from_github_commit(github_commit)
|
26
|
+
if github_commit.commit.message =~ /^#{PullRequest::MERGE_REQUEST_FIELD}: ([\w\-\.]+)$/
|
27
|
+
return find_or_create_by_login!($1)
|
28
|
+
end
|
29
|
+
find_or_create_from_github(github_commit.author.presence || github_commit.commit.author.presence)
|
30
|
+
end
|
31
|
+
|
19
32
|
def self.find_or_create_from_github(github_user)
|
20
|
-
find_from_github(github_user) || create_from_github
|
33
|
+
find_from_github(github_user) || create_from_github(github_user)
|
21
34
|
end
|
22
35
|
|
23
36
|
def self.find_from_github(github_user)
|
@@ -25,8 +38,8 @@ module Shipit
|
|
25
38
|
find_by(github_id: github_user.id)
|
26
39
|
end
|
27
40
|
|
28
|
-
def self.create_from_github
|
29
|
-
create
|
41
|
+
def self.create_from_github(github_user)
|
42
|
+
create(github_user: github_user)
|
30
43
|
end
|
31
44
|
|
32
45
|
def self.refresh_shard(shard_index, shards_count)
|