shipit-engine 0.29.0 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +37 -2
- data/app/assets/images/archive-solid.svg +1 -0
- data/app/assets/stylesheets/_pages/_stacks.scss +76 -0
- data/app/controllers/shipit/api/stacks_controller.rb +20 -1
- data/app/controllers/shipit/api_clients_controller.rb +49 -0
- data/app/controllers/shipit/merge_status_controller.rb +8 -4
- data/app/controllers/shipit/stacks_controller.rb +58 -9
- data/app/controllers/shipit/webhooks_controller.rb +2 -130
- data/app/helpers/shipit/stacks_helper.rb +4 -0
- data/app/jobs/shipit/background_job/unique.rb +3 -1
- data/app/jobs/shipit/continuous_delivery_job.rb +1 -0
- data/app/jobs/shipit/destroy_stack_job.rb +2 -2
- data/app/models/shipit/commit.rb +21 -9
- data/app/models/shipit/commit_deployment.rb +15 -11
- data/app/models/shipit/commit_deployment_status.rb +6 -2
- data/app/models/shipit/deploy.rb +48 -7
- data/app/models/shipit/deploy_stats.rb +57 -0
- data/app/models/shipit/repository.rb +38 -0
- data/app/models/shipit/stack.rb +41 -34
- data/app/models/shipit/task.rb +26 -4
- data/app/models/shipit/webhooks.rb +32 -0
- data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +19 -0
- data/app/models/shipit/webhooks/handlers/handler.rb +40 -0
- data/app/models/shipit/webhooks/handlers/membership_handler.rb +45 -0
- data/app/models/shipit/webhooks/handlers/push_handler.rb +20 -0
- data/app/models/shipit/webhooks/handlers/status_handler.rb +26 -0
- data/app/serializers/shipit/stack_serializer.rb +6 -1
- data/app/validators/ascii_only_validator.rb +3 -3
- data/app/views/layouts/_head.html.erb +0 -0
- data/app/views/layouts/shipit.html.erb +4 -2
- data/app/views/shipit/api_clients/index.html.erb +36 -0
- data/app/views/shipit/api_clients/new.html.erb +33 -0
- data/app/views/shipit/api_clients/show.html.erb +35 -0
- data/app/views/shipit/merge_status/logged_out.erb +1 -1
- data/app/views/shipit/stacks/_header.html.erb +12 -7
- data/app/views/shipit/stacks/_links.html.erb +1 -0
- data/app/views/shipit/stacks/index.html.erb +7 -2
- data/app/views/shipit/stacks/settings.html.erb +19 -0
- data/app/views/shipit/stacks/statistics.html.erb +82 -0
- data/config/locales/en.yml +14 -2
- data/config/routes.rb +4 -0
- data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
- data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
- data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
- data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
- data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
- data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
- data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
- data/lib/shipit/github_app.rb +32 -3
- data/lib/shipit/task_commands.rb +10 -2
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/api/ccmenu_controller_test.rb +1 -1
- data/test/controllers/api/stacks_controller_test.rb +14 -6
- data/test/controllers/api_clients_controller_test.rb +103 -0
- data/test/controllers/merge_status_controller_test.rb +21 -4
- data/test/controllers/stacks_controller_test.rb +35 -0
- data/test/controllers/webhooks_controller_test.rb +26 -0
- data/test/dummy/config/environments/development.rb +22 -4
- data/test/dummy/db/schema.rb +17 -6
- data/test/dummy/db/seeds.rb +20 -6
- data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
- data/test/fixtures/shipit/commit_deployments.yml +8 -8
- data/test/fixtures/shipit/commits.yml +23 -0
- data/test/fixtures/shipit/repositories.yml +23 -0
- data/test/fixtures/shipit/stacks.yml +100 -16
- data/test/fixtures/shipit/tasks.yml +66 -3
- data/test/jobs/destroy_stack_job_test.rb +9 -0
- data/test/models/commit_deployment_status_test.rb +33 -4
- data/test/models/commit_deployment_test.rb +8 -11
- data/test/models/commits_test.rb +22 -2
- data/test/models/deploy_stats_test.rb +112 -0
- data/test/models/deploys_test.rb +55 -17
- data/test/models/pull_request_test.rb +1 -1
- data/test/models/shipit/repository_test.rb +76 -0
- data/test/models/shipit/wehbooks/handlers_test.rb +26 -0
- data/test/models/stacks_test.rb +44 -51
- data/test/models/undeployed_commits_test.rb +13 -0
- data/test/test_helper.rb +3 -1
- data/test/unit/deploy_commands_test.rb +9 -0
- data/test/unit/github_app_test.rb +136 -0
- metadata +161 -128
@@ -4,6 +4,8 @@ module Shipit
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
DEFAULT_TIMEOUT = 10
|
6
6
|
|
7
|
+
ConcurrentJobError = Class.new(StandardError)
|
8
|
+
|
7
9
|
included do
|
8
10
|
around_perform { |job, block| job.acquire_lock(&block) }
|
9
11
|
cattr_accessor :lock_timeout
|
@@ -19,7 +21,7 @@ module Shipit
|
|
19
21
|
)
|
20
22
|
mutex.lock(&block)
|
21
23
|
rescue Redis::Lock::LockTimeout
|
22
|
-
raise unless self.class.drop_duplicate_jobs?
|
24
|
+
raise ConcurrentJobError unless self.class.drop_duplicate_jobs?
|
23
25
|
end
|
24
26
|
|
25
27
|
def lock_key(*args)
|
@@ -17,7 +17,8 @@ module Shipit
|
|
17
17
|
def perform(stack)
|
18
18
|
Shipit::ApiClient.where(stack_id: stack.id).delete_all
|
19
19
|
commits_ids = Shipit::Commit.where(stack_id: stack.id).pluck(:id)
|
20
|
-
|
20
|
+
tasks_ids = Shipit::Task.where(stack_id: stack.id).pluck(:id)
|
21
|
+
commit_deployments_ids = Shipit::CommitDeployment.where(task_id: tasks_ids).pluck(:id)
|
21
22
|
Shipit::CommitDeploymentStatus.where(commit_deployment_id: commit_deployments_ids).delete_all
|
22
23
|
Shipit::CommitDeployment.where(id: commit_deployments_ids).delete_all
|
23
24
|
Shipit::Status.where(commit_id: commits_ids).delete_all
|
@@ -25,7 +26,6 @@ module Shipit
|
|
25
26
|
Shipit::GithubHook.where(stack_id: stack.id).destroy_all
|
26
27
|
Shipit::Hook.where(stack_id: stack.id).delete_all
|
27
28
|
Shipit::PullRequest.where(stack_id: stack.id).delete_all
|
28
|
-
tasks_ids = Shipit::Task.where(stack_id: stack.id).pluck(:id)
|
29
29
|
tasks_ids.each_slice(100) do |ids|
|
30
30
|
Shipit::OutputChunk.where(task_id: ids).delete_all
|
31
31
|
Shipit::Task.where(id: ids).delete_all
|
data/app/models/shipit/commit.rb
CHANGED
@@ -2,6 +2,8 @@ module Shipit
|
|
2
2
|
class Commit < ActiveRecord::Base
|
3
3
|
include DeferredTouch
|
4
4
|
|
5
|
+
RECENT_COMMIT_THRESHOLD = 10.seconds
|
6
|
+
|
5
7
|
AmbiguousRevision = Class.new(StandardError)
|
6
8
|
|
7
9
|
belongs_to :stack
|
@@ -102,6 +104,14 @@ module Shipit
|
|
102
104
|
)
|
103
105
|
end
|
104
106
|
|
107
|
+
def message=(message)
|
108
|
+
limit = self.class.columns_hash['message'].limit
|
109
|
+
if limit && message && message.size > limit
|
110
|
+
message = message.slice(0, limit)
|
111
|
+
end
|
112
|
+
super(message)
|
113
|
+
end
|
114
|
+
|
105
115
|
def reload(*)
|
106
116
|
@status = nil
|
107
117
|
super
|
@@ -126,7 +136,9 @@ module Shipit
|
|
126
136
|
end
|
127
137
|
|
128
138
|
def refresh_statuses!
|
129
|
-
github_statuses = stack.handle_github_redirections
|
139
|
+
github_statuses = stack.handle_github_redirections do
|
140
|
+
Shipit.github.api.statuses(github_repo_name, sha, per_page: 100)
|
141
|
+
end
|
130
142
|
github_statuses.each do |status|
|
131
143
|
create_status_from_github!(status)
|
132
144
|
end
|
@@ -177,13 +189,7 @@ module Shipit
|
|
177
189
|
def active?
|
178
190
|
return false unless stack.active_task?
|
179
191
|
|
180
|
-
|
181
|
-
|
182
|
-
if active_task.since_commit == active_task.until_commit
|
183
|
-
id == active_task.since_commit.id
|
184
|
-
else
|
185
|
-
id > active_task.since_commit.id && id <= active_task.until_commit.id
|
186
|
-
end
|
192
|
+
stack.active_task.includes_commit?(self)
|
187
193
|
end
|
188
194
|
|
189
195
|
def deployable?
|
@@ -242,7 +248,9 @@ module Shipit
|
|
242
248
|
|
243
249
|
def schedule_continuous_delivery
|
244
250
|
return unless deployable? && stack.continuous_deployment? && stack.deployable?
|
245
|
-
|
251
|
+
# This buffer is to allow for statuses and checks to be refreshed before evaluating if the commit is deployable
|
252
|
+
# - e.g. if the commit was fast-forwarded with already passing CI.
|
253
|
+
ContinuousDeliveryJob.set(wait: RECENT_COMMIT_THRESHOLD).perform_later(stack)
|
246
254
|
end
|
247
255
|
|
248
256
|
def github_commit
|
@@ -311,6 +319,10 @@ module Shipit
|
|
311
319
|
update!(locked: false, lock_author: nil)
|
312
320
|
end
|
313
321
|
|
322
|
+
def recently_pushed?
|
323
|
+
created_at > RECENT_COMMIT_THRESHOLD.ago
|
324
|
+
end
|
325
|
+
|
314
326
|
private
|
315
327
|
|
316
328
|
def message_parser
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Shipit
|
2
2
|
class CommitDeployment < ActiveRecord::Base
|
3
|
-
belongs_to :commit
|
4
3
|
belongs_to :task
|
5
4
|
has_many :statuses, dependent: :destroy, class_name: 'CommitDeploymentStatus'
|
6
5
|
|
@@ -9,11 +8,10 @@ module Shipit
|
|
9
8
|
delegate :stack, :author, to: :task
|
10
9
|
|
11
10
|
def create_on_github!
|
12
|
-
return unless commit.pull_request?
|
13
|
-
|
14
11
|
create_deployment_on_github!
|
15
12
|
statuses.order(id: :asc).each(&:create_on_github!)
|
16
|
-
rescue Octokit::NotFound, Octokit::Forbidden
|
13
|
+
rescue Octokit::NotFound, Octokit::Forbidden => error
|
14
|
+
Rails.logger.warn("Got #{error.class.name} creating deployment or statuses: #{error.message}")
|
17
15
|
# If no one can create the deployment we can only give up
|
18
16
|
end
|
19
17
|
|
@@ -34,25 +32,31 @@ module Shipit
|
|
34
32
|
update!(github_id: response.id, api_url: response.url)
|
35
33
|
end
|
36
34
|
|
37
|
-
def pull_request_head
|
38
|
-
pull_request = Shipit.github.api.pull_request(stack.github_repo_name, commit.pull_request_number)
|
39
|
-
pull_request.head.sha
|
40
|
-
end
|
41
|
-
|
42
35
|
def schedule_create_on_github
|
43
36
|
CreateOnGithubJob.perform_later(self)
|
44
37
|
end
|
45
38
|
|
39
|
+
def short_sha
|
40
|
+
sha[0..9]
|
41
|
+
end
|
42
|
+
|
46
43
|
private
|
47
44
|
|
48
45
|
def create_deployment_on_github(client)
|
49
46
|
client.create_deployment(
|
50
47
|
stack.github_repo_name,
|
51
|
-
|
48
|
+
sha,
|
52
49
|
auto_merge: false,
|
53
50
|
required_contexts: [],
|
54
|
-
description: "Via Shipit",
|
51
|
+
description: "Via #{Shipit.app_name}",
|
55
52
|
environment: stack.environment,
|
53
|
+
payload: {
|
54
|
+
shipit: {
|
55
|
+
task_id: task.id,
|
56
|
+
from_sha: task.since_commit.sha,
|
57
|
+
to_sha: task.until_commit.sha,
|
58
|
+
},
|
59
|
+
},
|
56
60
|
)
|
57
61
|
end
|
58
62
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Shipit
|
2
2
|
class CommitDeploymentStatus < ActiveRecord::Base
|
3
|
+
DESCRIPTION_CHARACTER_LIMIT_ON_GITHUB = 140
|
4
|
+
|
3
5
|
belongs_to :commit_deployment
|
4
6
|
|
5
7
|
after_commit :schedule_create_on_github, on: :create
|
@@ -25,7 +27,7 @@ module Shipit
|
|
25
27
|
def description
|
26
28
|
I18n.t(
|
27
29
|
"deployment_description.#{task_type}.#{status}",
|
28
|
-
sha: task.until_commit.
|
30
|
+
sha: task.until_commit.short_sha,
|
29
31
|
author: task.author.login,
|
30
32
|
stack: stack.to_param,
|
31
33
|
)
|
@@ -45,8 +47,10 @@ module Shipit
|
|
45
47
|
client.create_deployment_status(
|
46
48
|
commit_deployment.api_url,
|
47
49
|
status,
|
50
|
+
accept: 'application/vnd.github.flash-preview+json',
|
48
51
|
target_url: url_helpers.stack_deploy_url(stack, task),
|
49
|
-
description: description,
|
52
|
+
description: description.truncate(DESCRIPTION_CHARACTER_LIMIT_ON_GITHUB),
|
53
|
+
environment_url: stack.deploy_url,
|
50
54
|
)
|
51
55
|
end
|
52
56
|
|
data/app/models/shipit/deploy.rb
CHANGED
@@ -14,20 +14,41 @@ module Shipit
|
|
14
14
|
after_transition any => any, do: :update_last_deploy_time
|
15
15
|
end
|
16
16
|
|
17
|
+
belongs_to :until_commit, class_name: 'Commit', required: true, inverse_of: :deploys
|
18
|
+
belongs_to :since_commit, class_name: 'Commit', required: true, inverse_of: :deploys
|
17
19
|
has_many :commit_deployments, dependent: :destroy, inverse_of: :task, foreign_key: :task_id do
|
18
20
|
GITHUB_STATUSES = {
|
19
21
|
'pending' => 'pending',
|
22
|
+
'running' => 'in_progress',
|
20
23
|
'failed' => 'failure',
|
24
|
+
'timedout' => 'failure',
|
21
25
|
'success' => 'success',
|
26
|
+
'faulty' => 'error',
|
22
27
|
'error' => 'error',
|
23
28
|
'aborted' => 'error',
|
24
29
|
}.freeze
|
25
30
|
|
26
31
|
def append_status(task_status)
|
27
32
|
if github_status = GITHUB_STATUSES[task_status]
|
28
|
-
|
33
|
+
# Deployments and statuses are created async, we reload the association to ensure we update all instances
|
34
|
+
reload.each do |deployment|
|
35
|
+
Rails.logger.info(
|
36
|
+
"Creating #{github_status} deploy status for deployment #{deployment.id}. "\
|
37
|
+
"Commit: #{deployment.sha}, Github id: #{deployment.github_id}, "\
|
38
|
+
"Repo: #{deployment.stack.repo_name}, Environment: #{deployment.stack.environment}, "\
|
39
|
+
"API Url: #{deployment.api_url}.",
|
40
|
+
)
|
29
41
|
deployment.statuses.create!(status: github_status)
|
30
42
|
end
|
43
|
+
else
|
44
|
+
each do |deployment|
|
45
|
+
Rails.logger.warn(
|
46
|
+
"No GitHub status for task status #{task_status}. "\
|
47
|
+
"Commit: #{deployment.sha}, Github id: #{deployment.github_id}, "\
|
48
|
+
"Repo: #{deployment.stack.repo_name}, Environment: #{deployment.stack.environment}, "\
|
49
|
+
"API Url: #{deployment.api_url}.",
|
50
|
+
)
|
51
|
+
end
|
31
52
|
end
|
32
53
|
end
|
33
54
|
end
|
@@ -215,12 +236,36 @@ module Shipit
|
|
215
236
|
end
|
216
237
|
end
|
217
238
|
|
239
|
+
def update_commit_deployments
|
240
|
+
commit_deployments.append_status(status)
|
241
|
+
end
|
242
|
+
|
218
243
|
private
|
219
244
|
|
220
245
|
def create_commit_deployments
|
221
|
-
|
222
|
-
|
246
|
+
# Create one deployment for the head of the batch
|
247
|
+
commit_deployments.create!(sha: until_commit.sha)
|
248
|
+
|
249
|
+
# Create one for each pull request in the batch, to give feedback on the PR timeline
|
250
|
+
commits.select(&:pull_request?).each do |commit|
|
251
|
+
if (pull_request_head = pull_request_head_for_commit(commit))
|
252
|
+
commit_deployments.create!(sha: pull_request_head)
|
253
|
+
end
|
223
254
|
end
|
255
|
+
|
256
|
+
# Immediately update to publish the status to the commit deployments
|
257
|
+
update_commit_deployments
|
258
|
+
rescue Octokit::ClientError => error
|
259
|
+
Rails.logger.warn("Got #{error.class.name} (#{error.message}) when creating CommitDeployments for Deploy##{id}")
|
260
|
+
end
|
261
|
+
|
262
|
+
def pull_request_head_for_commit(commit)
|
263
|
+
pull_request = Shipit.github.api.pull_request(commit.stack.github_repo_name, commit.pull_request_number)
|
264
|
+
pull_request.head.sha
|
265
|
+
rescue Octokit::ClientError => error
|
266
|
+
pr_ref = "#{commit.stack.github_repo_name}##{commit.pull_request_number}"
|
267
|
+
Rails.logger.warn("Got #{error.class.name} (#{error.message}) when loading pull request #{pr_ref}")
|
268
|
+
nil
|
224
269
|
end
|
225
270
|
|
226
271
|
def update_release_status
|
@@ -245,10 +290,6 @@ module Shipit
|
|
245
290
|
end
|
246
291
|
end
|
247
292
|
|
248
|
-
def update_commit_deployments
|
249
|
-
commit_deployments.append_status(status)
|
250
|
-
end
|
251
|
-
|
252
293
|
def trigger_revert_if_required
|
253
294
|
return unless rollback_once_aborted?
|
254
295
|
return unless supports_rollback?
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Shipit
|
2
|
+
class DeployStats
|
3
|
+
delegate :empty?, to: :@deploys
|
4
|
+
|
5
|
+
def initialize(deploys)
|
6
|
+
@deploys = deploys
|
7
|
+
@durations = @deploys.map { |d| d.duration.value }.compact
|
8
|
+
end
|
9
|
+
|
10
|
+
def count
|
11
|
+
@deploys.length
|
12
|
+
end
|
13
|
+
|
14
|
+
def average_duration
|
15
|
+
return if empty?
|
16
|
+
@durations.sum / @durations.length.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def max_duration
|
20
|
+
@durations.max
|
21
|
+
end
|
22
|
+
|
23
|
+
def min_duration
|
24
|
+
@durations.min
|
25
|
+
end
|
26
|
+
|
27
|
+
def median_duration
|
28
|
+
return if @durations.empty?
|
29
|
+
(sorted_durations[(@durations.length - 1) / 2] + sorted_durations[@durations.length / 2]) / 2.0
|
30
|
+
end
|
31
|
+
|
32
|
+
def success_rate
|
33
|
+
return if empty?
|
34
|
+
(@deploys.count(&:success?) / @deploys.length.to_f) * 100
|
35
|
+
end
|
36
|
+
|
37
|
+
def compare(compare_stats)
|
38
|
+
{
|
39
|
+
count: percent_change(compare_stats.count, count),
|
40
|
+
average_duration: percent_change(compare_stats.average_duration, average_duration),
|
41
|
+
median_duration: percent_change(compare_stats.median_duration, median_duration),
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def sorted_durations
|
48
|
+
@sorted ||= @durations.sort
|
49
|
+
end
|
50
|
+
|
51
|
+
def percent_change(from, to)
|
52
|
+
return if to.nil? || from.nil?
|
53
|
+
return to * 100 if from.zero?
|
54
|
+
((to - from) / from.to_f) * 100
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Shipit
|
2
|
+
class Repository < ApplicationRecord
|
3
|
+
OWNER_MAX_SIZE = 39
|
4
|
+
private_constant :OWNER_MAX_SIZE
|
5
|
+
|
6
|
+
NAME_MAX_SIZE = 100
|
7
|
+
private_constant :NAME_MAX_SIZE
|
8
|
+
|
9
|
+
validates :name, uniqueness: {scope: %i(owner), case_sensitive: false,
|
10
|
+
message: 'cannot be used more than once'}
|
11
|
+
validates :owner, :name, presence: true, ascii_only: true
|
12
|
+
validates :owner, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: OWNER_MAX_SIZE}
|
13
|
+
validates :name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: NAME_MAX_SIZE}
|
14
|
+
|
15
|
+
has_many :stacks, dependent: :destroy
|
16
|
+
|
17
|
+
def self.from_github_repo_name(github_repo_name)
|
18
|
+
repo_owner, repo_name = github_repo_name.downcase.split('/')
|
19
|
+
find_by(owner: repo_owner, name: repo_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def name=(n)
|
23
|
+
super(n&.downcase)
|
24
|
+
end
|
25
|
+
|
26
|
+
def owner=(o)
|
27
|
+
super(o&.downcase)
|
28
|
+
end
|
29
|
+
|
30
|
+
def http_url
|
31
|
+
Shipit.github.url("#{owner}/#{name}")
|
32
|
+
end
|
33
|
+
|
34
|
+
def git_url
|
35
|
+
"https://#{Shipit.github.domain}/#{owner}/#{name}.git"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/app/models/shipit/stack.rb
CHANGED
@@ -22,8 +22,6 @@ module Shipit
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
REPO_OWNER_MAX_SIZE = 39
|
26
|
-
REPO_NAME_MAX_SIZE = 100
|
27
25
|
ENVIRONMENT_MAX_SIZE = 50
|
28
26
|
REQUIRED_HOOKS = %i(push status).freeze
|
29
27
|
|
@@ -40,6 +38,14 @@ module Shipit
|
|
40
38
|
has_many :hooks, dependent: :destroy
|
41
39
|
has_many :api_clients, dependent: :destroy
|
42
40
|
belongs_to :lock_author, class_name: :User, optional: true
|
41
|
+
belongs_to :repository
|
42
|
+
validates_associated :repository
|
43
|
+
|
44
|
+
scope :not_archived, -> { where(archived_since: nil) }
|
45
|
+
|
46
|
+
def repository
|
47
|
+
super || build_repository
|
48
|
+
end
|
43
49
|
|
44
50
|
def lock_author(*)
|
45
51
|
super || AnonymousUser.new
|
@@ -49,11 +55,6 @@ module Shipit
|
|
49
55
|
super(user&.logged_in? ? user : nil)
|
50
56
|
end
|
51
57
|
|
52
|
-
def self.repo(full_name)
|
53
|
-
repo_owner, repo_name = full_name.downcase.split('/')
|
54
|
-
where(repo_owner: repo_owner, repo_name: repo_name)
|
55
|
-
end
|
56
|
-
|
57
58
|
before_validation :update_defaults
|
58
59
|
before_destroy :clear_local_files
|
59
60
|
before_save :set_locked_since
|
@@ -66,11 +67,10 @@ module Shipit
|
|
66
67
|
after_commit :sync_github, on: :create
|
67
68
|
after_commit :schedule_merges_if_necessary, on: :update
|
68
69
|
|
69
|
-
validates :
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
validates :repo_name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_NAME_MAX_SIZE}
|
70
|
+
validates :repository, uniqueness: {
|
71
|
+
scope: %i(environment), case_sensitive: false,
|
72
|
+
message: 'cannot be used more than once with this environment. Check archived stacks.'
|
73
|
+
}
|
74
74
|
validates :environment, format: {with: /\A[a-z0-9\-_\:]+\z/}, length: {maximum: ENVIRONMENT_MAX_SIZE}
|
75
75
|
validates :deploy_url, format: {with: URI.regexp(%w(http https ssh))}, allow_blank: true
|
76
76
|
|
@@ -157,7 +157,8 @@ module Shipit
|
|
157
157
|
return
|
158
158
|
end
|
159
159
|
|
160
|
-
if commit.deploy_failed? || (checks? && !EphemeralCommitChecks.new(commit).run.success?)
|
160
|
+
if commit.deploy_failed? || (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
|
161
|
+
commit.recently_pushed?
|
161
162
|
continuous_delivery_delayed!
|
162
163
|
return
|
163
164
|
end
|
@@ -320,21 +321,12 @@ module Shipit
|
|
320
321
|
cached_deploy_spec&.pull_request_merge_method || Shipit.default_merge_method
|
321
322
|
end
|
322
323
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
end
|
330
|
-
|
331
|
-
def repo_http_url
|
332
|
-
Shipit.github.url("#{repo_owner}/#{repo_name}")
|
333
|
-
end
|
334
|
-
|
335
|
-
def repo_git_url
|
336
|
-
"https://#{Shipit.github.domain}/#{repo_owner}/#{repo_name}.git"
|
337
|
-
end
|
324
|
+
delegate :name=, to: :repository, prefix: :repo
|
325
|
+
delegate :name, to: :repository, prefix: :repo
|
326
|
+
delegate :owner=, to: :repository, prefix: :repo
|
327
|
+
delegate :owner, to: :repository, prefix: :repo
|
328
|
+
delegate :http_url, to: :repository, prefix: :repo
|
329
|
+
delegate :git_url, to: :repository, prefix: :repo
|
338
330
|
|
339
331
|
def base_path
|
340
332
|
Rails.root.join('data', 'stacks', repo_owner, repo_name, environment)
|
@@ -389,7 +381,7 @@ module Shipit
|
|
389
381
|
if resource.try(:message) == 'Moved Permanently'
|
390
382
|
resource = Shipit.github.api.get(resource.url)
|
391
383
|
end
|
392
|
-
update!(
|
384
|
+
repository.update!(owner: resource.owner.login, name: resource.name)
|
393
385
|
end
|
394
386
|
|
395
387
|
def active_task?
|
@@ -414,6 +406,18 @@ module Shipit
|
|
414
406
|
update!(lock_reason: nil, lock_author: nil, locked_since: nil)
|
415
407
|
end
|
416
408
|
|
409
|
+
def archived?
|
410
|
+
archived_since.present?
|
411
|
+
end
|
412
|
+
|
413
|
+
def archive!(user)
|
414
|
+
update!(archived_since: Time.now, lock_reason: "Archived", lock_author: user)
|
415
|
+
end
|
416
|
+
|
417
|
+
def unarchive!
|
418
|
+
update!(archived_since: nil, lock_reason: nil, lock_author: nil, locked_since: nil)
|
419
|
+
end
|
420
|
+
|
417
421
|
def to_param
|
418
422
|
[repo_owner, repo_name, environment].join('/')
|
419
423
|
end
|
@@ -429,11 +433,14 @@ module Shipit
|
|
429
433
|
|
430
434
|
def self.from_param!(param)
|
431
435
|
repo_owner, repo_name, environment = param.split('/')
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
436
|
+
includes(:repository)
|
437
|
+
.where(
|
438
|
+
repositories: {
|
439
|
+
owner: repo_owner.downcase,
|
440
|
+
name: repo_name.downcase,
|
441
|
+
},
|
442
|
+
environment: environment,
|
443
|
+
).first!
|
437
444
|
end
|
438
445
|
|
439
446
|
delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
|