shipit-engine 0.39.0 → 0.40.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 (315) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -1
  3. data/Rakefile +2 -1
  4. data/app/assets/javascripts/shipit/continuous_delivery_schedule.js.coffee +15 -0
  5. data/app/controllers/concerns/shipit/active_model_serializers_patch.rb +1 -0
  6. data/app/controllers/concerns/shipit/api/cacheable.rb +1 -0
  7. data/app/controllers/concerns/shipit/api/paginable.rb +3 -2
  8. data/app/controllers/concerns/shipit/api/rendering.rb +1 -0
  9. data/app/controllers/concerns/shipit/authentication.rb +1 -0
  10. data/app/controllers/concerns/shipit/pagination.rb +3 -2
  11. data/app/controllers/shipit/api/base_controller.rb +10 -8
  12. data/app/controllers/shipit/api/ccmenu_controller.rb +2 -1
  13. data/app/controllers/shipit/api/commits_controller.rb +2 -3
  14. data/app/controllers/shipit/api/deploys_controller.rb +2 -1
  15. data/app/controllers/shipit/api/hooks_controller.rb +4 -3
  16. data/app/controllers/shipit/api/locks_controller.rb +1 -0
  17. data/app/controllers/shipit/api/merge_requests_controller.rb +6 -5
  18. data/app/controllers/shipit/api/outputs_controller.rb +1 -0
  19. data/app/controllers/shipit/api/release_statuses_controller.rb +2 -1
  20. data/app/controllers/shipit/api/rollbacks_controller.rb +1 -0
  21. data/app/controllers/shipit/api/stacks_controller.rb +15 -14
  22. data/app/controllers/shipit/api/tasks_controller.rb +6 -5
  23. data/app/controllers/shipit/api_clients_controller.rb +6 -7
  24. data/app/controllers/shipit/ccmenu_url_controller.rb +3 -2
  25. data/app/controllers/shipit/commit_checks_controller.rb +2 -1
  26. data/app/controllers/shipit/commits_controller.rb +1 -0
  27. data/app/controllers/shipit/continuous_delivery_schedules_controller.rb +42 -0
  28. data/app/controllers/shipit/deploys_controller.rb +6 -5
  29. data/app/controllers/shipit/github_authentication_controller.rb +6 -0
  30. data/app/controllers/shipit/merge_requests_controller.rb +1 -0
  31. data/app/controllers/shipit/merge_status_controller.rb +30 -26
  32. data/app/controllers/shipit/release_statuses_controller.rb +1 -0
  33. data/app/controllers/shipit/repositories_controller.rb +4 -7
  34. data/app/controllers/shipit/rollbacks_controller.rb +2 -1
  35. data/app/controllers/shipit/shipit_controller.rb +1 -0
  36. data/app/controllers/shipit/stacks_controller.rb +27 -31
  37. data/app/controllers/shipit/status_controller.rb +1 -0
  38. data/app/controllers/shipit/tasks_controller.rb +3 -1
  39. data/app/controllers/shipit/webhooks_controller.rb +2 -1
  40. data/app/helpers/shipit/api_clients_helper.rb +1 -0
  41. data/app/helpers/shipit/chunks_helper.rb +3 -1
  42. data/app/helpers/shipit/deploys_helper.rb +7 -3
  43. data/app/helpers/shipit/github_url_helper.rb +5 -4
  44. data/app/helpers/shipit/merge_status_helper.rb +1 -0
  45. data/app/helpers/shipit/shipit_helper.rb +11 -10
  46. data/app/helpers/shipit/stacks_helper.rb +10 -11
  47. data/app/helpers/shipit/tasks_helper.rb +2 -1
  48. data/app/jobs/shipit/background_job/unique.rb +3 -2
  49. data/app/jobs/shipit/background_job.rb +9 -1
  50. data/app/jobs/shipit/cache_deploy_spec_job.rb +2 -1
  51. data/app/jobs/shipit/chunk_rollup_job.rb +1 -0
  52. data/app/jobs/shipit/clear_git_cache_job.rb +1 -0
  53. data/app/jobs/shipit/continuous_delivery_job.rb +5 -0
  54. data/app/jobs/shipit/create_on_github_job.rb +1 -0
  55. data/app/jobs/shipit/create_release_statuses_job.rb +2 -0
  56. data/app/jobs/shipit/deferred_touch_job.rb +1 -0
  57. data/app/jobs/shipit/deliver_hook_job.rb +1 -0
  58. data/app/jobs/shipit/destroy_job.rb +1 -0
  59. data/app/jobs/shipit/destroy_repository_job.rb +1 -0
  60. data/app/jobs/shipit/destroy_stack_job.rb +36 -15
  61. data/app/jobs/shipit/emit_event_job.rb +1 -0
  62. data/app/jobs/shipit/fetch_commit_stats_job.rb +1 -0
  63. data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
  64. data/app/jobs/shipit/github_sync_job.rb +4 -2
  65. data/app/jobs/shipit/mark_deploy_healthy_job.rb +1 -0
  66. data/app/jobs/shipit/perform_commit_checks_job.rb +1 -0
  67. data/app/jobs/shipit/perform_task_job.rb +1 -0
  68. data/app/jobs/shipit/process_merge_requests_job.rb +2 -0
  69. data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
  70. data/app/jobs/shipit/reap_dead_tasks_job.rb +1 -0
  71. data/app/jobs/shipit/refresh_check_runs_job.rb +1 -0
  72. data/app/jobs/shipit/refresh_github_user_job.rb +1 -0
  73. data/app/jobs/shipit/refresh_merge_request_job.rb +1 -0
  74. data/app/jobs/shipit/refresh_statuses_job.rb +1 -0
  75. data/app/jobs/shipit/setup_github_hook_job.rb +1 -0
  76. data/app/jobs/shipit/update_estimated_deploy_duration_job.rb +1 -0
  77. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +6 -7
  78. data/app/models/concerns/shipit/deferred_touch.rb +5 -2
  79. data/app/models/shipit/anonymous_user.rb +4 -5
  80. data/app/models/shipit/api_client.rb +4 -2
  81. data/app/models/shipit/application_record.rb +1 -0
  82. data/app/models/shipit/check_run.rb +7 -6
  83. data/app/models/shipit/command_line_user.rb +4 -5
  84. data/app/models/shipit/commit.rb +46 -32
  85. data/app/models/shipit/commit_checks.rb +4 -2
  86. data/app/models/shipit/commit_deployment.rb +7 -5
  87. data/app/models/shipit/commit_deployment_status.rb +5 -2
  88. data/app/models/shipit/commit_message.rb +2 -0
  89. data/app/models/shipit/continuous_delivery_schedule.rb +84 -0
  90. data/app/models/shipit/delivery.rb +4 -3
  91. data/app/models/shipit/deploy.rb +46 -26
  92. data/app/models/shipit/deploy_spec/bundler_discovery.rb +3 -1
  93. data/app/models/shipit/deploy_spec/capistrano_discovery.rb +1 -0
  94. data/app/models/shipit/deploy_spec/file_system.rb +35 -16
  95. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +4 -3
  96. data/app/models/shipit/deploy_spec/lerna_discovery.rb +32 -31
  97. data/app/models/shipit/deploy_spec/npm_discovery.rb +18 -13
  98. data/app/models/shipit/deploy_spec/pypi_discovery.rb +5 -4
  99. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -0
  100. data/app/models/shipit/deploy_spec.rb +25 -30
  101. data/app/models/shipit/deploy_stats.rb +6 -1
  102. data/app/models/shipit/duration.rb +5 -3
  103. data/app/models/shipit/ephemeral_commit_checks.rb +8 -7
  104. data/app/models/shipit/github_hook.rb +1 -0
  105. data/app/models/shipit/github_status.rb +1 -0
  106. data/app/models/shipit/hook.rb +8 -6
  107. data/app/models/shipit/membership.rb +1 -0
  108. data/app/models/shipit/merge_request.rb +26 -16
  109. data/app/models/shipit/output_chunk.rb +1 -0
  110. data/app/models/shipit/provisioning_handler.rb +1 -0
  111. data/app/models/shipit/pull_request.rb +1 -1
  112. data/app/models/shipit/record.rb +1 -0
  113. data/app/models/shipit/release_status.rb +4 -3
  114. data/app/models/shipit/repository.rb +12 -11
  115. data/app/models/shipit/review_stack.rb +3 -1
  116. data/app/models/shipit/review_stack_provisioning_queue.rb +2 -2
  117. data/app/models/shipit/rollback.rb +2 -0
  118. data/app/models/shipit/stack.rb +59 -56
  119. data/app/models/shipit/status/common.rb +1 -0
  120. data/app/models/shipit/status/group.rb +5 -3
  121. data/app/models/shipit/status/missing.rb +2 -1
  122. data/app/models/shipit/status/unknown.rb +1 -0
  123. data/app/models/shipit/status.rb +5 -4
  124. data/app/models/shipit/task.rb +33 -28
  125. data/app/models/shipit/task_definition.rb +10 -7
  126. data/app/models/shipit/task_execution_strategy/default.rb +13 -13
  127. data/app/models/shipit/team.rb +13 -12
  128. data/app/models/shipit/undeployed_commit.rb +8 -3
  129. data/app/models/shipit/unlimited_api_client.rb +2 -2
  130. data/app/models/shipit/user.rb +21 -16
  131. data/app/models/shipit/variable_definition.rb +2 -1
  132. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +1 -0
  133. data/app/models/shipit/webhooks/handlers/handler.rb +1 -0
  134. data/app/models/shipit/webhooks/handlers/membership_handler.rb +1 -0
  135. data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +10 -10
  136. data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +1 -1
  137. data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +10 -10
  138. data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +2 -2
  139. data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +2 -2
  140. data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +1 -1
  141. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +3 -3
  142. data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +1 -1
  143. data/app/models/shipit/webhooks/handlers/push_handler.rb +2 -1
  144. data/app/models/shipit/webhooks/handlers/status_handler.rb +1 -0
  145. data/app/models/shipit/webhooks.rb +3 -2
  146. data/app/serializers/concerns/shipit/conditional_attributes.rb +1 -0
  147. data/app/serializers/shipit/anonymous_user_serializer.rb +1 -0
  148. data/app/serializers/shipit/command_line_user_serializer.rb +1 -0
  149. data/app/serializers/shipit/commit_serializer.rb +2 -1
  150. data/app/serializers/shipit/deploy_serializer.rb +1 -0
  151. data/app/serializers/shipit/hook_serializer.rb +1 -0
  152. data/app/serializers/shipit/merge_request_serializer.rb +2 -1
  153. data/app/serializers/shipit/rollback_serializer.rb +1 -0
  154. data/app/serializers/shipit/short_commit_serializer.rb +1 -0
  155. data/app/serializers/shipit/stack_serializer.rb +4 -3
  156. data/app/serializers/shipit/tail_task_serializer.rb +4 -1
  157. data/app/serializers/shipit/task_serializer.rb +1 -0
  158. data/app/serializers/shipit/user_serializer.rb +1 -0
  159. data/app/validators/ascii_only_validator.rb +4 -3
  160. data/app/validators/subset_validator.rb +1 -0
  161. data/app/views/shipit/ccmenu/project.xml.builder +2 -1
  162. data/app/views/shipit/continuous_delivery_schedules/show.html.erb +59 -0
  163. data/app/views/shipit/stacks/_settings_form.erb +1 -0
  164. data/config/initializers/inflections.rb +1 -0
  165. data/config/locales/en.yml +1 -0
  166. data/config/routes.rb +21 -18
  167. data/db/migrate/20240821003007_add_continuous_delivery_schedules.rb +13 -0
  168. data/db/migrate/20250207203053_embiggen_github_ids.rb +8 -0
  169. data/lib/shipit/cast_value.rb +1 -0
  170. data/lib/shipit/command.rb +29 -9
  171. data/lib/shipit/commands.rb +4 -2
  172. data/lib/shipit/csv_serializer.rb +3 -0
  173. data/lib/shipit/deploy_commands.rb +2 -1
  174. data/lib/shipit/engine.rb +5 -4
  175. data/lib/shipit/environment_variables.rb +2 -0
  176. data/lib/shipit/first_parent_commits_iterator.rb +2 -3
  177. data/lib/shipit/flock.rb +11 -9
  178. data/lib/shipit/github_app.rb +14 -15
  179. data/lib/shipit/github_http_cache_middleware.rb +1 -0
  180. data/lib/shipit/null_serializer.rb +1 -0
  181. data/lib/shipit/octokit_check_runs.rb +1 -0
  182. data/lib/shipit/octokit_iterator.rb +2 -0
  183. data/lib/shipit/paginator.rb +1 -0
  184. data/lib/shipit/rollback_commands.rb +2 -1
  185. data/lib/shipit/same_site_cookie_middleware.rb +1 -0
  186. data/lib/shipit/simple_message_verifier.rb +1 -0
  187. data/lib/shipit/stack_commands.rb +34 -26
  188. data/lib/shipit/stat.rb +1 -0
  189. data/lib/shipit/task_commands.rb +7 -6
  190. data/lib/shipit/version.rb +2 -1
  191. data/lib/shipit.rb +29 -16
  192. data/lib/tasks/cron.rake +2 -1
  193. data/lib/tasks/dev.rake +3 -2
  194. data/lib/tasks/shipit.rake +3 -2
  195. data/lib/tasks/teams.rake +3 -2
  196. data/test/controllers/api/base_controller_test.rb +1 -0
  197. data/test/controllers/api/ccmenu_controller_test.rb +4 -3
  198. data/test/controllers/api/commits_controller_test.rb +1 -0
  199. data/test/controllers/api/deploys_controller_test.rb +2 -1
  200. data/test/controllers/api/hooks_controller_test.rb +6 -5
  201. data/test/controllers/api/locks_controller_test.rb +1 -0
  202. data/test/controllers/api/merge_requests_controller_test.rb +1 -0
  203. data/test/controllers/api/outputs_controller_test.rb +1 -0
  204. data/test/controllers/api/release_statuses_controller_test.rb +4 -3
  205. data/test/controllers/api/rollback_controller_test.rb +3 -2
  206. data/test/controllers/api/stacks_controller_test.rb +13 -12
  207. data/test/controllers/api/tasks_controller_test.rb +7 -6
  208. data/test/controllers/api_clients_controller_test.rb +10 -10
  209. data/test/controllers/ccmenu_controller_test.rb +1 -0
  210. data/test/controllers/commit_checks_controller_test.rb +1 -0
  211. data/test/controllers/commits_controller_test.rb +9 -8
  212. data/test/controllers/continuous_delivery_schedules_controller_test.rb +66 -0
  213. data/test/controllers/deploys_controller_test.rb +4 -2
  214. data/test/controllers/github_authentication_controller_test.rb +6 -4
  215. data/test/controllers/merge_requests_controller_test.rb +1 -0
  216. data/test/controllers/merge_status_controller_test.rb +5 -4
  217. data/test/controllers/release_statuses_controller_test.rb +1 -0
  218. data/test/controllers/repositories_controller_test.rb +6 -5
  219. data/test/controllers/rollbacks_controller_test.rb +3 -2
  220. data/test/controllers/stacks_controller_test.rb +7 -5
  221. data/test/controllers/status_controller_test.rb +1 -0
  222. data/test/controllers/tasks_controller_test.rb +5 -4
  223. data/test/controllers/webhooks_controller_test.rb +10 -9
  224. data/test/dummy/config/application.rb +1 -1
  225. data/test/dummy/db/schema.rb +33 -6
  226. data/test/fixtures/shipit/commits.yml +7 -7
  227. data/test/fixtures/shipit/stacks.yml +4 -10
  228. data/test/fixtures/shipit/tasks.yml +3 -3
  229. data/test/helpers/api_helper.rb +2 -3
  230. data/test/helpers/fixture_aliases_helper.rb +1 -0
  231. data/test/helpers/hooks_helper.rb +1 -0
  232. data/test/helpers/json_helper.rb +4 -3
  233. data/test/helpers/links_helper.rb +2 -1
  234. data/test/helpers/payloads_helper.rb +1 -0
  235. data/test/helpers/queries_helper.rb +4 -3
  236. data/test/jobs/cache_deploy_spec_job_test.rb +3 -2
  237. data/test/jobs/chunk_rollup_job_test.rb +3 -2
  238. data/test/jobs/deliver_hook_job_test.rb +1 -0
  239. data/test/jobs/destroy_repository_job_test.rb +1 -0
  240. data/test/jobs/destroy_stack_job_test.rb +12 -0
  241. data/test/jobs/emit_event_job_test.rb +1 -0
  242. data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
  243. data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
  244. data/test/jobs/github_sync_job_test.rb +22 -21
  245. data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
  246. data/test/jobs/perform_task_job_test.rb +3 -3
  247. data/test/jobs/process_merge_requests_job_test.rb +7 -6
  248. data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
  249. data/test/jobs/reap_dead_tasks_job_test.rb +1 -0
  250. data/test/jobs/refresh_github_user_job_test.rb +1 -0
  251. data/test/jobs/refresh_status_job_test.rb +1 -0
  252. data/test/jobs/shipit/background_job_test.rb +35 -0
  253. data/test/jobs/shipit/continuous_delivery_job_test.rb +31 -0
  254. data/test/jobs/unique_job_test.rb +3 -1
  255. data/test/jobs/update_github_last_deployed_ref_job_test.rb +1 -0
  256. data/test/middleware/same_site_cookie_middleware_test.rb +2 -2
  257. data/test/models/api_client_test.rb +1 -0
  258. data/test/models/commit_checks_test.rb +2 -1
  259. data/test/models/commit_deployment_status_test.rb +2 -1
  260. data/test/models/commit_deployment_test.rb +4 -3
  261. data/test/models/commits_test.rb +72 -70
  262. data/test/models/delivery_test.rb +3 -2
  263. data/test/models/deploy_spec_test.rb +113 -109
  264. data/test/models/deploy_stats_test.rb +1 -0
  265. data/test/models/deploys_test.rb +65 -56
  266. data/test/models/duration_test.rb +1 -1
  267. data/test/models/github_hook_test.rb +1 -0
  268. data/test/models/hook_test.rb +7 -4
  269. data/test/models/membership_test.rb +1 -0
  270. data/test/models/merge_request_test.rb +23 -20
  271. data/test/models/release_statuses_test.rb +2 -1
  272. data/test/models/rollbacks_test.rb +4 -3
  273. data/test/models/shipit/check_run_test.rb +16 -15
  274. data/test/models/shipit/continuous_delivery_schedule_test.rb +109 -0
  275. data/test/models/shipit/deploy_spec/file_system_test.rb +54 -10
  276. data/test/models/shipit/pull_request_test.rb +9 -9
  277. data/test/models/shipit/repository_test.rb +3 -2
  278. data/test/models/shipit/review_stack_provisioning_queue_test.rb +2 -2
  279. data/test/models/shipit/stack_test.rb +30 -29
  280. data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +36 -34
  281. data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +28 -28
  282. data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +42 -42
  283. data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +33 -33
  284. data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +37 -37
  285. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +1 -1
  286. data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +44 -42
  287. data/test/models/shipit/webhooks/handlers_test.rb +1 -0
  288. data/test/models/status/group_test.rb +3 -2
  289. data/test/models/status/missing_test.rb +1 -0
  290. data/test/models/status_test.rb +2 -1
  291. data/test/models/task_definitions_test.rb +7 -6
  292. data/test/models/tasks_test.rb +5 -4
  293. data/test/models/team_test.rb +5 -4
  294. data/test/models/undeployed_commits_test.rb +10 -9
  295. data/test/models/users_test.rb +21 -20
  296. data/test/test_command_integration.rb +1 -1
  297. data/test/test_helper.rb +11 -9
  298. data/test/unit/anonymous_user_serializer_test.rb +1 -0
  299. data/test/unit/command_test.rb +10 -1
  300. data/test/unit/commands_test.rb +1 -0
  301. data/test/unit/commit_serializer_test.rb +1 -0
  302. data/test/unit/csv_serializer_test.rb +3 -2
  303. data/test/unit/deploy_commands_test.rb +33 -23
  304. data/test/unit/deploy_serializer_test.rb +1 -0
  305. data/test/unit/environment_variables_test.rb +2 -1
  306. data/test/unit/github_app_test.rb +10 -9
  307. data/test/unit/github_apps_test.rb +19 -18
  308. data/test/unit/github_url_helper_test.rb +1 -0
  309. data/test/unit/line_buffer_test.rb +1 -1
  310. data/test/unit/rollback_commands_test.rb +2 -1
  311. data/test/unit/shipit_helper_test.rb +1 -0
  312. data/test/unit/shipit_test.rb +47 -1
  313. data/test/unit/user_serializer_test.rb +1 -0
  314. data/test/unit/variable_definition_test.rb +4 -3
  315. metadata +53 -43
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class CommitDeployment < Record
4
5
  belongs_to :task
