shipit-engine 0.28.0 → 0.32.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 (314) 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/stylesheets/_pages/_stacks.scss +76 -3
  6. data/app/assets/stylesheets/_structure/_main.scss +2 -1
  7. data/app/assets/stylesheets/merge_status.scss +0 -3
  8. data/app/controllers/concerns/shipit/active_model_serializers_patch.rb +13 -0
  9. data/app/controllers/concerns/shipit/api/cacheable.rb +1 -0
  10. data/app/controllers/concerns/shipit/api/paginable.rb +3 -2
  11. data/app/controllers/concerns/shipit/api/rendering.rb +5 -4
  12. data/app/controllers/concerns/shipit/authentication.rb +3 -2
  13. data/app/controllers/concerns/shipit/pagination.rb +2 -1
  14. data/app/controllers/shipit/api/base_controller.rb +11 -6
  15. data/app/controllers/shipit/api/ccmenu_controller.rb +2 -1
  16. data/app/controllers/shipit/api/commits_controller.rb +2 -1
  17. data/app/controllers/shipit/api/deploys_controller.rb +4 -3
  18. data/app/controllers/shipit/api/hooks_controller.rb +6 -5
  19. data/app/controllers/shipit/api/locks_controller.rb +5 -4
  20. data/app/controllers/shipit/api/outputs_controller.rb +2 -1
  21. data/app/controllers/shipit/api/pull_requests_controller.rb +7 -6
  22. data/app/controllers/shipit/api/release_statuses_controller.rb +3 -2
  23. data/app/controllers/shipit/api/rollbacks_controller.rb +33 -0
  24. data/app/controllers/shipit/api/stacks_controller.rb +37 -5
  25. data/app/controllers/shipit/api/tasks_controller.rb +6 -5
  26. data/app/controllers/shipit/api_clients_controller.rb +50 -0
  27. data/app/controllers/shipit/ccmenu_url_controller.rb +4 -3
  28. data/app/controllers/shipit/commit_checks_controller.rb +2 -1
  29. data/app/controllers/shipit/commits_controller.rb +2 -1
  30. data/app/controllers/shipit/deploys_controller.rb +3 -2
  31. data/app/controllers/shipit/github_authentication_controller.rb +4 -3
  32. data/app/controllers/shipit/merge_status_controller.rb +19 -14
  33. data/app/controllers/shipit/pull_requests_controller.rb +3 -2
  34. data/app/controllers/shipit/release_statuses_controller.rb +3 -2
  35. data/app/controllers/shipit/rollbacks_controller.rb +3 -2
  36. data/app/controllers/shipit/shipit_controller.rb +2 -1
  37. data/app/controllers/shipit/stacks_controller.rb +78 -14
  38. data/app/controllers/shipit/status_controller.rb +2 -1
  39. data/app/controllers/shipit/tasks_controller.rb +6 -5
  40. data/app/controllers/shipit/webhooks_controller.rb +5 -132
  41. data/app/helpers/shipit/chunks_helper.rb +1 -0
  42. data/app/helpers/shipit/deploys_helper.rb +4 -3
  43. data/app/helpers/shipit/github_url_helper.rb +1 -0
  44. data/app/helpers/shipit/merge_status_helper.rb +1 -0
  45. data/app/helpers/shipit/shipit_helper.rb +1 -0
  46. data/app/helpers/shipit/stacks_helper.rb +5 -0
  47. data/app/helpers/shipit/tasks_helper.rb +1 -0
  48. data/app/jobs/shipit/background_job.rb +4 -0
  49. data/app/jobs/shipit/background_job/unique.rb +4 -1
  50. data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -0
  51. data/app/jobs/shipit/chunk_rollup_job.rb +4 -0
  52. data/app/jobs/shipit/clear_git_cache_job.rb +1 -0
  53. data/app/jobs/shipit/continuous_delivery_job.rb +3 -1
  54. data/app/jobs/shipit/create_on_github_job.rb +6 -1
  55. data/app/jobs/shipit/create_release_statuses_job.rb +1 -0
  56. data/app/jobs/shipit/deferred_touch_job.rb +4 -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_stack_job.rb +3 -2
  60. data/app/jobs/shipit/emit_event_job.rb +2 -1
  61. data/app/jobs/shipit/fetch_commit_stats_job.rb +1 -0
  62. data/app/jobs/shipit/fetch_deployed_revision_job.rb +2 -1
  63. data/app/jobs/shipit/github_sync_job.rb +3 -2
  64. data/app/jobs/shipit/{mark_deploy_healty_job.rb → mark_deploy_healthy_job.rb} +1 -0
  65. data/app/jobs/shipit/merge_pull_requests_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 +14 -5
  68. data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
  69. data/app/jobs/shipit/reap_dead_tasks_job.rb +21 -0
  70. data/app/jobs/shipit/refresh_check_runs_job.rb +1 -0
  71. data/app/jobs/shipit/refresh_github_user_job.rb +1 -0
  72. data/app/jobs/shipit/refresh_pull_request_job.rb +1 -0
  73. data/app/jobs/shipit/refresh_statuses_job.rb +1 -0
  74. data/app/jobs/shipit/setup_github_hook_job.rb +1 -0
  75. data/app/jobs/shipit/update_estimated_deploy_duration_job.rb +1 -0
  76. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +6 -3
  77. data/app/models/concerns/shipit/deferred_touch.rb +4 -3
  78. data/app/models/shipit/anonymous_user.rb +5 -0
  79. data/app/models/shipit/api_client.rb +3 -2
  80. data/app/models/shipit/application_record.rb +2 -1
  81. data/app/models/shipit/check_run.rb +4 -3
  82. data/app/models/shipit/command_line_user.rb +1 -0
  83. data/app/models/shipit/commit.rb +31 -12
  84. data/app/models/shipit/commit_checks.rb +1 -0
  85. data/app/models/shipit/commit_deployment.rb +17 -12
  86. data/app/models/shipit/commit_deployment_status.rb +8 -3
  87. data/app/models/shipit/commit_message.rb +1 -0
  88. data/app/models/shipit/delivery.rb +4 -3
  89. data/app/models/shipit/deploy.rb +40 -10
  90. data/app/models/shipit/deploy_spec.rb +22 -3
  91. data/app/models/shipit/deploy_spec/bundler_discovery.rb +2 -1
  92. data/app/models/shipit/deploy_spec/capistrano_discovery.rb +1 -0
  93. data/app/models/shipit/deploy_spec/file_system.rb +10 -3
  94. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -0
  95. data/app/models/shipit/deploy_spec/lerna_discovery.rb +1 -0
  96. data/app/models/shipit/deploy_spec/npm_discovery.rb +5 -4
  97. data/app/models/shipit/deploy_spec/pypi_discovery.rb +1 -0
  98. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -0
  99. data/app/models/shipit/deploy_stats.rb +58 -0
  100. data/app/models/shipit/duration.rb +3 -2
  101. data/app/models/shipit/ephemeral_commit_checks.rb +1 -0
  102. data/app/models/shipit/github_hook.rb +2 -1
  103. data/app/models/shipit/github_status.rb +3 -2
  104. data/app/models/shipit/hook.rb +6 -5
  105. data/app/models/shipit/membership.rb +3 -2
  106. data/app/models/shipit/output_chunk.rb +7 -2
  107. data/app/models/shipit/pull_request.rb +13 -7
  108. data/app/models/shipit/record.rb +18 -0
  109. data/app/models/shipit/release_status.rb +3 -2
  110. data/app/models/shipit/repository.rb +43 -0
  111. data/app/models/shipit/rollback.rb +1 -0
  112. data/app/models/shipit/stack.rb +109 -50
  113. data/app/models/shipit/status.rb +3 -2
  114. data/app/models/shipit/status/common.rb +7 -6
  115. data/app/models/shipit/status/group.rb +1 -0
  116. data/app/models/shipit/status/missing.rb +2 -1
  117. data/app/models/shipit/status/unknown.rb +2 -1
  118. data/app/models/shipit/task.rb +64 -9
  119. data/app/models/shipit/task_definition.rb +1 -0
  120. data/app/models/shipit/team.rb +2 -1
  121. data/app/models/shipit/undeployed_commit.rb +1 -0
  122. data/app/models/shipit/unlimited_api_client.rb +1 -0
  123. data/app/models/shipit/user.rb +29 -5
  124. data/app/models/shipit/variable_definition.rb +1 -0
  125. data/app/models/shipit/webhooks.rb +33 -0
  126. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +20 -0
  127. data/app/models/shipit/webhooks/handlers/handler.rb +41 -0
  128. data/app/models/shipit/webhooks/handlers/membership_handler.rb +46 -0
  129. data/app/models/shipit/webhooks/handlers/push_handler.rb +21 -0
  130. data/app/models/shipit/webhooks/handlers/status_handler.rb +27 -0
  131. data/app/serializers/concerns/shipit/conditional_attributes.rb +1 -0
  132. data/app/serializers/shipit/anonymous_user_serializer.rb +1 -0
  133. data/app/serializers/shipit/command_line_user_serializer.rb +1 -0
  134. data/app/serializers/shipit/commit_serializer.rb +1 -0
  135. data/app/serializers/shipit/deploy_serializer.rb +2 -1
  136. data/app/serializers/shipit/hook_serializer.rb +1 -0
  137. data/app/serializers/shipit/pull_request_serializer.rb +1 -0
  138. data/app/serializers/shipit/rollback_serializer.rb +1 -0
  139. data/app/serializers/shipit/short_commit_serializer.rb +1 -0
  140. data/app/serializers/shipit/stack_serializer.rb +7 -1
  141. data/app/serializers/shipit/tail_task_serializer.rb +1 -0
  142. data/app/serializers/shipit/task_serializer.rb +2 -17
  143. data/app/serializers/shipit/user_serializer.rb +6 -1
  144. data/app/validators/ascii_only_validator.rb +4 -3
  145. data/app/validators/subset_validator.rb +1 -0
  146. data/app/views/layouts/_head.html.erb +0 -0
  147. data/app/views/layouts/shipit.html.erb +6 -4
  148. data/app/views/shipit/_variables.html.erb +1 -1
  149. data/app/views/shipit/api_clients/index.html.erb +36 -0
  150. data/app/views/shipit/api_clients/new.html.erb +33 -0
  151. data/app/views/shipit/api_clients/show.html.erb +35 -0
  152. data/app/views/shipit/ccmenu/project.xml.builder +2 -1
  153. data/app/views/shipit/deploys/new.html.erb +17 -12
  154. data/app/views/shipit/deploys/show.html.erb +2 -2
  155. data/app/views/shipit/merge_status/logged_out.erb +1 -1
  156. data/app/views/shipit/stacks/_header.html.erb +27 -12
  157. data/app/views/shipit/stacks/_links.html.erb +1 -0
  158. data/app/views/shipit/stacks/all_tasks.html.erb +28 -0
  159. data/app/views/shipit/stacks/index.html.erb +7 -2
  160. data/app/views/shipit/stacks/settings.html.erb +19 -0
  161. data/app/views/shipit/stacks/statistics.html.erb +82 -0
  162. data/app/views/shipit/tasks/show.html.erb +1 -1
  163. data/config/initializers/inflections.rb +2 -1
  164. data/config/locales/en.yml +18 -5
  165. data/config/routes.rb +14 -2
  166. data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
  167. data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
  168. data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
  169. data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
  170. data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
  171. data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
  172. data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
  173. data/db/migrate/20200226211925_add_index_to_tasks_status.rb +5 -0
  174. data/db/migrate/20200427135152_add_pull_request_head_sha_to_commit.rb +5 -0
  175. data/db/migrate/20200615181558_add_rollback_once_aborted_to.rb +5 -0
  176. data/lib/shipit.rb +18 -20
  177. data/lib/shipit/cast_value.rb +1 -0
  178. data/lib/shipit/command.rb +14 -18
  179. data/lib/shipit/commands.rb +5 -4
  180. data/lib/shipit/csv_serializer.rb +1 -0
  181. data/lib/shipit/deploy_commands.rb +1 -0
  182. data/lib/shipit/engine.rb +11 -2
  183. data/lib/shipit/environment_variables.rb +11 -1
  184. data/lib/shipit/first_parent_commits_iterator.rb +1 -0
  185. data/lib/shipit/flock.rb +1 -0
  186. data/lib/shipit/github_app.rb +60 -6
  187. data/lib/shipit/github_http_cache_middleware.rb +57 -0
  188. data/lib/shipit/null_serializer.rb +1 -0
  189. data/lib/shipit/octokit_check_runs.rb +3 -2
  190. data/lib/shipit/octokit_iterator.rb +3 -2
  191. data/lib/shipit/paginator.rb +3 -2
  192. data/lib/shipit/rollback_commands.rb +1 -0
  193. data/lib/shipit/same_site_cookie_middleware.rb +29 -0
  194. data/lib/shipit/simple_message_verifier.rb +1 -0
  195. data/lib/shipit/stack_commands.rb +6 -3
  196. data/lib/shipit/stat.rb +1 -0
  197. data/lib/shipit/task_commands.rb +22 -14
  198. data/lib/shipit/version.rb +2 -1
  199. data/lib/snippets/release-gem +5 -1
  200. data/lib/tasks/cron.rake +2 -0
  201. data/lib/tasks/dev.rake +3 -2
  202. data/lib/tasks/shipit.rake +16 -17
  203. data/lib/tasks/teams.rake +1 -0
  204. data/test/controllers/api/base_controller_test.rb +3 -2
  205. data/test/controllers/api/ccmenu_controller_test.rb +9 -8
  206. data/test/controllers/api/commits_controller_test.rb +3 -2
  207. data/test/controllers/api/deploys_controller_test.rb +15 -14
  208. data/test/controllers/api/hooks_controller_test.rb +8 -7
  209. data/test/controllers/api/locks_controller_test.rb +7 -6
  210. data/test/controllers/api/outputs_controller_test.rb +3 -2
  211. data/test/controllers/api/pull_requests_controller_test.rb +8 -7
  212. data/test/controllers/api/release_statuses_controller_test.rb +2 -1
  213. data/test/controllers/api/rollback_controller_test.rb +113 -0
  214. data/test/controllers/api/stacks_controller_test.rb +44 -15
  215. data/test/controllers/api/tasks_controller_test.rb +13 -12
  216. data/test/controllers/api_clients_controller_test.rb +104 -0
  217. data/test/controllers/ccmenu_controller_test.rb +4 -3
  218. data/test/controllers/commit_checks_controller_test.rb +4 -3
  219. data/test/controllers/commits_controller_test.rb +3 -2
  220. data/test/controllers/deploys_controller_test.rb +33 -22
  221. data/test/controllers/github_authentication_controller_test.rb +1 -0
  222. data/test/controllers/merge_status_controller_test.rb +27 -9
  223. data/test/controllers/pull_requests_controller_test.rb +4 -3
  224. data/test/controllers/release_statuses_controller_test.rb +3 -2
  225. data/test/controllers/rollbacks_controller_test.rb +9 -8
  226. data/test/controllers/stacks_controller_test.rb +64 -15
  227. data/test/controllers/status_controller_test.rb +1 -0
  228. data/test/controllers/tasks_controller_test.rb +20 -19
  229. data/test/controllers/webhooks_controller_test.rb +36 -9
  230. data/test/dummy/config/application.rb +1 -1
  231. data/test/dummy/config/environments/development.rb +24 -4
  232. data/test/dummy/config/environments/test.rb +2 -0
  233. data/test/dummy/db/schema.rb +25 -11
  234. data/test/dummy/db/seeds.rb +34 -17
  235. data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
  236. data/test/fixtures/shipit/commit_deployments.yml +8 -8
  237. data/test/fixtures/shipit/commits.yml +38 -0
  238. data/test/fixtures/shipit/repositories.yml +27 -0
  239. data/test/fixtures/shipit/stacks.yml +190 -30
  240. data/test/fixtures/shipit/tasks.yml +66 -3
  241. data/test/fixtures/timeout +2 -1
  242. data/test/helpers/api_helper.rb +1 -0
  243. data/test/helpers/fixture_aliases_helper.rb +1 -0
  244. data/test/helpers/hooks_helper.rb +2 -1
  245. data/test/helpers/json_helper.rb +15 -11
  246. data/test/helpers/links_helper.rb +4 -3
  247. data/test/helpers/payloads_helper.rb +1 -0
  248. data/test/helpers/queries_helper.rb +3 -2
  249. data/test/jobs/cache_deploy_spec_job_test.rb +2 -1
  250. data/test/jobs/chunk_rollup_job_test.rb +1 -0
  251. data/test/jobs/deliver_hook_job_test.rb +2 -1
  252. data/test/jobs/destroy_stack_job_test.rb +10 -0
  253. data/test/jobs/emit_event_job_test.rb +2 -1
  254. data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
  255. data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
  256. data/test/jobs/github_sync_job_test.rb +1 -0
  257. data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
  258. data/test/jobs/merge_pull_requests_job_test.rb +5 -4
  259. data/test/jobs/perform_task_job_test.rb +4 -3
  260. data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
  261. data/test/jobs/reap_dead_tasks_job_test.rb +68 -0
  262. data/test/jobs/refresh_github_user_job_test.rb +1 -0
  263. data/test/jobs/refresh_status_job_test.rb +1 -0
  264. data/test/jobs/unique_job_test.rb +1 -0
  265. data/test/jobs/update_github_last_deployed_ref_job_test.rb +13 -11
  266. data/test/middleware/same_site_cookie_middleware_test.rb +52 -0
  267. data/test/models/api_client_test.rb +1 -0
  268. data/test/models/commit_checks_test.rb +1 -0
  269. data/test/models/commit_deployment_status_test.rb +34 -4
  270. data/test/models/commit_deployment_test.rb +9 -11
  271. data/test/models/commits_test.rb +99 -7
  272. data/test/models/delivery_test.rb +3 -2
  273. data/test/models/deploy_spec_test.rb +47 -42
  274. data/test/models/deploy_stats_test.rb +113 -0
  275. data/test/models/deploys_test.rb +60 -13
  276. data/test/models/duration_test.rb +1 -0
  277. data/test/models/github_hook_test.rb +1 -0
  278. data/test/models/hook_test.rb +20 -16
  279. data/test/models/membership_test.rb +1 -0
  280. data/test/models/output_chunk_test.rb +1 -0
  281. data/test/models/pull_request_test.rb +18 -11
  282. data/test/models/release_statuses_test.rb +1 -0
  283. data/test/models/rollbacks_test.rb +1 -0
  284. data/test/models/shipit/check_run_test.rb +1 -0
  285. data/test/models/shipit/repository_test.rb +77 -0
  286. data/test/models/shipit/wehbooks/handlers_test.rb +27 -0
  287. data/test/models/stacks_test.rb +110 -56
  288. data/test/models/status/group_test.rb +1 -0
  289. data/test/models/status/missing_test.rb +1 -0
  290. data/test/models/status_test.rb +1 -0
  291. data/test/models/task_definitions_test.rb +9 -8
  292. data/test/models/tasks_test.rb +18 -1
  293. data/test/models/team_test.rb +4 -2
  294. data/test/models/undeployed_commits_test.rb +14 -0
  295. data/test/models/users_test.rb +109 -1
  296. data/test/test_command_integration.rb +3 -2
  297. data/test/test_helper.rb +38 -34
  298. data/test/unit/anonymous_user_serializer_test.rb +14 -0
  299. data/test/unit/command_test.rb +12 -7
  300. data/test/unit/commands_test.rb +1 -0
  301. data/test/unit/commit_serializer_test.rb +16 -0
  302. data/test/unit/csv_serializer_test.rb +3 -2
  303. data/test/unit/deploy_commands_test.rb +14 -4
  304. data/test/unit/deploy_serializer_test.rb +17 -0
  305. data/test/unit/environment_variables_test.rb +5 -4
  306. data/test/unit/github_app_test.rb +165 -0
  307. data/test/unit/github_url_helper_test.rb +1 -0
  308. data/test/unit/rollback_commands_test.rb +2 -1
  309. data/test/unit/shipit_helper_test.rb +17 -0
  310. data/test/unit/shipit_test.rb +1 -0
  311. data/test/unit/user_serializer_test.rb +14 -0
  312. data/test/unit/variable_definition_test.rb +1 -0
  313. metadata +215 -157
  314. data/lib/shipit/strip_cache_control.rb +0 -40
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
- class Status < ActiveRecord::Base
3
+ class Status < Record
3
4
  include Common
