shipit-engine 0.32.0 → 0.35.1

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.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -2
  3. data/app/assets/images/magic-solid.svg +1 -0
  4. data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
  5. data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
  6. data/app/assets/stylesheets/_pages/_deploy.scss +2 -3
  7. data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
  8. data/app/assets/stylesheets/_pages/_stacks.scss +19 -0
  9. data/app/assets/stylesheets/shipit.scss +1 -0
  10. data/app/controllers/shipit/api/ccmenu_controller.rb +1 -1
  11. data/app/controllers/shipit/api/deploys_controller.rb +2 -0
  12. data/app/controllers/shipit/api/{pull_requests_controller.rb → merge_requests_controller.rb} +8 -8
  13. data/app/controllers/shipit/api/rollbacks_controller.rb +2 -1
  14. data/app/controllers/shipit/api/stacks_controller.rb +15 -1
  15. data/app/controllers/shipit/deploys_controller.rb +1 -1
  16. data/app/controllers/shipit/merge_requests_controller.rb +31 -0
  17. data/app/controllers/shipit/merge_status_controller.rb +15 -15
  18. data/app/controllers/shipit/repositories_controller.rb +74 -0
  19. data/app/controllers/shipit/stacks_controller.rb +2 -2
  20. data/app/controllers/shipit/tasks_controller.rb +2 -2
  21. data/app/controllers/shipit/webhooks_controller.rb +23 -4
  22. data/app/helpers/shipit/chunks_helper.rb +2 -2
  23. data/app/helpers/shipit/github_url_helper.rb +8 -0
  24. data/app/helpers/shipit/shipit_helper.rb +0 -1
  25. data/app/helpers/shipit/stacks_helper.rb +4 -0
  26. data/app/jobs/shipit/create_on_github_job.rb +1 -0
  27. data/app/jobs/shipit/deliver_hook_job.rb +1 -1
  28. data/app/jobs/shipit/destroy_repository_job.rb +24 -0
  29. data/app/jobs/shipit/destroy_stack_job.rb +2 -2
  30. data/app/jobs/shipit/github_sync_job.rb +13 -9
  31. data/app/jobs/shipit/perform_task_job.rb +4 -98
  32. data/app/jobs/shipit/process_merge_requests_job.rb +32 -0
  33. data/app/jobs/shipit/refresh_merge_request_job.rb +11 -0
  34. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +1 -1
  35. data/app/models/shipit/anonymous_user.rb +10 -2
  36. data/app/models/shipit/check_run.rb +38 -2
  37. data/app/models/shipit/command_line_user.rb +4 -0
  38. data/app/models/shipit/commit.rb +31 -20
  39. data/app/models/shipit/commit_checks.rb +14 -13
  40. data/app/models/shipit/commit_deployment.rb +3 -3
  41. data/app/models/shipit/commit_deployment_status.rb +3 -3
  42. data/app/models/shipit/deploy.rb +17 -11
  43. data/app/models/shipit/deploy_spec/file_system.rb +11 -5
  44. data/app/models/shipit/deploy_spec/lerna_discovery.rb +12 -4
  45. data/app/models/shipit/deploy_spec.rb +16 -4
  46. data/app/models/shipit/duration.rb +2 -0
  47. data/app/models/shipit/hook.rb +28 -2
  48. data/app/models/shipit/merge_request.rb +304 -0
  49. data/app/models/shipit/provisioning_handler/base.rb +30 -0
  50. data/app/models/shipit/provisioning_handler/unregistered_provisioning_handler.rb +35 -0
  51. data/app/models/shipit/provisioning_handler.rb +32 -0
  52. data/app/models/shipit/pull_request.rb +26 -265
  53. data/app/models/shipit/pull_request_assignment.rb +10 -0
  54. data/app/models/shipit/release_status.rb +1 -1
  55. data/app/models/shipit/repository.rb +63 -3
  56. data/app/models/shipit/review_stack.rb +130 -0
  57. data/app/models/shipit/review_stack_provisioning_queue.rb +39 -0
  58. data/app/models/shipit/rollback.rb +5 -0
  59. data/app/models/shipit/stack.rb +78 -30
  60. data/app/models/shipit/status/group.rb +1 -1
  61. data/app/models/shipit/task.rb +62 -9
  62. data/app/models/shipit/task_execution_strategy/base.rb +20 -0
  63. data/app/models/shipit/task_execution_strategy/default.rb +109 -0
  64. data/app/models/shipit/team.rb +4 -2
  65. data/app/models/shipit/user.rb +10 -1
  66. data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +74 -0
  67. data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +68 -0
  68. data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +74 -0
  69. data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +127 -0
  70. data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +106 -0
  71. data/app/models/shipit/webhooks/handlers/pull_request/opened_handler.rb +83 -0
  72. data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +88 -0
  73. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +103 -0
  74. data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +107 -0
  75. data/app/models/shipit/webhooks/handlers/push_handler.rb +4 -1
  76. data/app/models/shipit/webhooks.rb +10 -0
  77. data/app/serializers/shipit/deploy_serializer.rb +6 -0
  78. data/app/serializers/shipit/merge_request_serializer.rb +21 -0
  79. data/app/serializers/shipit/pull_request_serializer.rb +5 -8
  80. data/app/serializers/shipit/review_stack_serializer.rb +7 -0
  81. data/app/serializers/shipit/stack_serializer.rb +7 -6
  82. data/app/serializers/shipit/tail_task_serializer.rb +10 -2
  83. data/app/serializers/shipit/task_serializer.rb +1 -1
  84. data/app/validators/subset_validator.rb +1 -1
  85. data/app/views/layouts/merge_status.html.erb +1 -1
  86. data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
  87. data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
  88. data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
  89. data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
  90. data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
  91. data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
  92. data/app/views/shipit/merge_status/failure.html.erb +1 -1
  93. data/app/views/shipit/merge_status/locked.html.erb +1 -1
  94. data/app/views/shipit/merge_status/success.html.erb +2 -2
  95. data/app/views/shipit/repositories/_header.html.erb +19 -0
  96. data/app/views/shipit/repositories/index.html.erb +31 -0
  97. data/app/views/shipit/repositories/new.html.erb +23 -0
  98. data/app/views/shipit/repositories/settings.html.erb +53 -0
  99. data/app/views/shipit/repositories/show.html.erb +30 -0
  100. data/app/views/shipit/stacks/_banners.html.erb +15 -1
  101. data/app/views/shipit/stacks/_header.html.erb +5 -2
  102. data/app/views/shipit/stacks/_stack.html.erb +8 -0
  103. data/app/views/shipit/stacks/index.html.erb +2 -1
  104. data/app/views/shipit/stacks/new.html.erb +1 -1
  105. data/app/views/shipit/stacks/settings.html.erb +5 -5
  106. data/app/views/shipit/stacks/show.html.erb +1 -1
  107. data/app/views/shipit/tasks/_task_output.html.erb +1 -1
  108. data/config/routes.rb +15 -5
  109. data/config/secrets.development.example.yml +24 -0
  110. data/config/secrets.development.shopify.yml +20 -9
  111. data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
  112. data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
  113. data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
  114. data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
  115. data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
  116. data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
  117. data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
  118. data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
  119. data/db/migrate/20210325194053_remove_stacks_branch_default.rb +5 -0
  120. data/db/migrate/20210504200438_add_github_updated_at_to_check_runs.rb +5 -0
  121. data/db/migrate/20210823075617_change_check_runs_github_updated_at_default.rb +5 -0
  122. data/lib/shipit/command.rb +7 -6
  123. data/lib/shipit/commands.rb +18 -5
  124. data/lib/shipit/engine.rb +2 -0
  125. data/lib/shipit/flock.rb +8 -1
  126. data/lib/shipit/github_app.rb +8 -6
  127. data/lib/shipit/octokit_iterator.rb +3 -3
  128. data/lib/shipit/review_stack_commands.rb +8 -0
  129. data/lib/shipit/simple_message_verifier.rb +2 -2
  130. data/lib/shipit/stack_commands.rb +36 -7
  131. data/lib/shipit/task_commands.rb +8 -1
  132. data/lib/shipit/version.rb +1 -1
  133. data/lib/shipit.rb +50 -16
  134. data/lib/snippets/publish-lerna-independent-packages +35 -34
  135. data/lib/snippets/publish-lerna-independent-packages-legacy +39 -0
  136. data/lib/tasks/cron.rake +11 -2
  137. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  138. data/test/controllers/api/deploys_controller_test.rb +17 -0
  139. data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +12 -12
  140. data/test/controllers/api/outputs_controller_test.rb +1 -0
  141. data/test/controllers/api/rollback_controller_test.rb +1 -1
  142. data/test/controllers/api/stacks_controller_test.rb +42 -8
  143. data/test/controllers/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +6 -6
  144. data/test/controllers/repositories_controller_test.rb +71 -0
  145. data/test/controllers/stacks_controller_test.rb +9 -1
  146. data/test/controllers/tasks_controller_test.rb +14 -2
  147. data/test/controllers/webhooks_controller_test.rb +27 -12
  148. data/test/dummy/app/assets/config/manifest.js +3 -0
  149. data/test/dummy/config/application.rb +7 -2
  150. data/test/dummy/config/database.yml +9 -0
  151. data/test/dummy/config/environments/development.rb +1 -4
  152. data/test/dummy/config/environments/test.rb +0 -5
  153. data/test/dummy/config/secrets_double_github_app.yml +79 -0
  154. data/test/dummy/db/schema.rb +56 -17
  155. data/test/dummy/db/seeds.rb +2 -1
  156. data/test/fixtures/payloads/check_suite_master.json +4 -32
  157. data/test/fixtures/payloads/invalid_pull_request.json +117 -0
  158. data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
  159. data/test/fixtures/payloads/pull_request_assigned.json +480 -0
  160. data/test/fixtures/payloads/pull_request_closed.json +454 -0
  161. data/test/fixtures/payloads/pull_request_labeled.json +461 -0
  162. data/test/fixtures/payloads/pull_request_opened.json +454 -0
  163. data/test/fixtures/payloads/pull_request_reopened.json +454 -0
  164. data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
  165. data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
  166. data/test/fixtures/payloads/push_master.json +1 -1
  167. data/test/fixtures/payloads/push_not_master.json +1 -1
  168. data/test/fixtures/shipit/commits.yml +17 -4
  169. data/test/fixtures/shipit/hooks.yml +1 -0
  170. data/test/fixtures/shipit/merge_requests.yml +141 -0
  171. data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
  172. data/test/fixtures/shipit/pull_requests.yml +10 -131
  173. data/test/fixtures/shipit/repositories.yml +1 -0
  174. data/test/fixtures/shipit/stacks.yml +145 -0
  175. data/test/fixtures/shipit/statuses.yml +9 -0
  176. data/test/fixtures/shipit/tasks.yml +4 -1
  177. data/test/fixtures/shipit/users.yml +7 -0
  178. data/test/helpers/json_helper.rb +5 -1
  179. data/test/helpers/payloads_helper.rb +4 -0
  180. data/test/jobs/chunk_rollup_job_test.rb +15 -1
  181. data/test/jobs/destroy_repository_job_test.rb +27 -0
  182. data/test/jobs/github_sync_job_test.rb +2 -1
  183. data/test/jobs/perform_task_job_test.rb +8 -8
  184. data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +18 -18
  185. data/test/lib/shipit/deploy_commands_test.rb +16 -0
  186. data/test/lib/shipit/task_commands_test.rb +17 -0
  187. data/test/models/commit_deployment_status_test.rb +3 -3
  188. data/test/models/commits_test.rb +24 -13
  189. data/test/models/deploy_spec_test.rb +64 -24
  190. data/test/models/deploys_test.rb +188 -14
  191. data/test/models/hook_test.rb +30 -1
  192. data/test/models/{pull_request_test.rb → merge_request_test.rb} +49 -34
  193. data/test/models/pull_request_assignment_test.rb +16 -0
  194. data/test/models/shipit/check_run_test.rb +124 -5
  195. data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
  196. data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
  197. data/test/models/shipit/provisioning_handler_test.rb +64 -0
  198. data/test/models/shipit/pull_request_test.rb +52 -0
  199. data/test/models/shipit/repository_test.rb +5 -1
  200. data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
  201. data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
  202. data/test/models/shipit/review_stack_test.rb +91 -0
  203. data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +52 -8
  204. data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
  205. data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
  206. data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
  207. data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
  208. data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
  209. data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
  210. data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
  211. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +107 -0
  212. data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
  213. data/test/models/shipit/{wehbooks → webhooks}/handlers_test.rb +0 -0
  214. data/test/models/tasks_test.rb +66 -3
  215. data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
  216. data/test/test_helper.rb +15 -0
  217. data/test/unit/anonymous_user_serializer_test.rb +1 -1
  218. data/test/unit/command_test.rb +8 -3
  219. data/test/unit/commit_serializer_test.rb +1 -1
  220. data/test/unit/deploy_commands_test.rb +73 -17
  221. data/test/unit/deploy_serializer_test.rb +1 -1
  222. data/test/unit/github_app_test.rb +2 -3
  223. data/test/unit/github_apps_test.rb +416 -0
  224. data/test/unit/github_url_helper_test.rb +5 -0
  225. data/test/unit/shipit_deployment_checks_test.rb +77 -0
  226. data/test/unit/shipit_task_execution_strategy_test.rb +47 -0
  227. data/test/unit/shipit_test.rb +14 -0
  228. data/test/unit/user_serializer_test.rb +1 -1
  229. metadata +306 -188
  230. data/app/controllers/shipit/pull_requests_controller.rb +0 -31
  231. data/app/jobs/shipit/merge_pull_requests_job.rb +0 -32
  232. data/app/jobs/shipit/refresh_pull_request_job.rb +0 -11
  233. data/app/views/shipit/pull_requests/_pull_request.html.erb +0 -29
  234. data/test/fixtures/shipit/output_chunks.yml +0 -47
  235. data/test/models/output_chunk_test.rb +0 -21
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shipit
4
+ module ProvisioningHandler
5
+ class Base
6
+ def initialize(stack)
7
+ @stack = stack
8
+ end
9
+
10
+ def up
11
+ # Intentionally a noop
12
+ end
13
+
14
+ def down
15
+ # Intentionally a noop
16
+ end
17
+
18
+ # An (optional) guard to prevent provisioning. Intended to be
19
+ # use to set logic to determine if enough actual resources exist
20
+ # to complete the provisioning request.
21
+ def provision?
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ attr_accessor :stack
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shipit
4
+ module ProvisioningHandler
5
+ class UnregisteredProvisioningHandler < Shipit::ProvisioningHandler::Base
6
+ def up
7
+ lock_and_prevent_transition
8
+ end
9
+
10
+ def down
11
+ lock_and_prevent_transition
12
+ end
13
+
14
+ private
15
+
16
+ def lock_and_prevent_transition
17
+ stack.lock(
18
+ "Failed to find a provisioning handler named '#{stack.provisioning_handler_name}' in the " \
19
+ "ProvisioningHandler registry. Have you registered it via Provisioning::Handler.register?",
20
+ Shipit::AnonymousUser.new
21
+ )
22
+
23
+ # Prevent transition of the ReviewStack 'provision_status'
24
+ # state machine. This signals to the state_machines gem that
25
+ # the transition should be canceled.
26
+ #
27
+ # References:
28
+ #
29
+ # - https://github.com/state-machines/state_machines/blob/309668998449ca6c348de809f34660d822bc626e/lib/state_machines/callback.rb#L81-L89
30
+ # - https://github.com/state-machines/state_machines/blob/309668998449ca6c348de809f34660d822bc626e/lib/state_machines/transition_collection.rb#L63
31
+ throw(:halt)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shipit
4
+ module ProvisioningHandler
5
+ class << self
6
+ def registry
7
+ @registry ||= reset_registry!
8
+ end
9
+
10
+ def reset_registry!
11
+ @registry = {}
12
+ end
13
+
14
+ def register(handler_class)
15
+ registry[handler_class.to_s] = handler_class
16
+ end
17
+
18
+ def fetch(name)
19
+ return default if name.blank?
20
+ registry.fetch(name) { ProvisioningHandler::UnregisteredProvisioningHandler }
21
+ end
22
+
23
+ def default=(handler_class)
24
+ registry[:default] = handler_class
25
+ end
26
+
27
+ def default
28
+ registry.fetch(:default) { ProvisioningHandler::Base }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,299 +1,60 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
- class PullRequest < ApplicationRecord
4
+ class PullRequest < Record
4
5
  include DeferredTouch
