shipit-engine 0.27.1 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -1
  3. data/app/assets/stylesheets/_pages/_commits.scss +2 -0
  4. data/app/assets/stylesheets/_pages/_deploy.scss +6 -0
  5. data/app/controllers/shipit/api/release_statuses_controller.rb +22 -0
  6. data/app/controllers/shipit/api/stacks_controller.rb +6 -1
  7. data/app/controllers/shipit/commits_controller.rb +12 -1
  8. data/app/controllers/shipit/deploys_controller.rb +11 -0
  9. data/app/controllers/shipit/stacks_controller.rb +29 -1
  10. data/app/controllers/shipit/tasks_controller.rb +13 -1
  11. data/app/helpers/shipit/merge_status_helper.rb +2 -2
  12. data/app/helpers/shipit/stacks_helper.rb +10 -4
  13. data/app/jobs/shipit/perform_task_job.rb +1 -0
  14. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +41 -0
  15. data/app/models/shipit/command_line_user.rb +58 -0
  16. data/app/models/shipit/commit.rb +42 -2
  17. data/app/models/shipit/deploy.rb +31 -2
  18. data/app/models/shipit/deploy_spec/lerna_discovery.rb +43 -19
  19. data/app/models/shipit/deploy_spec/pypi_discovery.rb +5 -1
  20. data/app/models/shipit/rollback.rb +4 -2
  21. data/app/models/shipit/stack.rb +72 -15
  22. data/app/models/shipit/task.rb +30 -0
  23. data/app/models/shipit/undeployed_commit.rb +10 -1
  24. data/app/serializers/shipit/command_line_user_serializer.rb +4 -0
  25. data/app/views/layouts/shipit.html.erb +5 -1
  26. data/app/views/shipit/commits/_commit.html.erb +7 -2
  27. data/app/views/shipit/merge_status/_commit_count_warning.html.erb +1 -5
  28. data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
  29. data/app/views/shipit/merge_status/failure.html.erb +1 -1
  30. data/app/views/shipit/merge_status/locked.html.erb +1 -1
  31. data/app/views/shipit/merge_status/success.html.erb +1 -1
  32. data/app/views/shipit/stacks/show.html.erb +10 -1
  33. data/app/views/shipit/tasks/_task_output.html.erb +2 -2
  34. data/config/locales/en.yml +2 -1
  35. data/config/routes.rb +8 -1
  36. data/db/migrate/20190502020249_add_lock_author_id_to_commits.rb +5 -0
  37. data/lib/shipit.rb +14 -2
  38. data/lib/shipit/cast_value.rb +9 -0
  39. data/lib/shipit/command.rb +62 -16
  40. data/lib/shipit/line_buffer.rb +42 -0
  41. data/lib/shipit/version.rb +1 -1
  42. data/lib/tasks/shipit.rake +27 -0
  43. data/test/controllers/api/release_statuses_controller_test.rb +66 -0
  44. data/test/controllers/api/stacks_controller_test.rb +19 -0
  45. data/test/controllers/commits_controller_test.rb +30 -6
  46. data/test/controllers/deploys_controller_test.rb +51 -2
  47. data/test/controllers/tasks_controller_test.rb +24 -0
  48. data/test/dummy/db/schema.rb +2 -1
  49. data/test/dummy/db/seeds.rb +2 -0
  50. data/test/fixtures/shipit/check_runs.yml +11 -0
  51. data/test/fixtures/shipit/commits.yml +104 -0
  52. data/test/fixtures/shipit/stacks.yml +98 -3
  53. data/test/fixtures/shipit/tasks.yml +42 -0
  54. data/test/jobs/update_github_last_deployed_ref_job_test.rb +88 -0
  55. data/test/models/commits_test.rb +88 -1
  56. data/test/models/deploy_spec_test.rb +34 -6
  57. data/test/models/deploys_test.rb +308 -6
  58. data/test/models/rollbacks_test.rb +17 -11
  59. data/test/models/stacks_test.rb +217 -4
  60. data/test/models/tasks_test.rb +13 -0
  61. data/test/models/undeployed_commits_test.rb +62 -3
  62. data/test/test_helper.rb +0 -1
  63. data/test/unit/command_test.rb +55 -0
  64. data/test/unit/line_buffer_test.rb +20 -0
  65. metadata +142 -128
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 495cc8d38c4896d2059a99ef8e30073ffbabf569b1a23a8d71496989b6134a2a
4
- data.tar.gz: 2429d8adfffac81cf04174a9ea03d27cc64a535072433eccce9c3550dc2660a6
3
+ metadata.gz: bdb3736fb9999c0409f7b81081a52467f33a39ee419f2a40e14e6aca73866263
4
+ data.tar.gz: f58bfcbbc8084bb18cf2234ed0a75658a5c6bc9e2b593bc8a2b90e5f89d030b5
5
5
  SHA512:
