shipit-engine 0.20.0 → 0.21.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 (110) 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/bundler_discovery.rb +1 -10
  28. data/app/models/shipit/deploy_spec/file_system.rb +6 -0
  29. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -1
  30. data/app/models/shipit/deploy_spec/lerna_discovery.rb +85 -0
  31. data/app/models/shipit/deploy_spec/npm_discovery.rb +103 -5
  32. data/app/models/shipit/deploy_spec/pypi_discovery.rb +4 -2
  33. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +4 -2
  34. data/app/models/shipit/deploy_spec.rb +19 -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 +37 -16
  40. data/app/models/shipit/stack.rb +17 -11
  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/command.rb +11 -3
  63. data/lib/shipit/engine.rb +0 -4
  64. data/lib/shipit/stack_commands.rb +3 -1
  65. data/lib/shipit/version.rb +1 -1
  66. data/lib/shipit.rb +15 -23
  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 +7 -1
  97. data/test/models/pull_request_test.rb +78 -24
  98. data/test/models/stacks_test.rb +23 -19
  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 -224
  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
  110. data/test/dummy/db/test.sqlite3-journal +0 -0
@@ -70,6 +70,7 @@ module Shipit
70
70
  @task.reload
71
71
  assert_response :success
72
72
  assert_equal 'aborting', @task.reload.status
73
+ assert_equal shipit_users(:walrus).id, @task.aborted_by_id
73
74
  assert_equal false, @task.rollback_once_aborted?
74
75
  end
75
76
 
@@ -81,6 +82,7 @@ module Shipit
81
82
  @task.reload
82
83
  assert_response :success
83
84
  assert_equal 'aborting', @task.status
85
+ assert_equal shipit_users(:walrus).id, @task.aborted_by_id
84
86
  assert_equal true, @task.rollback_once_aborted?
85
87
  end
86
88
 
@@ -150,13 +150,6 @@ module Shipit
150
150
  end
151
151
  end
152
152
 
153
- test ":membership can append a user membership for an organization with capitals" do
154
- assert_difference -> { Membership.count }, 1 do
155
- post :membership, params: membership_params.merge(organization: {login: 'Shopify'}, member: {login: 'bob'})
156
- assert_response :ok
157
- end
158
- end
159
-
160
153
  private
161
154
 
162
155
  def membership_params
@@ -15,5 +15,3 @@ test:
15
15
  # -
16
16
  # -
17
17
  redis_url: "redis://127.0.0.1:6379/7"
18
- # features:
19
- # - bootstrap
Binary file
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20170629141736) do
13
+ ActiveRecord::Schema.define(version: 20180202220850) do
14
14
 
15
15
  create_table "api_clients", force: :cascade do |t|
16
16
  t.text "permissions", limit: 65535
@@ -80,9 +80,8 @@ ActiveRecord::Schema.define(version: 20170629141736) do
80
80
  t.datetime "delivered_at"
81
81
  t.datetime "created_at", null: false
82
82
  t.datetime "updated_at", null: false
83
- t.index ["created_at"], name: "index_deliveries_on_created_at"
84
83
  t.index ["hook_id", "event", "status"], name: "index_deliveries_on_hook_id_and_event_and_status"
85
- t.index ["status", "event"], name: "index_deliveries_on_status_and_event"
84
+ t.index ["hook_id", "status"], name: "index_deliveries_on_hook_id_and_status"
86
85
  end
87
86
 
88
87
  create_table "github_hooks", force: :cascade do |t|
@@ -148,6 +147,8 @@ ActiveRecord::Schema.define(version: 20170629141736) do
148
147
  t.string "branch"
149
148
  t.datetime "revalidated_at"
150
149
  t.datetime "merged_at"
150
+ t.string "base_ref", limit: 1024
151
+ t.integer "base_commit_id"
151
152
  t.index ["head_id"], name: "index_pull_requests_on_head_id"
152
153
  t.index ["merge_requested_by_id"], name: "index_pull_requests_on_merge_requested_by_id"
153
154
  t.index ["merge_status"], name: "index_pull_requests_on_merge_status"
@@ -215,6 +216,7 @@ ActiveRecord::Schema.define(version: 20170629141736) do
215
216
  t.datetime "started_at"
216
217
  t.datetime "ended_at"
