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
@@ -4,13 +4,13 @@ module Shipit
4
4
 
5
5
  class << self
6
6
  def load(payload)
7
- return unless payload.present?
7
+ return if payload.blank?
8
8
  json = JSON.parse(payload)
9
9
  new(json.delete('id'), json)
10
10
  end
11
11
 
12
12
  def dump(definition)
13
- return unless definition.present?
13
+ return if definition.blank?
14
14
  JSON.dump(definition.as_json)
15
15
  end
16
16
  end
@@ -9,9 +9,15 @@ module Shipit
9
9
 
10
10
  def deploy_state(bypass_safeties = false)
11
11
  state = deployable? ? 'allowed' : status.state
12
+
12
13
  unless bypass_safeties
13
- state = 'deploying' if stack.active_task?
14
- state = 'locked' if locked?
14
+ if blocked?
15
+ state = 'blocked'
16
+ elsif locked?
17
+ state = 'locked'
18
+ elsif stack.active_task?
19
+ state = 'deploying'
20
+ end
15
21
  end
16
22
  state
17
23
  end
@@ -31,5 +37,10 @@ module Shipit
31
37
  def deploy_discouraged?
32
38
  stack.maximum_commits_per_deploy && index >= stack.maximum_commits_per_deploy
33
39
  end
40
+
41
+ def blocked?
42
+ return @blocked if defined?(@blocked)
43
+ @blocked = super
44
+ end
34
45
  end
35
46
  end
@@ -71,7 +71,7 @@ module Shipit
71
71
  end
72
72
 
73
73
  def refresh_from_github!
74
- update!(github_user: Shipit.github_api.user(login))
74
+ update!(github_user: Shipit.github_api.user(github_id))
75
75
  rescue Octokit::NotFound
76
76
  identify_renamed_user!
77
77
  end
@@ -7,7 +7,7 @@ module Shipit
7
7
  has_one :head, serializer: ShortCommitSerializer
8
8
 
9
9
  attributes :id, :number, :title, :github_id, :additions, :deletions, :state, :merge_status, :mergeable,
10
- :merge_requested_at, :rejection_reason, :html_url
10
+ :merge_requested_at, :rejection_reason, :html_url, :branch, :base_ref
11
11
 
12
12
  def html_url
13
13
  github_pull_request_url(object)
@@ -11,7 +11,7 @@ module Shipit
11
11
  end
12
12
 
13
13
  def include_url?
14
- !url.blank?
14
+ url.present?
15
15
  end
16
16
 
17
17
  def output
@@ -2,12 +2,13 @@
2
2
  status_map = {'backlogged' => 'failure', 'locked' => 'failure'}
3
3
  xml.instruct!
4
4
  xml.Projects do
5
- xml.Project '', {
6
- :name => stack.to_param,
7
- :lastBuildStatus => status_map.fetch(stack.merge_status, stack.merge_status).capitalize,
8
- :activity => deploy.running? ? 'Building' : 'Sleeping',
9
- :lastBuildTime => deploy.ended_at || deploy.started_at || deploy.created_at,
10
- :lastBuildLabel => deploy.id,
11
- :webUrl => stack_url(stack),
12
- }
5
+ xml.Project(
6
+ '',
7
+ name: stack.to_param,
8
+ lastBuildStatus: status_map.fetch(stack.merge_status, stack.merge_status).capitalize,
9
+ activity: deploy.running? ? 'Building' : 'Sleeping',
10
+ lastBuildTime: deploy.ended_at || deploy.started_at || deploy.created_at,
11
+ lastBuildLabel: deploy.id,
12
+ webUrl: stack_url(stack),
13
+ )
13
14
  end
@@ -5,9 +5,9 @@
5
5
  <% cache deploy.author do %>
6
6
  <%= render 'shipit/shared/author', author: deploy.author %>
7
7
  <% end %>
