shipit-engine 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +43 -6
  3. data/app/assets/stylesheets/_base/_base.scss +4 -0
  4. data/app/assets/stylesheets/_pages/_commits.scss +3 -1
  5. data/app/assets/stylesheets/_pages/_deploy.scss +4 -2
  6. data/app/controllers/concerns/shipit/authentication.rb +1 -1
  7. data/app/controllers/shipit/api/base_controller.rb +6 -1
  8. data/app/controllers/shipit/api/pull_requests_controller.rb +1 -1
  9. data/app/controllers/shipit/commit_checks_controller.rb +1 -1
  10. data/app/controllers/shipit/shipit_controller.rb +1 -5
  11. data/app/controllers/shipit/stacks_controller.rb +2 -0
  12. data/app/controllers/shipit/tasks_controller.rb +1 -1
  13. data/app/controllers/shipit/webhooks_controller.rb +2 -2
  14. data/app/helpers/shipit/deploys_helper.rb +9 -0
  15. data/app/helpers/shipit/shipit_helper.rb +17 -15
  16. data/app/helpers/shipit/stacks_helper.rb +6 -1
  17. data/app/jobs/shipit/destroy_stack_job.rb +4 -2
  18. data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -1
  19. data/app/jobs/shipit/github_sync_job.rb +1 -1
  20. data/app/jobs/shipit/merge_pull_requests_job.rb +3 -3
  21. data/app/jobs/shipit/perform_task_job.rb +3 -0
  22. data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
  23. data/app/models/shipit/api_client.rb +1 -1
  24. data/app/models/shipit/commit.rb +29 -6
  25. data/app/models/shipit/commit_deployment.rb +1 -1
  26. data/app/models/shipit/commit_deployment_status.rb +1 -1
  27. data/app/models/shipit/deploy_spec.rb +19 -2
  28. data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -10
  29. data/app/models/shipit/deploy_spec/file_system.rb +6 -0
  30. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -1
  31. data/app/models/shipit/deploy_spec/lerna_discovery.rb +85 -0
  32. data/app/models/shipit/deploy_spec/npm_discovery.rb +103 -5
  33. data/app/models/shipit/deploy_spec/pypi_discovery.rb +4 -2
  34. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +4 -2
  35. data/app/models/shipit/duration.rb +1 -1
  36. data/app/models/shipit/github_status.rb +1 -1
  37. data/app/models/shipit/hook.rb +4 -5
  38. data/app/models/shipit/output_chunk.rb +1 -1
  39. data/app/models/shipit/pull_request.rb +36 -15
  40. data/app/models/shipit/stack.rb +15 -9
  41. data/app/models/shipit/status/common.rb +4 -0
  42. data/app/models/shipit/status/group.rb +4 -0
  43. data/app/models/shipit/task.rb +20 -8
  44. data/app/models/shipit/task_definition.rb +2 -2
  45. data/app/models/shipit/undeployed_commit.rb +13 -2
  46. data/app/models/shipit/user.rb +1 -1
  47. data/app/serializers/shipit/pull_request_serializer.rb +1 -1
  48. data/app/serializers/shipit/tail_task_serializer.rb +1 -1
  49. data/app/views/shipit/ccmenu/project.xml.builder +9 -8
  50. data/app/views/shipit/deploys/_deploy.html.erb +3 -2
  51. data/app/views/shipit/stacks/_banners.html.erb +4 -1
  52. data/app/views/shipit/stacks/settings.html.erb +4 -0
  53. data/app/views/shipit/statuses/_group.html.erb +1 -1
  54. data/app/views/shipit/statuses/_status.html.erb +1 -1
  55. data/app/views/shipit/tasks/_task.html.erb +1 -2
  56. data/config/locales/en.yml +2 -0
  57. data/config/secrets.development.example.yml +0 -4
  58. data/config/secrets.development.shopify.yml +1 -5
  59. data/db/migrate/20170904103242_reindex_deliveries.rb +7 -0
  60. data/db/migrate/20171120161420_add_base_info_to_pull_request.rb +7 -0
  61. data/db/migrate/20180202220850_add_aborted_by_to_tasks.rb +5 -0
  62. data/lib/shipit.rb +15 -23
  63. data/lib/shipit/command.rb +11 -3
  64. data/lib/shipit/engine.rb +0 -4
  65. data/lib/shipit/stack_commands.rb +3 -1
  66. data/lib/shipit/version.rb +1 -1
  67. data/lib/snippets/assert-lerna-fixed-version-tag +21 -0
  68. data/lib/snippets/assert-lerna-independent-version-tags +28 -0
  69. data/lib/snippets/generate-local-npmrc +19 -0
  70. data/lib/snippets/misconfigured-npm-publish-config +8 -0
  71. data/lib/snippets/publish-lerna-independent-packages +39 -0
  72. data/lib/snippets/push-to-heroku +5 -5
  73. data/lib/tasks/cron.rake +1 -1
  74. data/lib/tasks/dev.rake +1 -1
  75. data/test/controllers/api/deploys_controller_test.rb +19 -0
  76. data/test/controllers/api/stacks_controller_test.rb +1 -1
  77. data/test/controllers/github_authentication_controller_test.rb +1 -1
  78. data/test/controllers/stacks_controller_test.rb +10 -0
  79. data/test/controllers/tasks_controller_test.rb +2 -0
  80. data/test/controllers/webhooks_controller_test.rb +0 -7
  81. data/test/dummy/config/secrets.yml +0 -2
  82. data/test/dummy/db/development.sqlite3 +0 -0
  83. data/test/dummy/db/schema.rb +5 -3
  84. data/test/dummy/db/test.sqlite3 +0 -0
  85. data/test/fixtures/shipit/commits.yml +53 -0
  86. data/test/fixtures/shipit/pull_requests.yml +52 -0
  87. data/test/fixtures/shipit/stacks.yml +35 -0
  88. data/test/fixtures/shipit/statuses.yml +27 -0
  89. data/test/fixtures/shipit/tasks.yml +14 -0
  90. data/test/helpers/queries_helper.rb +1 -1
  91. data/test/jobs/merge_pull_requests_job_test.rb +19 -2
  92. data/test/jobs/perform_task_job_test.rb +26 -2
  93. data/test/models/commits_test.rb +55 -6
  94. data/test/models/deploy_spec_test.rb +288 -52
  95. data/test/models/deploys_test.rb +7 -7
  96. data/test/models/hook_test.rb +4 -3
  97. data/test/models/pull_request_test.rb +78 -24
  98. data/test/models/stacks_test.rb +21 -17
  99. data/test/models/status/group_test.rb +6 -0
  100. data/test/models/undeployed_commits_test.rb +9 -0
  101. data/test/models/users_test.rb +2 -2
  102. data/test/test_helper.rb +1 -1
  103. metadata +211 -222
  104. data/app/assets/javascripts/shipit_bs.js.coffee +0 -2
  105. data/app/assets/stylesheets/shipit_bs.scss +0 -22
  106. data/app/views/bootstrap/shipit/missing_settings.html.erb +0 -97
  107. data/app/views/bootstrap/shipit/stacks/new.html.erb +0 -44
  108. data/app/views/layouts/shipit_bootstrap.html.erb +0 -44
  109. data/lib/shipit/template_renderer_extension.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 32d85f27b707a0fd01fd1772e5803af8663d195b