217
218
  t.boolean "ignored_safeties", default: false, null: false
219
+ t.integer "aborted_by_id"
218
220
  t.index ["rolled_up", "created_at", "status"], name: "index_tasks_on_rolled_up_and_created_at_and_status"
219
221
  t.index ["since_commit_id"], name: "index_tasks_on_since_commit_id"
220
222
  t.index ["stack_id", "allow_concurrency", "status"], name: "index_active_tasks"
Binary file
@@ -102,3 +102,56 @@ shipit_pending_pr_head:
102
102
  deletions: 24
103
103
  updated_at: <%= 8.days.ago.to_s(:db) %>
104
104
  detached: true
105
+
106
+ cyclimse_merged:
107
+ id: 9
108
+ sha: 772b4795871e4a45aa17d53f02aa45d0
109
+ message: "Merge pull request #66 from cyclimse/yoloshipit\n\nyoloshipit!"
110
+ stack: cyclimse
111
+ author: walrus
112
+ committer: walrus
113
+ authored_at: <%= 4.days.ago.to_s(:db) %>
114
+ committed_at: <%= 3.days.ago.to_s(:db) %>
115
+ additions: 420
116
+ deletions: 342
117
+ updated_at: <%= 8.days.ago.to_s(:db) %>
118
+ pull_request_id: 99
119
+
120
+ soc_first:
121
+ id: 101
122
+ sha: 5e9278037b872fd9a6690523e411ecb3aa181355
123
+ message: "lets go"
124
+ stack: soc
125
+ author: walrus
126
+ committer: walrus
127
+ authored_at: <%= 10.days.ago.to_s(:db) %>
128
+ committed_at: <%= 9.days.ago.to_s(:db) %>
129
+ additions: 42
130
+ deletions: 24
131
+ updated_at: <%= 8.days.ago.to_s(:db) %>
132
+
133
+ soc_second:
134
+ id: 102
135
+ sha: e790fd8b5f2be05d1fedb763a3605ee461c39074
136
+ message: "sheep it!"
137
+ stack: soc
138
+ author: walrus
139
+ committer: walrus
140
+ authored_at: <%= 8.days.ago.to_s(:db) %>
141
+ committed_at: <%= 7.days.ago.to_s(:db) %>
142
+ additions: 1
143
+ deletions: 1
144
+ updated_at: <%= 8.days.ago.to_s(:db) %>
145
+
146
+ soc_third:
147
+ id: 103
148
+ sha: 477578b362bf2b4df5903e1c7960929361c39074
149
+ message: "fix it!"
150
+ stack: soc
151
+ author: walrus
152
+ committer: walrus
153
+ authored_at: <%= 6.days.ago.to_s(:db) %>
154
+ committed_at: <%= 5.days.ago.to_s(:db) %>
155
+ additions: 12
156
+ deletions: 64
157
+ updated_at: <%= 8.days.ago.to_s(:db) %>
@@ -87,3 +87,55 @@ shipit_pending_merged:
87
87
  mergeable: null
88
88
  additions: 23
89
89
  deletions: 43
90
+
91
+ shipit_mergeable_pending_ci:
92
+ stack: shipit
93
+ number: 67
94
+ title: Super duper nice feature
95
+ merge_status: pending
96
+ merge_requested_at: <%= 5.minute.ago.to_s(:db) %>
97
+ revalidated_at: <%= 5.minute.ago.to_s(:db) %>
98
+ merge_requested_by: walrus
99
+ github_id: 424244242424241
100
+ api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/67
101
+ state: open
102
+ branch: feature-67
103
+ head_id: 4
104
+ mergeable: true
105
+ additions: 23
106
+ deletions: 43
107
+
108
+ shipit_pending_expired:
109
+ stack: shipit
110
+ number: 68
111
+ title: Noice !
112
+ merge_status: pending
113
+ merge_requested_at: <%= 50.minute.ago.to_s(:db) %>
114
+ revalidated_at: <%= 25.minute.ago.to_s(:db) %>
115
+ merge_requested_by: walrus
116
+ github_id: 48484848484848
117
+ api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/68
118
+ state: open
119
+ branch: feature-68
120
+ head_id: 8
121
+ mergeable: true
122
+ additions: 23
123
+ deletions: 43
124
+
125
+ cyclimse_pending_merged:
126
+ id: 99
127
+ stack: cyclimse
128
+ number: 66
129
+ merge_status: merged
130
+ merge_requested_by: walrus
131
+ merge_requested_at: <%= 3.minute.ago.to_s(:db) %>
132
+ merged_at: <%= 2.minute.ago.to_s(:db) %>
133
+ revalidated_at: <%= 3.minute.ago.to_s(:db) %>
134
+ github_id: 43243243243232
135
+ api_url: https://api.github.com/repos/shopify/cyclimse/pulls/66
136
+ state: closed
137
+ branch: feature-64
138
+ head_id: 8
139
+ mergeable: null
140
+ additions: 23
141
+ deletions: 43
@@ -123,3 +123,38 @@ undeployed_stack:
123
123
  }