@@ -11,8 +12,8 @@ module Shipit
11
12
  def create_on_github!
12
13
  create_deployment_on_github!
13
14
  statuses.order(id: :asc).each(&:create_on_github!)
14
- rescue Octokit::NotFound, Octokit::Forbidden => error
15
- Rails.logger.warn("Got #{error.class.name} creating deployment or statuses: #{error.message}")
15
+ rescue Octokit::NotFound, Octokit::Forbidden => e
16
+ Rails.logger.warn("Got #{e.class.name} creating deployment or statuses: #{e.message}")
16
17
  # If no one can create the deployment we can only give up
17
18
  end
18
19
 
@@ -23,6 +24,7 @@ module Shipit
23
24
  create_deployment_on_github(stack.github_api)
24
25
  rescue Octokit::ClientError
25
26
  raise if Shipit.github(organization: stack.repository.owner).api == stack.github_api
27
+
26
28
  # If the deploy author didn't gave us the permission to create the deployment we falback the the main shipit
27
29
  # user.
28
30
  #
@@ -55,9 +57,9 @@ module Shipit
55
57
  shipit: {
56
58
  task_id: task.id,
57
59
  from_sha: task.since_commit.sha,
58
- to_sha: task.until_commit.sha,
59
- },
60
- }.to_json,
60
+ to_sha: task.until_commit.sha
61
+ }
62
+ }.to_json
61
63
  )