4
5
  include DeferredTouch
5
6
 
@@ -11,7 +12,7 @@ module Shipit
11
12
 
12
13
  deferred_touch commit: :updated_at
13
14
 
14
- validates :state, inclusion: {in: STATES, allow_blank: true}, presence: true
15
+ validates :state, inclusion: { in: STATES, allow_blank: true }, presence: true
15
16
 
16
17
  after_create :enable_ci_on_stack
17
18
  after_commit :schedule_continuous_delivery, :broadcast_update, on: :create
@@ -1,24 +1,25 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Status
3
4
  module Common
4
5
  def unknown?
5
- state == 'unknown'.freeze
6
+ state == 'unknown'
6
7
  end
7
8
 
8
9
  def pending?
9
- state == 'pending'.freeze
10
+ state == 'pending'
10
11
  end
11
12
 
12
13
  def success?
13
- state == 'success'.freeze
14
+ state == 'success'
14
15
  end
15
16
 
16
17
  def error?
17
- state == 'error'.freeze
18
+ state == 'error'
18
19
  end
19
20
 
20
21
  def failure?
21
- state == 'failure'.freeze
22
+ state == 'failure'
22
23
  end
23
24
 
24
25
  def missing?
@@ -30,7 +31,7 @@ module Shipit
30
31
  end
