shipit-engine 0.35.0 → 0.36.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -7
- data/app/controllers/concerns/shipit/authentication.rb +5 -1
- data/app/controllers/shipit/api/base_controller.rb +13 -1
- data/app/controllers/shipit/api/rollbacks_controller.rb +1 -1
- data/app/controllers/shipit/api/stacks_controller.rb +8 -2
- data/app/controllers/shipit/api/tasks_controller.rb +19 -2
- data/app/controllers/shipit/rollbacks_controller.rb +5 -1
- data/app/helpers/shipit/stacks_helper.rb +11 -0
- data/app/models/concerns/shipit/deferred_touch.rb +3 -3
- data/app/models/shipit/anonymous_user.rb +4 -0
- data/app/models/shipit/api_client.rb +1 -1
- data/app/models/shipit/commit_checks.rb +3 -3
- data/app/models/shipit/delivery.rb +1 -1
- data/app/models/shipit/deploy.rb +1 -0
- data/app/models/shipit/deploy_spec/file_system.rb +32 -4
- data/app/models/shipit/pull_request.rb +1 -1
- data/app/models/shipit/stack.rb +10 -10
- data/app/models/shipit/task.rb +31 -4
- data/app/models/shipit/user.rb +23 -9
- data/app/serializers/shipit/stack_serializer.rb +1 -1
- data/app/views/shipit/deploys/_deploy.html.erb +1 -5
- data/app/views/shipit/stacks/_banners.html.erb +1 -1
- data/app/views/shipit/stacks/_settings_form.erb +55 -0
- data/app/views/shipit/stacks/settings.html.erb +1 -55
- data/app/views/shipit/stacks/show.html.erb +1 -1
- data/config/locales/en.yml +1 -1
- data/config/routes.rb +4 -0
- data/db/migrate/20211103154121_increase_github_team_slug_size.rb +5 -0
- data/lib/shipit/engine.rb +15 -5
- data/lib/shipit/stack_commands.rb +2 -2
- data/lib/shipit/task_commands.rb +1 -1
- data/lib/shipit/version.rb +1 -1
- data/lib/shipit.rb +55 -3
- data/lib/snippets/fetch-gem-version +1 -1
- data/test/controllers/api/hooks_controller_test.rb +1 -1
- data/test/controllers/api/rollback_controller_test.rb +1 -0
- data/test/controllers/api/stacks_controller_test.rb +25 -0
- data/test/controllers/api/tasks_controller_test.rb +56 -0
- data/test/controllers/stacks_controller_test.rb +11 -0
- data/test/dummy/config/application.rb +1 -2
- data/test/dummy/db/schema.rb +2 -2
- data/test/fixtures/shipit/check_runs.yml +3 -3
- data/test/fixtures/shipit/commits.yml +101 -101
- data/test/fixtures/shipit/deliveries.yml +1 -1
- data/test/fixtures/shipit/merge_requests.yml +19 -19
- data/test/fixtures/shipit/stacks.yml +28 -28
- data/test/fixtures/shipit/statuses.yml +16 -16
- data/test/fixtures/shipit/tasks.yml +77 -65
- data/test/fixtures/shipit/users.yml +2 -5
- data/test/models/commits_test.rb +6 -6
- data/test/models/deploy_spec_test.rb +0 -23
- data/test/models/deploys_test.rb +26 -0
- data/test/models/shipit/deploy_spec/file_system_test.rb +81 -0
- data/test/models/tasks_test.rb +14 -2
- data/test/models/team_test.rb +21 -2
- data/test/models/users_test.rb +29 -9
- data/test/unit/deploy_commands_test.rb +2 -2
- metadata +189 -171
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17cf66ed4359907e35e37a0ac00bcb05884c24148386bf8712d8b73c3cc33708
|
4
|
+
data.tar.gz: b21d35454c554c7faccad5236333cefc3270434617e4bd192372c0a8baa66ac1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f8e6ba6aaaa5eaa4630cfb52a1445e416e4cb6566550c4a5e2ec447065b2d342c52a728b370552a8b7a2a7df8061c30b0d4987d637729d533fd1bfd907a074a
|
7
|
+
data.tar.gz: 94abc1c12513349c85d4d54c4668865f21a42bf055d630fca667fd636dd7119b42f31c11f5c2182f2412ed76601adecce38b096a1add9e978dd83279b600d7fb
|
data/README.md
CHANGED
@@ -134,6 +134,19 @@ Lastly, if you override the `app_name` configuration in your Shipit deployment,
|
|
134
134
|
|
135
135
|
* * *
|
136
136
|
|
137
|
+
<h3 id="respecting-bare-files">Respecting bare <code>shipit.yml</code> files</h3>
|
138
|
+
|
139
|
+
Shipit will, by default, respect the "bare" <code>shipit.yml</code> file as a fallback option if no more specifically-named file exists (such as <code>shipit.staging.yml</code>).
|
140
|
+
|
141
|
+
You can configure this behavior via the attribute <code>Shipit.respect_bare_shipit_file</code>.
|
142
|
+
|
143
|
+
- The value <code>false</code> will disable this behavior and instead cause Shipit to emit an error upon deploy if Shipit cannot find a more specifically-named file.
|
144
|
+
- Setting this attribute to any other value (**including <code>nil</code>**), or not setting this attribute, will cause Shipit to use the default behavior of respecting bare <code>shipit.yml</code> files.
|
145
|
+
|
146
|
+
You can determine if Shipit is configured to respect bare files using <code>Shipit.respect_bare_shipit_file?</code>.
|
147
|
+
|
148
|
+
* * *
|
149
|
+
|
137
150
|
<h3 id="installing-dependencies">Installing dependencies</h3>
|
138
151
|
|
139
152
|
The **<code>dependencies</code>** step allows you to install all the packages your deploy script needs.
|
@@ -390,7 +403,7 @@ machine:
|
|
390
403
|
|
391
404
|
<h3 id="ci">CI</h3>
|
392
405
|
|
393
|
-
**<code>ci.require</code>** contains an array of the [statuses context](https://
|
406
|
+
**<code>ci.require</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) you want Shipit to disallow deploys if any of them is missing on the commit being deployed.
|
394
407
|
|
395
408
|
For example:
|
396
409
|
```yml
|
@@ -399,7 +412,7 @@ ci:
|
|
399
412
|
- ci/circleci
|
400
413
|
```
|
401
414
|
|
402
|
-
**<code>ci.hide</code>** contains an array of the [statuses context](https://
|
415
|
+
**<code>ci.hide</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) you want Shipit to ignore.
|
403
416
|
|
404
417
|
For example:
|
405
418
|
```yml
|
@@ -408,7 +421,7 @@ ci:
|
|
408
421
|
- ci/circleci
|
409
422
|
```
|
410
423
|
|
411
|
-
**<code>ci.allow_failures</code>** contains an array of the [statuses context](https://
|
424
|
+
**<code>ci.allow_failures</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) you want to be visible but not to required for deploy.
|
412
425
|
|
413
426
|
For example:
|
414
427
|
```yml
|
@@ -417,7 +430,7 @@ ci:
|
|
417
430
|
- ci/circleci
|
418
431
|
```
|
419
432
|
|
420
|
-
**<code>ci.blocking</code>** contains an array of the [statuses context](https://
|
433
|
+
**<code>ci.blocking</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) you want to disallow deploys if any of them is missing or failing on any of the commits being deployed.
|
421
434
|
|
422
435
|
For example:
|
423
436
|
```yml
|
@@ -440,7 +453,7 @@ merge:
|
|
440
453
|
revalidate_after: 12m30s
|
441
454
|
```
|
442
455
|
|
443
|
-
**<code>merge.require</code>** contains an array of the [statuses context](https://
|
456
|
+
**<code>merge.require</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) that you want Shipit to consider as failing if they aren't present on the pull request. Defaults to `ci.require` if present, or empty otherwise.
|
444
457
|
|
445
458
|
For example:
|
446
459
|
```yml
|
@@ -449,7 +462,7 @@ merge:
|
|
449
462
|
- continuous-integration/travis-ci/push
|
450
463
|
```
|
451
464
|
|
452
|
-
**<code>merge.ignore</code>** contains an array of the [statuses context](https://
|
465
|
+
**<code>merge.ignore</code>** contains an array of the [statuses context](https://docs.github.com/en/rest/reference/commits#commit-statuses) that you want Shipit not to consider when merging pull requests. Defaults to the union of `ci.allow_failures` and `ci.hide` if any is present or empty otherwise.
|
453
466
|
|
454
467
|
For example:
|
455
468
|
```yml
|
@@ -458,7 +471,7 @@ merge:
|
|
458
471
|
- codeclimate
|
459
472
|
```
|
460
473
|
|
461
|
-
**<code>merge.method</code>** the [merge method](https://
|
474
|
+
**<code>merge.method</code>** the [merge method](https://docs.github.com/en/rest/reference/pulls#merge-a-pull-request--parameters) to use for this stack. If it's not set the default merge method will be used. Can be either `merge`, `squash` or `rebase`.
|
462
475
|
|
463
476
|
For example:
|
464
477
|
```yml
|
@@ -17,7 +17,11 @@ module Shipit
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def force_github_authentication
|
20
|
-
if
|
20
|
+
if current_user.logged_in? && current_user.requires_fresh_login?
|
21
|
+
Rails.logger.warn("User #{current_user.id} requires a fresh login, logging out...")
|
22
|
+
reset_session
|
23
|
+
redirect_to(Shipit::Engine.routes.url_helpers.github_authentication_path(origin: request.original_url))
|
24
|
+
elsif Shipit.authentication_disabled? || current_user.logged_in?
|
21
25
|
unless current_user.authorized?
|
22
26
|
team_handles = Shipit.github_teams.map(&:handle)
|
23
27
|
team_list = team_handles.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')
|
@@ -28,6 +28,18 @@ module Shipit
|
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
+
module BasicAuth
|
32
|
+
# Workaround for https://github.com/rails/rails/pull/44610
|
33
|
+
extend ActionController::HttpAuthentication::Basic
|
34
|
+
extend self
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def has_basic_credentials?(request)
|
39
|
+
request.authorization.present? && (auth_scheme(request).downcase == "basic")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
31
43
|
def namespace_for_serializer
|
32
44
|
nil
|
33
45
|
end
|
@@ -36,7 +48,7 @@ module Shipit
|
|
36
48
|
@current_api_client = if Shipit.disable_api_authentication
|
37
49
|
UnlimitedApiClient.new
|
38
50
|
else
|
39
|
-
|
51
|
+
BasicAuth.authenticate(request) do |*parts|
|
40
52
|
token = parts.select(&:present?).join('--')
|
41
53
|
ApiClient.authenticate(token)
|
42
54
|
end
|
@@ -21,7 +21,7 @@ module Shipit
|
|
21
21
|
param_error!(:force, "Can't rollback, deploy in progress")
|
22
22
|
elsif stack.active_task?
|
23
23
|
active_task = stack.active_task
|
24
|
-
active_task.abort!(aborted_by: current_user, rollback_once_aborted_to: deploy)
|
24
|
+
active_task.abort!(aborted_by: current_user, rollback_once_aborted_to: deploy, rollback_once_aborted: true)
|
25
25
|
response = active_task
|
26
26
|
else
|
27
27
|
response = deploy.trigger_rollback(current_user, env: deploy_env, force: params.force, lock: params.lock)
|
@@ -27,7 +27,7 @@ module Shipit
|
|
27
27
|
requires :repo_name, String
|
28
28
|
accepts :environment, String
|
29
29
|
accepts :branch, String
|
30
|
-
accepts :deploy_url, String
|
30
|
+
accepts :deploy_url, String, allow_nil: true
|
31
31
|
accepts :ignore_ci, Boolean
|
32
32
|
accepts :merge_queue_enabled, Boolean
|
33
33
|
accepts :continuous_deployment, Boolean
|
@@ -40,8 +40,9 @@ module Shipit
|
|
40
40
|
end
|
41
41
|
|
42
42
|
params do
|
43
|
+
accepts :environment, String
|
43
44
|
accepts :branch, String
|
44
|
-
accepts :deploy_url, String
|
45
|
+
accepts :deploy_url, String, allow_nil: true
|
45
46
|
accepts :ignore_ci, Boolean
|
46
47
|
accepts :merge_queue_enabled, Boolean
|
47
48
|
accepts :continuous_deployment, Boolean
|
@@ -60,6 +61,11 @@ module Shipit
|
|
60
61
|
head(:accepted)
|
61
62
|
end
|
62
63
|
|
64
|
+
def refresh
|
65
|
+
GithubSyncJob.perform_later(stack_id: stack.id)
|
66
|
+
render_resource(stack, status: :accepted)
|
67
|
+
end
|
68
|
+
|
63
69
|
private
|
64
70
|
|
65
71
|
def create_params
|
@@ -3,14 +3,14 @@ module Shipit
|
|
3
3
|
module Api
|
4
4
|
class TasksController < BaseController
|
5
5
|
require_permission :read, :stack
|
6
|
-
require_permission :deploy, :stack, only:
|
6
|
+
require_permission :deploy, :stack, only: %i(trigger abort)
|
7
7
|
|
8
8
|
def index
|
9
9
|
render_resources(stack.tasks)
|
10
10
|
end
|
11
11
|
|
12
12
|
def show
|
13
|
-
render_resource(
|
13
|
+
render_resource(task)
|
14
14
|
end
|
15
15
|
|
16
16
|
params do
|
@@ -23,6 +23,23 @@ module Shipit
|
|
23
23
|
message: 'A task is already running.',
|
24
24
|
})
|
25
25
|
end
|
26
|
+
|
27
|
+
def abort
|
28
|
+
if task.active?
|
29
|
+
task.abort!(aborted_by: current_user)
|
30
|
+
head(:accepted)
|
31
|
+
else
|
32
|
+
render(status: :method_not_allowed, json: {
|
33
|
+
message: "This task is not currently running.",
|
34
|
+
})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def task
|
41
|
+
stack.tasks.find(params[:id])
|
42
|
+
end
|
26
43
|
end
|
27
44
|
end
|
28
45
|
end
|
@@ -5,7 +5,11 @@ module Shipit
|
|
5
5
|
before_action :load_deploy
|
6
6
|
|
7
7
|
def create
|
8
|
-
@rollback = @deploy.trigger_rollback(
|
8
|
+
@rollback = @deploy.trigger_rollback(
|
9
|
+
current_user,
|
10
|
+
env: rollback_params[:env]&.to_unsafe_hash,
|
11
|
+
force: params[:force].present?,
|
12
|
+
)
|
9
13
|
redirect_to(stack_deploy_path(@stack, @rollback))
|
10
14
|
rescue Task::ConcurrentTaskRunning
|
11
15
|
redirect_to(rollback_stack_deploy_path(@stack, @deploy))
|
@@ -36,6 +36,17 @@ module Shipit
|
|
36
36
|
link_to(t("deploy_button.caption.#{deploy_state}"), url, class: classes, data: data)
|
37
37
|
end
|
38
38
|
|
39
|
+
def rollback_button(deploy)
|
40
|
+
if deploy.stack.active_task?
|
41
|
+
link_to('Deploy in progress...', '#', class: 'btn disabled deploy-action')
|
42
|
+
else
|
43
|
+
url = rollback_stack_deploy_path(deploy.stack, deploy)
|
44
|
+
classes = %w(btn btn--delete deploy-action rollback-action)
|
45
|
+
|
46
|
+
link_to('Rollback to this deploy...', url, class: classes)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
39
50
|
def github_change_url(commit)
|
40
51
|
if commit.pull_request?
|
41
52
|
github_pull_request_url(commit)
|
@@ -49,9 +49,9 @@ module Shipit
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def fetch_members
|
52
|
-
Shipit.redis.multi do
|
53
|
-
|
54
|
-
|
52
|
+
Shipit.redis.multi do |transaction|
|
53
|
+
transaction.sunionstore(TMP_KEY, SET_KEY)
|
54
|
+
transaction.del(SET_KEY)
|
55
55
|
end
|
56
56
|
|
57
57
|
yield Shipit.redis.smembers(TMP_KEY).map { |r| r.split('|') }
|
@@ -44,9 +44,9 @@ module Shipit
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def write(output)
|
47
|
-
Shipit.redis.pipelined do
|
48
|
-
|
49
|
-
|
47
|
+
Shipit.redis.pipelined do |pipeline|
|
48
|
+
pipeline.append(key('output'), output)
|
49
|
+
pipeline.expire(key('output'), OUTPUT_TTL)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -9,7 +9,7 @@ module Shipit
|
|
9
9
|
validates :url, presence: true, url: { no_local: true, allow_blank: true }
|
10
10
|
validates :content_type, presence: true
|
11
11
|
|
12
|
-
serialize :response_headers,
|
12
|
+
serialize :response_headers, SafeJSON
|
13
13
|
|
14
14
|
after_commit :purge_old_deliveries, on: :create
|
15
15
|
|
data/app/models/shipit/deploy.rb
CHANGED
@@ -128,6 +128,7 @@ module Shipit
|
|
128
128
|
lock_reason = "A rollback for #{until_commit.sha} has been triggered. " \
|
129
129
|
"Please make sure the reason for the rollback has been addressed before deploying again."
|
130
130
|
stack.update!(lock_reason: lock_reason, lock_author_id: user_id)
|
131
|
+
stack.emit_lock_hooks
|
131
132
|
rollback
|
132
133
|
end
|
133
134
|
|
@@ -91,10 +91,30 @@ module Shipit
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def load_config
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
return if config_file_path.nil?
|
95
|
+
|
96
|
+
if !Shipit.respect_bare_shipit_file? && config_file_path.to_s.end_with?(*bare_shipit_filenames)
|
97
|
+
return { 'deploy' => { 'pre' => [shipit_not_obeying_bare_file_echo_command, 'exit 1'] } }
|
98
|
+
end
|
99
|
+
|
100
|
+
read_config(config_file_path)
|
101
|
+
end
|
102
|
+
|
103
|
+
def shipit_file_names_in_priority_order
|
104
|
+
["#{app_name}.#{@env}.yml", "#{app_name}.yml", "shipit.#{@env}.yml", "shipit.yml"].uniq
|
105
|
+
end
|
106
|
+
|
107
|
+
def bare_shipit_filenames
|
108
|
+
["#{app_name}.yml", "shipit.yml"].uniq
|
109
|
+
end
|
110
|
+
|
111
|
+
def config_file_path
|
112
|
+
shipit_file_names_in_priority_order.each do |filename|
|
113
|
+
path = file(filename, root: true)
|
114
|
+
return path if path.exist?
|
115
|
+
end
|
116
|
+
|
117
|
+
nil
|
98
118
|
end
|
99
119
|
|
100
120
|
def app_name
|
@@ -104,6 +124,14 @@ module Shipit
|
|
104
124
|
def read_config(path)
|
105
125
|
SafeYAML.load(path.read) if path.exist?
|
106
126
|
end
|
127
|
+
|
128
|
+
def shipit_not_obeying_bare_file_echo_command
|
129
|
+
<<~EOM
|
130
|
+
echo \"\e[1;31mShipit is configured to ignore the bare '#{app_name}.yml' file.
|
131
|
+
Please rename this file to more specifically include the environment name.
|
132
|
+
Deployments will fail until a valid '#{app_name}.#{@env}.yml' file is found.\e[0m\"
|
133
|
+
EOM
|
134
|
+
end
|
107
135
|
end
|
108
136
|
end
|
109
137
|
end
|
@@ -11,7 +11,7 @@ module Shipit
|
|
11
11
|
has_many :pull_request_assignments
|
12
12
|
has_many :assignees, class_name: :User, through: :pull_request_assignments, source: :user
|
13
13
|
|
14
|
-
serialize :labels, Array
|
14
|
+
serialize :labels, Shipit.serialized_column(:labels, type: Array)
|
15
15
|
|
16
16
|
after_create_commit :emit_create_hooks
|
17
17
|
after_update_commit :emit_update_hooks
|
data/app/models/shipit/stack.rb
CHANGED
@@ -608,6 +608,16 @@ module Shipit
|
|
608
608
|
Shipit.deployment_checks.call(self)
|
609
609
|
end
|
610
610
|
|
611
|
+
def emit_lock_hooks
|
612
|
+
return unless previous_changes.include?('lock_reason')
|
613
|
+
|
614
|
+
lock_details = if previous_changes['lock_reason'].last.blank?
|
615
|
+
{ from: previous_changes['locked_since'].first, until: Time.zone.now }
|
616
|
+
end
|
617
|
+
|
618
|
+
Hook.emit(:lock, self, locked: locked?, lock_details: lock_details, stack: self)
|
619
|
+
end
|
620
|
+
|
611
621
|
private
|
612
622
|
|
613
623
|
def clear_cache
|
@@ -641,16 +651,6 @@ module Shipit
|
|
641
651
|
end
|
642
652
|
end
|
643
653
|
|
644
|
-
def emit_lock_hooks
|
645
|
-
return unless previous_changes.include?('lock_reason')
|
646
|
-
|
647
|
-
lock_details = if previous_changes['lock_reason'].last.blank?
|
648
|
-
{ from: previous_changes['locked_since'].first, until: Time.zone.now }
|
649
|
-
end
|
650
|
-
|
651
|
-
Hook.emit(:lock, self, locked: locked?, lock_details: lock_details, stack: self)
|
652
|
-
end
|
653
|
-
|
654
654
|
def emit_added_hooks
|
655
655
|
Hook.emit(:stack, self, action: :added, stack: self)
|
656
656
|
end
|
data/app/models/shipit/task.rb
CHANGED
@@ -27,8 +27,35 @@ module Shipit
|
|
27
27
|
|
28
28
|
deferred_touch stack: :updated_at
|
29
29
|
|
30
|
+
module EnvHash
|
31
|
+
class << self
|
32
|
+
def dump(hash)
|
33
|
+
raise TypeError, "Task#env should be a Hash[String => String]" unless hash.is_a?(Hash)
|
34
|
+
hash = hash.to_h.stringify_keys
|
35
|
+
hash.transform_values! do |value|
|
36
|
+
case value
|
37
|
+
when String, Symbol, Numeric
|
38
|
+
value.to_s
|
39
|
+
else
|
40
|
+
raise TypeError, "Task#env should be a Hash[String => String]" unless hash.is_a?(Hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
hash unless hash.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def load(hash)
|
48
|
+
hash&.to_h || {} # cast back to a real hash
|
49
|
+
end
|
50
|
+
|
51
|
+
def new
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
30
57
|
serialize :definition, TaskDefinition
|
31
|
-
serialize :env,
|
58
|
+
serialize :env, Shipit.serialized_column(:env, coder: EnvHash)
|
32
59
|
|
33
60
|
scope :success, -> { where(status: 'success') }
|
34
61
|
scope :completed, -> { where(status: COMPLETED_STATUSES) }
|
@@ -313,9 +340,9 @@ module Shipit
|
|
313
340
|
end
|
314
341
|
|
315
342
|
def request_abort
|
316
|
-
Shipit.redis.pipelined do
|
317
|
-
|
318
|
-
|
343
|
+
Shipit.redis.pipelined do |pipeline|
|
344
|
+
pipeline.incr(abort_key)
|
345
|
+
pipeline.expire(abort_key, 1.month.to_i)
|
319
346
|
end
|
320
347
|
end
|
321
348
|
|
data/app/models/shipit/user.rb
CHANGED
@@ -3,6 +3,8 @@ module Shipit
|
|
3
3
|
class User < Record
|
4
4
|
DEFAULT_AVATAR = URI.parse('https://avatars.githubusercontent.com/u/583231?')
|
5
5
|
|
6
|
+
self.ignored_columns = %w(encrypted_github_access_token_iv)
|
7
|
+
|
6
8
|
has_many :memberships
|
7
9
|
has_many :teams, through: :memberships
|
8
10
|
has_many :authored_commits, class_name: :Commit, foreign_key: :author_id, inverse_of: :author
|
@@ -11,7 +13,10 @@ module Shipit
|
|
11
13
|
|
12
14
|
validates :name, presence: true
|
13
15
|
|
14
|
-
|
16
|
+
encrypts :encrypted_github_access_token
|
17
|
+
alias_attribute :github_access_token, :encrypted_github_access_token
|
18
|
+
|
19
|
+
after_find :discard_outdated_credentials!
|
15
20
|
|
16
21
|
def self.find_or_create_by_login!(login)
|
17
22
|
find_or_create_by!(login: login) do |user|
|
@@ -56,14 +61,6 @@ module Shipit
|
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
59
|
-
alias_method :original_github_access_token, :github_access_token
|
60
|
-
def github_access_token
|
61
|
-
original_github_access_token
|
62
|
-
rescue OpenSSL::Cipher::CipherError
|
63
|
-
update_columns(encrypted_github_access_token: nil, encrypted_github_access_token_iv: nil)
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
|
67
64
|
def github_api
|
68
65
|
return Shipit.github.api unless github_access_token
|
69
66
|
|
@@ -123,8 +120,25 @@ module Shipit
|
|
123
120
|
DEFAULT_AVATAR.dup
|
124
121
|
end
|
125
122
|
|
123
|
+
# https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats
|
124
|
+
GITHUB_TOKEN_FORMAT = /^gh[a-z]_/
|
125
|
+
|
126
|
+
def requires_fresh_login?
|
127
|
+
github_access_token.present? && !github_access_token.match(GITHUB_TOKEN_FORMAT)
|
128
|
+
end
|
129
|
+
|
126
130
|
private
|
127
131
|
|
132
|
+
def discard_outdated_credentials!
|
133
|
+
if encrypted_github_access_token_before_type_cast.present?
|
134
|
+
begin
|
135
|
+
encrypted_github_access_token
|
136
|
+
rescue ActiveRecord::Encryption::Errors::Decryption
|
137
|
+
update_column(:encrypted_github_access_token, nil)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
128
142
|
def identify_renamed_user!
|
129
143
|
last_commit = commits.last
|
130
144
|
return unless last_commit
|
@@ -8,7 +8,7 @@ module Shipit
|
|
8
8
|
attributes :id, :repo_owner, :repo_name, :environment, :html_url, :url, :tasks_url, :deploy_url,
|
9
9
|
:merge_requests_url, :deploy_spec, :undeployed_commits_count, :is_locked, :lock_reason, :continuous_deployment,
|
10
10
|
:created_at, :updated_at, :locked_since, :last_deployed_at, :branch, :merge_queue_enabled, :is_archived,
|
11
|
-
:archived_since
|
11
|
+
:archived_since, :ignore_ci
|
12
12
|
|
13
13
|
def url
|
14
14
|
api_stack_url(object)
|
@@ -55,11 +55,7 @@
|
|
55
55
|
<% unless read_only %>
|
56
56
|
<div class="deploy-actions">
|
57
57
|
<% if deploy.rollbackable? %>
|
58
|
-
|
59
|
-
<%= link_to 'Deploy in progress...', '#', class: 'btn disabled deploy-action' %>
|
60
|
-
<% else %>
|
61
|
-
<%= link_to 'Rollback to this deploy...', rollback_stack_deploy_path(@stack, deploy), class: 'btn btn--delete deploy-action rollback-action' %>
|
62
|
-
<% end %>
|
58
|
+
<%= rollback_button(deploy) %>
|
63
59
|
<% elsif deploy.currently_deployed? && !deploy.stack.active_task? %>
|
64
60
|
<%= redeploy_button(deploy.until_commit) %>
|
65
61
|
<% end %>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<%= link_to stack.github_repo_name, github_repo_url(stack.repo_owner, stack.repo_name) %>
|
24
24
|
has been inaccessible for <%= time_ago_in_words(stack.inaccessible_since) %>.
|
25
25
|
|
26
|
-
This could be a permission issue, or the
|
26
|
+
This could be a permission issue, or the repo could have changed on GitHub.
|
27
27
|
</p>
|
28
28
|
</div>
|
29
29
|
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<div class="setting-section">
|
2
|
+
<%= form_with scope: :stack, url: stack_path(stack), method: :patch do |f| %>
|
3
|
+
<div class="field-wrapper">
|
4
|
+
<%= f.label :environment %>
|
5
|
+
<%= f.text_field :environment, placeholder: 'production' %>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="field-wrapper">
|
9
|
+
<span>Branch: <%= stack.branch %></span>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="field-wrapper">
|
13
|
+
<%= f.label :deploy_url, 'Deploy URL (Where is this stack deployed to?)' %>
|
14
|
+
<%= f.text_field :deploy_url, placeholder: 'https://' %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="field-wrapper">
|
18
|
+
<%= f.check_box :continuous_deployment %>
|
19
|
+
<%= f.label :continuous_deployment, 'Enable continuous deployment' %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="field-wrapper">
|
23
|
+
<%= f.check_box :merge_queue_enabled %>
|
24
|
+
<%= f.label :merge_queue_enabled, 'Enable merge queue' %>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="field-wrapper">
|
28
|
+
<%= f.check_box :ignore_ci %>
|
29
|
+
<%= f.label :ignore_ci, "Don't require CI to deploy" %>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<%= f.submit class: "btn", value: "Save" %>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div class="setting-section">
|
37
|
+
<h5>Lock deploys</h5>
|
38
|
+
<%= form_with scope: :stack, url: stack_path(@stack), method: :patch do |f| %>
|
39
|
+
<div class="field-wrapper">
|
40
|
+
<%= f.label :lock_reason, 'Reason for lock' %>
|
41
|
+
<%= f.text_area :lock_reason %>
|
42
|
+
</div>
|
43
|
+
<% if @stack.locked? %>
|
44
|
+
<%= f.submit class: "btn", value: "Update Reason" %>
|
45
|
+
<% else %>
|
46
|
+
<%= f.submit class: "btn", value: "Lock" %>
|
47
|
+
<% end %>
|
48
|
+
<% end %>
|
49
|
+
<% if @stack.locked? %>
|
50
|
+
<%= form_with scope: :stack, url: stack_path(@stack), method: :patch do |f| %>
|
51
|
+
<%= f.hidden_field :lock_reason, value: nil %>
|
52
|
+
<%= f.submit class: "btn btn--primary", value: "Unlock" %>
|
53
|
+
<%- end -%>
|
54
|
+
<% end %>
|
55
|
+
</div>
|