shipit-engine 0.31.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 (290) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/Rakefile +4 -2
  4. data/app/assets/stylesheets/_pages/_stacks.scss +0 -3
  5. data/app/assets/stylesheets/merge_status.scss +0 -3
  6. data/app/controllers/concerns/shipit/active_model_serializers_patch.rb +1 -0
  7. data/app/controllers/concerns/shipit/api/cacheable.rb +1 -0
  8. data/app/controllers/concerns/shipit/api/paginable.rb +3 -2
  9. data/app/controllers/concerns/shipit/api/rendering.rb +5 -4
  10. data/app/controllers/concerns/shipit/authentication.rb +3 -2
  11. data/app/controllers/concerns/shipit/pagination.rb +2 -1
  12. data/app/controllers/shipit/api/base_controller.rb +11 -6
  13. data/app/controllers/shipit/api/ccmenu_controller.rb +2 -1
  14. data/app/controllers/shipit/api/commits_controller.rb +2 -1
  15. data/app/controllers/shipit/api/deploys_controller.rb +4 -3
  16. data/app/controllers/shipit/api/hooks_controller.rb +6 -5
  17. data/app/controllers/shipit/api/locks_controller.rb +5 -4
  18. data/app/controllers/shipit/api/outputs_controller.rb +2 -1
  19. data/app/controllers/shipit/api/pull_requests_controller.rb +7 -6
  20. data/app/controllers/shipit/api/release_statuses_controller.rb +3 -2
  21. data/app/controllers/shipit/api/rollbacks_controller.rb +33 -0
  22. data/app/controllers/shipit/api/stacks_controller.rb +18 -5
  23. data/app/controllers/shipit/api/tasks_controller.rb +6 -5
  24. data/app/controllers/shipit/api_clients_controller.rb +4 -3
  25. data/app/controllers/shipit/ccmenu_url_controller.rb +4 -3
  26. data/app/controllers/shipit/commit_checks_controller.rb +2 -1
  27. data/app/controllers/shipit/commits_controller.rb +2 -1
  28. data/app/controllers/shipit/deploys_controller.rb +3 -2
  29. data/app/controllers/shipit/github_authentication_controller.rb +4 -3
  30. data/app/controllers/shipit/merge_status_controller.rb +17 -16
  31. data/app/controllers/shipit/pull_requests_controller.rb +3 -2
  32. data/app/controllers/shipit/release_statuses_controller.rb +3 -2
  33. data/app/controllers/shipit/rollbacks_controller.rb +3 -2
  34. data/app/controllers/shipit/shipit_controller.rb +2 -1
  35. data/app/controllers/shipit/stacks_controller.rb +22 -7
  36. data/app/controllers/shipit/status_controller.rb +2 -1
  37. data/app/controllers/shipit/tasks_controller.rb +6 -5
  38. data/app/controllers/shipit/webhooks_controller.rb +3 -2
  39. data/app/helpers/shipit/chunks_helper.rb +1 -0
  40. data/app/helpers/shipit/deploys_helper.rb +4 -3
  41. data/app/helpers/shipit/github_url_helper.rb +1 -0
  42. data/app/helpers/shipit/merge_status_helper.rb +1 -0
  43. data/app/helpers/shipit/shipit_helper.rb +1 -0
  44. data/app/helpers/shipit/stacks_helper.rb +1 -0
  45. data/app/helpers/shipit/tasks_helper.rb +1 -0
  46. data/app/jobs/shipit/background_job.rb +4 -0
  47. data/app/jobs/shipit/background_job/unique.rb +1 -0
  48. data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -0
  49. data/app/jobs/shipit/chunk_rollup_job.rb +4 -0
  50. data/app/jobs/shipit/clear_git_cache_job.rb +1 -0
  51. data/app/jobs/shipit/continuous_delivery_job.rb +2 -1
  52. data/app/jobs/shipit/create_on_github_job.rb +6 -1
  53. data/app/jobs/shipit/create_release_statuses_job.rb +1 -0
  54. data/app/jobs/shipit/deferred_touch_job.rb +4 -0
  55. data/app/jobs/shipit/deliver_hook_job.rb +1 -0
  56. data/app/jobs/shipit/destroy_job.rb +1 -0
  57. data/app/jobs/shipit/destroy_stack_job.rb +1 -0
  58. data/app/jobs/shipit/emit_event_job.rb +2 -1
  59. data/app/jobs/shipit/fetch_commit_stats_job.rb +1 -0
  60. data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
  61. data/app/jobs/shipit/github_sync_job.rb +2 -1
  62. data/app/jobs/shipit/mark_deploy_healthy_job.rb +1 -0
  63. data/app/jobs/shipit/merge_pull_requests_job.rb +1 -0
  64. data/app/jobs/shipit/perform_commit_checks_job.rb +1 -0
  65. data/app/jobs/shipit/perform_task_job.rb +12 -5
  66. data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
  67. data/app/jobs/shipit/reap_dead_tasks_job.rb +21 -0
  68. data/app/jobs/shipit/refresh_check_runs_job.rb +1 -0
  69. data/app/jobs/shipit/refresh_github_user_job.rb +1 -0
  70. data/app/jobs/shipit/refresh_pull_request_job.rb +1 -0
  71. data/app/jobs/shipit/refresh_statuses_job.rb +1 -0
  72. data/app/jobs/shipit/setup_github_hook_job.rb +1 -0
  73. data/app/jobs/shipit/update_estimated_deploy_duration_job.rb +1 -0
  74. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +4 -3
  75. data/app/models/concerns/shipit/deferred_touch.rb +4 -3
  76. data/app/models/shipit/anonymous_user.rb +5 -0
  77. data/app/models/shipit/api_client.rb +3 -2
  78. data/app/models/shipit/application_record.rb +2 -1
  79. data/app/models/shipit/check_run.rb +4 -3
  80. data/app/models/shipit/command_line_user.rb +1 -0
  81. data/app/models/shipit/commit.rb +11 -4
  82. data/app/models/shipit/commit_checks.rb +1 -0
  83. data/app/models/shipit/commit_deployment.rb +3 -2
  84. data/app/models/shipit/commit_deployment_status.rb +2 -1
  85. data/app/models/shipit/commit_message.rb +1 -0
  86. data/app/models/shipit/delivery.rb +4 -3
  87. data/app/models/shipit/deploy.rb +6 -17
  88. data/app/models/shipit/deploy_spec.rb +22 -3
  89. data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -0
  90. data/app/models/shipit/deploy_spec/capistrano_discovery.rb +1 -0
  91. data/app/models/shipit/deploy_spec/file_system.rb +10 -3
  92. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -0
  93. data/app/models/shipit/deploy_spec/lerna_discovery.rb +1 -0
  94. data/app/models/shipit/deploy_spec/npm_discovery.rb +5 -4
  95. data/app/models/shipit/deploy_spec/pypi_discovery.rb +1 -0
  96. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -0
  97. data/app/models/shipit/deploy_stats.rb +2 -1
  98. data/app/models/shipit/duration.rb +3 -2
  99. data/app/models/shipit/ephemeral_commit_checks.rb +1 -0
  100. data/app/models/shipit/github_hook.rb +2 -1
  101. data/app/models/shipit/github_status.rb +2 -1
  102. data/app/models/shipit/hook.rb +6 -5
  103. data/app/models/shipit/membership.rb +3 -2
  104. data/app/models/shipit/output_chunk.rb +7 -2
  105. data/app/models/shipit/pull_request.rb +6 -5
  106. data/app/models/shipit/record.rb +18 -0
  107. data/app/models/shipit/release_status.rb +3 -2
  108. data/app/models/shipit/repository.rb +10 -5
  109. data/app/models/shipit/rollback.rb +1 -0
  110. data/app/models/shipit/stack.rb +50 -16
  111. data/app/models/shipit/status.rb +3 -2
  112. data/app/models/shipit/status/common.rb +7 -6
  113. data/app/models/shipit/status/group.rb +1 -0
  114. data/app/models/shipit/status/missing.rb +2 -1
  115. data/app/models/shipit/status/unknown.rb +2 -1
  116. data/app/models/shipit/task.rb +38 -5
  117. data/app/models/shipit/task_definition.rb +1 -0
  118. data/app/models/shipit/team.rb +2 -1
  119. data/app/models/shipit/undeployed_commit.rb +1 -0
  120. data/app/models/shipit/unlimited_api_client.rb +1 -0
  121. data/app/models/shipit/user.rb +10 -8
  122. data/app/models/shipit/variable_definition.rb +1 -0
  123. data/app/models/shipit/webhooks.rb +1 -0
  124. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +1 -0
  125. data/app/models/shipit/webhooks/handlers/handler.rb +1 -0
  126. data/app/models/shipit/webhooks/handlers/membership_handler.rb +1 -0
  127. data/app/models/shipit/webhooks/handlers/push_handler.rb +1 -0
  128. data/app/models/shipit/webhooks/handlers/status_handler.rb +1 -0
  129. data/app/serializers/concerns/shipit/conditional_attributes.rb +1 -0
  130. data/app/serializers/shipit/anonymous_user_serializer.rb +1 -0
  131. data/app/serializers/shipit/command_line_user_serializer.rb +1 -0
  132. data/app/serializers/shipit/commit_serializer.rb +1 -0
  133. data/app/serializers/shipit/deploy_serializer.rb +2 -1
  134. data/app/serializers/shipit/hook_serializer.rb +1 -0
  135. data/app/serializers/shipit/pull_request_serializer.rb +1 -0
  136. data/app/serializers/shipit/rollback_serializer.rb +1 -0
  137. data/app/serializers/shipit/short_commit_serializer.rb +1 -0
  138. data/app/serializers/shipit/stack_serializer.rb +1 -0
  139. data/app/serializers/shipit/tail_task_serializer.rb +1 -0
  140. data/app/serializers/shipit/task_serializer.rb +2 -17
  141. data/app/serializers/shipit/user_serializer.rb +6 -1
  142. data/app/validators/ascii_only_validator.rb +1 -0
  143. data/app/validators/subset_validator.rb +1 -0
  144. data/app/views/layouts/shipit.html.erb +1 -1
  145. data/app/views/shipit/_variables.html.erb +1 -1
  146. data/app/views/shipit/ccmenu/project.xml.builder +2 -1
  147. data/app/views/shipit/deploys/show.html.erb +2 -2
  148. data/app/views/shipit/stacks/_header.html.erb +3 -0
  149. data/app/views/shipit/stacks/all_tasks.html.erb +28 -0
  150. data/app/views/shipit/stacks/index.html.erb +1 -1
  151. data/app/views/shipit/tasks/show.html.erb +1 -1
  152. data/config/initializers/inflections.rb +2 -1
  153. data/config/locales/en.yml +3 -3
  154. data/config/routes.rb +10 -2
  155. data/db/migrate/20200226211925_add_index_to_tasks_status.rb +5 -0
  156. data/db/migrate/20200427135152_add_pull_request_head_sha_to_commit.rb +5 -0
  157. data/db/migrate/20200615181558_add_rollback_once_aborted_to.rb +5 -0
  158. data/lib/shipit.rb +12 -2
  159. data/lib/shipit/cast_value.rb +1 -0
  160. data/lib/shipit/command.rb +13 -12
  161. data/lib/shipit/commands.rb +5 -4
  162. data/lib/shipit/csv_serializer.rb +1 -0
  163. data/lib/shipit/deploy_commands.rb +1 -0
  164. data/lib/shipit/engine.rb +7 -2
  165. data/lib/shipit/environment_variables.rb +2 -1
  166. data/lib/shipit/first_parent_commits_iterator.rb +1 -0
  167. data/lib/shipit/flock.rb +1 -0
  168. data/lib/shipit/github_app.rb +7 -6
  169. data/lib/shipit/github_http_cache_middleware.rb +1 -0
  170. data/lib/shipit/null_serializer.rb +1 -0
  171. data/lib/shipit/octokit_check_runs.rb +3 -2
  172. data/lib/shipit/octokit_iterator.rb +3 -2
  173. data/lib/shipit/paginator.rb +3 -2
  174. data/lib/shipit/rollback_commands.rb +1 -0
  175. data/lib/shipit/same_site_cookie_middleware.rb +29 -0
  176. data/lib/shipit/simple_message_verifier.rb +1 -0
  177. data/lib/shipit/stack_commands.rb +3 -2
  178. data/lib/shipit/stat.rb +1 -0
  179. data/lib/shipit/task_commands.rb +2 -0
  180. data/lib/shipit/version.rb +2 -1
  181. data/lib/snippets/release-gem +5 -1
  182. data/lib/tasks/cron.rake +2 -0
  183. data/lib/tasks/dev.rake +3 -2
  184. data/lib/tasks/shipit.rake +15 -14
  185. data/lib/tasks/teams.rake +1 -0
  186. data/test/controllers/api/base_controller_test.rb +3 -2
  187. data/test/controllers/api/ccmenu_controller_test.rb +8 -7
  188. data/test/controllers/api/commits_controller_test.rb +3 -2
  189. data/test/controllers/api/deploys_controller_test.rb +15 -14
  190. data/test/controllers/api/hooks_controller_test.rb +8 -7
  191. data/test/controllers/api/locks_controller_test.rb +7 -6
  192. data/test/controllers/api/outputs_controller_test.rb +2 -1
  193. data/test/controllers/api/pull_requests_controller_test.rb +8 -7
  194. data/test/controllers/api/release_statuses_controller_test.rb +2 -1
  195. data/test/controllers/api/rollback_controller_test.rb +113 -0
  196. data/test/controllers/api/stacks_controller_test.rb +30 -9
  197. data/test/controllers/api/tasks_controller_test.rb +13 -12
  198. data/test/controllers/api_clients_controller_test.rb +5 -4
  199. data/test/controllers/ccmenu_controller_test.rb +4 -3
  200. data/test/controllers/commit_checks_controller_test.rb +4 -3
  201. data/test/controllers/commits_controller_test.rb +3 -2
  202. data/test/controllers/deploys_controller_test.rb +32 -21
  203. data/test/controllers/github_authentication_controller_test.rb +1 -0
  204. data/test/controllers/merge_status_controller_test.rb +7 -6
  205. data/test/controllers/pull_requests_controller_test.rb +4 -3
  206. data/test/controllers/release_statuses_controller_test.rb +3 -2
  207. data/test/controllers/rollbacks_controller_test.rb +9 -8
  208. data/test/controllers/stacks_controller_test.rb +33 -19
  209. data/test/controllers/status_controller_test.rb +1 -0
  210. data/test/controllers/tasks_controller_test.rb +19 -18
  211. data/test/controllers/webhooks_controller_test.rb +11 -10
  212. data/test/dummy/config/environments/development.rb +2 -0
  213. data/test/dummy/config/environments/test.rb +2 -0
  214. data/test/dummy/db/schema.rb +4 -1
  215. data/test/fixtures/shipit/commits.yml +15 -0
  216. data/test/fixtures/shipit/repositories.yml +4 -0
  217. data/test/fixtures/shipit/stacks.yml +90 -14
  218. data/test/fixtures/timeout +2 -1
  219. data/test/helpers/api_helper.rb +1 -0
  220. data/test/helpers/fixture_aliases_helper.rb +1 -0
  221. data/test/helpers/hooks_helper.rb +2 -1
  222. data/test/helpers/json_helper.rb +15 -11
  223. data/test/helpers/links_helper.rb +4 -3
  224. data/test/helpers/payloads_helper.rb +1 -0
  225. data/test/helpers/queries_helper.rb +3 -2
  226. data/test/jobs/cache_deploy_spec_job_test.rb +2 -1
  227. data/test/jobs/chunk_rollup_job_test.rb +1 -0
  228. data/test/jobs/deliver_hook_job_test.rb +1 -0
  229. data/test/jobs/destroy_stack_job_test.rb +1 -0
  230. data/test/jobs/emit_event_job_test.rb +2 -1
  231. data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
  232. data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
  233. data/test/jobs/github_sync_job_test.rb +1 -0
  234. data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
  235. data/test/jobs/merge_pull_requests_job_test.rb +1 -0
  236. data/test/jobs/perform_task_job_test.rb +4 -3
  237. data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
  238. data/test/jobs/reap_dead_tasks_job_test.rb +68 -0
  239. data/test/jobs/refresh_github_user_job_test.rb +1 -0
  240. data/test/jobs/refresh_status_job_test.rb +1 -0
  241. data/test/jobs/unique_job_test.rb +1 -0
  242. data/test/jobs/update_github_last_deployed_ref_job_test.rb +1 -0
  243. data/test/middleware/same_site_cookie_middleware_test.rb +52 -0
  244. data/test/models/api_client_test.rb +1 -0
  245. data/test/models/commit_checks_test.rb +1 -0
  246. data/test/models/commit_deployment_status_test.rb +1 -0
  247. data/test/models/commit_deployment_test.rb +2 -1
  248. data/test/models/commits_test.rb +72 -6
  249. data/test/models/delivery_test.rb +2 -1
  250. data/test/models/deploy_spec_test.rb +47 -42
  251. data/test/models/deploy_stats_test.rb +1 -0
  252. data/test/models/deploys_test.rb +31 -22
  253. data/test/models/duration_test.rb +1 -0
  254. data/test/models/github_hook_test.rb +1 -0
  255. data/test/models/hook_test.rb +18 -10
  256. data/test/models/membership_test.rb +1 -0
  257. data/test/models/output_chunk_test.rb +1 -0
  258. data/test/models/pull_request_test.rb +4 -3
  259. data/test/models/release_statuses_test.rb +1 -0
  260. data/test/models/rollbacks_test.rb +1 -0
  261. data/test/models/shipit/check_run_test.rb +1 -0
  262. data/test/models/shipit/repository_test.rb +1 -0
  263. data/test/models/shipit/wehbooks/handlers_test.rb +1 -0
  264. data/test/models/stacks_test.rb +30 -8
  265. data/test/models/status/group_test.rb +1 -0
  266. data/test/models/status/missing_test.rb +1 -0
  267. data/test/models/status_test.rb +1 -0
  268. data/test/models/task_definitions_test.rb +9 -8
  269. data/test/models/tasks_test.rb +18 -1
  270. data/test/models/team_test.rb +4 -2
  271. data/test/models/undeployed_commits_test.rb +1 -0
  272. data/test/models/users_test.rb +13 -5
  273. data/test/test_command_integration.rb +3 -2
  274. data/test/test_helper.rb +34 -31
  275. data/test/unit/anonymous_user_serializer_test.rb +14 -0
  276. data/test/unit/command_test.rb +8 -7
  277. data/test/unit/commands_test.rb +1 -0
  278. data/test/unit/commit_serializer_test.rb +16 -0
  279. data/test/unit/csv_serializer_test.rb +3 -2
  280. data/test/unit/deploy_commands_test.rb +5 -4
  281. data/test/unit/deploy_serializer_test.rb +17 -0
  282. data/test/unit/environment_variables_test.rb +5 -4
  283. data/test/unit/github_app_test.rb +1 -0
  284. data/test/unit/github_url_helper_test.rb +1 -0
  285. data/test/unit/rollback_commands_test.rb +2 -1
  286. data/test/unit/shipit_helper_test.rb +17 -0
  287. data/test/unit/shipit_test.rb +1 -0
  288. data/test/unit/user_serializer_test.rb +14 -0
  289. data/test/unit/variable_definition_test.rb +1 -0
  290. metadata +155 -130
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class PullRequest < ApplicationRecord
3
4
  include DeferredTouch
