shipit-engine 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3b3153e7b24f260f276933b246bca9d9bc2ebdd
4
- data.tar.gz: 93ca93e3b0d8347a453979039c8cb16159ed044c
3
+ metadata.gz: 044a58eabe4689b586de4daf11cca00b72cf7d37
4
+ data.tar.gz: 39509907d1d0ae00f7573f699b6c2c9e63cf9999
5
5
  SHA512:
6
- metadata.gz: 7adfee4675b580ed2eba9e0f5057ba4647d51c9ce2b604ccfb598afbdbd9d8601841689e746de78d879975994d3b54eab450d8f0e11400774272702c9d6b399e
7
- data.tar.gz: 1cb49f0e1c633f95cf293b9df55314bc6f800920b59308cbcebb58aeaa56d236877762d74410b8f1088d85d651db8b0eb59b3b26a4750c2f3e5c2eb13d2896f9
6
+ metadata.gz: eef3d23d0c0b3cb858e7986332fda01cbfcfdc02a48cb7b3ea486d304e818366ea9365db89b1657354f3e90bb3ccb49818a0ea667c141eb6e39207d95ea2ab89
7
+ data.tar.gz: 1f96ec3403295af649196bcdc38a13bdbe6212931022ccb1b7882e638f3d4a80f8f820dc3fdb7b39ee7b4e45ff450c59d79626799a0fb5f2f212d415d818e46a
data/README.md CHANGED
@@ -118,7 +118,7 @@ The settings in the `shipit.yml` file relate to the different things you can do
118
118
 