5
6
 
6
- MERGE_REQUEST_FIELD = 'Merge-Requested-By'
7
-
8
- WAITING_STATUSES = %w(fetching pending).freeze
9
- QUEUED_STATUSES = %w(pending revalidating).freeze
10
- REJECTION_REASONS = %w(ci_missing ci_failing merge_conflict requires_rebase).freeze
11
- InvalidTransition = Class.new(StandardError)
12
- NotReady = Class.new(StandardError)
13
-
14
- class StatusChecker < Status::Group
15
- def initialize(commit, statuses, deploy_spec)
16
- @deploy_spec = deploy_spec
17
- super(commit, statuses)
18
- end
19
-
20
- private
21
-
22
- attr_reader :deploy_spec
23
-
24
- def reject_hidden(statuses)
25
- statuses.reject { |s| ignored_statuses.include?(s.context) }
26
- end
27
-
28
- def reject_allowed_to_fail(statuses)
29
- statuses.reject { |s| ignored_statuses.include?(s.context) }
30
- end
31
-
32
- def ignored_statuses
33
- deploy_spec&.pull_request_ignored_statuses || []
34
- end
35
-
36
- def required_statuses
37
- deploy_spec&.pull_request_required_statuses || []
38
- end
39
- end
40
-
41
7
  belongs_to :stack
