shipit-engine 0.28.1 → 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.
Files changed (410) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -2
  3. data/Rakefile +4 -2
  4. data/app/assets/images/archive-solid.svg +1 -0
  5. data/app/assets/images/magic-solid.svg +1 -0
  6. data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
  7. data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
  8. data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
  9. data/app/assets/stylesheets/_pages/_stacks.scss +95 -3
  10. data/app/assets/stylesheets/merge_status.scss +0 -3
  11. data/app/assets/stylesheets/shipit.scss +1 -0
  12. data/app/controllers/concerns/shipit/active_model_serializers_patch.rb +13 -0
  13. data/app/controllers/concerns/shipit/api/cacheable.rb +1 -0
  14. data/app/controllers/concerns/shipit/api/paginable.rb +3 -2
  15. data/app/controllers/concerns/shipit/api/rendering.rb +5 -4
  16. data/app/controllers/concerns/shipit/authentication.rb +3 -2
  17. data/app/controllers/concerns/shipit/pagination.rb +2 -1
  18. data/app/controllers/shipit/api/base_controller.rb +11 -6
  19. data/app/controllers/shipit/api/ccmenu_controller.rb +2 -1
  20. data/app/controllers/shipit/api/commits_controller.rb +2 -1
  21. data/app/controllers/shipit/api/deploys_controller.rb +4 -3
  22. data/app/controllers/shipit/api/hooks_controller.rb +6 -5
  23. data/app/controllers/shipit/api/locks_controller.rb +5 -4
  24. data/app/controllers/shipit/api/merge_requests_controller.rb +37 -0
  25. data/app/controllers/shipit/api/outputs_controller.rb +2 -1
  26. data/app/controllers/shipit/api/release_statuses_controller.rb +3 -2
  27. data/app/controllers/shipit/api/rollbacks_controller.rb +33 -0
  28. data/app/controllers/shipit/api/stacks_controller.rb +50 -5
  29. data/app/controllers/shipit/api/tasks_controller.rb +6 -5
  30. data/app/controllers/shipit/api_clients_controller.rb +50 -0
  31. data/app/controllers/shipit/ccmenu_url_controller.rb +4 -3
  32. data/app/controllers/shipit/commit_checks_controller.rb +2 -1
  33. data/app/controllers/shipit/commits_controller.rb +2 -1
  34. data/app/controllers/shipit/deploys_controller.rb +5 -4
  35. data/app/controllers/shipit/github_authentication_controller.rb +4 -3
  36. data/app/controllers/shipit/merge_requests_controller.rb +31 -0
  37. data/app/controllers/shipit/merge_status_controller.rb +33 -28
  38. data/app/controllers/shipit/release_statuses_controller.rb +3 -2
  39. data/app/controllers/shipit/repositories_controller.rb +74 -0
  40. data/app/controllers/shipit/rollbacks_controller.rb +3 -2
  41. data/app/controllers/shipit/shipit_controller.rb +2 -1
  42. data/app/controllers/shipit/stacks_controller.rb +78 -14
  43. data/app/controllers/shipit/status_controller.rb +2 -1
  44. data/app/controllers/shipit/tasks_controller.rb +9 -8
  45. data/app/controllers/shipit/webhooks_controller.rb +5 -132
  46. data/app/helpers/shipit/chunks_helper.rb +3 -2
  47. data/app/helpers/shipit/deploys_helper.rb +4 -3
  48. data/app/helpers/shipit/github_url_helper.rb +9 -0
  49. data/app/helpers/shipit/merge_status_helper.rb +1 -0
  50. data/app/helpers/shipit/shipit_helper.rb +1 -0
  51. data/app/helpers/shipit/stacks_helper.rb +9 -0
  52. data/app/helpers/shipit/tasks_helper.rb +1 -0
  53. data/app/jobs/shipit/background_job.rb +4 -0
  54. data/app/jobs/shipit/background_job/unique.rb +4 -1
  55. data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -0
  56. data/app/jobs/shipit/chunk_rollup_job.rb +4 -0
  57. data/app/jobs/shipit/clear_git_cache_job.rb +1 -0
  58. data/app/jobs/shipit/continuous_delivery_job.rb +3 -1
  59. data/app/jobs/shipit/create_on_github_job.rb +7 -1
  60. data/app/jobs/shipit/create_release_statuses_job.rb +1 -0
  61. data/app/jobs/shipit/deferred_touch_job.rb +4 -0
  62. data/app/jobs/shipit/deliver_hook_job.rb +1 -0
  63. data/app/jobs/shipit/destroy_job.rb +1 -0
  64. data/app/jobs/shipit/destroy_repository_job.rb +24 -0
  65. data/app/jobs/shipit/destroy_stack_job.rb +5 -4
  66. data/app/jobs/shipit/emit_event_job.rb +2 -1
  67. data/app/jobs/shipit/fetch_commit_stats_job.rb +1 -0
  68. data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
  69. data/app/jobs/shipit/github_sync_job.rb +2 -1
  70. data/app/jobs/shipit/{mark_deploy_healty_job.rb → mark_deploy_healthy_job.rb} +1 -0
  71. data/app/jobs/shipit/perform_commit_checks_job.rb +1 -0
  72. data/app/jobs/shipit/perform_task_job.rb +5 -90
  73. data/app/jobs/shipit/process_merge_requests_job.rb +32 -0
  74. data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
  75. data/app/jobs/shipit/reap_dead_tasks_job.rb +21 -0
  76. data/app/jobs/shipit/refresh_check_runs_job.rb +1 -0
  77. data/app/jobs/shipit/refresh_github_user_job.rb +1 -0
  78. data/app/jobs/shipit/refresh_merge_request_job.rb +11 -0
  79. data/app/jobs/shipit/refresh_statuses_job.rb +1 -0
  80. data/app/jobs/shipit/setup_github_hook_job.rb +1 -0
  81. data/app/jobs/shipit/update_estimated_deploy_duration_job.rb +1 -0
  82. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +4 -3
  83. data/app/models/concerns/shipit/deferred_touch.rb +4 -3
  84. data/app/models/shipit/anonymous_user.rb +9 -0
  85. data/app/models/shipit/api_client.rb +3 -2
  86. data/app/models/shipit/application_record.rb +2 -1
  87. data/app/models/shipit/check_run.rb +5 -4
  88. data/app/models/shipit/command_line_user.rb +5 -0
  89. data/app/models/shipit/commit.rb +41 -22
  90. data/app/models/shipit/commit_checks.rb +2 -0
  91. data/app/models/shipit/commit_deployment.rb +17 -12
  92. data/app/models/shipit/commit_deployment_status.rb +8 -3
  93. data/app/models/shipit/commit_message.rb +1 -0
  94. data/app/models/shipit/delivery.rb +4 -3
  95. data/app/models/shipit/deploy.rb +41 -10
  96. data/app/models/shipit/deploy_spec.rb +38 -7
  97. data/app/models/shipit/deploy_spec/bundler_discovery.rb +2 -1
  98. data/app/models/shipit/deploy_spec/capistrano_discovery.rb +1 -0
  99. data/app/models/shipit/deploy_spec/file_system.rb +20 -7
  100. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -0
  101. data/app/models/shipit/deploy_spec/lerna_discovery.rb +1 -0
  102. data/app/models/shipit/deploy_spec/npm_discovery.rb +5 -4
  103. data/app/models/shipit/deploy_spec/pypi_discovery.rb +1 -0
  104. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -0
  105. data/app/models/shipit/deploy_stats.rb +58 -0
  106. data/app/models/shipit/duration.rb +3 -2
  107. data/app/models/shipit/ephemeral_commit_checks.rb +1 -0
  108. data/app/models/shipit/github_hook.rb +2 -1
  109. data/app/models/shipit/github_status.rb +2 -1
  110. data/app/models/shipit/hook.rb +8 -5
  111. data/app/models/shipit/membership.rb +3 -2
  112. data/app/models/shipit/merge_request.rb +302 -0
  113. data/app/models/shipit/output_chunk.rb +7 -2
  114. data/app/models/shipit/provisioning_handler.rb +32 -0
  115. data/app/models/shipit/provisioning_handler/base.rb +30 -0
  116. data/app/models/shipit/provisioning_handler/unregistered_provisioning_handler.rb +35 -0
  117. data/app/models/shipit/pull_request.rb +27 -260
  118. data/app/models/shipit/pull_request_assignment.rb +10 -0
  119. data/app/models/shipit/record.rb +18 -0
  120. data/app/models/shipit/release_status.rb +3 -2
  121. data/app/models/shipit/repository.rb +97 -0
  122. data/app/models/shipit/review_stack.rb +116 -0
  123. data/app/models/shipit/review_stack_provisioning_queue.rb +39 -0
  124. data/app/models/shipit/rollback.rb +1 -0
  125. data/app/models/shipit/stack.rb +130 -57
  126. data/app/models/shipit/status.rb +3 -2
  127. data/app/models/shipit/status/common.rb +7 -6
  128. data/app/models/shipit/status/group.rb +1 -0
  129. data/app/models/shipit/status/missing.rb +2 -1
  130. data/app/models/shipit/status/unknown.rb +2 -1
  131. data/app/models/shipit/task.rb +118 -14
  132. data/app/models/shipit/task_definition.rb +1 -0
  133. data/app/models/shipit/task_execution_strategy/base.rb +20 -0
  134. data/app/models/shipit/task_execution_strategy/default.rb +110 -0
  135. data/app/models/shipit/team.rb +2 -1
  136. data/app/models/shipit/undeployed_commit.rb +1 -0
  137. data/app/models/shipit/unlimited_api_client.rb +1 -0
  138. data/app/models/shipit/user.rb +15 -8
  139. data/app/models/shipit/variable_definition.rb +1 -0
  140. data/app/models/shipit/webhooks.rb +43 -0
  141. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +20 -0
  142. data/app/models/shipit/webhooks/handlers/handler.rb +41 -0
  143. data/app/models/shipit/webhooks/handlers/membership_handler.rb +46 -0
  144. data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +74 -0
  145. data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +68 -0
  146. data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +74 -0
  147. data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +127 -0
  148. data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +106 -0
  149. data/app/models/shipit/webhooks/handlers/pull_request/opened_handler.rb +83 -0
  150. data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +88 -0
  151. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +103 -0
  152. data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +107 -0
  153. data/app/models/shipit/webhooks/handlers/push_handler.rb +21 -0
  154. data/app/models/shipit/webhooks/handlers/status_handler.rb +27 -0
  155. data/app/serializers/concerns/shipit/conditional_attributes.rb +1 -0
  156. data/app/serializers/shipit/anonymous_user_serializer.rb +1 -0
  157. data/app/serializers/shipit/command_line_user_serializer.rb +1 -0
  158. data/app/serializers/shipit/commit_serializer.rb +1 -0
  159. data/app/serializers/shipit/deploy_serializer.rb +8 -1
  160. data/app/serializers/shipit/hook_serializer.rb +1 -0
  161. data/app/serializers/shipit/merge_request_serializer.rb +21 -0
  162. data/app/serializers/shipit/pull_request_serializer.rb +6 -8
  163. data/app/serializers/shipit/review_stack_serializer.rb +7 -0
  164. data/app/serializers/shipit/rollback_serializer.rb +1 -0
  165. data/app/serializers/shipit/short_commit_serializer.rb +1 -0
  166. data/app/serializers/shipit/stack_serializer.rb +12 -5
  167. data/app/serializers/shipit/tail_task_serializer.rb +11 -2
  168. data/app/serializers/shipit/task_serializer.rb +2 -17
  169. data/app/serializers/shipit/user_serializer.rb +6 -1
  170. data/app/validators/ascii_only_validator.rb +4 -3
  171. data/app/validators/subset_validator.rb +1 -0
  172. data/app/views/layouts/_head.html.erb +0 -0
  173. data/app/views/layouts/shipit.html.erb +5 -3
  174. data/app/views/shipit/_variables.html.erb +1 -1
  175. data/app/views/shipit/api_clients/index.html.erb +36 -0
  176. data/app/views/shipit/api_clients/new.html.erb +33 -0
  177. data/app/views/shipit/api_clients/show.html.erb +35 -0
  178. data/app/views/shipit/ccmenu/project.xml.builder +2 -1
  179. data/app/views/shipit/deploys/show.html.erb +2 -2
  180. data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
  181. data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
  182. data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
  183. data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
  184. data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
  185. data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
  186. data/app/views/shipit/merge_status/failure.html.erb +1 -1
  187. data/app/views/shipit/merge_status/locked.html.erb +1 -1
  188. data/app/views/shipit/merge_status/logged_out.erb +1 -1
  189. data/app/views/shipit/merge_status/success.html.erb +2 -2
  190. data/app/views/shipit/repositories/_header.html.erb +19 -0
  191. data/app/views/shipit/repositories/index.html.erb +31 -0
  192. data/app/views/shipit/repositories/new.html.erb +23 -0
  193. data/app/views/shipit/repositories/settings.html.erb +53 -0
  194. data/app/views/shipit/repositories/show.html.erb +30 -0
  195. data/app/views/shipit/stacks/_banners.html.erb +13 -0
  196. data/app/views/shipit/stacks/_header.html.erb +30 -12
  197. data/app/views/shipit/stacks/_links.html.erb +1 -0
  198. data/app/views/shipit/stacks/_stack.html.erb +8 -0
  199. data/app/views/shipit/stacks/all_tasks.html.erb +28 -0
  200. data/app/views/shipit/stacks/index.html.erb +9 -3
  201. data/app/views/shipit/stacks/settings.html.erb +22 -3
  202. data/app/views/shipit/stacks/show.html.erb +1 -1
  203. data/app/views/shipit/stacks/statistics.html.erb +82 -0
  204. data/app/views/shipit/tasks/_task_output.html.erb +1 -1
  205. data/app/views/shipit/tasks/show.html.erb +1 -1
  206. data/config/initializers/inflections.rb +2 -1
  207. data/config/locales/en.yml +18 -5
  208. data/config/routes.rb +29 -7
  209. data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
  210. data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
  211. data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
  212. data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
  213. data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
  214. data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
  215. data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
  216. data/db/migrate/20200226211925_add_index_to_tasks_status.rb +5 -0
  217. data/db/migrate/20200427135152_add_pull_request_head_sha_to_commit.rb +5 -0
  218. data/db/migrate/20200615181558_add_rollback_once_aborted_to.rb +5 -0
  219. data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
  220. data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
  221. data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
  222. data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
  223. data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
  224. data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
  225. data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
  226. data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
  227. data/lib/shipit.rb +23 -3
  228. data/lib/shipit/cast_value.rb +1 -0
  229. data/lib/shipit/command.rb +14 -18
  230. data/lib/shipit/commands.rb +5 -4
  231. data/lib/shipit/csv_serializer.rb +1 -0
  232. data/lib/shipit/deploy_commands.rb +1 -0
  233. data/lib/shipit/engine.rb +11 -2
  234. data/lib/shipit/environment_variables.rb +11 -1
  235. data/lib/shipit/first_parent_commits_iterator.rb +1 -0
  236. data/lib/shipit/flock.rb +1 -0
  237. data/lib/shipit/github_app.rb +41 -10
  238. data/lib/shipit/github_http_cache_middleware.rb +1 -0
  239. data/lib/shipit/null_serializer.rb +1 -0
  240. data/lib/shipit/octokit_check_runs.rb +3 -2
  241. data/lib/shipit/octokit_iterator.rb +3 -2
  242. data/lib/shipit/paginator.rb +3 -2
  243. data/lib/shipit/review_stack_commands.rb +8 -0
  244. data/lib/shipit/rollback_commands.rb +1 -0
  245. data/lib/shipit/same_site_cookie_middleware.rb +29 -0
  246. data/lib/shipit/simple_message_verifier.rb +1 -0
  247. data/lib/shipit/stack_commands.rb +12 -4
  248. data/lib/shipit/stat.rb +1 -0
  249. data/lib/shipit/task_commands.rb +23 -14
  250. data/lib/shipit/version.rb +2 -1
  251. data/lib/snippets/release-gem +5 -1
  252. data/lib/tasks/cron.rake +13 -2
  253. data/lib/tasks/dev.rake +3 -2
  254. data/lib/tasks/shipit.rake +16 -17
  255. data/lib/tasks/teams.rake +1 -0
  256. data/test/controllers/api/base_controller_test.rb +3 -2
  257. data/test/controllers/api/ccmenu_controller_test.rb +9 -8
  258. data/test/controllers/api/commits_controller_test.rb +3 -2
  259. data/test/controllers/api/deploys_controller_test.rb +15 -14
  260. data/test/controllers/api/hooks_controller_test.rb +8 -7
  261. data/test/controllers/api/locks_controller_test.rb +7 -6
  262. data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +17 -16
  263. data/test/controllers/api/outputs_controller_test.rb +4 -2
  264. data/test/controllers/api/release_statuses_controller_test.rb +2 -1
  265. data/test/controllers/api/rollback_controller_test.rb +113 -0
  266. data/test/controllers/api/stacks_controller_test.rb +65 -16
  267. data/test/controllers/api/tasks_controller_test.rb +13 -12
  268. data/test/controllers/api_clients_controller_test.rb +104 -0
  269. data/test/controllers/ccmenu_controller_test.rb +4 -3
  270. data/test/controllers/commit_checks_controller_test.rb +4 -3
  271. data/test/controllers/commits_controller_test.rb +3 -2
  272. data/test/controllers/deploys_controller_test.rb +33 -22
  273. data/test/controllers/github_authentication_controller_test.rb +1 -0
  274. data/test/controllers/merge_requests_controller_test.rb +32 -0
  275. data/test/controllers/merge_status_controller_test.rb +27 -9
  276. data/test/controllers/release_statuses_controller_test.rb +3 -2
  277. data/test/controllers/repositories_controller_test.rb +71 -0
  278. data/test/controllers/rollbacks_controller_test.rb +9 -8
  279. data/test/controllers/stacks_controller_test.rb +72 -15
  280. data/test/controllers/status_controller_test.rb +1 -0
  281. data/test/controllers/tasks_controller_test.rb +33 -20
  282. data/test/controllers/webhooks_controller_test.rb +36 -9
  283. data/test/dummy/config/application.rb +7 -2
  284. data/test/dummy/config/environments/development.rb +23 -6
  285. data/test/dummy/config/environments/test.rb +2 -5
  286. data/test/dummy/db/schema.rb +76 -24
  287. data/test/dummy/db/seeds.rb +30 -16
  288. data/test/fixtures/payloads/check_suite_master.json +2 -2
  289. data/test/fixtures/payloads/invalid_pull_request.json +117 -0
  290. data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
  291. data/test/fixtures/payloads/pull_request_assigned.json +480 -0
  292. data/test/fixtures/payloads/pull_request_closed.json +454 -0
  293. data/test/fixtures/payloads/pull_request_labeled.json +461 -0
  294. data/test/fixtures/payloads/pull_request_opened.json +454 -0
  295. data/test/fixtures/payloads/pull_request_reopened.json +454 -0
  296. data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
  297. data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
  298. data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
  299. data/test/fixtures/shipit/commit_deployments.yml +8 -8
  300. data/test/fixtures/shipit/commits.yml +52 -1
  301. data/test/fixtures/shipit/merge_requests.yml +141 -0
  302. data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
  303. data/test/fixtures/shipit/pull_requests.yml +10 -131
  304. data/test/fixtures/shipit/repositories.yml +28 -0
  305. data/test/fixtures/shipit/stacks.yml +335 -30
  306. data/test/fixtures/shipit/statuses.yml +9 -0
  307. data/test/fixtures/shipit/tasks.yml +69 -3
  308. data/test/fixtures/shipit/users.yml +7 -0
  309. data/test/fixtures/timeout +2 -1
  310. data/test/helpers/api_helper.rb +1 -0
  311. data/test/helpers/fixture_aliases_helper.rb +1 -0
  312. data/test/helpers/hooks_helper.rb +2 -1
  313. data/test/helpers/json_helper.rb +15 -11
  314. data/test/helpers/links_helper.rb +4 -3
  315. data/test/helpers/payloads_helper.rb +5 -0
  316. data/test/helpers/queries_helper.rb +3 -2
  317. data/test/jobs/cache_deploy_spec_job_test.rb +2 -1
  318. data/test/jobs/chunk_rollup_job_test.rb +16 -1
  319. data/test/jobs/deliver_hook_job_test.rb +1 -0
  320. data/test/jobs/destroy_repository_job_test.rb +27 -0
  321. data/test/jobs/destroy_stack_job_test.rb +10 -0
  322. data/test/jobs/emit_event_job_test.rb +2 -1
  323. data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
  324. data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
  325. data/test/jobs/github_sync_job_test.rb +1 -0
  326. data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
  327. data/test/jobs/perform_task_job_test.rb +12 -11
  328. data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +19 -18
  329. data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
  330. data/test/jobs/reap_dead_tasks_job_test.rb +68 -0
  331. data/test/jobs/refresh_github_user_job_test.rb +1 -0
  332. data/test/jobs/refresh_status_job_test.rb +1 -0
  333. data/test/jobs/unique_job_test.rb +1 -0
  334. data/test/jobs/update_github_last_deployed_ref_job_test.rb +1 -0
  335. data/test/lib/shipit/deploy_commands_test.rb +16 -0
  336. data/test/lib/shipit/task_commands_test.rb +17 -0
  337. data/test/middleware/same_site_cookie_middleware_test.rb +52 -0
  338. data/test/models/api_client_test.rb +1 -0
  339. data/test/models/commit_checks_test.rb +1 -0
  340. data/test/models/commit_deployment_status_test.rb +34 -4
  341. data/test/models/commit_deployment_test.rb +9 -11
  342. data/test/models/commits_test.rb +116 -21
  343. data/test/models/delivery_test.rb +2 -1
  344. data/test/models/deploy_spec_test.rb +103 -65
  345. data/test/models/deploy_stats_test.rb +113 -0
  346. data/test/models/deploys_test.rb +207 -26
  347. data/test/models/duration_test.rb +1 -0
  348. data/test/models/github_hook_test.rb +1 -0
  349. data/test/models/hook_test.rb +20 -16
  350. data/test/models/membership_test.rb +1 -0
  351. data/test/models/{pull_request_test.rb → merge_request_test.rb} +48 -41
  352. data/test/models/pull_request_assignment_test.rb +16 -0
  353. data/test/models/release_statuses_test.rb +1 -0
  354. data/test/models/rollbacks_test.rb +1 -0
  355. data/test/models/shipit/check_run_test.rb +1 -0
  356. data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
  357. data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
  358. data/test/models/shipit/provisioning_handler_test.rb +64 -0
  359. data/test/models/shipit/pull_request_test.rb +52 -0
  360. data/test/models/shipit/repository_test.rb +81 -0
  361. data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
  362. data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
  363. data/test/models/shipit/review_stack_test.rb +59 -0
  364. data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +120 -60
  365. data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
  366. data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
  367. data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
  368. data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
  369. data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
  370. data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
  371. data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
  372. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +83 -0
  373. data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
  374. data/test/models/shipit/webhooks/handlers_test.rb +27 -0
  375. data/test/models/status/group_test.rb +1 -0
  376. data/test/models/status/missing_test.rb +1 -0
  377. data/test/models/status_test.rb +1 -0
  378. data/test/models/task_definitions_test.rb +9 -8
  379. data/test/models/tasks_test.rb +59 -1
  380. data/test/models/team_test.rb +4 -2
  381. data/test/models/undeployed_commits_test.rb +14 -0
  382. data/test/models/users_test.rb +13 -5
  383. data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
  384. data/test/test_command_integration.rb +3 -2
  385. data/test/test_helper.rb +37 -32
  386. data/test/unit/anonymous_user_serializer_test.rb +14 -0
  387. data/test/unit/command_test.rb +15 -10
  388. data/test/unit/commands_test.rb +1 -0
  389. data/test/unit/commit_serializer_test.rb +16 -0
  390. data/test/unit/csv_serializer_test.rb +3 -2
  391. data/test/unit/deploy_commands_test.rb +14 -4
  392. data/test/unit/deploy_serializer_test.rb +17 -0
  393. data/test/unit/environment_variables_test.rb +5 -4
  394. data/test/unit/github_app_test.rb +137 -0
  395. data/test/unit/github_url_helper_test.rb +6 -0
  396. data/test/unit/rollback_commands_test.rb +2 -1
  397. data/test/unit/shipit_helper_test.rb +17 -0
  398. data/test/unit/shipit_task_execution_strategy_test.rb +47 -0
  399. data/test/unit/shipit_test.rb +1 -0
  400. data/test/unit/user_serializer_test.rb +14 -0
  401. data/test/unit/variable_definition_test.rb +1 -0
  402. metadata +334 -169
  403. data/app/controllers/shipit/api/pull_requests_controller.rb +0 -36
  404. data/app/controllers/shipit/pull_requests_controller.rb +0 -30
  405. data/app/jobs/shipit/merge_pull_requests_job.rb +0 -31
  406. data/app/jobs/shipit/refresh_pull_request_job.rb +0 -10
  407. data/app/views/shipit/pull_requests/_pull_request.html.erb +0 -29
  408. data/test/controllers/pull_requests_controller_test.rb +0 -31
  409. data/test/fixtures/shipit/output_chunks.yml +0 -47
  410. data/test/models/output_chunk_test.rb +0 -20
