shipit-engine 0.27.1 → 0.28.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 (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