8
- <a href="<%= stack_deploy_path(@stack, deploy) %>" class="status status--<%= deploy.status %>" data-tooltip="<%= deploy.status.capitalize %>">
8
+ <a href="<%= stack_deploy_path(@stack, deploy) %>" class="status status--<%= deploy.status %>" data-tooltip="<%= deploy_status_in_words(deploy.status) %>">
9
9
  <i class="status__icon"></i>
10
- <span class="visually-hidden"><%= deploy.status %></span>
10
+ <span class="visually-hidden"><%= deploy_status_in_words(deploy.status) %></span>
11
11
  </a>
12
12
  <div class="commit-details">
13
13
  <span class="commit-title">
@@ -21,6 +21,7 @@
21
21
  <span class="code-additions">+<%= deploy.additions %></span>
22
22
  <span class="code-deletions">-<%= deploy.deletions %></span>
23
23
  <% if deploy.ignored_safeties? %><span class="ignored-safeties">ignoring safeties</span><% end %>
24
+ <% if deploy.aborted_by %><span class="aborted-by">aborted by <%= deploy.aborted_by.login %></span><% end %>
24
25
  </p>
25
26
  <p class="commit-meta">
26
27
  <% if read_only %>
@@ -36,7 +36,10 @@
36
36
  <div class="banner__content">
37
37
  <h2 class="banner__title">
38
38
  <i class="icon icon--lock"></i>
39
- Deploys are locked by <%= stack.lock_author.name %>
39
+ Deploys were locked by <%= stack.lock_author.name %>
40
+ <% unless stack.locked_since.nil? %>
41
+ <%= timeago_tag(stack.locked_since, force: true) %>
42
+ <% end %>
40
43
  </h2>
41
44
  <p class="banner__text">
42
45
  <%= auto_link emojify(stack.lock_reason) %>
@@ -13,6 +13,10 @@
13
13
  <%= f.text_field :environment, placeholder: 'production' %>
14
14
  </div>
15
15
 
16
+ <div class="field-wrapper">
17
+ <span>Branch: <%= @stack.branch %></span>
18
+ </div>
19
+
16
20
  <div class="field-wrapper">
17
21
  <%= f.label :deploy_url, 'Deploy URL (Where is this stack deployed to?)' %>
18
22
  <%= f.text_field :deploy_url, placeholder: 'https://' %>
@@ -9,7 +9,7 @@
9
9
  <% group.statuses.each do |status| %>
10
10
  <div class="status-item status-item--<%= status.state %> <%= :ignored if status.allowed_to_fail? %>">
11
11
  <i class="status-item__icon"></i>
12
- <a href="<%= status.target_url %>" target="_blank">
12
+ <a href="<%= status.target_url %>" <% unless status.target_url.present? %> disabled class="disabled" <% end %> target="_blank">
13
13
  <strong class="status-item__service"><%= status.context %></strong>
14
14
  </a>
15
15
  <span class="status-item__description">&mdash; <%= status.description %></span>
@@ -1,4 +1,4 @@
1
- <a class="status status--<%= status.state %> <%= :disabled unless status.target_url.present? %>" <% unless status.group? %>data-tooltip="<%= status.description.presence || status.state.capitalize %>"<% end %> href="<%= status.target_url%>" target="_blank">
1
+ <a class="status status--<%= status.state %> <%= 'disabled' unless status.target_url.present? %>" <%= 'disabled' unless status.target_url.present? %> <% unless status.group? %>data-tooltip="<%= status.description.presence || status.state.capitalize %>"<% end %> href="<%= status.target_url%>" target="_blank">
2
2
  <i class="status__icon"></i>
3
3
  <span class="visually-hidden"><%= status.state %></span>
4
4
  </a>
@@ -26,8 +26,7 @@
26
26
  <% else %>
27
27
  <%= timeago_tag(task.created_at, force: true) %>
28
28
  <% end %>
29
-
30
29
  </p>
31
30
  </div>
32
31
  </li>
33
- <% end %>
32
+ <% end %>
@@ -27,6 +27,7 @@ en:
27
27
  deploy_button:
28
28
  hint:
29
29
  max_commits: It is recommended not to deploy more than %{maximum} commits at once.
