shipit-engine 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -2
  3. data/app/assets/images/archive-solid.svg +1 -0
  4. data/app/assets/stylesheets/_pages/_stacks.scss +76 -0
  5. data/app/controllers/shipit/api/stacks_controller.rb +20 -1
  6. data/app/controllers/shipit/api_clients_controller.rb +49 -0
  7. data/app/controllers/shipit/merge_status_controller.rb +8 -4
  8. data/app/controllers/shipit/stacks_controller.rb +58 -9
  9. data/app/controllers/shipit/webhooks_controller.rb +2 -130
  10. data/app/helpers/shipit/stacks_helper.rb +4 -0
  11. data/app/jobs/shipit/background_job/unique.rb +3 -1
  12. data/app/jobs/shipit/continuous_delivery_job.rb +1 -0
  13. data/app/jobs/shipit/destroy_stack_job.rb +2 -2
  14. data/app/models/shipit/commit.rb +21 -9
  15. data/app/models/shipit/commit_deployment.rb +15 -11
  16. data/app/models/shipit/commit_deployment_status.rb +6 -2
  17. data/app/models/shipit/deploy.rb +48 -7
  18. data/app/models/shipit/deploy_stats.rb +57 -0
  19. data/app/models/shipit/repository.rb +38 -0
  20. data/app/models/shipit/stack.rb +41 -34
  21. data/app/models/shipit/task.rb +26 -4
  22. data/app/models/shipit/webhooks.rb +32 -0
  23. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +19 -0
  24. data/app/models/shipit/webhooks/handlers/handler.rb +40 -0
  25. data/app/models/shipit/webhooks/handlers/membership_handler.rb +45 -0
  26. data/app/models/shipit/webhooks/handlers/push_handler.rb +20 -0
  27. data/app/models/shipit/webhooks/handlers/status_handler.rb +26 -0
  28. data/app/serializers/shipit/stack_serializer.rb +6 -1
  29. data/app/validators/ascii_only_validator.rb +3 -3
  30. data/app/views/layouts/_head.html.erb +0 -0
  31. data/app/views/layouts/shipit.html.erb +4 -2
  32. data/app/views/shipit/api_clients/index.html.erb +36 -0
  33. data/app/views/shipit/api_clients/new.html.erb +33 -0
  34. data/app/views/shipit/api_clients/show.html.erb +35 -0
  35. data/app/views/shipit/merge_status/logged_out.erb +1 -1
  36. data/app/views/shipit/stacks/_header.html.erb +12 -7
  37. data/app/views/shipit/stacks/_links.html.erb +1 -0
  38. data/app/views/shipit/stacks/index.html.erb +7 -2
  39. data/app/views/shipit/stacks/settings.html.erb +19 -0
  40. data/app/views/shipit/stacks/statistics.html.erb +82 -0
  41. data/config/locales/en.yml +14 -2
  42. data/config/routes.rb +4 -0
  43. data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
  44. data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
  45. data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
  46. data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
  47. data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
  48. data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
  49. data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
  50. data/lib/shipit/github_app.rb +32 -3
  51. data/lib/shipit/task_commands.rb +10 -2
  52. data/lib/shipit/version.rb +1 -1
  53. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  54. data/test/controllers/api/stacks_controller_test.rb +14 -6
  55. data/test/controllers/api_clients_controller_test.rb +103 -0
  56. data/test/controllers/merge_status_controller_test.rb +21 -4
  57. data/test/controllers/stacks_controller_test.rb +35 -0
  58. data/test/controllers/webhooks_controller_test.rb +26 -0
  59. data/test/dummy/config/environments/development.rb +22 -4
  60. data/test/dummy/db/schema.rb +17 -6
  61. data/test/dummy/db/seeds.rb +20 -6
  62. data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
  63. data/test/fixtures/shipit/commit_deployments.yml +8 -8
  64. data/test/fixtures/shipit/commits.yml +23 -0
  65. data/test/fixtures/shipit/repositories.yml +23 -0
  66. data/test/fixtures/shipit/stacks.yml +100 -16
  67. data/test/fixtures/shipit/tasks.yml +66 -3
  68. data/test/jobs/destroy_stack_job_test.rb +9 -0
  69. data/test/models/commit_deployment_status_test.rb +33 -4
  70. data/test/models/commit_deployment_test.rb +8 -11
  71. data/test/models/commits_test.rb +22 -2
  72. data/test/models/deploy_stats_test.rb +112 -0
  73. data/test/models/deploys_test.rb +55 -17
  74. data/test/models/pull_request_test.rb +1 -1
  75. data/test/models/shipit/repository_test.rb +76 -0
  76. data/test/models/shipit/wehbooks/handlers_test.rb +26 -0
  77. data/test/models/stacks_test.rb +44 -51
  78. data/test/models/undeployed_commits_test.rb +13 -0
  79. data/test/test_helper.rb +3 -1
  80. data/test/unit/deploy_commands_test.rb +9 -0
  81. data/test/unit/github_app_test.rb +136 -0
  82. metadata +161 -128