4
- data.tar.gz: 3f5df442e05fc1048307226080695c438fce46aa
2
+ SHA256:
3
+ metadata.gz: f6940a4cd6a4608c60f6349020a01ef276049cf5aa78ff784abfe2cdf99a920f
4
+ data.tar.gz: f7056eb044a29a5646a349bcfe9fc8c6614fcbd7a199797275657c6c3598bb2c
5
5
  SHA512:
6
- metadata.gz: d7a9dbec229ef543d4e4b884c75539abf59f143d67fd068cee822c609a4fcd5232dae511215d54674064a52a912645036ed7f4bdcfeb2263343a4e57d5559965
7
- data.tar.gz: 3a02db736fb43949135fdada6d039c32f1a810204a20152c049c775d9bc17d5de526bf783bee98d9f2f892e753cf6bd329994881282344b40079329b6aaac809
6
+ metadata.gz: 4d61eb2c4fe287478d9ef7589b59ac268abbac149dc1783f15cf98d22230463aaeea4caf13060da7f0b2038742585e5df00cd29d05edd571a2326c030bbefbc0
7
+ data.tar.gz: 6d94a0ca00022f7727d7ecc9dd965f60ae9b92867c44434fa5057cd036a512ed04a685f0dd0e93a8886bd4df370a01682e6e2c213b5c8d65c1db4339efd79f2e
data/README.md CHANGED
@@ -122,8 +122,9 @@ The settings in the `shipit.yml` file relate to the different things you can do
122
122
  * [Deployment](#deployment) (`deploy`, `rollback`, `fetch`)
123
123
  * [Environment](#environment) (`machine.environment`, `machine.directory`, `machine.cleanup`)
124
124
  * [CI](#ci) (`ci.require`, `ci.hide`, `ci.allow_failures`)
125
- * [Custom Tasks](#custom-tasks) (`restart`, `unlock`)
126
- * [Review Process](#review-process) (`monitor`, `checklist`, `checks`)
125
+ * [Merge Queue](#merge-queue) (`merge.revalidate_after`, `merge.require`, `merge.ignore`, `merge.max_divergence`)
126
+ * [Custom Tasks](#custom-tasks) (`tasks`)
127
+ * [Review Process](#review-process) (`review.checklist`, `review.monitoring`, `review.checks`)
127
128
 
128
129
  All the settings in `shipit.yml` are optional. Most applications can be deployed from Shipit without any configuration.
129
130
 
@@ -269,7 +270,7 @@ deploy:
269
270
  ```
270
271
  <br>
271
272
 
272
- **<code>deploy.max_commits</code>** define the maximum number of commits that should be shipped per deploys. Defaults to `8`.
273
+ **<code>deploy.max_commits</code>** defines the maximum number of commits that should be shipped per deploys. Defaults to `8`.
273
274
 
274
275
  Human users will be warned that they are not respecting the recommendation, but allowed to continue.
275
276
  However continuous delivery will respect this limit. If there is no deployable commits in this range, a human intervention will be required.
@@ -282,6 +283,15 @@ deploy:
282
283
  ```
283
284
  <br>
284
285
 
286
+ **<code>deploy.interval</code>** defines the interval between the end of a deploy and the next deploy, when continuous delivery is enabled. You can use s, m, h, d as units for seconds, minutes, hours, and days. Defaults to 0, which means a new deploy will start as soon as the current one finishes.
287
+
288
+ For example, this will wait 5 minutes after the end of a deploy before starting a new one:
289
+
290
+ ```yaml
291
+ deploy:
292
+ interval: 5m
293
+ ```
294
+
285
295
  **<code>rollback.override</code>** contains an array of the shell commands required to rollback the application to a previous state. Shipit will try to infer it from the repository structure, but you can change the default inference. This key defaults to `disabled` unless Capistrano is detected.
286
296
 
287
297
  For example:
@@ -369,7 +379,7 @@ machine:
369
379
 
370
380
  <h3 id="ci">CI</h3>
371
381
 
372
- **<code>ci.require</code>** contains an array of the [statuses context](https://developer.github.com/v3/repos/statuses/) you want Shipit to disallow deploys if any of them is missing.
382
+ **<code>ci.require</code>** contains an array of the [statuses context](https://developer.github.com/v3/repos/statuses/) you want Shipit to disallow deploys if any of them is missing on the commit being deployed.
373
383
 
374
384
  For example:
375
385
  ```yml
@@ -396,6 +406,15 @@ ci:
396
406
  - ci/circleci
397
407
  ```
398
408
 
409
+ **<code>ci.blocking</code>** contains an array of the [statuses context](https://developer.github.com/v3/repos/statuses/) you want to disallow deploys if any of them is missing or failing on any of the commits being deployed.
410
+
411
+ For example:
412
+ ```yml
413
+ ci:
414
+ blocking:
415
+ - soc/compliance
416
+ ```
417
+
399
418
  <h3 id="merge-queue">Merge Queue</h3>
400
419
 
401
420
  The merge queue allow to register pull requests for them to be merged by Shipit once the stack is clear (no lock, no failing CI, no backlog). It can be enabled on a per stack basis via the settings page.
@@ -428,11 +447,29 @@ merge:
428
447
  - codeclimate
429
448
  ```
430
449
 
450
+ **<code>merge.max_divergence.commits</code>** the maximum number of commits a pull request can be behind its merge base, after which pull requests are rejected from the merge queue.
451
+
452
+ For example:
453
+ ```yml
454
+ merge:
455
+ max_divergence:
456
+ commits: 50
457
+ ```
458
+
459
+ **<code>merge.max_divergence.age</code>** a duration after the commit date of the merge base, after which pull requests will be rejected from the merge queue.
460
+
461
+ For example:
462
+ ```yml
463
+ merge:
464
+ max_divergence:
465
+ age: 72h
466
+ ```
467
+
431
468
  <h3 id="custom-tasks">Custom tasks</h3>
432
469
 
433
470
  You can create custom tasks that users execute directly from a stack's overview page in Shipit. To create a new custom task, specify its parameters in the `tasks` section of the `shipit.yml` file. For example:
434
471
 
435
- **<code>tasks</code>** restarts the application.
472
+ **<code>tasks.restart</code>** restarts the application.
436
473
 
437
474
  ```yml
438
475
  tasks:
@@ -649,7 +686,7 @@ These variables are accessible only during deploys and rollback:
649
686
 
650
687
  ### Heroku
651
688
 
652
- To use Heroku integration (`lib/snippets/push-to-heroku`), make sure that the environment has [Heroku toolbelt](https://devcenter.heroku.com/articles/heroku-cli) available.
689
+ To use Heroku integration (`lib/snippets/push-to-heroku`), make sure that the environment has [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) available.
653
690
 
654
691
  ### Kubernetes
655
692
 
@@ -106,6 +106,10 @@ a {
106
106
  text-decoration: none;
107
107
  cursor: pointer;
108
108
  color: $blue;
109
+
110
+ &.disabled {
111
+ cursor: default;
112
+ }
109
113
  }
110
114
 
111
115
  .more {
@@ -281,7 +281,9 @@
281
281
  }
282
282
 
283
283
  .status--error,
284
- [data-deploy-status='error'] {
284
+ [data-deploy-status='error'],
285
+ .status--timedout,
286
+ [data-deploy-status='timedout'] {
285
287
  border-color: #333;
286
288
 
287
289
  .status__icon {
@@ -93,7 +93,8 @@
93
93
  }
94
94
 
95
95
  &[data-status="failure"],
96
- &[data-status="error"] {
96
+ &[data-status="error"],
97
+ &[data-status="timedout"] {
97
98
  .deploy-banner-status {
98
99
  background-color: $bright-red;
99
100
  width: 100%;
@@ -196,7 +197,8 @@
196
197
 
197
198
  &[data-status="failed"],
198
199
  &[data-status="failure"],
199
- &[data-status="error"] {
200
+ &[data-status="error"],
201
+ &[data-status="timedout"] {
200
202
  border-color: $bright-red;
201
203
  }
202
204
 
@@ -32,7 +32,7 @@ module Shipit
32
32
  end
33
33
 
34
34
  def find_current_user
35
- session[:user_id].present? && User.find_by_id(session[:user_id])
35
+ session[:user_id].present? && User.find_by(id: session[:user_id])
36
36
  end
37
37
  end
38
38
  end
@@ -9,6 +9,7 @@ module Shipit
9
9
  rescue_from ApiClient::InsufficientPermission, with: :insufficient_permission
10
10
  rescue_from EnvironmentVariables::NotPermitted, with: :validation_error
11
11
  rescue_from TaskDefinition::NotFound, with: :not_found
12
+ rescue_from Task::ConcurrentTaskRunning, with: :conflict
12
13
 
13
14
  class << self
14
15
  def require_permission(operation, scope, options = {})
@@ -46,7 +47,7 @@ module Shipit
46
47
 
47
48
  def identify_user
48
49
  user_login = request.headers['X-Shipit-User'].presence
49
- User.find_by(login: user_login) if user_login
50
+ User.where('lower(login) = ?', user_login.downcase).first if user_login
50
51
  end
51
52
 
52
53
  def stacks
@@ -72,6 +73,10 @@ module Shipit
72
73
  def not_found(_error)
73
74
  render status: :not_found, json: {status: '404', error: 'Not Found'}
74
75
  end
76
+
77
+ def conflict(_error)
78
+ render status: :conflict, json: {status: '409', error: 'Conflict'}
79
+ end
75
80
  end
76
81
  end
77
82
  end
@@ -9,7 +9,7 @@ module Shipit
9
9
  end
10
10
 
11
11
  def show
12
- render_resource stack.pull_requests.find_by_number!(params[:id])
12
+ render_resource stack.pull_requests.find_by!(number: params[:id])
13
13
  end
14
14
 
15
15
  def update
@@ -23,7 +23,7 @@ module Shipit
23
23
  delegate :checks, to: :commit
24
24
 
25
25
  def commit
26
- @commit ||= stack.commits.find_by_sha!(params[:sha])
26
+ @commit ||= stack.commits.find_by!(sha: params[:sha])
27
27
  end
28
28
 
29
29
  def stack
@@ -11,7 +11,7 @@ module Shipit
11
11
  helper Shipit::Engine.routes.url_helpers
12
12
  include Shipit::Engine.routes.url_helpers
13
13
 
14
- before_action :toogle_bootstrap_feature, :ensure_required_settings
14
+ before_action :ensure_required_settings
15
15
 
16
16
  include Shipit::Authentication
17
17
 
@@ -24,10 +24,6 @@ module Shipit
24
24
 
25
25
  private
26
26
 
27
- def toogle_bootstrap_feature
28
- prepend_view_path(Shipit.bootstrap_view_path) if Shipit.feature_bootstrap?
29
- end
30
-
31
27
  def ensure_required_settings
32
28
  return if Shipit.all_settings_present?
33
29
 
@@ -66,11 +66,13 @@ module Shipit
66
66
 
67
67
  def sync_webhooks
68
68
  @stack.setup_hooks
69
+ flash[:success] = 'Webhooks syncing scheduled'
69
70
  redirect_to stack_settings_path(@stack)
70
71
  end
71
72
 
72
73
  def clear_git_cache
73
74
  ClearGitCacheJob.perform_later(@stack)
75
+ flash[:success] = 'Git Cache clearing scheduled'
74
76
  redirect_to stack_settings_path(@stack)
75
77
  end
76
78
 
@@ -42,7 +42,7 @@ module Shipit
42
42
  end
43
43
 
44
44
  def abort
45
- task.abort!(rollback_once_aborted: params[:rollback].present?)
45
+ task.abort!(rollback_once_aborted: params[:rollback].present?, aborted_by: current_user)
46
46
  head :ok
47
47
  end
48
48
 
@@ -27,7 +27,7 @@ module Shipit
27
27
  end
28
28
  end
29
29
  def state
30
- if commit = stack.commits.find_by_sha(params.sha)
30
+ if commit = stack.commits.find_by(sha: params.sha)
31
31
  commit.create_status_from_github!(params)
32
32
  end
33
33
  head :ok
@@ -88,7 +88,7 @@ module Shipit
88
88
  @webhook ||= if params[:stack_id]
89
89
  stack.github_hooks.find_by!(event: event)
90
90
  else
91
- GithubHook::Organization.find_by!(organization: params.organization.login.downcase, event: event)
91
+ GithubHook::Organization.find_by!(organization: params.organization.login, event: event)
92
92
  end
93
93
  end
94
94
 
@@ -24,5 +24,14 @@ module Shipit
24
24
  content_tag(:span, "#{panel_spec.inspect} is not a valid monitoring panel spec")
25
25
  end
26
26
  end
27
+
28
+ def deploy_status_in_words(status)
29
+ case status
30
+ when 'timedout'
31
+ "Timed out"
32
+ else
33
+ status.capitalize
34
+ end
35
+ end
27
36
  end
28
37
  end
@@ -10,21 +10,23 @@ module Shipit
10
10
  end
11
11
 
12
12
  def emojify(content)
13
- h(content).to_str.gsub(/:([\w+-]+):/) do |match|
14
- if emoji = Emoji.find_by_alias($1)
15
- %(
16
- <img
17
- alt="##{$1}"
18
- src="#{image_path("emoji/#{emoji.image_filename}")}"
19
- style="vertical-align:middle"
20
- width="20"
21
- height="20"
22
- />
23
- )
24
- else
25
- match
26
- end
27
- end.html_safe if content.present?
13
+ if content.present?
14
+ h(content).to_str.gsub(/:([\w+-]+):/) do |match|
15
+ if emoji = Emoji.find_by_alias($1)
16
+ %(
17
+ <img
18
+ alt="##{$1}"
19
+ src="#{image_path("emoji/#{emoji.image_filename}")}"
20
+ style="vertical-align:middle"
21
+ width="20"
22
+ height="20"
23
+ />
24
+ )
25
+ else
26
+ match
27
+ end
28
+ end.html_safe
29
+ end
28
30
  end
29
31
 
30
32
  def include_plugins(stack)
@@ -21,15 +21,20 @@ module Shipit
21
21
  def deploy_button(commit)
22
22
  url = new_stack_deploy_path(commit.stack, sha: commit.sha)
23
23
  classes = %W(btn btn--primary deploy-action #{commit.state})
24
+ deploy_state = commit.deploy_state(bypass_safeties?)
24
25
  data = {}
26
+
25
27
  if commit.deploy_disallowed?
26
28
  classes.push(bypass_safeties? ? 'btn--warning' : 'btn--disabled')
29
+ if deploy_state == 'blocked'
30
+ data[:tooltip] = t('deploy_button.hint.blocked')
31
+ end
27
32
  elsif commit.deploy_discouraged?
28
33
  classes.push('btn--warning')
29
34
  data[:tooltip] = t('deploy_button.hint.max_commits', maximum: commit.stack.maximum_commits_per_deploy)
30
35
  end
31
36
 
32
- link_to(t("deploy_button.caption.#{commit.deploy_state(bypass_safeties?)}"), url, class: classes, data: data)
37
+ link_to(t("deploy_button.caption.#{deploy_state}"), url, class: classes, data: data)
33
38
  end
34
39
 
35
40
  def github_change_url(commit)
@@ -26,8 +26,10 @@ module Shipit
26
26
  Shipit::Hook.where(stack_id: stack.id).delete_all
27
27
  Shipit::PullRequest.where(stack_id: stack.id).delete_all
28
28
  tasks_ids = Shipit::Task.where(stack_id: stack.id).pluck(:id)
29
- Shipit::OutputChunk.where(task_id: tasks_ids).delete_all
30
- Shipit::Task.where(id: tasks_ids).delete_all
29
+ tasks_ids.each_slice(100) do |ids|
30
+ Shipit::OutputChunk.where(task_id: ids).delete_all
31
+ Shipit::Task.where(id: ids).delete_all
32
+ end
31
33
  stack.destroy!
32
34
  end
33
35
  end
@@ -13,7 +13,7 @@ module Shipit
13
13
  rescue DeploySpec::Error
14
14
  end
15
15
 
16
- return unless sha.present?
16
+ return if sha.blank?
17
17
 
18
18
  begin
19
19
  stack.update_deployed_revision(sha)
@@ -54,7 +54,7 @@ module Shipit
54
54
  end
55
55
 
56
56
  def lookup_commit(sha)
57
- @stack.commits.find_by_sha(sha)
57
+ @stack.commits.find_by(sha: sha)
58
58
  end
59
59
  end
60
60
  end
@@ -8,15 +8,15 @@ module Shipit
8
8
  pull_requests.each do |pull_request|
9
9
  pull_request.refresh!
10
10
  pull_request.reject_unless_mergeable!
11
- if pull_request.closed?
12
- pull_request.merged_upstream? ? pull_request.complete! : pull_request.cancel!
13
- end
11
+ pull_request.cancel! if pull_request.closed?
12
+ pull_request.revalidate! if pull_request.need_revalidation?
14
13
  end
15
14
 
16
15
  return false unless stack.allows_merges?
17
16
 
18
17
  pull_requests.select(&:pending?).each do |pull_request|
19
18
  pull_request.refresh!
19
+ next unless pull_request.all_status_checks_passed?
20
20
  begin
21
21
  pull_request.merge!
22
22
  rescue PullRequest::NotReady
@@ -19,6 +19,9 @@ module Shipit
19
19
  checkout_repository
20
20
  perform_task
21
21
  @task.complete!
22
+ rescue Command::TimedOut => error
23
+ @task.write("\n#{error.message}\n")
24
+ @task.report_timeout!(error)
22
25
  rescue Command::Error => error
23
26
  @task.write("\n#{error.message}\n")
24
27
  @task.report_failure!(error)
@@ -3,6 +3,7 @@ module Shipit
3
3
  include BackgroundJob::Unique
4
4
 
5
5
  queue_as :low
6
+ on_duplicate :drop
6
7
 
7
8
  def perform(hook)
8
9
  hook.purge_old_deliveries!
@@ -20,7 +20,7 @@ module Shipit
20
20
 
21
21
  class << self
22
22
  def authenticate(token)
23
- find_by_id(message_verifier.verify(token).to_i)
23
+ find_by(id: message_verifier.verify(token).to_i)
24
24
  rescue Shipit::SimpleMessageVerifier::InvalidSignature
25
25
  end
26
26
 
@@ -23,7 +23,7 @@ module Shipit
23
23
 
24
24
  scope :reachable, -> { where(detached: false) }
25
25
 
26
- delegate :broadcast_update, :github_repo_name, :hidden_statuses, :required_statuses,
26
+ delegate :broadcast_update, :github_repo_name, :hidden_statuses, :required_statuses, :blocking_statuses,
27
27
  :soft_failing_statuses, to: :stack
28
28
 
29
29
  def self.newer_than(commit)
@@ -31,6 +31,11 @@ module Shipit
31
31
  where('id > ?', commit.try(:id) || commit)
32
32
  end
33
33
 
34
+ def self.older_than(commit)
35
+ return all unless commit
36
+ where('id < ?', commit.try(:id) || commit)
37
+ end
38
+
34
39
  def self.until(commit)
35
40
  return all unless commit
36
41
  where('id <= ?', commit.try(:id) || commit)
@@ -103,10 +108,18 @@ module Shipit
103
108
  @checks ||= CommitChecks.new(self)
104
109
  end
105
110
 
106
- delegate :pending?, :success?, :error?, :failure?, :state, to: :status
111
+ delegate :pending?, :success?, :error?, :failure?, :blocking?, :state, to: :status
107
112
 
108
113
  def deployable?
109
- !locked? && (success? || stack.ignore_ci?)
114
+ !locked? && (stack.ignore_ci? || (success? && !blocked?))
115
+ end
116
+
117
+ def blocked?
118
+ return false if stack.blocking_statuses.empty?
119
+
120
+ # TODO: Perfs might be horrible here if the range is big.
121
+ # We should look at fetching the undeployed commits only once
122
+ stack.commits.reachable.newer_than(stack.last_deployed_commit).older_than(self).any?(&:blocking?)
110
123
  end
111
124
 
112
125
  def children
@@ -121,7 +134,8 @@ module Shipit
121
134
  pull_request_number.present?
122
135
  end
123
136
 
124
- def pull_request_number # TODO: remove in a few versions when it is assumed the commits table was backfilled
137
+ # TODO: remove in a few versions when it is assumed the commits table was backfilled
138
+ def pull_request_number
125
139
  super || message_parser.pull_request_number
126
140
  end
127
141
 
@@ -133,7 +147,8 @@ module Shipit
133
147
  message.lines.first.strip
134
148
  end
135
149
 
136
- def pull_request_title # TODO: remove in a few versions when it is assumed the commits table was backfilled
150
+ # TODO: remove in a few versions when it is assumed the commits table was backfilled
151
+ def pull_request_title
137
152
  super || message_parser.pull_request_title
138
153
  end
139
154
 
@@ -183,7 +198,7 @@ module Shipit
183
198
 
184
199
  def identify_pull_request
185
200
  return unless message_parser.pull_request?
186
- if pull_request = stack.pull_requests.find_by_number(message_parser.pull_request_number)
201
+ if pull_request = stack.pull_requests.find_by(number: message_parser.pull_request_number)
187
202
  self.pull_request = pull_request
188
203
  self.pull_request_number = pull_request.number
189
204
  self.pull_request_title = pull_request.title
@@ -194,6 +209,14 @@ module Shipit
194
209
  self.pull_request_title = message_parser.pull_request_title unless self[:pull_request_title]
195
210
  end
196
211
 
212
+ def deploy_requested_at
213
+ if pull_request.try!(:merged?)
214
+ pull_request.merge_requested_at
215
+ else
216
+ created_at
217
+ end
218
+ end
219
+
197
220
  private
198
221
 
199
222
  def message_parser