@@ -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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Rollback < Deploy
3
4
  belongs_to :deploy, foreign_key: :parent_id, inverse_of: false
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require 'fileutils'
2
3
 
3
4
  module Shipit
4
- class Stack < ActiveRecord::Base
5
+ class Stack < Record
5
6
  module NoDeployedCommit
6
7
  extend self
7
8
 
@@ -22,13 +23,11 @@ module Shipit
22
23
  end
23
24
  end
24
25
 
25
- REPO_OWNER_MAX_SIZE = 39
26
- REPO_NAME_MAX_SIZE = 100
27
26
  ENVIRONMENT_MAX_SIZE = 50
28
27
  REQUIRED_HOOKS = %i(push status).freeze
29
28
 
30
29
  has_many :commits, dependent: :destroy
31
- has_many :pull_requests, dependent: :destroy
30
+ has_many :merge_requests, dependent: :destroy
32
31
  has_many :tasks, dependent: :destroy
33
32
  has_many :deploys
34
33
  has_many :rollbacks
@@ -40,6 +39,30 @@ module Shipit
40
39
  has_many :hooks, dependent: :destroy
41
40
  has_many :api_clients, dependent: :destroy
42
41
  belongs_to :lock_author, class_name: :User, optional: true
42
+ belongs_to :repository
43
+ validates_associated :repository
44
+
45
+ scope :not_archived, -> { where(archived_since: nil) }
46
+
47
+ include DeferredTouch
48
+ deferred_touch repository: :updated_at
49
+
50
+ default_scope { preload(:repository) }
51
+
52
+ def env
53
+ {
54
+ 'ENVIRONMENT' => environment,
55
+ 'LAST_DEPLOYED_SHA' => last_deployed_commit.sha,
56
+ 'GITHUB_REPO_OWNER' => repository.owner,
57
+ 'GITHUB_REPO_NAME' => repository.name,
58
+ 'DEPLOY_URL' => deploy_url,
59
+ 'BRANCH' => branch,
60
+ }
61
+ end
62
+
63
+ def repository
64
+ super || build_repository
65
+ end
43
66
 