31
32
 
32
33
  def simple_state
33
- state == 'error'.freeze ? 'failure'.freeze : state
34
+ state == 'error' ? 'failure' : state
34
35
  end
35
36
 
36
37
  def allowed_to_fail?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Status
3
4
  class Group
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Status
3
4
  class Missing
@@ -15,7 +16,7 @@ module Shipit
15
16
  end
16
17
 
17
18
  def state
18
- 'pending'.freeze
19
+ 'pending'
19
20
  end
20
21
 
21
22
  def missing?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Status
3
4
  class Unknown
@@ -21,7 +22,7 @@ module Shipit
21
22
  end
22
23
 
23
24
  def state
24
- 'unknown'.freeze
25
+ 'unknown'
25
26
  end
26
27
 
27
28
  def missing?
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
- class Task < ActiveRecord::Base
3
+ class Task < Record
3
4
  include DeferredTouch
4
5
 
5
6
  ConcurrentTaskRunning = Class.new(StandardError)
6
7
 
7
- PRESENCE_CHECK_TIMEOUT = 15
8
+ PRESENCE_CHECK_TIMEOUT = 30
8
9
  ACTIVE_STATUSES = %w(pending running aborting).freeze
9
10
  COMPLETED_STATUSES = %w(success flapping faulty validating).freeze
