shipit-engine 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/assets/images/caret-down.svg +1 -0
  4. data/app/assets/javascripts/shipit/stacks.js.coffee +10 -0
  5. data/app/assets/stylesheets/_base/_banner.scss +7 -3
  6. data/app/assets/stylesheets/_base/_base.scss +0 -74
  7. data/app/assets/stylesheets/_base/_buttons.scss +7 -3
  8. data/app/assets/stylesheets/_base/_colors.scss +2 -0
  9. data/app/assets/stylesheets/_base/_icons.scss +8 -0
  10. data/app/assets/stylesheets/_base/_spacing.scss +21 -0
  11. data/app/assets/stylesheets/_pages/_commits.scss +41 -3
  12. data/app/assets/stylesheets/_structure/_layout.scss +8 -35
  13. data/app/assets/stylesheets/_structure/_main.scss +2 -2
  14. data/app/assets/stylesheets/_structure/_navigation.scss +89 -0
  15. data/app/assets/stylesheets/shipit.scss +3 -0
  16. data/app/controllers/concerns/shipit/api/rendering.rb +3 -6
  17. data/app/controllers/shipit/api/ccmenu_controller.rb +4 -0
  18. data/app/controllers/shipit/commits_controller.rb +18 -0
  19. data/app/jobs/shipit/destroy_stack_job.rb +25 -0
  20. data/app/jobs/shipit/github_sync_job.rb +12 -1
  21. data/app/jobs/shipit/merge_pull_requests_job.rb +3 -0
  22. data/app/models/shipit/commit.rb +14 -2
  23. data/app/models/shipit/deploy.rb +5 -0
  24. data/app/models/shipit/deploy_spec.rb +9 -6
  25. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +24 -2
  26. data/app/models/shipit/pull_request.rb +16 -1
  27. data/app/models/shipit/stack.rb +11 -4
  28. data/app/models/shipit/task_definition.rb +4 -0
  29. data/app/models/shipit/team.rb +1 -1
  30. data/app/models/shipit/undeployed_commit.rb +1 -2
  31. data/app/models/shipit/user.rb +1 -1
  32. data/app/models/shipit/variable_definition.rb +5 -0
  33. data/app/serializers/shipit/stack_serializer.rb +1 -1
  34. data/app/views/layouts/shipit.html.erb +2 -1
  35. data/app/views/shipit/commits/_commit.html.erb +9 -1
  36. data/app/views/shipit/deploys/rollback.html.erb +1 -1
  37. data/app/views/shipit/stacks/_header.html.erb +15 -3
  38. data/app/views/shipit/stacks/settings.html.erb +1 -1
  39. data/config/locales/en.yml +9 -5
  40. data/config/routes.rb +1 -0
  41. data/db/migrate/20170310164315_add_merged_at_on_pull_requests.rb +9 -0
  42. data/db/migrate/20170314145604_add_last_deployed_at_to_stack.rb +9 -0
  43. data/db/migrate/20170320124156_add_locked_to_commits.rb +5 -0
  44. data/lib/shipit/task_commands.rb +1 -0
  45. data/lib/shipit/version.rb +1 -1
  46. data/test/controllers/api/ccmenu_controller_test.rb +6 -0
  47. data/test/controllers/api/stacks_controller_test.rb +6 -0
  48. data/test/controllers/api/tasks_controller_test.rb +31 -0
  49. data/test/controllers/commits_controller_test.rb +18 -0
  50. data/test/dummy/db/development.sqlite3 +0 -0
  51. data/test/dummy/db/schema.rb +4 -1
  52. data/test/dummy/db/test.sqlite3 +0 -0
  53. data/test/dummy/db/test.sqlite3-journal +0 -0
  54. data/test/fixtures/shipit/commits.yml +1 -1
  55. data/test/fixtures/shipit/pull_requests.yml +33 -0
  56. data/test/fixtures/shipit/stacks.yml +4 -1
  57. data/test/jobs/github_sync_job_test.rb +49 -0
  58. data/test/jobs/merge_pull_requests_job_test.rb +18 -0
  59. data/test/models/commits_test.rb +71 -0
  60. data/test/models/deploy_spec_test.rb +15 -0
  61. data/test/models/deploys_test.rb +7 -0
  62. data/test/models/pull_request_test.rb +19 -1
  63. data/test/models/task_definitions_test.rb +9 -0
  64. data/test/models/undeployed_commits_test.rb +2 -7
  65. data/test/unit/deploy_commands_test.rb +7 -0
  66. data/test/unit/variable_definition_test.rb +10 -0
  67. metadata +15 -7
  68. data/app/assets/images/github.svg +0 -9
  69. data/app/assets/images/refresh.svg +0 -8
  70. data/app/assets/images/settings.svg +0 -33
  71. data/lib/snippets/deploy-to-gke +0 -161