@@ -0,0 +1,103 @@
1
+
2
+ require 'test_helper'
3
+
4
+ module Shipit
5
+ class ApiClientsControllerTest < ActionController::TestCase
6
+ setup do
7
+ @routes = Shipit::Engine.routes
8
+ @api_client = shipit_api_clients(:here_come_the_walrus)
9
+ session[:user_id] = shipit_users(:walrus).id
10
+ end
11
+
12
+ test "GitHub authentication is mandatory" do
13
+ session[:user_id] = nil
14
+ get :index
15
+ assert_redirected_to '/github/auth/github?origin=http%3A%2F%2Ftest.host%2Fapi_clients'
16
+ end
17
+
18
+ test "current_user must be a member of at least a Shipit.github_teams" do
19
+ session[:user_id] = shipit_users(:bob).id
20
+ Shipit.stubs(:github_teams).returns([shipit_teams(:cyclimse_cooks), shipit_teams(:shopify_developers)])
21
+ get :index
22
+ assert_response :forbidden
23
+ assert_equal(
24
+ 'You must be a member of cyclimse/cooks or shopify/developers to access this application.',
25
+ response.body,
26
+ )
27
+ end
28
+
29
+ test "#index is successful with a valid user" do
30
+ get :index
31
+ assert_response :ok
32
+ end
33
+
34
+ test "#new is success" do
35
+ get :new
36
+ assert_response :ok
37
+ end
38
+
39
+ test "#create creates a new api_client" do
40
+ assert_difference "ApiClient.count", +1 do
41
+ post :create, params: {
42
+ api_client: {
43
+ name: 'walrus_app',
44
+ permissions: [
45
+ 'read:stack',
46
+ 'lock:stack',
47
+ ],
48
+ },
49
+ }
50
+ end
51
+
52
+ assert_redirected_to api_client_path(ApiClient.last)
53
+ end
54
+
55
+ test "#create attaches the current user to the created api client" do
56
+ post :create, params: {
57
+ api_client: {
58
+ name: 'walrus_app',
59
+ permissions: [
60
+ 'read:stack',
61
+ 'lock:stack',
62
+ ],
63
+ },
64
+ }
65
+
66
+ assert_equal shipit_users(:walrus).id, ApiClient.last.creator.id
67
+ end
68
+
69
+ test "#create when not valid renders new" do
70
+ assert_no_difference "Stack.count" do
71
+ post :create, params: {api_client: {name: 'walrus_app', permissions: ['invalid']}}
72
+ end
73
+
74
+ assert flash[:warning]
75
+ assert_response :success
76
+ end
77
+
78
+ test "#show is success" do
79
+ get :show, params: {id: @api_client.id}
80
+ assert_response :ok
81
+ end
82
+
83
+ test "#update updates an existing api_client" do
84
+ new_permissions = [
85
+ 'read:stack',
86
+ 'lock:stack',
87
+ ]
88
+
89
+ assert_difference "ApiClient.count", 0 do
90
+ patch :update, params: {
91
+ id: @api_client.id,
92
+ api_client: {
93
+ permissions: new_permissions,
94
+ },
95
+ }
96
+ end
97
+ @api_client.reload
98
+
99
+ assert_redirected_to api_client_path(@api_client)
100
+ assert_equal new_permissions, @api_client.permissions
101
+ end
102
+ end
103
+ end
@@ -37,13 +37,11 @@ module Shipit
37
37
  test "GET show prefers stacks with merge_queue_enabled" do
