shipit-engine 0.30.0 → 0.34.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 +19 -4
  3. data/Rakefile +4 -2
  4. data/app/assets/images/magic-solid.svg +1 -0
  5. data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
  6. data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
  7. data/app/assets/stylesheets/_pages/_deploy.scss +0 -2
  8. data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
  9. data/app/assets/stylesheets/_pages/_stacks.scss +19 -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 +1 -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 +6 -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 +34 -0
  28. data/app/controllers/shipit/api/stacks_controller.rb +32 -5
  29. data/app/controllers/shipit/api/tasks_controller.rb +6 -5
  30. data/app/controllers/shipit/api_clients_controller.rb +4 -3
  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 +4 -3
  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 +31 -30
  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 +24 -9
  43. data/app/controllers/shipit/status_controller.rb +2 -1
  44. data/app/controllers/shipit/tasks_controller.rb +7 -6
  45. data/app/controllers/shipit/webhooks_controller.rb +26 -6
  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 -1
  51. data/app/helpers/shipit/stacks_helper.rb +5 -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 +1 -0
  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 +2 -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 +2 -1
  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 +3 -2
  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 +15 -10
  70. data/app/jobs/shipit/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 -92
  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 +5 -4
  83. data/app/models/concerns/shipit/deferred_touch.rb +4 -3
  84. data/app/models/shipit/anonymous_user.rb +15 -2
  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 +41 -4
  88. data/app/models/shipit/command_line_user.rb +5 -0
  89. data/app/models/shipit/commit.rb +42 -24
  90. data/app/models/shipit/commit_checks.rb +15 -13
  91. data/app/models/shipit/commit_deployment.rb +6 -5
  92. data/app/models/shipit/commit_deployment_status.rb +5 -4
  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 +23 -28
  96. data/app/models/shipit/deploy_spec.rb +38 -7
  97. data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -0
  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 +13 -4
  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 +2 -1
  106. data/app/models/shipit/duration.rb +5 -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 +34 -7
  111. data/app/models/shipit/membership.rb +3 -2
  112. data/app/models/shipit/merge_request.rb +304 -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 +28 -266
  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 +4 -3
  121. data/app/models/shipit/repository.rb +71 -6
  122. data/app/models/shipit/review_stack.rb +130 -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 +144 -44
  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 +2 -1
  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 +98 -12
  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 +109 -0
  135. data/app/models/shipit/team.rb +6 -3
  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 +19 -8
  139. data/app/models/shipit/variable_definition.rb +1 -0
  140. data/app/models/shipit/webhooks.rb +11 -0
  141. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +1 -0
  142. data/app/models/shipit/webhooks/handlers/handler.rb +1 -0
  143. data/app/models/shipit/webhooks/handlers/membership_handler.rb +1 -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 +5 -1
  154. data/app/models/shipit/webhooks/handlers/status_handler.rb +1 -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 +8 -6
  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 +1 -0
  171. data/app/validators/subset_validator.rb +2 -1
  172. data/app/views/layouts/merge_status.html.erb +1 -1
  173. data/app/views/layouts/shipit.html.erb +1 -1
  174. data/app/views/shipit/_variables.html.erb +1 -1
  175. data/app/views/shipit/ccmenu/project.xml.builder +2 -1
  176. data/app/views/shipit/deploys/show.html.erb +2 -2
  177. data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
  178. data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
  179. data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
  180. data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
  181. data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
  182. data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
  183. data/app/views/shipit/merge_status/failure.html.erb +1 -1
  184. data/app/views/shipit/merge_status/locked.html.erb +1 -1
  185. data/app/views/shipit/merge_status/success.html.erb +2 -2
  186. data/app/views/shipit/repositories/_header.html.erb +19 -0
  187. data/app/views/shipit/repositories/index.html.erb +31 -0
  188. data/app/views/shipit/repositories/new.html.erb +23 -0
  189. data/app/views/shipit/repositories/settings.html.erb +53 -0
  190. data/app/views/shipit/repositories/show.html.erb +30 -0
  191. data/app/views/shipit/stacks/_banners.html.erb +15 -1
  192. data/app/views/shipit/stacks/_header.html.erb +20 -7
  193. data/app/views/shipit/stacks/_stack.html.erb +8 -0
  194. data/app/views/shipit/stacks/all_tasks.html.erb +28 -0
  195. data/app/views/shipit/stacks/index.html.erb +3 -2
  196. data/app/views/shipit/stacks/new.html.erb +1 -1
  197. data/app/views/shipit/stacks/settings.html.erb +5 -5
  198. data/app/views/shipit/stacks/show.html.erb +1 -1
  199. data/app/views/shipit/tasks/_task_output.html.erb +1 -1
  200. data/app/views/shipit/tasks/show.html.erb +1 -1
  201. data/config/initializers/inflections.rb +2 -1
  202. data/config/locales/en.yml +4 -3
  203. data/config/routes.rb +25 -7
  204. data/config/secrets.development.example.yml +24 -0
  205. data/config/secrets.development.shopify.yml +20 -9
  206. data/db/migrate/20200226211925_add_index_to_tasks_status.rb +5 -0
  207. data/db/migrate/20200427135152_add_pull_request_head_sha_to_commit.rb +5 -0
  208. data/db/migrate/20200615181558_add_rollback_once_aborted_to.rb +5 -0
  209. data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
  210. data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
  211. data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
  212. data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
  213. data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
  214. data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
  215. data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
  216. data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
  217. data/db/migrate/20210325194053_remove_stacks_branch_default.rb +5 -0
  218. data/db/migrate/20210504200438_add_github_updated_at_to_check_runs.rb +5 -0
  219. data/lib/shipit.rb +61 -17
  220. data/lib/shipit/cast_value.rb +1 -0
  221. data/lib/shipit/command.rb +20 -21
  222. data/lib/shipit/commands.rb +14 -6
  223. data/lib/shipit/csv_serializer.rb +1 -0
  224. data/lib/shipit/deploy_commands.rb +1 -0
  225. data/lib/shipit/engine.rb +9 -2
  226. data/lib/shipit/environment_variables.rb +11 -1
  227. data/lib/shipit/first_parent_commits_iterator.rb +1 -0
  228. data/lib/shipit/flock.rb +9 -1
  229. data/lib/shipit/github_app.rb +15 -12
  230. data/lib/shipit/github_http_cache_middleware.rb +1 -0
  231. data/lib/shipit/null_serializer.rb +1 -0
  232. data/lib/shipit/octokit_check_runs.rb +3 -2
  233. data/lib/shipit/octokit_iterator.rb +4 -3
  234. data/lib/shipit/paginator.rb +3 -2
  235. data/lib/shipit/review_stack_commands.rb +8 -0
  236. data/lib/shipit/rollback_commands.rb +1 -0
  237. data/lib/shipit/same_site_cookie_middleware.rb +29 -0
  238. data/lib/shipit/simple_message_verifier.rb +3 -2
  239. data/lib/shipit/stack_commands.rb +37 -7
  240. data/lib/shipit/stat.rb +1 -0
  241. data/lib/shipit/task_commands.rb +23 -16
  242. data/lib/shipit/version.rb +2 -1
  243. data/lib/snippets/publish-lerna-independent-packages +35 -34
  244. data/lib/snippets/publish-lerna-independent-packages-legacy +39 -0
  245. data/lib/snippets/release-gem +5 -1
  246. data/lib/tasks/cron.rake +13 -2
  247. data/lib/tasks/dev.rake +3 -2
  248. data/lib/tasks/shipit.rake +15 -14
  249. data/lib/tasks/teams.rake +1 -0
  250. data/test/controllers/api/base_controller_test.rb +3 -2
  251. data/test/controllers/api/ccmenu_controller_test.rb +9 -8
  252. data/test/controllers/api/commits_controller_test.rb +3 -2
  253. data/test/controllers/api/deploys_controller_test.rb +32 -14
  254. data/test/controllers/api/hooks_controller_test.rb +8 -7
  255. data/test/controllers/api/locks_controller_test.rb +7 -6
  256. data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +17 -16
  257. data/test/controllers/api/outputs_controller_test.rb +3 -1
  258. data/test/controllers/api/release_statuses_controller_test.rb +2 -1
  259. data/test/controllers/api/rollback_controller_test.rb +113 -0
  260. data/test/controllers/api/stacks_controller_test.rb +71 -16
  261. data/test/controllers/api/tasks_controller_test.rb +13 -12
  262. data/test/controllers/api_clients_controller_test.rb +5 -4
  263. data/test/controllers/ccmenu_controller_test.rb +4 -3
  264. data/test/controllers/commit_checks_controller_test.rb +4 -3
  265. data/test/controllers/commits_controller_test.rb +3 -2
  266. data/test/controllers/deploys_controller_test.rb +32 -21
  267. data/test/controllers/github_authentication_controller_test.rb +1 -0
  268. data/test/controllers/merge_requests_controller_test.rb +32 -0
  269. data/test/controllers/merge_status_controller_test.rb +7 -6
  270. data/test/controllers/release_statuses_controller_test.rb +3 -2
  271. data/test/controllers/repositories_controller_test.rb +71 -0
  272. data/test/controllers/rollbacks_controller_test.rb +9 -8
  273. data/test/controllers/stacks_controller_test.rb +41 -19
  274. data/test/controllers/status_controller_test.rb +1 -0
  275. data/test/controllers/tasks_controller_test.rb +32 -19
  276. data/test/controllers/webhooks_controller_test.rb +33 -17
  277. data/test/dummy/app/assets/config/manifest.js +3 -0
  278. data/test/dummy/config/application.rb +7 -2
  279. data/test/dummy/config/database.yml +9 -0
  280. data/test/dummy/config/environments/development.rb +3 -4
  281. data/test/dummy/config/environments/test.rb +2 -5
  282. data/test/dummy/config/secrets_double_github_app.yml +79 -0
  283. data/test/dummy/db/schema.rb +59 -17
  284. data/test/dummy/db/seeds.rb +2 -1
  285. data/test/fixtures/payloads/check_suite_master.json +4 -32
  286. data/test/fixtures/payloads/invalid_pull_request.json +117 -0
  287. data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
  288. data/test/fixtures/payloads/pull_request_assigned.json +480 -0
  289. data/test/fixtures/payloads/pull_request_closed.json +454 -0
  290. data/test/fixtures/payloads/pull_request_labeled.json +461 -0
  291. data/test/fixtures/payloads/pull_request_opened.json +454 -0
  292. data/test/fixtures/payloads/pull_request_reopened.json +454 -0
  293. data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
  294. data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
  295. data/test/fixtures/payloads/push_master.json +1 -1
  296. data/test/fixtures/payloads/push_not_master.json +1 -1
  297. data/test/fixtures/shipit/commits.yml +31 -3
  298. data/test/fixtures/shipit/hooks.yml +1 -0
  299. data/test/fixtures/shipit/merge_requests.yml +141 -0
  300. data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
  301. data/test/fixtures/shipit/pull_requests.yml +10 -131
  302. data/test/fixtures/shipit/repositories.yml +5 -0
  303. data/test/fixtures/shipit/stacks.yml +235 -14
  304. data/test/fixtures/shipit/statuses.yml +9 -0
  305. data/test/fixtures/shipit/tasks.yml +4 -1
  306. data/test/fixtures/shipit/users.yml +7 -0
  307. data/test/fixtures/timeout +2 -1
  308. data/test/helpers/api_helper.rb +1 -0
  309. data/test/helpers/fixture_aliases_helper.rb +1 -0
  310. data/test/helpers/hooks_helper.rb +2 -1
  311. data/test/helpers/json_helper.rb +20 -12
  312. data/test/helpers/links_helper.rb +4 -3
  313. data/test/helpers/payloads_helper.rb +5 -0
  314. data/test/helpers/queries_helper.rb +3 -2
  315. data/test/jobs/cache_deploy_spec_job_test.rb +2 -1
  316. data/test/jobs/chunk_rollup_job_test.rb +16 -1
  317. data/test/jobs/deliver_hook_job_test.rb +1 -0
  318. data/test/jobs/destroy_repository_job_test.rb +27 -0
  319. data/test/jobs/destroy_stack_job_test.rb +1 -0
  320. data/test/jobs/emit_event_job_test.rb +2 -1
  321. data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
  322. data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
  323. data/test/jobs/github_sync_job_test.rb +3 -1
  324. data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
  325. data/test/jobs/perform_task_job_test.rb +12 -11
  326. data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +19 -18
  327. data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
  328. data/test/jobs/reap_dead_tasks_job_test.rb +68 -0
  329. data/test/jobs/refresh_github_user_job_test.rb +1 -0
  330. data/test/jobs/refresh_status_job_test.rb +1 -0
  331. data/test/jobs/unique_job_test.rb +1 -0
  332. data/test/jobs/update_github_last_deployed_ref_job_test.rb +1 -0
  333. data/test/lib/shipit/deploy_commands_test.rb +16 -0
  334. data/test/lib/shipit/task_commands_test.rb +17 -0
  335. data/test/middleware/same_site_cookie_middleware_test.rb +52 -0
  336. data/test/models/api_client_test.rb +1 -0
  337. data/test/models/commit_checks_test.rb +1 -0
  338. data/test/models/commit_deployment_status_test.rb +4 -3
  339. data/test/models/commit_deployment_test.rb +2 -1
  340. data/test/models/commits_test.rb +96 -19
  341. data/test/models/delivery_test.rb +2 -1
  342. data/test/models/deploy_spec_test.rb +110 -65
  343. data/test/models/deploy_stats_test.rb +1 -0
  344. data/test/models/deploys_test.rb +197 -36
  345. data/test/models/duration_test.rb +1 -0
  346. data/test/models/github_hook_test.rb +1 -0
  347. data/test/models/hook_test.rb +47 -10
  348. data/test/models/membership_test.rb +1 -0
  349. data/test/models/{pull_request_test.rb → merge_request_test.rb} +53 -37
  350. data/test/models/pull_request_assignment_test.rb +16 -0
  351. data/test/models/release_statuses_test.rb +1 -0
  352. data/test/models/rollbacks_test.rb +1 -0
  353. data/test/models/shipit/check_run_test.rb +125 -5
  354. data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
  355. data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
  356. data/test/models/shipit/provisioning_handler_test.rb +64 -0
  357. data/test/models/shipit/pull_request_test.rb +52 -0
  358. data/test/models/shipit/repository_test.rb +6 -1
  359. data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
  360. data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
  361. data/test/models/shipit/review_stack_test.rb +91 -0
  362. data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +121 -16
  363. data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
  364. data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
  365. data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
  366. data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
  367. data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
  368. data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
  369. data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
  370. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +107 -0
  371. data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
  372. data/test/models/shipit/{wehbooks → webhooks}/handlers_test.rb +1 -0
  373. data/test/models/status/group_test.rb +1 -0
  374. data/test/models/status/missing_test.rb +1 -0
  375. data/test/models/status_test.rb +1 -0
  376. data/test/models/task_definitions_test.rb +9 -8
  377. data/test/models/tasks_test.rb +81 -1
  378. data/test/models/team_test.rb +4 -2
  379. data/test/models/undeployed_commits_test.rb +1 -0
  380. data/test/models/users_test.rb +13 -5
  381. data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
  382. data/test/test_command_integration.rb +3 -2
  383. data/test/test_helper.rb +49 -31
  384. data/test/unit/anonymous_user_serializer_test.rb +14 -0
  385. data/test/unit/command_test.rb +16 -10
  386. data/test/unit/commands_test.rb +1 -0
  387. data/test/unit/commit_serializer_test.rb +16 -0
  388. data/test/unit/csv_serializer_test.rb +3 -2
  389. data/test/unit/deploy_commands_test.rb +75 -18
  390. data/test/unit/deploy_serializer_test.rb +17 -0
  391. data/test/unit/environment_variables_test.rb +5 -4
  392. data/test/unit/github_app_test.rb +3 -3
  393. data/test/unit/github_apps_test.rb +416 -0
  394. data/test/unit/github_url_helper_test.rb +6 -0
  395. data/test/unit/rollback_commands_test.rb +2 -1
  396. data/test/unit/shipit_deployment_checks_test.rb +77 -0
  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 +15 -0
  400. data/test/unit/user_serializer_test.rb +14 -0
  401. data/test/unit/variable_definition_test.rb +1 -0
  402. metadata +320 -178
  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,18 @@
