shipit-engine 0.32.0 → 0.33.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/images/magic-solid.svg +1 -0
- data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
- data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
- data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
- data/app/assets/stylesheets/_pages/_stacks.scss +19 -0
- data/app/assets/stylesheets/shipit.scss +1 -0
- data/app/controllers/shipit/api/{pull_requests_controller.rb → merge_requests_controller.rb} +8 -8
- data/app/controllers/shipit/api/stacks_controller.rb +14 -1
- data/app/controllers/shipit/deploys_controller.rb +2 -2
- data/app/controllers/shipit/merge_requests_controller.rb +31 -0
- data/app/controllers/shipit/merge_status_controller.rb +15 -15
- data/app/controllers/shipit/repositories_controller.rb +74 -0
- data/app/controllers/shipit/tasks_controller.rb +4 -4
- data/app/helpers/shipit/chunks_helper.rb +2 -2
- data/app/helpers/shipit/github_url_helper.rb +8 -0
- data/app/helpers/shipit/stacks_helper.rb +4 -0
- data/app/jobs/shipit/create_on_github_job.rb +1 -0
- data/app/jobs/shipit/destroy_repository_job.rb +24 -0
- data/app/jobs/shipit/destroy_stack_job.rb +2 -2
- data/app/jobs/shipit/perform_task_job.rb +4 -98
- data/app/jobs/shipit/process_merge_requests_job.rb +32 -0
- data/app/jobs/shipit/refresh_merge_request_job.rb +11 -0
- data/app/models/shipit/anonymous_user.rb +4 -0
- data/app/models/shipit/check_run.rb +2 -2
- data/app/models/shipit/command_line_user.rb +4 -0
- data/app/models/shipit/commit.rb +11 -11
- data/app/models/shipit/commit_checks.rb +1 -0
- data/app/models/shipit/deploy.rb +1 -0
- data/app/models/shipit/deploy_spec.rb +16 -4
- data/app/models/shipit/deploy_spec/file_system.rb +11 -5
- data/app/models/shipit/hook.rb +2 -0
- data/app/models/shipit/merge_request.rb +302 -0
- data/app/models/shipit/provisioning_handler.rb +32 -0
- data/app/models/shipit/provisioning_handler/base.rb +30 -0
- data/app/models/shipit/provisioning_handler/unregistered_provisioning_handler.rb +35 -0
- data/app/models/shipit/pull_request.rb +25 -264
- data/app/models/shipit/pull_request_assignment.rb +10 -0
- data/app/models/shipit/repository.rb +54 -0
- data/app/models/shipit/review_stack.rb +116 -0
- data/app/models/shipit/review_stack_provisioning_queue.rb +39 -0
- data/app/models/shipit/stack.rb +22 -8
- data/app/models/shipit/task.rb +56 -7
- data/app/models/shipit/task_execution_strategy/base.rb +20 -0
- data/app/models/shipit/task_execution_strategy/default.rb +110 -0
- data/app/models/shipit/user.rb +6 -1
- data/app/models/shipit/webhooks.rb +10 -0
- data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +74 -0
- data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +68 -0
- data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +74 -0
- data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +127 -0
- data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +106 -0
- data/app/models/shipit/webhooks/handlers/pull_request/opened_handler.rb +83 -0
- data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +88 -0
- data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +103 -0
- data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +107 -0
- data/app/serializers/shipit/deploy_serializer.rb +6 -0
- data/app/serializers/shipit/merge_request_serializer.rb +21 -0
- data/app/serializers/shipit/pull_request_serializer.rb +5 -8
- data/app/serializers/shipit/review_stack_serializer.rb +7 -0
- data/app/serializers/shipit/stack_serializer.rb +7 -6
- data/app/serializers/shipit/tail_task_serializer.rb +10 -2
- data/app/serializers/shipit/task_serializer.rb +1 -1
- data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
- data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
- data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
- data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
- data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
- data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
- data/app/views/shipit/merge_status/failure.html.erb +1 -1
- data/app/views/shipit/merge_status/locked.html.erb +1 -1
- data/app/views/shipit/merge_status/success.html.erb +2 -2
- data/app/views/shipit/repositories/_header.html.erb +19 -0
- data/app/views/shipit/repositories/index.html.erb +31 -0
- data/app/views/shipit/repositories/new.html.erb +23 -0
- data/app/views/shipit/repositories/settings.html.erb +53 -0
- data/app/views/shipit/repositories/show.html.erb +30 -0
- data/app/views/shipit/stacks/_banners.html.erb +13 -0
- data/app/views/shipit/stacks/_header.html.erb +5 -2
- data/app/views/shipit/stacks/_stack.html.erb +8 -0
- data/app/views/shipit/stacks/index.html.erb +2 -1
- data/app/views/shipit/stacks/settings.html.erb +5 -5
- data/app/views/shipit/stacks/show.html.erb +1 -1
- data/app/views/shipit/tasks/_task_output.html.erb +1 -1
- data/config/routes.rb +15 -5
- data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
- data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
- data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
- data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
- data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
- data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
- data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
- data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
- data/lib/shipit.rb +11 -1
- data/lib/shipit/github_app.rb +1 -1
- data/lib/shipit/review_stack_commands.rb +8 -0
- data/lib/shipit/stack_commands.rb +6 -1
- data/lib/shipit/task_commands.rb +1 -0
- data/lib/shipit/version.rb +1 -1
- data/lib/tasks/cron.rake +11 -2
- data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +12 -12
- data/test/controllers/api/outputs_controller_test.rb +1 -0
- data/test/controllers/api/rollback_controller_test.rb +1 -1
- data/test/controllers/api/stacks_controller_test.rb +21 -1
- data/test/controllers/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +6 -6
- data/test/controllers/repositories_controller_test.rb +71 -0
- data/test/controllers/stacks_controller_test.rb +9 -1
- data/test/controllers/tasks_controller_test.rb +14 -2
- data/test/controllers/webhooks_controller_test.rb +1 -1
- data/test/dummy/config/application.rb +6 -1
- data/test/dummy/config/environments/development.rb +0 -3
- data/test/dummy/config/environments/test.rb +0 -5
- data/test/dummy/db/schema.rb +52 -14
- data/test/dummy/db/seeds.rb +1 -1
- data/test/fixtures/payloads/check_suite_master.json +2 -2
- data/test/fixtures/payloads/invalid_pull_request.json +117 -0
- data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
- data/test/fixtures/payloads/pull_request_assigned.json +480 -0
- data/test/fixtures/payloads/pull_request_closed.json +454 -0
- data/test/fixtures/payloads/pull_request_labeled.json +461 -0
- data/test/fixtures/payloads/pull_request_opened.json +454 -0
- data/test/fixtures/payloads/pull_request_reopened.json +454 -0
- data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
- data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
- data/test/fixtures/shipit/commits.yml +15 -2
- data/test/fixtures/shipit/merge_requests.yml +141 -0
- data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
- data/test/fixtures/shipit/pull_requests.yml +10 -131
- data/test/fixtures/shipit/repositories.yml +1 -0
- data/test/fixtures/shipit/stacks.yml +145 -0
- data/test/fixtures/shipit/statuses.yml +9 -0
- data/test/fixtures/shipit/tasks.yml +3 -0
- data/test/fixtures/shipit/users.yml +7 -0
- data/test/helpers/payloads_helper.rb +4 -0
- data/test/jobs/chunk_rollup_job_test.rb +15 -1
- data/test/jobs/destroy_repository_job_test.rb +27 -0
- data/test/jobs/perform_task_job_test.rb +8 -8
- data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +18 -18
- data/test/lib/shipit/deploy_commands_test.rb +16 -0
- data/test/lib/shipit/task_commands_test.rb +17 -0
- data/test/models/commits_test.rb +22 -13
- data/test/models/deploy_spec_test.rb +57 -24
- data/test/models/deploys_test.rb +148 -14
- data/test/models/{pull_request_test.rb → merge_request_test.rb} +30 -30
- data/test/models/pull_request_assignment_test.rb +16 -0
- data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
- data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
- data/test/models/shipit/provisioning_handler_test.rb +64 -0
- data/test/models/shipit/pull_request_test.rb +52 -0
- data/test/models/shipit/repository_test.rb +5 -1
- data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
- data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
- data/test/models/shipit/review_stack_test.rb +59 -0
- data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +10 -4
- data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
- data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
- data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
- data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
- data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
- data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
- data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
- data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +83 -0
- data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
- data/test/models/shipit/{wehbooks → webhooks}/handlers_test.rb +0 -0
- data/test/models/tasks_test.rb +44 -3
- data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
- data/test/unit/command_test.rb +3 -3
- data/test/unit/github_url_helper_test.rb +5 -0
- data/test/unit/shipit_task_execution_strategy_test.rb +47 -0
- metadata +260 -154
- data/app/controllers/shipit/pull_requests_controller.rb +0 -31
- data/app/jobs/shipit/merge_pull_requests_job.rb +0 -32
- data/app/jobs/shipit/refresh_pull_request_job.rb +0 -11
- data/app/views/shipit/pull_requests/_pull_request.html.erb +0 -29
- data/test/fixtures/shipit/output_chunks.yml +0 -47
- data/test/models/output_chunk_test.rb +0 -21
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shipit
|
|
4
|
+
class ReviewStack < Shipit::Stack
|
|
5
|
+
def self.clear_stale_caches
|
|
6
|
+
Shipit::ReviewStack.where(
|
|
7
|
+
"archived_since > :earliest AND archived_since < :latest",
|
|
8
|
+
earliest: 1.day.ago,
|
|
9
|
+
latest: 1.hour.ago
|
|
10
|
+
).each do |review_stack|
|
|
11
|
+
Shipit::ClearGitCacheJob.perform_later(review_stack)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.delete_old_deployment_directories
|
|
16
|
+
Shipit::Deploy.not_active.where(
|
|
17
|
+
"created_at > :earliest AND updated_at < :latest",
|
|
18
|
+
earliest: 1.day.ago,
|
|
19
|
+
latest: 1.hour.ago
|
|
20
|
+
).find_each do |deploy|
|
|
21
|
+
Shipit::Commands.for(deploy).clear_working_directory
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
has_one :pull_request, foreign_key: :stack_id
|
|
26
|
+
|
|
27
|
+
after_commit :emit_added_hooks, on: :create
|
|
28
|
+
after_commit :emit_updated_hooks, on: :update
|
|
29
|
+
after_commit :emit_removed_hooks, on: :destroy
|
|
30
|
+
|
|
31
|
+
state_machine :provision_status, initial: :deprovisioned do
|
|
32
|
+
state :provisioned
|
|
33
|
+
state :provisioning
|
|
34
|
+
state :deprovisioning
|
|
35
|
+
state :deprovisioned
|
|
36
|
+
|
|
37
|
+
event :provision do
|
|
38
|
+
transition deprovisioned: :provisioning
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
event :provision_success do
|
|
42
|
+
transition provisioning: :provisioned
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
event :provision_failure do
|
|
46
|
+
transition provisioning: :deprovisioned
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
event :deprovision do
|
|
50
|
+
transition provisioned: :deprovisioning
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
event :deprovision_success do
|
|
54
|
+
transition deprovisioning: :deprovisioned
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
event :deprovision_failure do
|
|
58
|
+
transition deprovisioning: :provisioned
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
after_transition deprovisioned: :provisioning do |stack, _|
|
|
62
|
+
stack.provisioner.up
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
after_transition provisioned: :deprovisioning do |stack, _|
|
|
66
|
+
stack.provisioner.down
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def env
|
|
71
|
+
return super unless pull_request.present?
|
|
72
|
+
|
|
73
|
+
super
|
|
74
|
+
.merge(
|
|
75
|
+
pull_request
|
|
76
|
+
.labels
|
|
77
|
+
.each_with_object({}) { |label_name, labels| labels[label_name.upcase] = "true" }
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def provisioner
|
|
82
|
+
provisioner_class.new(self)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def provisioner_class
|
|
86
|
+
ProvisioningHandler.fetch(provisioning_handler_name)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def enqueue_for_provisioning
|
|
90
|
+
return if awaiting_provision
|
|
91
|
+
update!(awaiting_provision: true)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def remove_from_provisioning_queue
|
|
95
|
+
return unless awaiting_provision
|
|
96
|
+
update!(awaiting_provision: false)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def to_partial_path
|
|
100
|
+
"shipit/stacks/stack"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def emit_added_hooks
|
|
104
|
+
Hook.emit(:review_stack, self, action: :added, review_stack: self)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def emit_updated_hooks
|
|
108
|
+
changed = !(previous_changes.keys - %w(updated_at)).empty?
|
|
109
|
+
Hook.emit(:review_stack, self, action: :updated, review_stack: self) if changed
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def emit_removed_hooks
|
|
113
|
+
Hook.emit(:review_stack, self, action: :removed, review_stack: self)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shipit
|
|
4
|
+
class ReviewStackProvisioningQueue
|
|
5
|
+
def self.work
|
|
6
|
+
new.work
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.add(stack)
|
|
10
|
+
stack.enqueue_for_provisioning
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.queued_stacks
|
|
14
|
+
new.queued_stacks
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def work
|
|
18
|
+
queued_stacks.find_each(&method(:provision))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def queued_stacks
|
|
22
|
+
@queued_stacks ||= Shipit::ReviewStack
|
|
23
|
+
.with_provision_status(:deprovisioned)
|
|
24
|
+
.where(awaiting_provision: true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def provision(stack)
|
|
30
|
+
if stack.provisioner.provision?
|
|
31
|
+
stack.provision
|
|
32
|
+
else
|
|
33
|
+
Rails.logger.info(
|
|
34
|
+
"Putting review ReviewStack<#{stack.id}> back into the provisioning queue - #provision? was falsey."
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/app/models/shipit/stack.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Shipit
|
|
|
27
27
|
REQUIRED_HOOKS = %i(push status).freeze
|
|
28
28
|
|
|
29
29
|
has_many :commits, dependent: :destroy
|
|
30
|
-
has_many :
|
|
30
|
+
has_many :merge_requests, dependent: :destroy
|
|
31
31
|
has_many :tasks, dependent: :destroy
|
|
32
32
|
has_many :deploys
|
|
33
33
|
has_many :rollbacks
|
|
@@ -44,6 +44,9 @@ module Shipit
|
|
|
44
44
|
|
|
45
45
|
scope :not_archived, -> { where(archived_since: nil) }
|
|
46
46
|
|
|
47
|
+
include DeferredTouch
|
|
48
|
+
deferred_touch repository: :updated_at
|
|
49
|
+
|
|
47
50
|
default_scope { preload(:repository) }
|
|
48
51
|
|
|
49
52
|
def env
|
|
@@ -91,8 +94,17 @@ module Shipit
|
|
|
91
94
|
validates :lock_reason, length: { maximum: 4096 }
|
|
92
95
|
|
|
93
96
|
serialize :cached_deploy_spec, DeploySpec
|
|
94
|
-
delegate
|
|
95
|
-
|
|
97
|
+
delegate(
|
|
98
|
+
:provisioning_handler_name,
|
|
99
|
+
:find_task_definition,
|
|
100
|
+
:release_status?,
|
|
101
|
+
:release_status_context,
|
|
102
|
+
:release_status_delay,
|
|
103
|
+
:supports_fetch_deployed_revision?,
|
|
104
|
+
:supports_rollback?,
|
|
105
|
+
to: :cached_deploy_spec,
|
|
106
|
+
allow_nil: true
|
|
107
|
+
)
|
|
96
108
|
|
|
97
109
|
def self.refresh_deployed_revisions
|
|
98
110
|
find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
|
|
@@ -139,6 +151,7 @@ module Shipit
|
|
|
139
151
|
env: filter_deploy_envs(env&.to_h || {}),
|
|
140
152
|
allow_concurrency: force,
|
|
141
153
|
ignored_safeties: force || !until_commit.deployable?,
|
|
154
|
+
max_retries: retries_on_deploy,
|
|
142
155
|
)
|
|
143
156
|
end
|
|
144
157
|
|
|
@@ -200,7 +213,7 @@ module Shipit
|
|
|
200
213
|
end
|
|
201
214
|
|
|
202
215
|
def schedule_merges
|
|
203
|
-
|
|
216
|
+
ProcessMergeRequestsJob.perform_later(self)
|
|
204
217
|
end
|
|
205
218
|
|
|
206
219
|
def next_commit_to_deploy
|
|
@@ -340,7 +353,7 @@ module Shipit
|
|
|
340
353
|
end
|
|
341
354
|
|
|
342
355
|
def deployable?
|
|
343
|
-
!locked? && !active_task?
|
|
356
|
+
!locked? && !active_task? && !awaiting_provision?
|
|
344
357
|
end
|
|
345
358
|
|
|
346
359
|
def allows_merges?
|
|
@@ -348,7 +361,7 @@ module Shipit
|
|
|
348
361
|
end
|
|
349
362
|
|
|
350
363
|
def merge_method
|
|
351
|
-
cached_deploy_spec&.
|
|
364
|
+
cached_deploy_spec&.merge_request_merge_method || Shipit.default_merge_method
|
|
352
365
|
end
|
|
353
366
|
|
|
354
367
|
delegate :name=, to: :repository, prefix: :repo
|
|
@@ -384,7 +397,7 @@ module Shipit
|
|
|
384
397
|
end
|
|
385
398
|
|
|
386
399
|
def github_repo_name
|
|
387
|
-
|
|
400
|
+
repository.github_repo_name
|
|
388
401
|
end
|
|
389
402
|
|
|
390
403
|
def github_commits
|
|
@@ -475,7 +488,8 @@ module Shipit
|
|
|
475
488
|
|
|
476
489
|
delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
|
|
477
490
|
:blocking_statuses, :deploy_variables, :filter_task_envs, :filter_deploy_envs,
|
|
478
|
-
:maximum_commits_per_deploy, :pause_between_deploys,
|
|
491
|
+
:maximum_commits_per_deploy, :pause_between_deploys, :retries_on_deploy, :retries_on_rollback,
|
|
492
|
+
to: :cached_deploy_spec
|
|
479
493
|
|
|
480
494
|
def monitoring?
|
|
481
495
|
monitoring.present?
|
data/app/models/shipit/task.rb
CHANGED
|
@@ -27,8 +27,6 @@ module Shipit
|
|
|
27
27
|
|
|
28
28
|
deferred_touch stack: :updated_at
|
|
29
29
|
|
|
30
|
-
has_many :chunks, -> { order(:id) }, class_name: 'OutputChunk', dependent: :delete_all, inverse_of: :task
|
|
31
|
-
|
|
32
30
|
serialize :definition, TaskDefinition
|
|
33
31
|
serialize :env, Hash
|
|
34
32
|
|
|
@@ -79,6 +77,10 @@ module Shipit
|
|
|
79
77
|
task.async_refresh_deployed_revision
|
|
80
78
|
end
|
|
81
79
|
|
|
80
|
+
after_transition any => %i(aborted success failed error timedout) do |task|
|
|
81
|
+
task.schedule_rollup_chunks
|
|
82
|
+
end
|
|
83
|
+
|
|
82
84
|
after_transition any => :flapping do |task|
|
|
83
85
|
task.update!(confirmations: 0)
|
|
84
86
|
end
|
|
@@ -87,6 +89,10 @@ module Shipit
|
|
|
87
89
|
task.async_update_estimated_deploy_duration
|
|
88
90
|
end
|
|
89
91
|
|
|
92
|
+
after_transition any => %i(failed error timedout) do |task|
|
|
93
|
+
task.retry_if_necessary
|
|
94
|
+
end
|
|
95
|
+
|
|
90
96
|
event :run do
|
|
91
97
|
transition pending: :running
|
|
92
98
|
end
|
|
@@ -197,16 +203,16 @@ module Shipit
|
|
|
197
203
|
|
|
198
204
|
def write(text)
|
|
199
205
|
log_output(text)
|
|
200
|
-
|
|
206
|
+
Shipit.redis.append(output_key, text)
|
|
201
207
|
end
|
|
202
208
|
|
|
203
209
|
def chunk_output
|
|
204
210
|
if rolled_up?
|
|
205
211
|
output
|
|
206
212
|
else
|
|
207
|
-
blob =
|
|
213
|
+
blob = Shipit.redis.get(output_key)
|
|
208
214
|
|
|
209
|
-
if blob.size > OUTPUT_SIZE_LIMIT
|
|
215
|
+
if blob && blob.size > OUTPUT_SIZE_LIMIT
|
|
210
216
|
Rails.logger.warn("Task #{id} output exceeds limit of #{HUMAN_READABLE_OUTPUT_LIMIT}, and will be truncated.")
|
|
211
217
|
blob = blob.last(OUTPUT_SIZE_LIMIT - OUTPUT_TRUNCATED_MESSAGE.size)
|
|
212
218
|
blob = OUTPUT_TRUNCATED_MESSAGE + blob
|
|
@@ -216,15 +222,30 @@ module Shipit
|
|
|
216
222
|
end
|
|
217
223
|
end
|
|
218
224
|
|
|
225
|
+
def chunk_output_size
|
|
226
|
+
return 0 if rolled_up?
|
|
227
|
+
|
|
228
|
+
Shipit.redis.strlen(output_key)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def tail_output(range_start)
|
|
232
|
+
Shipit.redis.getrange(output_key, range_start || 0, -1)
|
|
233
|
+
end
|
|
234
|
+
|
|
219
235
|
def schedule_rollup_chunks
|
|
220
236
|
ChunkRollupJob.perform_later(self)
|
|
221
237
|
end
|
|
222
238
|
|
|
223
239
|
def rollup_chunks
|
|
224
240
|
ActiveRecord::Base.transaction do
|
|
225
|
-
|
|
226
|
-
chunks
|
|
241
|
+
chunks = Shipit::OutputChunk.where(task: self).pluck(:text)
|
|
242
|
+
chunks << chunk_output
|
|
243
|
+
self.output = chunks.join("\n")
|
|
244
|
+
|
|
227
245
|
update_attribute(:rolled_up, true)
|
|
246
|
+
|
|
247
|
+
Shipit.redis.del(output_key)
|
|
248
|
+
Shipit::OutputChunk.where(task: self).delete_all
|
|
228
249
|
end
|
|
229
250
|
end
|
|
230
251
|
|
|
@@ -382,12 +403,32 @@ module Shipit
|
|
|
382
403
|
.reject(&:alive?)
|
|
383
404
|
end
|
|
384
405
|
|
|
406
|
+
def retry_if_necessary
|
|
407
|
+
return unless retries_configured? && !stack.reload.locked?
|
|
408
|
+
|
|
409
|
+
if retry_attempt < max_retries
|
|
410
|
+
retry_task = duplicate_task
|
|
411
|
+
retry_task.retry_attempt = duplicate_task.retry_attempt + 1
|
|
412
|
+
retry_task.save!
|
|
413
|
+
|
|
414
|
+
retry_task.enqueue
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def retries_configured?
|
|
419
|
+
!max_retries.nil? && max_retries > 0
|
|
420
|
+
end
|
|
421
|
+
|
|
385
422
|
private
|
|
386
423
|
|
|
387
424
|
def prevent_concurrency
|
|
388
425
|
raise ConcurrentTaskRunning if stack.tasks.active.exclusive.count > 1
|
|
389
426
|
end
|
|
390
427
|
|
|
428
|
+
def output_key
|
|
429
|
+
"#{status_key}:output"
|
|
430
|
+
end
|
|
431
|
+
|
|
391
432
|
def status_key
|
|
392
433
|
"shipit:task:#{id}"
|
|
393
434
|
end
|
|
@@ -405,5 +446,13 @@ module Shipit
|
|
|
405
446
|
def output_line_buffer
|
|
406
447
|
@output_line_buffer ||= LineBuffer.new
|
|
407
448
|
end
|
|
449
|
+
|
|
450
|
+
def duplicate_task
|
|
451
|
+
copy_task = dup
|
|
452
|
+
copy_task.status = 'pending'
|
|
453
|
+
copy_task.started_at = nil
|
|
454
|
+
copy_task.ended_at = nil
|
|
455
|
+
copy_task
|
|
456
|
+
end
|
|
408
457
|
end
|
|
409
458
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shipit
|
|
4
|
+
module TaskExecutionStrategy
|
|
5
|
+
class Base
|
|
6
|
+
def initialize(task)
|
|
7
|
+
self.task = task
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def execute
|
|
11
|
+
raise(
|
|
12
|
+
NotImplmentedError,
|
|
13
|
+
"subclasses of TaskExectuionStrategy::Base must implement the #execute method"
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_accessor :task
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shipit
|
|
4
|
+
module TaskExecutionStrategy
|
|
5
|
+
class Default < Base
|
|
6
|
+
def execute
|
|
7
|
+
@commands = Commands.for(@task)
|
|
8
|
+
unless @task.pending?
|
|
9
|
+
Rails.logger.error("Task ##{@task.id} already in `#{@task.status}` state. Aborting.")
|
|
10
|
+
return
|
|
11
|
+
end
|
|
12
|
+
run
|
|
13
|
+
ensure
|
|
14
|
+
@commands.clear_working_directory
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
@task.ping
|
|
19
|
+
@task.run!
|
|
20
|
+
checkout_repository
|
|
21
|
+
perform_task
|
|
22
|
+
@task.write("\nCompleted successfully\n")
|
|
23
|
+
@task.report_complete!
|
|
24
|
+
rescue Command::TimedOut => error
|
|
25
|
+
@task.write("\n#{error.message}\n")
|
|
26
|
+
@task.report_timeout!(error)
|
|
27
|
+
rescue Command::Error => error
|
|
28
|
+
@task.write("\n#{error.message}\n")
|
|
29
|
+
@task.report_failure!(error)
|
|
30
|
+
rescue StandardError => error
|
|
31
|
+
@task.report_error!(error)
|
|
32
|
+
rescue Exception => error
|
|
33
|
+
@task.report_error!(error)
|
|
34
|
+
raise
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def abort!(signal: 'TERM')
|
|
38
|
+
pid = @task.pid
|
|
39
|
+
if pid
|
|
40
|
+
@task.write("$ kill #{pid}\n")
|
|
41
|
+
Process.kill(signal, pid)
|
|
42
|
+
else
|
|
43
|
+
@task.write("Can't abort, no recorded pid, WTF?\n")
|
|
44
|
+
end
|
|
45
|
+
rescue SystemCallError => error
|
|
46
|
+
@task.write("kill: (#{pid}) - #{error.message}\n")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def check_for_abort
|
|
50
|
+
@task.should_abort? do |times_killed|
|
|
51
|
+
if times_killed > 3
|
|
52
|
+
abort!(signal: 'KILL')
|
|
53
|
+
else
|
|
54
|
+
abort!
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def perform_task
|
|
60
|
+
capture_all!(@commands.install_dependencies)
|
|
61
|
+
capture_all!(@commands.perform)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def checkout_repository
|
|
65
|
+
unless @commands.fetched?(@task.until_commit).tap(&:run).success?
|
|
66
|
+
# acquire_git_cache_lock can take upto 15 seconds
|
|
67
|
+
# to process. Try to make sure that the job isn't
|
|
68
|
+
# marked dead while we attempt to acquire the lock.
|
|
69
|
+
@task.ping
|
|
70
|
+
@task.acquire_git_cache_lock do
|
|
71
|
+
@task.ping
|
|
72
|
+
unless @commands.fetched?(@task.until_commit).tap(&:run).success?
|
|
73
|
+
capture!(@commands.fetch)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
capture_all!(@commands.clone)
|
|
78
|
+
capture!(@commands.checkout(@task.until_commit))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def capture_all!(commands)
|
|
82
|
+
commands.map { |c| capture!(c) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def capture!(command)
|
|
86
|
+
started_at = Time.now
|
|
87
|
+
command.start do
|
|
88
|
+
@task.ping
|
|
89
|
+
check_for_abort
|
|
90
|
+
end
|
|
91
|
+
@task.write("$ #{command}\npid: #{command.pid}\nstarted at: #{started_at}\n")
|
|
92
|
+
@task.pid = command.pid
|
|
93
|
+
command.stream! do |line|
|
|
94
|
+
@task.write(line)
|
|
95
|
+
end
|
|
96
|
+
@task.write("\n")
|
|
97
|
+
finished_at = Time.now
|
|
98
|
+
@task.write("pid: #{command.pid}\nfinished at: #{finished_at}\nran in: #{finished_at - started_at} seconds\n")
|
|
99
|
+
command.success?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def capture(command)
|
|
103
|
+
capture!(command)
|
|
104
|
+
command.success?
|
|
105
|
+
rescue Command::Error
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|