8
+ belongs_to :user
42
9
  belongs_to :head, class_name: 'Shipit::Commit', optional: true
43
- belongs_to :base_commit, class_name: 'Shipit::Commit', optional: true
44
- belongs_to :merge_requested_by, class_name: 'Shipit::User', optional: true
45
- has_one :merge_commit, class_name: 'Shipit::Commit'
46
-
47
- deferred_touch stack: :updated_at
48
-
49
- validates :number, presence: true, uniqueness: { scope: :stack_id }
50
-
51
- scope :waiting, -> { where(merge_status: WAITING_STATUSES) }
52
- scope :pending, -> { where(merge_status: 'pending') }
53
- scope :to_be_merged, -> { pending.order(merge_requested_at: :asc) }
54
- scope :queued, -> { where(merge_status: QUEUED_STATUSES).order(merge_requested_at: :asc) }
55
-
56
- after_save :record_merge_status_change
57
- after_commit :emit_hooks
58
-
59
- state_machine :merge_status, initial: :fetching do
60
- state :fetching
61
- state :pending
62
- state :rejected
63
- state :canceled
64
- state :merged
65
- state :revalidating
66
10
 
67
- event :fetched do
68
- transition fetching: :pending
69
- end
70
-
71
- event :reject do
72
- transition pending: :rejected
73
- end
11
+ has_many :pull_request_assignments
12
+ has_many :assignees, class_name: :User, through: :pull_request_assignments, source: :user
74
13
 