1
+ # frozen_string_literal: true
2
+ module Shipit
3
+ class Record < ActiveRecord::Base
4
+ self.abstract_class = true
5
+
6
+ class << self
7
+ def serializer_class
8
+ if defined? @serializer_class
9
+ @serializer_class
10
+ else
11
+ @serializer_class = "#{name}Serializer".safe_constantize
12
+ end
13
+ end
14
+ end
15
+
16
+ delegate :serializer_class, to: :class
17
+ end
18
+ end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
- class ReleaseStatus < ActiveRecord::Base
3
+ class ReleaseStatus < Record
3
4
  MAX_DESCRIPTION_LENGTH = 140
4
5
  include DeferredTouch
5
6
 
@@ -13,7 +14,7 @@ module Shipit
13
14
  scope :to_be_created, -> { where(github_id: nil).order(id: :asc) }
14
15
 
15
16
  STATES = %w(pending success failure error).freeze
16
- validates :state, presence: true, inclusion: {in: STATES}
17
+ validates :state, presence: true, inclusion: { in: STATES }
17
18
 
18
19
  def create_status_on_github!
19
20
  return true if github_id?
@@ -24,7 +25,7 @@ module Shipit
24
25
  private
25
26
 
26
27
  def create_status_on_github
27
- Shipit.github.api.create_status(
28
+ stack.github_api.create_status(
28
29
  stack.github_repo_name,
29
30
  commit.sha,
30
31
  state,
@@ -1,4 +1,35 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
3
+ class NullRepository
4
+ def id
5
+ nil
6
+ end
7
+
8
+ def stacks
9
+ Shipit::Stack.none
10
+ end
11
+
12
+ def review_stacks
13
+ Shipit::ReviewStack.none
14
+ end
15
+
16
+ def review_stacks_enabled
17
+ false
18
+ end
19
+
20
+ def provisioning_behavior_allow_all?
21
+ false
22
+ end
23
+
24
+ def provisioning_behavior_allow_with_label?
25
+ false
26
+ end
27
+
28
+ def provisioning_behavior_prevent_with_label?
29
+ false
30
+ end
31
+ end
32
+
2
33
  class Repository < ApplicationRecord
3
34
  OWNER_MAX_SIZE = 39
4
35
  private_constant :OWNER_MAX_SIZE
@@ -6,13 +37,17 @@ module Shipit
6
37
  NAME_MAX_SIZE = 100
7
38
  private_constant :NAME_MAX_SIZE
8
39
 
9
- validates :name, uniqueness: {scope: %i(owner), case_sensitive: false,
10
- message: 'cannot be used more than once'}
40
+ validates :name, uniqueness: { scope: %i(owner), case_sensitive: false,
41
+ message: 'cannot be used more than once', }
11
42
  validates :owner, :name, presence: true, ascii_only: true
12
- validates :owner, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: OWNER_MAX_SIZE}
13
- validates :name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: NAME_MAX_SIZE}
43
+ validates :owner, format: { with: /\A[a-z0-9_\-\.]+\z/ }, length: { maximum: OWNER_MAX_SIZE }
44
+ validates :name, format: { with: /\A[a-z0-9_\-\.]+\z/ }, length: { maximum: NAME_MAX_SIZE }
14
45
 