38
38
  existing = shipit_stacks(:shipit)
39
39
  Shipit::Stack.where(
40
- repo_owner: existing.repo_owner,
41
- repo_name: existing.repo_name,
40
+ repository: existing.repository,
42
41
  ).update_all(merge_queue_enabled: false)
43
42
 
44
43
  Shipit::Stack.create(
45
- repo_owner: existing.repo_owner,
46
- repo_name: existing.repo_name,
44
+ repository: existing.repository,
47
45
  environment: 'foo',
48
46
  branch: existing.branch,
49
47
  merge_queue_enabled: true,
@@ -53,5 +51,24 @@ module Shipit
53
51
  assert_response :ok
54
52
  assert_includes response.body, 'shipit-engine/foo'
55
53
  end
54
+
55
+ test "GET show prefers locked stacks above all else" do
56
+ existing = shipit_stacks(:shipit)
57
+ Shipit::Stack.where(
58
+ repository: existing.repository,
59
+ ).update_all(lock_reason: 'testing', merge_queue_enabled: false, locked_since: Time.now.utc)
60
+
61
+ # Shipit would otherwise prefer this, because it has the merge queue enabled
62
+ Shipit::Stack.create(
63
+ repository: existing.repository,
64
+ environment: 'foo',
65
+ branch: existing.branch,
66
+ merge_queue_enabled: true,
67
+ )
68
+
69
+ get :show, params: {referrer: 'https://github.com/Shopify/shipit-engine/pull/42', branch: 'master'}
70
+ assert_response :ok
71
+ assert_includes response.body, 'locked'
72
+ end
56
73
  end
57
74
  end
@@ -110,6 +110,17 @@ module Shipit
110
110
  assert_response :success
111
111
  end
112
112
 
113
+ test "#statistics is success" do
114
+ get :statistics, params: {id: @stack.to_param}
115
+ assert_response :success
116
+ end
117
+
118
+ test "#statistics redirects to #show if no deploys are present" do
119
+ @stack.deploys.destroy_all
120
+ get :statistics, params: {id: @stack.to_param}
121
+ assert_redirected_to stack_path(@stack)
122
+ end
123
+
113
124
  test "#update allows to lock the stack" do
114
125
  refute @stack.locked?
115
126
 
@@ -129,6 +140,30 @@ module Shipit
129
140
  assert_instance_of AnonymousUser, @stack.lock_author
130
141
  end
131
142
 
143
+ test "#update allows to archive the stack" do
144
+ refute @stack.archived?
145
+ refute @stack.locked?
146
+
147
+ patch :update, params: {id: @stack.to_param, stack: {archived: "true"}}
148
+ @stack.reload
149
+ assert @stack.archived?
150
+ assert @stack.locked?
151
+ assert_equal shipit_users(:walrus), @stack.lock_author
152
+ assert_equal "Archived", @stack.lock_reason
153
+ end
154
+
155
+ test "#update allows to dearchive the stack" do
156
+ @stack.archive!(shipit_users(:walrus))
157
+ assert @stack.locked?
158
+ assert @stack.archived?
159
+
160
+ patch :update, params: {id: @stack.to_param, stack: {archived: "false"}}
161
+ @stack.reload
162
+ refute @stack.archived?
163
+ refute @stack.locked?
164
+ assert_instance_of AnonymousUser, @stack.lock_author
165
+ end
166
+
132
167
  test "#refresh queues a RefreshStatusesJob and a GithubSyncJob" do
133
168
  request.env['HTTP_REFERER'] = stack_settings_path(@stack)
134
169
 
@@ -7,6 +7,17 @@ module Shipit
7
7
  GithubHook.any_instance.stubs(:verify_signature).returns(true)
8
8
  end
9
9
 
10
+ test "create github repository which is not yet present in the datastore" do
11
+ request.headers['X-Github-Event'] = 'push'
12
+ unknown_repo_payload = JSON.parse(payload(:push_master))
13
+ unknown_repo_payload["repository"]["full_name"] = "owner/unknown-repository"
14
+ unknown_repo_payload = unknown_repo_payload.to_json
15
+
16
+ assert_nothing_raised do
17
+ post :create, body: unknown_repo_payload, as: :json
18
+ end
19
+ end
20
+
10
21
  test ":push with the target branch queues a GithubSyncJob" do
11
22
  request.headers['X-Github-Event'] = 'push'
12
23
 
@@ -142,8 +153,23 @@ module Shipit
142
153
  end
143
154
  end
144
155
 
156
+ test "other events trigger custom handlers" do
157
+ event = 'pull_request'
158
+ mock_handler = mock
159
+ mock_handler.expects(:call).with(pull_request_params.stringify_keys).once
160
+ Shipit::Webhooks.register_handler(event, mock_handler)
161
+
162
+ @request.headers['X-Github-Event'] = event
163
+ post :create, body: pull_request_params.to_json, as: :json
164
+ assert_response :ok
165
+ end
166
+
145
167
  private
146
168
 
169
+ def pull_request_params
170
+ {action: 'opened', number: 2, pull_request: 'foobar'}
171
+ end
172
+
147
173
  def membership_params
148
174
  {action: 'added', team: team_params, organization: {login: 'shopify'}, member: {login: 'walrus'}}
149
175
  end
@@ -9,9 +9,24 @@ Rails.application.configure do
9
9
  # Do not eager load code on boot.
10
10
  config.eager_load = false
11
11
 
12
- # Show full error reports and disable caching.
13
- config.consider_all_requests_local = true
14
- config.action_controller.perform_caching = false
12
+ # Show full error reports
13
+ config.consider_all_requests_local = true
14
+
15
+ # Enable/disable caching. By default caching is disabled.
16
+ # Run rails dev:cache to toggle caching.
17
+ if Shipit::Engine.root.join("tmp", "caching-dev.txt").exist?
18
+ config.action_controller.perform_caching = true
19
+ config.action_controller.enable_fragment_cache_logging = true
20
+
21
+ config.cache_store = :memory_store
22
+ config.public_file_server.headers = {
23
+ "Cache-Control" => "public, max-age=#{2.days.to_i}",
24
+ }
25
+ else
26
+ config.action_controller.perform_caching = false
27
+
28
+ config.cache_store = :null_store
29
+ end
15
30
 
16
31
  # Don't care if the mailer can't send.
17
32
  config.action_mailer.raise_delivery_errors = false
@@ -39,8 +54,11 @@ Rails.application.configure do
39
54
  # Raises error for missing translations
40
55
  # config.action_view.raise_on_missing_translations = true
41
56
  if Rails.application.config_for(:database)['adapter'] == 'sqlite3'
42
- Shipit::DeferredTouch.enabled = false
57
+ config.to_prepare do
58
+ Shipit::DeferredTouch.enabled = false
59
+ end
43
60
  end
61
+
44
62
  config.active_job.queue_adapter = :async
45
63
 
46
64
  Pubsubstub.use_persistent_connections = false
@@ -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: 2019_05_02_020249) do
13
+ ActiveRecord::Schema.define(version: 2020_01_09_132519) do
14
14
 