124
124
  }
125
125
  updated_at: <%= 8.days.ago.to_s(:db) %>
126
+
127
+ soc:
128
+ repo_owner: "shopify"
129
+ repo_name: "soc"
130
+ environment: "production"
131
+ branch: master
132
+ tasks_count: 0
133
+ undeployed_commits_count: 2
134
+ cached_deploy_spec: >
135
+ {
136
+ "machine": {"environment": {}},
137
+ "review": {
138
+ "checklist": ["foo", "bar", "baz"],
139
+ "monitoring": [
140
+ {"image": "https://example.com/monitor.png", "width": 200, "height": 300}
141
+ ]
142
+ },
143
+ "dependencies": {"override": []},
144
+ "deploy": {"override": null},
145
+ "rollback": {"override": ["echo 'Rollback!'"]},
146
+ "fetch": ["echo '42'"],
147
+ "tasks": {
148
+ "restart": {
149
+ "action": "Restart application",
150
+ "description": "Restart app and job servers",
151
+ "steps": [
152
+ "cap $ENVIRONMENT deploy:restart"
153
+ ]
154
+ }
155
+ },
156
+ "ci": {
157
+ "blocking": ["soc/compliance"]
158
+ }
159
+ }
160
+ updated_at: <%= 8.days.ago.to_s(:db) %>
@@ -98,3 +98,30 @@ shipit_pending_pr_success_travis:
98
98
  created_at: <%= 9.days.ago.to_s(:db) %>
99
99
  state: success
100
100
  target_url: "http://www.example.com"
101
+
102
+ soc_first:
103
+ stack: soc
104
+ commit_id: 101 # soc_first
105
+ description: Woops
106
+ context: soc/compliance
107
+ created_at: <%= 9.days.ago.to_s(:db) %>
108
+ state: failure
109
+ target_url: "http://www.example.com"
110
+
111
+ soc_second:
112
+ stack: soc
113
+ commit_id: 102 # soc_second
114
+ description: All good
115
+ context: soc/compliance
116
+ created_at: <%= 7.days.ago.to_s(:db) %>
117
+ state: success
118
+ target_url: "http://www.example.com"
119
+
120
+ soc_third:
121
+ stack: soc
122
+ commit_id: 103 # soc_third
123
+ description: All good
124
+ context: soc/compliance
125
+ created_at: <%= 7.days.ago.to_s(:db) %>
126
+ state: success
127
+ target_url: "http://www.example.com"
@@ -120,3 +120,17 @@ shipit_rollback:
120
120
  created_at: <%= (60 - 8).minutes.ago.to_s(:db) %>
121
121
  started_at: <%= (60 - 8).minutes.ago.to_s(:db) %>
122
122
  ended_at: <%= (60 - 7).minutes.ago.to_s(:db) %>
123
+
124
+ soc_deploy:
125
+ id: 9
126
+ user: walrus
127
+ since_commit_id: 101 # soc_first
128
+ until_commit_id: 101 # soc_first
129
+ type: Shipit::Deploy
130
+ stack: soc
131
+ status: success
132
+ additions: 1
133
+ deletions: 1
134
+ created_at: <%= (60 - 1).minutes.ago.to_s(:db) %>
135
+ started_at: <%= (60 - 1).minutes.ago.to_s(:db) %>
136
+ ended_at: <%= (60 - 3).minutes.ago.to_s(:db) %>
@@ -50,7 +50,7 @@ module QueriesHelper
50
50
 