75
- event :revalidate do
76
- transition pending: :revalidating
77
- end
78
-
79
- event :cancel do
80
- transition any => :canceled
81
- end
14
+ serialize :labels, Array
82
15
 
83
- event :complete do
84
- transition pending: :merged
85
- end
86
-
87
- event :retry do
88
- transition %i(rejected canceled revalidating) => :pending
89
- end
90
-
91
- before_transition rejected: any do |pr|
92
- pr.rejection_reason = nil
93
- end
16
+ after_create_commit :emit_create_hooks
17
+ after_update_commit :emit_update_hooks
18
+ after_destroy_commit :emit_destroy_hooks
94
19
 
95
- before_transition %i(fetching rejected canceled) => :pending do |pr|
96
- pr.merge_requested_at = Time.now.utc
97
- end
98
-
99
- before_transition any => :pending do |pr|
100
- pr.revalidated_at = Time.now.utc
101
- end
102
-
103
- before_transition %i(pending) => :merged do |pr|
104
- Stack.increment_counter(:undeployed_commits_count, pr.stack_id)
105
- end
20
+ def emit_destroy_hooks
21
+ emit_hooks(:destroyed)
106
22
  end
107
23
 
108
- def self.schedule_merges
109
- Shipit::Stack.where(merge_queue_enabled: true).find_each(&:schedule_merges)
24
+ def emit_create_hooks
25
+ emit_hooks(:created)
110
26
  end