62
64
  end
63
65
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class CommitDeploymentStatus < Record
4
5
  DESCRIPTION_CHARACTER_LIMIT_ON_GITHUB = 140
@@ -11,10 +12,12 @@ module Shipit
11
12
 
12
13
  def create_on_github!
13
14
  return if github_id?
15
+
14
16
  response = begin
15
17
  create_status_on_github(stack.github_api)
16
18
  rescue Octokit::ClientError
17
19
  raise if Shipit.github(organization: stack.repository.owner).api == stack.github_api
20
+
18
21
  # If the deploy author didn't gave us the permission to create the deployment we falback the the main shipit
19
22
  # user.
20
23
  #
@@ -30,7 +33,7 @@ module Shipit
30
33
  "deployment_description.#{task_type}.#{status}",
31
34
  sha: task.until_commit.short_sha,
32
35
  author: task.author.login,
33
- stack: stack.to_param,
36
+ stack: stack.to_param
34
37
  )
35
38
  end
36
39
 
@@ -50,7 +53,7 @@ module Shipit
50
53
  status,
51
54
  target_url: url_helpers.stack_deploy_url(stack, task),
52
55
  description: description.truncate(DESCRIPTION_CHARACTER_LIMIT_ON_GITHUB),
53
- environment_url: stack.deploy_url,
56
+ environment_url: stack.deploy_url
54
57
  )