@@ -1,4 +1,4 @@
1
- <li class="commit" id="commit-<%= commit.id %>">
1
+ <li class="commit <%= 'locked' if commit.locked? %>" id="commit-<%= commit.id %>">
2
2
  <%= render 'shipit/shared/author', author: commit.author %>
3
3
  <%= render commit.status %>
4
4
  <div class="commit-details">
@@ -14,6 +14,14 @@
14
14
  <%= timeago_tag(commit.committed_at, force: true) %>
15
15
  </p>
16
16
  </div>
17
+ <div class="commit-lock" >
18
+ <%= link_to stack_commit_path(commit.stack, commit), class: 'action-lock-commit', data: {tooltip: t('commit.lock')} do %>
19
+ <i class="icon icon--lock"></i>
20
+ <% end %>
21
+ <%= link_to stack_commit_path(commit.stack, commit), class: 'action-unlock-commit', data: {tooltip: t('commit.unlock'), confirm: t('commit.confirm_unlock')} do %>
22
+ <i class="icon icon--lock"></i>
23
+ <% end %>
24
+ </div>
17
25
  <div class="commit-actions">
18
26
  <%= deploy_button(commit) %>
19
27
  </div>
@@ -11,7 +11,7 @@
11
11
 
12
12
  <section>
13
13
  <header class="section-header">
14
- <h2>Commits included in this rollback</h2>
14
+ <h2>Commits that will be rolled back</h2>
15
15
  </header>
16
16
 
17
17
  <p><%= link_to_github_deploy(@rollback) %></p>
@@ -6,9 +6,6 @@
6
6
 
7
7
  <% content_for :primary_navigation do %>
8
8
  <%= link_to 'Refresh statuses & commits', stack_refresh_path(stack), method: 'post', class: "header__btn btn" %>
9
- <% stack.task_definitions.each do |definition| %>
10
- <%= link_to "#{definition.action}…", new_stack_tasks_path(stack, definition_id: definition.id), class: %w(header__btn stack-action btn trigger-deploy) %>
11
- <% end %>
12
9
  <% end %>
13
10
 
14
11
  <% content_for :secondary_navigation do %>
@@ -27,7 +24,22 @@
27
24
  <%= link_to "Pull Requests (#{stack.pull_requests.queued.count})", stack_pull_requests_path(stack) %>
28
25
  </li>
29
26
  <% end %>
27
+
28
+ <% if stack.task_definitions.present? %>
29
+ <li class="nav__list__item nav__list__item--has-children">
30
+ Tasks
31
+
32
+ <ul class="nav__sub__list">
33
+ <% stack.task_definitions.each do |definition| %>
34
+ <li class="nav__list__sub__item">
35
+ <%= link_to "#{definition.action}…", new_stack_tasks_path(stack, definition_id: definition.id), class: "trigger-deploy" %>
36
+ </li>
37
+ <% end %>
38
+ </ul>
39
+ </li>
40
+ <% end %>
30
41
  </ul>
42
+
31
43
  <ul class="nav__list nav__list--secondary">
32
44
  <% if stack.links.present? %>
33
45
  <% stack.links.each do |name, url| %>
@@ -74,7 +74,7 @@
74
74
  <div class="setting-section setting-ccmenu">