44
67
  def lock_author(*)
45
68
  super || AnonymousUser.new
@@ -49,11 +72,6 @@ module Shipit
49
72
  super(user&.logged_in? ? user : nil)
50
73
  end
51
74
 
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
75
  before_validation :update_defaults
58
76
  before_destroy :clear_local_files
59
77
  before_save :set_locked_since
@@ -66,19 +84,27 @@ module Shipit
66
84
  after_commit :sync_github, on: :create
67
85
  after_commit :schedule_merges_if_necessary, on: :update
68
86
 
69
- validates :repo_name, uniqueness: {scope: %i(repo_owner environment),
70
- message: 'cannot be used more than once with this environment'}
71
- validates :repo_owner, :repo_name, :environment, presence: true, ascii_only: true
72
- validates :repo_owner, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_OWNER_MAX_SIZE}
73
- validates :repo_name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_NAME_MAX_SIZE}
74
- validates :environment, format: {with: /\A[a-z0-9\-_\:]+\z/}, length: {maximum: ENVIRONMENT_MAX_SIZE}
75
- validates :deploy_url, format: {with: URI.regexp(%w(http https ssh))}, allow_blank: true
87
+ validates :repository, uniqueness: {
88
+ scope: %i(environment), case_sensitive: false,
89
+ message: 'cannot be used more than once with this environment. Check archived stacks.'
90
+ }
91
+ validates :environment, format: { with: /\A[a-z0-9\-_\:]+\z/ }, length: { maximum: ENVIRONMENT_MAX_SIZE }
92
+ validates :deploy_url, format: { with: URI.regexp(%w(http https ssh)) }, allow_blank: true
76
93
 