10
11
  UNSUCCESSFUL_STATUSES = %w(error failed aborted flapping timedout faulty).freeze
12
+ OUTPUT_SIZE_LIMIT = 16.megabytes # A MySQL mediumblob
13
+ HUMAN_READABLE_OUTPUT_LIMIT = ActionController::Base.helpers.number_to_human_size(OUTPUT_SIZE_LIMIT)
14
+ OUTPUT_TRUNCATED_MESSAGE = "Output exceeded the limit of #{HUMAN_READABLE_OUTPUT_LIMIT} and was truncated\n"
11
15
 
12
16
  attr_accessor :pid
13
17
 
@@ -16,8 +20,10 @@ module Shipit
16
20
  belongs_to :user, optional: true
17
21
  belongs_to :aborted_by, class_name: 'User', optional: true
18
22
  belongs_to :stack, counter_cache: true
19
- belongs_to :until_commit, class_name: 'Commit'
20
- belongs_to :since_commit, class_name: 'Commit'
23
+ belongs_to :until_commit, class_name: 'Commit', required: false
24
+ belongs_to :since_commit, class_name: 'Commit', required: false
25
+
26
+ belongs_to :rollback_once_aborted_to, class_name: 'Task', optional: true
21
27
 
22
28
  deferred_touch stack: :updated_at