55
58
  end
56
59
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class CommitMessage
4
5
  GITHUB_MERGE_COMMIT_PATTERN = /\AMerge pull request #(?<pr_id>\d+) from \S+\n\n(?<pr_title>.*)/
@@ -27,6 +28,7 @@ module Shipit
27
28
 
28
29
  def parsed
29
30
  return @parsed if defined?(@parsed)
31
+
30
32
  @parsed = to_s.match(GITHUB_MERGE_COMMIT_PATTERN)
31
33
  end
32
34
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shipit
4
+ class ContinuousDeliverySchedule < Record
5
+ belongs_to(:stack)
6
+
7
+ DAYS = %w[sunday monday tuesday wednesday thursday friday saturday].freeze
8
+
9
+ validates(
10
+ *DAYS.map { |day| "#{day}_enabled" },
11
+ inclusion: [true, false]
12
+ )
13
+
14
+ validates(
15
+ *DAYS.product([:start, :end]).map { |parts| parts.join("_") },
16
+ presence: true
17
+ )
18
+
19
+ validate(:validate_time_windows)
20
+
21
+ DeploymentWindow = Struct.new(:starts_at, :ends_at, :enabled) do
22
+ alias_method :enabled?, :enabled
23
+ end
24
+
25
+ def can_deploy?(now = Time.current)
26
+ # Make sure time is in the default time zone so weekdays match what is
27
+ # stored in the database.
28
+ now = now.in_time_zone(Time.zone)
29
+
30
+ deployment_window = get_deployment_window(now.to_date)
31
+
32
+ deployment_window.enabled? &&
33
+ now >= deployment_window.starts_at &&
34
+ now <= deployment_window.ends_at
35
+ end
36
+
37
+ def get_deployment_window(date)
38
+ wday_name = DAYS.fetch(date.wday)
39
+
40
+ enabled = read_attribute("#{wday_name}_enabled")
41
+
42
+ starts_at, ends_at = [:start, :end].map do |bound|
43
+ raw_time = read_attribute("#{wday_name}_#{bound}")
44
+
45
+ # `ActiveRecord::Type::Time` attributes are stored as timestamps
46
+ # normalized to 2000-01-01 so they can't be used for comparisons without
47
+ # having their dates adjusted.
48
+ # https://github.com/rails/rails/blob/ec667e5f114df58087493096253541f1034815af/activemodel/lib/active_model/type/time.rb#L23
49
+ Time.zone.local(
50
+ date.year,
51
+ date.month,
52
+ date.day,
53
+ raw_time.hour,
54
+ raw_time.min
55
+ )
56
+ end
57
+
58
+ DeploymentWindow.new(
59
+ starts_at,
60
+ # Includes the full minute in the configured range. This is required so
61
+ # that a window configured to end at 17:59 actually ends at 17:59:59
62
+ # instead of 17:59:00.
63
+ ends_at.at_end_of_minute,
64
+ enabled
65
+ )
66
+ end
67
+
68
+ private
69
+
70
+ # Make sure every `*_end` attribute comes after its matching `*_start`
71
+ # attribute
72
+ def validate_time_windows
73
+ DAYS.each do |day|
74
+ day_start, day_end = [:start, :end].map { |bound| read_attribute("#{day}_#{bound}") }
75
+
76
+ next unless day_start && day_end
77
+
78
+ next if day_start <= day_end
79
+
80
+ errors.add("#{day}_end", :must_be_after_start, start: day_start.strftime("%I:%M %p"))
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class Delivery < Record
4
- STATUSES = %w(pending scheduled sent).freeze
5
- enum status: STATUSES.zip(STATUSES).to_h
5
+ STATUSES = %w[pending scheduled sent].freeze
6
+ enum :status, STATUSES.zip(STATUSES).to_h
6
7
 