4
5
 
5
- MERGE_REQUEST_FIELD = 'Merge-Requested-By'.freeze
6
+ MERGE_REQUEST_FIELD = 'Merge-Requested-By'
6
7
 
7
8
  WAITING_STATUSES = %w(fetching pending).freeze
8
9
  QUEUED_STATUSES = %w(pending revalidating).freeze
@@ -45,7 +46,7 @@ module Shipit
45
46
 
46
47
  deferred_touch stack: :updated_at
47
48
 
48
- validates :number, presence: true, uniqueness: {scope: :stack_id}
49
+ validates :number, presence: true, uniqueness: { scope: :stack_id }
49
50
 
50
51
  scope :waiting, -> { where(merge_status: WAITING_STATUSES) }
51
52
  scope :pending, -> { where(merge_status: 'pending') }
@@ -176,10 +177,10 @@ module Shipit
176
177
  # branch was already deleted somehow
177
178
  end
178
179
  complete!
179
- return true
180
+ true
180
181
  rescue Octokit::MethodNotAllowed # merge conflict
181
182
  reject!('merge_conflict')
182
- return false
183
+ false
183
184
  rescue Octokit::Conflict # shas didn't match, PR was updated.
184
185
  raise NotReady
185
186
  end
@@ -289,7 +290,7 @@ module Shipit
289
290
 