77
- validates :lock_reason, length: {maximum: 4096}
94
+ validates :lock_reason, length: { maximum: 4096 }
78
95
 
79
96
  serialize :cached_deploy_spec, DeploySpec
80
- delegate :find_task_definition, :supports_rollback?, :links, :release_status?, :release_status_delay,
81
- :release_status_context, :supports_fetch_deployed_revision?, to: :cached_deploy_spec, allow_nil: true
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
+ )
82
108
 
83
109
  def self.refresh_deployed_revisions
84
110
  find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
@@ -125,13 +151,29 @@ module Shipit
125
151
  env: filter_deploy_envs(env&.to_h || {}),
126
152
  allow_concurrency: force,
127
153
  ignored_safeties: force || !until_commit.deployable?,
154
+ max_retries: retries_on_deploy,
128
155
  )
129
156
  end
130
157
 
131
158
  def trigger_deploy(*args, **kwargs)
159
+ if changed?
160
+ # If this is the first deploy since the spec changed it's possible the record will be dirty here, meaning we
161
+ # cant lock. In this one case persist the changes, otherwise log a warning and let the lock raise, so we
162
+ # can debug what's going on here. We don't expect anything other than the deploy spec to dirty the model
163
+ # instance, because of how that field is serialised.
164
+ if changes.keys == ['cached_deploy_spec']
165
+ save!
166
+ else
167
+ Rails.logger.warning("#{changes.keys} field(s) were unexpectedly modified on stack #{id} while deploying")
168
+ end
169
+ end
170
+
132
171
  run_now = kwargs.delete(:run_now)