15
46
  has_many :stacks, dependent: :destroy
47
+ has_many :review_stacks, dependent: :destroy
48
+
49
+ PROVISIONING_BEHAVIORS = %w(allow_all allow_with_label prevent_with_label).freeze
50
+ enum provisioning_behavior: PROVISIONING_BEHAVIORS.zip(PROVISIONING_BEHAVIORS).to_h, _prefix: :provisioning_behavior
16
51
 
17
52
  def self.from_github_repo_name(github_repo_name)
18
53
  repo_owner, repo_name = github_repo_name.downcase.split('/')
@@ -27,12 +62,42 @@ module Shipit
27
62
  super(o&.downcase)
28
63
  end
29
64
 
65
+ def github_repo_name
66
+ [owner, name].join('/')
67
+ end
68
+
30
69
  def http_url
31
- Shipit.github.url("#{owner}/#{name}")
70
+ github_app.url(full_name)
71
+ end
72
+
73
+ def full_name
74
+ "#{owner}/#{name}"
32
75
  end
33
76
 
34
77
  def git_url
35
- "https://#{Shipit.github.domain}/#{owner}/#{name}.git"
78
+ "https://#{github_app.domain}/#{owner}/#{name}.git"
79
+ end
80
+
81
+ def schedule_for_destroy!
82
+ DestroyRepositoryJob.perform_later(self)
83
+ end
84
+
85
+ def to_param
86
+ github_repo_name
87
+ end
88
+
89
+ def self.from_param!(param)
90
+ repo_owner, repo_name = param.split('/')
91
+ where(
92
+ owner: repo_owner.downcase,
93
+ name: repo_name.downcase,
94
+ ).first!
95
+ end
96
+
97
+ protected
98
+
99
+ def github_app
100
+ Shipit.github(organization: owner)
36
101
  end