75
75
  <h5>Miscellaneous</h5>
76
76
  <div class="field-wrapper">
77
- <label>CCMenu URL</label>
77
+ <label>CCMenu URL (choose “Use URL as entered above” during CCMenu setup)</label>
78
78
  <input id="ccmenu-url" class="hidden" type="text" disabled />
79
79
  </div>
80
80
  <%= button_to "Fetch URL", "", class: 'btn', data: {remote: ccmenu_url_url(stack_id: @stack.to_param)} %>
@@ -20,21 +20,25 @@
20
20
  # available at http://guides.rubyonrails.org/i18n.html.
21
21
 
22
22
  en:
23
+ commit:
24
+ lock: This commit is safe to deploy. Click to mark it as unsafe.
25
+ unlock: This commit is unsafe to deploy. Click to mark it as safe.
26
+ confirm_unlock: Mark this commit as safe to deploy?
23
27
  deploy_button:
24
28
  hint:
25
29
  max_commits: It is recommended not to deploy more than %{maximum} commits at once.
26
30
  caption:
27
- pending: CI Pending...
28
- failure: CI Failure
29
- error: CI Error
31
+ pending: Pending CI
32
+ failure: Failing CI
33
+ error: Failing CI
30
34
  unknown: Not Run
31
35
  locked: Locked
32
- deploying: A Deploy is in Progress...
36
+ deploying: A Deploy is in Progress
33
37
  allowed: Deploy
34
38
  missing: Missing CI
35
39
  redeploy_button:
36
40
  caption:
37
- deploying: A Deploy is in Progress...
41
+ deploying: A Deploy is in Progress
38
42
  allowed: Redeploy
39
43
  locked: Locked
40
44
  deploy_spec:
data/config/routes.rb CHANGED
@@ -73,6 +73,7 @@ Shipit::Engine.routes.draw do
73
73
  get '/commit/:sha/checks/tail' => 'commit_checks#tail', as: :tail_commit_checks, defaults: {format: :json}
74
74
 
75
75
  resources :rollbacks, only: %i(create)
76
+ resources :commits, only: %i(update)
76
77
  resources :tasks, only: %i(show) do
77
78
  collection do
78
79
  get '' => 'tasks#index', as: :index
@@ -0,0 +1,9 @@
1
+ class AddMergedAtOnPullRequests < ActiveRecord::Migration[5.0]
2
+ def up
3
+ add_column :pull_requests, :merged_at, :datetime
4
+ end
5
+
6
+ def down
7
+ remove_column :pull_requests, :merged_at
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddLastDeployedAtToStack < ActiveRecord::Migration[5.0]
2
+ def up
3
+ add_column :stacks, :last_deployed_at, :datetime
4
+ end
5
+
6
+ def down
7
+ remove_column :stacks, :last_deployed_at
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockedToCommits < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :commits, :locked, :boolean, default: false, null: false
4
+ end
5
+ end
@@ -37,6 +37,7 @@ module Shipit
37
37
  'BUNDLE_PATH' => Rails.root.join('data', 'bundler').to_s,
38
38
  'SHIPIT_LINK' => permalink,
39
39
  'LAST_DEPLOYED_SHA' => @stack.last_deployed_commit.sha,
40
+ 'TASK_ID' => @task.id.to_s,
40
41
  ).merge(deploy_spec.machine_env).merge(@task.env)
41
42
  end
42
43
 
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.16.0'.freeze
2
+ VERSION = '0.17.0'.freeze
3
3
  end
@@ -42,6 +42,12 @@ module Shipit
42
42
  assert_payload 'lastBuildStatus', 'Failure'
43
43
  end
44
44
 
45
+ test "stacks with no deploys render correctly" do
46
+ stack = Stack.create!(repo_owner: 'foo', repo_name: 'bar')
47
+ get :show, params: {stack_id: stack.to_param}
48
+ assert_payload 'lastBuildStatus', 'Success'
49
+ end
50
+
45
51
  private
46
52
 