30
+ blocked: This commit range includes a commit that can't be deployed.
30
31
  caption:
31
32
  pending: Pending CI
32
33
  failure: Failing CI
@@ -36,6 +37,7 @@ en:
36
37
  deploying: A Deploy is in Progress
37
38
  allowed: Deploy
38
39
  missing: Missing CI
40
+ blocked: Blocked
39
41
  redeploy_button:
40
42
  caption:
41
43
  deploying: A Deploy is in Progress
@@ -13,10 +13,6 @@ github_oauth:
13
13
  secret:
14
14
  # teams: # Optional
15
15
 
16
- # To work on the beta bootstrap re-write
17
- # features:
18
- # - bootstrap
19
-
20
16
  # To work on the kubernetes deploy script
21
17
  # env:
22
18
  # KUBECONFIG: # Path of the kubeconfig you want to use.
@@ -1,4 +1,4 @@
1
- host: 'http://shipit-engine.localhost'
1
+ host: 'https://shipit-engine.myshopify.io'
2
2
  redis_url: 'redis://shipit-engine.railgun:6379'
3
3
 
4
4
  github_api:
@@ -13,10 +13,6 @@ github_oauth:
13
13
  secret:
14
14
  # teams: # Optional
15
15
 
16
- # To work on the beta bootstrap re-write
17
- # features:
18
- # - bootstrap
19
-
20
16
  # To work on the kubernetes deploy script
21
17
  # env:
22
18
  # KUBECONFIG: # Path of the kubeconfig you want to use.
@@ -0,0 +1,7 @@
1
+ class ReindexDeliveries < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_index :deliveries, [:hook_id, :status]
4
+ remove_index :deliveries, [:created_at]
5
+ remove_index :deliveries, [:status, :event]
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class AddBaseInfoToPullRequest < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :pull_requests, :base_ref, :string, limit: 1024
4
+ add_column :pull_requests, :base_commit_id, :integer
5
+ add_foreign_key :pull_requests, :commits, column: :base_commit_id
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class AddAbortedByToTasks < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :tasks, :aborted_by_id, :integer
4
+ end
5
+ end
@@ -8,11 +8,19 @@ module Shipit
8
8
  MAX_READ = 64.kilobytes
9
9
 
10
10
  Error = Class.new(StandardError)
11
- Failed = Class.new(Error)
12
11
  NotFound = Class.new(Error)
13
12
  Denied = Class.new(Error)
14
13
  TimedOut = Class.new(Error)
15
14
 
15
+ class Failed < Error
16
+ attr_reader :exit_code
17
+
18
+ def initialize(message, exit_code)
19
+ super(message)
20
+ @exit_code = exit_code
21
+ end
22
+ end
23
+
16
24
  attr_reader :out, :code, :chdir, :env, :args, :pid, :timeout
17
25
 
18
26
  def initialize(*args, default_timeout: Shipit.default_inactivity_timeout, env: {}, chdir:)
@@ -44,7 +52,7 @@ module Shipit
44
52
  end
45
53
 
46
54
  def success?
47
- code == 0
55
+ !code.nil? && code.zero?
48
56
  end
49
57
 
50
58
  def exit_message
@@ -121,7 +129,7 @@ module Shipit
121
129
 
122
130
  def stream!(&block)
123
131
  stream(&block)
124
- raise Failed.new(exit_message) unless success?
132
+ raise Failed.new(exit_message, code) unless success?
125
133
  self
126
134
  end
127
135
 
data/lib/shipit/engine.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'bootstrap'
2
-
3
1
  module Shipit
4
2
  class Engine < ::Rails::Engine
5
3
  isolate_namespace Shipit
@@ -17,8 +15,6 @@ module Shipit
17
15
  task.js
18
16
  shipit.js
19
17
  shipit.css
20
- shipit_bs.js
21
- shipit_bs.css
22
18
  )
23
19
  app.config.assets.precompile << proc do |path|
24
20
  path =~ %r{\Aplugins/[\-\w]+\.(js|css)\Z}