7
8
  belongs_to :hook
8
9
 
@@ -47,7 +48,7 @@ module Shipit
47
48
  'Content-Type' => content_type,
48
49
  'X-Shipit-Event' => event,
49
50
  'X-Shipit-Delivery' => id.to_s,
50
- 'Accept' => '*/*',
51
+ 'Accept' => '*/*'
51
52
  }
52
53
  end
53
54
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'fileutils'
3
4
 
4
5
  module Shipit
@@ -26,7 +27,7 @@ module Shipit
26
27
  'success' => 'success',
27
28
  'faulty' => 'error',
28
29
  'error' => 'error',
29
- 'aborted' => 'error',
30
+ 'aborted' => 'error'
30
31
  }.freeze
31
32
 
32
33
  def append_status(task_status)
@@ -37,7 +38,7 @@ module Shipit
37
38
  "Creating #{github_status} deploy status for deployment #{deployment.id}. "\
38
39
  "Commit: #{deployment.sha}, Github id: #{deployment.github_id}, "\
39
40
  "Repo: #{deployment.stack.repo_name}, Environment: #{deployment.stack.environment}, "\
40
- "API Url: #{deployment.api_url}.",
41
+ "API Url: #{deployment.api_url}."
41
42
  )
42
43
  deployment.statuses.create!(status: github_status)