133
- deploy = build_deploy(*args, **kwargs)
134
- deploy.save!
172
+ deploy = with_lock do
173
+ deploy = build_deploy(*args, **kwargs)
174
+ deploy.save!
175
+ deploy
176
+ end
135
177
  run_now ? deploy.run_now! : deploy.enqueue
136
178
  continuous_delivery_resumed!
137
179
  deploy
@@ -150,14 +192,16 @@ module Shipit
150
192
  end
151
193
 
152
194
  def trigger_continuous_delivery
195
+ return if cached_deploy_spec.blank?
196
+
153
197
  commit = next_commit_to_deploy
154
198
 
155
- if !deployable? || deployed_too_recently? || commit.nil? || commit.deployed?
199
+ if should_resume_continuous_delivery?(commit)
156
200
  continuous_delivery_resumed!
157
201
  return
158
202
  end
159
203
 
160
- if commit.deploy_failed? || (checks? && !EphemeralCommitChecks.new(commit).run.success?)
204
+ if should_delay_continuous_delivery?(commit)
161
205
  continuous_delivery_delayed!
162
206
  return
163
207
  end
@@ -169,7 +213,7 @@ module Shipit
169
213
  end
170
214
 
171
215
  def schedule_merges
172
- MergePullRequestsJob.perform_later(self)
216
+ ProcessMergeRequestsJob.perform_later(self)
173
217
  end