51
51
  # FIXME: this seems bad. we should probably have a better way to indicate
52
52
  # the query was cached
53
- return if 'CACHE' == values[:name] || ignore.any? { |x| x =~ sql }
53
+ return if values[:name] == 'CACHE' || ignore.any? { |x| x =~ sql }
54
54
  log << sql
55
55
  end
56
56
  end
@@ -11,6 +11,8 @@ module Shipit
11
11
  @not_ready_pr = shipit_pull_requests(:shipit_pending_not_mergeable_yet)
12
12
  @closed_pr = shipit_pull_requests(:shipit_pending_closed)
13
13
  @merged_pr = shipit_pull_requests(:shipit_pending_merged)
14
+ @expired_pr = shipit_pull_requests(:shipit_pending_expired)
15
+ @mergable_pending_ci = shipit_pull_requests(:shipit_mergeable_pending_ci)
14
16
  end
15
17
 
16
18
  test "#perform rejects unmergeable PRs and merge the others" do
@@ -50,6 +52,13 @@ module Shipit
50
52
  assert_predicate @pending_pr.reload, :pending?
51
53
  end
52
54
 
55
+ test "#perform revalidate PRs but do not attempt to merge any if the stack doesn't allow merges" do
56
+ PullRequest.any_instance.stubs(:refresh!)
57
+ @stack.update!(lock_reason: 'Maintenance')
58
+ @job.perform(@stack)
59
+ assert_predicate @expired_pr.reload, :revalidating?
60
+ end
61
+
53
62
  test "#perform schedules a new job if the first PR in the queue is not mergeable yet" do
54
63
  PullRequest.any_instance.stubs(:refresh!)
55
64
 
@@ -67,11 +76,19 @@ module Shipit
67
76
  assert_predicate @closed_pr.reload, :canceled?
68
77
  end
69
78
 
70
- test "#perform completes merge requests for already merged PRs" do
79
+ test "#perform cancels merge requests for manually merged PRs" do
80
+ @pending_pr.cancel!
81
+ PullRequest.any_instance.stubs(:refresh!)
82
+ @job.perform(@stack)
83
+ assert_predicate @merged_pr.reload, :canceled?
84
+ end
85
+
86
+ test "#perform does not reject pull requests with pending statuses" do
71
87
  @pending_pr.cancel!
72
88
  PullRequest.any_instance.stubs(:refresh!)
73
89
  @job.perform(@stack)
74
- assert_predicate @merged_pr.reload, :merged?
90
+ refute_predicate @mergable_pending_ci.reload, :rejected?
91
+ refute_predicate @mergable_pending_ci.reload, :merged?
75
92
  end
76
93
  end
77
94
  end
@@ -67,15 +67,39 @@ module Shipit
67
67
  @job.perform(@deploy)
68
68
  end
69
69
 
70
- test "mark deploy as error if a command timeout" do
71
- Command.any_instance.expects(:stream!).at_least_once.raises(Command::TimedOut)
70
+ test "mark deploy as error an unexpected exception is raised" do
71
+ Command.any_instance.expects(:stream!).at_least_once.raises(Command::Denied)
72
72
 
73
73
  @job.perform(@deploy)
74
74
 
75
75
  assert_equal 'failed', @deploy.reload.status
76
+ assert_includes @deploy.chunk_output, 'Denied'
77
+ end
78
+
79
+ test "mark deploy as timedout if a command timeout" do
80
+ Command.any_instance.expects(:stream!).at_least_once.raises(Command::TimedOut)
81
+
82
+ @job.perform(@deploy)
83
+
84
+ assert_equal 'timedout', @deploy.reload.status
76
85
  assert_includes @deploy.chunk_output, 'TimedOut'
77
86
  end
78
87
 
88
+ test "mark deploy as timedout if a command exit in one of the codes in Shipit.timeout_exit_codes" do
89
+ previous_exit_codes = Shipit.timeout_exit_codes
90
+ begin
91
+ Shipit.timeout_exit_codes = [70].freeze
92
+
93
+ Command.any_instance.expects(:stream!).at_least_once.raises(Command::Failed.new('Blah', 70))
94
+
95
+ @job.perform(@deploy)
96
+
97
+ assert_equal 'timedout', @deploy.reload.status
98
+ ensure
99
+ Shipit.timeout_exit_codes = previous_exit_codes
100
+ end
101
+ end
102
+
79
103
  test "records stack support for rollbacks and fetching deployed revision" do