43
44
  end
@@ -47,7 +48,7 @@ module Shipit
47
48
  "No GitHub status for task status #{task_status}. "\
48
49
  "Commit: #{deployment.sha}, Github id: #{deployment.github_id}, "\
49
50
  "Repo: #{deployment.stack.repo_name}, Environment: #{deployment.stack.environment}, "\
50
- "API Url: #{deployment.api_url}.",
51
+ "API Url: #{deployment.api_url}."
51
52
  )
52
53
  end
53
54
  end
@@ -64,70 +65,75 @@ module Shipit
64
65
 
65
66
  def self.newer_than(deploy)
66
67
  return all unless deploy
68
+
67
69
  where('id > ?', deploy.try(:id) || deploy)
68
70
  end
69
71
 
70
72
  def self.older_than(deploy)
71
73
  return all unless deploy
74
+
72
75
  where('id < ?', deploy.try(:id) || deploy)
73
76
  end
74
77
 
75
78
  def self.since(deploy)
76
79
  return all unless deploy
80
+
77
81
  where('id >= ?', deploy.try(:id) || deploy)
78
82
  end
79
83
 
80
84
  def self.until(deploy)
81
85
  return all unless deploy
86
+
82
87
  where('id <= ?', deploy.try(:id) || deploy)
83
88
  end
84
89
 
85
90
  def build_rollback(user = nil, env: nil, force: false)
86
91
  Rollback.new(
87
92
  user_id: user&.id,
88
- stack_id: stack_id,
93
+ stack_id:,
89
94
  parent_id: id,
90
95
  since_commit: stack.last_deployed_commit,
91
- until_commit: until_commit,
92
- env: env&.to_h || {},
96
+ until_commit:,
97
+ env: env.to_h,
93
98
  allow_concurrency: force,
94
99
  ignored_safeties: force,
95
- max_retries: stack.retries_on_rollback,
100
+ max_retries: stack.retries_on_rollback
96
101
  )
97
102
  end
98
103
 
99
104
  # Rolls the stack back to this deploy
100
105
  def trigger_rollback(user = AnonymousUser.new, env: nil, force: false, lock: true)
101
- rollback = build_rollback(user, env: env, force: force)
106
+ rollback = build_rollback(user, env:, force:)
102
107
  rollback.save!
103
108
  rollback.enqueue
104
109
 
105
110
  if lock
106
111
  lock_reason = "A rollback for #{rollback.since_commit.sha} has been triggered. " \
107
112
  "Please make sure the reason for the rollback has been addressed before deploying again."
108
- stack.update!(lock_reason: lock_reason, lock_author_id: user.id)
113
+ stack.update!(lock_reason:, lock_author_id: user.id)
109
114
  end
110
115
 
111
116
  rollback
112
117
  end
113
118
 
114
119
  # Rolls the stack back to the most recent **previous** successful deploy
115
- def trigger_revert(force: false, rollback_to: nil)
120
+ def trigger_revert(force: false, rollback_to: nil, env: nil)
116
121
  previous_successful_commit = rollback_to&.until_commit || commit_to_rollback_to
117
122
 
118
123
  rollback = Rollback.create!(
119
- user_id: user_id,
120
- stack_id: stack_id,
124
+ user_id:,
125
+ stack_id:,
121
126
  parent_id: id,
122
127
  since_commit: until_commit,
123
128
  until_commit: previous_successful_commit,
124
- allow_concurrency: force,
129
+ env: env || self.env,
130
+ allow_concurrency: force
125
131
  )
126
132
 
127
133
  rollback.enqueue
128
134
  lock_reason = "A rollback for #{until_commit.sha} has been triggered. " \
129
135
  "Please make sure the reason for the rollback has been addressed before deploying again."
130
- stack.update!(lock_reason: lock_reason, lock_author_id: user_id)
136
+ stack.update!(lock_reason:, lock_author_id: user_id)
131
137
  stack.emit_lock_hooks
132
138
  rollback
133
139
  end
@@ -176,6 +182,7 @@ module Shipit
176
182
 
177
183
  def reject!
