shipit-engine 0.29.0 → 0.30.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 +37 -2
- data/app/assets/images/archive-solid.svg +1 -0
- data/app/assets/stylesheets/_pages/_stacks.scss +76 -0
- data/app/controllers/shipit/api/stacks_controller.rb +20 -1
- data/app/controllers/shipit/api_clients_controller.rb +49 -0
- data/app/controllers/shipit/merge_status_controller.rb +8 -4
- data/app/controllers/shipit/stacks_controller.rb +58 -9
- data/app/controllers/shipit/webhooks_controller.rb +2 -130
- data/app/helpers/shipit/stacks_helper.rb +4 -0
- data/app/jobs/shipit/background_job/unique.rb +3 -1
- data/app/jobs/shipit/continuous_delivery_job.rb +1 -0
- data/app/jobs/shipit/destroy_stack_job.rb +2 -2
- data/app/models/shipit/commit.rb +21 -9
- data/app/models/shipit/commit_deployment.rb +15 -11
- data/app/models/shipit/commit_deployment_status.rb +6 -2
- data/app/models/shipit/deploy.rb +48 -7
- data/app/models/shipit/deploy_stats.rb +57 -0
- data/app/models/shipit/repository.rb +38 -0
- data/app/models/shipit/stack.rb +41 -34
- data/app/models/shipit/task.rb +26 -4
- data/app/models/shipit/webhooks.rb +32 -0
- data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +19 -0
- data/app/models/shipit/webhooks/handlers/handler.rb +40 -0
- data/app/models/shipit/webhooks/handlers/membership_handler.rb +45 -0
- data/app/models/shipit/webhooks/handlers/push_handler.rb +20 -0
- data/app/models/shipit/webhooks/handlers/status_handler.rb +26 -0
- data/app/serializers/shipit/stack_serializer.rb +6 -1
- data/app/validators/ascii_only_validator.rb +3 -3
- data/app/views/layouts/_head.html.erb +0 -0
- data/app/views/layouts/shipit.html.erb +4 -2
- data/app/views/shipit/api_clients/index.html.erb +36 -0
- data/app/views/shipit/api_clients/new.html.erb +33 -0
- data/app/views/shipit/api_clients/show.html.erb +35 -0
- data/app/views/shipit/merge_status/logged_out.erb +1 -1
- data/app/views/shipit/stacks/_header.html.erb +12 -7
- data/app/views/shipit/stacks/_links.html.erb +1 -0
- data/app/views/shipit/stacks/index.html.erb +7 -2
- data/app/views/shipit/stacks/settings.html.erb +19 -0
- data/app/views/shipit/stacks/statistics.html.erb +82 -0
- data/config/locales/en.yml +14 -2
- data/config/routes.rb +4 -0
- data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
- data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
- data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
- data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
- data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
- data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
- data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
- data/lib/shipit/github_app.rb +32 -3
- data/lib/shipit/task_commands.rb +10 -2
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/api/ccmenu_controller_test.rb +1 -1
- data/test/controllers/api/stacks_controller_test.rb +14 -6
- data/test/controllers/api_clients_controller_test.rb +103 -0
- data/test/controllers/merge_status_controller_test.rb +21 -4
- data/test/controllers/stacks_controller_test.rb +35 -0
- data/test/controllers/webhooks_controller_test.rb +26 -0
- data/test/dummy/config/environments/development.rb +22 -4
- data/test/dummy/db/schema.rb +17 -6
- data/test/dummy/db/seeds.rb +20 -6
- data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
- data/test/fixtures/shipit/commit_deployments.yml +8 -8
- data/test/fixtures/shipit/commits.yml +23 -0
- data/test/fixtures/shipit/repositories.yml +23 -0
- data/test/fixtures/shipit/stacks.yml +100 -16
- data/test/fixtures/shipit/tasks.yml +66 -3
- data/test/jobs/destroy_stack_job_test.rb +9 -0
- data/test/models/commit_deployment_status_test.rb +33 -4
- data/test/models/commit_deployment_test.rb +8 -11
- data/test/models/commits_test.rb +22 -2
- data/test/models/deploy_stats_test.rb +112 -0
- data/test/models/deploys_test.rb +55 -17
- data/test/models/pull_request_test.rb +1 -1
- data/test/models/shipit/repository_test.rb +76 -0
- data/test/models/shipit/wehbooks/handlers_test.rb +26 -0
- data/test/models/stacks_test.rb +44 -51
- data/test/models/undeployed_commits_test.rb +13 -0
- data/test/test_helper.rb +3 -1
- data/test/unit/deploy_commands_test.rb +9 -0
- data/test/unit/github_app_test.rb +136 -0
- metadata +161 -128
data/app/models/shipit/task.rb
CHANGED
@@ -16,8 +16,8 @@ module Shipit
|
|
16
16
|
belongs_to :user, optional: true
|
17
17
|
belongs_to :aborted_by, class_name: 'User', optional: true
|
18
18
|
belongs_to :stack, counter_cache: true
|
19
|
-
belongs_to :until_commit, class_name: 'Commit'
|
20
|
-
belongs_to :since_commit, class_name: 'Commit'
|
19
|
+
belongs_to :until_commit, class_name: 'Commit', required: false
|
20
|
+
belongs_to :since_commit, class_name: 'Commit', required: false
|
21
21
|
|
22
22
|
deferred_touch stack: :updated_at
|
23
23
|
|
@@ -29,14 +29,18 @@ module Shipit
|
|
29
29
|
scope :success, -> { where(status: 'success') }
|
30
30
|
scope :completed, -> { where(status: COMPLETED_STATUSES) }
|
31
31
|
scope :active, -> { where(status: ACTIVE_STATUSES) }
|
32
|
+
scope :not_active, -> { where(status: COMPLETED_STATUSES + UNSUCCESSFUL_STATUSES) }
|
32
33
|
scope :exclusive, -> { where(allow_concurrency: false) }
|
33
34
|
scope :unsuccessful, -> { where(status: UNSUCCESSFUL_STATUSES) }
|
35
|
+
scope :last_seven_days, -> { where("created_at > ?", 7.days.ago) }
|
36
|
+
scope :previous_seven_days, -> { where(created_at: 14.days.ago..7.days.ago) }
|
34
37
|
|
35
38
|
scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
|
36
39
|
|
37
40
|
after_save :record_status_change
|
38
41
|
after_create :prevent_concurrency, unless: :allow_concurrency?
|
39
|
-
after_commit :emit_hooks
|
42
|
+
after_commit :emit_hooks, on: :create
|
43
|
+
after_commit :emit_hooks_if_status_changed, on: :update
|
40
44
|
|
41
45
|
class << self
|
42
46
|
def durations
|
@@ -303,9 +307,13 @@ module Shipit
|
|
303
307
|
end
|
304
308
|
end
|
305
309
|
|
306
|
-
def
|
310
|
+
def emit_hooks_if_status_changed
|
307
311
|
return unless @status_changed
|
308
312
|
@status_changed = nil
|
313
|
+
emit_hooks
|
314
|
+
end
|
315
|
+
|
316
|
+
def emit_hooks
|
309
317
|
Hook.emit(hook_event, stack, hook_event => self, status: status, stack: stack)
|
310
318
|
end
|
311
319
|
|
@@ -327,6 +335,20 @@ module Shipit
|
|
327
335
|
end
|
328
336
|
end
|
329
337
|
|
338
|
+
def commit_range?
|
339
|
+
since_commit && until_commit
|
340
|
+
end
|
341
|
+
|
342
|
+
def includes_commit?(commit)
|
343
|
+
return false unless commit_range?
|
344
|
+
|
345
|
+
if since_commit == until_commit
|
346
|
+
commit.id == since_commit.id
|
347
|
+
else
|
348
|
+
commit.id > since_commit.id && commit.id <= until_commit.id
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
330
352
|
private
|
331
353
|
|
332
354
|
def prevent_concurrency
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
class << self
|
4
|
+
def default_handlers
|
5
|
+
{
|
6
|
+
'push' => [Handlers::PushHandler],
|
7
|
+
'status' => [Handlers::StatusHandler],
|
8
|
+
'membership' => [Handlers::MembershipHandler],
|
9
|
+
'check_suite' => [Handlers::CheckSuiteHandler],
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def handlers
|
14
|
+
@handlers ||= reset_handlers!
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset_handlers!
|
18
|
+
@handlers = default_handlers
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_handler(event, callable = nil, &block)
|
22
|
+
handlers[event] ||= []
|
23
|
+
handlers[event] << callable if callable
|
24
|
+
handlers[event] << block if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
def for_event(event)
|
28
|
+
handlers.fetch(event) { [] }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
module Handlers
|
4
|
+
class CheckSuiteHandler < Handler
|
5
|
+
params do
|
6
|
+
requires :check_suite do
|
7
|
+
requires :head_sha, String
|
8
|
+
requires :head_branch, String
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def process
|
12
|
+
stacks.where(branch: params.check_suite.head_branch).each do |stack|
|
13
|
+
stack.commits.where(sha: params.check_suite.head_sha).each(&:schedule_refresh_check_runs!)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
module Handlers
|
4
|
+
class Handler
|
5
|
+
class << self
|
6
|
+
attr_reader :param_parser
|
7
|
+
|
8
|
+
def params(&block)
|
9
|
+
@param_parser = ExplicitParameters::Parameters.define(&block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.call(params)
|
14
|
+
new(params).process
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :params, :payload
|
18
|
+
|
19
|
+
def initialize(payload)
|
20
|
+
@payload = payload
|
21
|
+
@params = self.class.param_parser.parse!(payload)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def stacks
|
31
|
+
@stacks ||= Repository.from_github_repo_name(repository_name)&.stacks || Stack.none
|
32
|
+
end
|
33
|
+
|
34
|
+
def repository_name
|
35
|
+
payload.dig('repository', 'full_name')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
module Handlers
|
4
|
+
class MembershipHandler < Handler
|
5
|
+
params do
|
6
|
+
requires :action, String
|
7
|
+
requires :team do
|
8
|
+
requires :id, Integer
|
9
|
+
requires :name, String
|
10
|
+
requires :slug, String
|
11
|
+
requires :url, String
|
12
|
+
end
|
13
|
+
requires :organization do
|
14
|
+
requires :login, String
|
15
|
+
end
|
16
|
+
requires :member do
|
17
|
+
requires :login, String
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def process
|
21
|
+
team = find_or_create_team!
|
22
|
+
member = User.find_or_create_by_login!(params.member.login)
|
23
|
+
|
24
|
+
case params.action
|
25
|
+
when 'added'
|
26
|
+
team.add_member(member)
|
27
|
+
when 'removed'
|
28
|
+
team.members.delete(member)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "Don't know how to perform action: `#{action.inspect}`"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def find_or_create_team!
|
37
|
+
Team.find_or_create_by!(github_id: params.team.id) do |team|
|
38
|
+
team.github_team = params.team
|
39
|
+
team.organization = params.organization.login
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
module Handlers
|
4
|
+
class PushHandler < Handler
|
5
|
+
params do
|
6
|
+
requires :ref
|
7
|
+
end
|
8
|
+
def process
|
9
|
+
stacks.where(branch: branch).each(&:sync_github)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def branch
|
15
|
+
params.ref.gsub('refs/heads/', '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Webhooks
|
3
|
+
module Handlers
|
4
|
+
class StatusHandler < Handler
|
5
|
+
params do
|
6
|
+
requires :sha, String
|
7
|
+
requires :state, String
|
8
|
+
accepts :description, String
|
9
|
+
accepts :target_url, String
|
10
|
+
accepts :context, String
|
11
|
+
accepts :created_at, String
|
12
|
+
|
13
|
+
accepts :branches, Array do
|
14
|
+
requires :name, String
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
Commit.where(sha: params.sha).each do |commit|
|
20
|
+
commit.create_status_from_github!(params)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -5,7 +5,8 @@ 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, :merge_queue_enabled
|
8
|
+
:updated_at, :locked_since, :last_deployed_at, :branch, :merge_queue_enabled, :is_archived,
|
9
|
+
:archived_since
|
9
10
|
|
10
11
|
def url
|
11
12
|
api_stack_url(object)
|
@@ -39,6 +40,10 @@ module Shipit
|
|
39
40
|
object.locked?
|
40
41
|
end
|
41
42
|
|
43
|
+
def is_archived
|
44
|
+
object.archived?
|
45
|
+
end
|
46
|
+
|
42
47
|
def deploy_spec
|
43
48
|
object.cached_deploy_spec.cacheable.config
|
44
49
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class AsciiOnlyValidator < ActiveModel::EachValidator
|
2
2
|
def validate_each(record, attribute, value)
|
3
|
-
value.
|
4
|
-
|
5
|
-
|
3
|
+
if value && !value.ascii_only?
|
4
|
+
record.errors.add(attribute, :ascii)
|
5
|
+
end
|
6
6
|
end
|
7
7
|
end
|
File without changes
|
@@ -12,6 +12,8 @@
|
|
12
12
|
<%= javascript_include_tag :shipit %>
|
13
13
|
<%= yield :update_subscription %>
|
14
14
|
<%= csrf_meta_tags %>
|
15
|
+
|
16
|
+
<%= render 'layouts/head' %>
|
15
17
|
</head>
|
16
18
|
<body>
|
17
19
|
<% flash.each do |name, msg| %>
|
@@ -52,11 +54,11 @@
|
|
52
54
|
<% end %>
|
53
55
|
|
54
56
|
<header class="header">
|
55
|
-
<%= link_to "Shipit v#{Shipit::VERSION}", "https://github.com/Shopify/shipit-engine/tree/v#{Shipit::VERSION}", class: 'powered-by' %>
|
57
|
+
<%= link_to "Powered by Shipit v#{Shipit::VERSION}", "https://github.com/Shopify/shipit-engine/tree/v#{Shipit::VERSION}", class: 'powered-by' %>
|
56
58
|
<div class="wrapper">
|
57
59
|
<div class="header__inner">
|
58
60
|
<a href="/" class="logo">
|
59
|
-
<span class="visually-hidden"
|
61
|
+
<span class="visually-hidden"><%= Shipit.app_name %></span>
|
60
62
|
</a>
|
61
63
|
<div class="header__page-title">
|
62
64
|
<%= yield :page_title %>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
<div class="wrapper">
|
3
|
+
<section>
|
4
|
+
<%= link_to "Create new api client", new_api_client_path, class: 'btn'%>
|
5
|
+
</section>
|
6
|
+
</div>
|
7
|
+
<div class="wrapper">
|
8
|
+
<section>
|
9
|
+
<ul class="task-list">
|
10
|
+
<% @api_clients.each do |api_client|%>
|
11
|
+
<li>
|
12
|
+
<p>
|
13
|
+
<%= link_to api_client.name, api_client_path(api_client) %>
|
14
|
+
created by <%= api_client.creator.name %> on <%= api_client.created_at%>
|
15
|
+
</p>
|
16
|
+
</li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
</section>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="wrapper">
|
23
|
+
<section class="pagination">
|
24
|
+
<% if params[:since].present? %>
|
25
|
+
<%= link_to 'Start', @links[:first], class: 'header__btn btn' %>
|
26
|
+
<% else %>
|
27
|
+
<%= link_to 'Start', '', class: 'header__btn btn btn--disabled disabled' %>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<% if url = @links[:next] %>
|
31
|
+
<%= link_to 'Older', url, class: 'header__btn btn' %>
|
32
|
+
<% else %>
|
33
|
+
<%= link_to 'Older', '', class: 'header__btn btn btn--disabled disabled' %>
|
34
|
+
<% end %>
|
35
|
+
</section>
|
36
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div class="wrapper">
|
2
|
+
<section>
|
3
|
+
<header class="section-header">
|
4
|
+
<p>New Api Client</p>
|
5
|
+
</header>
|
6
|
+
</section>
|
7
|
+
|
8
|
+
<%= form_for @api_client, url: api_clients_path(@api_client) do |f| %>
|
9
|
+
<section>
|
10
|
+
<%= label_tag "App Name" %>
|
11
|
+
<%= f.text_field :name, placeholder: 'e.g. spy', required: true %>
|
12
|
+
</section>
|
13
|
+
|
14
|
+
<section>
|
15
|
+
<%= label_tag "Permissions" %>
|
16
|
+
<ul class="deploy-checklist">
|
17
|
+
<% Shipit::ApiClient::PERMISSIONS.each do |permission| %>
|
18
|
+
<li class="deploy-checklist__item">
|
19
|
+
<%= check_box_tag 'api_client[permissions][]', permission, false,
|
20
|
+
class: 'deploy-checklist__item__checkbox', id: "checkbox_" + permission %>
|
21
|
+
<label class="deploy-checklist__item__label" for="checkbox_<%= permission %>">
|
22
|
+
<%= permission %>
|
23
|
+
</label>
|
24
|
+
</li>
|
25
|
+
<% end %>
|
26
|
+
</ul>
|
27
|
+
</section>
|
28
|
+
|
29
|
+
<section class="submit-section">
|
30
|
+
<%= f.submit "Create", :class => ['btn', 'primary'] %>
|
31
|
+
</section>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div class="wrapper">
|
2
|
+
<section>
|
3
|
+
<header class="section-header">
|
4
|
+
<h2>Api client: <b><%= @api_client.name %></b></h2>
|
5
|
+
</header>
|
6
|
+
|
7
|
+
<p>Created by <%= @api_client.creator.name %> on <%= @api_client.created_at %></p>
|
8
|
+
</section>
|
9
|
+
|
10
|
+
<section>
|
11
|
+
<h3>Authentication token:</h3>
|
12
|
+
<code style="background-color: yellow">
|
13
|
+
<b><%= @api_client.authentication_token %></b>
|
14
|
+
</code>
|
15
|
+
</section>
|
16
|
+
|
17
|
+
<section>
|
18
|
+
<%= form_for @api_client, url: api_client_path(@api_client) do |f| %>
|
19
|
+
<h3> Permissions </h3>
|
20
|
+
<ul class="deploy-checklist">
|
21
|
+
<% Shipit::ApiClient::PERMISSIONS.each do |permission| %>
|
22
|
+
<li class="deploy-checklist__item">
|
23
|
+
<%= check_box_tag 'api_client[permissions][]', permission, @api_client.permissions.include?(permission),
|
24
|
+
class: 'deploy-checklist__item__checkbox', id: "checkbox_" + permission %>
|
25
|
+
<label class="deploy-checklist__item__label" for="checkbox_<%= permission %>">
|
26
|
+
<%= permission %>
|
27
|
+
</label>
|
28
|
+
</li>
|
29
|
+
<% end %>
|
30
|
+
</ul>
|
31
|
+
<%= f.submit "Update", :class => ['btn', 'primary'] %>
|
32
|
+
<% end %>
|
33
|
+
</section>
|
34
|
+
|
35
|
+
</div>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<%= render 'anchor', color: '#bd2c00' %>
|
4
4
|
</div>
|
5
5
|
<h4 class="status-heading text-red">
|
6
|
-
Please log in to <%= link_to
|
6
|
+
Please log in to <%= link_to Shipit.app_name, '/', target: '_blank', rel: 'noopener' %>
|
7
7
|
</h4>
|
8
8
|
<span class="status-meta">
|
9
9
|
Here Comes The Walrus is unable to check merge status.
|
@@ -5,23 +5,26 @@
|
|
5
5
|
|
6
6
|
|
7
7
|
<% content_for :primary_navigation do %>
|
8
|
-
<%= link_to '
|
8
|
+
<%= link_to t('stack.nav.refresh'), stack_refresh_path(stack), method: 'post', class: "header__btn btn" %>
|
9
9
|
<% end %>
|
10
10
|
|
11
11
|
<% content_for :secondary_navigation do %>
|
12
12
|
<ul class="nav__list nav__list--primary">
|
13
13
|
<li class="nav__list__item">
|
14
|
-
<%= link_to
|
14
|
+
<%= link_to t('stack.nav.commits'), stack_path(stack) %>
|
15
15
|
</li>
|
16
16
|
<li class="nav__list__item">
|
17
|
-
<%= link_to '
|
17
|
+
<%= link_to t('stack.nav.settings'), stack_settings_path(stack) %>
|
18
18
|
</li>
|
19
19
|
<li class="nav__list__item">
|
20
|
-
<%= link_to '
|
20
|
+
<%= link_to t('stack.nav.timeline'), index_stack_tasks_path(stack) %>
|
21
|
+
</li>
|
22
|
+
<li class="nav__list__item">
|
23
|
+
<%= link_to t('stack.nav.statistics'), stack_statistics_path(stack) %>
|
21
24
|
</li>
|
22
25
|
<% if stack.merge_queue_enabled? %>
|
23
26
|
<li class="nav__list__item">
|
24
|
-
<%= link_to
|
27
|
+
<%= link_to t('stack.nav.merge_queue', count: stack.pull_requests.queued.count), stack_pull_requests_path(stack) %>
|
25
28
|
</li>
|
26
29
|
<% end %>
|
27
30
|
|
@@ -38,6 +41,8 @@
|
|
38
41
|
</ul>
|
39
42
|
</li>
|
40
43
|
<% end %>
|
44
|
+
|
45
|
+
<%= render partial: 'shipit/stacks/links', locals: { stack: stack } %>
|
41
46
|
</ul>
|
42
47
|
|
43
48
|
<ul class="nav__list nav__list--secondary">
|
@@ -49,11 +54,11 @@
|
|
49
54
|
<% end %>
|
50
55
|
<% end %>
|
51
56
|
<li class="nav__list__item">
|
52
|
-
<%= link_to '
|
57
|
+
<%= link_to t('stack.nav.view_on_github'), github_repo_url(stack.repo_owner, stack.repo_name) %>
|
53
58
|
</li>
|
54
59
|
<% if stack.deploy_url.present? %>
|
55
60
|
<li class="nav__list__item">
|
56
|
-
<%= sanitize link_to '
|
61
|
+
<%= sanitize link_to t('stack.nav.deploy_link'), stack.deploy_url, :target => '_blank', :class => 'deploy-url' %>
|
57
62
|
</li>
|
58
63
|
<% end %>
|
59
64
|
</ul>
|