174
218
 
175
219
  def next_commit_to_deploy
@@ -189,7 +233,7 @@ module Shipit
189
233
  def async_refresh_deployed_revision
190
234
  async_refresh_deployed_revision!
191
235
  rescue => error
192
- logger.warn "Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}"
236
+ logger.warn("Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}")
193
237
  end
194
238
 
195
239
  def async_refresh_deployed_revision!
@@ -257,8 +301,8 @@ module Shipit
257
301
  next if commits_to_lock.empty?
258
302
 
259
303
  affected_rows += commits
260
- .where(id: commits_to_lock.map(&:id).uniq)
261
- .lock_all(revert.author)
304
+ .where(id: commits_to_lock.map(&:id).uniq)
305
+ .lock_all(revert.author)
262
306
  end
263
307
 
264
308
  touch if affected_rows > 1
@@ -309,7 +353,7 @@ module Shipit
309
353
  end
310
354
 
311
355
  def deployable?
312
- !locked? && !active_task?
356
+ !locked? && !active_task? && !awaiting_provision?
313
357
  end
314
358
 
315
359
  def allows_merges?
@@ -317,24 +361,15 @@ module Shipit
317
361
  end
318
362
 
319
363
  def merge_method
320
- cached_deploy_spec&.pull_request_merge_method || Shipit.default_merge_method
321
- end
322
-
323
- def repo_name=(name)
324
- super(name&.downcase)
325
- end
326
-
327
- def repo_owner=(name)
328
- super(name&.downcase)
364
+ cached_deploy_spec&.merge_request_merge_method || Shipit.default_merge_method
329
365
  end