37
102
  end
38
103
  end
@@ -0,0 +1,130 @@
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
+ ).find_each do |review_stack|
11
+ review_stack.clear_local_files
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
+ def update_latest_deployed_ref
26
+ # noop: last deployed ref is useless for review stacks
27
+ end
28
+
29
+ model_name.class_eval do
30
+ def route_key
31
+ "stacks"
32
+ end
33
+
34
+ def singular_route_key
35
+ "stack"
36
+ end
37
+ end
38
+
39
+ has_one :pull_request, foreign_key: :stack_id
40
+
41
+ after_commit :emit_added_hooks, on: :create
42
+ after_commit :emit_updated_hooks, on: :update
43
+ after_commit :emit_removed_hooks, on: :destroy
44
+
45
+ state_machine :provision_status, initial: :deprovisioned do
46
+ state :provisioned
47
+ state :provisioning
48
+ state :deprovisioning
49
+ state :deprovisioned
50
+
51
+ event :provision do
52
+ transition deprovisioned: :provisioning
53
+ end
54
+
55
+ event :provision_success do
56
+ transition provisioning: :provisioned
57
+ end
58
+
59
+ event :provision_failure do
60
+ transition provisioning: :deprovisioned
61
+ end
62
+
63
+ event :deprovision do
64
+ transition provisioned: :deprovisioning
65
+ end
66
+
67
+ event :deprovision_success do
68
+ transition deprovisioning: :deprovisioned
69
+ end
70
+
71
+ event :deprovision_failure do
72
+ transition deprovisioning: :provisioned
73
+ end
74
+
75
+ after_transition deprovisioned: :provisioning do |stack, _|
76
+ stack.provisioner.up
77
+ end
78
+
79
+ after_transition provisioned: :deprovisioning do |stack, _|
80
+ stack.provisioner.down
81
+ end
82
+ end
83
+
84
+ def env
85
+ return super unless pull_request.present?
86
+
87
+ super
88
+ .merge(
89
+ pull_request
90
+ .labels
91
+ .each_with_object({}) { |label_name, labels| labels[label_name.upcase] = "true" }
92
+ )
93
+ end
94
+
95
+ def provisioner
96
+ provisioner_class.new(self)
97
+ end
98
+
99
+ def provisioner_class
100
+ ProvisioningHandler.fetch(provisioning_handler_name)
101
+ end
102
+
103
+ def enqueue_for_provisioning
104
+ return if awaiting_provision
105
+ update!(awaiting_provision: true)
106
+ end
107
+
108
+ def remove_from_provisioning_queue
109
+ return unless awaiting_provision
110
+ update!(awaiting_provision: false)
111
+ end
112
+
113
+ def to_partial_path
114
+ "shipit/stacks/stack"
115
+ end
116
+
117
+ def emit_added_hooks
118
+ Hook.emit(:review_stack, self, action: :added, review_stack: self)
119
+ end
120
+
121
+ def emit_updated_hooks
122
+ changed = !(previous_changes.keys - %w(updated_at)).empty?
123
+ Hook.emit(:review_stack, self, action: :updated, review_stack: self) if changed
124
+ end
125
+
126
+ def emit_removed_hooks
127
+ Hook.emit(:review_stack, self, action: :removed, review_stack: self)
128
+ end
129
+ end
130
+ 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
 