6
- metadata.gz: 8721520921c29b8ba7a5b1af921b1bd2d870a9047916370ce250415f5a39f9298b1f3df414ca2dfe674b1c0bf6e6459b2145a94eef99aac1ebd27aae6af5af28
7
- data.tar.gz: fd4320cd7b7ca5b2b4030abb6c045a4106ba6ef2fc785b9caa08e7ccd4482033ad1584ad806638937eb5557628b914dd6acd4452b01cfe6c17f0f091377a6219
6
+ metadata.gz: 19ffafe44e93c6f02f277c5208160a605286557f5080cf3569355fcfa85b2a8583eace47a89c1ac223490ad157de451f0837343d5b818e891c9794b27ca20554
7
+ data.tar.gz: 57be6895de1d5757a48b603cfb4796316f477b791feef7cfc876dc57b5226d869e65b2332e920e695e3edbba3bd99b39dc75e0e3ceeab2b6aee851eaeea15c2e
data/README.md CHANGED
@@ -35,6 +35,11 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
35
35
  * [Configuring providers](#configuring-providers)
36
36
  * [Free samples](/examples/shipit.yml)
37
37
 
38
+ **IV. CONTRIBUTING**
39
+
40
+ * [Instructions](#contributing-instructions)
41
+ * [Local development](#contributing-local-dev)
42
+
38
43
  * * *
39
44
 
40
45
  <h2 id="installation-and-setup">I. INSTALLATION & SETUP</h2>
@@ -112,7 +117,7 @@ The settings in the `shipit.yml` file relate to the different things you can do
112
117
  * [CI](#ci) (`ci.require`, `ci.hide`, `ci.allow_failures`)
113
118
  * [Merge Queue](#merge-queue) (`merge.revalidate_after`, `merge.require`, `merge.ignore`, `merge.max_divergence`)
114
119
  * [Custom Tasks](#custom-tasks) (`tasks`)
115
- * [Custom links](#custom-links) (`links`)
120
+ * [Custom links](#custom-links) (`links`)
116
121
  * [Review Process](#review-process) (`review.checklist`, `review.monitoring`, `review.checks`)
117
122
 
118
123
  All the settings in `shipit.yml` are optional. Most applications can be deployed from Shipit without any configuration.
@@ -628,3 +633,27 @@ For Kubernetes, you have to provision Shipit environment with the following tool
628
633
 
629
634
  * `kubectl`
630
635
  * `kubernetes-deploy` [gem](https://github.com/Shopify/kubernetes-deploy)
636
+
637
+ <h2 id="contributing">IV. CONTRIBUTING</h2>
638
+
639
+ <h3 id="contributing-instructions">Instructions</h3>
640
+
641
+ 1. Fork it ( https://github.com/shopify/shipit-engine/fork )
642
+ 1. Create your feature branch (git checkout -b my-new-feature)
643
+ 1. Commit your changes (git commit -am 'Add some feature')
644
+ 1. Push to the branch (git push origin my-new-feature)
645
+ 1. Create a new Pull Request
646
+
647
+ <h3 id="contributing-local-dev">Local development</h3>
648
+
649
+ This repository has a [test/dummy](/test/dummy) app in it which can be used for local development without having to setup a new rails application.
650
+
651
+ Run `./bin/bootstrap` in order to bootstrap the dummy application. The bootstrap script is going to:
652
+
653
+ - Copy `config/secrets.development.example.yml` to `config/secrets.development.yml`;
654
+ - Make sure all dependencies are installed;
655
+ - Create and seed database (recreate database if already available);
656
+
657
+ Run `./test/dummy/bin/rails server` to run the rails dummy application.
658
+
659
+ Set the environment variable `SHIPIT_DISABLE_AUTH=1` in order to disable authentication.
@@ -77,6 +77,8 @@
77
77
  margin-bottom: 1rem;
78
78
  order: -1;
79
79
  }
80
+
81
+ @include truncate;
80
82
  }
81
83
 
82
84
  .commit-title {
@@ -144,6 +144,12 @@
144
144
  font-size: .875rem;
145
145
  padding: .25em .45em;
146
146
  }
147
+
148
+ .short-sha-no-bg {
149
+ font-family: Menlo, monospace;
150
+ border-radius: 4px;
151
+ font-size: .875rem;
152
+ }
147
153
  }
148
154
 
149
155
  .search-bar {
@@ -0,0 +1,22 @@
1
+ module Shipit
2
+ module Api
3
+ class ReleaseStatusesController < BaseController
4
+ require_permission :deploy, :stack
5
+
6
+ params do
7
+ requires :status, String
8
+ validates :status, inclusion: {in: %w(success failure)}
9
+ end
10
+ def create
11
+ deploy = stack.deploys_and_rollbacks.find(params[:deploy_id])
12
+ case params[:status]
13
+ when 'success'
14
+ deploy.report_healthy!(user: current_user)
15
+ when 'failure'
16
+ deploy.report_faulty!(user: current_user)
17
+ end
18
+ render_resource deploy, status: :created
19
+ end
20
+ end
21
+ end
22
+ end
@@ -2,7 +2,7 @@ module Shipit
2
2
  module Api
3
3
  class StacksController < BaseController
4
4
  require_permission :read, :stack, only: %i(index show)
5
- require_permission :write, :stack, only: %i(create)
5
+ require_permission :write, :stack, only: %i(create destroy)
6
6
 
7
7
  def index
8
8
  render_resources stacks
@@ -25,6 +25,11 @@ module Shipit
25
25
  render_resource stack
26
26
  end
27
27
 
28
+ def destroy
29
+ stack.schedule_for_destroy!
30
+ head :accepted
31
+ end
32
+
28
33
  private
29
34
 
30
35
  def stack
@@ -1,7 +1,14 @@
1
1
  module Shipit
2
2
  class CommitsController < ShipitController
3
3
  def update
4
- commit.update(params.require(:commit).permit(:locked))
4
+ if update_params[:locked].present?
5
+ if Shipit::CastValue.to_boolean(update_params[:locked])
6
+ commit.lock(current_user)
7
+ else
8
+ commit.unlock
9
+ end
10
+ end
11
+
5
12
  head :ok
6
13
  end
7
14
 
@@ -14,5 +21,9 @@ module Shipit
14
21
  def stack
15
22
  @stack ||= Stack.from_param!(params[:stack_id])
16
23
  end
24
+
25
+ def update_params
26
+ @update_params ||= params.require(:commit).permit(:locked)
27
+ end
17
28
  end
18
29
  end
@@ -5,6 +5,7 @@ module Shipit
5
5
  before_action :load_stack
6
6
  before_action :load_deploy, only: %i(show rollback revert)
7
7
  before_action :load_until_commit, only: :create
8
+ helper_method :short_commit_sha
8
9
 
9
10
  def new
10
11
  @commit = @stack.commits.by_sha!(params[:sha])
@@ -40,6 +41,12 @@ module Shipit
40
41
  redirect_to rollback_stack_deploy_path(@stack, previous_deploy)
41
42
  end
42
43
 
44
+ def short_commit_sha(task)
45
+ if previous_successful_deploy_commit(task)
46
+ @short_commit_sha ||= @previous_successful_deploy_commit&.short_sha
47
+ end
48
+ end
49
+
43
50
  private
44
51
 
45
52
  def load_deploy
@@ -57,5 +64,9 @@ module Shipit
57
64
  def deploy_params
58
65
  @deploy_params ||= params.require(:deploy).permit(:until_commit_id, env: @stack.deploy_variables.map(&:name))
59
66
  end
67
+
68
+ def previous_successful_deploy_commit(task)
69
+ @previous_successful_deploy_commit ||= task.commit_to_rollback_to
70
+ end
60
71
  end
61
72
  end
@@ -17,7 +17,28 @@ module Shipit
17
17
  return if flash.empty? && !stale?(last_modified: @stack.updated_at)
18
18
 
19
19
  @tasks = @stack.tasks.order(id: :desc).preload(:since_commit, :until_commit, :user).limit(10)
20
- @commits = @stack.undeployed_commits { |scope| scope.preload(:author, :statuses, :check_runs) }
20
+
21
+ commits = @stack.undeployed_commits do |scope|
22
+ scope.preload(:author, :statuses, :check_runs, :lock_author)
23
+ end
24
+
25
+ next_expected_commit_to_deploy = @stack.next_expected_commit_to_deploy(commits: commits)
26
+
27
+ @active_commits = []
28
+ @undeployed_commits = []
29
+
30
+ commits.each do |commit|
31
+ (commit.active? ? @active_commits : @undeployed_commits) << commit
32
+ end
33
+
34
+ @active_commits = map_to_undeployed_commit(
35
+ @active_commits,
36
+ next_expected_commit_to_deploy: next_expected_commit_to_deploy,
37
+ )
38
+ @undeployed_commits = map_to_undeployed_commit(
39
+ @undeployed_commits,
40
+ next_expected_commit_to_deploy: next_expected_commit_to_deploy,
41
+ )
21
42
  end
22
43
 
23
44
  def lookup
@@ -73,6 +94,13 @@ module Shipit
73
94
 
74
95
  private
75
96
 
97
+ def map_to_undeployed_commit(commits, next_expected_commit_to_deploy:)
98
+ commits.map.with_index do |c, i|
99
+ index = commits.size - i - 1
100
+ UndeployedCommit.new(c, index: index, next_expected_commit_to_deploy: next_expected_commit_to_deploy)
101
+ end
102
+ end
103
+
76
104
  def load_stack
77
105
  @stack = Stack.from_param!(params[:id])
78
106
  end
@@ -2,7 +2,7 @@ module Shipit
2
2
  class TasksController < ShipitController
3
3
  include Pagination
4
4
 
5
- before_action :stack
5
+ before_action :stack, except: [:lookup]
6
6
 
7
7
  self.default_page_size = 20
8
8
 
@@ -50,8 +50,20 @@ module Shipit
50
50
  render json: TailTaskSerializer.new(task, context: params)
51
51
  end
52
52
 
53
+ def lookup
54
+ @task = Task.find(params[:id])
55
+
56
+ redirect_to url_for_task
57
+ end
58
+
53
59
  private
54
60
 
61
+ def url_for_task
62
+ base_task = @task.is_a?(Deploy) ? @task.becomes(Deploy) : @task
63
+
64
+ url_for([base_task.stack, base_task])
65
+ end
66
+
55
67
  def task
56
68
  @task ||= stack.tasks.find(params[:id])
57
69
  end
@@ -1,7 +1,7 @@
1
1
  module Shipit
2
2
  module MergeStatusHelper
3
- def too_many_commits?(commits)
4
- commits > 4
3
+ def display_commit_count_warning?(commits)
4
+ commits > 4 && @stack.merge_queue_enabled?
5
5
  end
6
6
  end
7
7
  end
@@ -1,9 +1,7 @@
1
1
  module Shipit
2
2
  module StacksHelper
3
- COMMIT_TITLE_LENGTH = 79
4
-
5
3
  def redeploy_button(deployed_commit)
6
- commit = UndeployedCommit.new(deployed_commit, 0)
4
+ commit = UndeployedCommit.new(deployed_commit, index: 0)
7
5
  url = new_stack_deploy_path(commit.stack, sha: commit.sha)
8
6
  classes = %W(btn btn--primary deploy-action #{commit.state})
9
7
 
@@ -46,7 +44,7 @@ module Shipit
46
44
  end
47
45
 
48
46
  def render_commit_message(pull_request_or_commit)
49
- message = pull_request_or_commit.title.to_s.truncate(COMMIT_TITLE_LENGTH)
47
+ message = pull_request_or_commit.title.to_s
50
48
  content_tag(:span, emojify(message), class: 'event-message')
51
49
  end
52
50
 
@@ -80,5 +78,13 @@ module Shipit
80
78
  def render_raw_commit_id_link(commit)
81
79
  link_to(commit.short_sha, github_commit_url(commit), target: '_blank', class: 'number')
82
80
  end
81
+
82
+ def unlock_commit_tooltip(commit)
83
+ if commit.lock_author.present?
84
+ t('commit.unlock_with_author', author: commit.lock_author.name)
85
+ else
86
+ t('commit.unlock')
87
+ end
88
+ end
83
89
  end
84
90
  end
@@ -18,6 +18,7 @@ module Shipit
18
18
  @task.run!
19
19
  checkout_repository
20
20
  perform_task
21
+ @task.write("\nCompleted successfully\n")
21
22
  @task.report_complete!
22
23
  rescue Command::TimedOut => error
23
24
  @task.write("\n#{error.message}\n")
@@ -0,0 +1,41 @@
1
+ module Shipit
2
+ class UpdateGithubLastDeployedRefJob < BackgroundJob
3
+ queue_as :default
4
+
5
+ DEPLOY_PREFIX = 'shipit-deploy'.freeze
6
+
7
+ def perform(stack)
8
+ stack_sha = stack.last_successful_deploy_commit&.sha
9
+ return unless stack_sha
10
+
11
+ environment = stack.environment
12
+ stack_ref = create_full_ref(environment)
13
+ client = Shipit.github.api
14
+
15
+ full_repo_name = stack.github_repo_name
16
+
17
+ update_or_create_ref(client: client, repo_name: full_repo_name, ref: stack_ref, new_sha: stack_sha)
18
+ end
19
+
20
+ private
21
+
22
+ def create_full_ref(stack_environment)
23
+ [DEPLOY_PREFIX, stack_environment].join("/")
24
+ end
25
+
26
+ def create_ref(client:, repo_name:, ref:, sha:)
27
+ client.create_ref(repo_name, ref, sha)
28
+ end
29
+
30
+ def update_or_create_ref(client:, repo_name:, ref:, new_sha:)
31
+ client.update_ref(repo_name, ref, new_sha)
32
+ rescue Octokit::UnprocessableEntity => e
33
+ error_msg = e.message
34
+ if error_msg.include? "Reference does not exist"
35
+ create_ref(client: client, repo_name: repo_name, ref: ref, sha: new_sha)
36
+ else
37
+ raise
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ module Shipit
2
+ class CommandLineUser
3
+ def present?
4
+ false
5
+ end
6
+
7
+ def email
8
+ 'command_line@example.com'
9
+ end
10
+
11
+ def login
12
+ 'command_line'
13
+ end
14
+
15
+ def name
16
+ 'CommandLine'
17
+ end
18
+
19
+ def avatar_url
20
+ 'https://github.com/images/error/octocat_happy.gif'
21
+ end
22
+
23
+ def id
24
+ end
25
+
26
+ def github_id
27
+ end
28
+
29
+ def logged_in?
30
+ false
31
+ end
32
+
33
+ def authorized?
34
+ Shipit.authentication_disabled?
35
+ end
36
+
37
+ def stacks_contributed_to
38
+ []
39
+ end
40
+
41
+ def avatar_uri
42
+ User::DEFAULT_AVATAR.dup
43
+ end
44
+
45
+ def created_at
46
+ Time.at(0).utc
47
+ end
48
+ alias_method :updated_at, :created_at
49
+
50
+ def read_attribute_for_serialization(attr)
51
+ public_send(attr)
52
+ end
53
+
54
+ def github_api
55
+ Shipit.github.api
56
+ end
57
+ end
58
+ end
@@ -7,7 +7,7 @@ module Shipit
7
7
  belongs_to :stack
8
8
  has_many :deploys
9
9
  has_many :statuses, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
10
- has_many :check_runs, dependent: :destroy
10
+ has_many :check_runs, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
11
11
  has_many :commit_deployments, dependent: :destroy
12
12
  has_many :release_statuses, dependent: :destroy
13
13
  belongs_to :pull_request, inverse_of: :merge_commit, optional: true
@@ -23,6 +23,7 @@ module Shipit
23
23
 
24
24
  belongs_to :author, class_name: 'User', inverse_of: :authored_commits
25
25
  belongs_to :committer, class_name: 'User', inverse_of: :commits
26
+ belongs_to :lock_author, class_name: :User, optional: true, inverse_of: false
26
27
 
27
28
  def author
28
29
  super || AnonymousUser.new
@@ -32,6 +33,10 @@ module Shipit
32
33
  super || AnonymousUser.new
33
34
  end
34
35
 
36
+ def lock_author
37
+ super || AnonymousUser.new
38
+ end
39
+
35
40
  scope :reachable, -> { where(detached: false) }
36
41
 
37
42
  delegate :broadcast_update, :github_repo_name, :hidden_statuses, :required_statuses, :blocking_statuses,
@@ -47,6 +52,11 @@ module Shipit
47
52
  where('id < ?', commit.try(:id) || commit)
48
53
  end
49
54
 
55
+ def self.since(commit)
56
+ return all unless commit
57
+ where('id >= ?', commit.try(:id) || commit)
58
+ end
59
+
50
60
  def self.until(commit)
51
61
  return all unless commit
52
62
  where('id <= ?', commit.try(:id) || commit)
@@ -164,6 +174,18 @@ module Shipit
164
174
 
165
175
  delegate :pending?, :success?, :error?, :failure?, :blocking?, :state, to: :status
166
176
 
177
+ def active?
178
+ return false unless stack.active_task?
179
+
180
+ active_task = stack.active_task
181
+
182
+ if active_task.since_commit == active_task.until_commit
183
+ id == active_task.since_commit.id
184
+ else
185
+ id > active_task.since_commit.id && id <= active_task.until_commit.id
186
+ end
187
+ end
188
+
167
189
  def deployable?
168
190
  !locked? && (stack.ignore_ci? || (success? && !blocked?))
169
191
  end
@@ -198,7 +220,7 @@ module Shipit
198
220
  end
199
221
 
200
222
  def message_header
201
- message.lines.first.strip
223
+ message.lines.first.to_s.strip
202
224
  end
203
225
 
204
226
  # TODO: remove in a few versions when it is assumed the commits table was backfilled
@@ -271,6 +293,24 @@ module Shipit
271
293
  end
272
294
  end
273
295
 
296
+ def lock(user)
297
+ update!(
298
+ locked: true,
299
+ lock_author_id: user.id,
300
+ )
301
+ end
302
+
303
+ def self.lock_all(user)
304
+ update_all(
305
+ locked: true,
306
+ lock_author_id: user.id,
307
+ )
308
+ end
309
+
310
+ def unlock
311
+ update!(locked: false, lock_author: nil)
312
+ end
313
+
274
314
  private
275
315
 
276
316
  def message_parser