@@ -26,7 +26,7 @@ module Shipit
26
26
  end
27
27
 
28
28
  def fetch_deployed_revision
29
- with_temporary_working_directory do |dir|
29
+ with_temporary_working_directory(commit: @stack.commits.last) do |dir|
30
30
  spec = DeploySpec::FileSystem.new(dir, @stack.environment)
31
31
  outputs = spec.fetch_deployed_revision_steps!.map do |command_line|
32
32
  Command.new(command_line, env: env, chdir: dir).run
@@ -42,6 +42,8 @@ module Shipit
42
42
  end
43
43
 
44
44
  def with_temporary_working_directory(commit: nil)
45
+ commit ||= @stack.last_deployed_commit.presence || @stack.commits.last
46
+
45
47
  @stack.acquire_git_cache_lock do
46
48
  if !commit || !fetched?(commit).tap(&:run).success?
47
49
  fetch.run!
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.20.0'.freeze
2
+ VERSION = '0.21.0'.freeze
3
3
  end
data/lib/shipit.rb CHANGED
@@ -30,8 +30,6 @@ require 'faraday-http-cache'
30
30
 
31
31
  require 'shipit/version'
32
32
 
33
- require 'shipit/template_renderer_extension'
34
-
35
33
  require 'shipit/paginator'
36
34
  require 'shipit/null_serializer'
37
35
  require 'shipit/csv_serializer'
@@ -56,8 +54,9 @@ module Shipit
56
54
 
57
55
  delegate :table_name_prefix, to: :secrets
58
56
 
59
- attr_accessor :disable_api_authentication
60
- attr_writer :automatically_prepend_bundle_exec
57
+ attr_accessor :disable_api_authentication, :timeout_exit_codes
58
+
59
+ self.timeout_exit_codes = [].freeze
61
60
 
62
61
  def app_name
63
62
  @app_name ||= secrets.app_name || Rails.application.class.name.split(':').first || 'Shipit'
@@ -138,6 +137,18 @@ module Shipit
138
137
  secrets.host.presence
139
138
  end
140
139
 
140
+ def enforce_publish_config
141
+ secrets.enforce_publish_config.presence
142
+ end
143
+
144
+ def npm_org_scope
145
+ secrets.npm_org_scope.presence
146
+ end
147
+
148
+ def private_npm_registry
149
+ secrets.private_npm_registry.presence
150
+ end
151
+
141
152
  def github_teams
142
153
  @github_teams ||= github_teams_handles.map { |t| Team.find_or_create_by_handle(t) }
143
154
  end
@@ -195,29 +206,10 @@ module Shipit
195
206
  end
196
207
  end
197
208
 
198
- def feature_bootstrap?
199
- secrets.features.try!(:include?, 'bootstrap')
200
- end
201
-
202
- def bootstrap_view_path
203
- @bootstrap_view_path ||= Engine.root.join('app/views/bootstrap')
204
- end
205
-
206
209
  def default_inactivity_timeout
207
210
  secrets.commands_inactivity_timeout || 5.minutes.to_i
208
211
  end
209
212
 
210
- def automatically_prepend_bundle_exec
211
- unless defined?(@automatically_prepend_bundle_exec)
212
- ActiveSupport::Deprecation.warn(
213
- 'Automatically prepending `bundle exec` will be removed in a future version of Shipit, '\
214
- 'set `Shipit.automatically_prepend_bundle_exec = false` to test the new behaviour.',
215
- )
216
- @automatically_prepend_bundle_exec = true
217
- end
218
- @automatically_prepend_bundle_exec
219
- end
220
-
221
213
  protected
222
214
 
