shipit-engine 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -2
- data/app/assets/stylesheets/_pages/_deploy.scss +3 -2
- data/app/controllers/shipit/api/base_controller.rb +5 -0
- data/app/controllers/shipit/api/deploys_controller.rb +2 -1
- data/app/controllers/shipit/api/tasks_controller.rb +4 -1
- data/app/controllers/shipit/deploys_controller.rb +1 -1
- data/app/controllers/shipit/github_authentication_controller.rb +4 -2
- data/app/controllers/shipit/rollbacks_controller.rb +1 -1
- data/app/controllers/shipit/tasks_controller.rb +14 -2
- data/app/helpers/shipit/github_url_helper.rb +4 -2
- data/app/helpers/shipit/stacks_helper.rb +3 -3
- data/app/jobs/shipit/continuous_delivery_job.rb +12 -0
- data/app/jobs/shipit/create_on_github_job.rb +11 -0
- data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -1
- data/app/models/shipit/anonymous_user.rb +4 -0
- data/app/models/shipit/commit.rb +8 -10
- data/app/models/shipit/commit_deployment.rb +56 -0
- data/app/models/shipit/commit_deployment_status.rb +57 -0
- data/app/models/shipit/deploy.rb +32 -6
- data/app/models/shipit/deploy_spec.rb +8 -0
- data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -1
- data/app/models/shipit/rollback.rb +10 -0
- data/app/models/shipit/stack.rb +30 -12
- data/app/models/shipit/status_group.rb +1 -1
- data/app/models/shipit/task.rb +1 -0
- data/app/models/shipit/task_definition.rb +20 -1
- data/app/models/shipit/user.rb +10 -2
- data/app/models/shipit/variable_definition.rb +2 -4
- data/app/serializers/shipit/commit_serializer.rb +19 -1
- data/app/serializers/shipit/deploy_serializer.rb +7 -1
- data/app/serializers/shipit/short_commit_serializer.rb +1 -1
- data/app/serializers/shipit/task_serializer.rb +17 -1
- data/app/views/shipit/_variables.html.erb +15 -0
- data/app/views/shipit/deploys/_concurrent_deploy_warning.html.erb +1 -1
- data/app/views/shipit/deploys/_deploy.html.erb +2 -2
- data/app/views/shipit/deploys/new.html.erb +3 -17
- data/app/views/shipit/deploys/rollback.html.erb +3 -17
- data/app/views/shipit/tasks/new.html.erb +12 -4
- data/config/locales/en.yml +11 -0
- data/db/migrate/20160210183823_add_allow_concurrency_to_tasks.rb +5 -0
- data/db/migrate/20160303163611_create_shipit_commit_deployments.rb +14 -0
- data/db/migrate/20160303170913_create_shipit_commit_deployment_statuses.rb +12 -0
- data/db/migrate/20160303203940_add_encrypted_token_to_users.rb +6 -0
- data/lib/shipit.rb +7 -1
- data/lib/shipit/engine.rb +3 -1
- data/lib/shipit/environment_variables.rb +34 -0
- data/lib/shipit/simple_message_verifier.rb +0 -1
- data/lib/shipit/task_commands.rb +1 -0
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/api/deploys_controller_test.rb +15 -0
- data/test/controllers/api/tasks_controller_test.rb +15 -0
- data/test/controllers/github_authentication_controller_test.rb +23 -5
- data/test/controllers/tasks_controller_test.rb +27 -2
- data/test/controllers/webhooks_controller_test.rb +8 -2
- data/test/dummy/config/database.mysql.yml +1 -1
- data/test/dummy/config/secrets.example.yml +2 -2
- data/test/dummy/config/secrets.yml +2 -2
- data/test/dummy/data/stacks/byroot/junk/production/git/bar.txt +1 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/dkfdsf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/dskjfsd +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/dslkjfjsdf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/plopfizz +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/sd +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/sdkfjsdf +1 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/sdlfjsdfdsfj +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/sdlkfjsdlkfjsdlkfjdsfsdfksdfjsldkfjsdlkfjsdf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/shipit.yml +27 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/toto.txt +2 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +35 -7
- data/test/dummy/db/seeds.rb +3 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/shipit/commit_deployment_statuses.yml +19 -0
- data/test/fixtures/shipit/commit_deployments.yml +37 -0
- data/test/fixtures/shipit/commits.yml +1 -1
- data/test/fixtures/shipit/stacks.yml +12 -0
- data/test/fixtures/shipit/tasks.yml +4 -0
- data/test/fixtures/shipit/users.yml +2 -0
- data/test/jobs/fetch_deployed_revision_job_test.rb +3 -3
- data/test/models/commit_deployment_status_test.rb +27 -0
- data/test/models/commit_deployment_test.rb +37 -0
- data/test/models/commits_test.rb +7 -4
- data/test/models/deploys_test.rb +17 -1
- data/test/models/stacks_test.rb +13 -13
- data/test/models/task_definitions_test.rb +10 -0
- data/test/models/team_test.rb +8 -2
- data/test/models/users_test.rb +20 -2
- data/test/unit/deploy_spec_test.rb +29 -0
- data/test/unit/environment_variables_test.rb +36 -0
- data/test/unit/github_url_helper_test.rb +0 -8
- metadata +76 -20
data/app/models/shipit/deploy.rb
CHANGED
@@ -8,12 +8,32 @@ module Shipit
|
|
8
8
|
after_transition to: :success, do: :schedule_continuous_delivery
|
9
9
|
after_transition to: :success, do: :update_undeployed_commits_count
|
10
10
|
after_transition to: :aborted, do: :trigger_revert_if_required
|
11
|
+
after_transition any => any, do: :update_commit_deployments
|
12
|
+
end
|
13
|
+
|
14
|
+
has_many :commit_deployments, inverse_of: :task, foreign_key: :task_id do
|
15
|
+
GITHUB_STATUSES = {
|
16
|
+
'pending' => 'pending',
|
17
|
+
'failed' => 'failed',
|
18
|
+
'success' => 'success',
|
19
|
+
'error' => 'error',
|
20
|
+
'aborted' => 'error',
|
21
|
+
}
|
22
|
+
|
23
|
+
def append_status(task_status)
|
24
|
+
if github_status = GITHUB_STATUSES[task_status]
|
25
|
+
each do |deployment|
|
26
|
+
deployment.statuses.create!(status: github_status)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
11
30
|
end
|
12
31
|
|
13
32
|
before_create :denormalize_commit_stats
|
33
|
+
after_create :create_commit_deployments
|
14
34
|
after_commit :broadcast_update
|
15
35
|
|
16
|
-
delegate :broadcast_update, to: :stack
|
36
|
+
delegate :broadcast_update, :filter_deploy_envs, to: :stack
|
17
37
|
|
18
38
|
def build_rollback(user = nil, env: nil)
|
19
39
|
Rollback.new(
|
@@ -117,6 +137,16 @@ module Shipit
|
|
117
137
|
|
118
138
|
private
|
119
139
|
|
140
|
+
def create_commit_deployments
|
141
|
+
commits.each do |commit|
|
142
|
+
commit_deployments.create!(commit: commit)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def update_commit_deployments
|
147
|
+
commit_deployments.append_status(status)
|
148
|
+
end
|
149
|
+
|
120
150
|
def trigger_revert_if_required
|
121
151
|
return unless rollback_once_aborted?
|
122
152
|
return unless supports_rollback?
|
@@ -135,11 +165,7 @@ module Shipit
|
|
135
165
|
|
136
166
|
def schedule_continuous_delivery
|
137
167
|
return unless stack.continuous_deployment?
|
138
|
-
|
139
|
-
to_deploy = stack.commits.order(:id).newer_than(until_commit).successful.last
|
140
|
-
return unless to_deploy
|
141
|
-
|
142
|
-
stack.trigger_deploy(to_deploy, to_deploy.committer)
|
168
|
+
ContinuousDeliveryJob.perform_later(stack)
|
143
169
|
end
|
144
170
|
|
145
171
|
def last_successful_deploy
|
@@ -90,6 +90,14 @@ module Shipit
|
|
90
90
|
TaskDefinition.new(id, coerce_task_definition(config('tasks', id)) || task_not_found!(id))
|
91
91
|
end
|
92
92
|
|
93
|
+
def filter_deploy_envs(env)
|
94
|
+
EnvironmentVariables.with(env).permit(deploy_variables)
|
95
|
+
end
|
96
|
+
|
97
|
+
def filter_task_envs(id, env)
|
98
|
+
find_task_definition(id).filter_envs(env)
|
99
|
+
end
|
100
|
+
|
93
101
|
def review_checklist
|
94
102
|
(config('review', 'checklist') || discover_review_checklist || []).map(&:strip).select(&:present?)
|
95
103
|
end
|
@@ -15,7 +15,7 @@ module Shipit
|
|
15
15
|
|
16
16
|
def discover_gem_checklist
|
17
17
|
[%(<strong>Don't forget to add a tag before deploying!</strong> You can do this with:
|
18
|
-
git tag
|
18
|
+
git tag v<strong>x.y.z</strong> && git push --tags)] if gem?
|
19
19
|
end
|
20
20
|
|
21
21
|
def gem?
|
@@ -27,5 +27,15 @@ module Shipit
|
|
27
27
|
def to_partial_path
|
28
28
|
'deploys/deploy'
|
29
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def create_commit_deployments
|
34
|
+
# Rollback events are confusing in GitHub
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_commit_deployments
|
38
|
+
# Rollback events are confusing in GitHub
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
data/app/models/shipit/stack.rb
CHANGED
@@ -55,13 +55,14 @@ module Shipit
|
|
55
55
|
undeployed_commits_count > 0
|
56
56
|
end
|
57
57
|
|
58
|
-
def trigger_task(definition_id, user)
|
58
|
+
def trigger_task(definition_id, user, env: nil)
|
59
59
|
commit = last_deployed_commit
|
60
60
|
task = tasks.create(
|
61
61
|
user_id: user.id,
|
62
62
|
definition: find_task_definition(definition_id),
|
63
63
|
until_commit_id: commit.id,
|
64
64
|
since_commit_id: commit.id,
|
65
|
+
env: filter_task_envs(definition_id, (env || {})),
|
65
66
|
)
|
66
67
|
task.enqueue
|
67
68
|
task
|
@@ -74,18 +75,26 @@ module Shipit
|
|
74
75
|
user_id: user.id,
|
75
76
|
until_commit: until_commit,
|
76
77
|
since_commit: since_commit,
|
77
|
-
env: env || {},
|
78
|
+
env: filter_deploy_envs(env || {}),
|
78
79
|
)
|
79
80
|
deploy.enqueue
|
80
81
|
deploy
|
81
82
|
end
|
82
83
|
|
84
|
+
def trigger_continuous_deploy
|
85
|
+
return unless deployable?
|
86
|
+
if commit = last_deployable_commit
|
87
|
+
return if commit.deployed?
|
88
|
+
trigger_deploy(commit, commit.committer)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
83
92
|
def async_refresh_deployed_revision
|
84
93
|
FetchDeployedRevisionJob.perform_later(self)
|
85
94
|
end
|
86
95
|
|
87
96
|
def update_deployed_revision(sha)
|
88
|
-
return if
|
97
|
+
return if active_task?
|
89
98
|
|
90
99
|
last_deploy = deploys_and_rollbacks.last
|
91
100
|
actual_deployed_commit = commits.reachable.by_sha!(sha)
|
@@ -108,7 +117,7 @@ module Shipit
|
|
108
117
|
end
|
109
118
|
|
110
119
|
def status
|
111
|
-
return :deploying if
|
120
|
+
return :deploying if active_task?
|
112
121
|
:default
|
113
122
|
end
|
114
123
|
|
@@ -124,6 +133,10 @@ module Shipit
|
|
124
133
|
end
|
125
134
|
end
|
126
135
|
|
136
|
+
def last_deployable_commit
|
137
|
+
commits.order(id: :desc).newer_than(last_deployed_commit).reachable.preload(:statuses).to_a.find(&:deployable?)
|
138
|
+
end
|
139
|
+
|
127
140
|
def filter_visible_statuses(statuses)
|
128
141
|
statuses.reject { |s| hidden_statuses.include?(s.context) }
|
129
142
|
end
|
@@ -133,7 +146,7 @@ module Shipit
|
|
133
146
|
end
|
134
147
|
|
135
148
|
def deployable?
|
136
|
-
!locked? && !
|
149
|
+
!locked? && !active_task?
|
137
150
|
end
|
138
151
|
|
139
152
|
def repo_name=(name)
|
@@ -190,13 +203,13 @@ module Shipit
|
|
190
203
|
Shipit.github_api.commits(github_repo_name, sha: branch)
|
191
204
|
end
|
192
205
|
|
193
|
-
def
|
194
|
-
!!
|
206
|
+
def active_task?
|
207
|
+
!!active_task
|
195
208
|
end
|
196
209
|
|
197
|
-
def
|
198
|
-
return @
|
199
|
-
@
|
210
|
+
def active_task
|
211
|
+
return @active_task if defined?(@active_task)
|
212
|
+
@active_task ||= tasks.active.exclusive.last
|
200
213
|
end
|
201
214
|
|
202
215
|
def locked?
|
@@ -217,7 +230,7 @@ module Shipit
|
|
217
230
|
end
|
218
231
|
|
219
232
|
delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
|
220
|
-
:deploy_variables, to: :cached_deploy_spec
|
233
|
+
:deploy_variables, :filter_task_envs, :filter_deploy_envs, to: :cached_deploy_spec
|
221
234
|
|
222
235
|
def monitoring?
|
223
236
|
monitoring.present?
|
@@ -280,10 +293,15 @@ module Shipit
|
|
280
293
|
inaccessible_since?
|
281
294
|
end
|
282
295
|
|
296
|
+
def reload(*)
|
297
|
+
clear_cache
|
298
|
+
super
|
299
|
+
end
|
300
|
+
|
283
301
|
private
|
284
302
|
|
285
303
|
def clear_cache
|
286
|
-
remove_instance_variable(:@
|
304
|
+
remove_instance_variable(:@active_task) if defined?(@active_task)
|
287
305
|
end
|
288
306
|
|
289
307
|
def sync_github
|
@@ -9,7 +9,7 @@ module Shipit
|
|
9
9
|
@statuses = visible_statuses
|
10
10
|
end
|
11
11
|
|
12
|
-
delegate :success?, :state, to: :significant_status
|
12
|
+
delegate :pending?, :success?, :error?, :failure?, :state, to: :significant_status
|
13
13
|
|
14
14
|
def description
|
15
15
|
"#{success_count} / #{statuses.count} checks OK"
|
data/app/models/shipit/task.rb
CHANGED
@@ -15,6 +15,7 @@ module Shipit
|
|
15
15
|
scope :success, -> { where(status: 'success') }
|
16
16
|
scope :completed, -> { where(status: %w(success error failed flapping aborted)) }
|
17
17
|
scope :active, -> { where(status: %w(pending running aborting)) }
|
18
|
+
scope :exclusive, -> { where(allow_concurrency: false) }
|
18
19
|
|
19
20
|
scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
|
20
21
|
|
@@ -15,14 +15,21 @@ module Shipit
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
attr_reader :id, :action, :description, :steps, :checklist
|
18
|
+
attr_reader :id, :action, :description, :steps, :checklist, :variables
|
19
|
+
alias_method :to_param, :id
|
19
20
|
|
20
21
|
def initialize(id, config)
|
21
22
|
@id = id
|
22
23
|
@action = config['action']
|
23
24
|
@description = config['description'] || ''
|
24
25
|
@steps = config['steps'] || []
|
26
|
+
@variables = task_variables(config['variables'] || [])
|
25
27
|
@checklist = config['checklist'] || []
|
28
|
+
@allow_concurrency = config['allow_concurrency'] || false
|
29
|
+
end
|
30
|
+
|
31
|
+
def allow_concurrency?
|
32
|
+
@allow_concurrency
|
26
33
|
end
|
27
34
|
|
28
35
|
def as_json
|
@@ -31,8 +38,20 @@ module Shipit
|
|
31
38
|
action: action,
|
32
39
|
description: description,
|
33
40
|
steps: steps,
|
41
|
+
variables: variables.map(&:to_h),
|
34
42
|
checklist: checklist,
|
43
|
+
allow_concurrency: allow_concurrency?,
|
35
44
|
}
|
36
45
|
end
|
46
|
+
|
47
|
+
def filter_envs(env)
|
48
|
+
EnvironmentVariables.with(env).permit(variables)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def task_variables(config_variables)
|
54
|
+
config_variables.map(&VariableDefinition.method(:new))
|
55
|
+
end
|
37
56
|
end
|
38
57
|
end
|
data/app/models/shipit/user.rb
CHANGED
@@ -8,6 +8,8 @@ module Shipit
|
|
8
8
|
has_many :commits, foreign_key: :committer_id, inverse_of: :committer
|
9
9
|
has_many :tasks
|
10
10
|
|
11
|
+
attr_encrypted :github_access_token, key: Shipit.user_access_tokens_key
|
12
|
+
|
11
13
|
def self.find_or_create_by_login!(login)
|
12
14
|
find_or_create_by!(login: login) do |user|
|
13
15
|
user.github_user = Shipit.github_api.user(login)
|
@@ -33,6 +35,12 @@ module Shipit
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
38
|
+
def github_api
|
39
|
+
return Shipit.github_api unless github_access_token
|
40
|
+
|
41
|
+
@github_api ||= Octokit::Client.new(access_token: github_access_token)
|
42
|
+
end
|
43
|
+
|
36
44
|
def identifiers_for_ping
|
37
45
|
{github_id: github_id, name: name, email: email, github_login: login}
|
38
46
|
end
|
@@ -60,8 +68,8 @@ module Shipit
|
|
60
68
|
name: github_user.name || github_user.login, # Name is not mandatory on GitHub
|
61
69
|
email: github_user.email,
|
62
70
|
login: github_user.login,
|
63
|
-
avatar_url: github_user.
|
64
|
-
api_url: github_user.
|
71
|
+
avatar_url: github_user.avatar_url,
|
72
|
+
api_url: github_user.url,
|
65
73
|
)
|
66
74
|
end
|
67
75
|
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module Shipit
|
2
2
|
class VariableDefinition
|
3
|
-
attr_reader :name, :title, :
|
3
|
+
attr_reader :name, :title, :default
|
4
4
|
|
5
5
|
def initialize(attributes)
|
6
6
|
@name = attributes.fetch('name')
|
7
7
|
@title = attributes['title']
|
8
|
-
@
|
9
|
-
@default = attributes['default']
|
8
|
+
@default = attributes['default'].to_s
|
10
9
|
end
|
11
10
|
|
12
11
|
def to_h
|
13
12
|
{
|
14
13
|
'name' => @name,
|
15
14
|
'title' => @title,
|
16
|
-
'value' => @value,
|
17
15
|
'default' => @default,
|
18
16
|
}
|
19
17
|
end
|
@@ -1,8 +1,26 @@
|
|
1
1
|
module Shipit
|
2
2
|
class CommitSerializer < ShortCommitSerializer
|
3
|
+
include GithubUrlHelper
|
4
|
+
include ConditionalAttributes
|
5
|
+
|
3
6
|
has_one :author
|
4
7
|
has_one :committer
|
5
8
|
|
6
|
-
attributes :additions, :deletions, :authored_at, :committed_at
|
9
|
+
attributes :additions, :deletions, :authored_at, :committed_at, :html_url, :pull_request
|
10
|
+
|
11
|
+
def html_url
|
12
|
+
github_commit_url(object)
|
13
|
+
end
|
14
|
+
|
15
|
+
def pull_request
|
16
|
+
{
|
17
|
+
number: object.pull_request_number,
|
18
|
+
html_url: github_pull_request_url(object),
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def include_pull_request?
|
23
|
+
object.pull_request?
|
24
|
+
end
|
7
25
|
end
|
8
26
|
end
|
@@ -1,13 +1,19 @@
|
|
1
1
|
module Shipit
|
2
2
|
class DeploySerializer < TaskSerializer
|
3
|
+
include GithubUrlHelper
|
4
|
+
|
3
5
|
has_many :commits
|
4
6
|
|
5
|
-
attributes :additions, :deletions
|
7
|
+
attributes :compare_url, :additions, :deletions
|
6
8
|
|
7
9
|
def html_url
|
8
10
|
stack_deploy_url(object.stack, object)
|
9
11
|
end
|
10
12
|
|
13
|
+
def compare_url
|
14
|
+
github_commit_range_url(object.stack, object.since_commit, object.until_commit)
|
15
|
+
end
|
16
|
+
|
11
17
|
def type
|
12
18
|
:deploy
|
13
19
|
end
|
@@ -5,7 +5,7 @@ module Shipit
|
|
5
5
|
has_one :author
|
6
6
|
has_one :revision, serializer: ShortCommitSerializer
|
7
7
|
|
8
|
-
attributes :id, :url, :html_url, :output_url, :type, :status, :updated_at, :created_at
|
8
|
+
attributes :id, :url, :html_url, :output_url, :type, :status, :action, :description, :updated_at, :created_at
|
9
9
|
|
10
10
|
def revision
|
11
11
|
object.until_commit
|
@@ -26,5 +26,21 @@ module Shipit
|
|
26
26
|
def type
|
27
27
|
:task
|
28
28
|
end
|
29
|
+
|
30
|
+
def action
|
31
|
+
object.definition.try!(:action)
|
32
|
+
end
|
33
|
+
|
34
|
+
def include_action?
|
35
|
+
type == :task
|
36
|
+
end
|
37
|
+
|
38
|
+
def description
|
39
|
+
object.definition.try!(:action)
|
40
|
+
end
|
41
|
+
|
42
|
+
def include_description?
|
43
|
+
type == :task
|
44
|
+
end
|
29
45
|
end
|
30
46
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% unless variables.empty? %>
|
2
|
+
<section>
|
3
|
+
<header class="section-header variables-header">
|
4
|
+
<h2><%= header %></h2>
|
5
|
+
</header>
|
6
|
+
<%= form.fields_for field_name do |field| %>
|
7
|
+
<% variables.each do |variable| %>
|
8
|
+
<p class="variables-fields">
|
9
|
+
<%= field.text_field variable.name, value: variable.default %>
|
10
|
+
<%= field.label variable.name, variable.title || variable.name %>
|
11
|
+
</p>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</section>
|
15
|
+
<% end %>
|