15
15
  create_table "api_clients", force: :cascade do |t|
16
16
  t.text "permissions", limit: 65535
@@ -55,6 +55,7 @@ ActiveRecord::Schema.define(version: 2019_05_02_020249) do
55
55
  t.string "api_url"
56
56
  t.datetime "created_at", null: false
57
57
  t.datetime "updated_at", null: false
58
+ t.string "sha", limit: 40
58
59
  t.index ["commit_id", "task_id"], name: "index_commit_deployments_on_commit_id_and_task_id", unique: true
59
60
  t.index ["task_id"], name: "index_commit_deployments_on_task_id"
60
61
  end
@@ -190,9 +191,15 @@ ActiveRecord::Schema.define(version: 2019_05_02_020249) do
190
191
  t.index ["user_id"], name: "index_deploy_statuses_on_user_id"
191
192
  end
192
193
 
194
+ create_table "repositories", force: :cascade do |t|
195
+ t.string "owner", limit: 39, null: false
196
+ t.string "name", limit: 100, null: false
197
+ t.datetime "created_at", precision: 6, null: false
198
+ t.datetime "updated_at", precision: 6, null: false
199
+ t.index ["owner", "name"], name: "repository_unicity", unique: true
200
+ end
201
+
193
202
  create_table "stacks", force: :cascade do |t|