223
215
  def revision_file
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+
3
+ VERSION=$(node --eval "console.log(require('./lerna.json').version);")
4
+ echo -e "\033[0;32mTrying to publish version $VERSION\033[0m"
5
+
6
+ git tag | grep "^v$VERSION$" > /dev/null
7
+ if [ $? != '0' ]; then
8
+ echo -e "\033[1;31mYou need to create the \033[0;33mv$VERSION\033[1;31m tag first.\033[0m"
9
+ exit 1
10
+ fi
11
+
12
+ TAG_REV=$(git rev-list -n1 "v$VERSION")
13
+ HEAD_REV=$(git rev-parse HEAD)
14
+
15
+ if [ "$TAG_REV" != "$HEAD_REV" ]; then
16
+ echo -e "\033[1;31mYou're attempting to publish \033[0;33m$HEAD_REV\033[1;31m as \033[0;33mv$VERSION\033[1;31m but it already points to \033[0;33m$TAG_REV\033[1;31m.\033[0m"
17
+ exit 1
18
+ fi
19
+
20
+ echo -e "\033[0;32mAll clear!\033[0m"
21
+ exit 0
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+
3
+ TAGS=$(git tag --points-at HEAD)
4
+
5
+ if [ -z "$TAGS" ]; then
6
+ HEAD_REV=$(git rev-parse HEAD)
7
+ echo -e "\033[1;31mNo tags associated with $HEAD_REV. Did you push your commits and tags?\033[0m"
8
+ exit 1
9
+ fi
10
+
11
+ for TAG in $TAGS
12
+ do
13
+ if [[ "${TAG}" == @* ]]; then
14
+ TAG=$(echo "$TAG" | cut -d \/ -f 2)
15
+ fi
16
+
17
+ PACKAGE_NAME=$(echo "$TAG" | cut -d \@ -f 1)
18
+ TAG_PACKAGE_VERSION=$(echo "$TAG" | cut -d \@ -f 2)
19
+ ACTUAL_PACKAGE_VERSION=$(node -e "console.log(require('./packages/$PACKAGE_NAME/package.json').version)")
20
+
21
+ if [ "$TAG_PACKAGE_VERSION" != "$ACTUAL_PACKAGE_VERSION" ]; then
22
+ echo -e "\033[1;31m$PACKAGE_NAME is tagged as $TAG_PACKAGE_VERSION, but its package.json version is $ACTUAL_PACKAGE_VERSION.\033[0m"
23
+ exit 1
24
+ fi
25
+ done
26
+
27
+ echo -e "\033[0;32mAll clear!\033[0m"
28
+ exit 0
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ const {resolve} = require('path');
3
+ const {existsSync, writeFileSync, readFileSync} = require('fs');
4
+ const npmrc = resolve('.', '.npmrc');
5
+ const npmrcContents = process.argv[2];
6
+
7
+ if (!npmrcContents) {
8
+ console.error('npmrc contents not specified.');
9
+ process.exit(1);
10
+ }
11
+
12
+ if (existsSync(npmrc)) {
13
+ console.log('.npmrc already exists.')
14
+ } else {
15
+ writeFileSync(npmrc, npmrcContents)
16
+
17
+ console.log('\nGenerated .npmrc:');
18
+ console.log(readFileSync(npmrc, {encoding: 'utf8'}))
19
+ }
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ echo -e "\033[1;31m Misconfigured package.json, please ensure the following has been met:\n \033[0m"
4
+
5
+ echo -e "\033[1;31m * Please add a publishConfig with an access setting in your package.json. e.g.,\033[0m"
6
+ echo -e "\n\033[1;31m\t\"publishConfig:\" {\n\t \"access\": \"<public|restricted>\" \n\t} \033[0m"
7
+ echo -e "\n\033[1;31m * If your package is private, ensure your the package name is scoped (@organization/package) \033[0m"
8
+ exit 1
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ const {resolve} = require('path');
3
+ const {execSync} = require('child_process')
4
+ const Repository = require(resolve('.', 'node_modules', 'lerna', 'lib', 'Repository'));
5
+ const PackageUtilities = require(resolve('.', 'node_modules', 'lerna', 'lib', 'PackageUtilities'));
6
+
7
+ function npmTag(version) {
8
+ const isNext = ['-beta', '-alpha', '-rc', '-next'].some(distributionType =>
9
+ version.includes(distributionType),
10
+ );
11
+ return isNext ? 'next' : 'latest';
12
+ }
13
+
14
+ const taggedPackages = execSync('git tag --points-at HEAD')
15
+ .toString()
16
+ .trim()
17
+ .split('\n')
18
+ .map(tag => {
19
+ if (tag.startsWith('@')) {
20
+ return `@${tag.split('@')[1]}`;
21
+ }
22
+ return tag.split('@')[0];
23
+ });
24
+
25
+ const repository = new Repository('.');
26
+ const unsortedPackages = PackageUtilities.getPackages({rootPath: '.', packageConfigs: repository.packageConfigs});
27
+ const packages = PackageUtilities.topologicallyBatchPackages(unsortedPackages, true)
28
+ .reduce((acc, packageGroup) => [...acc, ...packageGroup], [])
29
+ .filter(({name}) => taggedPackages.includes(name));
30
+
31
+ packages.forEach(({name, version}) => {
32
+ const command = `node_modules/.bin/lerna publish --yes --npm-client=npm --skip-npm=false --skip-git --force-publish=${name} --repo-version=${version} --scope=${name} --npm-tag=${npmTag(
33
+ version,
34
+ )}`;
35
+
36
+ // eslint-disable-next-line no-console
37
+ console.log(command);
38
+ require('child_process').execSync(command);
39
+ });
@@ -23,17 +23,17 @@ if [[ -z "${HEROKU_APP}" ]]; then
23
23
  exit 1;