@@ -26,14 +27,14 @@ module Shipit
26
27
  REQUIRED_HOOKS = %i(push status).freeze
27
28
 
28
29
  has_many :commits, dependent: :destroy
29
- has_many :pull_requests, dependent: :destroy
30
+ has_many :merge_requests, dependent: :destroy
30
31
  has_many :tasks, dependent: :destroy
31
32
  has_many :deploys
32
33
  has_many :rollbacks
33
34
  has_many :deploys_and_rollbacks,
34
- -> { where(type: %w(Shipit::Deploy Shipit::Rollback)) },
35
- class_name: 'Task',
36
- inverse_of: :stack
35
+ -> { where(type: %w(Shipit::Deploy Shipit::Rollback)) },
36
+ class_name: 'Task',
37
+ inverse_of: :stack
37
38
  has_many :github_hooks, dependent: :destroy, class_name: 'Shipit::GithubHook::Repo'
38
39
  has_many :hooks, dependent: :destroy
39
40
  has_many :api_clients, dependent: :destroy
@@ -43,6 +44,22 @@ module Shipit
43
44
 
44
45
  scope :not_archived, -> { where(archived_since: nil) }
45
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
+
46
63
  def repository
47
64
  super || build_repository
48
65
  end
@@ -66,19 +83,36 @@ module Shipit
66
83
  after_commit :emit_merge_status_hooks, on: :update