23
29
 
@@ -29,14 +35,18 @@ module Shipit
29
35
  scope :success, -> { where(status: 'success') }
30
36
  scope :completed, -> { where(status: COMPLETED_STATUSES) }
31
37
  scope :active, -> { where(status: ACTIVE_STATUSES) }
38
+ scope :not_active, -> { where(status: COMPLETED_STATUSES + UNSUCCESSFUL_STATUSES) }
32
39
  scope :exclusive, -> { where(allow_concurrency: false) }
33
40
  scope :unsuccessful, -> { where(status: UNSUCCESSFUL_STATUSES) }
41
+ scope :last_seven_days, -> { where("created_at > ?", 7.days.ago) }
42
+ scope :previous_seven_days, -> { where(created_at: 14.days.ago..7.days.ago) }
34
43
 
35
44
  scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
36
45
 
37
46
  after_save :record_status_change
38
47
  after_create :prevent_concurrency, unless: :allow_concurrency?
39
- after_commit :emit_hooks
48
+ after_commit :emit_hooks, on: :create
49
+ after_commit :emit_hooks_if_status_changed, on: :update
40
50
 
41
51
  class << self
42
52
  def durations
@@ -194,7 +204,15 @@ module Shipit
194
204
  if rolled_up?
195
205
  output