24
24
  fi
25
25
 
26
- echo "$HEROKU_RELEASES" | head -n2 | tail -n1 | grep '^\(v[0-9]\+\)' >/dev/null
26
+ echo "$HEROKU_RELEASES" | grep 'Current: \(v[0-9]\+\)' >/dev/null
27
27
 
28
28
  if [[ $? -ne 0 ]]; then
29
29
  red "Error detecting current heroku release. Message from 'heroku releases':"
30
30
  echo "${HEROKU_RELEASES}"
31
31
  red "Please contact a heroku collaborator for ${HEROKU_APP}:"
32
- heroku sharing --app $HEROKU_APP
32
+ heroku access --app $HEROKU_APP
33
33
  exit 1;
34
34
  fi
35
35
 
36
- HEROKU_LAST_GOOD_RELEASE=`echo "$HEROKU_RELEASES" | head -n2 | tail -n1 | sed -e 's/^\(v[0-9]\+\).*/\1/'`
36
+ HEROKU_LAST_GOOD_RELEASE=`echo "$HEROKU_RELEASES" | grep 'Current: \(v[0-9]\+\)' | sed -e 's/.*\(v[0-9]\+\).*/\1/'`
37
37
  green "Current heroku release: ${HEROKU_LAST_GOOD_RELEASE}"
38
38
 
39
39
  green "Pushing to heroku."
@@ -41,7 +41,7 @@ $GIT_COMMAND
41
41
  if [[ $? -ne 0 ]]; then
42
42
  red "Heroku push rejected. See message above."
43
43
  red "Please contact a heroku collaborator for ${HEROKU_APP}:"
44
- heroku sharing --app $HEROKU_APP
44
+ heroku access --app $HEROKU_APP
45
45
  exit 1;
46
46
  fi
47
47
 
@@ -58,7 +58,7 @@ if [[ "${MIGRATION_STATUS}" != "" ]]; then
58
58
  if [[ "${POST_MIGRATION_STATUS}" != "" ]]; then
59
59
  red "Migration failed! This is bad."
60
60
  red "Please contact a heroku collaborator for ${HEROKU_APP}:"
61
- heroku sharing --app $HEROKU_APP
61
+ heroku access --app $HEROKU_APP
62
62
  red "The app ${HEROKU_APP} will now be reverted to its last working version ${HEROKU_LAST_GOOD_RELEASE}."
63
63
  heroku releases:rollback $HEROKU_LAST_GOOD_RELEASE --app $HEROKU_APP
64
64
  red "Abort."