290
291
  def find_or_create_commit_from_github_by_sha!(sha, attributes)
291
292
  if commit = stack.commits.by_sha(sha)
292
- return commit
293
+ commit
293
294
  else
294
295
  github_commit = Shipit.github.api.commit(stack.github_repo_name, sha)
295
296
  stack.commits.create_from_github!(github_commit, attributes)
@@ -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?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Shipit
2
3
  class Repository < ApplicationRecord
3
4
  OWNER_MAX_SIZE = 39
@@ -6,11 +7,11 @@ module Shipit
6
7
  NAME_MAX_SIZE = 100
7
8
  private_constant :NAME_MAX_SIZE
8
9
 
9
- validates :name, uniqueness: {scope: %i(owner), case_sensitive: false,
10
- message: 'cannot be used more than once'}
10
+ validates :name, uniqueness: { scope: %i(owner), case_sensitive: false,
11
+ message: 'cannot be used more than once' }
11
12
  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}
13
+ validates :owner, format: { with: /\A[a-z0-9_\-\.]+\z/ }, length: { maximum: OWNER_MAX_SIZE }
14
+ validates :name, format: { with: /\A[a-z0-9_\-\.]+\z/ }, length: { maximum: NAME_MAX_SIZE }
14
15
 
15
16
  has_many :stacks, dependent: :destroy