67
84
  after_commit :sync_github, on: :create
68
85
  after_commit :schedule_merges_if_necessary, on: :update
86
+ after_commit :sync_github_if_necessary, on: :update
87
+
88
+ def sync_github_if_necessary
89
+ if (archived_since_previously_changed? && archived_since.nil?) || branch_previously_changed?
90
+ sync_github
91
+ end
92
+ end
69
93
 
70
94
  validates :repository, uniqueness: {
71
95
  scope: %i(environment), case_sensitive: false,
72
- message: 'cannot be used more than once with this environment. Check archived stacks.'
96
+ message: 'cannot be used more than once with this environment. Check archived stacks.',
73
97
  }
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
98
+ validates :environment, format: { with: /\A[a-z0-9\-_\:]+\z/ }, length: { maximum: ENVIRONMENT_MAX_SIZE }
99
+ validates :deploy_url, format: { with: URI.regexp(%w(http https ssh)) }, allow_blank: true
100
+ validates :branch, presence: true
76
101
 
77
- validates :lock_reason, length: {maximum: 4096}
102
+ validates :lock_reason, length: { maximum: 4096 }
78
103
 
79
104
  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
105
+ delegate(
106
+ :provisioning_handler_name,
107
+ :find_task_definition,
108
+ :release_status?,
109
+ :release_status_context,
110
+ :release_status_delay,
111
+ :supports_fetch_deployed_revision?,
112
+ :supports_rollback?,
113
+ to: :cached_deploy_spec,
114
+ allow_nil: true
115
+ )
82
116
 
83
117
  def self.refresh_deployed_revisions
84
118
  find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
@@ -125,13 +159,29 @@ module Shipit
125
159
  env: filter_deploy_envs(env&.to_h || {}),
126
160
  allow_concurrency: force,
127
161
  ignored_safeties: force || !until_commit.deployable?,
162
+ max_retries: retries_on_deploy,
128
163
  )
129
164
  end
130
165
 
131
166
  def trigger_deploy(*args, **kwargs)
167
+ if changed?
168
+ # If this is the first deploy since the spec changed it's possible the record will be dirty here, meaning we
169
+ # cant lock. In this one case persist the changes, otherwise log a warning and let the lock raise, so we
170
+ # can debug what's going on here. We don't expect anything other than the deploy spec to dirty the model
171
+ # instance, because of how that field is serialised.
172
+ if changes.keys == ['cached_deploy_spec']
173
+ save!
174
+ else
175
+ Rails.logger.warning("#{changes.keys} field(s) were unexpectedly modified on stack #{id} while deploying")
176
+ end
177
+ end
178
+
132
179
  run_now = kwargs.delete(:run_now)
133
- deploy = build_deploy(*args, **kwargs)
134
- deploy.save!
180
+ deploy = with_lock do
181
+ deploy = build_deploy(*args, **kwargs)
182
+ deploy.save!
183
+ deploy
184
+ end
135
185
  run_now ? deploy.run_now! : deploy.enqueue
136
186
  continuous_delivery_resumed!
137
187
  deploy
@@ -142,7 +192,7 @@ module Shipit
142
192
  end
143
193
 
144
194
  def continuous_delivery_delayed?
145
- continuous_delivery_delayed_since? && continuous_deployment? && checks?
195
+ continuous_delivery_delayed_since? && continuous_deployment? && (checks? || deployment_checks?)
146
196
  end
147
197
 
148
198
  def continuous_delivery_delayed!
@@ -150,15 +200,16 @@ module Shipit
150
200
  end
151
201
 