119
119
  * [Installing dependencies](#installing-dependencies) (`dependencies`)
120
120
  * [Deployment](#deployment) (`deploy`, `rollback`, `fetch`)
121
- * [Environment](#environment) (`machine.environment`)
121
+ * [Environment](#environment) (`machine.environment`, `machine.directory`, `machine.cleanup`)
122
122
  * [CI](#ci) (`ci.require`, `ci.hide`, `ci.allow_failures`)
123
123
  * [Custom tasks](#custom-tasks) (`restart`, `unlock`)
124
124
  * [Review Process](#review-process) (`monitor`, `checklist`, `checks`)
@@ -172,9 +172,33 @@ dependencies:
172
172
  - npm install
173
173
  ```
174
174
 
175
+ **<code>dependencies.pre</code>** If you wish to execute commands before Shipit install the dependencies you can specify them here:
176
+
177
+ For example:
178
+
179
+ ```yml
180
+ depedencies:
181
+ pre:
182
+ - mkdir tmp/
183
+ - cp -R /var/cache/ tmp/cache
184
+ ```
185
+ <br>
186
+
187
+ **<code>dependencies.post</code>** If you wish to execute commands after Shipit installed the dependencies you can specify them here:
188
+
189
+ For example:
190
+
191
+ ```yml
192
+ depedencies:
193
+ post:
194
+ - cp -R tmp/cache /var/cache/
195
+ ```
196
+ <br>
197
+
198
+
175
199
  <h3 id="deployment">Deployment</h3>
176
200
 
177
- The `override` and `deployment` deployment tasks are the core of Shipit:
201
+ The `deploy` and `rollback` sections are the core of Shipit:
178
202
 
179
203
  **<code>deploy.override</code>** contains an array of the shell commands required to deploy the application. Shipit will try to infer it from the repository structure, but you can change the default inference.
180
204
 
@@ -187,6 +211,29 @@ deploy:
187
211
  ```
188
212
  <br>
189
213
 
214
+ **<code>deploy.pre</code>** If you wish to execute commands before Shipit execute your deploy script you can specify them here:
215
+
216
+ For example:
217
+
218
+ ```yml
219
+ deploy:
220
+ pre:
221
+ - ./script/notify_deploy_start
222
+ ```
223
+ <br>
224
+
225
+ **<code>deploy.post</code>** If you wish to execute commands after Shipit executed your deploy script you can specify them here:
226
+
227
+ For example:
228
+
229
+ ```yml
230
+ deploy:
231
+ post:
232
+ - ./script/notify_deploy_end
233
+ ```
234
+ <br>
235
+
236
+
190
237
  You can also accept custom environment variables defined by the user that trigger the deploy:
191
238
 
192
239
  **<code>deploy.variables</code>** contains an array of variable definitions.
@@ -214,6 +261,29 @@ rollback:
214
261
  ```
215
262
  <br>
216
263
 
264
+ **<code>rollback.pre</code>** If you wish to execute commands before Shipit execute your rollback script you can specify them here:
265
+
266
+ For example:
267
+
268
+ ```yml
269
+ rollback:
270
+ pre:
271
+ - ./script/notify_rollback_start
272
+ ```
273
+ <br>
274
+
275
+ **<code>rollback.post</code>** If you wish to execute commands after Shipit executed your rollback script you can specify them here:
276
+
277
+ For example:
278
+
279
+ ```yml
280
+ rollback:
281
+ post:
282
+ - ./script/notify_rollback_end
283
+ ```
284
+ <br>
285
+
286
+
217
287
  **<code>fetch</code>** contains an array of the shell commands that Shipit executes to check the revision of the currently-deployed version. This key defaults to disabled.
218
288
 
219
289
  For example:
@@ -232,6 +302,26 @@ machine:
232
302
  key: val # things added as environment variables
233
303
  ```
234
304
 
305
+ <h3 id="directory">Directory</h3>
306
+
307
+ **<code>machine.directory</code>** specify a subfolder in which to execute all tasks. Useful for repositories containing multiple applications or if you don't want your deploy scripts to be located at the root.
308
+
309
+ For example:
310
+ ```yml
311
+ machine:
312
+ directory: scripts/deploy/
313
+ ```
314
+
315
+ <h3 id="cleanup">Cleanup</h3>
316
+
317
+ **<code>machine.cleanup</code>** specify wether or not the deploy working directory should cleaned up once the deploy completed. Defaults to `true`, but can be useful to disable temporarily to investigate bugs.
318
+
319
+ For example:
320
+ ```yml
321
+ machine:
322
+ cleanup: false
323
+ ```
324
+
235
325
  <h3 id="ci">CI</h3>
236
326
  **<code>ci.require</code>** contains an array of the [statuses context](https://developer.github.com/v3/repos/statuses/) you want Shipit to disallow deploys if any of them is missing.
237
327
 
@@ -132,6 +132,9 @@
132
132
 
133
133
  .status-group {
134
134
  position: relative; // Required to position the status-items div correctly
135
+ .status-item.ignored {
136
+ opacity: .5;
137
+ }
135
138
  }
136
139
 
137
140
  .status {
@@ -63,9 +63,14 @@
63
63
  flex-wrap: wrap;
64
64
 
65
65
  .deploy-banner-section {
66
+ display: inline-block;
66
67
  padding: .95rem 1.5rem 1.125rem;
67
68
  }
68
69
 
70
+ .stack-link {
71
+ display: none;
72
+ }
73
+
69
74
  .action-buttons {
70
75
  flex: none;
71
76
  }
@@ -75,6 +80,10 @@
75
80
  top: 0;
76
81
  left: 0;
77
82
  right: 0;
83
+
84
+ .stack-link {
85
+ display: inline-block;
86
+ }
78
87
  }
79
88
 
80
89
  &[data-status="running"]:before,
@@ -38,6 +38,15 @@
38
38
  margin: .4em .5em .4em 0;
39
39
  }
40
40
 
41
+ .powered-by {
42
+ float: right;
43
+ position: relative;
44
+ top: .5rem;
45
+ right: 1rem;
46
+ font-size: 12px;
47
+ color: #999;
48
+ }
49
+
41
50
 
42
51
  // LOGO
43
52
  // -----------------------------------------------------------------------------
@@ -11,7 +11,7 @@ module Shipit
11
11
  end
12
12
  run
13
13
  ensure
14
- @task.clear_working_directory
14
+ @commands.clear_working_directory
15
15
  end
16
16
 
17
17
  def run
@@ -9,7 +9,7 @@ module Shipit
9
9
  after_commit { broadcast_update }
10
10
  after_create { stack.update_undeployed_commits_count }
11
11
 
12
- after_commit :schedule_refresh_statuses!, :schedule_fetch_stats!, on: :create
12
+ after_commit :schedule_refresh_statuses!, :schedule_fetch_stats!, :schedule_continuous_delivery, on: :create
13
13
 
14
14
  after_touch :touch_stack
15
15
 
@@ -92,7 +92,7 @@ module Shipit
92
92
 
93
93
  payload = {commit: self, stack: stack, status: new_status.state}
94
94
  Hook.emit(:commit_status, stack, payload.merge(commit_status: new_status)) if previous_status != new_status
95
- if simple_state(previous_status) != simple_state(new_status) && !new_status.pending?
95
+ if previous_status.simple_state != new_status.simple_state && !new_status.pending?
96
96
  Hook.emit(:deployable_status, stack, payload.merge(deployable_status: new_status))
97
97
  end
98
98
  new_status
@@ -141,7 +141,7 @@ module Shipit
141
141
  end
142
142
 
143
143
  def schedule_continuous_delivery
144
- return unless state == 'success' && stack.continuous_deployment? && stack.deployable?
144
+ return unless deployable? && stack.continuous_deployment? && stack.deployable?
145
145
  ContinuousDeliveryJob.perform_later(stack)
146
146
  end
147
147
 
@@ -199,9 +199,5 @@ module Shipit
199
199
  def touch_stack
200
200
  stack.touch
201
201
  end
202
-
203
- def simple_state(status)
204
- status.state == 'error' ? 'failure' : status.state
205
- end
206
202
  end
207
203
  end
@@ -48,6 +48,7 @@ module Shipit
48
48
  stack.github_repo_name,
49
49
  pull_request_head,
50
50
  auto_merge: false,
51
+ required_contexts: [],
51
52
  description: "Via Shipit",
52
53
  environment: stack.environment,
53
54
  )
@@ -14,7 +14,7 @@ module Shipit
14
14
  has_many :commit_deployments, inverse_of: :task, foreign_key: :task_id do
15
15
  GITHUB_STATUSES = {
16
16
  'pending' => 'pending',
17
- 'failed' => 'failed',
17
+ 'failed' => 'failure',
18
18
  'success' => 'success',
19
19
  'error' => 'error',
20
20
  'aborted' => 'error',
@@ -29,8 +29,15 @@ module Shipit
29
29
  self
30
30
  end
31
31
 
32
- def config(*keys)
33
- keys.flatten.reduce(@config) { |h, k| h[k] if h.respond_to?(:[]) }
32
+ def config(*keys, &default)
33
+ default ||= -> { nil }
34
+ keys.flatten.reduce(@config) do |hash, key|
35
+ if hash.is_a?(Hash)
36
+ hash.fetch(key) { return default.call }
37
+ else
38
+ return default.call
39
+ end
40
+ end
34
41
  end
35
42
 
36
43
  def supports_fetch_deployed_revision?
@@ -50,12 +57,16 @@ module Shipit
50
57
  end
51
58
 
52
59
  def dependencies_steps
53
- config('dependencies', 'override') || discover_dependencies_steps || []
60
+ around_steps('dependencies') do
61
+ config('dependencies', 'override') { discover_dependencies_steps || [] }
62
+ end
54
63
  end
55
64
  alias_method :dependencies_steps!, :dependencies_steps
56
65
 
57
66
  def deploy_steps
58
- config('deploy', 'override') || discover_deploy_steps
67
+ around_steps('deploy') do
68
+ config('deploy', 'override') { discover_deploy_steps }
69
+ end
59
70
  end
60
71
 
61
72
  def deploy_steps!
@@ -67,7 +78,9 @@ module Shipit
67
78
  end
68
79
 
69
80
  def rollback_steps
70
- config('rollback', 'override') || discover_rollback_steps
81
+ around_steps('rollback') do
82
+ config('rollback', 'override') { discover_rollback_steps }
83
+ end
71
84
  end
72
85
 
73
86
  def rollback_steps!
@@ -126,8 +139,18 @@ module Shipit
126
139
  config('plugins') || {}
127
140
  end
128
141
 
142
+ def clear_working_directory?
143
+ config('machine', 'cleanup') { true }
144
+ end
145
+
129
146
  private
130
147
 
148
+ def around_steps(section)
149
+ steps = yield
150
+ return unless steps
151
+ config(section, 'pre') { [] } + steps + config(section, 'post') { [] }
152
+ end
153
+
131
154
  def coerce_task_definition(config)
132
155
  config
133
156
  end
@@ -15,6 +15,14 @@ module Shipit
15
15
  DeploySpec.new(cacheable_config)
16
16
  end
17
17
 
18
+ def file(path, root: false)
19
+ if root || directory.blank?
20
+ @app_dir.join(path)
21
+ else
22
+ Pathname.new(File.join(@app_dir, directory, path))
23
+ end
24
+ end
25
+
18
26
  private
19
27
 
20
28
  def cacheable_config
@@ -24,7 +32,11 @@ module Shipit
24
32
  'allow_failures' => soft_failing_statuses,
25
33
  'require' => required_statuses,
26
34
  },
27
- 'machine' => {'environment' => machine_env, 'directory' => directory},
35
+ 'machine' => {
36
+ 'environment' => machine_env,
37
+ 'directory' => directory,
38
+ 'cleanup' => true,
39
+ },
28
40
  'review' => {
29
41
  'checklist' => review_checklist,
30
42
  'monitoring' => review_monitoring,
@@ -49,16 +61,13 @@ module Shipit
49
61
  end
50
62
 
51
63
  def load_config
52
- read_config(file("shipit.#{@env}.yml")) || read_config(file('shipit.yml'))
64
+ read_config(file("shipit.#{@env}.yml", root: true)) ||
65
+ read_config(file('shipit.yml', root: true))
53
66
  end
54
67
 
55
68
  def read_config(path)
56
69
  SafeYAML.load(path.read) if path.exist?
57
70
  end
58
-
59
- def file(path)
60
- @app_dir.join(path)
61
- end
62
71
  end
63
72
  end
64
73
  end
@@ -15,6 +15,7 @@ module Shipit
15
15
  lock
16
16
  commit_status
17
17
  deployable_status
18
+ merge_status
18
19
  ).freeze
19
20
 
20
21
  belongs_to :stack, required: false
@@ -32,6 +33,7 @@ module Shipit
32
33
 
33
34
  class << self
34
35
  def emit(event, stack, payload)
36
+ raise "#{event} is not declared in Shipit::Hook::EVENTS" unless EVENTS.include?(event.to_s)
35
37
  Shipit::EmitEventJob.perform_later(
36
38
  event: event.to_s,
37
39
  stack_id: stack.try!(:id),
@@ -32,6 +32,7 @@ module Shipit
32
32
  after_commit :emit_updated_hooks, on: :update
33
33
  after_commit :emit_removed_hooks, on: :destroy
34
34
  after_commit :broadcast_update, on: :update
35
+ after_commit :emit_merge_status_hooks, on: :update
35
36
  after_commit :setup_hooks, :sync_github, on: :create
36
37
  after_touch :clear_cache
37
38
 
@@ -116,11 +117,25 @@ module Shipit
116
117
  commits.reachable.first.try!(:sha)
117
118
  end
118
119
 
120
+ def merge_status
121
+ if locked?
122
+ 'locked'
123
+ else
124
+ significant_statuses = undeployed_commits.map(&:significant_status)
125
+ last_finalized_status = significant_statuses.reject { |s| %w(pending unknown).include?(s.state) }.first
126
+ last_finalized_status.try!(:simple_state) || 'pending'
127
+ end
128
+ end
129
+
119
130
  def status
120
131
  return :deploying if active_task?
121
132
  :default
122
133
  end
123
134
 
135
+ def undeployed_commits
136
+ commits.reachable.newer_than(last_deployed_commit).order(id: :desc)
137
+ end
138
+
124
139
  def last_successful_deploy
125
140
  deploys_and_rollbacks.success.order(created_at: :desc).first
126
141
  end
@@ -150,11 +165,11 @@ module Shipit
150
165
  end
151
166
 
152
167
  def repo_name=(name)
153
- super(name.try(:downcase))
168
+ super(name.try!(:downcase))
154
169
  end
155
170
 
156
171
  def repo_owner=(name)
157
- super(name.try(:downcase))
172
+ super(name.try!(:downcase))
158
173
  end
159
174
 
160
175
  def repo_http_url
@@ -250,7 +265,7 @@ module Shipit
250
265
 
251
266
  def update_undeployed_commits_count(after_commit = nil)
252
267
  after_commit ||= last_deployed_commit
253
- undeployed_commits = commits.reachable.select('count(*) as count').where('id > ?', after_commit.id)
268
+ undeployed_commits = commits.reachable.newer_than(after_commit).select('count(*) as count')
254
269
  self.class.where(id: id).update_all("undeployed_commits_count = (#{undeployed_commits.to_sql})")
255
270
  end
256
271
 
@@ -335,6 +350,10 @@ module Shipit
335
350
  Hook.emit(:stack, self, action: :removed, stack: self)
336
351
  end
337
352
 
353
+ def emit_merge_status_hooks
354
+ Hook.emit(:merge_status, self, merge_status: merge_status, stack: self)
355
+ end
356
+
338
357
  def ci_enabled_cache_key
339
358
  "stacks:#{id}:ci_enabled"
340
359
  end
@@ -23,10 +23,20 @@ module Shipit
23
23
  )
24
24
  end
25
25
 
26
+ delegate :stack, to: :commit
27
+
28
+ def ignored?
29
+ stack.soft_failing_statuses.include?(context)
30
+ end
31
+
26
32
  def group?
27
33
  false
28
34
  end
29
35
 
36
+ def simple_state
37
+ state == 'error' ? 'failure' : state
38
+ end
39
+
30
40
  private
31
41
 
32
42
  def enable_ci_on_stack
@@ -181,10 +181,6 @@ module Shipit
181
181
  File.join(stack.deploys_path, id.to_s)
182
182
  end
183
183
 
184
- def clear_working_directory
185
- FileUtils.rm_rf(working_directory)
186
- end
187
-
188
184
  def record_status_change
189
185
  @status_changed ||= status_changed?
190
186
  end
@@ -3,6 +3,7 @@ module Shipit
3
3
  def state
4
4
  'unknown'
5
5
  end
6
+ alias_method :simple_state, :state
6
7
 
7
8
  def pending?
8
9
  false
@@ -25,6 +25,7 @@
25
25
  </div>
26
26
 
27
27
  <header class="header">
28
+ <%= link_to "Shipit v#{Shipit::VERSION}", "https://github.com/Shopify/shipit-engine/tree/v#{Shipit::VERSION}", class: 'powered-by' %>
28
29
  <div class="wrapper">
29
30
  <div class="header__inner">
30
31
  <a href="/" class="logo">
@@ -11,7 +11,7 @@
11
11
  <%= Shipit.github_url %>
12
12
  <%= f.text_field :repo_owner, placeholder: 'e.g. Shopify', required: true, class: "repo" %>
13
13
  /
14
- <%= f.text_field :repo_name, required: true, pattern: "^[a-z0-9\-_\.]+$", class: "repo" %>
14
+ <%= f.text_field :repo_name, required: true, pattern: "^[a-zA-Z0-9\-_\.]+$", class: "repo" %>
15
15
  </p>
16
16
  <p>
17
17
  <%= f.label :branch %>
@@ -7,7 +7,7 @@
7
7
  <strong class="status-item__service"><%= group.description %></strong>
8
8
  </div>
9
9
  <% group.statuses.each do |status| %>
10
- <div class="status-item status-item--<%= status.state %>">
10
+ <div class="status-item status-item--<%= status.state %> <%= :ignored if status.ignored? %>">
11
11
  <i class="status-item__icon"></i>
12
12
  <a href="<%= status.target_url %>" target="_blank">
13
13
  <strong class="status-item__service"><%= status.context %></strong>
@@ -9,26 +9,29 @@
9
9
  <div class="deploy-main" data-task="<%= {repo: @stack.github_repo_name, description: task_description(task)}.to_json %>">
10
10
  <span class="deploy-tasks"></span>
11
11
  <div class="deploy-banner" data-status="<%= task.status %>">
12
- <div class="deploy-banner-section">
13
- <a href="#" class="user main-user disabled"><%= task.author.name %></a>
14
- <span class="deploy-status">
15
- <%= content_for :task_title %>
16
- </span>
17
- </div>
12
+ <div class="deploy-banner-section stack-link">
13
+ <%= link_to "Return to #{@stack.repo_name}/#{@stack.environment}", stack_path(@stack) %>
14
+ </div>
15
+ <div class="deploy-banner-section">
16
+ <a href="#" class="user main-user disabled"><%= task.author.name %></a>
17
+ <span class="deploy-status">
18
+ <%= content_for :task_title %>
19
+ </span>
20
+ </div>
18
21
 
19
- <div class="deploy-banner-section action-buttons">
20
- <%= link_to abort_stack_task_path(@stack, task), class: "btn btn--alert action-button", data: { action: "abort", status: task.status } do %>
21
- <span class="caption--ready">Abort</span>
22
- <span class="caption--pending">Aborting...</span>
23
- <% end %>
22
+ <div class="deploy-banner-section action-buttons">
23
+ <%= link_to abort_stack_task_path(@stack, task), class: "btn btn--alert action-button", data: { action: "abort", status: task.status } do %>
24
+ <span class="caption--ready">Abort</span>
25
+ <span class="caption--pending">Aborting...</span>
26
+ <% end %>
24
27
 
25
- <% if task.supports_rollback? %>
26
- <%= link_to abort_stack_task_path(@stack, task, rollback: true), class: "btn btn--delete action-button", data: { action: "abort", rollback: true, status: task.status } do %>
27
- <span class="caption--ready">Abort and Rollback</span>
28
- <span class="caption--pending">Aborting with Rollback...</span>
29
- <% end %>
28
+ <% if task.supports_rollback? %>
29
+ <%= link_to abort_stack_task_path(@stack, task, rollback: true), class: "btn btn--delete action-button", data: { action: "abort", rollback: true, status: task.status } do %>
30
+ <span class="caption--ready">Abort and Rollback</span>
31
+ <span class="caption--pending">Aborting with Rollback...</span>
30
32
  <% end %>
31
- </div>
33
+ <% end %>
34
+ </div>
32
35
  </div>
33
36
 
34
37
  <pre class="nowrap" data-status="<%= task.status %>"><code data-next-chunks-url="<%= next_chunks_url(task) %>"><%= task.chunk_output %></code></pre>
@@ -26,6 +26,7 @@ require 'redis-namespace'
26
26
  require 'octokit'
27
27
  require 'faraday-http-cache'
28
28
 
29
+ require 'shipit/version'
29
30
  require 'shipit/paginator'
30
31
  require 'shipit/null_serializer'
31
32
  require 'shipit/csv_serializer'
@@ -52,6 +52,10 @@ module Shipit
52
52
  @stack_commands = StackCommands.new(@stack)
53
53
  end
54
54
 
55
+ def clear_working_directory
56
+ FileUtils.rm_rf(@task.working_directory) if deploy_spec.clear_working_directory?
57
+ end
58
+
55
59
  protected
56
60
 
57
61
  def steps_directory
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.8.0'
2
+ VERSION = '0.8.1'
3
3
  end
Binary file
@@ -19,7 +19,7 @@ module Shipit
19
19
  @commands.expects(:install_dependencies).returns([]).once
20
20
  @commands.expects(:perform).returns([]).once
21
21
 
22
- @deploy.expects(:clear_working_directory)
22
+ @commands.expects(:clear_working_directory)
23
23
 
24
24
  @job.perform(@deploy)
25
25
  end
@@ -83,7 +83,7 @@ module Shipit
83
83
  @commands.stubs(:install_dependencies).returns([])
84
84
  @commands.stubs(:perform).returns([])
85
85
  DeployCommands.expects(:new).with(@deploy).returns(@commands)
86
- @deploy.stubs(:clear_working_directory)
86
+ @commands.stubs(:clear_working_directory)
87
87
 
88
88
  @stack.update!(cached_deploy_spec: DeploySpec.new({}))
89
89
 
@@ -25,6 +25,7 @@ module Shipit
25
25
  'shopify/shipit-engine',
26
26
  pull_request_response.head.sha,
27
27
  auto_merge: false,
28
+ required_contexts: [],
28
29
  description: "Via Shipit",
29
30
  environment: @stack.environment,
30
31
  ).returns(deployment_response)
@@ -19,6 +19,22 @@ module Shipit
19
19
  refute_predicate @stack, :valid?
20
20
  end
21
21
 
22
+ test "repo_owner and repo_name are case insensitive" do
23
+ assert_no_difference -> { Stack.count } do
24
+ error = assert_raises ActiveRecord::RecordInvalid do
25
+ Stack.create!(
26
+ repo_owner: @stack.repo_owner.upcase,
27
+ repo_name: @stack.repo_name.upcase,
28
+ environment: @stack.environment,
29
+ )
30
+ end
31
+ assert_equal 'Validation failed: Repo name has already been taken', error.message
32
+ end
33
+
34
+ new_stack = Stack.create!(repo_owner: 'FOO', repo_name: 'BAR')
35
+ assert_equal new_stack, Stack.find_by(repo_owner: 'foo', repo_name: 'bar')
36
+ end
37
+
22
38
  test "repo_owner is automatically downcased" do
23
39
  @stack.repo_owner = 'George'
24
40
  assert_equal 'george', @stack.repo_owner
@@ -360,5 +376,34 @@ module Shipit
360
376
  @stack.update(updated_at: Time.zone.now)
361
377
  end
362
378
  end
379
+
380
+ test "#merge_status returns locked if stack is locked" do
381
+ @stack.update!(lock_reason: 'Maintenance operation')
382
+ assert_equal 'locked', @stack.merge_status
383
+ end
384
+
385
+ test "#merge_status returns state of last finalized undeployed commit" do
386
+ @stack.deploys_and_rollbacks.destroy_all
387
+ shipit_commits(:fifth).statuses.destroy_all
388
+ shipit_commits(:fourth).statuses.update_all(state: 'pending')
389
+ shipit_commits(:third).statuses.update_all(state: 'success')
390
+ shipit_commits(:second).statuses.update_all(state: 'failure')
391
+
392
+ assert_equal 'success', @stack.merge_status
393
+ end
394
+
395
+ test "#merge_status returns pending if all undeployed commits are in pending or unknown state" do
396
+ shipit_commits(:fifth).statuses.destroy_all
397
+ shipit_commits(:fourth).statuses.update_all(state: 'pending')
398
+ @stack.expects(:last_deployed_commit).returns(shipit_commits(:third))
399
+
400
+ assert_equal 'pending', @stack.merge_status
401
+ end
402
+
403
+ test "#merge_status returns pending if there are no undeployed commits" do
404
+ @stack.expects(:last_deployed_commit).returns(shipit_commits(:fifth))
405
+
406
+ assert_equal 'pending', @stack.merge_status
407
+ end
363
408
  end
364
409
  end
@@ -32,6 +32,15 @@ module Shipit
32
32
  assert_not_equal stack_last_updated_at, @stack.reload.updated_at
33
33
  end
34
34
 
35
+ test ".simple_state returns failure when status is error" do
36
+ assert_equal 'failure', Status.new(state: 'error').simple_state
37
+ end
38
+
39
+ test ".simple_state returns status when status is not error" do
40
+ assert_equal 'success', Status.new(state: 'success').simple_state
41
+ assert_equal 'failure', Status.new(state: 'failure').simple_state
42
+ end
43
+
35
44
  private
36
45
 
37
46
  def github_status
@@ -12,6 +12,7 @@ module Shipit
12
12
  rollback_steps!: ['bundle exec cap $ENVIRONMENT deploy:rollback'],
13
13
  machine_env: {'GLOBAL' => '1'},
14
14
  directory: nil,
15
+ clear_working_directory?: true,
15
16
  )
16
17
  @commands.stubs(:deploy_spec).returns(@deploy_spec)
17
18
 
@@ -179,5 +180,16 @@ module Shipit
179
180
  command = @commands.install_dependencies.first
180
181
  assert_equal 'BAR', command.env['FOO']
181
182
  end
183
+
184
+ test "#clear_working_directory rm -rf the working directory" do
185
+ FileUtils.expects(:rm_rf).with(@deploy.working_directory)
186
+ @commands.clear_working_directory
187
+ end
188
+
189
+ test "#clear_working_directory is a noop if the deploy spec disabled cleanup" do
190
+ @deploy_spec.expects(:clear_working_directory?).returns(false)
191
+ FileUtils.expects(:rm_rf).never
192
+ @commands.clear_working_directory
193
+ end
182
194
  end
183
195
  end
@@ -3,7 +3,8 @@ require 'test_helper'
3
3
  module Shipit
4
4
  class DeploySpecTest < ActiveSupport::TestCase
5
5
  setup do
6
- @spec = DeploySpec::FileSystem.new('/tmp/', 'env')
6
+ @app_dir = '/tmp/'
7
+ @spec = DeploySpec::FileSystem.new(@app_dir, 'env')
7
8
  @spec.stubs(:load_config).returns({})
8
9
  end
9
10
 
@@ -37,8 +38,15 @@ module Shipit
37
38
 
38
39
  test '#dependencies_steps returns `bundle install` if a `Gemfile` is present' do
39
40
  @spec.expects(:bundler?).returns(true)
40
- @spec.expects(:bundle_install).returns(:bundle_install)
41
- assert_equal :bundle_install, @spec.dependencies_steps
41
+ @spec.expects(:bundle_install).returns(['bundle install'])
42
+ assert_equal ['bundle install'], @spec.dependencies_steps
43
+ end
44
+
45
+ test "#dependencies_steps prepend and append pre and post steps" do
46
+ @spec.stubs(:load_config).returns('dependencies' => {'pre' => ['before'], 'post' => ['after']})
47
+ @spec.expects(:bundler?).returns(true)
48
+ @spec.expects(:bundle_install).returns(['bundle install'])
49
+ assert_equal ['before', 'bundle install', 'after'], @spec.dependencies_steps
42
50
  end
43
51
 
44
52
  test '#fetch_deployed_revision_steps! is unknown by default' do
@@ -108,6 +116,13 @@ module Shipit
108
116
  assert_equal ['bundle exec cap $ENVIRONMENT deploy'], @spec.deploy_steps
109
117
  end
110
118
 
119
+ test "#deploy_steps prepend and append pre and post steps" do
120
+ @spec.stubs(:load_config).returns('deploy' => {'pre' => ['before'], 'post' => ['after']})
121
+ @spec.expects(:bundler?).returns(true)
122
+ @spec.expects(:capistrano?).returns(true)
123
+ assert_equal ['before', 'bundle exec cap $ENVIRONMENT deploy', 'after'], @spec.deploy_steps
124
+ end
125
+
111
126
  test '#deploy_steps raise a DeploySpec::Error! if it dont know how to deploy the app' do
112
127
  @spec.expects(:capistrano?).returns(false)
113
128
  assert_raise DeploySpec::Error do
@@ -126,6 +141,13 @@ module Shipit
126
141
  assert_equal ['bundle exec cap $ENVIRONMENT deploy:rollback'], @spec.rollback_steps
127
142
  end
128
143
 
144
+ test "#rollback_steps prepend and append pre and post steps" do
145
+ @spec.stubs(:load_config).returns('rollback' => {'pre' => ['before'], 'post' => ['after']})
146
+ @spec.expects(:bundler?).returns(true)
147
+ @spec.expects(:capistrano?).returns(true)
148
+ assert_equal ['before', 'bundle exec cap $ENVIRONMENT deploy:rollback', 'after'], @spec.rollback_steps
149
+ end
150
+
129
151
  test '#machine_env return an environment hash' do
130
152
  @spec.stubs(:load_config).returns('machine' => {'environment' => {'GLOBAL' => '1'}})
131
153
  assert_equal({'GLOBAL' => '1'}, @spec.machine_env)
@@ -136,7 +158,7 @@ module Shipit
136
158
  config.expects(:exist?).returns(true)
137
159
  config.expects(:read).returns({'dependencies' => {'override' => %w(foo bar baz)}}.to_yaml)
138
160
  spec = DeploySpec::FileSystem.new('.', 'staging')
139
- spec.expects(:file).with('shipit.staging.yml').returns(config)
161
+ spec.expects(:file).with('shipit.staging.yml', root: true).returns(config)
140
162
  assert_equal %w(foo bar baz), spec.dependencies_steps
141
163
  end
142
164
 
@@ -149,8 +171,8 @@ module Shipit
149
171
  config.expects(:read).returns({'dependencies' => {'override' => %w(foo bar baz)}}.to_yaml)
150
172
 
151
173
  spec = DeploySpec::FileSystem.new('.', 'staging')
152
- spec.expects(:file).with('shipit.staging.yml').returns(not_config)
153
- spec.expects(:file).with('shipit.yml').returns(config)
174
+ spec.expects(:file).with('shipit.staging.yml', root: true).returns(not_config)
175
+ spec.expects(:file).with('shipit.yml', root: true).returns(config)
154
176
  assert_equal %w(foo bar baz), spec.dependencies_steps
155
177
  end
156
178
 
@@ -204,7 +226,7 @@ module Shipit
204
226
  assert_instance_of DeploySpec, @spec.cacheable
205
227
  config = {
206
228
  'ci' => {'hide' => [], 'allow_failures' => [], 'require' => []},
207
- 'machine' => {'environment' => {}, 'directory' => nil},
229
+ 'machine' => {'environment' => {}, 'directory' => nil, 'cleanup' => true},
208
230
  'review' => {'checklist' => [], 'monitoring' => [], 'checks' => []},
209
231
  'dependencies' => {'override' => []},
210
232
  'plugins' => {},
@@ -315,5 +337,16 @@ module Shipit
315
337
  @spec.expects(:load_config).returns('ci' => {'hide' => %w(ci/circleci ci/jenkins)})
316
338
  assert_equal %w(ci/circleci ci/jenkins), @spec.hidden_statuses
317
339
  end
340
+
341
+ test "#file is impacted by `machine.directory`" do
342
+ subdir = '/foo/bar'
343
+ @spec.stubs(:load_config).returns('machine' => {'directory' => subdir})
344
+ assert_instance_of Pathname, @spec.file('baz')
345
+ assert_equal File.join(@app_dir, subdir, 'baz'), @spec.file('baz').to_s
346
+ end
347
+
348
+ test "#clear_working_directory? returns true by default" do
349
+ assert_predicate @spec, :clear_working_directory?
350
+ end
318
351
  end
319
352
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipit-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-04 00:00:00.000000000 Z
11
+ date: 2016-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -599,7 +599,6 @@ files:
599
599
  - test/controllers/status_controller_test.rb
600
600
  - test/controllers/tasks_controller_test.rb
601
601
  - test/controllers/webhooks_controller_test.rb
602
- - test/dummy/README.rdoc
603
602
  - test/dummy/Rakefile
604
603
  - test/dummy/app/assets/javascripts/application.js
605
604
  - test/dummy/app/assets/stylesheets/application.css
@@ -807,7 +806,6 @@ test_files:
807
806
  - test/dummy/public/500.html
808
807
  - test/dummy/public/favicon.ico
809
808
  - test/dummy/Rakefile
810
- - test/dummy/README.rdoc
811
809
  - test/fixtures/payloads/push_master.json
812
810
  - test/fixtures/payloads/push_not_master.json
813
811
  - test/fixtures/payloads/status_master.json
@@ -1,28 +0,0 @@
1
- == README
2
-
3
- This README would normally document whatever steps are necessary to get the
4
- application up and running.
5
-
6
- Things you may want to cover:
7
-
8
- * Ruby version
9
-
10
- * System dependencies
11
-
12
- * Configuration
13
-
14
- * Database creation
15
-
16
- * Database initialization
17
-
18
- * How to run the test suite
19
-
20
- * Services (job queues, cache servers, search engines, etc.)
21
-
22
- * Deployment instructions
23
-
24
- * ...
25
-
26
-
27
- Please feel free to use a different markup language if you do not plan to run
28
- <tt>rake doc:app</tt>.