16
17
 
@@ -28,7 +29,11 @@ module Shipit
28
29
  end
29
30
 
30
31
  def http_url
31
- Shipit.github.url("#{owner}/#{name}")
32
+ Shipit.github.url(full_name)
33
+ end
34
+
35
+ def full_name
36
+ "#{owner}/#{name}"
32
37
  end
33
38
 
34
39
  def git_url
@@ -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
 
@@ -43,6 +44,8 @@ module Shipit
43
44
 
44
45
  scope :not_archived, -> { where(archived_since: nil) }
45
46
 
47
+ default_scope { preload(:repository) }
48
+
46
49
  def env
47
50
  {
48
51
  'ENVIRONMENT' => environment,
@@ -82,10 +85,10 @@ module Shipit
82
85
  scope: %i(environment), case_sensitive: false,
83
86
  message: 'cannot be used more than once with this environment. Check archived stacks.'
84
87
  }
85
- validates :environment, format: {with: /\A[a-z0-9\-_\:]+\z/}, length: {maximum: ENVIRONMENT_MAX_SIZE}
86
- validates :deploy_url, format: {with: URI.regexp(%w(http https ssh))}, allow_blank: true
88
+ validates :environment, format: { with: /\A[a-z0-9\-_\:]+\z/ }, length: { maximum: ENVIRONMENT_MAX_SIZE }
89
+ validates :deploy_url, format: { with: URI.regexp(%w(http https ssh)) }, allow_blank: true
87
90
 