152
202
  def trigger_continuous_delivery
203
+ return if cached_deploy_spec.blank?
204
+
153
205
  commit = next_commit_to_deploy
154
206
 
155
- if !deployable? || deployed_too_recently? || commit.nil? || commit.deployed?
207
+ if should_resume_continuous_delivery?(commit)
156
208
  continuous_delivery_resumed!
157
209
  return
158
210
  end
159
211
 
160
- if commit.deploy_failed? || (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
161
- commit.recently_pushed?
212
+ if should_delay_continuous_delivery?(commit)
162
213
  continuous_delivery_delayed!
163
214
  return
164
215
  end
@@ -170,7 +221,7 @@ module Shipit
170
221
  end
171
222
 
172
223
  def schedule_merges
173
- MergePullRequestsJob.perform_later(self)
224
+ ProcessMergeRequestsJob.perform_later(self)
174
225
  end
175
226
 
176
227
  def next_commit_to_deploy
@@ -190,7 +241,7 @@ module Shipit
190
241
  def async_refresh_deployed_revision
191
242
  async_refresh_deployed_revision!
192
243
  rescue => error
193
- logger.warn "Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}"
244
+ logger.warn("Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}")
194
245
  end
195
246
 
196
247
  def async_refresh_deployed_revision!
@@ -258,8 +309,8 @@ module Shipit
258
309
  next if commits_to_lock.empty?
259
310
 
260
311
  affected_rows += commits
261
- .where(id: commits_to_lock.map(&:id).uniq)
262
- .lock_all(revert.author)
312
+ .where(id: commits_to_lock.map(&:id).uniq)
313
+ .lock_all(revert.author)
263
314
  end
264
315
 
265
316
  touch if affected_rows > 1
@@ -310,7 +361,7 @@ module Shipit
310
361
  end
311
362
 
312
363
  def deployable?
313
- !locked? && !active_task?
364
+ !locked? && !active_task? && !awaiting_provision? && deployment_checks_passed?
314
365
  end
315
366
 
316
367
  def allows_merges?
@@ -318,7 +369,7 @@ module Shipit
318
369
  end
319
370
 
320
371
  def merge_method
321
- cached_deploy_spec&.pull_request_merge_method || Shipit.default_merge_method
372
+ cached_deploy_spec&.merge_request_merge_method || Shipit.default_merge_method
322
373
  end
323
374
 
324
375
  delegate :name=, to: :repository, prefix: :repo
@@ -329,42 +380,51 @@ module Shipit
329
380
  delegate :git_url, to: :repository, prefix: :repo
330
381
 
331
382
  def base_path
332
- Rails.root.join('data', 'stacks', repo_owner, repo_name, environment)
383
+ @base_path ||= Rails.root.join('data', 'stacks', repo_owner, repo_name, environment)
333
384
  end
334
385
 
335
386
  def deploys_path
336
- File.join(base_path, "deploys")
387
+ @deploys_path ||= base_path.join("deploys")
337
388
  end
338
389
 
339
390
  def git_path
340
- File.join(base_path, "git")
391
+ @git_path ||= base_path.join("git")
341
392
  end
342
393
 
343
394
  def acquire_git_cache_lock(timeout: 15, &block)
344
- Flock.new(git_path.to_s + '.lock').lock(timeout: timeout, &block)
395
+ @git_cache_lock ||= Flock.new(git_path.to_s + '.lock')
396
+ @git_cache_lock.lock(timeout: timeout, &block)
345
397
  end
346
398
 
347
399
  def clear_git_cache!
348
400
  tmp_path = "#{git_path}-#{SecureRandom.hex}"
349
- return unless File.exist?(git_path)
401
+ return unless git_path.exist?
350
402
  acquire_git_cache_lock do
351
- File.rename(git_path, tmp_path)
403
+ git_path.rename(tmp_path)
352
404
  end
353
405
  FileUtils.rm_rf(tmp_path)
354
406
  end
355
407
 
356
408
  def github_repo_name
357
- [repo_owner, repo_name].join('/')
409
+ repository.github_repo_name
358
410
  end
359
411
 
360
412
  def github_commits
361
413
  handle_github_redirections do
362
- Shipit.github.api.commits(github_repo_name, sha: branch)
414
+ github_api.commits(github_repo_name, sha: branch)
363
415
  end
364
416
  rescue Octokit::Conflict
365
417
  [] # Repository is empty...
366
418
  end
367
419
 
420
+ def github_api
421
+ github_app.api
422
+ end
423
+
424
+ def github_app
425
+ Shipit.github(organization: repository.owner)
426
+ end
427
+
368
428
  def handle_github_redirections
369
429
  # https://developer.github.com/v3/#http-redirects
370
430
  resource = yield
@@ -377,9 +437,9 @@ module Shipit
377
437
  end
378
438
 
379
439
  def refresh_repository!
380
- resource = Shipit.github.api.repo(github_repo_name)
440
+ resource = github_api.repo(github_repo_name)
381
441
  if resource.try(:message) == 'Moved Permanently'
382
- resource = Shipit.github.api.get(resource.url)
442
+ resource = github_api.get(resource.url)
383
443
  end
384
444
  repository.update!(owner: resource.owner.login, name: resource.name)
385
445
  end
@@ -398,7 +458,7 @@ module Shipit
398
458
  end
399
459
 
400
460
  def lock(reason, user)
401
- params = {lock_reason: reason, lock_author: user}
461
+ params = { lock_reason: reason, lock_author: user }
402
462
  update!(params)
403
463
  end
404
464
 
@@ -444,8 +504,9 @@ module Shipit
444
504
  end
445
505
 
446
506
  delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
447
- :blocking_statuses, :deploy_variables, :filter_task_envs, :filter_deploy_envs,
448
- :maximum_commits_per_deploy, :pause_between_deploys, to: :cached_deploy_spec
507
+ :blocking_statuses, :deploy_variables, :filter_task_envs, :filter_deploy_envs,
508
+ :maximum_commits_per_deploy, :pause_between_deploys, :retries_on_deploy, :retries_on_rollback,
509
+ to: :cached_deploy_spec
449
510
 
450
511
  def monitoring?
451
512
  monitoring.present?
@@ -470,13 +531,15 @@ module Shipit
470
531
  end
471
532
 
472
533
  def update_latest_deployed_ref
473
- UpdateGithubLastDeployedRefJob.perform_later(self)
534
+ if Shipit.update_latest_deployed_ref
535
+ UpdateGithubLastDeployedRefJob.perform_later(self)
536
+ end
474
537
  end
475
538
 
476
539
  def broadcast_update
477
540
  Pubsubstub.publish(
478
541
  "stack.#{id}",
479
- {id: id, updated_at: updated_at}.to_json,
542
+ { id: id, updated_at: updated_at }.to_json,
480
543
  name: 'update',
481
544
  )
482
545
  end
@@ -528,19 +591,38 @@ module Shipit
528
591
  GithubSyncJob.perform_later(stack_id: id)
529
592
  end
530
593
 
531
- private
594
+ def links
595
+ links_spec = cached_deploy_spec&.links || {}
596
+ context = EnvironmentVariables.with(env)
532
597
 
533
- def clear_cache
534
- remove_instance_variable(:@active_task) if defined?(@active_task)
598
+ links_spec.transform_values { |url| context.interpolate(url) }
535
599
  end
536
600
 
537
601
  def clear_local_files
538
602
  FileUtils.rm_rf(base_path.to_s)
539
603
  end
540
604
 
605
+ def deployment_checks_passed?
606
+ return true unless deployment_checks?
607
+
608
+ Shipit.deployment_checks.call(self)
609
+ end
610
+
611
+ private
612
+
613
+ def clear_cache
614
+ remove_instance_variable(:@active_task) if defined?(@active_task)
615
+ end
616
+
541
617
  def update_defaults
542
618
  self.environment = 'production' if environment.blank?
543
- self.branch = 'master' if branch.blank?
619
+ self.branch = default_branch_name if branch.blank?
620
+ end
621
+
622
+ def default_branch_name
623
+ Shipit.github.api.repo(github_repo_name).default_branch
624
+ rescue Octokit::NotFound, Octokit::InvalidRepository
625
+ nil
544
626
  end
545
627
 
546
628
  def set_locked_since
@@ -554,7 +636,7 @@ module Shipit
554
636
  end
555
637
 
556
638
  def schedule_merges_if_necessary
557
- if previous_changes.include?('lock_reason') && previous_changes['lock_reason'].last.blank?
639
+ if lock_reason_previously_changed? && lock_reason.blank?
558
640
  schedule_merges
559
641
  end
560
642
  end
@@ -563,7 +645,7 @@ module Shipit
563
645
  return unless previous_changes.include?('lock_reason')
564
646
 
565
647
  lock_details = if previous_changes['lock_reason'].last.blank?
566
- {from: previous_changes['locked_since'].first, until: Time.zone.now}
648
+ { from: previous_changes['locked_since'].first, until: Time.zone.now }
567
649
  end
568
650
 
569
651
  Hook.emit(:lock, self, locked: locked?, lock_details: lock_details, stack: self)
@@ -589,5 +671,23 @@ module Shipit
589
671
  def ci_enabled_cache_key
590
672
  "stacks:#{id}:ci_enabled"
591
673
  end
674
+
675
+ def should_resume_continuous_delivery?(commit)
676
+ (deployment_checks_passed? && !deployable?) ||
677
+ deployed_too_recently? ||
678
+ commit.nil? ||
679
+ commit.deployed?
680
+ end
681
+
682
+ def should_delay_continuous_delivery?(commit)
683
+ commit.deploy_failed? ||
684
+ (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
685
+ !deployment_checks_passed? ||
686
+ commit.recently_pushed?
687
+ end
688
+
689
+ def deployment_checks?
690
+ Shipit.deployment_checks.present?
691
+ end
592
692
  end
593
693
  end