shipit-engine 0.10.0 → 0.11.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.
- checksums.yaml +4 -4
- data/README.md +33 -11
- data/app/assets/javascripts/shipit.js.coffee +7 -1
- data/app/assets/stylesheets/_base/_buttons.scss +3 -0
- data/app/assets/stylesheets/_base/_colors.scss +1 -1
- data/app/assets/stylesheets/_base/_status-items.scss +1 -1
- data/app/assets/stylesheets/_pages/_deploy.scss +1 -1
- data/app/assets/stylesheets/_structure/_main.scss +1 -1
- data/app/controllers/shipit/stacks_controller.rb +1 -5
- data/app/helpers/shipit/shipit_helper.rb +7 -9
- data/app/helpers/shipit/stacks_helper.rb +14 -28
- data/app/jobs/shipit/continuous_delivery_job.rb +1 -1
- data/app/jobs/shipit/update_estimated_deploy_duration.rb +9 -0
- data/app/models/shipit/commit_checks.rb +1 -1
- data/app/models/shipit/deploy.rb +1 -1
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -1
- data/app/models/shipit/deploy_spec/file_system.rb +6 -1
- data/app/models/shipit/deploy_spec.rb +12 -0
- data/app/models/shipit/duration.rb +29 -9
- data/app/models/shipit/github_hook.rb +0 -2
- data/app/models/shipit/stack.rb +48 -11
- data/app/models/shipit/task.rb +13 -2
- data/app/models/shipit/team.rb +1 -1
- data/app/models/shipit/undeployed_commit.rb +36 -0
- data/app/views/layouts/shipit.html.erb +6 -4
- data/app/views/shipit/deploys/show.html.erb +1 -1
- data/app/views/shipit/stacks/_header.html.erb +7 -0
- data/app/views/shipit/stacks/show.html.erb +1 -1
- data/config/locales/en.yml +9 -3
- data/config/secrets.development.example.yml +19 -0
- data/config/secrets.development.shopify.yml +19 -0
- data/config/secrets.development.yml +16 -0
- data/db/migrate/20160502150713_add_estimated_deploy_duration_to_stacks.rb +5 -0
- data/db/migrate/20160526192650_reorder_active_tasks_index.rb +7 -0
- data/lib/shipit/command.rb +15 -2
- data/lib/shipit/engine.rb +2 -1
- data/lib/shipit/stat.rb +13 -0
- data/lib/shipit/version.rb +1 -1
- data/lib/shipit.rb +13 -0
- data/lib/tasks/cron.rake +1 -0
- data/test/controllers/github_authentication_controller_test.rb +5 -5
- data/test/dummy/config/initializers/0_load_development_secrets.rb +9 -0
- data/test/dummy/config/secrets.yml +5 -16
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +14 -14
- data/test/dummy/db/seeds.rb +1 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/shipit/stacks.yml +2 -2
- data/test/fixtures/shipit/tasks.yml +3 -1
- data/test/fixtures/shipit/users.yml +5 -0
- data/test/helpers/queries_helper.rb +1 -1
- data/test/models/deploys_test.rb +7 -0
- data/test/models/duration_test.rb +23 -0
- data/test/models/stacks_test.rb +93 -4
- data/test/models/undeployed_commits_test.rb +100 -0
- data/test/test_helper.rb +1 -0
- data/test/unit/deploy_spec_test.rb +1 -1
- data/test/unit/shipit_test.rb +2 -1
- metadata +19 -10
- data/db/schema.rb +0 -188
- data/test/dummy/config/secrets.example.yml +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4329e9e481f6b7065a401570ebd011d1ea4fb19
|
4
|
+
data.tar.gz: ef736fcc3a561d2db82c35795092388abbbfc90a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e79a3a01a5d2c71d48dc0bc0ec8ae032721046cccecb5680c876b82c0c246da5ce75a4812ee24afe9aeeaca06d5749637bffb6cb2f9b9aa374fe96f2c790cbff
|
7
|
+
data.tar.gz: 60bde57fcfbd57ff17bc6d0d64785b81bf2e2cfd9ee69d5c9849911a5a6936ceccd9ac80fb2a50bb10254a9789084b26f1095acc43b7c7593ebcd19255d93554
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
|
|
33
33
|
* [Format and content of shipit.yml](#configuring-shipit)
|
34
34
|
* [Format and content of secrets.yml](#configuring-secrets)
|
35
35
|
* [Script parameters](#script-parameters)
|
36
|
-
* [Free samples](
|
36
|
+
* [Free samples](/examples/shipit.yml)
|
37
37
|
|
38
38
|
* * *
|
39
39
|
|
@@ -45,8 +45,8 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
|
|
45
45
|
|
46
46
|
Shipit provides you with a Rails template. To bootstrap your Shipit installation:
|
47
47
|
|
48
|
-
1. If you don't have Rails installed, run this command: `gem install rails`
|
49
|
-
2. Run this command: `rails new shipit -m https://raw.githubusercontent.com/Shopify/shipit-engine/master/template.rb`
|
48
|
+
1. If you don't have Rails installed, run this command: `gem install rails -v 4.2.6`
|
49
|
+
2. Run this command: `rails _4.2.6_ new shipit -m https://raw.githubusercontent.com/Shopify/shipit-engine/master/template.rb`
|
50
50
|
3. Enter your **Client ID**, **Client Secret**, and **GitHub API access token** when prompted. These can be found on your application's GitHub page.
|
51
51
|
4. To setup the database, run this command: `rake db:setup`
|
52
52
|
|
@@ -250,6 +250,18 @@ deploy:
|
|
250
250
|
```
|
251
251
|
<br>
|
252
252
|
|
253
|
+
**<code>deploy.max_commits</code>** allow to set a limit to the number of commits being shipped per deploys.
|
254
|
+
|
255
|
+
Human users will be warned that they are not respecting the recommandation, but allowed to continue.
|
256
|
+
|
257
|
+
For example:
|
258
|
+
|
259
|
+
```yaml
|
260
|
+
deploy:
|
261
|
+
max_commits: 5
|
262
|
+
```
|
263
|
+
<br>
|
264
|
+
|
253
265
|
**<code>rollback.override</code>** contains an array of the shell commands required to rollback the application to a previous state. Shipit will try to infer it from the repository structure, but you can change the default inference. This key defaults to disabled unless Capistrano is detected.
|
254
266
|
|
255
267
|
For example:
|
@@ -360,7 +372,7 @@ You can create custom tasks that users execute directly from a stack's overview
|
|
360
372
|
tasks:
|
361
373
|
restart:
|
362
374
|
action: "Restart Application"
|
363
|
-
description: "Sometimes needed if you the application to restart but don't want to ship any new code."
|
375
|
+
description: "Sometimes needed if you want the application to restart but don't want to ship any new code."
|
364
376
|
steps:
|
365
377
|
- ssh deploy@myserver.example.com 'touch myapp/restart.txt'
|
366
378
|
```
|
@@ -382,7 +394,7 @@ Tasks like deploys can prompt for user defined environment variables:
|
|
382
394
|
tasks:
|
383
395
|
restart:
|
384
396
|
action: "Restart Application"
|
385
|
-
description: "Sometimes needed if you the application to restart but don't want to ship any new code."
|
397
|
+
description: "Sometimes needed if you want the application to restart but don't want to ship any new code."
|
386
398
|
steps:
|
387
399
|
- ssh deploy@myserver.example.com 'touch myapp/restart.txt'
|
388
400
|
variables:
|
@@ -444,7 +456,7 @@ The settings in the `secrets.yml` file relate to the ways that GitHub connects w
|
|
444
456
|
For example:
|
445
457
|
|
446
458
|
```yml
|
447
|
-
|
459
|
+
production:
|
448
460
|
secret_key_base: s3cr3t # This needs to be a very long, fully random
|
449
461
|
```
|
450
462
|
<br>
|
@@ -462,7 +474,7 @@ After you change the list of time you have to invoke `bin/rake teams:fetch` in p
|
|
462
474
|
For example:
|
463
475
|
|
464
476
|
```yml
|
465
|
-
|
477
|
+
production:
|
466
478
|
github_oauth:
|
467
479
|
id: (your application's Client ID)
|
468
480
|
secret: (your application's Client Secret)
|
@@ -479,7 +491,7 @@ If you specify an `access_token`, you don't need a `login` and `password`. The o
|
|
479
491
|
For example:
|
480
492
|
|
481
493
|
```yml
|
482
|
-
|
494
|
+
production:
|
483
495
|
github_api:
|
484
496
|
access_token: 10da65c687f6degaf5475ce12a980d5vd8c44d2a
|
485
497
|
```
|
@@ -489,7 +501,7 @@ development:
|
|
489
501
|
|
490
502
|
For example:
|
491
503
|
```yml
|
492
|
-
|
504
|
+
production:
|
493
505
|
host: 'http://localhost:3000'
|
494
506
|
```
|
495
507
|
<br>
|
@@ -499,7 +511,7 @@ development:
|
|
499
511
|
For example:
|
500
512
|
|
501
513
|
```yml
|
502
|
-
|
514
|
+
production:
|
503
515
|
redis_url: "redis://127.0.0.1:6379/7"
|
504
516
|
```
|
505
517
|
|
@@ -509,11 +521,21 @@ If you use GitHub Enterprise, you must also specify the `github_domain`.
|
|
509
521
|
|
510
522
|
For example:
|
511
523
|
```yml
|
512
|
-
|
524
|
+
production:
|
513
525
|
github_domain: "github.example.com"
|
514
526
|
|
515
527
|
```
|
516
528
|
|
529
|
+
<br>
|
530
|
+
|
531
|
+
**`commands_inactivity_timeout`** is the duration after which shipit will terminate a command if no ouput was received. Default is `300` (5 minutes).
|
532
|
+
|
533
|
+
For example:
|
534
|
+
```yml
|
535
|
+
production:
|
536
|
+
commands_inactivity_timeout: 900 # 15 minutes
|
537
|
+
```
|
538
|
+
|
517
539
|
<h2 id="script-parameters">Script parameters</h2>
|
518
540
|
|
519
541
|
Your deploy scripts have access to the following environment variables:
|
@@ -20,8 +20,14 @@
|
|
20
20
|
$(document).on 'click', '.disabled, .btn--disabled', (event) ->
|
21
21
|
event.preventDefault()
|
22
22
|
|
23
|
+
$(document).on 'click', '.enable-notifications .banner__dismiss', (event) ->
|
24
|
+
$(event.target).closest('.banner').addClass('hidden')
|
25
|
+
localStorage.setItem("dismissed-enable-notifications", true)
|
26
|
+
|
23
27
|
jQuery ->
|
24
|
-
|
28
|
+
if(localStorage.getItem("dismissed-enable-notifications"))
|
29
|
+
return
|
30
|
+
$notificationNotice = $('.enable-notifications')
|
25
31
|
|
26
32
|
if $.notifyCheck() == $.NOTIFY_NOT_ALLOWED
|
27
33
|
$button = $notificationNotice.find('button')
|
@@ -17,11 +17,7 @@ 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.
|
21
|
-
if deployed_commit = @stack.last_deployed_commit
|
22
|
-
@commits = @commits.where('id > ?', deployed_commit.id)
|
23
|
-
end
|
24
|
-
@commits = @commits.to_a
|
20
|
+
@commits = @stack.undeployed_commits { |scope| scope.preload(:author, :statuses) }
|
25
21
|
end
|
26
22
|
|
27
23
|
def lookup
|
@@ -19,9 +19,7 @@ module Shipit
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def include_plugins(stack)
|
22
|
-
stack.plugins.flat_map
|
23
|
-
plugin_tags(plugin, config)
|
24
|
-
end.join.html_safe
|
22
|
+
safe_join(stack.plugins.flat_map { |plugin, config| plugin_tags(plugin, config) })
|
25
23
|
end
|
26
24
|
|
27
25
|
def plugin_tags(plugin, config)
|
@@ -33,7 +31,7 @@ module Shipit
|
|
33
31
|
end
|
34
32
|
|
35
33
|
def missing_github_oauth_message
|
36
|
-
|
34
|
+
<<-MESSAGE.html_safe
|
37
35
|
Shipit requires a GitHub application to authenticate users.
|
38
36
|
If you haven't created an application on GitHub yet, you can do so in the
|
39
37
|
#{link_to 'Settings', Shipit.github_url('/settings/applications/new'), target: '_blank'}
|
@@ -42,21 +40,21 @@ module Shipit
|
|
42
40
|
end
|
43
41
|
|
44
42
|
def missing_github_oauth_id_message
|
45
|
-
|
43
|
+
<<-MESSAGE.html_safe
|
46
44
|
Copy the Client ID from your GitHub application,
|
47
45
|
and paste it into the secrets.yml file under <code>github_oauth.id</code>.
|
48
46
|
MESSAGE
|
49
47
|
end
|
50
48
|
|
51
49
|
def missing_github_oauth_secret_message
|
52
|
-
|
50
|
+
<<-MESSAGE.html_safe
|
53
51
|
Copy the Client Secret from your GitHub application,
|
54
52
|
and paste it into the secrets.yml file under <code>github_oauth.secret</code>.
|
55
53
|
MESSAGE
|
56
54
|
end
|
57
55
|
|
58
56
|
def missing_github_api_credentials_message
|
59
|
-
|
57
|
+
<<-MESSAGE.html_safe
|
60
58
|
Shipit needs API access to GitHub. You can
|
61
59
|
#{link_to 'create an access token', Shipit.github_url('/settings/tokens'), target: '_blank'}
|
62
60
|
with the following permissions:
|
@@ -66,14 +64,14 @@ module Shipit
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def missing_redis_url_message
|
69
|
-
|
67
|
+
<<-MESSAGE.html_safe
|
70
68
|
Shipit needs a Redis server. Please configure the Redis URL in the secrets.yml file of your app,
|
71
69
|
under the key <code>redis_url</code>.
|
72
70
|
MESSAGE
|
73
71
|
end
|
74
72
|
|
75
73
|
def missing_host_message
|
76
|
-
|
74
|
+
<<-MESSAGE.html_safe
|
77
75
|
Shipit needs the host of the application before generating links in background jobs.
|
78
76
|
Add the host name to the secrets.yml file, under the <code>host</code> key.
|
79
77
|
MESSAGE
|
@@ -2,33 +2,34 @@ module Shipit
|
|
2
2
|
module StacksHelper
|
3
3
|
COMMIT_TITLE_LENGTH = 79
|
4
4
|
|
5
|
-
def redeploy_button(
|
6
|
-
|
5
|
+
def redeploy_button(deployed_commit)
|
6
|
+
commit = UndeployedCommit.new(deployed_commit, 0)
|
7
|
+
url = new_stack_deploy_path(commit.stack, sha: commit.sha)
|
7
8
|
classes = %W(btn btn--primary deploy-action #{commit.state})
|
8
9
|
|
9
10
|
unless commit.stack.deployable?
|
10
|
-
classes.push(
|
11
|
+
classes.push(bypass_safeties? ? 'btn--warning' : 'btn--disabled')
|
11
12
|
end
|
12
13
|
|
13
|
-
caption
|
14
|
-
caption = 'Locked' if commit.stack.locked? && !ignore_lock?
|
15
|
-
caption = 'Deploy in progress...' if commit.stack.active_task?
|
16
|
-
|
17
|
-
link_to(caption, url, class: classes)
|
14
|
+
link_to(t("redeploy_button.caption.#{commit.redeploy_state(bypass_safeties?)}"), url, class: classes)
|
18
15
|
end
|
19
16
|
|
20
|
-
def
|
17
|
+
def bypass_safeties?
|
21
18
|
params[:force].present?
|
22
19
|
end
|
23
20
|
|
24
21
|
def deploy_button(commit)
|
25
|
-
url = new_stack_deploy_path(
|
22
|
+
url = new_stack_deploy_path(commit.stack, sha: commit.sha)
|
26
23
|
classes = %W(btn btn--primary deploy-action #{commit.state})
|
27
|
-
|
28
|
-
|
24
|
+
data = {}
|
25
|
+
if commit.deploy_disallowed?
|
26
|
+
classes.push(bypass_safeties? ? 'btn--warning' : 'btn--disabled')
|
27
|
+
elsif commit.deploy_discouraged?
|
28
|
+
classes.push('btn--warning')
|
29
|
+
data[:tooltip] = t('deploy_button.hint.max_commits', maximum: commit.stack.maximum_commits_per_deploy)
|
29
30
|
end
|
30
31
|
|
31
|
-
link_to(
|
32
|
+
link_to(t("deploy_button.caption.#{commit.deploy_state(bypass_safeties?)}"), url, class: classes, data: data)
|
32
33
|
end
|
33
34
|
|
34
35
|
def github_change_url(commit)
|
@@ -59,20 +60,5 @@ module Shipit
|
|
59
60
|
def render_raw_commit_id_link(commit)
|
60
61
|
link_to(commit.short_sha, github_commit_url(commit), target: '_blank', class: 'number')
|
61
62
|
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def deploy_button_disabled?(commit)
|
66
|
-
!commit.deployable? || !commit.stack.deployable?
|
67
|
-
end
|
68
|
-
|
69
|
-
def deploy_button_caption(commit)
|
70
|
-
state = commit.status.state
|
71
|
-
state = 'locked' if commit.stack.locked? && !ignore_lock?
|
72
|
-
if commit.deployable?
|
73
|
-
state = commit.stack.active_task? ? 'deploying' : 'enabled'
|
74
|
-
end
|
75
|
-
t("deploy_button.caption.#{state}")
|
76
|
-
end
|
77
63
|
end
|
78
64
|
end
|
data/app/models/shipit/deploy.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Shipit
|
2
2
|
class DeploySpec
|
3
3
|
module BundlerDiscovery
|
4
|
-
DEFAULT_BUNDLER_WITHOUT = %w(default production development test staging benchmark debug)
|
4
|
+
DEFAULT_BUNDLER_WITHOUT = %w(default production development test staging benchmark debug).freeze
|
5
5
|
|
6
6
|
def discover_dependencies_steps
|
7
7
|
discover_bundler || super
|
@@ -44,7 +44,12 @@ module Shipit
|
|
44
44
|
},
|
45
45
|
'plugins' => plugins,
|
46
46
|
'dependencies' => {'override' => dependencies_steps},
|
47
|
-
'deploy' => {
|
47
|
+
'deploy' => {
|
48
|
+
'override' => deploy_steps,
|
49
|
+
'variables' => deploy_variables.map(&:to_h),
|
50
|
+
'max_commits' => maximum_commits_per_deploy,
|
51
|
+
'interval' => pause_between_deploys,
|
52
|
+
},
|
48
53
|
'rollback' => {'override' => rollback_steps},
|
49
54
|
'fetch' => fetch_deployed_revision_steps,
|
50
55
|
'tasks' => cacheable_tasks,
|
@@ -63,6 +63,14 @@ module Shipit
|
|
63
63
|
end
|
64
64
|
alias_method :dependencies_steps!, :dependencies_steps
|
65
65
|
|
66
|
+
def maximum_commits_per_deploy
|
67
|
+
config('deploy', 'max_commits')
|
68
|
+
end
|
69
|
+
|
70
|
+
def pause_between_deploys
|
71
|
+
Duration.parse(config('deploy', 'interval') { 0 })
|
72
|
+
end
|
73
|
+
|
66
74
|
def deploy_steps
|
67
75
|
around_steps('deploy') do
|
68
76
|
config('deploy', 'override') { discover_deploy_steps }
|
@@ -143,6 +151,10 @@ module Shipit
|
|
143
151
|
config('machine', 'cleanup') { true }
|
144
152
|
end
|
145
153
|
|
154
|
+
def links
|
155
|
+
config('links') { {} }
|
156
|
+
end
|
157
|
+
|
146
158
|
private
|
147
159
|
|
148
160
|
def around_steps(section)
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module Shipit
|
2
|
-
class Duration
|
2
|
+
class Duration < ActiveSupport::Duration
|
3
|
+
FORMAT = /
|
4
|
+
\A
|
5
|
+
(?<days>\d+d)?
|
6
|
+
(?<hours>\d+h)?
|
7
|
+
(?<minutes>\d+m)?
|
8
|
+
(?<seconds>\d+s?)?
|
9
|
+
\z
|
10
|
+
/x
|
3
11
|
UNITS = {
|
4
12
|
's' => :seconds,
|
5
13
|
'm' => :minutes,
|
@@ -7,21 +15,33 @@ module Shipit
|
|
7
15
|
'd' => :days,
|
8
16
|
}.freeze
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
class << self
|
19
|
+
def parse(value)
|
20
|
+
unless match = FORMAT.match(value.to_s)
|
21
|
+
raise ArgumentError, "not a duration: #{value.inspect}"
|
22
|
+
end
|
23
|
+
parts = []
|
24
|
+
UNITS.values.each do |unit|
|
25
|
+
if value = match[unit]
|
26
|
+
parts << [unit, value.to_i]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
time = ::Time.current
|
31
|
+
new(time.advance(parts.to_h) - time, parts)
|
32
|
+
end
|
12
33
|
end
|
13
34
|
|
14
|
-
def
|
15
|
-
|
35
|
+
def initialize(value, parts = [[:seconds, value]])
|
36
|
+
super
|
16
37
|
end
|
17
38
|
|
18
39
|
def to_s
|
19
|
-
|
20
|
-
days, seconds = seconds.divmod(1.day.to_i)
|
40
|
+
days, seconds_left = value.divmod(1.day.to_i)
|
21
41
|
if days > 0
|
22
|
-
"#{days}d#{Time.at(
|
42
|
+
"#{days}d#{Time.at(seconds_left).utc.strftime('%Hh%Mm%Ss')}"
|
23
43
|
else
|
24
|
-
Time.at(
|
44
|
+
Time.at(value).utc.strftime('%Hh%Mm%Ss')[/[^0a-z]\w+/] || '0s'
|
25
45
|
end
|
26
46
|
end
|
27
47
|
end
|
data/app/models/shipit/stack.rb
CHANGED
@@ -21,7 +21,7 @@ module Shipit
|
|
21
21
|
REPO_OWNER_MAX_SIZE = 39
|
22
22
|
REPO_NAME_MAX_SIZE = 100
|
23
23
|
ENVIRONMENT_MAX_SIZE = 50
|
24
|
-
REQUIRED_HOOKS = %i(push status)
|
24
|
+
REQUIRED_HOOKS = %i(push status).freeze
|
25
25
|
|
26
26
|
has_many :commits, dependent: :destroy
|
27
27
|
has_many :tasks, dependent: :destroy
|
@@ -62,13 +62,19 @@ module Shipit
|
|
62
62
|
validates :lock_reason, length: {maximum: 4096}
|
63
63
|
|
64
64
|
serialize :cached_deploy_spec, DeploySpec
|
65
|
-
delegate :find_task_definition, :supports_rollback?,
|
65
|
+
delegate :find_task_definition, :supports_rollback?, :links,
|
66
66
|
:supports_fetch_deployed_revision?, to: :cached_deploy_spec, allow_nil: true
|
67
67
|
|
68
68
|
def self.refresh_deployed_revisions
|
69
69
|
find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
|
70
70
|
end
|
71
71
|
|
72
|
+
def self.schedule_continuous_delivery
|
73
|
+
where(continuous_deployment: true).find_each do |stack|
|
74
|
+
ContinuousDeliveryJob.perform_later(stack)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
72
78
|
def undeployed_commits?
|
73
79
|
undeployed_commits_count > 0
|
74
80
|
end
|
@@ -103,11 +109,25 @@ module Shipit
|
|
103
109
|
deploy
|
104
110
|
end
|
105
111
|
|
106
|
-
def
|
112
|
+
def trigger_continuous_delivery
|
107
113
|
return unless deployable?
|
108
|
-
if
|
114
|
+
return if deployed_too_recently?
|
115
|
+
|
116
|
+
if commit = next_commit_to_deploy
|
109
117
|
return if commit.deployed?
|
110
|
-
trigger_deploy(commit,
|
118
|
+
trigger_deploy(commit, Shipit.user)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def next_commit_to_deploy
|
123
|
+
commits_to_deploy = commits.order(id: :asc).newer_than(last_deployed_commit).reachable.preload(:statuses)
|
124
|
+
commits_to_deploy = commits_to_deploy.limit(maximum_commits_per_deploy) if maximum_commits_per_deploy
|
125
|
+
commits_to_deploy.to_a.reverse.find(&:deployable?)
|
126
|
+
end
|
127
|
+
|
128
|
+
def deployed_too_recently?
|
129
|
+
if task = last_active_task
|
130
|
+
task.ended_at? && (task.ended_at + pause_between_deploys).future?
|
111
131
|
end
|
112
132
|
end
|
113
133
|
|
@@ -144,6 +164,8 @@ module Shipit
|
|
144
164
|
'locked'
|
145
165
|
else
|
146
166
|
significant_statuses = undeployed_commits.map(&:significant_status)
|
167
|
+
significant_statuses << last_deployed_commit.significant_status unless last_deployed_commit.blank?
|
168
|
+
|
147
169
|
last_finalized_status = significant_statuses.reject { |s| %w(pending unknown).include?(s.state) }.first
|
148
170
|
last_finalized_status.try!(:simple_state) || 'pending'
|
149
171
|
end
|
@@ -155,13 +177,19 @@ module Shipit
|
|
155
177
|
end
|
156
178
|
|
157
179
|
def undeployed_commits
|
158
|
-
commits.reachable.newer_than(last_deployed_commit).order(id: :
|
180
|
+
scope = commits.reachable.newer_than(last_deployed_commit).order(id: :asc)
|
181
|
+
yield scope if block_given?
|
182
|
+
scope.map.with_index { |c, i| UndeployedCommit.new(c, i) }.reverse
|
159
183
|
end
|
160
184
|
|
161
185
|
def last_successful_deploy
|
162
186
|
deploys_and_rollbacks.success.order(created_at: :desc).first
|
163
187
|
end
|
164
188
|
|
189
|
+
def last_active_task
|
190
|
+
tasks.exclusive.last
|
191
|
+
end
|
192
|
+
|
165
193
|
def last_deployed_commit
|
166
194
|
if deploy = last_successful_deploy
|
167
195
|
deploy.until_commit
|
@@ -170,10 +198,6 @@ module Shipit
|
|
170
198
|
end
|
171
199
|
end
|
172
200
|
|
173
|
-
def last_deployable_commit
|
174
|
-
commits.order(id: :desc).newer_than(last_deployed_commit).reachable.preload(:statuses).to_a.find(&:deployable?)
|
175
|
-
end
|
176
|
-
|
177
201
|
def filter_visible_statuses(statuses)
|
178
202
|
statuses.reject { |s| hidden_statuses.include?(s.context) }
|
179
203
|
end
|
@@ -288,7 +312,8 @@ module Shipit
|
|
288
312
|
end
|
289
313
|
|
290
314
|
delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
|
291
|
-
:deploy_variables, :filter_task_envs, :filter_deploy_envs,
|
315
|
+
:deploy_variables, :filter_task_envs, :filter_deploy_envs, :maximum_commits_per_deploy,
|
316
|
+
:pause_between_deploys, to: :cached_deploy_spec
|
292
317
|
|
293
318
|
def monitoring?
|
294
319
|
monitoring.present?
|
@@ -356,6 +381,18 @@ module Shipit
|
|
356
381
|
super
|
357
382
|
end
|
358
383
|
|
384
|
+
def async_update_estimated_deploy_duration
|
385
|
+
UpdateEstimatedDeployDurationJob.perform_later(self)
|
386
|
+
end
|
387
|
+
|
388
|
+
def update_estimated_deploy_duration!
|
389
|
+
update!(estimated_deploy_duration: Stat.p90(recent_deploys_durations) || 1)
|
390
|
+
end
|
391
|
+
|
392
|
+
def recent_deploys_durations
|
393
|
+
tasks.where(type: 'Shipit::Deploy').success.order(id: :desc).limit(100).durations
|
394
|
+
end
|
395
|
+
|
359
396
|
private
|
360
397
|
|
361
398
|
def clear_cache
|
data/app/models/shipit/task.rb
CHANGED
@@ -13,7 +13,7 @@ module Shipit
|
|
13
13
|
belongs_to :until_commit, class_name: 'Commit'
|
14
14
|
belongs_to :since_commit, class_name: 'Commit'
|
15
15
|
|
16
|
-
has_many :chunks, -> { order(:id) }, class_name: 'OutputChunk', dependent: :
|
16
|
+
has_many :chunks, -> { order(:id) }, class_name: 'OutputChunk', dependent: :delete_all
|
17
17
|
|
18
18
|
serialize :definition, TaskDefinition
|
19
19
|
serialize :env, Hash
|
@@ -28,6 +28,12 @@ module Shipit
|
|
28
28
|
after_save :record_status_change
|
29
29
|
after_commit :emit_hooks
|
30
30
|
|
31
|
+
class << self
|
32
|
+
def durations
|
33
|
+
pluck(:started_at, :ended_at).select { |s, e| s && e }.map { |s, e| e - s }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
31
37
|
state_machine :status, initial: :pending do
|
32
38
|
before_transition any => :running do |task|
|
33
39
|
task.started_at ||= Time.now.utc
|
@@ -45,6 +51,10 @@ module Shipit
|
|
45
51
|
task.update!(confirmations: 0)
|
46
52
|
end
|
47
53
|
|
54
|
+
after_transition any => :success do |task|
|
55
|
+
task.async_update_estimated_deploy_duration
|
56
|
+
end
|
57
|
+
|
48
58
|
event :run do
|
49
59
|
transition pending: :running
|
50
60
|
end
|
@@ -101,7 +111,8 @@ module Shipit
|
|
101
111
|
error!
|
102
112
|
end
|
103
113
|
|
104
|
-
delegate :acquire_git_cache_lock, :async_refresh_deployed_revision,
|
114
|
+
delegate :acquire_git_cache_lock, :async_refresh_deployed_revision, :async_update_estimated_deploy_duration,
|
115
|
+
to: :stack
|
105
116
|
|
106
117
|
delegate :checklist, to: :definition
|
107
118
|
|