111
27
 
112
- def self.extract_number(stack, number_or_url)
113
- case number_or_url
114
- when /\A#?(\d+)\z/
115
- $1.to_i
116
- when %r{\Ahttps://#{Regexp.escape(Shipit.github.domain)}/([^/]+)/([^/]+)/pull/(\d+)}
117
- return unless $1.downcase == stack.repo_owner.downcase
118
- return unless $2.downcase == stack.repo_name.downcase
119
- $3.to_i
120
- end
28
+ def emit_update_hooks
29
+ emit_hooks(:updated)
121
30
  end
122
31
 
123
- def self.request_merge!(stack, number, user)
124
- now = Time.now.utc
125
- pull_request = begin
126
- create_with(
127
- merge_requested_at: now,
128
- merge_requested_by: user.presence,
129
- ).find_or_create_by!(
130
- stack: stack,
131
- number: number,
132
- )
133
- rescue ActiveRecord::RecordNotUnique
134
- retry
135
- end
136
- pull_request.update!(merge_requested_by: user.presence)
137
- pull_request.retry! if pull_request.rejected? || pull_request.canceled? || pull_request.revalidating?
138
- pull_request.schedule_refresh!
139
- pull_request
140
- end
141
-
142
- def reject!(reason)
143
- unless REJECTION_REASONS.include?(reason)
144
- raise ArgumentError, "invalid reason: #{reason.inspect}, must be one of: #{REJECTION_REASONS.inspect}"
145
- end
146
- self.rejection_reason = reason.presence
147
- super()
148
- true
149
- end
150
-
151
- def reject_unless_mergeable!
152
- return reject!('merge_conflict') if merge_conflict?
153
- return reject!('ci_missing') if any_status_checks_missing?
154
- return reject!('ci_failing') if any_status_checks_failed?
155
- return reject!('requires_rebase') if stale?
156
- false
157
- end
158
-
159
- def merge!
160
- raise InvalidTransition unless pending?
161
-
162
- raise NotReady if not_mergeable_yet?
163
-
164
- Shipit.github.api.merge_pull_request(
165
- stack.github_repo_name,
166
- number,
167
- merge_message,
168
- sha: head.sha,
169
- commit_message: 'Merged by Shipit',
170
- merge_method: stack.merge_method,
171
- )
172
- begin
173
- if Shipit.github.api.pull_requests(stack.github_repo_name, base: branch).empty?
174
- Shipit.github.api.delete_branch(stack.github_repo_name, branch)
175
- end
176
- rescue Octokit::UnprocessableEntity
177
- # branch was already deleted somehow
178
- end
179
- complete!
180
- true
181
- rescue Octokit::MethodNotAllowed # merge conflict
182
- reject!('merge_conflict')
183
- false
184
- rescue Octokit::Conflict # shas didn't match, PR was updated.
185
- raise NotReady
186
- end
187
-
188
- def all_status_checks_passed?
189
- return false unless head
190
- StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec).success?
191
- end
192
-
193
- def any_status_checks_failed?
194
- status = StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec)
195
- status.failure? || status.error?
196
- end
197
-
198
- def any_status_checks_missing?
199
- StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec).missing?
200
- end
201
-
202
- def waiting?
203
- WAITING_STATUSES.include?(merge_status)
204
- end
205
-
206
- def need_revalidation?
207
- timeout = stack.cached_deploy_spec&.revalidate_pull_requests_after
208
- return false unless timeout
209
- (revalidated_at + timeout).past?
210
- end
211
-
212
- def merge_conflict?
213
- mergeable == false
214
- end
215
-
216
- def not_mergeable_yet?
217
- mergeable.nil?
218
- end
219
-
220
- def schedule_refresh!
221
- RefreshPullRequestJob.perform_later(self)
222
- end
223
-
224
- def closed?
225
- state == "closed"
226
- end
227
-
228
- def merged_upstream?
229
- closed? && merged_at
230
- end
231
-
232
- def refresh!
233
- update!(github_pull_request: Shipit.github.api.pull_request(stack.github_repo_name, number))
234
- head.refresh_statuses!
235
- fetched! if fetching?
236
- @comparison = nil
32
+ def emit_hooks(reason)
33
+ Hook.emit('pull_request', stack, action: reason, pull_request: self, stack: stack)
237
34
  end