196
206
  else
197
- chunks.pluck(:text).join
207
+ blob = chunks.pluck(:text).join
208
+
209
+ if blob.size > OUTPUT_SIZE_LIMIT
210
+ Rails.logger.warn("Task #{id} output exceeds limit of #{HUMAN_READABLE_OUTPUT_LIMIT}, and will be truncated.")
211
+ blob = blob.last(OUTPUT_SIZE_LIMIT - OUTPUT_TRUNCATED_MESSAGE.size)
212
+ blob = OUTPUT_TRUNCATED_MESSAGE + blob
213
+ end
214
+
215
+ blob
198
216
  end
199
217
  end
200
218
 
@@ -276,8 +294,12 @@ module Shipit
276
294
  end
277
295
  end
278
296
 
279
- def abort!(rollback_once_aborted: false, aborted_by:)
280
- update!(rollback_once_aborted: rollback_once_aborted, aborted_by_id: aborted_by.id)
297
+ def abort!(rollback_once_aborted: false, rollback_once_aborted_to: nil, aborted_by:)
298
+ update!(
299
+ rollback_once_aborted: rollback_once_aborted,
300
+ rollback_once_aborted_to: rollback_once_aborted_to,
301
+ aborted_by_id: aborted_by.id
302
+ )
281
303
 