47
53
  def get_project_from_xml(xml)
@@ -101,6 +101,12 @@ module Shipit
101
101
  assert_response :ok
102
102
  assert_json 'id', @stack.id
103
103
  end
104
+
105
+ test "#show returns last_deployed_at column for stack" do
106
+ get :show, params: {id: @stack.to_param}
107
+ assert_response :ok
108
+ assert_json 'last_deployed_at', @stack.last_deployed_at
109
+ end
104
110
  end
105
111
  end
106
112
  end
@@ -24,11 +24,22 @@ module Shipit
24
24
  assert_json 'id', task.id
25
25
  end
26
26
 
27
+ test "#trigger returns 404 with unknown task" do
28
+ post :trigger, params: {stack_id: @stack.to_param, task_name: 'shave_the_yak'}
29
+ assert_response :not_found
30
+ end
31
+
27
32
  test "#trigger triggers a custom task" do
28
33
  post :trigger, params: {stack_id: @stack.to_param, task_name: 'restart'}
29
34
  assert_response :accepted
30
35
  assert_json 'type', 'task'
31
36
  assert_json 'status', 'pending'
37
+
38
+ expected_env = {
39
+ "FOO" => "1",
40
+ "BAR" => "0",
41
+ }
42
+ assert_equal expected_env, Shipit::Task.last.env
32
43
  end
33
44
 
34
45
  test "#trigger refuses to trigger a task with tasks not whitelisted" do
@@ -44,6 +55,26 @@ module Shipit
44
55
  assert_response :accepted
45
56
  assert_json 'type', 'task'
46
57
  assert_json 'status', 'pending'
58
+
59
+ expected_env = {
60
+ "FOO" => "bar",
61
+ "BAR" => "0",
62
+ }
63
+ assert_equal expected_env, Shipit::Task.last.env
64
+ end
65
+
66
+ test "#trigger triggers a task with explicitly passed and default variables" do
67
+ env = {'WALRUS' => 'overridden value'}
68
+ post :trigger, params: {stack_id: @stack.to_param, task_name: 'restart', env: env}
69
+ assert_response :accepted
70
+
71
+ # FOO and BAR are variables with a default value
72
+ expected_env = {
73
+ "FOO" => "1",
74
+ "BAR" => "0",
75
+ "WALRUS" => "overridden value",
76
+ }
77
+ assert_equal expected_env, Shipit::Task.last.env
47
78
  end
48
79
 
49
80
  test "#trigger returns a 404 when the task doesn't exist" do
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ module Shipit
4
+ class CommitsControllerTest < ActionController::TestCase
5
+ setup do
6
+ @stack = shipit_stacks(:shipit)
7
+ @commit = shipit_commits(:first)
8
+ session[:user_id] = shipit_users(:walrus).id
9
+ end
10
+
11
+ test "#update allows to lock a commit" do
12
+ refute_predicate @commit, :locked?
13
+ patch :update, params: {stack_id: @stack.to_param, id: @commit.id, commit: {locked: true}}
14
+ assert_response :ok
15
+ assert_predicate @commit.reload, :locked?
16
+ end
17
+ end
18
+ end
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: 20170221130336) do
13
+ ActiveRecord::Schema.define(version: 20170320124156) do
14
14
 
15
15
  create_table "api_clients", force: :cascade do |t|
16
16
  t.text "permissions", limit: 65535
@@ -59,6 +59,7 @@ ActiveRecord::Schema.define(version: 20170221130336) do
59
59
  t.integer "pull_request_number"
60
60
  t.string "pull_request_title", limit: 1024
61
61
  t.integer "pull_request_id"
62
+ t.boolean "locked", default: false, null: false
62
63
  t.index ["author_id"], name: "index_commits_on_author_id"
63
64
  t.index ["committer_id"], name: "index_commits_on_committer_id"
64
65
  t.index ["created_at"], name: "index_commits_on_created_at"
@@ -145,6 +146,7 @@ ActiveRecord::Schema.define(version: 20170221130336) do
145
146
  t.datetime "updated_at", null: false
