shipit-engine 0.24.0 → 0.25.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/app/assets/javascripts/shipit/stacks.js.coffee +15 -1
- data/app/assets/stylesheets/_base/_icons.scss +18 -0
- data/app/assets/stylesheets/_base/_status-items.scss +28 -0
- data/app/assets/stylesheets/_pages/_commits.scss +1 -5
- data/app/assets/stylesheets/_pages/_deploy.scss +60 -3
- data/app/controllers/concerns/shipit/authentication.rb +1 -1
- data/app/controllers/shipit/merge_status_controller.rb +2 -0
- data/app/controllers/shipit/release_statuses_controller.rb +36 -0
- data/app/jobs/shipit/append_delayed_release_status_job.rb +17 -0
- data/app/jobs/shipit/cache_deploy_spec_job.rb +2 -0
- data/app/jobs/shipit/clear_git_cache_job.rb +1 -1
- data/app/jobs/shipit/create_release_statuses_job.rb +11 -0
- data/app/jobs/shipit/deferred_touch_job.rb +2 -0
- data/app/jobs/shipit/deliver_hook_job.rb +1 -0
- data/app/jobs/shipit/merge_pull_requests_job.rb +2 -0
- data/app/jobs/shipit/perform_commit_checks_job.rb +2 -0
- data/app/jobs/shipit/perform_task_job.rb +2 -4
- data/app/jobs/shipit/refresh_pull_request_job.rb +2 -0
- data/app/models/shipit/anonymous_user.rb +1 -1
- data/app/models/shipit/commit.rb +36 -3
- data/app/models/shipit/deploy.rb +43 -0
- data/app/models/shipit/deploy_spec.rb +16 -2
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +5 -1
- data/app/models/shipit/deploy_spec/file_system.rb +4 -0
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -1
- data/app/models/shipit/ephemeral_commit_checks.rb +2 -4
- data/app/models/shipit/hook.rb +36 -3
- data/app/models/shipit/pull_request.rb +4 -2
- data/app/models/shipit/release_status.rb +41 -0
- data/app/models/shipit/rollback.rb +9 -0
- data/app/models/shipit/stack.rb +4 -9
- data/app/models/shipit/status/common.rb +4 -0
- data/app/models/shipit/status/group.rb +2 -1
- data/app/models/shipit/status/missing.rb +4 -0
- data/app/models/shipit/status/unknown.rb +15 -0
- data/app/models/shipit/task.rb +4 -0
- data/app/models/shipit/user.rb +16 -3
- data/app/serializers/shipit/stack_serializer.rb +1 -1
- data/app/views/shipit/deploys/_deploy.html.erb +18 -2
- data/config/locales/en.yml +3 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20180802172632_allow_commit_without_author.rb +6 -0
- data/db/migrate/20180906083930_create_release_statuses.rb +21 -0
- data/lib/shipit.rb +5 -0
- data/lib/shipit/command.rb +14 -18
- data/lib/shipit/deploy_commands.rb +0 -4
- data/lib/shipit/engine.rb +1 -1
- data/lib/shipit/first_parent_commits_iterator.rb +1 -1
- data/lib/shipit/flock.rb +43 -0
- data/lib/shipit/github_app.rb +5 -3
- data/lib/shipit/rollback_commands.rb +6 -0
- data/lib/shipit/task_commands.rb +1 -5
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/release_statuses_controller_test.rb +23 -0
- data/test/dummy/db/schema.rb +18 -3
- data/test/dummy/db/seeds.rb +4 -0
- data/test/fixtures/shipit/commits.yml +13 -0
- data/test/fixtures/shipit/release_statuses.yml +16 -0
- data/test/fixtures/shipit/stacks.yml +4 -0
- data/test/jobs/append_delayed_release_status_job_test.rb +25 -0
- data/test/jobs/cache_deploy_spec_job_test.rb +1 -2
- data/test/jobs/emit_event_job_test.rb +1 -1
- data/test/jobs/github_sync_job_test.rb +1 -0
- data/test/models/commits_test.rb +54 -1
- data/test/models/deploy_spec_test.rb +83 -11
- data/test/models/deploys_test.rb +52 -0
- data/test/models/hook_test.rb +1 -28
- data/test/models/pull_request_test.rb +19 -0
- data/test/models/release_statuses_test.rb +28 -0
- data/test/models/rollbacks_test.rb +2 -0
- data/test/models/stacks_test.rb +1 -1
- data/test/test_helper.rb +5 -0
- data/test/unit/rollback_commands_test.rb +35 -0
- metadata +121 -104
@@ -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, :last_deployed_at, :branch
|
8
|
+
:updated_at, :locked_since, :last_deployed_at, :branch, :merge_queue_enabled
|
9
9
|
|
10
10
|
def url
|
11
11
|
api_stack_url(object)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%- read_only ||= false -%>
|
2
2
|
|
3
|
-
<li class="task deploy" id="task-<%= deploy.id %>" data-status="<%= deploy.status %>">
|
3
|
+
<li class="task deploy" id="task-<%= deploy.id %>" data-status="<%= deploy.status %>" data-release-status="<%= deploy.last_release_status.state %>">
|
4
4
|
<% cache deploy do %>
|
5
5
|
<% cache deploy.author do %>
|
6
6
|
<%= render 'shipit/shared/author', author: deploy.author %>
|
@@ -16,6 +16,11 @@
|
|
16
16
|
</a>
|
17
17
|
</span>
|
18
18
|
<p class="commit-meta">
|
19
|
+
<% if @stack.release_status? %>
|
20
|
+
<%= link_to '#', data: {tooltip: deploy.last_release_status.description.presence} do %>
|
21
|
+
<i class="deploy-status__icon"></i>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
19
24
|
<%= deploy.rollback? ? 'rolled back' : 'deployed' %>
|
20
25
|
<span class="sha"><%= link_to_github_deploy(deploy) %></span>
|
21
26
|
<span class="code-additions">+<%= deploy.additions %></span>
|
@@ -34,10 +39,21 @@
|
|
34
39
|
<% end %>
|
35
40
|
</p>
|
36
41
|
</div>
|
42
|
+
|
43
|
+
<% if @stack.release_status? %>
|
44
|
+
<div class="release-validation" >
|
45
|
+
<%= link_to stack_deploy_release_statuses_path(@stack, deploy), class: 'action-set-release-status action-reject-release', data: {tooltip: t('release.reject'), status: 'failure'} do %>
|
46
|
+
<i class="icon icon--reject"></i>
|
47
|
+
<% end %>
|
48
|
+
<%= link_to stack_deploy_release_statuses_path(@stack, deploy), class: 'action-set-release-status action-validate-release', data: {tooltip: t('release.validate'), status: 'success'} do %>
|
49
|
+
<i class="icon icon--validate"></i>
|
50
|
+
<% end %>
|
51
|
+
</div>
|
52
|
+
<% end %>
|
37
53
|
<% end %>
|
38
54
|
|
39
55
|
<% unless read_only %>
|
40
|
-
<div class="
|
56
|
+
<div class="deploy-actions">
|
41
57
|
<% if deploy.rollbackable? %>
|
42
58
|
<% if deploy.stack.active_task? %>
|
43
59
|
<%= link_to 'Deploy in progress...', '#', class: 'btn disabled deploy-action' %>
|
data/config/locales/en.yml
CHANGED
@@ -24,6 +24,9 @@ en:
|
|
24
24
|
lock: This commit is safe to deploy. Click to mark it as unsafe.
|
25
25
|
unlock: This commit is unsafe to deploy. Click to mark it as safe.
|
26
26
|
confirm_unlock: Mark this commit as safe to deploy?
|
27
|
+
release:
|
28
|
+
validate: Mark the release as healthy
|
29
|
+
reject: Mark the release as faulty
|
27
30
|
deploy_button:
|
28
31
|
hint:
|
29
32
|
max_commits: It is recommended not to deploy more than %{maximum} commits at once.
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateReleaseStatuses < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :release_statuses do |t|
|
4
|
+
t.references :stack, foreign_key: false, null: false, index: false
|
5
|
+
t.references :commit, foreign_key: false, null: false, index: false
|
6
|
+
t.references :user, foreign_key: false, null: true
|
7
|
+
|
8
|
+
t.string :state, limit: 10, null: false
|
9
|
+
t.string :description, limit: 1024, null: true
|
10
|
+
t.string :target_url, limit: 1024, null: true
|
11
|
+
|
12
|
+
t.bigint :github_id, null: true
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
|
16
|
+
t.index %i(commit_id github_id)
|
17
|
+
t.index %i(stack_id commit_id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/shipit.rb
CHANGED
@@ -30,6 +30,7 @@ require 'faraday-http-cache'
|
|
30
30
|
|
31
31
|
require 'shipit/version'
|
32
32
|
|
33
|
+
require 'shipit/flock'
|
33
34
|
require 'shipit/github_app'
|
34
35
|
require 'shipit/paginator'
|
35
36
|
require 'shipit/null_serializer'
|
@@ -59,6 +60,10 @@ module Shipit
|
|
59
60
|
|
60
61
|
self.timeout_exit_codes = [].freeze
|
61
62
|
|
63
|
+
def authentication_disabled?
|
64
|
+
ENV['SHIPIT_DISABLE_AUTH'].present?
|
65
|
+
end
|
66
|
+
|
62
67
|
def app_name
|
63
68
|
@app_name ||= secrets.app_name || Rails.application.class.name.split(':').first || 'Shipit'
|
64
69
|
end
|
data/lib/shipit/command.rb
CHANGED
@@ -12,6 +12,8 @@ module Shipit
|
|
12
12
|
Denied = Class.new(Error)
|
13
13
|
TimedOut = Class.new(Error)
|
14
14
|
|
15
|
+
BASE_ENV = Bundler.clean_env.merge((ENV.keys - Bundler.clean_env.keys).map { |k| [k, nil] }.to_h)
|
16
|
+
|
15
17
|
class Failed < Error
|
16
18
|
attr_reader :exit_code
|
17
19
|
|
@@ -75,14 +77,6 @@ module Shipit
|
|
75
77
|
output.join
|
76
78
|
end
|
77
79
|
|
78
|
-
def with_full_path
|
79
|
-
old_path = ENV['PATH']
|
80
|
-
ENV['PATH'] = "#{ENV['PATH']}:#{Shipit.shell_paths.join(':')}"
|
81
|
-
yield
|
82
|
-
ensure
|
83
|
-
ENV['PATH'] = old_path
|
84
|
-
end
|
85
|
-
|
86
80
|
def interpolated_arguments
|
87
81
|
interpolate_environment_variables(@args)
|
88
82
|
end
|
@@ -90,22 +84,24 @@ module Shipit
|
|
90
84
|
def start(&block)
|
91
85
|
return if @started
|
92
86
|
@control_block = block
|
93
|
-
|
87
|
+
@out = @pid = nil
|
94
88
|
FileUtils.mkdir_p(@chdir)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
raise Denied, "#{Shellwords.split(interpolated_arguments.first).first}: Permission denied"
|
103
|
-
end
|
89
|
+
begin
|
90
|
+
@out, child_in, @pid = PTY.spawn(clean_env, *interpolated_arguments, chdir: @chdir)
|
91
|
+
child_in.close
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
raise NotFound, "#{Shellwords.split(interpolated_arguments.first).first}: command not found"
|
94
|
+
rescue Errno::EACCES
|
95
|
+
raise Denied, "#{Shellwords.split(interpolated_arguments.first).first}: Permission denied"
|
104
96
|
end
|
105
97
|
@started = true
|
106
98
|
self
|
107
99
|
end
|
108
100
|
|
101
|
+
def clean_env
|
102
|
+
BASE_ENV.merge('PATH' => "#{ENV['PATH']}:#{Shipit.shell_paths.join(':')}").merge(@env.stringify_keys)
|
103
|
+
end
|
104
|
+
|
109
105
|
def stream(&block)
|
110
106
|
start
|
111
107
|
begin
|
data/lib/shipit/engine.rb
CHANGED
@@ -22,7 +22,7 @@ module Shipit
|
|
22
22
|
path =~ %r{\Aplugins/[\-\w]+\.(js|css)\Z}
|
23
23
|
end
|
24
24
|
app.config.assets.precompile << proc do |path|
|
25
|
-
path.start_with?('emoji/') && path.end_with?('.png')
|
25
|
+
path.end_with?('.svg') || (path.start_with?('emoji/') && path.end_with?('.png'))
|
26
26
|
end
|
27
27
|
|
28
28
|
ActionDispatch::ExceptionWrapper.rescue_responses[Shipit::TaskDefinition::NotFound.name] = :not_found
|
data/lib/shipit/flock.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'timeout'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Shipit
|
6
|
+
class Flock
|
7
|
+
TimeoutError = Class.new(::Timeout::Error)
|
8
|
+
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@path = Pathname.new(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def lock(timeout:)
|
16
|
+
path.parent.mkpath
|
17
|
+
path.open('w') do |file|
|
18
|
+
if retrying(timeout: timeout) { file.flock(File::LOCK_EX | File::LOCK_NB) }
|
19
|
+
file.write($PROCESS_ID.to_s)
|
20
|
+
return yield
|
21
|
+
else
|
22
|
+
raise TimeoutError, "Couldn't acquire lock for #{path} in #{timeout} seconds"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def retrying(timeout:, breathing_time: 0.01)
|
30
|
+
started_at = Time.now.to_f
|
31
|
+
|
32
|
+
loop do
|
33
|
+
if yield
|
34
|
+
return true
|
35
|
+
elsif Time.now.to_f - started_at < timeout
|
36
|
+
sleep(breathing_time)
|
37
|
+
else
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/shipit/github_app.rb
CHANGED
@@ -22,8 +22,11 @@ module Shipit
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def api
|
25
|
-
|
26
|
-
|
25
|
+
client = Thread.current[:github_client]
|
26
|
+
if !client || client.access_token != token
|
27
|
+
client = Thread.current[:github_client] = new_client(access_token: token)
|
28
|
+
end
|
29
|
+
client
|
27
30
|
end
|
28
31
|
|
29
32
|
def verify_webhook_signature(signature, message)
|
@@ -66,7 +69,6 @@ module Shipit
|
|
66
69
|
[
|
67
70
|
oauth_id,
|
68
71
|
oauth_secret,
|
69
|
-
scope: 'email,repo_deployment',
|
70
72
|
client_options: options,
|
71
73
|
]
|
72
74
|
end
|
data/lib/shipit/task_commands.rb
CHANGED
@@ -35,7 +35,7 @@ module Shipit
|
|
35
35
|
'SHIPIT_USER' => "#{@task.author.login} (#{normalized_name}) via Shipit",
|
36
36
|
'EMAIL' => @task.author.email,
|
37
37
|
'BUNDLE_PATH' => Rails.root.join('data', 'bundler').to_s,
|
38
|
-
'SHIPIT_LINK' => permalink,
|
38
|
+
'SHIPIT_LINK' => @task.permalink,
|
39
39
|
'LAST_DEPLOYED_SHA' => @stack.last_deployed_commit.sha,
|
40
40
|
'TASK_ID' => @task.id.to_s,
|
41
41
|
'IGNORED_SAFETIES' => @task.ignored_safeties? ? '1' : '0',
|
@@ -80,9 +80,5 @@ module Shipit
|
|
80
80
|
@task.working_directory
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
84
|
-
def permalink
|
85
|
-
Shipit::Engine.routes.url_helpers.stack_task_url(@stack, @task)
|
86
|
-
end
|
87
83
|
end
|
88
84
|
end
|
data/lib/shipit/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Shipit
|
4
|
+
class ReleaseStatusesControllerTest < ActionController::TestCase
|
5
|
+
setup do
|
6
|
+
@stack = shipit_stacks(:shipit)
|
7
|
+
@deploy = shipit_deploys(:shipit)
|
8
|
+
session[:user_id] = shipit_users(:walrus).id
|
9
|
+
end
|
10
|
+
|
11
|
+
test ":create allow users to append release statuses" do
|
12
|
+
assert_difference -> { ReleaseStatus.count }, +1 do
|
13
|
+
post :create, params: {stack_id: @stack, deploy_id: @deploy.id, status: 'success'}
|
14
|
+
assert_response :created
|
15
|
+
end
|
16
|
+
|
17
|
+
status = ReleaseStatus.last
|
18
|
+
assert_equal 'success', status.state
|
19
|
+
assert_equal '@walrus signaled this release as healthy.', status.description
|
20
|
+
assert_equal @deploy.permalink, status.target_url
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/test/dummy/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 20180906083930) do
|
14
14
|
|
15
15
|
create_table "api_clients", force: :cascade do |t|
|
16
16
|
t.text "permissions", limit: 65535
|
@@ -45,8 +45,8 @@ ActiveRecord::Schema.define(version: 20180417130436) do
|
|
45
45
|
|
46
46
|
create_table "commits", force: :cascade do |t|
|
47
47
|
t.integer "stack_id", limit: 4, null: false
|
48
|
-
t.integer "author_id", limit: 4
|
49
|
-
t.integer "committer_id", limit: 4
|
48
|
+
t.integer "author_id", limit: 4
|
49
|
+
t.integer "committer_id", limit: 4
|
50
50
|
t.string "sha", limit: 40, null: false
|
51
51
|
t.text "message", limit: 65535, null: false
|
52
52
|
t.datetime "created_at"
|
@@ -158,6 +158,21 @@ ActiveRecord::Schema.define(version: 20180417130436) do
|
|
158
158
|
t.index ["stack_id"], name: "index_pull_requests_on_stack_id"
|
159
159
|
end
|
160
160
|
|
161
|
+
create_table "release_statuses", force: :cascade do |t|
|
162
|
+
t.integer "stack_id", null: false
|
163
|
+
t.integer "commit_id", null: false
|
164
|
+
t.integer "user_id"
|
165
|
+
t.string "state", limit: 10, null: false
|
166
|
+
t.string "description", limit: 1024
|
167
|
+
t.string "target_url", limit: 1024
|
168
|
+
t.bigint "github_id"
|
169
|
+
t.datetime "created_at", null: false
|
170
|
+
t.datetime "updated_at", null: false
|
171
|
+
t.index ["commit_id", "github_id"], name: "index_deploy_statuses_on_commit_id_and_github_id"
|
172
|
+
t.index ["stack_id", "commit_id"], name: "index_deploy_statuses_on_stack_id_and_commit_id"
|
173
|
+
t.index ["user_id"], name: "index_deploy_statuses_on_user_id"
|
174
|
+
end
|
175
|
+
|
161
176
|
create_table "stacks", force: :cascade do |t|
|
162
177
|
t.string "repo_name", limit: 100, null: false
|
163
178
|
t.string "repo_owner", limit: 39, null: false
|
data/test/dummy/db/seeds.rb
CHANGED
@@ -117,6 +117,19 @@ cyclimse_merged:
|
|
117
117
|
updated_at: <%= 8.days.ago.to_s(:db) %>
|
118
118
|
pull_request_id: 99
|
119
119
|
|
120
|
+
anonymous:
|
121
|
+
id: 10
|
122
|
+
sha: 216100bc78431fd73cac00592cd016d0d508458e
|
123
|
+
message: "whoami"
|
124
|
+
stack: shipit
|
125
|
+
author:
|
126
|
+
committer:
|
127
|
+
authored_at: <%= 10.days.ago.to_s(:db) %>
|
128
|
+
committed_at: <%= 9.days.ago.to_s(:db) %>
|
129
|
+
additions: 42
|
130
|
+
deletions: 24
|
131
|
+
updated_at: <%= 8.days.ago.to_s(:db) %>
|
132
|
+
|
120
133
|
soc_first:
|
121
134
|
id: 101
|
122
135
|
sha: 5e9278037b872fd9a6690523e411ecb3aa181355
|
@@ -0,0 +1,16 @@
|
|
1
|
+
to_be_created:
|
2
|
+
stack: shipit
|
3
|
+
commit_id: 4
|
4
|
+
user: walrus
|
5
|
+
state: pending
|
6
|
+
description: Deploy started
|
7
|
+
target_url: https://example.com/deploys/42
|
8
|
+
|
9
|
+
created:
|
10
|
+
stack: shipit
|
11
|
+
commit_id: 1
|
12
|
+
user: walrus
|
13
|
+
state: pending
|
14
|
+
description: Deploy started
|
15
|
+
target_url: https://example.com/deploys/24
|
16
|
+
github_id: 234234234
|
@@ -20,6 +20,10 @@ shipit:
|
|
20
20
|
"deploy": {"override": null, "interval": 60, "max_commits": 3, "variables": [{"name": "SAFETY_DISABLED", "title": "Set to 1 to do dangerous things", "default": 0}]},
|
21
21
|
"rollback": {"override": ["echo 'Rollback!'"]},
|
22
22
|
"fetch": ["echo '42'"],
|
23
|
+
"status": {
|
24
|
+
"context": "shipit/production",
|
25
|
+
"delay": 60
|
26
|
+
},
|
23
27
|
"tasks": {
|
24
28
|
"restart": {
|
25
29
|
"action": "Restart application",
|