330
366
 
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
367
+ delegate :name=, to: :repository, prefix: :repo
368
+ delegate :name, to: :repository, prefix: :repo
369
+ delegate :owner=, to: :repository, prefix: :repo
370
+ delegate :owner, to: :repository, prefix: :repo
371
+ delegate :http_url, to: :repository, prefix: :repo
372
+ delegate :git_url, to: :repository, prefix: :repo
338
373
 
339
374
  def base_path
340
375
  Rails.root.join('data', 'stacks', repo_owner, repo_name, environment)
@@ -354,15 +389,15 @@ module Shipit
354
389
 
355
390
  def clear_git_cache!
356
391
  tmp_path = "#{git_path}-#{SecureRandom.hex}"
392
+ return unless File.exist?(git_path)
357
393
  acquire_git_cache_lock do
358
- return unless File.exist?(git_path)
359
394
  File.rename(git_path, tmp_path)
360
395
  end
361
396
  FileUtils.rm_rf(tmp_path)
362
397
  end
363
398
 
364
399
  def github_repo_name
365
- [repo_owner, repo_name].join('/')
400
+ repository.github_repo_name
366
401
  end
367
402
 
368
403
  def github_commits
@@ -389,7 +424,7 @@ module Shipit
389
424
  if resource.try(:message) == 'Moved Permanently'