88
- validates :lock_reason, length: {maximum: 4096}
91
+ validates :lock_reason, length: { maximum: 4096 }
89
92
 
90
93
  serialize :cached_deploy_spec, DeploySpec
91
94
  delegate :find_task_definition, :supports_rollback?, :release_status?, :release_status_delay,
@@ -140,9 +143,24 @@ module Shipit
140
143
  end
141
144
 
142
145
  def trigger_deploy(*args, **kwargs)
146
+ if changed?
147
+ # If this is the first deploy since the spec changed it's possible the record will be dirty here, meaning we
148
+ # cant lock. In this one case persist the changes, otherwise log a warning and let the lock raise, so we
149
+ # can debug what's going on here. We don't expect anything other than the deploy spec to dirty the model
150
+ # instance, because of how that field is serialised.
151
+ if changes.keys == ['cached_deploy_spec']
152
+ save!
153
+ else
154
+ Rails.logger.warning("#{changes.keys} field(s) were unexpectedly modified on stack #{id} while deploying")
155
+ end
156
+ end
157
+
143
158
  run_now = kwargs.delete(:run_now)
144
- deploy = build_deploy(*args, **kwargs)
145
- deploy.save!
159
+ deploy = with_lock do
160
+ deploy = build_deploy(*args, **kwargs)
161
+ deploy.save!
162
+ deploy
163
+ end
146
164
  run_now ? deploy.run_now! : deploy.enqueue
