shipit-engine 0.16.0 → 0.17.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 +1 -0
- data/app/assets/images/caret-down.svg +1 -0
- data/app/assets/javascripts/shipit/stacks.js.coffee +10 -0
- data/app/assets/stylesheets/_base/_banner.scss +7 -3
- data/app/assets/stylesheets/_base/_base.scss +0 -74
- data/app/assets/stylesheets/_base/_buttons.scss +7 -3
- data/app/assets/stylesheets/_base/_colors.scss +2 -0
- data/app/assets/stylesheets/_base/_icons.scss +8 -0
- data/app/assets/stylesheets/_base/_spacing.scss +21 -0
- data/app/assets/stylesheets/_pages/_commits.scss +41 -3
- data/app/assets/stylesheets/_structure/_layout.scss +8 -35
- data/app/assets/stylesheets/_structure/_main.scss +2 -2
- data/app/assets/stylesheets/_structure/_navigation.scss +89 -0
- data/app/assets/stylesheets/shipit.scss +3 -0
- data/app/controllers/concerns/shipit/api/rendering.rb +3 -6
- data/app/controllers/shipit/api/ccmenu_controller.rb +4 -0
- data/app/controllers/shipit/commits_controller.rb +18 -0
- data/app/jobs/shipit/destroy_stack_job.rb +25 -0
- data/app/jobs/shipit/github_sync_job.rb +12 -1
- data/app/jobs/shipit/merge_pull_requests_job.rb +3 -0
- data/app/models/shipit/commit.rb +14 -2
- data/app/models/shipit/deploy.rb +5 -0
- data/app/models/shipit/deploy_spec.rb +9 -6
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +24 -2
- data/app/models/shipit/pull_request.rb +16 -1
- data/app/models/shipit/stack.rb +11 -4
- data/app/models/shipit/task_definition.rb +4 -0
- data/app/models/shipit/team.rb +1 -1
- data/app/models/shipit/undeployed_commit.rb +1 -2
- data/app/models/shipit/user.rb +1 -1
- data/app/models/shipit/variable_definition.rb +5 -0
- data/app/serializers/shipit/stack_serializer.rb +1 -1
- data/app/views/layouts/shipit.html.erb +2 -1
- data/app/views/shipit/commits/_commit.html.erb +9 -1
- data/app/views/shipit/deploys/rollback.html.erb +1 -1
- data/app/views/shipit/stacks/_header.html.erb +15 -3
- data/app/views/shipit/stacks/settings.html.erb +1 -1
- data/config/locales/en.yml +9 -5
- data/config/routes.rb +1 -0
- data/db/migrate/20170310164315_add_merged_at_on_pull_requests.rb +9 -0
- data/db/migrate/20170314145604_add_last_deployed_at_to_stack.rb +9 -0
- data/db/migrate/20170320124156_add_locked_to_commits.rb +5 -0
- data/lib/shipit/task_commands.rb +1 -0
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/api/ccmenu_controller_test.rb +6 -0
- data/test/controllers/api/stacks_controller_test.rb +6 -0
- data/test/controllers/api/tasks_controller_test.rb +31 -0
- data/test/controllers/commits_controller_test.rb +18 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +4 -1
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3-journal +0 -0
- data/test/fixtures/shipit/commits.yml +1 -1
- data/test/fixtures/shipit/pull_requests.yml +33 -0
- data/test/fixtures/shipit/stacks.yml +4 -1
- data/test/jobs/github_sync_job_test.rb +49 -0
- data/test/jobs/merge_pull_requests_job_test.rb +18 -0
- data/test/models/commits_test.rb +71 -0
- data/test/models/deploy_spec_test.rb +15 -0
- data/test/models/deploys_test.rb +7 -0
- data/test/models/pull_request_test.rb +19 -1
- data/test/models/task_definitions_test.rb +9 -0
- data/test/models/undeployed_commits_test.rb +2 -7
- data/test/unit/deploy_commands_test.rb +7 -0
- data/test/unit/variable_definition_test.rb +10 -0
- metadata +15 -7
- data/app/assets/images/github.svg +0 -9
- data/app/assets/images/refresh.svg +0 -8
- data/app/assets/images/settings.svg +0 -33
- data/lib/snippets/deploy-to-gke +0 -161
@@ -3,12 +3,15 @@
|
|
3
3
|
|
4
4
|
@import "_base/_media-queries";
|
5
5
|
@import "_base/_utility";
|
6
|
+
@import "_base/_spacing";
|
6
7
|
@import "_base/_colors";
|
7
8
|
@import "_base/_base";
|
8
9
|
@import "_base/_forms";
|
10
|
+
@import "_base/_icons";
|
9
11
|
@import "_base/_buttons";
|
10
12
|
@import "_base/_banner";
|
11
13
|
@import "_structure/_layout";
|
14
|
+
@import "_structure/_navigation";
|
12
15
|
@import "_structure/_main";
|
13
16
|
@import "_base/status-items";
|
14
17
|
@import "_pages/_commits";
|
@@ -10,15 +10,12 @@ module Shipit
|
|
10
10
|
|
11
11
|
def render_resource(resource, options = {})
|
12
12
|
if resource.destroyed?
|
13
|
-
options
|
14
|
-
options[:text] = nil
|
13
|
+
head :no_content, options.reverse_merge(content_type: 'application/json')
|
15
14
|
elsif resource.errors.any?
|
16
|
-
options
|
17
|
-
options[:status] = :unprocessable_entity
|
15
|
+
render options.reverse_merge(status: :unprocessable_entity, json: {errors: resource.errors})
|
18
16
|
else
|
19
|
-
options
|
17
|
+
render options.reverse_merge(json: resource)
|
20
18
|
end
|
21
|
-
render options
|
22
19
|
end
|
23
20
|
end
|
24
21
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Shipit
|
2
|
+
class CommitsController < ShipitController
|
3
|
+
def update
|
4
|
+
commit.update(params.require(:commit).permit(:locked))
|
5
|
+
head :ok
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def commit
|
11
|
+
@commit ||= stack.commits.find(params[:id])
|
12
|
+
end
|
13
|
+
|
14
|
+
def stack
|
15
|
+
@stack ||= Stack.from_param!(params[:stack_id])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,7 +2,32 @@ module Shipit
|
|
2
2
|
class DestroyStackJob < BackgroundJob
|
3
3
|
queue_as :default
|
4
4
|
|
5
|
+
# stack
|
6
|
+
# +-- api_clients
|
7
|
+
# +-- commits
|
8
|
+
# | +-- commit_deployments
|
9
|
+
# | | +-- statuses
|
10
|
+
# | +-- statuses
|
11
|
+
# +-- github_hooks
|
12
|
+
# +-- hooks
|
13
|
+
# +-- pull_requests
|
14
|
+
# +-- tasks
|
15
|
+
# +-- chunks
|
16
|
+
|
5
17
|
def perform(stack)
|
18
|
+
Shipit::ApiClient.where(stack_id: stack.id).delete_all
|
19
|
+
commits_ids = Shipit::Commit.where(stack_id: stack.id).pluck(:id)
|
20
|
+
commit_deployments_ids = Shipit::CommitDeployment.where(commit_id: commits_ids).pluck(:id)
|
21
|
+
Shipit::CommitDeploymentStatus.where(commit_deployment_id: commit_deployments_ids).delete_all
|
22
|
+
Shipit::CommitDeployment.where(id: commit_deployments_ids).delete_all
|
23
|
+
Shipit::Status.where(commit_id: commits_ids).delete_all
|
24
|
+
Shipit::Commit.where(id: commits_ids).delete_all
|
25
|
+
Shipit::GithubHook.where(stack_id: stack.id).destroy_all
|
26
|
+
Shipit::Hook.where(stack_id: stack.id).delete_all
|
27
|
+
Shipit::PullRequest.where(stack_id: stack.id).delete_all
|
28
|
+
tasks_ids = Shipit::Task.where(stack_id: stack.id).pluck(:id)
|
29
|
+
Shipit::OutputChunk.where(task_id: tasks_ids).delete_all
|
30
|
+
Shipit::Task.where(id: tasks_ids).delete_all
|
6
31
|
stack.destroy!
|
7
32
|
end
|
8
33
|
end
|
@@ -17,13 +17,24 @@ module Shipit
|
|
17
17
|
@stack.transaction do
|
18
18
|
shared_parent.try!(:detach_children!)
|
19
19
|
new_commits.each do |gh_commit|
|
20
|
-
|
20
|
+
append_commit(gh_commit)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
CacheDeploySpecJob.perform_later(@stack)
|
25
25
|
end
|
26
26
|
|
27
|
+
def append_commit(gh_commit)
|
28
|
+
appended_commit = @stack.commits.create_from_github!(gh_commit)
|
29
|
+
if appended_commit.revert?
|
30
|
+
impacted_commits = @stack.undeployed_commits.reverse.drop_while { |c| !appended_commit.revert_of?(c) }
|
31
|
+
impacted_commits.pop # appended_commit
|
32
|
+
impacted_commits.each do |impacted_commit|
|
33
|
+
impacted_commit.update!(locked: true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
27
38
|
def fetch_missing_commits(&block)
|
28
39
|
commits = []
|
29
40
|
iterator = Shipit::FirstParentCommitsIterator.new(&block)
|
@@ -8,6 +8,9 @@ module Shipit
|
|
8
8
|
pull_requests.each do |pull_request|
|
9
9
|
pull_request.refresh!
|
10
10
|
pull_request.reject_unless_mergeable!
|
11
|
+
if pull_request.closed?
|
12
|
+
pull_request.merged_upstream? ? pull_request.complete! : pull_request.cancel!
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
return false unless stack.allows_merges?
|
data/app/models/shipit/commit.rb
CHANGED
@@ -106,7 +106,7 @@ module Shipit
|
|
106
106
|
delegate :pending?, :success?, :error?, :failure?, :state, to: :status
|
107
107
|
|
108
108
|
def deployable?
|
109
|
-
success? || stack.ignore_ci?
|
109
|
+
!locked? && (success? || stack.ignore_ci?)
|
110
110
|
end
|
111
111
|
|
112
112
|
def children
|
@@ -126,13 +126,25 @@ module Shipit
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def title
|
129
|
-
pull_request_title ||
|
129
|
+
pull_request_title || message_header
|
130
|
+
end
|
131
|
+
|
132
|
+
def message_header
|
133
|
+
message.lines.first.strip
|
130
134
|
end
|
131
135
|
|
132
136
|
def pull_request_title # TODO: remove in a few versions when it is assumed the commits table was backfilled
|
133
137
|
super || message_parser.pull_request_title
|
134
138
|
end
|
135
139
|
|
140
|
+
def revert?
|
141
|
+
title.start_with?('Revert "') && title.end_with?('"')
|
142
|
+
end
|
143
|
+
|
144
|
+
def revert_of?(commit)
|
145
|
+
title == %(Revert "#{commit.title}") || title == %(Revert "#{commit.message_header}")
|
146
|
+
end
|
147
|
+
|
136
148
|
def short_sha
|
137
149
|
sha[0..9]
|
138
150
|
end
|
data/app/models/shipit/deploy.rb
CHANGED
@@ -10,6 +10,7 @@ module Shipit
|
|
10
10
|
after_transition to: :success, do: :update_undeployed_commits_count
|
11
11
|
after_transition to: :aborted, do: :trigger_revert_if_required
|
12
12
|
after_transition any => any, do: :update_commit_deployments
|
13
|
+
after_transition any => any, do: :update_last_deploy_time
|
13
14
|
end
|
14
15
|
|
15
16
|
has_many :commit_deployments, dependent: :destroy, inverse_of: :task, foreign_key: :task_id do
|
@@ -180,5 +181,9 @@ module Shipit
|
|
180
181
|
def update_undeployed_commits_count
|
181
182
|
stack.update_undeployed_commits_count(until_commit)
|
182
183
|
end
|
184
|
+
|
185
|
+
def update_last_deploy_time
|
186
|
+
stack.update(last_deployed_at: ended_at)
|
187
|
+
end
|
183
188
|
end
|
184
189
|
end
|
@@ -108,21 +108,20 @@ module Shipit
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def task_definitions
|
111
|
-
(config('tasks') || {}).map
|
111
|
+
discover_task_definitions.merge(config('tasks') || {}).map do |name, definition|
|
112
|
+
TaskDefinition.new(name, coerce_task_definition(definition))
|
113
|
+
end
|
112
114
|
end
|
113
115
|
|
114
116
|
def find_task_definition(id)
|
115
|
-
|
117
|
+
definition = config('tasks', id) || discover_task_definitions[id]
|
118
|
+
TaskDefinition.new(id, coerce_task_definition(definition) || task_not_found!(id))
|
116
119
|
end
|
117
120
|
|
118
121
|
def filter_deploy_envs(env)
|
119
122
|
EnvironmentVariables.with(env).permit(deploy_variables)
|
120
123
|
end
|
121
124
|
|
122
|
-
def filter_task_envs(id, env)
|
123
|
-
find_task_definition(id).filter_envs(env)
|
124
|
-
end
|
125
|
-
|
126
125
|
def review_checklist
|
127
126
|
(config('review', 'checklist') || discover_review_checklist || []).map(&:strip).select(&:present?)
|
128
127
|
end
|
@@ -199,6 +198,10 @@ module Shipit
|
|
199
198
|
def discover_review_checklist
|
200
199
|
end
|
201
200
|
|
201
|
+
def discover_task_definitions
|
202
|
+
{}
|
203
|
+
end
|
204
|
+
|
202
205
|
def discover_dependencies_steps
|
203
206
|
end
|
204
207
|
|
@@ -9,6 +9,20 @@ module Shipit
|
|
9
9
|
discover_kubernetes || super
|
10
10
|
end
|
11
11
|
|
12
|
+
def discover_task_definitions
|
13
|
+
if kube_config.present?
|
14
|
+
{
|
15
|
+
'restart' => {
|
16
|
+
'action' => "Restart application",
|
17
|
+
'description' => "Simulates a rollout of Kubernetes deployments by using kubernetes-restart utility",
|
18
|
+
'steps' => [kubernetes_restart_cmd],
|
19
|
+
},
|
20
|
+
}
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
12
26
|
private
|
13
27
|
|
14
28
|
def discover_kubernetes
|
@@ -20,8 +34,8 @@ module Shipit
|
|
20
34
|
cmd << kube_config['template_dir']
|
21
35
|
end
|
22
36
|
|
23
|
-
cmd << kube_config
|
24
|
-
cmd << kube_config
|
37
|
+
cmd << kube_config.fetch('namespace')
|
38
|
+
cmd << kube_config.fetch('context')
|
25
39
|
|
26
40
|
[Shellwords.join(cmd)]
|
27
41
|
end
|
@@ -29,6 +43,14 @@ module Shipit
|
|
29
43
|
def kube_config
|
30
44
|
@kube_config ||= config('kubernetes') || {}
|
31
45
|
end
|
46
|
+
|
47
|
+
def kubernetes_restart_cmd
|
48
|
+
Shellwords.join([
|
49
|
+
"kubernetes-restart",
|
50
|
+
kube_config.fetch('namespace'),
|
51
|
+
kube_config.fetch('context'),
|
52
|
+
])
|
53
|
+
end
|
32
54
|
end
|
33
55
|
end
|
34
56
|
end
|
@@ -95,6 +95,10 @@ module Shipit
|
|
95
95
|
before_transition any => :pending do |pr|
|
96
96
|
pr.revalidated_at = Time.now.utc
|
97
97
|
end
|
98
|
+
|
99
|
+
before_transition %i(pending) => :merged do |pr|
|
100
|
+
Stack.increment_counter(:undeployed_commits_count, pr.stack_id)
|
101
|
+
end
|
98
102
|
end
|
99
103
|
|
100
104
|
def self.schedule_merges
|
@@ -164,7 +168,9 @@ module Shipit
|
|
164
168
|
merge_method: 'merge',
|
165
169
|
)
|
166
170
|
begin
|
167
|
-
Shipit.github_api.
|
171
|
+
if Shipit.github_api.pull_requests(stack.github_repo_name, base: branch).empty?
|
172
|
+
Shipit.github_api.delete_branch(stack.github_repo_name, branch)
|
173
|
+
end
|
168
174
|
rescue Octokit::UnprocessableEntity
|
169
175
|
# branch was already deleted somehow
|
170
176
|
end
|
@@ -203,6 +209,14 @@ module Shipit
|
|
203
209
|
RefreshPullRequestJob.perform_later(self)
|
204
210
|
end
|
205
211
|
|
212
|
+
def closed?
|
213
|
+
state == "closed"
|
214
|
+
end
|
215
|
+
|
216
|
+
def merged_upstream?
|
217
|
+
closed? && merged_at
|
218
|
+
end
|
219
|
+
|
206
220
|
def refresh!
|
207
221
|
update!(github_pull_request: Shipit.github_api.pull_request(stack.github_repo_name, number))
|
208
222
|
head.refresh_statuses!
|
@@ -219,6 +233,7 @@ module Shipit
|
|
219
233
|
self.deletions = github_pull_request.deletions
|
220
234
|
self.branch = github_pull_request.head.ref
|
221
235
|
self.head = find_or_create_commit_from_github_by_sha!(github_pull_request.head.sha, detached: true)
|
236
|
+
self.merged_at = github_pull_request.merged_at
|
222
237
|
end
|
223
238
|
|
224
239
|
def merge_message
|
data/app/models/shipit/stack.rb
CHANGED
@@ -82,13 +82,20 @@ module Shipit
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def trigger_task(definition_id, user, env: nil)
|
85
|
+
definition = find_task_definition(definition_id)
|
86
|
+
env = env.try!(:to_h) || {}
|
87
|
+
|
88
|
+
definition.variables_with_defaults.each do |variable|
|
89
|
+
env[variable.name] ||= variable.default
|
90
|
+
end
|
91
|
+
|
85
92
|
commit = last_deployed_commit.presence || commits.first
|
86
93
|
task = tasks.create(
|
87
94
|
user_id: user.id,
|
88
|
-
definition:
|
95
|
+
definition: definition,
|
89
96
|
until_commit_id: commit.id,
|
90
97
|
since_commit_id: commit.id,
|
91
|
-
env:
|
98
|
+
env: definition.filter_envs(env),
|
92
99
|
)
|
93
100
|
task.enqueue
|
94
101
|
task
|
@@ -372,8 +379,8 @@ module Shipit
|
|
372
379
|
|
373
380
|
def update_undeployed_commits_count(after_commit = nil)
|
374
381
|
after_commit ||= last_deployed_commit
|
375
|
-
undeployed_commits = commits.reachable.newer_than(after_commit).
|
376
|
-
|
382
|
+
undeployed_commits = commits.reachable.newer_than(after_commit).count
|
383
|
+
update(undeployed_commits_count: undeployed_commits)
|
377
384
|
end
|
378
385
|
|
379
386
|
def broadcast_update
|
data/app/models/shipit/team.rb
CHANGED
@@ -2,8 +2,8 @@ module Shipit
|
|
2
2
|
class Team < ActiveRecord::Base
|
3
3
|
REQUIRED_HOOKS = %i(membership).freeze
|
4
4
|
|
5
|
-
has_many :members, class_name: :User, through: :memberships, source: :user
|
6
5
|
has_many :memberships
|
6
|
+
has_many :members, class_name: :User, through: :memberships, source: :user
|
7
7
|
|
8
8
|
has_many :github_hooks,
|
9
9
|
-> { where(event: REQUIRED_HOOKS) },
|
@@ -11,7 +11,7 @@ module Shipit
|
|
11
11
|
state = deployable? ? 'allowed' : status.state
|
12
12
|
unless bypass_safeties
|
13
13
|
state = 'deploying' if stack.active_task?
|
14
|
-
state = 'locked' if
|
14
|
+
state = 'locked' if locked?
|
15
15
|
end
|
16
16
|
state
|
17
17
|
end
|
@@ -19,7 +19,6 @@ module Shipit
|
|
19
19
|
def redeploy_state(bypass_safeties = false)
|
20
20
|
state = 'allowed'
|
21
21
|
unless bypass_safeties
|
22
|
-
state = 'locked' if stack.locked?
|
23
22
|
state = 'deploying' if stack.active_task?
|
24
23
|
end
|
25
24
|
state
|
data/app/models/shipit/user.rb
CHANGED
@@ -2,8 +2,8 @@ module Shipit
|
|
2
2
|
class User < ActiveRecord::Base
|
3
3
|
DEFAULT_AVATAR = URI.parse('https://avatars.githubusercontent.com/u/583231?')
|
4
4
|
|
5
|
-
has_many :teams, through: :memberships
|
6
5
|
has_many :memberships
|
6
|
+
has_many :teams, through: :memberships
|
7
7
|
has_many :authored_commits, class_name: :Commit, foreign_key: :author_id, inverse_of: :author
|
8
8
|
has_many :commits, foreign_key: :committer_id, inverse_of: :committer
|
9
9
|
has_many :tasks
|
@@ -6,9 +6,14 @@ module Shipit
|
|
6
6
|
@name = attributes.fetch('name')
|
7
7
|
@title = attributes['title']
|
8
8
|
@default = attributes['default'].to_s
|
9
|
+
@default_provided = attributes.key?('default')
|
9
10
|
@select = attributes['select'].presence
|
10
11
|
end
|
11
12
|
|
13
|
+
def default_provided?
|
14
|
+
@default_provided
|
15
|
+
end
|
16
|
+
|
12
17
|
def to_h
|
13
18
|
{
|
14
19
|
'name' => @name,
|
@@ -5,7 +5,7 @@ module Shipit
|
|
5
5
|
has_one :lock_author
|
6
6
|
attributes :id, :repo_owner, :repo_name, :environment, :html_url, :url, :tasks_url, :deploy_url, :pull_requests_url,
|
7
7
|
:deploy_spec, :undeployed_commits_count, :is_locked, :lock_reason, :continuous_deployment, :created_at,
|
8
|
-
:updated_at, :locked_since
|
8
|
+
:updated_at, :locked_since, :last_deployed_at
|
9
9
|
|
10
10
|
def url
|
11
11
|
api_stack_url(object)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html data-controller="<%= controller_name %>" data-action="<%= action_name %>">
|
2
|
+
<html lang="<%= I18n.locale %>" data-controller="<%= controller_name %>" data-action="<%= action_name %>">
|
3
3
|
<head>
|
4
4
|
<title><%= [Shipit.app_name, @stack.try!(:repo_name)].compact.join(' - ') %></title>
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
6
|
<%= favicon_link_tag %>
|
6
7
|
<%= stylesheet_link_tag :shipit, media: 'all' %>
|
7
8
|
<%= javascript_include_tag :shipit %>
|