178
184
  return if failed? || aborted?
185
+
179
186
  transaction do
180
187
  flap! unless flapping?
181
188
  update!(confirmations: [confirmations - 1, -1].min)
@@ -185,6 +192,7 @@ module Shipit
185
192
 
186
193
  def accept!
187
194
  return if success?
195
+
188
196
  transaction do
189
197
  flap! unless flapping?
190
198
  update!(confirmations: [confirmations + 1, 1].max)
@@ -198,13 +206,12 @@ module Shipit
198
206
 
199
207
  delegate :last_release_status, to: :until_commit
200
208
  def append_release_status(state, description, user: self.user)
201
- status = until_commit.create_release_status!(
209
+ until_commit.create_release_status!(
202
210
  state,
203
211
  user: user.presence,
204
212
  target_url: permalink,
205
- description: description,
213
+ description:
206
214
  )
207
- status
208
215
  end
209
216
 
210
217
  def permalink
@@ -225,7 +232,7 @@ module Shipit
225
232
  append_release_status(
226
233
  'success',
227
234
  description,
228
- user: user,
235
+ user:
229
236
  )
230
237
  end
231
238
  end
@@ -236,7 +243,7 @@ module Shipit
236
243
  append_release_status(
237
244
  'failure',
238
245
  description,
239
- user: user,
246
+ user:
240
247
  )
241
248
  end
242
249
  end
@@ -254,6 +261,7 @@ module Shipit
254
261
  # Create one for each pull request in the batch, to give feedback on the PR timeline
255
262
  commits.select(&:pull_request?).each do |commit|
256
263
  next if commit.pull_request_head_sha.blank? # This attribute was not always populated
264
+
257
265
  commit_deployments.create!(sha: commit.pull_request_head_sha)
258
266
  end
259
267
 
@@ -272,13 +280,17 @@ module Shipit
272
280
  when 'aborted', 'aborting'
273
281
  append_release_status('failure', "The deploy on #{stack.environment} was canceled")
274
282
  when 'validating'
275
- append_release_status(
276
- 'pending',
277
- "The deploy on #{stack.environment} succeeded"
278
- ) unless stack.release_status_delay.zero?
283
+ unless stack.release_status_delay.zero?
284
+ append_release_status(
285
+ 'pending',
286
+ "The deploy on #{stack.environment} succeeded"
287
+ )
288
+ end
279
289
 
280
- MarkDeployHealthyJob.set(wait: stack.release_status_delay)
281
- .perform_later(self) if stack.release_status_delay.positive?
290
+ if stack.release_status_delay.positive?
291
+ MarkDeployHealthyJob.set(wait: stack.release_status_delay)
292
+ .perform_later(self)
293
+ end
282
294
  when 'success'
283
295
  if stack.release_status_delay.zero?
284
296
  append_release_status('success', "The deploy on #{stack.environment} succeeded")
@@ -289,11 +301,17 @@ module Shipit
289
301
  def trigger_revert_if_required
290
302
  return unless rollback_once_aborted?
291
303
  return unless supports_rollback?
292
- trigger_revert(rollback_to: rollback_once_aborted_to)
304
+
305
+ if rollback_once_aborted_to
306
+ rollback_once_aborted_to.trigger_rollback(aborted_by, env:, force: true)
307
+ else
308
+ trigger_revert(force: true, env:)
309
+ end
293
310
  end
294
311
 
295
312
  def default_since_commit_id
296
313
  return unless stack
314
+
297
315
  @default_since_commit_id ||= stack.last_completed_deploy&.until_commit_id
298
316
  end
299
317
 
@@ -308,6 +326,7 @@ module Shipit
308
326
 
309
327
  def schedule_continuous_delivery
310
328
  return unless stack.continuous_deployment?
329
+
311
330
  ContinuousDeliveryJob.perform_later(stack)
312
331
  end
313
332
 
@@ -321,6 +340,7 @@ module Shipit
321
340
 
322
341
  def update_latest_deployed_ref
323
342
  return unless previous_changes.include?(:status)
343
+
324
344
  stack.update_latest_deployed_ref if previous_changes[:status].last == 'success'
325
345
  end
326
346
  end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class DeploySpec
4
5
  module BundlerDiscovery
5
- DEFAULT_BUNDLER_WITHOUT = %w(default production development test staging benchmark debug).freeze
6
+ DEFAULT_BUNDLER_WITHOUT = %w[default production development test staging benchmark debug].freeze
6
7
 
7
8
  def discover_dependencies_steps
8
9
  discover_bundler || super
@@ -43,6 +44,7 @@ module Shipit
43
44
  def frozen_flag
44
45
  return unless gemfile_lock_exists?
45
46
  return if config('dependencies', 'bundler', 'frozen') == false
47
+
46
48
  '--frozen'
47
49
  end
48
50
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class DeploySpec
4
5
  module CapistranoDiscovery
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class DeploySpec
4
5
  class FileSystem < DeploySpec
@@ -10,9 +11,12 @@ module Shipit
10
11
  include BundlerDiscovery
11
12
  include KubernetesDiscovery
12
13
 
13
- def initialize(app_dir, env)
14
+ attr_reader :stack
15
+
16
+ def initialize(app_dir, stack)
14
17
  @app_dir = Pathname(app_dir)
15
- @env = env
18
+ @env = stack.environment
19
+ @stack = stack
16
20
  super(nil)
17
21
  end
18
22
 
@@ -39,29 +43,29 @@ module Shipit
39
43
  'method' => merge_request_merge_method,
40
44
  'max_divergence' => {
41
45
  'commits' => max_divergence_commits&.to_i,
42
- 'age' => max_divergence_age&.to_i,
43
- },
46
+ 'age' => max_divergence_age&.to_i
47
+ }
44
48
  },