146
147
  t.string "branch"
147
148
  t.datetime "revalidated_at"
149
+ t.datetime "merged_at"
148
150
  t.index ["head_id"], name: "index_pull_requests_on_head_id"
149
151
  t.index ["merge_requested_by_id"], name: "index_pull_requests_on_merge_requested_by_id"
150
152
  t.index ["stack_id", "github_id"], name: "index_pull_requests_on_stack_id_and_github_id", unique: true
@@ -173,6 +175,7 @@ ActiveRecord::Schema.define(version: 20170221130336) do
173
175
  t.datetime "continuous_delivery_delayed_since"
174
176
  t.datetime "locked_since"
175
177
  t.boolean "merge_queue_enabled", default: false, null: false
178
+ t.datetime "last_deployed_at"
176
179
  t.index ["repo_owner", "repo_name", "environment"], name: "stack_unicity", unique: true
177
180
  end
178
181
 
Binary file
Binary file
@@ -53,7 +53,7 @@ fourth:
53
53
  fifth:
54
54
  id: 5
55
55
  sha: 567578b362bf2b4df5903e1c7960929361c3abcd
56
- message: "fix all teh things"
56
+ message: "fix all the things"
57
57
  stack: shipit
58
58
  author: walrus
59
59
  committer: walrus
@@ -54,3 +54,36 @@ shipit_pending_not_mergeable_yet:
54
54
  mergeable: null
55
55
  additions: 23
56
56
  deletions: 43
57
+
58
+ shipit_pending_closed:
59
+ stack: shipit
60
+ number: 65
61
+ merge_status: pending
62
+ merge_requested_by: walrus
63
+ merge_requested_at: <%= 3.minute.ago.to_s(:db) %>
64
+ revalidated_at: <%= 3.minute.ago.to_s(:db) %>
65
+ github_id: 43243243243243
66
+ api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/65
67
+ state: closed
68
+ branch: feature-64
69
+ head_id: 8
70
+ mergeable: null
71
+ additions: 23
72
+ deletions: 43
73
+
74
+ shipit_pending_merged:
75
+ stack: shipit
76
+ number: 66
77
+ merge_status: pending
78
+ merge_requested_by: walrus
79
+ merge_requested_at: <%= 3.minute.ago.to_s(:db) %>
80
+ merged_at: <%= 2.minute.ago.to_s(:db) %>
81
+ revalidated_at: <%= 3.minute.ago.to_s(:db) %>
82
+ github_id: 43243243243232
83
+ api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/66
84
+ state: closed
85
+ branch: feature-64
86
+ head_id: 8
87
+ mergeable: null
88
+ additions: 23
89
+ deletions: 43
@@ -26,7 +26,8 @@ shipit:
26
26
  "description": "Restart app and job servers",
27
27
  "variables": [
28
28
  {"name": "FOO", "title": "Set to 0 to foo", "default": 1},
29
- {"name": "BAR", "title": "Set to 1 to bar", "default": 0}
29
+ {"name": "BAR", "title": "Set to 1 to bar", "default": 0},
30
+ {"name": "WALRUS", "title": "Walrus without a default value"}
30
31
  ],
31
32
  "steps": [
32
33
  "cap $ENVIRONMENT deploy:restart"
@@ -49,6 +50,7 @@ shipit:
49
50
  "allow_failures": ["ci/ok_to_fail"]
50
51
  }
51
52
  }
53
+ last_deployed_at: <%= 8.days.ago.to_s(:db) %>
52
54
  updated_at: <%= 8.days.ago.to_s(:db) %>
53
55
 
54
56
  cyclimse:
@@ -82,6 +84,7 @@ cyclimse:
82
84
  }
83
85
  }
84
86
  }
87
+ last_deployed_at: <%= 8.days.ago.to_s(:db) %>
85
88
  updated_at: <%= 8.days.ago.to_s(:db) %>
86
89
 
87
90
  undeployed_stack:
@@ -36,6 +36,55 @@ module Shipit
36
36
  assert shipit_commits(:fifth).reload.detached?