147
165
  continuous_delivery_resumed!
148
166
  deploy
@@ -161,15 +179,16 @@ module Shipit
161
179
  end
162
180
 
163
181
  def trigger_continuous_delivery
182
+ return if cached_deploy_spec.blank?
183
+
164
184
  commit = next_commit_to_deploy
165
185
 
166
- if !deployable? || deployed_too_recently? || commit.nil? || commit.deployed?
186
+ if should_resume_continuous_delivery?(commit)
167
187
  continuous_delivery_resumed!
168
188
  return
169
189
  end
170
190
 
171
- if commit.deploy_failed? || (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
172
- commit.recently_pushed?
191
+ if should_delay_continuous_delivery?(commit)
173
192
  continuous_delivery_delayed!
174
193
  return
175
194
  end
@@ -201,7 +220,7 @@ module Shipit
201
220
  def async_refresh_deployed_revision
202
221
  async_refresh_deployed_revision!
203
222
  rescue => error
204
- logger.warn "Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}"
223
+ logger.warn("Failed to dispatch FetchDeployedRevisionJob: [#{error.class.name}] #{error.message}")
205
224
  end
206
225
 
207
226
  def async_refresh_deployed_revision!
@@ -269,8 +288,8 @@ module Shipit
269
288
  next if commits_to_lock.empty?
270
289
 
271
290
  affected_rows += commits
272
- .where(id: commits_to_lock.map(&:id).uniq)
273
- .lock_all(revert.author)
291
+ .where(id: commits_to_lock.map(&:id).uniq)
292
+ .lock_all(revert.author)
274
293
  end
275
294
 
276
295
  touch if affected_rows > 1
@@ -409,7 +428,7 @@ module Shipit
409
428
  end
410
429
 
411
430
  def lock(reason, user)
412
- params = {lock_reason: reason, lock_author: user}
431
+ params = { lock_reason: reason, lock_author: user }
413
432
  update!(params)
414
433
  end
415
434
 
@@ -481,13 +500,15 @@ module Shipit
481
500
  end
482
501
 
483
502
  def update_latest_deployed_ref
484
- UpdateGithubLastDeployedRefJob.perform_later(self)
503
+ if Shipit.update_latest_deployed_ref
504
+ UpdateGithubLastDeployedRefJob.perform_later(self)
505
+ end
485
506
  end
486
507
 
487
508
  def broadcast_update
488
509
  Pubsubstub.publish(
489
510
  "stack.#{id}",
490
- {id: id, updated_at: updated_at}.to_json,
511
+ { id: id, updated_at: updated_at }.to_json,
491
512
  name: 'update',
492
513
  )
493
514
  end
@@ -581,7 +602,7 @@ module Shipit
581
602
  return unless previous_changes.include?('lock_reason')
582
603
 
583
604
  lock_details = if previous_changes['lock_reason'].last.blank?
584
- {from: previous_changes['locked_since'].first, until: Time.zone.now}
605
+ { from: previous_changes['locked_since'].first, until: Time.zone.now }
585
606
  end
586
607
 
587
608
  Hook.emit(:lock, self, locked: locked?, lock_details: lock_details, stack: self)
@@ -607,5 +628,18 @@ module Shipit
607
628
  def ci_enabled_cache_key
608
629
  "stacks:#{id}:ci_enabled"
609
630
  end
631
+
632
+ def should_resume_continuous_delivery?(commit)
633
+ !deployable? ||
634
+ deployed_too_recently? ||
635
+ commit.nil? ||
636
+ commit.deployed?
637
+ end
638
+
639
+ def should_delay_continuous_delivery?(commit)
640
+ commit.deploy_failed? ||
641
+ (checks? && !EphemeralCommitChecks.new(commit).run.success?) ||
642
+ commit.recently_pushed?
643
+ end
610
644
  end
611
645
  end
@@ -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
 
@@ -19,6 +23,8 @@ module Shipit
19
23
  belongs_to :until_commit, class_name: 'Commit', required: false
20
24
  belongs_to :since_commit, class_name: 'Commit', required: false
21
25
 
26
+ belongs_to :rollback_once_aborted_to, class_name: 'Task', optional: true
27
+
22
28
  deferred_touch stack: :updated_at
23
29
 
24
30
  has_many :chunks, -> { order(:id) }, class_name: 'OutputChunk', dependent: :delete_all, inverse_of: :task
@@ -198,7 +204,15 @@ module Shipit
198
204
  if rolled_up?
199
205
  output
200
206
  else
201
- 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
202
216
  end
203
217
  end
204
218
 
@@ -280,8 +294,12 @@ module Shipit
280
294
  end
281
295
  end
282
296
 
283
- def abort!(rollback_once_aborted: false, aborted_by:)
284
- 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
+ )
285
303
 
286
304
  if alive?
287
305
  aborting
@@ -349,6 +367,21 @@ module Shipit
349
367
  end
350
368
  end
351
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
+
352
385
  private
353
386
 
354
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