238
35
 
239
36
  def github_pull_request=(github_pull_request)
240
37
  self.github_id = github_pull_request.id
38
+ self.number = github_pull_request.number
241
39
  self.api_url = github_pull_request.url
242
40
  self.title = github_pull_request.title
243
41
  self.state = github_pull_request.state
244
- self.mergeable = github_pull_request.mergeable
245
42
  self.additions = github_pull_request.additions
246
43
  self.deletions = github_pull_request.deletions
247
- self.branch = github_pull_request.head.ref
248
- self.head = find_or_create_commit_from_github_by_sha!(github_pull_request.head.sha, detached: true)
249
- self.merged_at = github_pull_request.merged_at
250
- self.base_ref = github_pull_request.base.ref
251
- self.base_commit = find_or_create_commit_from_github_by_sha!(github_pull_request.base.sha, detached: true)
252
- end
253
-
254
- def merge_message
255
- return title unless merge_requested_by
256
- "#{title}\n\n#{MERGE_REQUEST_FIELD}: #{merge_requested_by.login}\n"
257
- end
258
-
259
- def stale?
260
- return false unless base_commit
261
- spec = stack.cached_deploy_spec
262
- if max_branch_age = spec.max_divergence_age
263
- return true if Time.now.utc - head.committed_at > max_branch_age
264
- end
265
- if commit_count_limit = spec.max_divergence_commits
266
- return true if comparison.behind_by > commit_count_limit
44
+ self.user = User.find_or_create_by_login!(github_pull_request.user.login)
45
+ self.assignees = github_pull_request.assignees.map do |github_user|
46
+ User.find_or_create_by_login!(github_user.login)
267
47
  end