37
37
  end
38
38
 
39
+ test "#perform locks all commits leading to a revert" do
40
+ @stack.deploys_and_rollbacks.destroy_all
41
+
42
+ initial_queue = [
43
+ ["fix all the things", false],
44
+ ["yoloshipit!", false],
45
+ ["fix it!", false],
46
+ ["sheep it!", false],
47
+ ["lets go", false],
48
+ ]
49
+ assert_equal initial_queue, @stack.undeployed_commits.map { |c| [c.title, c.locked?] }
50
+
51
+ author = stub(
52
+ id: 1234,
53
+ login: 'bob',
54
+ name: 'Bob the Builder',
55
+ email: 'bob@bob.com',
56
+ date: '2011-04-14T16:00:49Z',
57
+ )
58
+ @job.expects(:fetch_missing_commits).returns([
59
+ [
60
+ stub(
61
+ sha: '36514755579bfb5bc313f403b216f4347a027990',
62
+ author: author,
63
+ committer: author,
64
+ stats: nil,
65
+ commit: stub(
66
+ sha: '36514755579bfb5bc313f403b216f4347a027990',
67
+ message: 'Revert "fix it!"',
68
+ author: author,
69
+ committer: author,
70
+ ),
71
+ ),
72
+ ],
73
+ shipit_commits(:fifth),
74
+ ])
75
+ @job.perform(stack_id: @stack.id)
76
+
77
+ final_queue = [
78
+ ['Revert "fix it!"', false],
79
+ ["fix all the things", true],
80
+ ["yoloshipit!", true],
81
+ ["fix it!", true],
82
+ ["sheep it!", false],
83
+ ["lets go", false],
84
+ ]
85
+ assert_equal final_queue, @stack.reload.undeployed_commits.map { |c| [c.title, c.locked?] }
86
+ end
87
+
39
88
  test "#fetch_missing_commits returns the commits in the reverse order if it doesn't know the parent" do
40
89
  last = stub(sha: 123)
41
90
  first = stub(sha: 345)
@@ -9,6 +9,8 @@ module Shipit
9
9
  @pending_pr = shipit_pull_requests(:shipit_pending)
10
10
  @unmergeable_pr = shipit_pull_requests(:shipit_pending_unmergeable)
11
11
  @not_ready_pr = shipit_pull_requests(:shipit_pending_not_mergeable_yet)
12
+ @closed_pr = shipit_pull_requests(:shipit_pending_closed)
13
+ @merged_pr = shipit_pull_requests(:shipit_pending_merged)
12
14
  end
13
15
 
14
16
  test "#perform rejects unmergeable PRs and merge the others" do
@@ -20,6 +22,8 @@ module Shipit
20
22
  }.to_json)
21
23
  branch_url = "https://api.github.com/repos/shopify/shipit-engine/git/refs/heads/feature-62"
22
24
  FakeWeb.register_uri(:delete, branch_url, status: %w(204 No content))
25
+ pulls_url = "https://api.github.com/repos/shopify/shipit-engine/pulls?base=feature-62"
26
+ FakeWeb.register_uri(:get, pulls_url, status: %w(200 OK), body: '[]')
23
27
 
24
28
  @job.perform(@stack)
25
29
 
@@ -55,5 +59,19 @@ module Shipit
55
59
  end
56
60
  assert_predicate @pending_pr.reload, :pending?
57
61
  end
62
+
63
+ test "#perform cancels merge requests for closed PRs" do
64
+ @pending_pr.cancel!
65
+ PullRequest.any_instance.stubs(:refresh!)
66
+ @job.perform(@stack)
67
+ assert_predicate @closed_pr.reload, :canceled?
68
+ end
69
+
70
+ test "#perform completes merge requests for already merged PRs" do
71
+ @pending_pr.cancel!
72
+ PullRequest.any_instance.stubs(:refresh!)
73
+ @job.perform(@stack)
74
+ assert_predicate @merged_pr.reload, :merged?
75
+ end
58
76
  end
59
77
  end