282
304
  if alive?
283
305
  aborting
@@ -303,9 +325,13 @@ module Shipit
303
325
  end
304
326
  end
305
327
 
306
- def emit_hooks
328
+ def emit_hooks_if_status_changed
307
329
  return unless @status_changed
308
330
  @status_changed = nil
331
+ emit_hooks
332
+ end
333
+
334
+ def emit_hooks
309
335
  Hook.emit(hook_event, stack, hook_event => self, status: status, stack: stack)
310
336
  end
311
337
 
@@ -327,6 +353,35 @@ module Shipit
327
353
  end
328
354
  end
329
355
 
356
+ def commit_range?
357
+ since_commit && until_commit
358
+ end
359
+
360
+ def includes_commit?(commit)
361
+ return false unless commit_range?
362
+
363
+ if since_commit == until_commit
364
+ commit.id == since_commit.id
365
+ else
366
+ commit.id > since_commit.id && commit.id <= until_commit.id
367
+ end
368
+ end
369
+
370
+ def self.recently_created_at
371
+ 5.minutes.ago
372
+ end
373
+
374
+ ZOMBIE_STATES = %w(running aborting).freeze
375
+ private_constant :ZOMBIE_STATES
376
+ def self.zombies
377
+ where(status: ZOMBIE_STATES)
378
+ .where(
379
+ "created_at <= :recently",
380
+ recently: recently_created_at,
381
+ )
382
+ .reject(&:alive?)
383
+ end
384
+
330
385
  private
331
386
 
332
387
  def prevent_concurrency
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class TaskDefinition
3
4
  NotFound = Class.new(StandardError)
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
- class Team < ActiveRecord::Base
3
+ class Team < Record
3
4
  REQUIRED_HOOKS = %i(membership).freeze
4
5
 
5
6
  has_many :memberships
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class UndeployedCommit < DelegateClass(Commit)
3
4
  attr_reader :index
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class UnlimitedApiClient
3
4
  def stack_id?
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
- class User < ActiveRecord::Base
3
+ class User < Record
3
4
  DEFAULT_AVATAR = URI.parse('https://avatars.githubusercontent.com/u/583231?')
4
5
 
5
6
  has_many :memberships
@@ -23,9 +24,9 @@ module Shipit
23
24
  end
24
25
 
25
26
  def self.find_or_create_author_from_github_commit(github_commit)