45
49
  'ci' => {
46
50
  'hide' => hidden_statuses,
47
51
  'allow_failures' => soft_failing_statuses,
48
52
  'require' => required_statuses,
49
- 'blocking' => blocking_statuses,
53
+ 'blocking' => blocking_statuses
50
54
  },
51
55
  'machine' => {
52
56
  'environment' => discover_machine_env.merge(machine_env),
53
57
  'directory' => directory,
54
- 'cleanup' => true,
58
+ 'cleanup' => true
55
59
  },
56
60
  'review' => {
57
61
  'checklist' => review_checklist,
58
62
  'monitoring' => review_monitoring,
59
- 'checks' => review_checks,
63
+ 'checks' => review_checks
60
64
  },
61
65
  'plugins' => plugins,
62
66
  'status' => {
63
67
  'context' => release_status_context,
64
- 'delay' => release_status_delay,
68
+ 'delay' => release_status_delay
65
69
  },
66
70
  'dependencies' => { 'override' => dependencies_steps },
67
71
  'provision' => { 'handler_name' => provisioning_handler_name },
@@ -70,19 +74,19 @@ module Shipit
70
74
  'variables' => deploy_variables.map(&:to_h),
71
75
  'max_commits' => maximum_commits_per_deploy,
72
76
  'interval' => pause_between_deploys,
73
- 'retries' => retries_on_deploy,
77
+ 'retries' => retries_on_deploy
74
78
  },
75
79
  'rollback' => {
76
80
  'override' => rollback_steps,
77
- 'retries' => retries_on_rollback,
81
+ 'retries' => retries_on_rollback
78
82
  },
79
83
  'fetch' => fetch_deployed_revision_steps,
80
- 'tasks' => cacheable_tasks,
84
+ 'tasks' => cacheable_tasks
81
85
  )
82
86
  end
83
87
 
84
88
  def cacheable_tasks
85
- discover_task_definitions.map { |k, c| [k, coerce_task_definition(c)] }.to_h
89
+ discover_task_definitions.transform_values { |c| coerce_task_definition(c) }
86
90
  end
87
91
 
88
92
  def config(*)
@@ -97,7 +101,8 @@ module Shipit
97
101
  return { 'deploy' => { 'pre' => [shipit_not_obeying_bare_file_echo_command, 'exit 1'] } }
98
102
  end
99
103
 
100
- read_config(config_file_path)
104
+ config_obj = read_config(config_file_path)
105
+ build_config(config_file_path, config_obj)
101
106
  end
102
107
 
103
108
  def shipit_file_names_in_priority_order
@@ -112,7 +117,7 @@ module Shipit
112
117
  ".shipit/#{@env}.yml",
113
118
 
114
119
  "shipit.yml",
115
- ".shipit/shipit.yml",
120
+ ".shipit/shipit.yml"
116
121
  ].uniq
117
122
  end
118
123
 
@@ -133,16 +138,30 @@ module Shipit
133
138
  @app_name ||= Shipit.app_name.downcase
134
139
  end
135
140
 
141
+ SHIPIT_CONFIG_INHERIT_FROM_KEY = "inherit_from"
142
+ def build_config(path, config_obj)
143
+ return config_obj if config_obj.blank? || !config_obj.key?(SHIPIT_CONFIG_INHERIT_FROM_KEY)
144
+
145
+ inherits_from_path = path.dirname.join(config_obj.delete(SHIPIT_CONFIG_INHERIT_FROM_KEY))
146
+ if inherits_from_path.exist?
147
+ inherits_config_obj = read_config(inherits_from_path)
148
+ config_obj = inherits_config_obj.deep_merge(config_obj)
149
+ path = inherits_from_path
150
+ end
151
+
152
+ build_config(path, config_obj)
153
+ end
154
+
136
155
  def read_config(path)
137
156
  SafeYAML.load(path.read) if path.exist?
138
157
  end
139
158
 
140
159
  def shipit_not_obeying_bare_file_echo_command
141
- <<~EOM
160
+ <<~WARNING_MESSAGE
142
161
  echo \"\e[1;31mShipit is configured to ignore the bare '#{app_name}.yml' file.
143
162
  Please rename this file to more specifically include the environment name.
144
163
  Deployments will fail until a valid '#{app_name}.#{@env}.yml' file is found.\e[0m\"
145
- EOM
164
+ WARNING_MESSAGE
146
165
  end
147
166
  end
148
167
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shipit
3
4
  class DeploySpec
4
5
  module KubernetesDiscovery
@@ -16,8 +17,8 @@ module Shipit
16
17
  'restart' => {
17
18
  'action' => "Restart application",
18
19
  'description' => "Simulates a rollout of Kubernetes deployments by using kubernetes-restart utility",
19
- 'steps' => [kubernetes_restart_cmd],
20
- },
20
+ 'steps' => [kubernetes_restart_cmd]
21
+ }
21
22
  }.merge!(super)
22
23
  else
23
24
  super
@@ -55,7 +56,7 @@ module Shipit
55
56
  cmd = [
56
57
  "kubernetes-restart",
57
58
  kube_config.fetch('namespace'),
58
- kube_config.fetch('context'),
59
+ kube_config.fetch('context')
59
60
  ]
60
61
  cmd += ["--max-watch-seconds", timeout_duration] if timeout_duration
61
62
  Shellwords.join(cmd)