390
425
  resource = Shipit.github.api.get(resource.url)
391
426
  end
392
- update!(repo_owner: resource.owner.login, repo_name: resource.name)
427
+ repository.update!(owner: resource.owner.login, name: resource.name)
393
428
  end
394
429
 
395
430
  def active_task?
@@ -406,7 +441,7 @@ module Shipit
406
441
  end
407
442
 
408
443
  def lock(reason, user)
409
- params = {lock_reason: reason, lock_author: user}
444
+ params = { lock_reason: reason, lock_author: user }
410
445
  update!(params)
411
446
  end
412
447
 
@@ -414,6 +449,18 @@ module Shipit
414
449
  update!(lock_reason: nil, lock_author: nil, locked_since: nil)
415
450
  end
416
451
 
452
+ def archived?
453
+ archived_since.present?
454
+ end
455
+
456
+ def archive!(user)
457
+ update!(archived_since: Time.now, lock_reason: "Archived", lock_author: user)
458
+ end
459
+
460
+ def unarchive!
461
+ update!(archived_since: nil, lock_reason: nil, lock_author: nil, locked_since: nil)
462
+ end
463
+
417
464
  def to_param
418
465
  [repo_owner, repo_name, environment].join('/')
419
466
  end
@@ -429,16 +476,20 @@ module Shipit
429
476
 
430
477
  def self.from_param!(param)
431
478
  repo_owner, repo_name, environment = param.split('/')
432
- where(
433
- repo_owner: repo_owner.downcase,
434
- repo_name: repo_name.downcase,
435
- environment: environment,
436
- ).first!
479
+ includes(:repository)
480
+ .where(
481
+ repositories: {
482
+ owner: repo_owner.downcase,
483
+ name: repo_name.downcase,
484
+ },
485
+ environment: environment,
486
+ ).first!
437
487
  end
438
488
 
439
489
  delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
440
490
  :blocking_statuses, :deploy_variables, :filter_task_envs, :filter_deploy_envs,
441
- :maximum_commits_per_deploy, :pause_between_deploys, to: :cached_deploy_spec
491
+ :maximum_commits_per_deploy, :pause_between_deploys, :retries_on_deploy, :retries_on_rollback,
492
+ to: :cached_deploy_spec
442
493
 
443
494
  def monitoring?
444
495
  monitoring.present?
@@ -463,13 +514,15 @@ module Shipit
463
514
  end
464
515
 
465
516
  def update_latest_deployed_ref
466
- UpdateGithubLastDeployedRefJob.perform_later(self)
517
+ if Shipit.update_latest_deployed_ref
518
+ UpdateGithubLastDeployedRefJob.perform_later(self)
519
+ end
467
520
  end
468
521
 
469
522
  def broadcast_update
470
523
  Pubsubstub.publish(
471
524
  "stack.#{id}",
472
- {id: id, updated_at: updated_at}.to_json,
525
+ { id: id, updated_at: updated_at }.to_json,
473
526
  name: 'update',
474
527
  )
475
528
  end
@@ -521,6 +574,13 @@ module Shipit
521
574
  GithubSyncJob.perform_later(stack_id: id)
522
575
  end
523
576
 
577
+ def links
578
+ links_spec = cached_deploy_spec&.links || {}
579
+ context = EnvironmentVariables.with(env)
580
+
581
+ links_spec.transform_values { |url| context.interpolate(url) }
582
+ end
583
+
524
584
  private
525
585
 
526
586
  def clear_cache
@@ -556,7 +616,7 @@ module Shipit
556
616
  return unless previous_changes.include?('lock_reason')
557
617
 
558
618
  lock_details = if previous_changes['lock_reason'].last.blank?
559
- {from: previous_changes['locked_since'].first, until: Time.zone.now}
619
+ { from: previous_changes['locked_since'].first, until: Time.zone.now }
560
620
  end
561
621
 
562
622
  Hook.emit(:lock, self, locked: locked?, lock_details: lock_details, stack: self)
@@ -582,5 +642,18 @@ module Shipit
582
642
  def ci_enabled_cache_key
583
643
  "stacks:#{id}:ci_enabled"
584
644
  end
645
+
646
+ def should_resume_continuous_delivery?(commit)
647
+ !deployable? ||
648
+ deployed_too_recently? ||
649
+ commit.nil? ||
650
+ commit.deployed?
651
+ end
652
+
653
+ def should_delay_continuous_delivery?(commit)
654
+ commit.deploy_failed? ||
655
+ (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
656
+ commit.recently_pushed?
657
+ end
585
658
  end
586
659
  end