data/lib/tasks/cron.rake CHANGED
@@ -7,7 +7,7 @@ namespace :cron do
7
7
  Shipit::PullRequest.schedule_merges
8
8
  end
9
9
 
10
- task hourly: [:rollup, :refresh_users]
10
+ task hourly: %i(rollup refresh_users)
11
11
 
12
12
  desc "Rolls-up output chunks for completed deploys older than an hour"
13
13
  task rollup: :environment do
data/lib/tasks/dev.rake CHANGED
@@ -15,7 +15,7 @@ namespace :dev do
15
15
 
16
16
  loop do
17
17
  sentence = Faker::Lorem.sentence.split.map do |word|
18
- if rand(4) == 0
18
+ if rand(4).zero?
19
19
  "\e[#{rand(30..37)}m#{word}\e[0m"
20
20
  else
21
21
  word
@@ -40,6 +40,13 @@ module Shipit
40
40
  deploy.user == @user
41
41
  end
42
42
 
43
+ test "#create normalises the claimed user" do
44
+ request.headers['X-Shipit-User'] = @user.login.swapcase
45
+ post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
46
+ deploy = Deploy.last
47
+ assert_equal deploy.user, @user
48
+ end
49
+
43
50
  test "#create renders a 422 if the sha isn't found" do
44
51
  post :create, params: {stack_id: @stack.to_param, sha: '123443543545'}
45
52
  assert_response :unprocessable_entity
@@ -52,6 +59,18 @@ module Shipit
52
59
  assert_json 'errors', 'sha' => ['is too short (minimum is 6 characters)']
53
60
  end
54
61
 
62
+ test "#create renders a 409 if a concurrent task is already running" do
63
+ assert_difference -> { @stack.deploys.count }, 1 do
64
+ post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
65
+ end
66
+
67
+ assert_no_difference -> { @stack.deploys.count } do
68
+ post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
69
+ end
70
+
71
+ assert_response :conflict
72
+ end
73
+
55
74
  test "#create refuses to deploy locked stacks" do
56
75
  @stack.update!(lock_reason: 'Something broken')
57
76
 
@@ -60,7 +60,7 @@ module Shipit
60
60
  assert_response :ok
61
61
  assert_json '0.id', stack.id
62
62
  assert_json do |stacks|
63
- assert_equal 3, stacks.size
63
+ assert_equal Stack.count, stacks.size
64
64
  end
65
65
  end
66
66
 
@@ -24,7 +24,7 @@ module Shipit
24
24
  get :callback
25
25
  end
26
26
 
27
- user = User.find_by_login('shipit-user')
27
+ user = User.find_by(login: 'shipit-user')
28
28
  assert_equal 's3cr3t', user.github_access_token
29
29
  assert_equal 44, user.github_id
30
30
  end
@@ -149,6 +149,11 @@ module Shipit
149
149
  assert_redirected_to stack_settings_path(@stack)
150
150
  end
151
151
 
152
+ test "#sync_webhooks displays a flash message" do
153
+ post :sync_webhooks, params: {id: @stack.to_param}
154
+ assert_equal 'Webhooks syncing scheduled', flash[:success]
155
+ end
156
+
152
157
  test "#clear_git_cache queues a ClearGitCacheJob" do
153
158
  assert_enqueued_with(job: ClearGitCacheJob, args: [@stack]) do
154
159
  post :clear_git_cache, params: {id: @stack.to_param}
@@ -156,6 +161,11 @@ module Shipit
156
161
  assert_redirected_to stack_settings_path(@stack)
157
162
  end
158
163
 
164
+ test "#clear_git_cache displays a flash message" do
165
+ post :clear_git_cache, params: {id: @stack.to_param}
166
+ assert_equal 'Git Cache clearing scheduled', flash[:success]
167
+ end
168
+
159
169
  test "#update redirects to return_to parameter" do
160
170
  patch :update, params: {id: @stack.to_param, stack: {ignore_ci: false}, return_to: stack_path(@stack)}
161
171
  assert_redirected_to stack_path(@stack)