26
- if github_commit.commit.message =~ /^#{PullRequest::MERGE_REQUEST_FIELD}: ([\w\-\.]+)$/
27
+ if (match_info = github_commit.commit.message.match(/^#{PullRequest::MERGE_REQUEST_FIELD}: ([\w\-\.]+)$/))
27
28
  begin
28
- return find_or_create_by_login!($1)
29
+ return find_or_create_by_login!(match_info[1])
29
30
  rescue Octokit::NotFound
30
31
  # Corner case where the merge-requested-by user cannot be found (renamed/deleted).
31
32
  # In this case we carry on and search for the commit author
@@ -68,7 +69,7 @@ module Shipit
68
69
  end
69
70
 
70
71
  def identifiers_for_ping
71
- {github_id: github_id, name: name, email: email, github_login: login}
72
+ { github_id: github_id, name: name, email: email, github_login: login }
72
73
  end
73
74
 
74
75
  def logged_in?
@@ -100,7 +101,7 @@ module Shipit
100
101
  assign_attributes(
101
102
  github_id: github_user.id,
102
103
  name: github_user.name || github_user.login, # Name is not mandatory on GitHub
103
- email: github_user.email,
104
+ email: appropriate_email_for(github_user),
104
105
  login: github_user.login,
105
106
  avatar_url: github_user.avatar_url,
106
107
  api_url: github_user.url,
@@ -123,5 +124,28 @@ module Shipit
123
124
  rescue Octokit::NotFound
124
125
  false
125
126
  end
127
+
128
+ def email_valid_and_preferred?(email_address)
129
+ org_domains = Shipit.preferred_org_emails
130
+ return true if org_domains.blank?
131
+ return false if email_address.blank?
132
+
133
+ org_domains.any? { |domain| email_address.end_with?("@#{domain}") }
134
+ end
135
+
136
+ def appropriate_email_for(github_user)
137
+ return github_user.email if email_valid_and_preferred?(github_user.email)
138
+
139
+ begin
140
+ github_api.emails
141
+ .sort_by { |e| e.primary ? 0 : 1 }
142
+ .map(&:email)
143
+ .find { |e| email_valid_and_preferred?(e) }
144
+ rescue Octokit::NotFound, Octokit::Forbidden, Octokit::Unauthorized
145
+ # If the user hasn't agreed to the necessary permission, we can't access their private emails.
146
+ Rails.logger.warn("Failed to retrieve emails for user '#{github_user.name || github_user.login}'")
147
+ nil
148
+ end
149
+ end
126
150
  end
127
151
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class VariableDefinition
3
4
  attr_reader :name, :title, :default, :select
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Shipit
3
+ module Webhooks
4
+ class << self
5
+ def default_handlers
6
+ {
7
+ 'push' => [Handlers::PushHandler],
8
+ 'status' => [Handlers::StatusHandler],
9
+ 'membership' => [Handlers::MembershipHandler],
10
+ 'check_suite' => [Handlers::CheckSuiteHandler],
11
+ }
12
+ end
13
+
14
+ def handlers
15
+ @handlers ||= reset_handlers!
16
+ end
17
+
18
+ def reset_handlers!
19
+ @handlers = default_handlers
20
+ end
21
+
22
+ def register_handler(event, callable = nil, &block)
23
+ handlers[event] ||= []
24
+ handlers[event] << callable if callable
25
+ handlers[event] << block if block_given?
26
+ end
27
+
28
+ def for_event(event)
29
+ handlers.fetch(event) { [] }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Shipit
3
+ module Webhooks
4
+ module Handlers
5
+ class CheckSuiteHandler < Handler
6
+ params do
7
+ requires :check_suite do
8
+ requires :head_sha, String
9
+ requires :head_branch, String
10
+ end
11
+ end
12
+ def process
13
+ stacks.where(branch: params.check_suite.head_branch).each do |stack|
14
+ stack.commits.where(sha: params.check_suite.head_sha).each(&:schedule_refresh_check_runs!)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module Shipit
3
+ module Webhooks
4
+ module Handlers
5
+ class Handler
6
+ class << self
7
+ attr_reader :param_parser
8
+
9
+ def params(&block)
10
+ @param_parser = ExplicitParameters::Parameters.define(&block)
11
+ end
12
+ end
13
+
14
+ def self.call(params)
15
+ new(params).process
16
+ end
17
+
18
+ attr_reader :params, :payload
19
+
20
+ def initialize(payload)
21
+ @payload = payload
22
+ @params = self.class.param_parser.parse!(payload)
23
+ end
24
+
25
+ def process
26
+ raise NotImplementedError
27
+ end
28
+
29
+ private
30
+
31
+ def stacks
32
+ @stacks ||= Repository.from_github_repo_name(repository_name)&.stacks || Stack.none
33
+ end
34
+
35
+ def repository_name
36
+ payload.dig('repository', 'full_name')
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ module Shipit
3
+ module Webhooks
4
+ module Handlers
5
+ class MembershipHandler < Handler
6
+ params do
7
+ requires :action, String
8
+ requires :team do
9
+ requires :id, Integer
10
+ requires :name, String
11
+ requires :slug, String
12
+ requires :url, String
13
+ end
14
+ requires :organization do
15
+ requires :login, String
16
+ end
17
+ requires :member do
18
+ requires :login, String
19
+ end
20
+ end
21
+ def process
22
+ team = find_or_create_team!
23
+ member = User.find_or_create_by_login!(params.member.login)
24
+
25
+ case params.action
26
+ when 'added'
27
+ team.add_member(member)
28
+ when 'removed'
29
+ team.members.delete(member)
30
+ else
31
+ raise ArgumentError, "Don't know how to perform action: `#{action.inspect}`"
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def find_or_create_team!
38
+ Team.find_or_create_by!(github_id: params.team.id) do |team|
39
+ team.github_team = params.team
40
+ team.organization = params.organization.login
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end