268
- false
269
- end
270
-
271
- def comparison
272
- @comparison ||= Shipit.github.api.compare(
273
- stack.github_repo_name,
274
- base_ref,
275
- head.sha,
276
- )
277
- end
278
-
279
- private
280
-
281
- def record_merge_status_change
282
- @merge_status_changed ||= saved_change_to_attribute?(:merge_status)
283
- end
284
-
285
- def emit_hooks
286
- return unless @merge_status_changed
287
- @merge_status_changed = nil
288
- Hook.emit('merge', stack, pull_request: self, status: merge_status, stack: stack)
48
+ self.labels = github_pull_request.labels.map(&:name)
49
+ self.head = find_or_create_commit_from_github_by_sha!(github_pull_request.head.sha)
289
50
  end
290
51
 
291
- def find_or_create_commit_from_github_by_sha!(sha, attributes)
52
+ def find_or_create_commit_from_github_by_sha!(sha)
292
53
  if commit = stack.commits.by_sha(sha)
293
54
  commit
294
55
  else
295
- github_commit = Shipit.github.api.commit(stack.github_repo_name, sha)
296
- stack.commits.create_from_github!(github_commit, attributes)
56
+ github_commit = stack.github_api.commit(stack.github_repo_name, sha)
57
+ stack.commits.create_from_github!(github_commit)
297
58
  end
298
59
  rescue ActiveRecord::RecordNotUnique
299
60
  retry
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shipit
4
+ class PullRequestAssignment < Record
5
+ belongs_to :pull_request, required: true
6
+ belongs_to :user, required: true
7
+
8
+ validates :user_id, uniqueness: { scope: :pull_request_id }
9
+ end
10
+ end
@@ -25,7 +25,7 @@ module Shipit
25
25
  private
26
26
 
27
27
  def create_status_on_github
28
- Shipit.github.api.create_status(
28
+ stack.github_api.create_status(
29
29
  stack.github_repo_name,
30
30
  commit.sha,
31
31
  state,