80
104
  @job.stubs(:capture!)
81
105
  @commands = stub(:commands)
@@ -148,8 +148,8 @@ module Shipit
148
148
  author: walrus,
149
149
  committer: walrus,
150
150
  sha: "ab12",
151
- authored_at: DateTime.now,
152
- committed_at: DateTime.now,
151
+ authored_at: Time.now,
152
+ committed_at: Time.now,
153
153
  message: "more fish!",
154
154
  )
155
155
  end
@@ -176,8 +176,8 @@ module Shipit
176
176
  author: walrus,
177
177
  committer: walrus,
178
178
  sha: "ab12",
179
- authored_at: DateTime.now,
180
- committed_at: DateTime.now,
179
+ authored_at: Time.now,
180
+ committed_at: Time.now,
181
181
  message: "more fish!",
182
182
  )
183
183
  @stack.reload
@@ -217,8 +217,8 @@ module Shipit
217
217
  author: walrus,
218
218
  committer: walrus,
219
219
  sha: "ab12",
220
- authored_at: DateTime.now,
221
- committed_at: DateTime.now,
220
+ authored_at: Time.now,
221
+ committed_at: Time.now,
222
222
  message: "more fish!",
223
223
  )
224
224
  stack.reload
@@ -306,6 +306,37 @@ module Shipit
306
306
  refute_predicate commit, :deployable?
307
307
  end
308
308
 
309
+ test "#deployable? is false if a blocking status is missing on a previous undeployed commit" do
310
+ blocking_commit = shipit_commits(:soc_second)
311
+ blocking_commit.statuses.delete_all
312
+
313
+ assert_predicate blocking_commit, :pending?
314
+ assert_predicate blocking_commit, :blocking?
315
+
316
+ commit = shipit_commits(:soc_third)
317
+ refute_predicate commit, :deployable?
318
+ end
319
+
320
+ test "#deployable? is false if a blocking status is failing on a previous undeployed commit" do
321
+ blocking_commit = shipit_commits(:soc_second)
322
+ blocking_commit.statuses.update_all(state: 'failure')
323
+
324
+ assert_predicate blocking_commit, :failure?
325
+ assert_predicate blocking_commit, :blocking?
326
+
327
+ commit = shipit_commits(:soc_third)
328
+ refute_predicate commit, :deployable?
329
+ end
330
+
331
+ test "#deployable? is true if no blocking status is failing or missing on a previous undeployed commit" do
332
+ blocking_commit = shipit_commits(:soc_second)
333
+ assert_predicate blocking_commit, :success?
334
+ refute_predicate blocking_commit, :blocking?
335
+
336
+ commit = shipit_commits(:soc_third)
337
+ assert_predicate commit, :deployable?
338
+ end
339
+
309
340
  expected_webhook_transitions = { # we expect deployable_status to fire on these transitions, and not on any others
310
341
  'unknown' => %w(pending success failure error),
311
342
  'pending' => %w(success failure error),
@@ -551,6 +582,24 @@ module Shipit
551
582
  assert revert.revert_of?(commit)
552
583
  end
553
584
 
585
+ test "deploy_requested_at defaults to commit created_at" do
586
+ assert_equal @commit.deploy_requested_at, @commit.created_at
587
+ end
588
+
589
+ test "when merged via the queue, deploy_requested_at is merge_requested_at" do
590
+ commit = shipit_commits(:cyclimse_merged)
591
+ assert_predicate commit, :pull_request?
592
+ assert_equal commit.pull_request, shipit_pull_requests(:cyclimse_pending_merged)
593
+ assert_equal commit.deploy_requested_at, commit.pull_request.merge_requested_at
594
+ end
595
+
596
+ test "when merged manually after being queued, deploy_requested_at is created_at" do
597
+ pr = shipit_pull_requests(:cyclimse_pending_merged)
598
+ pr.cancel!
599
+ commit = shipit_commits(:cyclimse_merged)
600
+ assert_equal commit.deploy_requested_at, commit.created_at
601
+ end
602
+
554
603
  private
555
604
 
556
605
  def expect_event(stack)