194
- t.string "repo_name", limit: 100, null: false
195
- t.string "repo_owner", limit: 39, null: false
196
203
  t.string "environment", limit: 50, default: "production", null: false
197
204
  t.datetime "created_at"
198
205
  t.datetime "updated_at"
@@ -211,7 +218,11 @@ ActiveRecord::Schema.define(version: 2019_05_02_020249) do
211
218
  t.datetime "locked_since"
212
219
  t.boolean "merge_queue_enabled", default: false, null: false
213
220
  t.datetime "last_deployed_at"
214
- t.index ["repo_owner", "repo_name", "environment"], name: "stack_unicity", unique: true
221
+ t.integer "repository_id", null: false
222
+ t.datetime "archived_since"
223
+ t.index ["archived_since"], name: "index_stacks_on_archived_since"
224
+ t.index ["repository_id", "environment"], name: "stack_unicity", unique: true
225
+ t.index ["repository_id"], name: "index_stacks_on_repository_id"
215
226
  end
216
227
 
217
228
  create_table "statuses", force: :cascade do |t|
@@ -228,8 +239,8 @@ ActiveRecord::Schema.define(version: 2019_05_02_020249) do
228
239
 
229
240
  create_table "tasks", force: :cascade do |t|
230
241
  t.integer "stack_id", limit: 4, null: false
231
- t.integer "since_commit_id", limit: 4, null: false
232
- t.integer "until_commit_id", limit: 4, null: false
242
+ t.integer "since_commit_id", limit: 4
243
+ t.integer "until_commit_id", limit: 4
233
244
  t.string "status", limit: 10, default: "pending", null: false
234
245
  t.datetime "created_at"
235
246
  t.datetime "updated_at"
@@ -14,6 +14,8 @@ module Shipit
14
14
  Commit.send(:define_method, :refresh_statuses!) {}
15
15
  Commit.send(:define_method, :refresh_check_runs!) {}
16
16
  ReleaseStatus.send(:define_method, :create_status_on_github!) {}
17
+ CommitDeployment.send(:define_method, :schedule_create_on_github) {}
18
+ CommitDeploymentStatus.send(:define_method, :schedule_create_on_github) {}
17
19
 
18
20
  users = 3.times.map do
19
21
  User.create!(
@@ -25,8 +27,10 @@ module Shipit
25
27
 
26
28
  stacks = 3.times.map do
27
29
  Stack.create!(
28
- repo_name: Faker::Internet.domain_name.parameterize,
29
- repo_owner: Faker::Company.name.parameterize,
30
+ repository: Repository.find_or_create_by(
31
+ name: Faker::Internet.domain_name.parameterize,
32
+ owner: Faker::Company.name.parameterize
33
+ ),
30
34
  deploy_url: "https://#{Faker::Internet.domain_name.parameterize}.#{Faker::Internet.domain_suffix}/",
31
35
  cached_deploy_spec: DeploySpec.load(%(
32
36
  {
@@ -195,9 +199,19 @@ module Shipit
195
199
  )
196
200
  write_output(rollback)
197
201
 
198
- task = stack.tasks.create!(
199
- since_commit_id: stack.last_deployed_commit.id,
200
- until_commit_id: stack.last_deployed_commit.id,
202
+ provision_task = stack.tasks.create!(
203
+ status: "pending",
204
+ user: users.sample,
205
+ definition: TaskDefinition.new('provision',
206
+ 'action' => 'Provision some resources',
207
+ 'description' => 'Provisions servers for the application to be deployed to',
208
+ 'steps' => ['./provision.sh'],
209
+ ),
210
+ started_at: Random.rand(15.minutes.to_i).seconds.ago,
211
+ )
212
+ write_output(provision_task)
213
+
214
+ restart_task = stack.tasks.create!(
201
215
  status: "success",
202
216
  user: users.sample,
203
217
  definition: TaskDefinition.new('restart',
@@ -208,6 +222,6 @@ module Shipit
208
222
  started_at: Random.rand(15.minutes.to_i).seconds.ago,
209
223
  ended_at: Time.now.utc,
210
224
  )
211
- write_output(rollback)
225
+ write_output(restart_task)
212
226
  end
213
227
  end
@@ -1,6 +1,6 @@
1
- shipit_deploy_second_pending:
1
+ shipit_deploy_second_in_progress:
2
2
  commit_deployment: shipit_deploy_second
3
- status: pending
3
+ status: in_progress
4
4
  github_id: 42
5
5
  api_url: https://api.github.com/repos/shopify/shipit-engine/deployments/1/statuses/42
6
6
 
@@ -10,9 +10,9 @@ shipit_deploy_second_success:
10
10
  github_id: 43
11
11
  api_url: https://api.github.com/repos/shopify/shipit-engine/deployments/1/statuses/43
12
12
 
13
- shipit2_deploy_third_pending:
13
+ shipit2_deploy_third_in_progress:
14
14
  commit_deployment: shipit2_deploy_third
15
- status: pending
15
+ status: in_progress
16
16
 
17
17
  shipit2_deploy_third_failure:
18
18
  commit_deployment: shipit2_deploy_third
@@ -1,37 +1,37 @@
1
1
  shipit_deploy_second:
2
- commit_id: 2 # second
2
+ sha: f890fd8b5f2be05d1fedb763a3605ee461c39074 # second
3
3
  task_id: 1 # shipit
4
4
  api_url: https://api.github.com/repos/shopify/shipit-engine/deployments/1
5
5
  github_id: 1
6
6
 
7
7
  shipit2_deploy_third:
8
- commit_id: 3 # third
8
+ sha: 367578b362bf2b4df5903e1c7960929361c39074 # third
9
9
  task_id: 2 # shipit2
10
10
  api_url: https://api.github.com/repos/shopify/shipit-engine/deployments/2
11
11
  github_id: 2
12
12
 
13
13
  shipit_pending_third:
14
- commit_id: 3 # third
14
+ sha: 367578b362bf2b4df5903e1c7960929361c39074 # third
15
15
  task_id: 4 # shipit_pending
16
16
  api_url: https://api.github.com/repos/shopify/shipit-engine/deployments/3
17
17
  github_id: 3
18
18
 
19
19
  shipit_pending_fourth:
20
- commit_id: 4 # fourth
20
+ sha: 467578b362bf2b4df5903e1c7960929361c3435a # fourth
21
21
  task_id: 4 # shipit_pending
22
22
 
23
23
  shipit_running_fourth:
24
- commit_id: 4 # fourth
24
+ sha: 467578b362bf2b4df5903e1c7960929361c3435a # fourth
25
25
  task_id: 5 # shipit_running
26
26
 
27
27
  shipit_complete_fourth:
28
- commit_id: 4 # fourth
28
+ sha: 467578b362bf2b4df5903e1c7960929361c3435a # fourth
29
29
  task_id: 6 # shipit_complete
30
30
 
31
31
  shipit_aborted_fourth:
32
- commit_id: 4 # fourth
32
+ sha: 467578b362bf2b4df5903e1c7960929361c3435a # fourth
33
33
  task_id: 7 # shipit_aborted
34
34
 
35
35
  shipit_rollback_fourth:
36
- commit_id: 4 # fourth
36
+ sha: 467578b362bf2b4df5903e1c7960929361c3435a # fourth
37
37
  task_id: 8 # shipit_rollback