From b14dcfd1d617d9cf9524b3bd1694739cd8a28c61 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 18 Apr 2018 18:48:19 +0100 Subject: Use cards for label list --- app/assets/stylesheets/pages/labels.scss | 14 ++++++++++++++ app/views/projects/labels/index.html.haml | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index b0852adb459..bd9b3169d2c 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -143,6 +143,10 @@ cursor: move; cursor: -webkit-grab; cursor: -moz-grab; + margin-bottom: 5px; + + padding: 11px 10px 11px $gl-padding; + border-radius: $border-radius-default; &:active { cursor: -webkit-grabbing; @@ -152,6 +156,10 @@ &.sortable-ghost { opacity: 0.3; } + + .prioritized-labels & { + box-shadow: 0 1px 2px $issue-boards-card-shadow; + } } .btn-action { @@ -319,3 +327,9 @@ font-size: $label-font-size; } } + +.labels-container { + background-color: $gray-light; + border-radius: $border-radius-default; + padding: $gl-padding $gl-padding-8; +} \ No newline at end of file diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 9c78bade254..5456ddd9a8f 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -17,12 +17,12 @@ = link_to new_project_label_path(@project), class: "btn btn-new" do New label - .labels + .labels-container.prepend-top-5 - if can_admin_label -# Only show it in the first page - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } - %h5 Prioritized Labels + %h5.prepend-top-0 Prioritized Labels %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' -- cgit v1.2.1 From 8e342f1c7f7623304419d1dfeca68bf1b106eb89 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 00:09:44 +0100 Subject: Start restyle label list items --- app/assets/stylesheets/pages/labels.scss | 31 +++++++- app/views/shared/_label.html.haml | 120 +++++++++++-------------------- app/views/shared/_label_row.html.haml | 30 +++----- 3 files changed, 82 insertions(+), 99 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index bd9b3169d2c..3449c90e3ad 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -63,8 +63,7 @@ margin-bottom: 10px; @media (min-width: $screen-sm-min) { - width: 200px; - margin-left: $gl-padding * 2; + width: 100px; margin-bottom: 0; } @@ -332,4 +331,32 @@ background-color: $gray-light; border-radius: $border-radius-default; padding: $gl-padding $gl-padding-8; +} + +.label-actions-list { + list-style: none; +} + +.label-badge { + color: $theme-gray-900; + font-weight: $gl-font-weight-bold; + padding: $gl-padding-4; + border-radius: $border-radius-default; +} + +.label-badge-blue { + background-color: $theme-blue-100; +} + +.label-badge-gray { + background-color: $theme-gray-100; +} + +.label-links { + list-style: none; + padding: 0; +} + +.label-link-item { + padding: 0; } \ No newline at end of file diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 836df57a3a2..578c6c73fe2 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -7,87 +7,51 @@ %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label - - .visible-xs.visible-sm-inline-block.dropdown - %button.btn.btn-default.label-options-toggle{ type: 'button', data: { toggle: "dropdown" } } - Options - = icon('caret-down') - .dropdown-menu.dropdown-menu-align-right - %ul - - if show_label_merge_requests_link - %li - = link_to_label(label, subject: subject, type: :merge_request) do - View merge requests - - if show_label_issues_link - %li - = link_to_label(label, subject: subject) do - View open issues - - if current_user - %li.label-subscription - - if can_subscribe_to_label_in_different_levels?(label) - %a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } - %span Unsubscribe - %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } - %span Subscribe at project level - %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } - %span Subscribe at group level - - else - %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', data: { status: status, url: toggle_subscription_path } } - %span= label_subscription_toggle_button_text(label, @project) - - - if can?(current_user, :admin_label, label) - %li - = link_to 'Edit', edit_label_path(label) - %li - = link_to 'Delete', - destroy_label_path(label), - title: 'Delete', - method: :delete, - data: {confirm: 'Remove this label? Are you sure?'}, - class: 'text-danger' - - .pull-right.hidden-xs.hidden-sm - - if can?(current_user, :admin_label, label) - - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'), - disabled: true, - type: 'button', - data: { url: promote_project_label_path(label.project, label), - label_title: label.title, - label_color: label.color, - label_text_color: label.text_color, - group_name: label.project.group.name, - target: '#promote-label-modal', - container: 'body', - toggle: 'modal' } } - = sprite_icon('level-up') - = link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do - %span.sr-only Edit - = sprite_icon('pencil') - %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } } - = link_to "#", title: "Delete", class: 'btn btn-transparent btn-action remove-row', data: { toggle: "tooltip" } do - %span.sr-only Delete - = sprite_icon('remove') + %ul.label-actions-list.inline + %li.inline + .label-badge.label-badge-gray= label.model_name.human.titleize + - if can?(current_user, :admin_label, @project) + %li.inline.js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + dom_id: dom_id(label), type: label.type } } + %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } + = icon('star-o') + %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } + = icon('star') + %li.inline + = link_to edit_label_path(label) do + = icon('pencil') + %li.inline + .dropdown + %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown', data: { toggle: "dropdown" } } + = custom_icon('ellipsis_v') + .dropdown-menu.dropdown-menu-align-right + %ul + - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) + %li + %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'), + disabled: true, + type: 'button', + data: { url: promote_project_label_path(label.project, label), + label_title: label.title, + label_color: label.color, + label_text_color: label.text_color, + group_name: label.project.group.name, + target: '#promote-label-modal', + container: 'body', + toggle: 'modal' } } + = sprite_icon('level-up') + %li + = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: 'Remove this label? Are you sure?' }, class: 'text-danger' - if current_user - .label-subscription.inline + %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) - %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } + %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } %span Unsubscribe - = icon('spinner spin', class: 'label-subscribe-button-loading') - - .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } - %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } - %span Subscribe - = icon('chevron-down') - %ul.dropdown-menu - %li - %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } - Project level - %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } - Group level + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } + %span Subscribe at project level + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } + %span Subscribe at group level - else - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', data: { status: status, url: toggle_subscription_path } } - %span= label_subscription_toggle_button_text(label, @project) - = icon('spinner spin', class: 'label-subscribe-button-loading') + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path } } = render 'shared/delete_label_modal', label: label diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index bd4f191502e..fa72086e048 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -2,29 +2,21 @@ - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) -%span.label-row - - if can?(current_user, :admin_label, @project) - .draggable-handler - = icon('bars') - .js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), - dom_id: dom_id(label), type: label.type } } - %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } - = icon('star-o') - %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } - = icon('star') - %span.label-name +.label-row.inline + .label-name = link_to_label(label, subject: @project, tooltip: false) - - if defined?(@project) && @project.group.present? - %span.label-type - = label.model_name.human.titleize - - %span.label-description + .label-description - if label.description.present? .description-text = markdown_field(label, :description) - .hidden-xs.hidden-sm + %ul.label-links - if show_label_issues_link - = link_to_label(label, subject: subject) { 'Issues' } + %li.label-link-item.inline + = link_to_label(label, subject: subject) { 'Issues' } - if show_label_merge_requests_link · - = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } + %li.label-link-item.inline + = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } + - if label.priorities.present? + %li.label-link-item.inline + .label-badge.label-badge-blue Prioritized label \ No newline at end of file -- cgit v1.2.1 From ce0677e2b7d37aa3dff1e96b06145d1a7c164629 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 00:11:15 +0100 Subject: add changelog --- .../39549-label-list-page-redesign-with-draggable-labels.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml diff --git a/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml b/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml new file mode 100644 index 00000000000..fb4fbf80575 --- /dev/null +++ b/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml @@ -0,0 +1,5 @@ +--- +title: Label list page redesign +merge_request: 18466 +author: +type: changed -- cgit v1.2.1 From 3466bf1cc183641077013d553bd59dcc76074dc3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 00:34:03 +0100 Subject: flex it --- app/assets/stylesheets/pages/labels.scss | 72 +++++++------------------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3449c90e3ad..5d15d03a7bd 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -58,14 +58,11 @@ } .label-row { - .label-name { - display: inline-block; - margin-bottom: 10px; + display: flex; - @media (min-width: $screen-sm-min) { - width: 100px; - margin-bottom: 0; - } + .label-name { + width: 200px; + flex-shrink: 0; .label { overflow: hidden; @@ -78,14 +75,6 @@ display: block; margin-bottom: 10px; margin-left: 50px; - - @media (min-width: $screen-sm-min) { - display: inline-block; - width: 100px; - margin-left: 10px; - margin-bottom: 0; - vertical-align: top; - } } .label-description { @@ -99,14 +88,6 @@ a { color: $blue-600; } - - @media (min-width: $screen-sm-min) { - display: inline-block; - max-width: 50%; - margin-left: 10px; - margin-bottom: 0; - vertical-align: top; - } } .label { @@ -131,19 +112,14 @@ } .manage-labels-list { - @media(min-width: $screen-md-min) { - &.content-list li { - padding: $gl-padding 0; - } - } - > li:not(.empty-message):not(.is-not-draggable) { background-color: $white-light; cursor: move; cursor: -webkit-grab; cursor: -moz-grab; margin-bottom: 5px; - + display: flex; + justify-content: space-between; padding: 11px 10px 11px $gl-padding; border-radius: $border-radius-default; @@ -176,27 +152,6 @@ } } } - - .dropdown { - @media (min-width: $screen-sm-min) { - float: right; - } - } - - @media (max-width: $screen-xs-max) { - .dropdown-menu { - min-width: 100%; - } - } -} - -.draggable-handler { - display: inline-block; - vertical-align: top; - margin: 5px 0; - opacity: 0; - transition: opacity .3s; - color: $gray-darkest; } .prioritized-labels { @@ -290,11 +245,6 @@ } .label-subscribe-button { - @media(min-width: $screen-md-min) { - min-width: 105px; - margin-left: $gl-padding; - } - .label-subscribe-button-icon { &[disabled] { opacity: 0.5; @@ -335,6 +285,7 @@ .label-actions-list { list-style: none; + flex-shrink: 0; } .label-badge { @@ -355,8 +306,17 @@ .label-links { list-style: none; padding: 0; + white-space: nowrap; } .label-link-item { padding: 0; +} + +.label-list-item { + .content-list &:before, + .content-list &:after { + content: none; + display: block; + } } \ No newline at end of file -- cgit v1.2.1 From f416c8a128e846d3c8d0a0050626600f665866a1 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 00:36:09 +0100 Subject: Margin left for priority label --- app/views/shared/_label_row.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index fa72086e048..2d7fdfae46d 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -18,5 +18,5 @@ %li.label-link-item.inline = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } - if label.priorities.present? - %li.label-link-item.inline + %li.label-link-item.inline.prepend-left-10 .label-badge.label-badge-blue Prioritized label \ No newline at end of file -- cgit v1.2.1 From d0d521f362b5f8ba67e1a8b259db48d4b2a9b541 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 01:17:14 +0100 Subject: Dont hide sub button text when toggling --- app/assets/javascripts/project_label_subscription.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index f31beb4dc78..e3824d83728 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -20,7 +20,6 @@ export default class ProjectLabelSubscription { const oldStatus = $btn.attr('data-status'); $btn.addClass('disabled'); - $span.toggleClass('hidden'); axios.post(url).then(() => { let newStatus; @@ -32,7 +31,6 @@ export default class ProjectLabelSubscription { [newStatus, newAction] = ['unsubscribed', 'Subscribe']; } - $span.toggleClass('hidden'); $btn.removeClass('disabled'); this.$buttons.attr('data-status', newStatus); -- cgit v1.2.1 From c9f23ffe5bb4cc5bf28e28e7736d867ec0de5443 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 01:17:29 +0100 Subject: Remove unneeded inlines and remove hand where needed --- app/assets/stylesheets/pages/labels.scss | 10 +++++++--- app/views/shared/_label.html.haml | 5 +++-- app/views/shared/_label_row.html.haml | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 5d15d03a7bd..c84c1594986 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -79,10 +79,9 @@ .label-description { display: block; - margin-bottom: 10px; .description-text { - margin-bottom: $gl-padding; + margin: 0 $gl-padding 10px 0; } a { @@ -135,6 +134,10 @@ .prioritized-labels & { box-shadow: 0 1px 2px $issue-boards-card-shadow; } + + &.no-hand { + cursor: auto; + } } .btn-action { @@ -286,6 +289,7 @@ .label-actions-list { list-style: none; flex-shrink: 0; + padding: 0; } .label-badge { @@ -305,7 +309,7 @@ .label-links { list-style: none; - padding: 0; + padding: 0 $gl-padding 0 0; white-space: nowrap; } diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 578c6c73fe2..8302918ebc9 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -5,9 +5,9 @@ - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -%li.label-list-item{ id: label_css_id, data: { id: label.id } } +%li.label-list-item{ class: label.priorities.present? ? '' : 'no-hand', id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label - %ul.label-actions-list.inline + %ul.label-actions-list %li.inline .label-badge.label-badge-gray= label.model_name.human.titleize - if can?(current_user, :admin_label, @project) @@ -53,5 +53,6 @@ %span Subscribe at group level - else %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path } } + %span= label_subscription_toggle_button_text(label, @project) = render 'shared/delete_label_modal', label: label diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 2d7fdfae46d..a2997bb9146 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -2,7 +2,7 @@ - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) -.label-row.inline +.label-row .label-name = link_to_label(label, subject: @project, tooltip: false) .label-description -- cgit v1.2.1 From 8b4e1d0ccd6c2624ac230597c4e63c8b51bc76ba Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 01:20:38 +0100 Subject: Add/use Label#priority? and remove weird padding --- app/assets/stylesheets/pages/labels.scss | 4 ++-- app/models/label.rb | 4 ++++ app/views/shared/_label.html.haml | 2 +- app/views/shared/_label_row.html.haml | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index c84c1594986..85740b91ba6 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -81,7 +81,7 @@ display: block; .description-text { - margin: 0 $gl-padding 10px 0; + margin: 0 0 10px 0; } a { @@ -309,7 +309,7 @@ .label-links { list-style: none; - padding: 0 $gl-padding 0 0; + padding: 0; white-space: nowrap; } diff --git a/app/models/label.rb b/app/models/label.rb index f3496884cff..12e8c5695d4 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -137,6 +137,10 @@ class Label < ActiveRecord::Base priority.try(:priority) end + def priority? + priorities.present? + end + def template? template end diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 8302918ebc9..3d7cad8ecf8 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -5,7 +5,7 @@ - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -%li.label-list-item{ class: label.priorities.present? ? '' : 'no-hand', id: label_css_id, data: { id: label.id } } +%li.label-list-item{ class: label.priority? ? '' : 'no-hand', id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label %ul.label-actions-list %li.inline diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index a2997bb9146..c098c916915 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -17,6 +17,6 @@ · %li.label-link-item.inline = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } - - if label.priorities.present? + - if label.priority? %li.label-link-item.inline.prepend-left-10 .label-badge.label-badge-blue Prioritized label \ No newline at end of file -- cgit v1.2.1 From 8ce9a09da0d186ad9ee5eadb87ad84f95d388bb6 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 03:02:38 +0100 Subject: More stylezz --- app/assets/stylesheets/framework/common.scss | 2 + app/assets/stylesheets/pages/labels.scss | 72 +++++++++++++--------------- app/views/shared/_label_row.html.haml | 10 ++-- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index d0dda50a835..0db1b78ac9f 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -463,11 +463,13 @@ img.emoji { .prepend-left-5 { margin-left: 5px; } .prepend-left-8 { margin-left: 8px; } .prepend-left-10 { margin-left: 10px; } +.prepend-left-15 { margin-left: 15px; } .prepend-left-default { margin-left: $gl-padding; } .prepend-left-20 { margin-left: 20px; } .append-right-5 { margin-right: 5px; } .append-right-8 { margin-right: 8px; } .append-right-10 { margin-right: 10px; } +.append-right-15 { margin-right: 15px; } .append-right-default { margin-right: $gl-padding; } .append-right-20 { margin-right: 20px; } .append-bottom-0 { margin-bottom: 0; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 85740b91ba6..c4fb0c2c8e9 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -57,46 +57,6 @@ border-bottom-left-radius: $border-radius-base; } -.label-row { - display: flex; - - .label-name { - width: 200px; - flex-shrink: 0; - - .label { - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - } - } - - .label-type { - display: block; - margin-bottom: 10px; - margin-left: 50px; - } - - .label-description { - display: block; - - .description-text { - margin: 0 0 10px 0; - } - - a { - color: $blue-600; - } - } - - .label { - padding: 4px $grid-size; - font-size: $label-font-size; - position: relative; - top: ($grid-size / 2); - } -} - .color-label { padding: 0 $grid-size; line-height: 16px; @@ -323,4 +283,36 @@ content: none; display: block; } + + .label-name { + width: 150px; + flex-shrink: 0; + + .label { + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + } + } + + .label-type { + display: block; + margin-bottom: 10px; + margin-left: 50px; + } + + .label-description { + flex-grow: 1; + + a { + color: $blue-600; + } + } + + .label { + padding: 4px $grid-size; + font-size: $label-font-size; + position: relative; + top: ($grid-size / 2); + } } \ No newline at end of file diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index c098c916915..89dea6c6f10 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -2,12 +2,12 @@ - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) -.label-row - .label-name - = link_to_label(label, subject: @project, tooltip: false) - .label-description +.label-name + = link_to_label(label, subject: @project, tooltip: false) +.label-description + .append-right-15.prepend-left-15 - if label.description.present? - .description-text + .description-text.append-bottom-10 = markdown_field(label, :description) %ul.label-links - if show_label_issues_link -- cgit v1.2.1 From 5099e73f9fcf6a835db8f8cff5380a4a3db1c736 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 03:09:20 +0100 Subject: Move new label to header_content --- app/views/projects/labels/index.html.haml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 5456ddd9a8f..a5f7ce8e2ad 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,7 +1,14 @@ - @no_container = true - page_title "Labels" -- hide_class = '' - can_admin_label = can?(current_user, :admin_label, @project) +- hide_class = '' + +- if can_admin_label + - content_for(:header_content) do + .nav-controls + = link_to new_project_label_path(@project), class: "btn btn-new" do + New label + - if @labels.exists? || @prioritized_labels.exists? #promote-label-modal @@ -12,11 +19,6 @@ - if can_admin_label Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. - - if can_admin_label - .nav-controls - = link_to new_project_label_path(@project), class: "btn btn-new" do - New label - .labels-container.prepend-top-5 - if can_admin_label -# Only show it in the first page -- cgit v1.2.1 From 53a4437a6450a35942f91a92d0fcb296cd6dc989 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 03:19:55 +0100 Subject: Add and remove priority label --- app/assets/javascripts/label_manager.js | 15 ++++++++++++++- app/views/projects/labels/index.html.haml | 5 ++++- app/views/shared/_label_row.html.haml | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index e230dbbd4ac..628e084a5e0 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -13,6 +13,7 @@ export default class LabelManager { this.otherLabels = otherLabels || $('.js-other-labels'); this.errorMessage = 'Unable to update label prioritization at this time'; this.emptyState = document.querySelector('#js-priority-labels-empty-state'); + this.$badgeItemTemplate = $(document.getElementById('js-badge-item-template').innerHTML); this.sortable = Sortable.create(this.prioritizedLabels.get(0), { filter: '.empty-message', forceFallback: true, @@ -63,7 +64,11 @@ export default class LabelManager { $target = this.otherLabels; $from = this.prioritizedLabels; } - $label.detach().appendTo($target); + + const $detachedLabel = $label.detach(); + this.toggleLabelPriorityBadge($detachedLabel, action); + $detachedLabel.appendTo($target); + if ($from.find('li').length) { $from.find('.empty-message').removeClass('hidden'); } @@ -88,6 +93,14 @@ export default class LabelManager { } } + toggleLabelPriorityBadge($label, action) { + if (action === 'remove') { + $('.js-priority-badge', $label).remove(); + } else { + $('.label-links', $label).append(this.$badgeItemTemplate); + } + } + onPrioritySortUpdate() { this.savePrioritySort() .catch(() => flash(this.errorMessage)); diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index a5f7ce8e2ad..632355fa338 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -9,7 +9,6 @@ = link_to new_project_label_path(@project), class: "btn btn-new" do New label - - if @labels.exists? || @prioritized_labels.exists? #promote-label-modal %div{ class: container_class } @@ -40,3 +39,7 @@ = paginate @labels, theme: 'gitlab' - else = render 'shared/empty_states/labels' + +%template#js-badge-item-template + %li.label-link-item.js-priority-badge.inline.prepend-left-10 + .label-badge.label-badge-blue Prioritized label \ No newline at end of file diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 89dea6c6f10..51ff88b9981 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -18,5 +18,5 @@ %li.label-link-item.inline = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } - if label.priority? - %li.label-link-item.inline.prepend-left-10 + %li.label-link-item.js-priority-badge.inline.prepend-left-10 .label-badge.label-badge-blue Prioritized label \ No newline at end of file -- cgit v1.2.1 From bc3fd40480d3cafc97e0aea1a1a047cce53ab3f5 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 19 Apr 2018 03:44:53 +0100 Subject: Subscription options dropdown --- app/views/shared/_label.html.haml | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 3d7cad8ecf8..cf42c0d1bd4 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -5,11 +5,11 @@ - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -%li.label-list-item{ class: label.priority? ? '' : 'no-hand', id: label_css_id, data: { id: label.id } } +%li.label-list-item{ class: ('no-hand' unless label.priority?), id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label %ul.label-actions-list %li.inline - .label-badge.label-badge-gray= label.model_name.human.titleize + .label-badge.label-badge-gray= label.model_name.human.capitalize - if can?(current_user, :admin_label, @project) %li.inline.js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } @@ -22,15 +22,13 @@ = icon('pencil') %li.inline .dropdown - %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown', data: { toggle: "dropdown" } } + %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown', data: { toggle: 'dropdown' } } = custom_icon('ellipsis_v') .dropdown-menu.dropdown-menu-align-right %ul - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) %li - %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'), - disabled: true, - type: 'button', + %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button', data: { url: promote_project_label_path(label.project, label), label_title: label.title, label_color: label.color, @@ -39,18 +37,27 @@ target: '#promote-label-modal', container: 'body', toggle: 'modal' } } - = sprite_icon('level-up') - %li - = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: 'Remove this label? Are you sure?' }, class: 'text-danger' + Promote to group label + %li + = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: 'Remove this label? Are you sure?' }, class: 'text-danger' - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } %span Unsubscribe - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } - %span Subscribe at project level - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } - %span Subscribe at group level + .dropdown{ class: ('hidden' unless status.unsubscribed?) } + %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } + %span + Subscribe + = icon('chevron-down') + .dropdown-menu.dropdown-menu-align-right + %ul + %li + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } + %span Subscribe at project level + %li + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } + %span Subscribe at group level - else %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path } } %span= label_subscription_toggle_button_text(label, @project) -- cgit v1.2.1 From 523daec15074d406dac9bd2d59e9838ac1dcc200 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 24 Apr 2018 13:19:26 +0100 Subject: Fix group dropdown --- app/views/shared/_label.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index cf42c0d1bd4..c2357a97975 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -45,7 +45,7 @@ - if can_subscribe_to_label_in_different_levels?(label) %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } %span Unsubscribe - .dropdown{ class: ('hidden' unless status.unsubscribed?) } + .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } %span Subscribe @@ -53,10 +53,10 @@ .dropdown-menu.dropdown-menu-align-right %ul %li - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } } + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } %span Subscribe at project level %li - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } %span Subscribe at group level - else %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path } } -- cgit v1.2.1 From 752e4e0042db64ae67cc0c1a2971a91920a1cecd Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 24 Apr 2018 13:19:47 +0100 Subject: Remove span --- app/assets/javascripts/project_label_subscription.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index e3824d83728..c5607a3cada 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -15,7 +15,6 @@ export default class ProjectLabelSubscription { event.preventDefault(); const $btn = $(event.currentTarget); - const $span = $btn.find('span'); const url = $btn.attr('data-url'); const oldStatus = $btn.attr('data-status'); -- cgit v1.2.1 From dc67ab29784b0895a76d3de705826a8749e8bf9b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 24 Apr 2018 13:26:39 +0100 Subject: Clone pri label --- app/assets/javascripts/label_manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index 628e084a5e0..8013df53f25 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -97,7 +97,7 @@ export default class LabelManager { if (action === 'remove') { $('.js-priority-badge', $label).remove(); } else { - $('.label-links', $label).append(this.$badgeItemTemplate); + $('.label-links', $label).append(this.$badgeItemTemplate.clone()); } } -- cgit v1.2.1 From 6b07a352a9ad23416f962846fd77bcc52ae06186 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 24 Apr 2018 13:26:50 +0100 Subject: Fix cursor for pri/other --- app/assets/stylesheets/pages/labels.scss | 18 +++++++----------- app/views/shared/_label.html.haml | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index c4fb0c2c8e9..3ea6be9f638 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -73,30 +73,26 @@ .manage-labels-list { > li:not(.empty-message):not(.is-not-draggable) { background-color: $white-light; - cursor: move; - cursor: -webkit-grab; - cursor: -moz-grab; margin-bottom: 5px; display: flex; justify-content: space-between; padding: 11px 10px 11px $gl-padding; border-radius: $border-radius-default; - &:active { - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - } - &.sortable-ghost { opacity: 0.3; } .prioritized-labels & { box-shadow: 0 1px 2px $issue-boards-card-shadow; - } + cursor: move; + cursor: -webkit-grab; + cursor: -moz-grab; - &.no-hand { - cursor: auto; + &:active { + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } } } diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index c2357a97975..d1d5d707410 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -5,7 +5,7 @@ - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -%li.label-list-item{ class: ('no-hand' unless label.priority?), id: label_css_id, data: { id: label.id } } +%li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label %ul.label-actions-list %li.inline -- cgit v1.2.1 From a72fc8e650128d56ec9af1f3ea5960c0f4321db0 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 24 Apr 2018 14:32:51 +0100 Subject: add label actions styling --- app/assets/stylesheets/pages/labels.scss | 33 ++++++++++++++++---------------- app/views/shared/_label.html.haml | 10 +++++----- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3ea6be9f638..b63b552415d 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -135,22 +135,6 @@ } } -.toggle-priority { - display: inline-block; - vertical-align: top; - - button { - border-color: transparent; - padding: 5px 8px; - vertical-align: top; - font-size: 14px; - - &:hover { - border-color: transparent; - } - } -} - .filtered-labels { font-size: 0; padding: 12px 16px; @@ -311,4 +295,21 @@ position: relative; top: ($grid-size / 2); } + + .label-action { + color: $theme-gray-800; + cursor: pointer; + + svg { + fill: $theme-gray-800; + } + + &:hover { + color: $blue-600; + + svg { + fill: $blue-600; + } + } + } } \ No newline at end of file diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index d1d5d707410..bccca0bb7d6 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -11,18 +11,18 @@ %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - if can?(current_user, :admin_label, @project) - %li.inline.js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } - %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } = icon('star-o') - %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } = icon('star') %li.inline - = link_to edit_label_path(label) do + = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do = icon('pencil') %li.inline .dropdown - %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown', data: { toggle: 'dropdown' } } + %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' } } = custom_icon('ellipsis_v') .dropdown-menu.dropdown-menu-align-right %ul -- cgit v1.2.1 From e5ae43e23ffd8d75865a3572439afe40a0dd9444 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 25 Apr 2018 11:56:54 +0100 Subject: Start changing group label page --- app/controllers/groups/labels_controller.rb | 30 ++++++++------- app/views/groups/labels/index.html.haml | 58 ++++++++++++++++++++--------- app/views/shared/_label.html.haml | 17 +++++---- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 58be330f466..596dd5b2d69 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -2,27 +2,20 @@ class Groups::LabelsController < Groups::ApplicationController include ToggleSubscriptionAction before_action :label, only: [:edit, :update, :destroy] + before_action :find_labels, only: [:index] before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy] before_action :save_previous_label_path, only: [:edit] respond_to :html def index - respond_to do |format| - format.html do - @labels = @group.labels.page(params[:page]) - end + @prioritized_labels = @available_labels.prioritized(@group) + @labels = @available_labels.unprioritized(@group).page(params[:page]) + respond_to do |format| + format.html format.json do - available_labels = LabelsFinder.new( - current_user, - group_id: @group.id, - only_group_labels: params[:only_group_labels], - include_ancestor_groups: params[:include_ancestor_groups], - include_descendant_groups: params[:include_descendant_groups] - ).execute - - render json: LabelSerializer.new.represent_appearance(available_labels) + render json: LabelSerializer.new.represent_appearance(@available_labels) end end end @@ -113,4 +106,15 @@ class Groups::LabelsController < Groups::ApplicationController def save_previous_label_path session[:previous_labels_path] = URI(request.referer || '').path end + + def find_labels + @available_labels ||= + LabelsFinder.new( + current_user, + group_id: @group.id, + only_group_labels: params[:only_group_labels], + include_ancestor_groups: params[:include_ancestor_groups], + include_descendant_groups: params[:include_descendant_groups] + ).execute + end end diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index ac7e12fcd0b..1173499f921 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,21 +1,45 @@ -- page_title 'Labels' +- @no_container = true +- page_title "Labels" +- can_admin_label = can?(current_user, :admin_label, @group) +- hide_class = '' -- issuables = ['issues', 'merge requests'] +- if can_admin_label + - content_for(:header_content) do + .nav-controls + = link_to new_group_label_path(@group), class: "btn btn-new" do + New label -.top-area.adjust - .nav-text - = _("Labels can be applied to %{features}. Group labels are available for any project within the group.") % { features: issuables.to_sentence } +- if @labels.exists? || @prioritized_labels.exists? + #promote-label-modal + %div{ class: container_class } + .top-area.adjust + .nav-text + Labels can be applied to issues, merge requests and epics. Group labels are available for any project within the group. + - if can_admin_label + Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. - .nav-controls - - if can?(current_user, :admin_label, @group) - = link_to "New label", new_group_label_path(@group), class: "btn btn-new" + .labels-container.prepend-top-5 + - if can_admin_label + -# Only show it in the first page + - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') + .prioritized-labels{ class: ('hide' if hide) } + %h5.prepend-top-0 Prioritized Labels + %ul.content-list.manage-labels-list.js-prioritized-labels + #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } + = render 'shared/empty_states/priority_labels' + - if @prioritized_labels.present? + = render partial: 'shared/label', subject: @group, collection: @prioritized_labels, as: :label -.labels - .other-labels - - if @labels.present? - %ul.content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @group, collection: @labels, as: :label - = paginate @labels, theme: 'gitlab' - - else - .nothing-here-block - = _("No labels created yet.") + - if @labels.present? + .other-labels + - if can_admin_label + %h5{ class: ('hide' if hide) } Other Labels + %ul.content-list.manage-labels-list.js-other-labels + = render partial: 'shared/label', subject: @group, collection: @labels, as: :label + = paginate @labels, theme: 'gitlab' +- else + = render 'shared/empty_states/labels' + +%template#js-badge-item-template + %li.label-link-item.js-priority-badge.inline.prepend-left-10 + .label-badge.label-badge-blue Prioritized label diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index bccca0bb7d6..ec84c565aae 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -6,17 +6,18 @@ - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) %li.label-list-item{ id: label_css_id, data: { id: label.id } } - = render "shared/label_row", label: label + = render "shared/label_row", label: label, subject: subject %ul.label-actions-list %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - - if can?(current_user, :admin_label, @project) - %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), - dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } - = icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } - = icon('star') + - if can?(current_user, :admin_label, label) + - if subject.is_a?(Project) + %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + dom_id: dom_id(label), type: label.type } } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } + = icon('star-o') + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } + = icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do = icon('pencil') -- cgit v1.2.1 From c792501440c7ecc2123d654747364447f74b7491 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 25 Apr 2018 12:49:01 +0100 Subject: Fix pri toggle conditional --- app/views/shared/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index ec84c565aae..f325c45a53f 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -11,7 +11,7 @@ %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - if can?(current_user, :admin_label, label) - - if subject.is_a?(Project) + - if @project.present? %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } -- cgit v1.2.1 From 57129140ba1160fd3d61588708d98602c421ee97 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 26 Apr 2018 01:18:22 +0200 Subject: backport changes from gitlab-org/gitlab-ee!5461 --- .../services/projects/housekeeping_service_spec.rb | 72 ++++++++++++---------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index b7b5de07380..1cf373d1d72 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::HousekeepingService do subject { described_class.new(project) } - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } before do project.reset_pushes_since_gc @@ -16,12 +16,12 @@ describe Projects::HousekeepingService do it 'enqueues a sidekiq job' do expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) expect(subject).to receive(:lease_key).and_return(:the_lease_key) - expect(subject).to receive(:task).and_return(:the_task) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :the_task, :the_lease_key, :the_uuid) + expect(subject).to receive(:task).and_return(:incremental_repack) + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original - subject.execute - - expect(project.reload.pushes_since_gc).to eq(0) + Sidekiq::Testing.fake! do + expect { subject.execute }.to change(GitGarbageCollectWorker.jobs, :size).by(1) + end end it 'yields the block if given' do @@ -30,6 +30,16 @@ describe Projects::HousekeepingService do end.to yield_with_no_args end + it 'resets counter after execution' do + expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) + allow(subject).to receive(:gc_period).and_return(1) + project.increment_pushes_since_gc + + Sidekiq::Testing.inline! do + expect { subject.execute }.to change { project.pushes_since_gc }.to(0) + end + end + context 'when no lease can be obtained' do before do expect(subject).to receive(:try_obtain_lease).and_return(false) @@ -54,6 +64,30 @@ describe Projects::HousekeepingService do end.not_to yield_with_no_args end end + + context 'task type' do + it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do + allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) + allow(subject).to receive(:lease_key).and_return(:the_lease_key) + + # At push 200 + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) + .exactly(1).times + # At push 50, 100, 150 + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) + .exactly(3).times + # At push 10, 20, ... (except those above) + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) + .exactly(16).times + + 201.times do + subject.increment! + subject.execute if subject.needed? + end + + expect(project.pushes_since_gc).to eq(1) + end + end end describe '#needed?' do @@ -69,31 +103,7 @@ describe Projects::HousekeepingService do describe '#increment!' do it 'increments the pushes_since_gc counter' do - expect do - subject.increment! - end.to change { project.pushes_since_gc }.from(0).to(1) + expect { subject.increment! }.to change { project.pushes_since_gc }.by(1) end end - - it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do - allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - allow(subject).to receive(:lease_key).and_return(:the_lease_key) - - # At push 200 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) - .exactly(1).times - # At push 50, 100, 150 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) - .exactly(3).times - # At push 10, 20, ... (except those above) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) - .exactly(16).times - - 201.times do - subject.increment! - subject.execute if subject.needed? - end - - expect(project.pushes_since_gc).to eq(1) - end end -- cgit v1.2.1 From 892717452c1fef5e31c1372f1e96d6312a2d3328 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 8 May 2018 20:53:40 +0100 Subject: UX review changes --- app/assets/stylesheets/pages/labels.scss | 9 +++++++-- app/views/groups/labels/index.html.haml | 2 +- app/views/projects/labels/index.html.haml | 2 +- app/views/shared/_label.html.haml | 10 +++++----- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index b63b552415d..591a3414355 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -234,9 +234,10 @@ .label-badge { color: $theme-gray-900; - font-weight: $gl-font-weight-bold; - padding: $gl-padding-4; + font-weight: $gl-font-weight-normal; + padding: $gl-padding-4 $gl-padding-8; border-radius: $border-radius-default; + font-size: $label-font-size; } .label-badge-blue { @@ -312,4 +313,8 @@ } } } +} + +.priority-labels-empty-state .svg-content img { + max-width: 114px; } \ No newline at end of file diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 1173499f921..1fa6389de0f 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -25,7 +25,7 @@ .prioritized-labels{ class: ('hide' if hide) } %h5.prepend-top-0 Prioritized Labels %ul.content-list.manage-labels-list.js-prioritized-labels - #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } + #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? = render partial: 'shared/label', subject: @group, collection: @prioritized_labels, as: :label diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 632355fa338..1bb90281093 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -25,7 +25,7 @@ .prioritized-labels{ class: ('hide' if hide) } %h5.prepend-top-0 Prioritized Labels %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } - #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } + #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index f325c45a53f..4d6a822ca14 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -15,16 +15,16 @@ %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } - = icon('star-o') + = sprite_icon('star-o') %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } - = icon('star') + = sprite_icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do - = icon('pencil') + = sprite_icon('pencil') %li.inline .dropdown %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' } } - = custom_icon('ellipsis_v') + = sprite_icon('ellipsis_v') .dropdown-menu.dropdown-menu-align-right %ul - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) @@ -50,7 +50,7 @@ %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } %span Subscribe - = icon('chevron-down') + = sprite_icon('chevron-down') .dropdown-menu.dropdown-menu-align-right %ul %li -- cgit v1.2.1 From 736292c9502cc57242b4411430f9d9f7faebda9a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 14 May 2018 12:30:28 +0100 Subject: Add unsub tooltip (broken) --- app/views/shared/_label.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 4d6a822ca14..78daac26b26 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -4,6 +4,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) +- unsubscribe_tooltip_title = status.subscribed?(current_user, label.group) ? 'Unsubscribe at group level' : status.subscribed?(current_user, @project) ? 'Unsubscribe at project level' : 'Unsubscribe' %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject @@ -44,7 +45,7 @@ - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) - %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } } + %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: unsubscribe_tooltip_title, test: status.subscribed?(current_user, label.group), test_two: status.subscribed? } %span Unsubscribe .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } -- cgit v1.2.1 From 6d3bc3970dbced33aac6a91b9ddeba777a6acad6 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 03:00:35 +0100 Subject: UX review changes --- app/assets/javascripts/label_manager.js | 3 ++- app/assets/stylesheets/pages/labels.scss | 2 ++ app/views/projects/labels/index.html.haml | 16 +++++++++++++--- app/views/shared/_label.html.haml | 6 +++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index 8013df53f25..43621deb4fd 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -9,7 +9,7 @@ import axios from './lib/utils/axios_utils'; export default class LabelManager { constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) { this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority'); - this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels'); + this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels-sortable'); this.otherLabels = otherLabels || $('.js-other-labels'); this.errorMessage = 'Unable to update label prioritization at this time'; this.emptyState = document.querySelector('#js-priority-labels-empty-state'); @@ -102,6 +102,7 @@ export default class LabelManager { } onPrioritySortUpdate() { + debugger; this.savePrioritySort() .catch(() => flash(this.errorMessage)); } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 591a3414355..a04f1bb9131 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -188,6 +188,8 @@ } .label-subscribe-button { + width: 105px; + .label-subscribe-button-icon { &[disabled] { opacity: 0.5; diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 1bb90281093..9d3213b0cda 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -24,17 +24,27 @@ - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } %h5.prepend-top-0 Prioritized Labels - %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } + .content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? - = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label + %table + %thead + %tr + %th + %th + %th + %th + %th + %th + %th + %tbody.js-prioritized-labels-sortable= render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label - if @labels.present? .other-labels - if can_admin_label %h5{ class: ('hide' if hide) } Other Labels - %ul.content-list.manage-labels-list.js-other-labels + .content-list.manage-labels-list.js-other-labels = render partial: 'shared/label', subject: @project, collection: @labels, as: :label = paginate @labels, theme: 'gitlab' - else diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 78daac26b26..7be4d4cf786 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -4,7 +4,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- unsubscribe_tooltip_title = status.subscribed?(current_user, label.group) ? 'Unsubscribe at group level' : status.subscribed?(current_user, @project) ? 'Unsubscribe at project level' : 'Unsubscribe' +- tooltip_title = "#{status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'} at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject @@ -45,7 +45,7 @@ - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) - %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: unsubscribe_tooltip_title, test: status.subscribed?(current_user, label.group), test_two: status.subscribed? } + %button.js-unsubscribe-button.test.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } %span Unsubscribe .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } @@ -61,7 +61,7 @@ %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } %span Subscribe at group level - else - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path } } + %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } %span= label_subscription_toggle_button_text(label, @project) = render 'shared/delete_label_modal', label: label -- cgit v1.2.1 From 2e1c546b087a47afd6b4077fdbae309eba52a3cf Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 03:03:09 +0100 Subject: i18n --- app/views/shared/_label.html.haml | 16 ++++++++-------- app/views/shared/_label_row.html.haml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 7be4d4cf786..1f41cb26588 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -15,9 +15,9 @@ - if @project.present? %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', :'data-placement' => 'top' } = sprite_icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' } + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', :'data-placement' => 'top' } = sprite_icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do @@ -39,27 +39,27 @@ target: '#promote-label-modal', container: 'body', toggle: 'modal' } } - Promote to group label + = _('Promote to group label') %li - = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: 'Remove this label? Are you sure?' }, class: 'text-danger' + = link_to _('Delete'), destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: _('Remove this label? Are you sure?') }, class: 'text-danger' - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) %button.js-unsubscribe-button.test.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } - %span Unsubscribe + %span= _('Unsubscribe') .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } %span - Subscribe + = _('Subscribe') = sprite_icon('chevron-down') .dropdown-menu.dropdown-menu-align-right %ul %li %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } - %span Subscribe at project level + %span= _('Subscribe at project level') %li %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } - %span Subscribe at group level + %span= _('Subscribe at group level') - else %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } %span= label_subscription_toggle_button_text(label, @project) diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 51ff88b9981..578fe6bcd8a 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -16,7 +16,7 @@ - if show_label_merge_requests_link · %li.label-link-item.inline - = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' } + = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') } - if label.priority? %li.label-link-item.js-priority-badge.inline.prepend-left-10 - .label-badge.label-badge-blue Prioritized label \ No newline at end of file + .label-badge.label-badge-blue= _('Prioritized label') \ No newline at end of file -- cgit v1.2.1 From bc2144d1a74eed8f7f76813d4ad8ca16b8f95922 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 03:19:31 +0100 Subject: Scroll items on overflow --- app/assets/stylesheets/pages/labels.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index a04f1bb9131..9357621e435 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -261,6 +261,8 @@ } .label-list-item { + overflow-x: scroll; + .content-list &:before, .content-list &:after { content: none; -- cgit v1.2.1 From 097f6f5ef86bb8cb8fbc8459ebce782647c7d8f1 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 03:20:21 +0100 Subject: revert scrollable --- app/assets/javascripts/label_manager.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index 43621deb4fd..8013df53f25 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -9,7 +9,7 @@ import axios from './lib/utils/axios_utils'; export default class LabelManager { constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) { this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority'); - this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels-sortable'); + this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels'); this.otherLabels = otherLabels || $('.js-other-labels'); this.errorMessage = 'Unable to update label prioritization at this time'; this.emptyState = document.querySelector('#js-priority-labels-empty-state'); @@ -102,7 +102,6 @@ export default class LabelManager { } onPrioritySortUpdate() { - debugger; this.savePrioritySort() .catch(() => flash(this.errorMessage)); } -- cgit v1.2.1 From 256467f31080ac317228daba2cd74e810d918485 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 03:28:48 +0100 Subject: Revert table --- app/views/projects/labels/index.html.haml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 9d3213b0cda..725fd8107e8 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -28,17 +28,7 @@ #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? - %table - %thead - %tr - %th - %th - %th - %th - %th - %th - %th - %tbody.js-prioritized-labels-sortable= render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label + = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label - if @labels.present? .other-labels -- cgit v1.2.1 From bd47118e1b5a814b7660b8b2d808690ca971871e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 15:57:46 +0100 Subject: Make sub button text light --- app/assets/stylesheets/pages/labels.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 9357621e435..8e6b9ca6725 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -189,6 +189,7 @@ .label-subscribe-button { width: 105px; + font-weight: 200; .label-subscribe-button-icon { &[disabled] { -- cgit v1.2.1 From 2fd334b86a33cd96c7708980e1f9cd7f8717877a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 16:14:17 +0100 Subject: Fix group labels list --- app/assets/stylesheets/pages/labels.scss | 2 -- app/controllers/groups/labels_controller.rb | 6 +++--- app/views/groups/labels/index.html.haml | 27 ++++++++------------------- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 8e6b9ca6725..6348c5a878d 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -262,8 +262,6 @@ } .label-list-item { - overflow-x: scroll; - .content-list &:before, .content-list &:after { content: none; diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 596dd5b2d69..0570edb51c2 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -9,11 +9,11 @@ class Groups::LabelsController < Groups::ApplicationController respond_to :html def index - @prioritized_labels = @available_labels.prioritized(@group) - @labels = @available_labels.unprioritized(@group).page(params[:page]) + @labels = @group.labels.page(params[:page]) respond_to do |format| - format.html + format.html do + end format.json do render json: LabelSerializer.new.represent_appearance(@available_labels) end diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 1fa6389de0f..44b64a31972 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -2,6 +2,7 @@ - page_title "Labels" - can_admin_label = can?(current_user, :admin_label, @group) - hide_class = '' +- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') - if can_admin_label - content_for(:header_content) do @@ -9,7 +10,7 @@ = link_to new_group_label_path(@group), class: "btn btn-new" do New label -- if @labels.exists? || @prioritized_labels.exists? +- if @labels.exists? #promote-label-modal %div{ class: container_class } .top-area.adjust @@ -19,24 +20,12 @@ Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. .labels-container.prepend-top-5 - - if can_admin_label - -# Only show it in the first page - - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') - .prioritized-labels{ class: ('hide' if hide) } - %h5.prepend-top-0 Prioritized Labels - %ul.content-list.manage-labels-list.js-prioritized-labels - #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } - = render 'shared/empty_states/priority_labels' - - if @prioritized_labels.present? - = render partial: 'shared/label', subject: @group, collection: @prioritized_labels, as: :label - - - if @labels.present? - .other-labels - - if can_admin_label - %h5{ class: ('hide' if hide) } Other Labels - %ul.content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @group, collection: @labels, as: :label - = paginate @labels, theme: 'gitlab' + .other-labels + - if can_admin_label + %h5{ class: ('hide' if hide) } Labels + %ul.content-list.manage-labels-list.js-other-labels + = render partial: 'shared/label', subject: @group, collection: @labels, as: :label + = paginate @labels, theme: 'gitlab' - else = render 'shared/empty_states/labels' -- cgit v1.2.1 From e1ef4ac620d691af9f1d74da7e821acaa7577102 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 17 May 2018 16:18:56 +0100 Subject: Remove group label badge on group labels page --- app/views/shared/_label.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 1f41cb26588..6272189e289 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -9,8 +9,9 @@ %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject %ul.label-actions-list - %li.inline - .label-badge.label-badge-gray= label.model_name.human.capitalize + - if controller.is_a?(Projects::LabelsController) + %li.inline + .label-badge.label-badge-gray= label.model_name.human.capitalize - if can?(current_user, :admin_label, label) - if @project.present? %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), -- cgit v1.2.1 From aba153a46d99977b6d05b0d6fb93a796cf551ae3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 24 May 2018 02:49:14 +0100 Subject: update gitlab-svgs --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 45bea12fd9b..af1da9cffa8 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { - "@gitlab-org/gitlab-svgs": "^1.18.0", + "@gitlab-org/gitlab-svgs": "1.23.0", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.0", diff --git a/yarn.lock b/yarn.lock index 55a86a9a577..7cf8f11d2c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,9 +54,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.18.0": - version "1.18.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.18.0.tgz#7829f0e6de0647dace54c1fcd597ee3424afb233" +"@gitlab-org/gitlab-svgs@1.23.0": + version "1.23.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.23.0.tgz#42047aeedcc06bc12d417ed1efadad1749af9670" "@types/jquery@^2.0.40": version "2.0.48" -- cgit v1.2.1 From d400c94e601df96b0a58a7e1098ee04b3e7459f5 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 24 May 2018 13:29:00 +0100 Subject: Fix accuracy of 'prioritized label' badge, when given different contexts --- app/views/groups/labels/index.html.haml | 2 +- app/views/projects/labels/index.html.haml | 2 +- app/views/shared/_label.html.haml | 4 +++- app/views/shared/_label_row.html.haml | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 44b64a31972..a72ea0681c8 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -24,7 +24,7 @@ - if can_admin_label %h5{ class: ('hide' if hide) } Labels %ul.content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @group, collection: @labels, as: :label + = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: true } = paginate @labels, theme: 'gitlab' - else = render 'shared/empty_states/labels' diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 725fd8107e8..4d423827832 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -28,7 +28,7 @@ #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? - = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label + = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true } - if @labels.present? .other-labels diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 6272189e289..6dfa1ffe9b5 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -1,13 +1,15 @@ - label_css_id = dom_id(label) - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] +- use_label_priority = local_assigns.fetch(:use_label_priority, false) +- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority? : false) - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) - tooltip_title = "#{status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'} at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" %li.label-list-item{ id: label_css_id, data: { id: label.id } } - = render "shared/label_row", label: label, subject: subject + = render "shared/label_row", label: label, subject: subject, force_priority: force_priority %ul.label-actions-list - if controller.is_a?(Projects::LabelsController) %li.inline diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 578fe6bcd8a..42d9a97d3a1 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -1,4 +1,5 @@ - subject = local_assigns[:subject] +- force_priority = local_assigns.fetch(:force_priority, false) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) @@ -17,6 +18,6 @@ · %li.label-link-item.inline = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') } - - if label.priority? + - if force_priority %li.label-link-item.js-priority-badge.inline.prepend-left-10 .label-badge.label-badge-blue= _('Prioritized label') \ No newline at end of file -- cgit v1.2.1 From c896ae7be889dd3935795b1079ab7a11a12f52e0 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 25 May 2018 12:38:45 +0100 Subject: BE review --- app/controllers/groups/labels_controller.rb | 7 +++---- app/views/shared/_label.html.haml | 2 +- spec/features/projects/labels/update_prioritization_spec.rb | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 0570edb51c2..863f50e8e66 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -2,17 +2,16 @@ class Groups::LabelsController < Groups::ApplicationController include ToggleSubscriptionAction before_action :label, only: [:edit, :update, :destroy] - before_action :find_labels, only: [:index] + before_action :available_labels, only: [:index] before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy] before_action :save_previous_label_path, only: [:edit] respond_to :html def index - @labels = @group.labels.page(params[:page]) - respond_to do |format| format.html do + @labels = @group.labels.page(params[:page]) end format.json do render json: LabelSerializer.new.represent_appearance(@available_labels) @@ -107,7 +106,7 @@ class Groups::LabelsController < Groups::ApplicationController session[:previous_labels_path] = URI(request.referer || '').path end - def find_labels + def available_labels @available_labels ||= LabelsFinder.new( current_user, diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 6dfa1ffe9b5..15e56b2cad9 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -2,7 +2,7 @@ - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] - use_label_priority = local_assigns.fetch(:use_label_priority, false) -- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority? : false) +- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false) - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index ae8b1364ec7..9500e3f3311 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -23,8 +23,10 @@ feature 'Prioritize labels' do expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do + screenshot_and_open_image all('.js-toggle-priority')[1].click wait_for_requests + screenshot_and_open_image expect(page).not_to have_content('feature') end -- cgit v1.2.1 From 22c1e96f4ee4cf7388fe8dc5aa0c3084ceb08d19 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 25 May 2018 12:38:56 +0100 Subject: last newline labels.scss --- app/assets/stylesheets/pages/labels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 6348c5a878d..97985dc69c2 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -320,4 +320,4 @@ .priority-labels-empty-state .svg-content img { max-width: 114px; -} \ No newline at end of file +} -- cgit v1.2.1 From 8c2640a553388f12be39eaf499a63662e19f86b8 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 11:39:34 +0100 Subject: missing i18n --- app/views/groups/labels/index.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index a72ea0681c8..5e37fcf0fee 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -8,16 +8,16 @@ - content_for(:header_content) do .nav-controls = link_to new_group_label_path(@group), class: "btn btn-new" do - New label + = _('New label') - if @labels.exists? #promote-label-modal %div{ class: container_class } .top-area.adjust .nav-text - Labels can be applied to issues, merge requests and epics. Group labels are available for any project within the group. + = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence } - if can_admin_label - Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. + = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.') .labels-container.prepend-top-5 .other-labels -- cgit v1.2.1 From e56c0a4763c5b1718bbaace8cd8189f1cbf27a2f Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 11:39:42 +0100 Subject: newline --- app/views/projects/labels/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 4d423827832..134b212298e 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -42,4 +42,4 @@ %template#js-badge-item-template %li.label-link-item.js-priority-badge.inline.prepend-left-10 - .label-badge.label-badge-blue Prioritized label \ No newline at end of file + .label-badge.label-badge-blue Prioritized label -- cgit v1.2.1 From 778e9d90da9ccc7a4b79860a98e84356dd3adaf2 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 12:05:40 +0100 Subject: Fix tooltips --- app/assets/javascripts/group_label_subscription.js | 11 +++++++++++ app/assets/javascripts/project_label_subscription.js | 15 +++++++++++---- app/views/shared/_label.html.haml | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index 5648cb9a888..a0716cdfdbe 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -35,6 +35,7 @@ export default class GroupLabelSubscription { this.$unsubscribeButtons.attr('data-url', url); axios.post(url) + .then(() => GroupLabelSubscription.setNewTooltip($btn)) .then(() => this.toggleSubscriptionButtons()) .catch(() => flash(__('There was an error when subscribing to this label.'))); } @@ -44,4 +45,14 @@ export default class GroupLabelSubscription { this.$subscribeButtons.toggleClass('hidden'); this.$unsubscribeButtons.toggleClass('hidden'); } + + static setNewTooltip($button) { + if (!$button.hasClass('js-subscribe-button')) return; + + const type = $button.hasClass('js-group-level') ? 'group' : 'project'; + const title = `Unsubscribe at ${type} level`; + const $unsubscribeButton = $('.js-unsubscribe-button', $button.closest('.label-actions-list')); + + $unsubscribeButton.tooltip('hide').attr('title', title).tooltip('fixTitle'); + } } diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index c5607a3cada..72f03b02131 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -35,15 +35,22 @@ export default class ProjectLabelSubscription { this.$buttons.attr('data-status', newStatus); this.$buttons.find('> span').text(newAction); - this.$buttons.map((button) => { + this.$buttons.map((i, button) => { const $button = $(button); + const originalTitle = $button.attr('data-original-title'); - if ($button.attr('data-original-title')) { - $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle'); - } + if (originalTitle) ProjectLabelSubscription.setNewTitle($button, originalTitle, newStatus, newAction); return button; }); }).catch(() => flash(__('There was an error subscribing to this label.'))); } + + static setNewTitle($button, originalTitle, newStatus, newAction) { + const newStatusVerb = newStatus.slice(0, -1); + const actionRegexp = new RegExp(newStatusVerb, 'i'); + const newTitle = originalTitle.replace(actionRegexp, newAction); + + $button.tooltip('hide').attr('data-original-title', newTitle).tooltip('fixTitle'); + } } diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 15e56b2cad9..71215526182 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -6,7 +6,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- tooltip_title = "#{status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'} at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" +- tooltip_title = "#{status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'} at #{status.sub('-', ' ')}" %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject, force_priority: force_priority @@ -61,7 +61,7 @@ %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } %span= _('Subscribe at project level') %li - %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } + %button.js-subscribe-button.js-group-level.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } %span= _('Subscribe at group level') - else %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } -- cgit v1.2.1 From 0f925d626af5206820462d489edb53d15c25cdea Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 12:05:52 +0100 Subject: Remove unneeded class --- app/views/shared/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 71215526182..cf11cf795c3 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -48,7 +48,7 @@ - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) - %button.js-unsubscribe-button.test.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } + %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } %span= _('Unsubscribe') .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } } -- cgit v1.2.1 From 2ed974d22b386def4b7a3b0ab7881ffde8b8528a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 12:13:48 +0100 Subject: Fix features --- app/views/groups/labels/index.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 5e37fcf0fee..215e782d3da 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -3,6 +3,7 @@ - can_admin_label = can?(current_user, :admin_label, @group) - hide_class = '' - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') +- issuables = ['issues', 'merge requests'] - if can_admin_label - content_for(:header_content) do -- cgit v1.2.1 From 3984067c5a1f6da34fd03f760f7329405e346bc9 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 12:14:01 +0100 Subject: Temp revert to label.priority? need to fix group page --- app/views/shared/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index cf11cf795c3..8e3101e6248 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -2,7 +2,7 @@ - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] - use_label_priority = local_assigns.fetch(:use_label_priority, false) -- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false) +- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority? : false) - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- cgit v1.2.1 From a3e472e0a7a6b6ec5654edd20b947ba660ed2dc3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Mon, 28 May 2018 12:22:30 +0100 Subject: Fix tooltip_title --- app/views/shared/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 8e3101e6248..fae264ef3cc 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -6,7 +6,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- tooltip_title = "#{status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'} at #{status.sub('-', ' ')}" +- tooltip_title = status.unsubscribed? ? "Subscribe at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" : "Unsubscribe at #{status.sub('-', ' ')}" %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject, force_priority: force_priority -- cgit v1.2.1 From fad343ca5b1fcdd4d01e415f31ee4a9fddb4eb99 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 09:53:33 +0100 Subject: Remove screenshots --- spec/features/projects/labels/update_prioritization_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index 9500e3f3311..ae8b1364ec7 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -23,10 +23,8 @@ feature 'Prioritize labels' do expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do - screenshot_and_open_image all('.js-toggle-priority')[1].click wait_for_requests - screenshot_and_open_image expect(page).not_to have_content('feature') end -- cgit v1.2.1 From c9e4c1760dae5e1916bb5ad22f260b7fd0b58475 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:25:21 +0100 Subject: Fix project subscription_spec --- spec/features/projects/labels/subscription_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index 70e8d436dcb..d5f5036ec72 100644 --- a/spec/features/projects/labels/subscription_spec.rb +++ b/spec/features/projects/labels/subscription_spec.rb @@ -36,7 +36,7 @@ feature 'Labels subscription' do within "#group_label_#{feature.id}" do expect(page).not_to have_button 'Unsubscribe' - click_link_on_dropdown('Group level') + click_link_on_dropdown('Subscribe at group level') expect(page).not_to have_selector('.dropdown-group-label') expect(page).to have_button 'Unsubscribe' @@ -45,7 +45,7 @@ feature 'Labels subscription' do expect(page).to have_selector('.dropdown-group-label') - click_link_on_dropdown('Project level') + click_link_on_dropdown('Subscribe at project level') expect(page).not_to have_selector('.dropdown-group-label') expect(page).to have_button 'Unsubscribe' @@ -67,6 +67,8 @@ feature 'Labels subscription' do def click_link_on_dropdown(text) find('.dropdown-group-label').click + screenshot_and_open_image + page.within('.dropdown-group-label') do find('a.js-subscribe-button', text: text).click end -- cgit v1.2.1 From 5e5828a41a95e06e7aa66368efbd296846aa1e8b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:25:45 +0100 Subject: Tidy tooltip_title and fix for signed out user --- app/helpers/labels_helper.rb | 10 ++++++++++ app/views/shared/_label.html.haml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index e1b0e7a4a3e..7fddf74c654 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -211,6 +211,16 @@ module LabelsHelper end end + def label_status_tooltip(status) + return '' unless status + + if status.unsubscribed? + "Subscribe at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" + else + "Unsubscribe at #{status.sub('-', ' ')}" + end + end + # Required for Banzai::Filter::LabelReferenceFilter module_function :render_colored_label, :text_color_for_bg, :escape_once end diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index fae264ef3cc..d8ab63e053c 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -6,7 +6,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- tooltip_title = status.unsubscribed? ? "Subscribe at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" : "Unsubscribe at #{status.sub('-', ' ')}" +- tooltip_title = label_status_tooltip(status) %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject, force_priority: force_priority -- cgit v1.2.1 From 4a1d19a61d1c3d8908487dfc4a30b6962206a21b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:35:26 +0100 Subject: Tidy tooltip_title and fix dropdown open left --- app/helpers/labels_helper.rb | 12 +++++------- app/views/shared/_label.html.haml | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 7fddf74c654..c7df25cecef 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -211,14 +211,12 @@ module LabelsHelper end end - def label_status_tooltip(status) - return '' unless status + def label_status_tooltip(label, status) + type = label.is_a?(ProjectLabel) ? 'project' : 'group' + level = status.unsubscribed? ? type : status.sub('-level', '') + action = status.unsubscribed? ? 'Subscribe' : 'Unsubscribe' - if status.unsubscribed? - "Subscribe at #{label.is_a?(ProjectLabel) ? 'project' : 'group'} level" - else - "Unsubscribe at #{status.sub('-', ' ')}" - end + "#{action} at #{level} level" end # Required for Banzai::Filter::LabelReferenceFilter diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index d8ab63e053c..296988ddddf 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -6,7 +6,7 @@ - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- tooltip_title = label_status_tooltip(status) +- tooltip_title = label_status_tooltip(label, status) if status %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject, force_priority: force_priority @@ -29,7 +29,7 @@ .dropdown %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' } } = sprite_icon('ellipsis_v') - .dropdown-menu.dropdown-menu-align-right + .dropdown-menu.dropdown-open-left %ul - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) %li @@ -55,7 +55,7 @@ %span = _('Subscribe') = sprite_icon('chevron-down') - .dropdown-menu.dropdown-menu-align-right + .dropdown-menu.dropdown-open-left %ul %li %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } -- cgit v1.2.1 From 97a2f2968bdea16af21c6649319349dfde0b5d46 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:47:58 +0100 Subject: Correct fixTitle --- app/assets/javascripts/group_label_subscription.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index a0716cdfdbe..4683cc43017 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -53,6 +53,6 @@ export default class GroupLabelSubscription { const title = `Unsubscribe at ${type} level`; const $unsubscribeButton = $('.js-unsubscribe-button', $button.closest('.label-actions-list')); - $unsubscribeButton.tooltip('hide').attr('title', title).tooltip('fixTitle'); + $unsubscribeButton.tooltip('hide').attr('title', title).tooltip('_fixTitle'); } } -- cgit v1.2.1 From 35b5c830a783eb36f86d9f5e8f8c08595fd62c54 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:48:26 +0100 Subject: Further fixes for project subscription_spec --- spec/features/projects/labels/subscription_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index d5f5036ec72..fafd338e448 100644 --- a/spec/features/projects/labels/subscription_spec.rb +++ b/spec/features/projects/labels/subscription_spec.rb @@ -67,10 +67,8 @@ feature 'Labels subscription' do def click_link_on_dropdown(text) find('.dropdown-group-label').click - screenshot_and_open_image - page.within('.dropdown-group-label') do - find('a.js-subscribe-button', text: text).click + find('.js-subscribe-button', text: text).click end end end -- cgit v1.2.1 From 3b3645136bd524a5e9f79558ab16be976cfe5282 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 11:07:48 +0100 Subject: Fix update_prioritization_spec --- app/views/shared/_label.html.haml | 15 +++++++-------- .../projects/labels/update_prioritization_spec.rb | 8 ++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 296988ddddf..c9ff5defdc7 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -14,14 +14,13 @@ - if controller.is_a?(Projects::LabelsController) %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - - if can?(current_user, :admin_label, label) - - if @project.present? - %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), - dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', :'data-placement' => 'top' } - = sprite_icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', :'data-placement' => 'top' } - = sprite_icon('star') + - if can?(current_user, :admin_label, @project) + %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + dom_id: dom_id(label), type: label.type } } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', :'data-placement' => 'top' } + = sprite_icon('star-o') + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', :'data-placement' => 'top' } + = sprite_icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do = sprite_icon('pencil') diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index ae8b1364ec7..359381c391c 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -102,16 +102,16 @@ feature 'Prioritize labels' do drag_to(selector: '.label-list-item', from_index: 1, to_index: 2) page.within('.prioritized-labels') do - expect(first('li')).to have_content('feature') - expect(page.all('li').last).to have_content('bug') + expect(first('.label-list-item')).to have_content('feature') + expect(page.all('.label-list-item').last).to have_content('bug') end refresh wait_for_requests page.within('.prioritized-labels') do - expect(first('li')).to have_content('feature') - expect(page.all('li').last).to have_content('bug') + expect(first('.label-list-item')).to have_content('feature') + expect(page.all('.label-list-item').last).to have_content('bug') end end -- cgit v1.2.1 From cd0be3ba456f5cf61ed72f1c6b36bef799d7eaaf Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 12:11:14 +0100 Subject: Fix user_removes_labels_spec --- app/views/shared/_label.html.haml | 3 ++- .../projects/labels/user_removes_labels_spec.rb | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index c9ff5defdc7..391021a7665 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -43,7 +43,8 @@ toggle: 'modal' } } = _('Promote to group label') %li - = link_to _('Delete'), destroy_label_path(label), title: 'Delete', method: :delete, data: { confirm: _('Remove this label? Are you sure?') }, class: 'text-danger' + %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } } + = button_tag 'Delete', type: 'button', class: 'text-danger remove-row' - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index f4fda6de465..120c618b530 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -17,8 +17,9 @@ describe "User removes labels" do end it "removes label" do - page.within(".labels") do + page.within(".other-labels") do page.first(".label-list-item") do + first('.js-label-options-dropdown').click first(".remove-row").click first(:link, "Delete label").click end @@ -36,17 +37,15 @@ describe "User removes labels" do end it "removes all labels" do - page.within(".labels") do - loop do - li = page.first(".label-list-item") - break unless li - - li.click_link("Delete") - click_link("Delete label") - end - - expect(page).to have_content("Generate a default set of labels").and have_content("New label") + loop do + li = page.first(".label-list-item") + break unless li + li.find('.js-label-options-dropdown').click + li.click_button("Delete") + click_link("Delete label") end + + expect(page).to have_content("Generate a default set of labels").and have_content("New label") end end end -- cgit v1.2.1 From dcacfaa197d913633935dddaa9bfba5cf295c570 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 12:11:20 +0100 Subject: Fix lint --- app/assets/stylesheets/pages/labels.scss | 4 ++-- app/views/shared/_label_row.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index c25e6ce23bb..bbde68cd08f 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -267,8 +267,8 @@ } .label-list-item { - .content-list &:before, - .content-list &:after { + .content-list &::before, + .content-list &::after { content: none; display: block; } diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 42d9a97d3a1..ed23db156bc 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -20,4 +20,4 @@ = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') } - if force_priority %li.label-link-item.js-priority-badge.inline.prepend-left-10 - .label-badge.label-badge-blue= _('Prioritized label') \ No newline at end of file + .label-badge.label-badge-blue= _('Prioritized label') -- cgit v1.2.1 From 9b9cdc984cb785ceee4ed1902625c65492640621 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 12:50:29 +0100 Subject: fix lint --- spec/features/projects/labels/user_removes_labels_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index 120c618b530..efa74015c6e 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -40,6 +40,7 @@ describe "User removes labels" do loop do li = page.first(".label-list-item") break unless li + li.find('.js-label-options-dropdown').click li.click_button("Delete") click_link("Delete label") -- cgit v1.2.1 From 64679a0d9a654d4df88af45b7fdd4e322091f51e Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 29 May 2018 15:25:43 +0300 Subject: Backport of https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5876 --- config/initializers/rack_attack_global.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/initializers/rack_attack_global.rb b/config/initializers/rack_attack_global.rb index a90516eee7d..45963831c41 100644 --- a/config/initializers/rack_attack_global.rb +++ b/config/initializers/rack_attack_global.rb @@ -26,7 +26,7 @@ class Rack::Attack throttle('throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req| Gitlab::Throttle.settings.throttle_unauthenticated_enabled && req.unauthenticated? && - !req.api_internal_request? && + !req.should_be_skipped? && req.ip end @@ -59,6 +59,10 @@ class Rack::Attack path =~ %r{^/api/v\d+/internal/} end + def should_be_skipped? + api_internal_request? + end + def web_request? !api_request? end -- cgit v1.2.1 From 458b5a10ddb4432f1abd5cb0e1b92fc591f4e966 Mon Sep 17 00:00:00 2001 From: Chantal Rollison Date: Thu, 31 May 2018 07:37:00 -0700 Subject: Backport of 5942-extract-ee-specific-files --- app/controllers/concerns/issuable_actions.rb | 30 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index c925b4aada5..d04eb192129 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -7,6 +7,19 @@ module IssuableActions before_action :authorize_admin_issuable!, only: :bulk_update end + def permitted_keys + [ + :issuable_ids, + :assignee_id, + :milestone_id, + :state_event, + :subscription_event, + label_ids: [], + add_label_ids: [], + remove_label_ids: [] + ] + end + def show respond_to do |format| format.html @@ -140,24 +153,15 @@ module IssuableActions end def bulk_update_params - permitted_keys = [ - :issuable_ids, - :assignee_id, - :milestone_id, - :state_event, - :subscription_event, - label_ids: [], - add_label_ids: [], - remove_label_ids: [] - ] + permitted_keys_array = permitted_keys.dup if resource_name == 'issue' - permitted_keys << { assignee_ids: [] } + permitted_keys_array << { assignee_ids: [] } else - permitted_keys.unshift(:assignee_id) + permitted_keys_array.unshift(:assignee_id) end - params.require(:update).permit(permitted_keys) + params.require(:update).permit(permitted_keys_array) end def resource_name -- cgit v1.2.1 From 93ff116023628c9b007475eaecbd3d4efa5831a7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 1 Jun 2018 18:04:16 +0100 Subject: Update installation from source guide --- doc/install/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index a0ae9017f71..f6bc9b8ed4a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -301,9 +301,9 @@ sudo usermod -aG redis git ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-8-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-0-stable gitlab -**Note:** You can change `10-8-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `11-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It -- cgit v1.2.1 From 0e54777ee3e605201fad968a31de8c1c9c40e4a5 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 1 Jun 2018 18:09:36 +0100 Subject: Create update guide --- doc/update/10.8-to-11.0.md | 362 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 doc/update/10.8-to-11.0.md diff --git a/doc/update/10.8-to-11.0.md b/doc/update/10.8-to-11.0.md new file mode 100644 index 00000000000..64b268f7ca2 --- /dev/null +++ b/doc/update/10.8-to-11.0.md @@ -0,0 +1,362 @@ +--- +comments: false +--- + +# From 10.8 to 11.0 + +Make sure you view this update guide from the tag (version) of GitLab you would +like to install. In most cases this should be the highest numbered production +tag (without rc in it). You can select the tag in the version dropdown at the +top left corner of GitLab (below the menu bar). + +If the highest number stable branch is unclear please check the +[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation +guide links by version. + +### 1. Stop server + +```bash +sudo service gitlab stop +``` + +### 2. Backup + +NOTE: If you installed GitLab from source, make sure `rsync` is installed. + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Update Ruby + +NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be +sure to upgrade your interpreter if necessary. + +You can check which version you are running with `ruby -v`. + +Download Ruby and compile it: + + ```bash + mkdir /tmp/ruby && cd /tmp/ruby + curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz + echo '540996fec64984ab6099e34d2f5820b14904f15a ruby-2.3.7.tar.gz' | shasum -c - && tar xzf ruby-2.3.7.tar.gz + cd ruby-2.3.7 + + ./configure --disable-install-rdoc + make + sudo make install + ``` + +Install Bundler: + +```bash +sudo gem install bundler --no-ri --no-rdoc +``` + +### 4. Update Node + +GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets. +This requires a minimum version of node v6.0.0. + +You can check which version you are running with `node -v`. If you are running +a version older than `v6.0.0` you will need to update to a newer version. You +can find instructions to install from community maintained packages or compile +from source at the nodejs.org website. + + + +GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript +dependencies. + +```bash +curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list +sudo apt-get update +sudo apt-get install yarn +``` + +More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install). + +### 5. Update Go + +NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go +1.5.x through 1.7.x. Be sure to upgrade your installation if necessary. + +You can check which version you are running with `go version`. + +Download and install Go: + +```bash +# Remove former Go installation folder +sudo rm -rf /usr/local/go + +curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz +echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ + sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ +rm go1.8.3.linux-amd64.tar.gz +``` + +### 6. Get latest code + +```bash +cd /home/git/gitlab + +sudo -u git -H git fetch --all --prune +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +sudo -u git -H git checkout -- locale +``` + +For GitLab Community Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 11-0-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 11-0-stable-ee +``` + +### 7. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell + +sudo -u git -H git fetch --all --tags --prune +sudo -u git -H git checkout v$( Date: Fri, 1 Jun 2018 23:39:24 +0200 Subject: Add deploy_strategy to ProjectAutoDevops --- app/models/project_auto_devops.rb | 5 +++++ ...01213245_add_deploy_strategy_to_project_auto_devops.rb | 15 +++++++++++++++ db/schema.rb | 3 ++- spec/models/project_auto_devops_spec.rb | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index ed6c1eddbc1..8464b1d9157 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -1,6 +1,11 @@ class ProjectAutoDevops < ActiveRecord::Base belongs_to :project + enum deploy_strategy: { + manual: 1, + continuous: 2 + } + scope :enabled, -> { where(enabled: true) } scope :disabled, -> { where(enabled: false) } diff --git a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb new file mode 100644 index 00000000000..d5ea25f02d4 --- /dev/null +++ b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb @@ -0,0 +1,15 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def change + change_table :project_auto_devops do |t| + t.integer :deploy_strategy, null: false, default: 0 + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 97247387bc7..fc0a6347c6e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180529093006) do +ActiveRecord::Schema.define(version: 20180601213245) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1490,6 +1490,7 @@ ActiveRecord::Schema.define(version: 20180529093006) do t.datetime_with_timezone "updated_at", null: false t.boolean "enabled" t.string "domain" + t.integer "deploy_strategy", default: 0, null: false end add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7545c0797e9..1bfc6befb4b 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -5,6 +5,8 @@ describe ProjectAutoDevops do it { is_expected.to belong_to(:project) } + it { is_expected.to define_enum_for(:deploy_strategy) } + it { is_expected.to respond_to(:created_at) } it { is_expected.to respond_to(:updated_at) } -- cgit v1.2.1 From 39412d0a163625bd91632267a1dd36c1ebb23906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 1 Jun 2018 23:47:38 +0200 Subject: Add deploy strategy related predefined variables --- app/models/project_auto_devops.rb | 5 +++++ spec/factories/project_auto_devops.rb | 1 + spec/models/project_auto_devops_spec.rb | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index 8464b1d9157..d5e00f84cf7 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -25,6 +25,11 @@ class ProjectAutoDevops < ActiveRecord::Base variables.append(key: 'AUTO_DEVOPS_DOMAIN', value: domain.presence || instance_domain) end + + if continuous? + variables.append(key: 'STAGING_ENABLED', value: 1) + variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 1) + end end end end diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 5ce1988c76f..a59087cd7eb 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -3,5 +3,6 @@ FactoryBot.define do project enabled true domain "example.com" + deploy_strategy :continuous end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 1bfc6befb4b..37cd1f571e5 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -69,6 +69,19 @@ describe ProjectAutoDevops do end end + context 'when deploy_strategy is continuous' do + let(:domain) { 'example.com' } + + before do + auto_devops.deploy_strategy = 'continuous' + end + + it do + expect(auto_devops.predefined_variables.map { |var| var[:key] }) + .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") + end + end + def domain_variable { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end -- cgit v1.2.1 From 5403a4a07ed86770f1ab11108ab5a86fa2b90e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sat, 2 Jun 2018 11:46:55 +0200 Subject: Add deploy_strategy to safe model attributes --- spec/lib/gitlab/import_export/safe_model_attributes.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 74e7a45fd6c..5e6311441f8 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -539,6 +539,7 @@ ProjectAutoDevops: - id - enabled - domain +- deploy_strategy - project_id - created_at - updated_at -- cgit v1.2.1 From 2121e3b15f0e65e322e8f14690159e2e07905849 Mon Sep 17 00:00:00 2001 From: Marc Shaw Date: Wed, 18 Apr 2018 23:22:53 +1200 Subject: 44790: Disable the action mailer base logging when emails are disabled --- config/initializers/disable_email_interceptor.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb index c76a6b8b19f..e8770c8d460 100644 --- a/config/initializers/disable_email_interceptor.rb +++ b/config/initializers/disable_email_interceptor.rb @@ -1,2 +1,5 @@ # Interceptor in lib/disable_email_interceptor.rb -ActionMailer::Base.register_interceptor(DisableEmailInterceptor) unless Gitlab.config.gitlab.email_enabled +unless Gitlab.config.gitlab.email_enabled + ActionMailer::Base.register_interceptor(DisableEmailInterceptor) + ActionMailer::Base.logger = nil +end -- cgit v1.2.1 From 95e41bc54bb314755486587141e977034f332b60 Mon Sep 17 00:00:00 2001 From: Marc Shaw Date: Sat, 12 May 2018 12:45:17 +1200 Subject: 44790: Add change log --- changelogs/unreleased/44790-disabled-emails-logging.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/44790-disabled-emails-logging.yml diff --git a/changelogs/unreleased/44790-disabled-emails-logging.yml b/changelogs/unreleased/44790-disabled-emails-logging.yml new file mode 100644 index 00000000000..90125dc0300 --- /dev/null +++ b/changelogs/unreleased/44790-disabled-emails-logging.yml @@ -0,0 +1,5 @@ +--- +title: Stop logging email information when emails are disabled +merge_request: 18521 +author: Marc Shaw +type: fixed -- cgit v1.2.1 From 3f55e0b29fe39c5fd31c703911c96808bd17335d Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sun, 3 Jun 2018 23:45:26 +0900 Subject: Use the default strings of timeago.js for timeago --- .../javascripts/lib/utils/datetime_utility.js | 36 +++++++++++----------- ...47182-use-the-default-strings-of-timeago-js.yml | 5 +++ .../admin/admin_uses_repository_checks_spec.rb | 2 +- .../merge_request/user_posts_notes_spec.rb | 2 +- 4 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 0ff23bbb061..c3644fb7ff6 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -79,37 +79,37 @@ export function getTimeago() { if (!timeagoInstance) { const localeRemaining = function getLocaleRemaining(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|right now')], - [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], - [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], + [s__('Timeago|just now'), s__('Timeago|right now')], + [s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')], + [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')], [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], - [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], - [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], - [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], + [s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')], + [s__('Timeago|%s hours ago'), s__('Timeago|%s hours remaining')], + [s__('Timeago|1 day ago'), s__('Timeago|1 day remaining')], [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], - [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], + [s__('Timeago|1 week ago'), s__('Timeago|1 week remaining')], [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], - [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], + [s__('Timeago|1 month ago'), s__('Timeago|1 month remaining')], [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], - [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], + [s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')], [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')], ][index]; }; const locale = function getLocale(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|right now')], - [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], - [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], + [s__('Timeago|just now'), s__('Timeago|right now')], + [s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')], + [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')], [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], - [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], - [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], - [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], + [s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')], + [s__('Timeago|%s hours ago'), s__('Timeago|in %s hours')], + [s__('Timeago|1 day ago'), s__('Timeago|in 1 day')], [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], - [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], + [s__('Timeago|1 week ago'), s__('Timeago|in 1 week')], [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], - [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], + [s__('Timeago|1 month ago'), s__('Timeago|in 1 month')], [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], - [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], + [s__('Timeago|1 year ago'), s__('Timeago|in 1 year')], [s__('Timeago|%s years ago'), s__('Timeago|in %s years')], ][index]; }; diff --git a/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml b/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml new file mode 100644 index 00000000000..010b1db5aac --- /dev/null +++ b/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml @@ -0,0 +1,5 @@ +--- +title: Use the default strings of timeago.js for timeago +merge_request: 19350 +author: Takuya Noguchi +type: other diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 90cf5a53787..7371a494d36 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -28,7 +28,7 @@ feature 'Admin uses repository checks' do visit_admin_project_page(project) page.within('.alert') do - expect(page.text).to match(/Last repository check \(.* ago\) failed/) + expect(page.text).to match(/Last repository check \(just now\) failed/) end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index b54addce993..3bd9f5e2298 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -139,7 +139,7 @@ describe 'Merge request > User posts notes', :js do page.within("#note_#{note.id}") do is_expected.to have_css('.note_edited_ago') expect(find('.note_edited_ago').text) - .to match(/less than a minute ago/) + .to match(/just now/) end end end -- cgit v1.2.1 From ea535bfbc56a18ebfbfdb2764e19a00ffc4b1d7f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 5 Jun 2018 09:28:28 +0000 Subject: Update installation.md --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index f6bc9b8ed4a..73f399ff1d6 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -303,7 +303,7 @@ sudo usermod -aG redis git # Clone GitLab repository sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-0-stable gitlab -**Note:** You can change `11-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `11-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It -- cgit v1.2.1 From 7e36fa398b6f2e0304d852b7c77ef30d83e62ed4 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 5 Jun 2018 10:47:43 +0100 Subject: FE review changes --- app/assets/javascripts/group_label_subscription.js | 15 ++++++++++----- app/assets/javascripts/label_manager.js | 4 ++-- app/assets/javascripts/project_label_subscription.js | 20 +++++++++++++++----- app/assets/stylesheets/framework/common.scss | 1 - app/assets/stylesheets/pages/labels.scss | 11 ++--------- app/views/groups/labels/index.html.haml | 5 ++--- app/views/projects/labels/index.html.haml | 13 ++++++------- app/views/shared/_label.html.haml | 12 ++++++------ app/views/shared/_label_row.html.haml | 2 +- 9 files changed, 44 insertions(+), 39 deletions(-) diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index 4683cc43017..755f94a3d1e 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -1,7 +1,12 @@ import $ from 'jquery'; +import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; import flash from './flash'; -import { __ } from './locale'; + +const tooltipTitles = { + group: __('Unsubscribe at group level'), + project: __('Unsubscribe at project level'), +}; export default class GroupLabelSubscription { constructor(container) { @@ -48,11 +53,11 @@ export default class GroupLabelSubscription { static setNewTooltip($button) { if (!$button.hasClass('js-subscribe-button')) return; - + const type = $button.hasClass('js-group-level') ? 'group' : 'project'; - const title = `Unsubscribe at ${type} level`; - const $unsubscribeButton = $('.js-unsubscribe-button', $button.closest('.label-actions-list')); + const newTitle = tooltipTitles[type]; - $unsubscribeButton.tooltip('hide').attr('title', title).tooltip('_fixTitle'); + $('.js-unsubscribe-button', $button.closest('.label-actions-list')) + .tooltip('hide').attr('title', newTitle).tooltip('_fixTitle'); } } diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index a8a314abd6b..06023e98ec3 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -13,7 +13,7 @@ export default class LabelManager { this.otherLabels = otherLabels || $('.js-other-labels'); this.errorMessage = 'Unable to update label prioritization at this time'; this.emptyState = document.querySelector('#js-priority-labels-empty-state'); - this.$badgeItemTemplate = $(document.getElementById('js-badge-item-template').innerHTML); + this.$badgeItemTemplate = $('#js-badge-item-template'); this.sortable = Sortable.create(this.prioritizedLabels.get(0), { filter: '.empty-message', forceFallback: true, @@ -97,7 +97,7 @@ export default class LabelManager { if (action === 'remove') { $('.js-priority-badge', $label).remove(); } else { - $('.label-links', $label).append(this.$badgeItemTemplate.clone()); + $('.label-links', $label).append(this.$badgeItemTemplate.clone().html()); } } diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index a5e68d1b0a0..35a76875a7a 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -3,6 +3,17 @@ import { __ } from './locale'; import axios from './lib/utils/axios_utils'; import flash from './flash'; +const tooltipTitles = { + group: { + subscribed: __('Unsubscribe at group level'), + unsubscribed: __('Subscribe at group level'), + }, + project: { + subscribed: __('Unsubscribe at project level'), + unsubscribed: __('Subscribe at project level'), + }, +}; + export default class ProjectLabelSubscription { constructor(container) { this.$container = $(container); @@ -48,11 +59,10 @@ export default class ProjectLabelSubscription { }).catch(() => flash(__('There was an error subscribing to this label.'))); } - static setNewTitle($button, originalTitle, newStatus, newAction) { - const newStatusVerb = newStatus.slice(0, -1); - const actionRegexp = new RegExp(newStatusVerb, 'i'); - const newTitle = originalTitle.replace(actionRegexp, newAction); + static setNewTitle($button, originalTitle, newStatus) { + const type = /group/.test(originalTitle) ? 'group' : 'project'; + const newTitle = tooltipTitles[type][newStatus]; - $button.tooltip('hide').attr('data-original-title', newTitle).tooltip('_fixTitle'); + $button.tooltip('hide').attr('title', newTitle).tooltip('_fixTitle'); } } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index a8d13249ecb..d65fc0d2d68 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -464,7 +464,6 @@ img.emoji { .prepend-left-5 { margin-left: 5px; } .prepend-left-8 { margin-left: 8px; } .prepend-left-10 { margin-left: 10px; } -.prepend-left-15 { margin-left: 15px; } .prepend-left-default { margin-left: $gl-padding; } .prepend-left-20 { margin-left: 20px; } .append-right-5 { margin-right: 5px; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index bbde68cd08f..a80061c511d 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -77,7 +77,7 @@ margin-bottom: 5px; display: flex; justify-content: space-between; - padding: 11px 10px 11px $gl-padding; + padding: 8px 8px 8px $gl-padding; border-radius: $border-radius-default; &.sortable-ghost { @@ -270,7 +270,6 @@ .content-list &::before, .content-list &::after { content: none; - display: block; } .label-name { @@ -284,12 +283,6 @@ } } - .label-type { - display: block; - margin-bottom: 10px; - margin-left: 50px; - } - .label-description { flex-grow: 1; @@ -302,7 +295,7 @@ padding: 4px $grid-size; font-size: $label-font-size; position: relative; - top: ($grid-size / 2); + top: $gl-padding-4; } .label-action { diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 215e782d3da..e6b20385792 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -8,8 +8,7 @@ - if can_admin_label - content_for(:header_content) do .nav-controls - = link_to new_group_label_path(@group), class: "btn btn-new" do - = _('New label') + = link_to _('New label'), new_group_label_path(@group), class: "btn btn-new" - if @labels.exists? #promote-label-modal @@ -32,4 +31,4 @@ %template#js-badge-item-template %li.label-link-item.js-priority-badge.inline.prepend-left-10 - .label-badge.label-badge-blue Prioritized label + .label-badge.label-badge-blue= _('Prioritized label') diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 134b212298e..ba7bce6ab55 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -6,24 +6,23 @@ - if can_admin_label - content_for(:header_content) do .nav-controls - = link_to new_project_label_path(@project), class: "btn btn-new" do - New label + = link_to _('New label'), new_project_label_path(@project), class: "btn btn-new" - if @labels.exists? || @prioritized_labels.exists? #promote-label-modal %div{ class: container_class } .top-area.adjust .nav-text - Labels can be applied to issues and merge requests. + = _('Labels can be applied to issues and merge requests.') - if can_admin_label - Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. + = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.') .labels-container.prepend-top-5 - if can_admin_label -# Only show it in the first page - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } - %h5.prepend-top-0 Prioritized Labels + %h5.prepend-top-0= _('Prioritized Labels') .content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' @@ -33,7 +32,7 @@ - if @labels.present? .other-labels - if can_admin_label - %h5{ class: ('hide' if hide) } Other Labels + %h5{ class: ('hide' if hide) }= _('Other Labels') .content-list.manage-labels-list.js-other-labels = render partial: 'shared/label', subject: @project, collection: @labels, as: :label = paginate @labels, theme: 'gitlab' @@ -42,4 +41,4 @@ %template#js-badge-item-template %li.label-link-item.js-priority-badge.inline.prepend-left-10 - .label-badge.label-badge-blue Prioritized label + .label-badge.label-badge-blue= _('Prioritized label') diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 391021a7665..658d546a4a4 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -11,22 +11,22 @@ %li.label-list-item{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label, subject: subject, force_priority: force_priority %ul.label-actions-list - - if controller.is_a?(Projects::LabelsController) + - if @project %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - if can?(current_user, :admin_label, @project) %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', :'data-placement' => 'top' } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') } = sprite_icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', :'data-placement' => 'top' } + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') } = sprite_icon('star') %li.inline - = link_to edit_label_path(label), class: 'btn btn-transparent label-action' do + = link_to edit_label_path(label), class: 'btn btn-transparent label-action', aria_label: 'Edit label' do = sprite_icon('pencil') %li.inline .dropdown - %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' } } + %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') } = sprite_icon('ellipsis_v') .dropdown-menu.dropdown-open-left %ul @@ -44,7 +44,7 @@ = _('Promote to group label') %li %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } } - = button_tag 'Delete', type: 'button', class: 'text-danger remove-row' + %button.text-danger.remove-row{ type: 'button' }= _('Delete') - if current_user %li.inline.label-subscription - if can_subscribe_to_label_in_different_levels?(label) diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index ed23db156bc..0ae3ab8f090 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -6,7 +6,7 @@ .label-name = link_to_label(label, subject: @project, tooltip: false) .label-description - .append-right-15.prepend-left-15 + .append-right-default.prepend-left-default - if label.description.present? .description-text.append-bottom-10 = markdown_field(label, :description) -- cgit v1.2.1 From 932b9f8cb2a90884f4f22747dfc86075b082bf4f Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 5 Jun 2018 13:11:48 +0100 Subject: Fix eslint --- app/assets/javascripts/group_label_subscription.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index 755f94a3d1e..d33e3a37580 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -53,7 +53,7 @@ export default class GroupLabelSubscription { static setNewTooltip($button) { if (!$button.hasClass('js-subscribe-button')) return; - + const type = $button.hasClass('js-group-level') ? 'group' : 'project'; const newTitle = tooltipTitles[type]; -- cgit v1.2.1 From c8a4d202c99c772822a2b9b09fa6da2c90b2ae81 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 09:51:24 +0200 Subject: favicon uploader generating ci status favicons --- Gemfile | 1 + Gemfile.lock | 2 + .../overlays/favicon_status_canceled.png | Bin 0 -> 864 bytes .../overlays/favicon_status_created.png | Bin 0 -> 889 bytes .../ci_favicons/overlays/favicon_status_failed.png | Bin 0 -> 1015 bytes .../ci_favicons/overlays/favicon_status_manual.png | Bin 0 -> 1067 bytes .../overlays/favicon_status_not_found.png | Bin 0 -> 945 bytes .../overlays/favicon_status_pending.png | Bin 0 -> 919 bytes .../overlays/favicon_status_running.png | Bin 0 -> 1077 bytes .../overlays/favicon_status_skipped.png | Bin 0 -> 923 bytes .../overlays/favicon_status_success.png | Bin 0 -> 1044 bytes .../overlays/favicon_status_warning.png | Bin 0 -> 830 bytes app/uploaders/favicon_uploader.rb | 44 +++++++++++++++++++++ spec/uploaders/favicon_uploader_spec.rb | 38 ++++++++++++++++++ 14 files changed, 85 insertions(+) create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_canceled.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_created.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_failed.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_manual.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_not_found.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_pending.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_running.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_skipped.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_success.png create mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_warning.png create mode 100644 app/uploaders/favicon_uploader.rb create mode 100644 spec/uploaders/favicon_uploader_spec.rb diff --git a/Gemfile b/Gemfile index 90fa659fe78..b508fb17742 100644 --- a/Gemfile +++ b/Gemfile @@ -104,6 +104,7 @@ gem 'hamlit', '~> 2.6.1' # Files attachments gem 'carrierwave', '~> 1.2' +gem 'mini_magick' # Drag and Drop UI gem 'dropzonejs-rails', '~> 0.7.1' diff --git a/Gemfile.lock b/Gemfile.lock index 2daaa3b516e..7437b6e7898 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -494,6 +494,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mimemagic (0.3.0) + mini_magick (4.8.0) mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.7.0) @@ -1078,6 +1079,7 @@ DEPENDENCIES loofah (~> 2.2) mail_room (~> 0.9.1) method_source (~> 0.8) + mini_magick minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.10) diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png b/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png new file mode 100644 index 00000000000..8adaa9c600b Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_created.png b/app/assets/images/ci_favicons/overlays/favicon_status_created.png new file mode 100644 index 00000000000..ca788dd0034 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_created.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_failed.png b/app/assets/images/ci_favicons/overlays/favicon_status_failed.png new file mode 100644 index 00000000000..93f1e2772fd Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_failed.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_manual.png b/app/assets/images/ci_favicons/overlays/favicon_status_manual.png new file mode 100644 index 00000000000..c926062c806 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_manual.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png b/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png new file mode 100644 index 00000000000..df3049315a9 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_pending.png b/app/assets/images/ci_favicons/overlays/favicon_status_pending.png new file mode 100644 index 00000000000..f7d67d4a230 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_pending.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_running.png b/app/assets/images/ci_favicons/overlays/favicon_status_running.png new file mode 100644 index 00000000000..ff4167c4b20 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_running.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png b/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png new file mode 100644 index 00000000000..a9c36464b69 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_success.png b/app/assets/images/ci_favicons/overlays/favicon_status_success.png new file mode 100644 index 00000000000..bcc30c73f5f Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_success.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_warning.png b/app/assets/images/ci_favicons/overlays/favicon_status_warning.png new file mode 100644 index 00000000000..6db3b0280f5 Binary files /dev/null and b/app/assets/images/ci_favicons/overlays/favicon_status_warning.png differ diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb new file mode 100644 index 00000000000..dc30e838337 --- /dev/null +++ b/app/uploaders/favicon_uploader.rb @@ -0,0 +1,44 @@ +class FaviconUploader < AttachmentUploader + include CarrierWave::MiniMagick + + STATUS_ICON_NAMES = [ + :status_not_found, + :status_canceled, + :status_success, + :status_skipped, + :status_created, + :status_failed, + :status_warning, + :status_pending, + :status_manual, + :status_running + ].freeze + + version :default_without_format_conversion do + process resize_to_fill: [32, 32] + end + + # this intermediate version generates an image in the ico format but with the + # original file suffix. + version :_default, from_version: :default_without_format_conversion do + process convert: 'ico' + end + + version :default, from_version: :_default + + STATUS_ICON_NAMES.each do |status_name| + version status_name, from_version: :default do + process status_favicon: status_name + end + end + + def status_favicon(status_name) + manipulate! do |img| + overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/favicon_#{status_name}.png") + overlay = MiniMagick::Image.open(overlay_path) + img.composite(overlay) do |c| + c.compose 'over' + end + end + end +end diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb new file mode 100644 index 00000000000..5989d294112 --- /dev/null +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +RSpec.describe FaviconUploader do + include CarrierWave::Test::Matchers + + let(:uploader) { described_class.new(build_stubbed(:user)) } + + after do + uploader.remove! + end + + def upload_fixture(filename) + fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + end + + context 'versions' do + before do + uploader.store!(upload_fixture('dk.png')) + end + + it 'has the correct format' do + expect(uploader.default).to be_format('ico') + end + + it 'has the correct dimensions' do + expect(uploader.default).to have_dimensions(32, 32) + end + + it 'generates all the status icons' do + # make sure that the following each statement actually loops + expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 + + FaviconUploader::STATUS_ICON_NAMES.each do |status_name| + expect(File.exist?(uploader.status_not_found.file.file)).to be true + end + end + end +end -- cgit v1.2.1 From ce6172e863f982c73fedc9f4a73877543c30cb1c Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 09:54:32 +0200 Subject: allow uploading favicon in appearance settings --- app/controllers/admin/appearances_controller.rb | 9 +++++++++ app/controllers/concerns/uploads_actions.rb | 2 +- app/models/appearance.rb | 1 + app/views/admin/appearances/_form.html.haml | 17 +++++++++++++++++ config/routes/admin.rb | 1 + config/routes/uploads.rb | 2 +- db/migrate/20170925184228_add_favicon_to_appearances.rb | 7 +++++++ db/schema.rb | 1 + 8 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20170925184228_add_favicon_to_appearances.rb diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb index ea302f17d16..9aaec905734 100644 --- a/app/controllers/admin/appearances_controller.rb +++ b/app/controllers/admin/appearances_controller.rb @@ -41,6 +41,13 @@ class Admin::AppearancesController < Admin::ApplicationController redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.' end + def favicon + @appearance.remove_favicon! + @appearance.save + + redirect_to admin_appearances_path, notice: 'Favicon was succesfully removed.' + end + private # Use callbacks to share common setup or constraints between actions. @@ -61,6 +68,8 @@ class Admin::AppearancesController < Admin::ApplicationController logo_cache header_logo header_logo_cache + favicon + favicon_cache new_project_guidelines updated_by ] diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index b9b9b6e4e88..80049044124 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -2,7 +2,7 @@ module UploadsActions include Gitlab::Utils::StrongMemoize include SendFileUpload - UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo).freeze + UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze def create link_to_file = UploadService.new(model, params[:file], uploader_class).execute diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 67cc84a9140..b770aadef0e 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -14,6 +14,7 @@ class Appearance < ActiveRecord::Base mount_uploader :logo, AttachmentUploader mount_uploader :header_logo, AttachmentUploader + mount_uploader :favicon, FaviconUploader # Overrides CacheableAttributes.current_without_cache def self.current_without_cache diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 5e08837255f..163ce55eb53 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -55,6 +55,23 @@ .hint Guidelines parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}. + %fieldset.app_logo + %legend + Favicon: + .form-group + = f.label :favicon, 'Favicon', class: 'control-label' + .col-sm-10 + - if @appearance.favicon? + = image_tag @appearance.favicon.default_without_format_conversion.url, class: 'appearance-light-logo-preview' + - if @appearance.persisted? + %br + = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" + %hr + = f.hidden_field :favicon_cache + = f.file_field :favicon, class: '' + .hint + Maximum file size is 1MB. The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px. + .form-actions = f.submit 'Save', class: 'btn btn-save append-right-10' - if @appearance.persisted? diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 3cca1210e39..ff27ceb50dc 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -102,6 +102,7 @@ namespace :admin do get :preview_sign_in delete :logo delete :header_logos + delete :favicon end end diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb index 6370645bcb9..6becadd57ae 100644 --- a/config/routes/uploads.rb +++ b/config/routes/uploads.rb @@ -17,7 +17,7 @@ scope path: :uploads do # Appearance get "-/system/:model/:mounted_as/:id/:filename", to: "uploads#show", - constraints: { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ } + constraints: { model: /appearance/, mounted_as: /logo|header_logo|favicon/, filename: /.+/ } # Project markdown uploads get ":namespace_id/:project_id/:secret/:filename", diff --git a/db/migrate/20170925184228_add_favicon_to_appearances.rb b/db/migrate/20170925184228_add_favicon_to_appearances.rb new file mode 100644 index 00000000000..65083733afb --- /dev/null +++ b/db/migrate/20170925184228_add_favicon_to_appearances.rb @@ -0,0 +1,7 @@ +class AddFaviconToAppearances < ActiveRecord::Migration + DOWNTIME = false + + def change + add_column :appearances, :favicon, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index a6b0706b02a..89c8acba3e6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -38,6 +38,7 @@ ActiveRecord::Schema.define(version: 20180529152628) do t.integer "cached_markdown_version" t.text "new_project_guidelines" t.text "new_project_guidelines_html" + t.string "favicon" end create_table "application_setting_terms", force: :cascade do |t| -- cgit v1.2.1 From 18d4f121d347bbc91f76b8112f797076864c6b33 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 15:02:00 +0200 Subject: fix carrierwave suffix for different format when versions have a different file format from the original file carrierwave constructs a wrong url (with the original file suffix). --- app/uploaders/favicon_uploader.rb | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index dc30e838337..b48ef770461 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -14,24 +14,27 @@ class FaviconUploader < AttachmentUploader :status_running ].freeze - version :default_without_format_conversion do + version :default do process resize_to_fill: [32, 32] - end - - # this intermediate version generates an image in the ico format but with the - # original file suffix. - version :_default, from_version: :default_without_format_conversion do process convert: 'ico' - end - version :default, from_version: :_default + def full_filename(filename) + filename_for_different_format(super(filename), 'ico') + end + end STATUS_ICON_NAMES.each do |status_name| version status_name, from_version: :default do process status_favicon: status_name + + def full_filename(filename) + filename_for_different_format(super(filename), 'ico') + end end end + private + def status_favicon(status_name) manipulate! do |img| overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/favicon_#{status_name}.png") @@ -41,4 +44,8 @@ class FaviconUploader < AttachmentUploader end end end + + def filename_for_different_format(filename, format) + filename.chomp(File.extname(filename)) + ".#{format}" + end end -- cgit v1.2.1 From ff24be48556c8a7d8e9a55fc667d0713b90ac591 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 15:02:55 +0200 Subject: show avatar status versions on appearance page --- app/views/admin/appearances/_form.html.haml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 163ce55eb53..8d73bd8fb55 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -62,7 +62,13 @@ = f.label :favicon, 'Favicon', class: 'control-label' .col-sm-10 - if @appearance.favicon? - = image_tag @appearance.favicon.default_without_format_conversion.url, class: 'appearance-light-logo-preview' + = image_tag @appearance.favicon.default.url, class: 'appearance-light-logo-preview' + - if @appearance.favicon? + = f.label :favicon, 'Generated status icons', class: 'control-label' + .col-sm-10 + - if @appearance.favicon? + - FaviconUploader::STATUS_ICON_NAMES.each do |status_name| + = image_tag @appearance.favicon.public_send(status_name).url, class: 'appearance-light-logo-preview' - if @appearance.persisted? %br = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" -- cgit v1.2.1 From 40d8d7df4bd437efc81f0bdff5f93b4b65844cb5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 16:07:53 +0200 Subject: feature spec for managing appearance > favicon --- spec/features/admin/admin_appearance_spec.rb | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index d91dcf76191..556aa10d226 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,6 +76,40 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end + scenario 'Favicon' do + sign_in(create(:admin)) + visit admin_appearances_path + + attach_file(:appearance_favicon, logo_fixture) + click_button 'Save' + + expect(page).to have_css('//img[data-src$="/default_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_canceled_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_created_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_failed_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_manual_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_not_found_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_pending_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_running_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_skipped_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_success_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_warning_dk.ico"]') + + click_link 'Remove favicon' + + expect(page).not_to have_css('//img[data-src$="/default_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_canceled_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_created_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_failed_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_manual_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_not_found_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_pending_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_running_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + end + def expect_custom_sign_in_appearance(appearance) expect(page).to have_content appearance.title expect(page).to have_content appearance.description -- cgit v1.2.1 From 8967fc0477d176cb5b93ad3a9f2cf19eaca14876 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 16:22:04 +0200 Subject: use custom main favicon --- app/helpers/appearances_helper.rb | 4 ++++ app/helpers/page_layout_helper.rb | 1 + spec/helpers/page_layout_helper_spec.rb | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index f48db024e3f..52ee8aec4ad 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -33,4 +33,8 @@ module AppearancesHelper render 'shared/logo_type.svg' end end + + def brand_favicon + brand_item&.favicon + end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index a8397b03d63..b9b0e08cde5 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -39,6 +39,7 @@ module PageLayoutHelper end def favicon + return brand_favicon.default.url if brand_favicon return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) return 'favicon-blue.ico' if Rails.env.development? diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index b77114a8152..53ecf25612f 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -55,6 +55,11 @@ describe PageLayoutHelper do stub_env('CANARY', 'true') expect(helper.favicon).to eq 'favicon-yellow.ico' end + + it 'uses the custom favicon if an favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(helper.favicon).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + end end describe 'page_image' do -- cgit v1.2.1 From 606b23dd39bc67b01a52bb189dd938fba87badd2 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 16:23:18 +0200 Subject: sort status icon names by name --- app/uploaders/favicon_uploader.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index b48ef770461..d64fa5b1609 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -2,16 +2,16 @@ class FaviconUploader < AttachmentUploader include CarrierWave::MiniMagick STATUS_ICON_NAMES = [ - :status_not_found, :status_canceled, - :status_success, - :status_skipped, :status_created, :status_failed, - :status_warning, - :status_pending, :status_manual, - :status_running + :status_not_found, + :status_pending, + :status_running, + :status_skipped, + :status_success, + :status_warning ].freeze version :default do -- cgit v1.2.1 From a6f3f6b8cd2e79acbc824c401435284635071e1a Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 17:04:37 +0200 Subject: extract favicon logic to lib class --- app/helpers/appearances_helper.rb | 4 ---- app/helpers/page_layout_helper.rb | 6 +----- lib/gitlab/favicon.rb | 23 +++++++++++++++++++++++ spec/helpers/page_layout_helper_spec.rb | 22 ---------------------- spec/lib/gitlab/favicon_spec.rb | 25 +++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 lib/gitlab/favicon.rb create mode 100644 spec/lib/gitlab/favicon_spec.rb diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index 52ee8aec4ad..f48db024e3f 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -33,8 +33,4 @@ module AppearancesHelper render 'shared/logo_type.svg' end end - - def brand_favicon - brand_item&.favicon - end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index b9b0e08cde5..c3dd204181d 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -39,11 +39,7 @@ module PageLayoutHelper end def favicon - return brand_favicon.default.url if brand_favicon - return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) - return 'favicon-blue.ico' if Rails.env.development? - - 'favicon.ico' + Gitlab::Favicon.default end def page_image diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb new file mode 100644 index 00000000000..27150f8d1ea --- /dev/null +++ b/lib/gitlab/favicon.rb @@ -0,0 +1,23 @@ +module Gitlab + class Favicon + class << self + def default + return appearance_favicon.default.url if appearance_favicon + return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) + return 'favicon-blue.ico' if Rails.env.development? + + 'favicon.ico' + end + + private + + def appearance + @appearance ||= Appearance.current + end + + def appearance_favicon + appearance&.favicon + end + end + end +end diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 53ecf25612f..cf98eed27f1 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -40,28 +40,6 @@ describe PageLayoutHelper do end end - describe 'favicon' do - it 'defaults to favicon.ico' do - allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(helper.favicon).to eq 'favicon.ico' - end - - it 'has blue favicon for development' do - allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(helper.favicon).to eq 'favicon-blue.ico' - end - - it 'has yellow favicon for canary' do - stub_env('CANARY', 'true') - expect(helper.favicon).to eq 'favicon-yellow.ico' - end - - it 'uses the custom favicon if an favicon appearance is present' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(helper.favicon).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} - end - end - describe 'page_image' do it 'defaults to the GitLab logo' do expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png' diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb new file mode 100644 index 00000000000..51a8ed47abf --- /dev/null +++ b/spec/lib/gitlab/favicon_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Favicon do + describe '.default' do + it 'defaults to favicon.ico' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + expect(described_class.default).to eq 'favicon.ico' + end + + it 'has blue favicon for development' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) + expect(described_class.default).to eq 'favicon-blue.ico' + end + + it 'has yellow favicon for canary' do + stub_env('CANARY', 'true') + expect(described_class.favicon).to eq 'favicon-yellow.ico' + end + + it 'uses the custom favicon if a favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + end + end +end -- cgit v1.2.1 From 44d7b1583348513f8faa680a864efdbb39be70ab Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:18:49 +0200 Subject: use custom favicon for ci build status favicons --- app/serializers/status_entity.rb | 11 +---------- lib/gitlab/ci/status/canceled.rb | 2 +- lib/gitlab/ci/status/created.rb | 2 +- lib/gitlab/ci/status/failed.rb | 2 +- lib/gitlab/ci/status/manual.rb | 2 +- lib/gitlab/ci/status/pending.rb | 2 +- lib/gitlab/ci/status/running.rb | 2 +- lib/gitlab/ci/status/skipped.rb | 2 +- lib/gitlab/ci/status/success.rb | 2 +- lib/gitlab/favicon.rb | 18 +++++++++++++++--- spec/lib/gitlab/favicon_spec.rb | 13 +++++++++++++ 11 files changed, 37 insertions(+), 21 deletions(-) diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index 8e8bda2f9df..f4d0a33d466 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -7,16 +7,7 @@ class StatusEntity < Grape::Entity expose :details_path expose :favicon do |status| - dir = - if Gitlab::Utils.to_boolean(ENV['CANARY']) - File.join('ci_favicons', 'canary') - elsif Rails.env.development? - File.join('ci_favicons', 'dev') - else - 'ci_favicons' - end - - ActionController::Base.helpers.image_path(File.join(dir, "#{status.favicon}.ico")) + ActionController::Base.helpers.image_path(status.favicon) end expose :action, if: -> (status, _) { status.has_action? } do diff --git a/lib/gitlab/ci/status/canceled.rb b/lib/gitlab/ci/status/canceled.rb index e6195a60d4f..0d71ff03163 100644 --- a/lib/gitlab/ci/status/canceled.rb +++ b/lib/gitlab/ci/status/canceled.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_canceled' + Gitlab::Favicon.status('canceled') end end end diff --git a/lib/gitlab/ci/status/created.rb b/lib/gitlab/ci/status/created.rb index 846f00b83dd..de86191dfeb 100644 --- a/lib/gitlab/ci/status/created.rb +++ b/lib/gitlab/ci/status/created.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_created' + Gitlab::Favicon.status('created') end end end diff --git a/lib/gitlab/ci/status/failed.rb b/lib/gitlab/ci/status/failed.rb index 27ce85bd3ed..20e2050108c 100644 --- a/lib/gitlab/ci/status/failed.rb +++ b/lib/gitlab/ci/status/failed.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_failed' + Gitlab::Favicon.status('failed') end end end diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb index fc387e2fd25..2c02ce6e870 100644 --- a/lib/gitlab/ci/status/manual.rb +++ b/lib/gitlab/ci/status/manual.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_manual' + Gitlab::Favicon.status('manual') end end end diff --git a/lib/gitlab/ci/status/pending.rb b/lib/gitlab/ci/status/pending.rb index 6780780db32..9122d11cfed 100644 --- a/lib/gitlab/ci/status/pending.rb +++ b/lib/gitlab/ci/status/pending.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_pending' + Gitlab::Favicon.status('pending') end end end diff --git a/lib/gitlab/ci/status/running.rb b/lib/gitlab/ci/status/running.rb index ee13905e46d..9bc48ec2c29 100644 --- a/lib/gitlab/ci/status/running.rb +++ b/lib/gitlab/ci/status/running.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_running' + Gitlab::Favicon.status('running') end end end diff --git a/lib/gitlab/ci/status/skipped.rb b/lib/gitlab/ci/status/skipped.rb index 0dbdc4de426..b404118cd3b 100644 --- a/lib/gitlab/ci/status/skipped.rb +++ b/lib/gitlab/ci/status/skipped.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_skipped' + Gitlab::Favicon.status('skipped') end end end diff --git a/lib/gitlab/ci/status/success.rb b/lib/gitlab/ci/status/success.rb index 731013ec017..be7e5d60b26 100644 --- a/lib/gitlab/ci/status/success.rb +++ b/lib/gitlab/ci/status/success.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - 'favicon_status_success' + Gitlab::Favicon.status('success') end end end diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 27150f8d1ea..17e737ac913 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -2,21 +2,33 @@ module Gitlab class Favicon class << self def default - return appearance_favicon.default.url if appearance_favicon + return appearance_favicon.default.url if appearance_favicon.exists? return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) return 'favicon-blue.ico' if Rails.env.development? 'favicon.ico' end + def status(status_name) + if appearance_favicon.exists? + appearance_favicon.public_send("status_#{status_name}").url + else + dir = 'ci_favicons' + dir = File.join(dir, 'dev') if Rails.env.development? + dir = File.join(dir, 'canary') if Gitlab::Utils.to_boolean(ENV['CANARY']) + + File.join(dir, "favicon_status_#{status_name}.ico") + end + end + private def appearance - @appearance ||= Appearance.current + Appearance.current || Appearance.new end def appearance_favicon - appearance&.favicon + appearance.favicon end end end diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 51a8ed47abf..d74dabf1458 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -22,4 +22,17 @@ RSpec.describe Gitlab::Favicon do expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} end end + + describe '.status' do + subject { described_class.status('created') } + + it 'defaults to the stock icon' do + expect(subject).to eq 'ci_favicons/favicon_status_created.ico' + end + + it 'uses the custom favicon if a favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico}) + end + end end -- cgit v1.2.1 From 0a76bcb412b6a193d30c930312ac40dc21e777e6 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:21:31 +0200 Subject: register ico mime type `#send_file` won't properly set the favicon's content type otherwise. --- config/initializers/mime_types.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index e9326653cbe..acbdf8de5a6 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -15,3 +15,5 @@ Mime::Type.register "video/ogg", :ogv Mime::Type.unregister :json Mime::Type.register 'application/json', :json, [LfsRequest::CONTENT_TYPE, 'application/json'] + +Mime::Type.register 'image/x-icon', :ico -- cgit v1.2.1 From bf27c6841c1cb6b68f67d33d6eb2de63ad8b390f Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:21:58 +0200 Subject: send ico files with inline disposition --- app/uploaders/uploader_helper.rb | 2 +- spec/models/group_spec.rb | 2 +- spec/models/project_spec.rb | 2 +- spec/models/user_spec.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb index fd446d31092..207928b61d0 100644 --- a/app/uploaders/uploader_helper.rb +++ b/app/uploaders/uploader_helper.rb @@ -1,6 +1,6 @@ # Extra methods for uploader module UploaderHelper - IMAGE_EXT = %w[png jpg jpeg gif bmp tiff].freeze + IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze # We recommend using the .mp4 format over .mov. Videos in .mov format can # still be used but you really need to make sure they are served with the # proper MIME type video/mp4 and not video/quicktime or your videos won't play diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index f83b52e8975..8f1f4938065 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -240,7 +240,7 @@ describe Group do it "is false if avatar is html page" do group.update_attribute(:avatar, 'uploads/avatar.html') - expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff"]) + expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico"]) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a76452a808..ed1253c6d97 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -960,7 +960,7 @@ describe Project do it 'is false if avatar is html page' do project.update_attribute(:avatar, 'uploads/avatar.html') - expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff']) + expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico']) end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 09dfeae6377..097144d04ce 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1260,7 +1260,7 @@ describe User do it 'is false if avatar is html page' do user.update_attribute(:avatar, 'uploads/avatar.html') - expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff']) + expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico']) end end -- cgit v1.2.1 From 822023c64ccab23cfdacb42e191dcec4f812adfd Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:23:05 +0200 Subject: Add a '?' to the custom favicon's urls Without the '?' at the end of the favicon url the custom favicon (i.e. the favicons that are served through `UploadController`) are not shown in the browser. It may have something to do with how `#send_file` / `#send_data` set http headers. When serving the same icon file from the public directory everything is fine. --- lib/gitlab/favicon.rb | 11 +++++++++-- spec/lib/gitlab/favicon_spec.rb | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 17e737ac913..e7fe0bff5b8 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -2,7 +2,7 @@ module Gitlab class Favicon class << self def default - return appearance_favicon.default.url if appearance_favicon.exists? + return custom_favicon_url(appearance_favicon.default.url) if appearance_favicon.exists? return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) return 'favicon-blue.ico' if Rails.env.development? @@ -11,7 +11,7 @@ module Gitlab def status(status_name) if appearance_favicon.exists? - appearance_favicon.public_send("status_#{status_name}").url + custom_favicon_url(appearance_favicon.public_send("status_#{status_name}").url) # rubocop:disable GitlabSecurity/PublicSend else dir = 'ci_favicons' dir = File.join(dir, 'dev') if Rails.env.development? @@ -30,6 +30,13 @@ module Gitlab def appearance_favicon appearance.favicon end + + # Without the '?' at the end of the favicon url the custom favicon (i.e. + # the favicons that are served through `UploadController`) are not shown + # in the browser. + def custom_favicon_url(url) + "#{url}?" + end end end end diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index d74dabf1458..fe0bff51308 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico\?} end end @@ -32,7 +32,7 @@ RSpec.describe Gitlab::Favicon do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico}) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico\?}) end end end -- cgit v1.2.1 From 85a8e6f26a8fa0ea9f430f0094fb14706bfd2991 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 15:03:49 +0200 Subject: whitelist allowed file types for custom favicons --- app/uploaders/favicon_uploader.rb | 4 ++++ config/locales/carrierwave.en.yml | 14 ++++++++++++++ spec/features/admin/admin_appearance_spec.rb | 6 ++++++ 3 files changed, 24 insertions(+) create mode 100644 config/locales/carrierwave.en.yml diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index d64fa5b1609..d3debc16fa9 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -33,6 +33,10 @@ class FaviconUploader < AttachmentUploader end end + def extension_whitelist + UploaderHelper::IMAGE_EXT + end + private def status_favicon(status_name) diff --git a/config/locales/carrierwave.en.yml b/config/locales/carrierwave.en.yml new file mode 100644 index 00000000000..12619226460 --- /dev/null +++ b/config/locales/carrierwave.en.yml @@ -0,0 +1,14 @@ +en: + errors: + messages: + carrierwave_processing_error: failed to be processed + carrierwave_integrity_error: is not of an allowed file type + carrierwave_download_error: could not be downloaded + extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}" + extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}" + content_type_whitelist_error: "You are not allowed to upload %{content_type} files" + content_type_blacklist_error: "You are not allowed to upload %{content_type} files" + rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?" + mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}" + min_size_error: "File size should be greater than %{min_size}" + max_size_error: "File size should be less than %{max_size}" diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 556aa10d226..ffffd14752e 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -108,6 +108,12 @@ feature 'Admin Appearance' do expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + + # allowed file types + attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) + click_button 'Save' + + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, jpg, jpeg, gif, bmp, tiff, ico' end def expect_custom_sign_in_appearance(appearance) -- cgit v1.2.1 From 40ffa8401b96dda5f67ea699dbcca0ff64263810 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 28 Sep 2017 10:45:50 +0200 Subject: add request store caching to favicon --- lib/gitlab/favicon.rb | 2 +- spec/lib/gitlab/favicon_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index e7fe0bff5b8..8802f58e31c 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -24,7 +24,7 @@ module Gitlab private def appearance - Appearance.current || Appearance.new + RequestStore.store[:appearance] ||= (Appearance.current || Appearance.new) end def appearance_favicon diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index fe0bff51308..ddfa81c0b5d 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Gitlab::Favicon do +RSpec.describe Gitlab::Favicon, :request_store do describe '.default' do it 'defaults to favicon.ico' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) -- cgit v1.2.1 From 67fe0a17d87a7a5380b41e04ef23212d5da637ba Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 28 Sep 2017 13:57:08 +0200 Subject: call Gitlab::Favicon.status in serializer this ways we can keep the `lib/gitlab/ci/status/*` classes to return the bare favicon name as it was before. also the favicon uploader versions are now have the same names as the stock favicons (+ `favicon_` prefix), which makes working with the status names easier. --- app/helpers/page_layout_helper.rb | 2 +- app/serializers/status_entity.rb | 2 +- app/uploaders/favicon_uploader.rb | 26 +++++++++++++------------- app/views/admin/appearances/_form.html.haml | 2 +- lib/gitlab/ci/status/canceled.rb | 2 +- lib/gitlab/ci/status/created.rb | 2 +- lib/gitlab/ci/status/failed.rb | 2 +- lib/gitlab/ci/status/manual.rb | 2 +- lib/gitlab/ci/status/pending.rb | 2 +- lib/gitlab/ci/status/running.rb | 2 +- lib/gitlab/ci/status/skipped.rb | 2 +- lib/gitlab/ci/status/success.rb | 2 +- lib/gitlab/favicon.rb | 17 ++++++++++------- spec/lib/gitlab/favicon_spec.rb | 16 ++++++++-------- spec/uploaders/favicon_uploader_spec.rb | 6 +++--- 15 files changed, 45 insertions(+), 42 deletions(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index c3dd204181d..68d892393ef 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -39,7 +39,7 @@ module PageLayoutHelper end def favicon - Gitlab::Favicon.default + Gitlab::Favicon.main end def page_image diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index f4d0a33d466..2f3d4b80565 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -7,7 +7,7 @@ class StatusEntity < Grape::Entity expose :details_path expose :favicon do |status| - ActionController::Base.helpers.image_path(status.favicon) + Gitlab::Favicon.status(status.favicon) end expose :action, if: -> (status, _) { status.has_action? } do diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index d3debc16fa9..7697f5fe885 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -2,19 +2,19 @@ class FaviconUploader < AttachmentUploader include CarrierWave::MiniMagick STATUS_ICON_NAMES = [ - :status_canceled, - :status_created, - :status_failed, - :status_manual, - :status_not_found, - :status_pending, - :status_running, - :status_skipped, - :status_success, - :status_warning + :favicon_status_canceled, + :favicon_status_created, + :favicon_status_failed, + :favicon_status_manual, + :favicon_status_not_found, + :favicon_status_pending, + :favicon_status_running, + :favicon_status_skipped, + :favicon_status_success, + :favicon_status_warning ].freeze - version :default do + version :favicon_main do process resize_to_fill: [32, 32] process convert: 'ico' @@ -24,7 +24,7 @@ class FaviconUploader < AttachmentUploader end STATUS_ICON_NAMES.each do |status_name| - version status_name, from_version: :default do + version status_name, from_version: :favicon_main do process status_favicon: status_name def full_filename(filename) @@ -41,7 +41,7 @@ class FaviconUploader < AttachmentUploader def status_favicon(status_name) manipulate! do |img| - overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/favicon_#{status_name}.png") + overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/#{status_name}.png") overlay = MiniMagick::Image.open(overlay_path) img.composite(overlay) do |c| c.compose 'over' diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 8d73bd8fb55..1119c75637c 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -62,7 +62,7 @@ = f.label :favicon, 'Favicon', class: 'control-label' .col-sm-10 - if @appearance.favicon? - = image_tag @appearance.favicon.default.url, class: 'appearance-light-logo-preview' + = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview' - if @appearance.favicon? = f.label :favicon, 'Generated status icons', class: 'control-label' .col-sm-10 diff --git a/lib/gitlab/ci/status/canceled.rb b/lib/gitlab/ci/status/canceled.rb index 0d71ff03163..e6195a60d4f 100644 --- a/lib/gitlab/ci/status/canceled.rb +++ b/lib/gitlab/ci/status/canceled.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('canceled') + 'favicon_status_canceled' end end end diff --git a/lib/gitlab/ci/status/created.rb b/lib/gitlab/ci/status/created.rb index de86191dfeb..846f00b83dd 100644 --- a/lib/gitlab/ci/status/created.rb +++ b/lib/gitlab/ci/status/created.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('created') + 'favicon_status_created' end end end diff --git a/lib/gitlab/ci/status/failed.rb b/lib/gitlab/ci/status/failed.rb index 20e2050108c..27ce85bd3ed 100644 --- a/lib/gitlab/ci/status/failed.rb +++ b/lib/gitlab/ci/status/failed.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('failed') + 'favicon_status_failed' end end end diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb index 2c02ce6e870..fc387e2fd25 100644 --- a/lib/gitlab/ci/status/manual.rb +++ b/lib/gitlab/ci/status/manual.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('manual') + 'favicon_status_manual' end end end diff --git a/lib/gitlab/ci/status/pending.rb b/lib/gitlab/ci/status/pending.rb index 9122d11cfed..6780780db32 100644 --- a/lib/gitlab/ci/status/pending.rb +++ b/lib/gitlab/ci/status/pending.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('pending') + 'favicon_status_pending' end end end diff --git a/lib/gitlab/ci/status/running.rb b/lib/gitlab/ci/status/running.rb index 9bc48ec2c29..ee13905e46d 100644 --- a/lib/gitlab/ci/status/running.rb +++ b/lib/gitlab/ci/status/running.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('running') + 'favicon_status_running' end end end diff --git a/lib/gitlab/ci/status/skipped.rb b/lib/gitlab/ci/status/skipped.rb index b404118cd3b..0dbdc4de426 100644 --- a/lib/gitlab/ci/status/skipped.rb +++ b/lib/gitlab/ci/status/skipped.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('skipped') + 'favicon_status_skipped' end end end diff --git a/lib/gitlab/ci/status/success.rb b/lib/gitlab/ci/status/success.rb index be7e5d60b26..731013ec017 100644 --- a/lib/gitlab/ci/status/success.rb +++ b/lib/gitlab/ci/status/success.rb @@ -15,7 +15,7 @@ module Gitlab end def favicon - Gitlab::Favicon.status('success') + 'favicon_status_success' end end end diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 8802f58e31c..51a25b408ee 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -1,8 +1,8 @@ module Gitlab class Favicon class << self - def default - return custom_favicon_url(appearance_favicon.default.url) if appearance_favicon.exists? + def main + return custom_favicon_url(appearance_favicon.favicon_main.url) if appearance_favicon.exists? return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) return 'favicon-blue.ico' if Rails.env.development? @@ -11,13 +11,16 @@ module Gitlab def status(status_name) if appearance_favicon.exists? - custom_favicon_url(appearance_favicon.public_send("status_#{status_name}").url) # rubocop:disable GitlabSecurity/PublicSend + custom_favicon_url(appearance_favicon.public_send("#{status_name}").url) # rubocop:disable GitlabSecurity/PublicSend else - dir = 'ci_favicons' - dir = File.join(dir, 'dev') if Rails.env.development? - dir = File.join(dir, 'canary') if Gitlab::Utils.to_boolean(ENV['CANARY']) + path = File.join( + 'ci_favicons', + Rails.env.development? ? 'dev' : '', + Gitlab::Utils.to_boolean(ENV['CANARY']) ? 'canary' : '', + "#{status_name}.ico" + ) - File.join(dir, "favicon_status_#{status_name}.ico") + ActionController::Base.helpers.image_path(path) end end diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index ddfa81c0b5d..51b8fda81d1 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -1,38 +1,38 @@ require 'rails_helper' RSpec.describe Gitlab::Favicon, :request_store do - describe '.default' do + describe '.main' do it 'defaults to favicon.ico' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(described_class.default).to eq 'favicon.ico' + expect(described_class.main).to eq 'favicon.ico' end it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(described_class.default).to eq 'favicon-blue.ico' + expect(described_class.main).to eq 'favicon-blue.ico' end it 'has yellow favicon for canary' do stub_env('CANARY', 'true') - expect(described_class.favicon).to eq 'favicon-yellow.ico' + expect(described_class.main).to eq 'favicon-yellow.ico' end it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico\?} + expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.ico} end end describe '.status' do - subject { described_class.status('created') } + subject { described_class.status('favicon_status_created') } it 'defaults to the stock icon' do - expect(subject).to eq 'ci_favicons/favicon_status_created.ico' + expect(subject).to eq '/assets/ci_favicons/favicon_status_created.ico' end it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico\?}) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/favicon_status_created_dk.ico}) end end end diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb index 5989d294112..b521670addb 100644 --- a/spec/uploaders/favicon_uploader_spec.rb +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -19,11 +19,11 @@ RSpec.describe FaviconUploader do end it 'has the correct format' do - expect(uploader.default).to be_format('ico') + expect(uploader.favicon_main).to be_format('ico') end it 'has the correct dimensions' do - expect(uploader.default).to have_dimensions(32, 32) + expect(uploader.favicon_main).to have_dimensions(32, 32) end it 'generates all the status icons' do @@ -31,7 +31,7 @@ RSpec.describe FaviconUploader do expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 FaviconUploader::STATUS_ICON_NAMES.each do |status_name| - expect(File.exist?(uploader.status_not_found.file.file)).to be true + expect(File.exist?(uploader.favicon_status_not_found.file.file)).to be true end end end -- cgit v1.2.1 From 5202c3f0c8da618e2d3821917f6f5d48ae8ae3c2 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 5 Dec 2017 18:47:31 +0100 Subject: fix resetFavicon so that it actually resets --- app/assets/javascripts/lib/utils/common_utils.js | 3 ++- app/views/layouts/_head.html.haml | 2 +- spec/javascripts/lib/utils/common_utils_spec.js | 11 ++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 8b5445d012b..310b731c6d8 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -393,8 +393,9 @@ export const setFavicon = (faviconPath) => { export const resetFavicon = () => { const faviconEl = document.getElementById('favicon'); - const originalFavicon = faviconEl ? faviconEl.getAttribute('href') : null; + if (faviconEl) { + const originalFavicon = faviconEl.getAttribute('data-default-href'); faviconEl.setAttribute('href', originalFavicon); } }; diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 02bdfe9aa3c..024f80e9935 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -25,7 +25,7 @@ %title= page_title(site_name) %meta{ name: "description", content: page_description } - = favicon_link_tag favicon, id: 'favicon' + = favicon_link_tag favicon, id: 'favicon', :'data-default-href': favicon = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 27f06573432..64d13275a59 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -395,6 +395,7 @@ describe('common_utils', () => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); favicon.setAttribute('href', 'default/favicon'); + favicon.setAttribute('data-default-href', 'default/favicon'); document.body.appendChild(favicon); }); @@ -413,7 +414,7 @@ describe('common_utils', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); - favicon.setAttribute('href', 'default/favicon'); + favicon.setAttribute('data-original-href', 'default/favicon'); document.body.appendChild(favicon); }); @@ -421,7 +422,9 @@ describe('common_utils', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should reset page favicon to tanuki', () => { + it('should reset page favicon to the default icon', () => { + const favicon = document.getElementById('favicon'); + favicon.setAttribute('href', 'new/favicon'); commonUtils.resetFavicon(); expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon'); }); @@ -434,6 +437,8 @@ describe('common_utils', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('href', 'null'); + favicon.setAttribute('data-original-href', faviconDataUrl); document.body.appendChild(favicon); mock = new MockAdapter(axios); }); @@ -449,7 +454,7 @@ describe('common_utils', () => { commonUtils.setCiStatusFavicon(BUILD_URL) .then(() => { const favicon = document.getElementById('favicon'); - expect(favicon.getAttribute('href')).toEqual('null'); + expect(favicon.getAttribute('href')).toEqual(faviconDataUrl); done(); }) // Error is already caught in catch() block of setCiStatusFavicon, -- cgit v1.2.1 From 9e14f437b6ed205744d916f5566ee2c11e52b734 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 6 Dec 2017 21:04:53 +0100 Subject: create favicon overlay on the client the initial reason for this change was that graphicsmagick does not support writing to ico files. this fact lead to a chain of changes: 1. use png instead of ico (browser support is good enough) 2. render the overlays on the client using the canvas API. this way we only need to store the original favion and generate the overlay versions dynamically. this change also enables (next step) to simplify the handling of the stock favicons as well, as we don't need to generate all the versions upfront. --- app/assets/javascripts/favicon_admin.js | 19 ++++++++ app/assets/javascripts/lib/utils/common_utils.js | 50 ++++++++++++++++++++-- .../vue_merge_request_widget/mr_widget_options.vue | 5 ++- app/serializers/status_entity.rb | 2 +- app/uploaders/favicon_uploader.rb | 37 +--------------- app/views/admin/appearances/_form.html.haml | 7 ++- app/views/layouts/_head.html.haml | 2 +- lib/gitlab/favicon.rb | 33 ++++++++------ spec/features/admin/admin_appearance_spec.rb | 27 ++---------- spec/javascripts/lib/utils/common_utils_spec.js | 36 ++++++++++++++-- spec/javascripts/lib/utils/mock_data.js | 5 +++ .../vue_mr_widget/mr_widget_options_spec.js | 13 ++++-- spec/lib/gitlab/favicon_spec.rb | 30 +++++++++---- spec/uploaders/favicon_uploader_spec.rb | 11 +---- 14 files changed, 169 insertions(+), 108 deletions(-) create mode 100644 app/assets/javascripts/favicon_admin.js create mode 100644 spec/javascripts/lib/utils/mock_data.js diff --git a/app/assets/javascripts/favicon_admin.js b/app/assets/javascripts/favicon_admin.js new file mode 100644 index 00000000000..6b2dcf4502e --- /dev/null +++ b/app/assets/javascripts/favicon_admin.js @@ -0,0 +1,19 @@ +import {createOverlayIcon} from '~/lib/utils/common_utils'; + +export default class FaviconAdmin { + constructor() { + const faviconContainer = $('.js-favicons'); + const faviconUrl = faviconContainer.data('favicon'); + const overlayUrls = faviconContainer.data('status-overlays'); + + overlayUrls.forEach((statusOverlay) => { + createOverlayIcon(faviconUrl, statusOverlay).then((faviconWithOverlayUrl) => { + const image = $(''); + image.addClass('appearance-light-logo-preview'); + image.attr('src', faviconWithOverlayUrl); + + faviconContainer.append(image); + }); + }); + } +} diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 310b731c6d8..d55d0585031 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -384,6 +384,49 @@ export const backOff = (fn, timeout = 60000) => { }); }; +export const createOverlayIcon = (iconPath, overlayPath) => { + const faviconImage = document.createElement('img'); + + return new Promise((resolve) => { + faviconImage.onload = () => { + const size = 32; + + const canvas = document.createElement('canvas'); + canvas.width = size; + canvas.height = size; + + const context = canvas.getContext('2d'); + context.clearRect(0, 0, size, size); + context.drawImage( + faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size, + ); + + const overlayImage = document.createElement('img'); + overlayImage.onload = () => { + context.drawImage( + overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size, + ); + + const faviconWithOverlayUrl = canvas.toDataURL(); + + resolve(faviconWithOverlayUrl); + }; + overlayImage.src = overlayPath; + }; + faviconImage.src = iconPath; + }); +}; + +export const setFaviconOverlay = (overlayPath) => { + const faviconEl = document.getElementById('favicon'); + + if (!faviconEl) { return null; } + + const iconPath = faviconEl.getAttribute('data-original-href'); + + return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl)); +}; + export const setFavicon = (faviconPath) => { const faviconEl = document.getElementById('favicon'); if (faviconEl && faviconPath) { @@ -395,7 +438,7 @@ export const resetFavicon = () => { const faviconEl = document.getElementById('favicon'); if (faviconEl) { - const originalFavicon = faviconEl.getAttribute('data-default-href'); + const originalFavicon = faviconEl.getAttribute('data-original-href'); faviconEl.setAttribute('href', originalFavicon); } }; @@ -404,10 +447,9 @@ export const setCiStatusFavicon = pageUrl => axios.get(pageUrl) .then(({ data }) => { if (data && data.favicon) { - setFavicon(data.favicon); - } else { - resetFavicon(); + return setFaviconOverlay(data.favicon); } + return resetFavicon(); }) .catch(resetFavicon); diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index f69fe03fcb3..d6cba878b28 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -36,7 +36,7 @@ import { notify, SourceBranchRemovalStatus, } from './dependencies'; -import { setFavicon } from '../lib/utils/common_utils'; +import { setFaviconOverlay } from '../lib/utils/common_utils'; export default { el: '#js-vue-mr-widget', @@ -159,8 +159,9 @@ export default { }, setFaviconHelper() { if (this.mr.ciStatusFaviconPath) { - setFavicon(this.mr.ciStatusFaviconPath); + return setFaviconOverlay(this.mr.ciStatusFaviconPath); } + return Promise.resolve(); }, fetchDeployments() { return this.service.fetchDeployments() diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index 2f3d4b80565..47df7f9dcf9 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -7,7 +7,7 @@ class StatusEntity < Grape::Entity expose :details_path expose :favicon do |status| - Gitlab::Favicon.status(status.favicon) + Gitlab::Favicon.status_overlay(status.favicon) end expose :action, if: -> (status, _) { status.has_action? } do diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index 7697f5fe885..d7be77477b2 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -1,35 +1,12 @@ class FaviconUploader < AttachmentUploader include CarrierWave::MiniMagick - STATUS_ICON_NAMES = [ - :favicon_status_canceled, - :favicon_status_created, - :favicon_status_failed, - :favicon_status_manual, - :favicon_status_not_found, - :favicon_status_pending, - :favicon_status_running, - :favicon_status_skipped, - :favicon_status_success, - :favicon_status_warning - ].freeze - version :favicon_main do process resize_to_fill: [32, 32] - process convert: 'ico' + process convert: 'png' def full_filename(filename) - filename_for_different_format(super(filename), 'ico') - end - end - - STATUS_ICON_NAMES.each do |status_name| - version status_name, from_version: :favicon_main do - process status_favicon: status_name - - def full_filename(filename) - filename_for_different_format(super(filename), 'ico') - end + filename_for_different_format(super(filename), 'png') end end @@ -39,16 +16,6 @@ class FaviconUploader < AttachmentUploader private - def status_favicon(status_name) - manipulate! do |img| - overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/#{status_name}.png") - overlay = MiniMagick::Image.open(overlay_path) - img.composite(overlay) do |c| - c.compose 'over' - end - end - end - def filename_for_different_format(filename, format) filename.chomp(File.extname(filename)) + ".#{format}" end diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 1119c75637c..f77e22bcc45 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -62,13 +62,12 @@ = f.label :favicon, 'Favicon', class: 'control-label' .col-sm-10 - if @appearance.favicon? - = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview' + = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview js-main-favicon' - if @appearance.favicon? - = f.label :favicon, 'Generated status icons', class: 'control-label' + = f.label :favicon, 'Status icons preview', class: 'control-label' .col-sm-10 - if @appearance.favicon? - - FaviconUploader::STATUS_ICON_NAMES.each do |status_name| - = image_tag @appearance.favicon.public_send(status_name).url, class: 'appearance-light-logo-preview' + .js-favicons{ data: { favicon: @appearance.favicon.favicon_main.url, status_overlays: Gitlab::Favicon.available_status_overlays } } - if @appearance.persisted? %br = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 024f80e9935..9253a0652da 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -25,7 +25,7 @@ %title= page_title(site_name) %meta{ name: "description", content: page_description } - = favicon_link_tag favicon, id: 'favicon', :'data-default-href': favicon + = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png' = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 51a25b408ee..e28d4c67661 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -9,18 +9,27 @@ module Gitlab 'favicon.ico' end - def status(status_name) - if appearance_favicon.exists? - custom_favicon_url(appearance_favicon.public_send("#{status_name}").url) # rubocop:disable GitlabSecurity/PublicSend - else - path = File.join( - 'ci_favicons', - Rails.env.development? ? 'dev' : '', - Gitlab::Utils.to_boolean(ENV['CANARY']) ? 'canary' : '', - "#{status_name}.ico" - ) - - ActionController::Base.helpers.image_path(path) + def status_overlay(status_name) + path = File.join( + 'ci_favicons', + 'overlays', + "#{status_name}.png" + ) + + ActionController::Base.helpers.image_path(path) + end + + def available_status_overlays + available_status_names.map do |status_name| + status_overlay(status_name) + end + end + + def available_status_names + @available_status_names ||= begin + Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', 'overlays', "*.png")) + .map { |file| File.basename(file, '.png') } + .sort end end diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index ffffd14752e..0ac4f111c52 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,38 +76,19 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end - scenario 'Favicon' do + scenario 'Favicon', :js do sign_in(create(:admin)) visit admin_appearances_path attach_file(:appearance_favicon, logo_fixture) click_button 'Save' - expect(page).to have_css('//img[data-src$="/default_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_canceled_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_created_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_failed_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_manual_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_not_found_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_pending_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_running_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_skipped_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_success_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_warning_dk.ico"]') + # 11 = 1 original + 10 overlay variations + expect(page).to have_css('.appearance-light-logo-preview', count: 11) click_link 'Remove favicon' - expect(page).not_to have_css('//img[data-src$="/default_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_canceled_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_created_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_failed_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_manual_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_not_found_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_pending_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_running_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + expect(page).not_to have_css('.appearance-light-logo-preview') # allowed file types attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 64d13275a59..2d7cc3443cf 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -2,6 +2,7 @@ import axios from '~/lib/utils/axios_utils'; import * as commonUtils from '~/lib/utils/common_utils'; import MockAdapter from 'axios-mock-adapter'; +import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data'; describe('common_utils', () => { describe('parseUrl', () => { @@ -430,6 +431,35 @@ describe('common_utils', () => { }); }); + describe('createOverlayIcon', () => { + it('should return the favicon with the overlay', (done) => { + commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => { + expect(url).toEqual(faviconWithOverlayDataUrl); + done(); + }); + }); + }); + + describe('setFaviconOverlay', () => { + beforeEach(() => { + const favicon = document.createElement('link'); + favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('data-original-href', faviconDataUrl); + document.body.appendChild(favicon); + }); + + afterEach(() => { + document.body.removeChild(document.getElementById('favicon')); + }); + + it('should set page favicon to provided favicon overlay', (done) => { + commonUtils.setFaviconOverlay(overlayDataUrl).then(() => { + expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl); + done(); + }); + }); + }); + describe('setCiStatusFavicon', () => { const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`; let mock; @@ -463,16 +493,14 @@ describe('common_utils', () => { }); it('should set page favicon to CI status favicon based on provided status', (done) => { - const FAVICON_PATH = '//icon_status_success'; - mock.onGet(BUILD_URL).reply(200, { - favicon: FAVICON_PATH, + favicon: overlayDataUrl, }); commonUtils.setCiStatusFavicon(BUILD_URL) .then(() => { const favicon = document.getElementById('favicon'); - expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH); + expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); done(); }) .catch(done.fail); diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js new file mode 100644 index 00000000000..fd0d62b751f --- /dev/null +++ b/spec/javascripts/lib/utils/mock_data.js @@ -0,0 +1,5 @@ +export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC'; + +export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC'; + +export const faviconWithOverlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGt0lEQVRYR8WWf3DT5R3H35/vN0lDiCztSuiPAEnTFhSOUamIzFGokwMH55g0E845yjbP6+4qIoiHY6JjnHLeRI6h9jgQpQcD3ImH2u00eHBwjGthKLI1TSm26S8oKYVS0vT7/X52z7ckTUhqC/yx55/kks/zeb+ez6/nIWYm/B8XJQB4SGq6lL+CJA47vvRtvWs2D0nNl/Kf0qBZxx6u23arv0QAAIHivK8BynB4ffa7BgDQXJx/ngGnw+uThgTwP5ZnMocoJAxVxZDVZ+0L5n5WF75TkMafjLdJxpSg2E+gqW1X7zk3rbpaifhLiEBgTv4mEFbpBoTyu01DoDj/dQAv9rvjtdnp/k3Yx9rgAMV5QYCsAAwAgg6vL/1OTy/2BYrzzwLIBWACuNHhrXPG+otGoKaw0JA58kqGJtOFfgPS8yWT4sz88nzj7UIIfz+wd0mRdEZPLMnp2V/8R0+JrhLbBYFHJvwWzBUxYgqYNzhG+zfEhm24MIE5ectBtP0W+y0Or29FcoDifHFSRxwAcMrh9c0YrmisXaA4r0V0U8xvopgDDq9PpCQ+Ag0/zbEbNUNbMiG9fTwkDTsKHpJa2t1Zmiw1AqLg+tMZ+R6WVVtnZ2qP6Ib+FIjh05G3lsDrB4xjUIbRDeM+WZLJYZ4B1rKMzKPm/fdyzs9qg6WT225IMnPcuYjxbPZhn57qaA00zc4/QYT7b1b/wAZmDYSLjsN1WcmiM+6jXz7JTCs1aNPASBjrtrCGOXVBLK9ph72772bc0REZcsQlkEVoOxblhaFBH0Bxi6GBWFNC8gpV0XqYSe/hI85R9o1zxr/QaZbdbmuW9oRzljRrzBRkW9JhMaTgYugKzl35DlXNJ/Fp43FImoZnz7T0ln7bLihM0g85N627vkWPgLrbvYyCvAP1+rRIWETA5QsyQlcJYOCbMRasWpALtljwSsFyeJxFYsoNWqdN1y/ildM78Y/WGjxx8TL+ol3oluy8VupKe7cfoNLdCJkdqEUPOmBJ5ksJoae91mBps5lQ6pkIm20MPiz6A3KsmcNukDe/3Ye3zh3A77Q2XqcGjslLz88i/nB8pkpSoL8nAFSTBpUN4qSxS5KB5jOGUOniCebmzFQcevSN2xKP+Fp7ajt21f8TOxU/5i45JZFS6XwcTB9HxZgUnGTRNgk31x5jet+aGU7jWw+UweOcPeyTxxoqrGL25+UwdjehSvnmOVIqcz4C8y8GAABcQwjnYI5NheikhQWT+EZmDh2ev/l7cz4U2cGmYyg78TYqVH87Kbtd1wFY4hsVQAt14zu2RiDaTUZMf/BHWD35STx37wDv94k1dLeh7MRmvDZ1GR5Inxg17dX6MPnjZfh5X6tGSqXrV2B8ACIx98UNGOlV4CxCuA6zqIeq9FQ8c68bhx7ZiIK06CQdVF+Il3y1Hq03gnDfk4Uj8zbH2T51dCPOtlW39Q+iPTl2VSMfwKPiKw8aTuhgpl1Zdqxzj8PphRWwm21xZjv9VcgYkYb52dP132PFbSYr/la0DpNtrrg9a2oqsKfB2zlwG+4nSe1z7QDjaQBi2Eh6J4QRwimYt43LwOsuB2oX7YLVMCLqTAya3xx/EwZJxtYHy3WhyMkHExebXz3zAbbXfdo7AFBRaMAz1Ypa6XoaoPejKRGteZm6D3SlWVdOcOHo/Lfj2u9aXw+WHNmA00G/DiFEO0Jd+meyk0fIf/+vLfik6Xhj4qN0v7i5HCY1bBQPk+ij9GSzNbzYNdH03kMrscARfzvHQgiBocSFTVHVCrW+u+WrpK9iCIgS1rRK93oG/1GkRJVIup8KMNs1Sw/1rUtALD36ZzRca8XeJDmPtRc18vDn5SCJViYHENY3IZTK3JkE7RAYtpdkp3bAaJeOzN+CsSMTX+wqa7ih9sbVSLI2WV3znihAJYXZPThA7M6KQoM2MniyhUxTioxTpKLMadjx8Jqh5k3S//8d9GOh92XWmP/aXLKvfHgA0ZTklL0jj9m6UR6L5+9bjFWTPLcFIWbCY1+8pHb0drWybJ4aWLQrODyAWJndzoyylNyGg0hL+bV7Ll4rKIWB5CFBxMlLj21SL4W6QjDQjwOL9n4tNt0+AADPfo+UqgXPHJLSJrkso7F6ylLMy56OFMmYACIKblvtQext8Iqp0swyLYiI3zEAbs6Ml3cXv/p3Y+ryq5KcnSKb1Jmj75P7X0Rm/UV0tvO86r/WIhORwszvkmHEehH2WMo7ikDUQUWhoaIG+NNc96Os8eMEmklE2Qy2ANTO0OrA+CwFOFBfsq8pWZ7+B25aDBxvPp+QAAAAAElFTkSuQmCC'; diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 30918428da2..6342ea00436 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -5,6 +5,7 @@ import notify from '~/lib/utils/notify'; import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mockData from './mock_data'; +import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data'; const returnPromise = data => new Promise((resolve) => { resolve({ @@ -273,6 +274,7 @@ describe('mrWidgetOptions', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('data-original-href', faviconDataUrl); document.body.appendChild(favicon); faviconElement = document.getElementById('favicon'); @@ -282,10 +284,13 @@ describe('mrWidgetOptions', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should call setFavicon method', () => { - vm.setFaviconHelper(); - - expect(faviconElement.getAttribute('href')).toEqual(vm.mr.ciStatusFaviconPath); + it('should call setFavicon method', (done) => { + vm.mr.ciStatusFaviconPath = overlayDataUrl; + vm.setFaviconHelper().then(() => { + expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); + done(); + }) + .catch(done.fail); }); it('should not call setFavicon when there is no ciStatusFaviconPath', () => { diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 51b8fda81d1..22b9c631ed8 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -19,20 +19,34 @@ RSpec.describe Gitlab::Favicon, :request_store do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.ico} + expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png} end end - describe '.status' do - subject { described_class.status('favicon_status_created') } + describe '.status_overlay' do + subject { described_class.status_overlay('favicon_status_created') } - it 'defaults to the stock icon' do - expect(subject).to eq '/assets/ci_favicons/favicon_status_created.ico' + it 'returns the overlay for the status' do + expect(subject).to eq '/assets/ci_favicons/overlays/favicon_status_created.png' end + end - it 'uses the custom favicon if a favicon appearance is present' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/favicon_status_created_dk.ico}) + describe '.available_status_names' do + subject { described_class.available_status_names } + + it 'returns the available status names' do + expect(subject).to eq %w( + favicon_status_canceled + favicon_status_created + favicon_status_failed + favicon_status_manual + favicon_status_not_found + favicon_status_pending + favicon_status_running + favicon_status_skipped + favicon_status_success + favicon_status_warning + ) end end end diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb index b521670addb..db8a3207f4d 100644 --- a/spec/uploaders/favicon_uploader_spec.rb +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -19,20 +19,11 @@ RSpec.describe FaviconUploader do end it 'has the correct format' do - expect(uploader.favicon_main).to be_format('ico') + expect(uploader.favicon_main).to be_format('png') end it 'has the correct dimensions' do expect(uploader.favicon_main).to have_dimensions(32, 32) end - - it 'generates all the status icons' do - # make sure that the following each statement actually loops - expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 - - FaviconUploader::STATUS_ICON_NAMES.each do |status_name| - expect(File.exist?(uploader.favicon_status_not_found.file.file)).to be true - end - end end end -- cgit v1.2.1 From 949c30d42b91a0dd3959a3ca303b8f76158a2556 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 7 Dec 2017 13:15:49 +0100 Subject: remove all .ico favicon variations, use png always the ci status icons are generated client side, wo we don't need the static files anymore. --- .../ci_favicons/dev/favicon_status_canceled.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_created.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_failed.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_manual.ico | Bin 4286 -> 0 bytes .../ci_favicons/dev/favicon_status_not_found.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_pending.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_running.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_skipped.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_success.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/dev/favicon_status_warning.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_canceled.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_canceled.png | Bin 0 -> 864 bytes .../images/ci_favicons/favicon_status_created.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_created.png | Bin 0 -> 889 bytes .../images/ci_favicons/favicon_status_failed.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_failed.png | Bin 0 -> 1015 bytes .../images/ci_favicons/favicon_status_manual.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_manual.png | Bin 0 -> 1067 bytes .../images/ci_favicons/favicon_status_not_found.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_not_found.png | Bin 0 -> 945 bytes .../images/ci_favicons/favicon_status_pending.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_pending.png | Bin 0 -> 919 bytes .../images/ci_favicons/favicon_status_running.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_running.png | Bin 0 -> 1077 bytes .../images/ci_favicons/favicon_status_skipped.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_skipped.png | Bin 0 -> 923 bytes .../images/ci_favicons/favicon_status_success.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_success.png | Bin 0 -> 1044 bytes .../images/ci_favicons/favicon_status_warning.ico | Bin 4286 -> 0 bytes .../images/ci_favicons/favicon_status_warning.png | Bin 0 -> 830 bytes .../ci_favicons/overlays/favicon_status_canceled.png | Bin 864 -> 0 bytes .../ci_favicons/overlays/favicon_status_created.png | Bin 889 -> 0 bytes .../ci_favicons/overlays/favicon_status_failed.png | Bin 1015 -> 0 bytes .../ci_favicons/overlays/favicon_status_manual.png | Bin 1067 -> 0 bytes .../ci_favicons/overlays/favicon_status_not_found.png | Bin 945 -> 0 bytes .../ci_favicons/overlays/favicon_status_pending.png | Bin 919 -> 0 bytes .../ci_favicons/overlays/favicon_status_running.png | Bin 1077 -> 0 bytes .../ci_favicons/overlays/favicon_status_skipped.png | Bin 923 -> 0 bytes .../ci_favicons/overlays/favicon_status_success.png | Bin 1044 -> 0 bytes .../ci_favicons/overlays/favicon_status_warning.png | Bin 830 -> 0 bytes app/assets/images/favicon-blue.png | Bin 0 -> 1522 bytes app/assets/images/favicon-yellow.ico | Bin 5430 -> 0 bytes app/assets/images/favicon-yellow.png | Bin 0 -> 1667 bytes app/assets/images/favicon.ico | Bin 5430 -> 0 bytes app/assets/images/favicon.png | Bin 0 -> 1611 bytes app/assets/javascripts/favicon_admin.js | 2 +- .../vue_shared/components/file_icon/file_icon_map.js | 2 +- app/models/project_services/jira_service.rb | 2 +- doc/user/reserved_names.md | 2 +- lib/gitlab/favicon.rb | 9 ++++----- lib/gitlab/path_regex.rb | 2 +- public/favicon.ico | Bin 5430 -> 0 bytes public/favicon.png | Bin 0 -> 1611 bytes spec/controllers/projects/jobs_controller_spec.rb | 2 +- .../projects/merge_requests_controller_spec.rb | 2 +- .../controllers/projects/pipelines_controller_spec.rb | 2 +- .../user_creates_image_diff_notes_spec.rb | 2 +- spec/javascripts/jobs/mock_data.js | 4 ++-- spec/javascripts/pipelines/graph/mock_data.js | 18 +++++++++--------- spec/lib/gitlab/favicon_spec.rb | 10 +++++----- spec/models/project_services/jira_service_spec.rb | 2 +- spec/serializers/build_serializer_spec.rb | 4 ++-- spec/serializers/pipeline_serializer_spec.rb | 2 +- spec/serializers/status_entity_spec.rb | 6 +++--- spec/services/system_note_service_spec.rb | 6 +++--- 65 files changed, 39 insertions(+), 40 deletions(-) delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_canceled.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_created.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_failed.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_manual.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_not_found.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_pending.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_running.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_skipped.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_success.ico delete mode 100644 app/assets/images/ci_favicons/dev/favicon_status_warning.ico delete mode 100644 app/assets/images/ci_favicons/favicon_status_canceled.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_canceled.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_created.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_created.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_failed.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_failed.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_manual.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_manual.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_not_found.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_not_found.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_pending.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_pending.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_running.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_running.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_skipped.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_skipped.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_success.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_success.png delete mode 100644 app/assets/images/ci_favicons/favicon_status_warning.ico create mode 100644 app/assets/images/ci_favicons/favicon_status_warning.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_canceled.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_created.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_failed.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_manual.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_not_found.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_pending.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_running.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_skipped.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_success.png delete mode 100644 app/assets/images/ci_favicons/overlays/favicon_status_warning.png create mode 100644 app/assets/images/favicon-blue.png delete mode 100644 app/assets/images/favicon-yellow.ico create mode 100644 app/assets/images/favicon-yellow.png delete mode 100644 app/assets/images/favicon.ico create mode 100644 app/assets/images/favicon.png delete mode 100644 public/favicon.ico create mode 100644 public/favicon.png diff --git a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico deleted file mode 100644 index 4af3582b60d..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_created.ico b/app/assets/images/ci_favicons/dev/favicon_status_created.ico deleted file mode 100644 index 13639da2e8a..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_created.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico deleted file mode 100644 index 5f0e711b104..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico deleted file mode 100644 index 8b1168a1267..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico b/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico deleted file mode 100644 index ed19b69e1c5..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico deleted file mode 100644 index 5dfefd4cc5a..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_running.ico b/app/assets/images/ci_favicons/dev/favicon_status_running.ico deleted file mode 100644 index a41539c0e3e..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_running.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico deleted file mode 100644 index 2c1ae552b93..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_success.ico b/app/assets/images/ci_favicons/dev/favicon_status_success.ico deleted file mode 100644 index 70f0ca61eca..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_success.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico deleted file mode 100644 index db289e03eb1..00000000000 Binary files a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico deleted file mode 100644 index 23adcffff50..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_canceled.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.png b/app/assets/images/ci_favicons/favicon_status_canceled.png new file mode 100644 index 00000000000..8adaa9c600b Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_canceled.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico deleted file mode 100644 index f9d93b390d8..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_created.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_created.png b/app/assets/images/ci_favicons/favicon_status_created.png new file mode 100644 index 00000000000..ca788dd0034 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_created.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico deleted file mode 100644 index 28a22ebf724..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_failed.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_failed.png b/app/assets/images/ci_favicons/favicon_status_failed.png new file mode 100644 index 00000000000..93f1e2772fd Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_failed.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico deleted file mode 100644 index dbbf1abf30c..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_manual.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_manual.png b/app/assets/images/ci_favicons/favicon_status_manual.png new file mode 100644 index 00000000000..c926062c806 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_manual.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.ico b/app/assets/images/ci_favicons/favicon_status_not_found.ico deleted file mode 100644 index 49b9b232dd1..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_not_found.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.png b/app/assets/images/ci_favicons/favicon_status_not_found.png new file mode 100644 index 00000000000..df3049315a9 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_not_found.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico deleted file mode 100644 index 05962f3f148..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_pending.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_pending.png b/app/assets/images/ci_favicons/favicon_status_pending.png new file mode 100644 index 00000000000..f7d67d4a230 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_pending.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico deleted file mode 100644 index 7fa3d4d48d4..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_running.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_running.png b/app/assets/images/ci_favicons/favicon_status_running.png new file mode 100644 index 00000000000..ff4167c4b20 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_running.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico deleted file mode 100644 index b0c26b62068..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_skipped.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.png b/app/assets/images/ci_favicons/favicon_status_skipped.png new file mode 100644 index 00000000000..a9c36464b69 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_skipped.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico deleted file mode 100644 index b150960b5be..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_success.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_success.png b/app/assets/images/ci_favicons/favicon_status_success.png new file mode 100644 index 00000000000..bcc30c73f5f Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_success.png differ diff --git a/app/assets/images/ci_favicons/favicon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico deleted file mode 100644 index 7e71d71684d..00000000000 Binary files a/app/assets/images/ci_favicons/favicon_status_warning.ico and /dev/null differ diff --git a/app/assets/images/ci_favicons/favicon_status_warning.png b/app/assets/images/ci_favicons/favicon_status_warning.png new file mode 100644 index 00000000000..6db3b0280f5 Binary files /dev/null and b/app/assets/images/ci_favicons/favicon_status_warning.png differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png b/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png deleted file mode 100644 index 8adaa9c600b..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_canceled.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_created.png b/app/assets/images/ci_favicons/overlays/favicon_status_created.png deleted file mode 100644 index ca788dd0034..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_created.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_failed.png b/app/assets/images/ci_favicons/overlays/favicon_status_failed.png deleted file mode 100644 index 93f1e2772fd..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_failed.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_manual.png b/app/assets/images/ci_favicons/overlays/favicon_status_manual.png deleted file mode 100644 index c926062c806..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_manual.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png b/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png deleted file mode 100644 index df3049315a9..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_not_found.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_pending.png b/app/assets/images/ci_favicons/overlays/favicon_status_pending.png deleted file mode 100644 index f7d67d4a230..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_pending.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_running.png b/app/assets/images/ci_favicons/overlays/favicon_status_running.png deleted file mode 100644 index ff4167c4b20..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_running.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png b/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png deleted file mode 100644 index a9c36464b69..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_skipped.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_success.png b/app/assets/images/ci_favicons/overlays/favicon_status_success.png deleted file mode 100644 index bcc30c73f5f..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_success.png and /dev/null differ diff --git a/app/assets/images/ci_favicons/overlays/favicon_status_warning.png b/app/assets/images/ci_favicons/overlays/favicon_status_warning.png deleted file mode 100644 index 6db3b0280f5..00000000000 Binary files a/app/assets/images/ci_favicons/overlays/favicon_status_warning.png and /dev/null differ diff --git a/app/assets/images/favicon-blue.png b/app/assets/images/favicon-blue.png new file mode 100644 index 00000000000..2229fe79462 Binary files /dev/null and b/app/assets/images/favicon-blue.png differ diff --git a/app/assets/images/favicon-yellow.ico b/app/assets/images/favicon-yellow.ico deleted file mode 100644 index b650f277fb6..00000000000 Binary files a/app/assets/images/favicon-yellow.ico and /dev/null differ diff --git a/app/assets/images/favicon-yellow.png b/app/assets/images/favicon-yellow.png new file mode 100644 index 00000000000..2d5289818b4 Binary files /dev/null and b/app/assets/images/favicon-yellow.png differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico deleted file mode 100644 index 3479cbbb46f..00000000000 Binary files a/app/assets/images/favicon.ico and /dev/null differ diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png new file mode 100644 index 00000000000..845e0ec34a5 Binary files /dev/null and b/app/assets/images/favicon.png differ diff --git a/app/assets/javascripts/favicon_admin.js b/app/assets/javascripts/favicon_admin.js index 6b2dcf4502e..97e87054ce0 100644 --- a/app/assets/javascripts/favicon_admin.js +++ b/app/assets/javascripts/favicon_admin.js @@ -4,7 +4,7 @@ export default class FaviconAdmin { constructor() { const faviconContainer = $('.js-favicons'); const faviconUrl = faviconContainer.data('favicon'); - const overlayUrls = faviconContainer.data('status-overlays'); + const overlayUrls = faviconContainer.data('status-overlays') || []; overlayUrls.forEach((statusOverlay) => { createOverlayIcon(faviconUrl, statusOverlay).then((faviconWithOverlayUrl) => { diff --git a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js index 9ffbaae3ea5..9bca1993ccc 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js +++ b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js @@ -513,7 +513,7 @@ const fileNameIcons = { 'credits.md': 'credits', 'credits.md.rendered': 'credits', '.flowconfig': 'flow', - 'favicon.ico': 'favicon', + 'favicon.png': 'favicon', 'karma.conf.js': 'karma', 'karma.conf.ts': 'karma', 'karma.conf.coffee': 'karma', diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index eb3261c902f..2edfe7d81f8 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -265,7 +265,7 @@ class JiraService < IssueTrackerService title: title, status: status, icon: { - title: 'GitLab', url16x16: asset_url('favicon.ico', host: gitlab_config.url) + title: 'GitLab', url16x16: asset_url('favicon.png', host: gitlab_config.url) } } } diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md index 50ec99be48b..6c1378560ef 100644 --- a/doc/user/reserved_names.md +++ b/doc/user/reserved_names.md @@ -58,7 +58,7 @@ Currently the following names are reserved as top level groups: - dashboard - deploy.html - explore -- favicon.ico +- favicon.png - groups - header_logo_dark.png - header_logo_light.png diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index e28d4c67661..d0178d0fdf9 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -3,16 +3,15 @@ module Gitlab class << self def main return custom_favicon_url(appearance_favicon.favicon_main.url) if appearance_favicon.exists? - return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) - return 'favicon-blue.ico' if Rails.env.development? + return ActionController::Base.helpers.image_path('favicon-yellow.png') if Gitlab::Utils.to_boolean(ENV['CANARY']) + return ActionController::Base.helpers.image_path('favicon-blue.png') if Rails.env.development? - 'favicon.ico' + ActionController::Base.helpers.image_path('favicon.png') end def status_overlay(status_name) path = File.join( 'ci_favicons', - 'overlays', "#{status_name}.png" ) @@ -27,7 +26,7 @@ module Gitlab def available_status_names @available_status_names ||= begin - Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', 'overlays', "*.png")) + Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', '*.png')) .map { |file| File.basename(file, '.png') } .sort end diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb index 4dc38aae61e..e5191f5c7f9 100644 --- a/lib/gitlab/path_regex.rb +++ b/lib/gitlab/path_regex.rb @@ -30,7 +30,7 @@ module Gitlab dashboard deploy.html explore - favicon.ico + favicon.png files groups health_check diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index 3479cbbb46f..00000000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 00000000000..845e0ec34a5 Binary files /dev/null and b/public/favicon.png differ diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index a08fcea27a5..06c8a432561 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -265,7 +265,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico" + expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png" end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 6e8de6db9c3..eb83884d54e 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -681,7 +681,7 @@ describe Projects::MergeRequestsController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico" + expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png" end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 9e7bc20a6d1..c524f5d7f8a 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -190,7 +190,7 @@ describe Projects::PipelinesController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index 7c4fd25bb39..25c408516d1 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -12,7 +12,7 @@ feature 'Merge request > User creates image diff notes', :js do # Stub helper to return any blob file as image from public app folder. # This is necessary to run this specs since we don't display repo images in capybara. allow_any_instance_of(DiffHelper).to receive(:diff_file_blob_raw_url).and_return('/apple-touch-icon.png') - allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.ico') + allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.png') end context 'create commit diff notes' do diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 25ca8eb6c0b..dd025255bd1 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -20,7 +20,7 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/-/jobs/4757', - favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -78,7 +78,7 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/pipelines/140', - favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, duration: 6, finished_at: '2017-06-01T17:32:00.042Z', diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js index 70eba98e939..9e25a4b3fed 100644 --- a/spec/javascripts/pipelines/graph/mock_data.js +++ b/spec/javascripts/pipelines/graph/mock_data.js @@ -20,7 +20,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, duration: 9, finished_at: '2017-04-19T14:30:27.542Z', @@ -40,7 +40,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4153', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -65,7 +65,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4153', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -85,7 +85,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123#test', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, path: '/root/ci-mock/pipelines/123#test', dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test', @@ -105,7 +105,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4166', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -130,7 +130,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4166', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -152,7 +152,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4159', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -177,7 +177,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4159', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -197,7 +197,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123#deploy', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, path: '/root/ci-mock/pipelines/123#deploy', dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=deploy', diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 22b9c631ed8..fdc5c0180e4 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -2,19 +2,19 @@ require 'rails_helper' RSpec.describe Gitlab::Favicon, :request_store do describe '.main' do - it 'defaults to favicon.ico' do + it 'defaults to favicon.png' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(described_class.main).to eq 'favicon.ico' + expect(described_class.main).to match_asset_path '/assets/favicon.png' end it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(described_class.main).to eq 'favicon-blue.ico' + expect(described_class.main).to match_asset_path '/assets/favicon-blue.png' end it 'has yellow favicon for canary' do stub_env('CANARY', 'true') - expect(described_class.main).to eq 'favicon-yellow.ico' + expect(described_class.main).to match_asset_path 'favicon-yellow.png' end it 'uses the custom favicon if a favicon appearance is present' do @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Favicon, :request_store do subject { described_class.status_overlay('favicon_status_created') } it 'returns the overlay for the status' do - expect(subject).to eq '/assets/ci_favicons/overlays/favicon_status_created.png' + expect(subject).to match_asset_path '/assets/ci_favicons/favicon_status_created.png' end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 54ef0be67ff..3a6a6c116c2 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -173,7 +173,7 @@ describe JiraService do object: { url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: true } } ) diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb index 98cd15e248b..52459cd369d 100644 --- a/spec/serializers/build_serializer_spec.rb +++ b/spec/serializers/build_serializer_spec.rb @@ -39,7 +39,7 @@ describe BuildSerializer do expect(subject[:label]).to eq('failed') expect(subject[:tooltip]).to eq('failed
(unknown failure)') expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end @@ -54,7 +54,7 @@ describe BuildSerializer do expect(subject[:label]).to eq('passed') expect(subject[:tooltip]).to eq('passed') expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end end diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index b741308e2c5..33c8213f9a7 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -174,7 +174,7 @@ describe PipelineSerializer do expect(subject[:text]).to eq(status.text) expect(subject[:label]).to eq(status.label) expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end end diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 559475e571c..73f81405d54 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -18,17 +18,17 @@ describe StatusEntity do it 'contains status details' do expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip expect(subject).to include :has_details, :details_path - expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico') + expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end it 'contains a dev namespaced favicon if dev env' do allow(Rails.env).to receive(:development?) { true } - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/dev/favicon_status_success.ico') + expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end it 'contains a canary namespaced favicon if canary env' do stub_env('CANARY', 'true') - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.ico') + expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.png') end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index e28b0ea5cf2..b7f38874c26 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -789,7 +789,7 @@ describe SystemNoteService do object: { url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) @@ -815,7 +815,7 @@ describe SystemNoteService do object: { url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) @@ -841,7 +841,7 @@ describe SystemNoteService do object: { url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) -- cgit v1.2.1 From 9ae08342eb568af47dd815f585ce714aff22ffec Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 7 Dec 2017 13:17:05 +0100 Subject: force minimagick to use graphicsmagick --- config/initializers/mini_magick.rb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 config/initializers/mini_magick.rb diff --git a/config/initializers/mini_magick.rb b/config/initializers/mini_magick.rb new file mode 100644 index 00000000000..db0e7bbaaa3 --- /dev/null +++ b/config/initializers/mini_magick.rb @@ -0,0 +1,3 @@ +MiniMagick.configure do |config| + config.cli = :graphicsmagick +end -- cgit v1.2.1 From 36d000cb7884a0ab53f504e91fd0336cd848b32c Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 5 Feb 2018 15:32:28 +0100 Subject: use inverted style for remove buttons --- app/views/admin/appearances/_form.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index f77e22bcc45..308a779cb88 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -11,7 +11,7 @@ = image_tag @appearance.header_logo_url, class: 'appearance-light-logo-preview' - if @appearance.persisted? %br - = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" + = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" %hr = f.hidden_field :header_logo_cache = f.file_field :header_logo, class: "" @@ -38,7 +38,7 @@ = image_tag @appearance.logo_url, class: 'appearance-logo-preview' - if @appearance.persisted? %br - = link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" + = link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" %hr = f.hidden_field :logo_cache = f.file_field :logo, class: "" @@ -70,7 +70,7 @@ .js-favicons{ data: { favicon: @appearance.favicon.favicon_main.url, status_overlays: Gitlab::Favicon.available_status_overlays } } - if @appearance.persisted? %br - = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo" + = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" %hr = f.hidden_field :favicon_cache = f.file_field :favicon, class: '' -- cgit v1.2.1 From b4d84c07bcf143aeab7abccb8d0cdb849f605af5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 5 Feb 2018 15:41:37 +0100 Subject: remove favicon preview on appearance page --- app/assets/javascripts/favicon_admin.js | 19 ------------------- app/views/admin/appearances/_form.html.haml | 7 +------ lib/gitlab/favicon.rb | 6 ------ spec/features/admin/admin_appearance_spec.rb | 5 ++--- 4 files changed, 3 insertions(+), 34 deletions(-) delete mode 100644 app/assets/javascripts/favicon_admin.js diff --git a/app/assets/javascripts/favicon_admin.js b/app/assets/javascripts/favicon_admin.js deleted file mode 100644 index 97e87054ce0..00000000000 --- a/app/assets/javascripts/favicon_admin.js +++ /dev/null @@ -1,19 +0,0 @@ -import {createOverlayIcon} from '~/lib/utils/common_utils'; - -export default class FaviconAdmin { - constructor() { - const faviconContainer = $('.js-favicons'); - const faviconUrl = faviconContainer.data('favicon'); - const overlayUrls = faviconContainer.data('status-overlays') || []; - - overlayUrls.forEach((statusOverlay) => { - createOverlayIcon(faviconUrl, statusOverlay).then((faviconWithOverlayUrl) => { - const image = $(''); - image.addClass('appearance-light-logo-preview'); - image.attr('src', faviconWithOverlayUrl); - - faviconContainer.append(image); - }); - }); - } -} diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 308a779cb88..81979f7b331 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -62,12 +62,7 @@ = f.label :favicon, 'Favicon', class: 'control-label' .col-sm-10 - if @appearance.favicon? - = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview js-main-favicon' - - if @appearance.favicon? - = f.label :favicon, 'Status icons preview', class: 'control-label' - .col-sm-10 - - if @appearance.favicon? - .js-favicons{ data: { favicon: @appearance.favicon.favicon_main.url, status_overlays: Gitlab::Favicon.available_status_overlays } } + = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview' - if @appearance.persisted? %br = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index d0178d0fdf9..e3e4a18e241 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -18,12 +18,6 @@ module Gitlab ActionController::Base.helpers.image_path(path) end - def available_status_overlays - available_status_names.map do |status_name| - status_overlay(status_name) - end - end - def available_status_names @available_status_names ||= begin Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', '*.png')) diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 0ac4f111c52..bd879635d2f 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,15 +76,14 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end - scenario 'Favicon', :js do + scenario 'Favicon' do sign_in(create(:admin)) visit admin_appearances_path attach_file(:appearance_favicon, logo_fixture) click_button 'Save' - # 11 = 1 original + 10 overlay variations - expect(page).to have_css('.appearance-light-logo-preview', count: 11) + expect(page).to have_css('.appearance-light-logo-preview') click_link 'Remove favicon' -- cgit v1.2.1 From 46328b1242d6508f7ee13c6b6e40d9aad6af07ea Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 9 Apr 2018 16:49:41 +0200 Subject: the '?' favicon hack doesn't seem to be required probably due to recent changes in `UploadsController`. --- lib/gitlab/favicon.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index e3e4a18e241..d554d100ad1 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -2,7 +2,7 @@ module Gitlab class Favicon class << self def main - return custom_favicon_url(appearance_favicon.favicon_main.url) if appearance_favicon.exists? + return appearance_favicon.favicon_main.url if appearance_favicon.exists? return ActionController::Base.helpers.image_path('favicon-yellow.png') if Gitlab::Utils.to_boolean(ENV['CANARY']) return ActionController::Base.helpers.image_path('favicon-blue.png') if Rails.env.development? @@ -35,13 +35,6 @@ module Gitlab def appearance_favicon appearance.favicon end - - # Without the '?' at the end of the favicon url the custom favicon (i.e. - # the favicons that are served through `UploadController`) are not shown - # in the browser. - def custom_favicon_url(url) - "#{url}?" - end end end end -- cgit v1.2.1 From 256d959729f14094a490c102508e2878c1dd87fc Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 12 Apr 2018 14:11:21 +0200 Subject: ability to get an image's alternative version --- app/controllers/concerns/uploads_actions.rb | 8 +++++++- spec/controllers/uploads_controller_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 80049044124..a62d45db43d 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -31,7 +31,13 @@ module UploadsActions disposition = uploader.image_or_video? ? 'inline' : 'attachment' - send_upload(uploader, attachment: uploader.filename, disposition: disposition) + uploader_version = uploader.versions.values.find { |version| version.filename == params[:filename] } + + if uploader_version + return send_upload(uploader_version, attachment: uploader_version.filename, disposition: disposition) + end + + return send_upload(uploader, attachment: uploader.filename, disposition: disposition) end private diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 376b229ffc9..ae62039fb32 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -560,5 +560,27 @@ describe UploadsController do end end end + + context 'the version filename must match' do + let!(:appearance) { create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + + context 'has a valid filename on the version file' do + it 'successfully returns the file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to eq 'inline; filename="favicon_main_dk.png"' + end + end + + context 'has an invalid filename on the version file' do + it 'returns the original file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to eq 'inline; filename="dk.png"' + end + end + end end end -- cgit v1.2.1 From 96d0b1c67bc1f2a2881298ff898954ba00cd563f Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 12 Apr 2018 14:13:06 +0200 Subject: require uploaded file's name to match in any case --- app/controllers/concerns/uploads_actions.rb | 6 +- spec/controllers/uploads_controller_spec.rb | 92 +++++++++++++++++------------ 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index a62d45db43d..98a55a6d82c 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -31,13 +31,17 @@ module UploadsActions disposition = uploader.image_or_video? ? 'inline' : 'attachment' + if uploader.filename == params[:filename] + return send_upload(uploader, attachment: uploader.filename, disposition: disposition) + end + uploader_version = uploader.versions.values.find { |version| version.filename == params[:filename] } if uploader_version return send_upload(uploader_version, attachment: uploader_version.filename, disposition: disposition) end - return send_upload(uploader, attachment: uploader.filename, disposition: disposition) + render_404 end private diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index ae62039fb32..912aa82526a 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -136,7 +136,7 @@ describe UploadsController do context 'for PNG files' do it 'returns Content-Disposition: inline' do note = create(:note, :with_attachment, project: project) - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' expect(response['Content-Disposition']).to start_with('inline;') end @@ -145,7 +145,7 @@ describe UploadsController do context 'for SVG files' do it 'returns Content-Disposition: attachment' do note = create(:note, :with_svg_attachment, project: project) - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.svg' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'unsanitized.svg' expect(response['Content-Disposition']).to start_with('attachment;') end @@ -164,7 +164,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -172,14 +172,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png' + get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' response end @@ -189,14 +189,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png' + get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' response end @@ -214,14 +214,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -234,14 +234,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -256,7 +256,7 @@ describe UploadsController do context "when not signed in" do it "redirects to the sign in page" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -279,7 +279,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -287,14 +287,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -304,7 +304,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -319,14 +319,14 @@ describe UploadsController do context "when the group is public" do context "when not signed in" do it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -339,14 +339,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -375,7 +375,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -383,14 +383,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -400,7 +400,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -420,14 +420,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -440,14 +440,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -462,7 +462,7 @@ describe UploadsController do context "when not signed in" do it "redirects to the sign in page" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -485,7 +485,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -493,14 +493,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -510,7 +510,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -561,24 +561,40 @@ describe UploadsController do end end - context 'the version filename must match' do + context 'original filename or a version filename must match' do let!(:appearance) { create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + context 'has a valid filename on the original file' do + it 'successfully returns the file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to end_with 'filename="dk.png"' + end + end + + context 'has an invalid filename on the original file' do + it 'returns a 404' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'bogus.png' + + expect(response).to have_gitlab_http_status(404) + end + end + context 'has a valid filename on the version file' do it 'successfully returns the file' do get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png' expect(response).to have_gitlab_http_status(200) - expect(response.header['Content-Disposition']).to eq 'inline; filename="favicon_main_dk.png"' + expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"' end end context 'has an invalid filename on the version file' do - it 'returns the original file' do + it 'returns a 404' do get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png' - expect(response).to have_gitlab_http_status(200) - expect(response.header['Content-Disposition']).to eq 'inline; filename="dk.png"' + expect(response).to have_gitlab_http_status(404) end end end -- cgit v1.2.1 From b606636eab1d9beb0331703dfca7399bba111f46 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 13 Apr 2018 21:29:01 +0200 Subject: simplify uploader versions check --- app/controllers/concerns/uploads_actions.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 98a55a6d82c..170bca8b56f 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -31,17 +31,12 @@ module UploadsActions disposition = uploader.image_or_video? ? 'inline' : 'attachment' - if uploader.filename == params[:filename] - return send_upload(uploader, attachment: uploader.filename, disposition: disposition) - end - - uploader_version = uploader.versions.values.find { |version| version.filename == params[:filename] } + uploaders = [uploader, *uploader.versions.values] + uploader = uploaders.find { |version| version.filename == params[:filename] } - if uploader_version - return send_upload(uploader_version, attachment: uploader_version.filename, disposition: disposition) - end + return render_404 unless uploader - render_404 + send_upload(uploader, attachment: uploader.filename, disposition: disposition) end private -- cgit v1.2.1 From 9151aed773fb32363abbae2fb7a06610915cf882 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 13 Apr 2018 21:40:32 +0200 Subject: dry up asset path helper calls --- lib/gitlab/favicon.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index d554d100ad1..451c9daf761 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -3,10 +3,17 @@ module Gitlab class << self def main return appearance_favicon.favicon_main.url if appearance_favicon.exists? - return ActionController::Base.helpers.image_path('favicon-yellow.png') if Gitlab::Utils.to_boolean(ENV['CANARY']) - return ActionController::Base.helpers.image_path('favicon-blue.png') if Rails.env.development? - ActionController::Base.helpers.image_path('favicon.png') + image_name = + if Gitlab::Utils.to_boolean(ENV['CANARY']) + 'favicon-yellow.png' + elsif Rails.env.development? + 'favicon-blue.png' + else + 'favicon.png' + end + + ActionController::Base.helpers.image_path(image_name) end def status_overlay(status_name) -- cgit v1.2.1 From 7706b3a471de37240c0da6fcc14c7d38c6d62eb9 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 13 Apr 2018 22:30:08 +0200 Subject: use Gitlab::Favicon for jira service --- app/models/project_services/jira_service.rb | 2 +- spec/models/project_services/jira_service_spec.rb | 18 +++++++++++++++++- spec/services/system_note_service_spec.rb | 8 +++++--- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2edfe7d81f8..412d62388f0 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -265,7 +265,7 @@ class JiraService < IssueTrackerService title: title, status: status, icon: { - title: 'GitLab', url16x16: asset_url('favicon.png', host: gitlab_config.url) + title: 'GitLab', url16x16: asset_url(Gitlab::Favicon.main, host: gitlab_config.url) } } } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 3a6a6c116c2..50bdb80ff92 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -164,6 +164,8 @@ describe JiraService do it "creates Remote Link reference in JIRA for comment" do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) + favicon_path = "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" + # Creates comment expect(WebMock).to have_requested(:post, @comment_url) # Creates Remote Link in JIRA issue fields @@ -173,7 +175,7 @@ describe JiraService do object: { url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: true } } ) @@ -464,4 +466,18 @@ describe JiraService do end end end + + describe 'favicon urls', :request_store do + it 'includes the standard favicon' do + props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') + expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/assets/favicon(?:-\h+).png$} + end + + it 'includes returns the custom favicon' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + + props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') + expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$} + end + end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index b7f38874c26..e2ee421921c 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -769,6 +769,8 @@ describe SystemNoteService do end describe "new reference" do + let(:favicon_path) { "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" } + before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) end @@ -789,7 +791,7 @@ describe SystemNoteService do object: { url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) @@ -815,7 +817,7 @@ describe SystemNoteService do object: { url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) @@ -841,7 +843,7 @@ describe SystemNoteService do object: { url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) -- cgit v1.2.1 From 1e9c33acd1129557124e330df2f178fb097d67e5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Sat, 14 Apr 2018 15:37:32 +0200 Subject: remove obsolete favicon related spec the favicon variations don't need to be tested on the entity anymore, as they are A) tested in the dedicated `Gitlab::Favicon` and B) they are created on the client (i.e. the overlays are always the same for all base favicon variants). --- spec/serializers/status_entity_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 73f81405d54..0b010ebd507 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -20,15 +20,5 @@ describe StatusEntity do expect(subject).to include :has_details, :details_path expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end - - it 'contains a dev namespaced favicon if dev env' do - allow(Rails.env).to receive(:development?) { true } - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') - end - - it 'contains a canary namespaced favicon if canary env' do - stub_env('CANARY', 'true') - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.png') - end end end -- cgit v1.2.1 From 197932a2225904898778e7edadc0e447b91cc2ef Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 30 Apr 2018 12:45:07 +0200 Subject: allow only png, ico for favicon uploads the related omnibus graphicsmagick package only supports those formats. see https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1975 --- app/uploaders/favicon_uploader.rb | 2 +- spec/features/admin/admin_appearance_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index d7be77477b2..aa4c78da7a6 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -11,7 +11,7 @@ class FaviconUploader < AttachmentUploader end def extension_whitelist - UploaderHelper::IMAGE_EXT + %w[png ico] end private diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index bd879635d2f..a5e0ac592b9 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -93,7 +93,7 @@ feature 'Admin Appearance' do attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) click_button 'Save' - expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, jpg, jpeg, gif, bmp, tiff, ico' + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' end def expect_custom_sign_in_appearance(appearance) -- cgit v1.2.1 From d35ad403f50b2d496f0d5938502fb55ef2190f7c Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 5 Jun 2018 15:34:51 +0100 Subject: FE review changes --- app/assets/javascripts/project_label_subscription.js | 2 +- app/assets/stylesheets/framework/common.scss | 1 - app/assets/stylesheets/framework/variables.scss | 2 ++ app/assets/stylesheets/pages/labels.scss | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index 35a76875a7a..9049f87e037 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -63,6 +63,6 @@ export default class ProjectLabelSubscription { const type = /group/.test(originalTitle) ? 'group' : 'project'; const newTitle = tooltipTitles[type][newStatus]; - $button.tooltip('hide').attr('title', newTitle).tooltip('_fixTitle'); + $button.attr('title', newTitle).tooltip('_fixTitle'); } } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index d65fc0d2d68..1e7b9534275 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -469,7 +469,6 @@ img.emoji { .append-right-5 { margin-right: 5px; } .append-right-8 { margin-right: 8px; } .append-right-10 { margin-right: 10px; } -.append-right-15 { margin-right: 15px; } .append-right-default { margin-right: $gl-padding; } .append-right-20 { margin-right: 20px; } .append-bottom-0 { margin-bottom: 0; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 946223cfff0..0702272e3ed 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -776,3 +776,5 @@ $modal-body-height: 134px; Prometheus */ $prometheus-table-row-highlight-color: $theme-gray-100; + +$priority-label-empty-state-width: 114px; \ No newline at end of file diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 8ccf778e169..712e881de6f 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -325,5 +325,5 @@ } .priority-labels-empty-state .svg-content img { - max-width: 114px; + max-width: $priority-label-empty-state-width; } -- cgit v1.2.1 From e0d70e4d26934b8fcb0218b957f45c6c3eb28799 Mon Sep 17 00:00:00 2001 From: Edwin Torres Date: Tue, 5 Jun 2018 17:30:06 +0000 Subject: Update index.md; typo "all all" lines 25-26 --- doc/user/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/index.md b/doc/user/index.md index a50e5e8fbf8..4b18c8370f8 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -23,7 +23,7 @@ documentation. GitLab is a fully integrated software development platform that enables you and your team to work cohesively, faster, transparently, and effectively, since the discussion of a new idea until taking that idea to production all -all the way through, from within the same platform. +the way through, from within the same platform. Please check this page for an overview on [GitLab's features](https://about.gitlab.com/features/). -- cgit v1.2.1 From b8bc25d9160fb3d0091bbe2b4a744f6558907ddd Mon Sep 17 00:00:00 2001 From: Annabel Gray Date: Tue, 5 Jun 2018 21:15:07 +0000 Subject: Revert "Merge branch 'jivl-fix-labels-page' into 'master'" This reverts merge request !19383 --- app/assets/stylesheets/pages/labels.scss | 8 -------- app/views/projects/labels/index.html.haml | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 25f011a534b..e178371d21f 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -196,10 +196,6 @@ .prioritized-labels { margin-bottom: 30px; - h5 { - font-size: $gl-font-size; - } - .add-priority { display: none; color: $gray-light; @@ -214,10 +210,6 @@ } .other-labels { - h5 { - font-size: $gl-font-size; - } - .remove-priority { display: none; } diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 1f183c274be..9c78bade254 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -22,7 +22,7 @@ -# Only show it in the first page - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } - %h5.prepend-top-10 Prioritized Labels + %h5 Prioritized Labels %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) } #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" } = render 'shared/empty_states/priority_labels' -- cgit v1.2.1 From 6585e3d8644fb18bd3110bb48866ddf0ada80897 Mon Sep 17 00:00:00 2001 From: Dennis Tang Date: Tue, 5 Jun 2018 15:17:48 -0700 Subject: use bootstrap 4 utilities for error states --- .../components/gke_machine_type_dropdown.vue | 10 ++++++---- .../components/gke_project_id_dropdown.vue | 10 ++++++---- .../gke_cluster_dropdowns/components/gke_zone_dropdown.vue | 10 ++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue index ab7d2d41ece..6ed35c0a981 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue @@ -89,14 +89,13 @@ export default {
{{ errorMessage }} diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue index 25350ef0fa9..542d4d21a22 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue @@ -147,7 +147,6 @@ export default {
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue index 8ee4eefcd91..bc28f8b5df4 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue @@ -63,14 +63,13 @@ export default {
{{ errorMessage }} -- cgit v1.2.1 From cd47c0b807948129909f03ff78800a3906b7d436 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 5 Jun 2018 17:58:14 -0700 Subject: Fix member tabs --- app/views/projects/project_members/index.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index a56023e98cd..0c5be8adc3d 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -18,11 +18,11 @@ .light - if can?(current_user, :admin_project_member, @project) %ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' } - %li.active{ role: 'presentation' } - %a{ href: '#add-member-pane', id: 'add-member-tab', data: { toggle: 'tab' }, role: 'tab' } Add member + %li.nav-tab{ role: 'presentation' } + %a.nav-link.active{ href: '#add-member-pane', id: 'add-member-tab', data: { toggle: 'tab' }, role: 'tab' } Add member - if @project.allowed_to_share_with_group? - %li{ role: 'presentation' } - %a{ href: '#share-with-group-pane', id: 'share-with-group-tab', data: { toggle: 'tab' }, role: 'tab' } Share with group + %li.nav-tab{ role: 'presentation' } + %a.nav-link{ href: '#share-with-group-pane', id: 'share-with-group-tab', data: { toggle: 'tab' }, role: 'tab' } Share with group .tab-content.gitlab-tab-content .tab-pane.active{ id: 'add-member-pane', role: 'tabpanel' } -- cgit v1.2.1 From bc56f1f5c611a26a2f3f06070312b5a6705c002c Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Tue, 5 Jun 2018 21:17:47 -0500 Subject: Add polyfill and comments to utils/sticky --- app/assets/javascripts/init_changes_dropdown.js | 2 +- app/assets/javascripts/job.js | 11 ++--------- app/assets/javascripts/lib/utils/sticky.js | 23 ++++++++++++++++++++++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/init_changes_dropdown.js b/app/assets/javascripts/init_changes_dropdown.js index 09cca1dc7d9..5c5a6e01848 100644 --- a/app/assets/javascripts/init_changes_dropdown.js +++ b/app/assets/javascripts/init_changes_dropdown.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import stickyMonitor from './lib/utils/sticky'; +import { stickyMonitor } from './lib/utils/sticky'; export default (stickyTop) => { stickyMonitor(document.querySelector('.js-diff-files-changed'), stickyTop); diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js index 611e8200b4d..a59a7b59ba4 100644 --- a/app/assets/javascripts/job.js +++ b/app/assets/javascripts/job.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import _ from 'underscore'; -import StickyFill from 'stickyfilljs'; +import { polyfillSticky } from './lib/utils/sticky'; import axios from './lib/utils/axios_utils'; import { visitUrl } from './lib/utils/url_utility'; import bp from './breakpoints'; @@ -80,14 +80,7 @@ export default class Job { } initAffixTopArea() { - /** - If the browser does not support position sticky, it returns the position as static. - If the browser does support sticky, then we allow the browser to handle it, if not - then we use a polyfill - */ - if (this.$topBar.css('position') !== 'static') return; - - StickyFill.add(this.$topBar); + polyfillSticky(this.$topBar); } // eslint-disable-next-line class-methods-use-this diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js index 098afcfa1b4..15a4dd62012 100644 --- a/app/assets/javascripts/lib/utils/sticky.js +++ b/app/assets/javascripts/lib/utils/sticky.js @@ -1,3 +1,5 @@ +import StickyFill from 'stickyfilljs'; + export const createPlaceholder = () => { const placeholder = document.createElement('div'); placeholder.classList.add('sticky-placeholder'); @@ -28,7 +30,16 @@ export const isSticky = (el, scrollY, stickyTop, insertPlaceholder) => { } }; -export default (el, stickyTop, insertPlaceholder = true) => { +/** + * Create a listener that will toggle a 'is-stuck' class, based on the current scroll position. + * + * - If the current environment does not support `position: sticky`, do nothing. + * + * @param {HTMLElement} el The `position: sticky` element. + * @param {Number} stickyTop Used to determine when an element is stuck. + * @param {Boolean} insertPlaceholder Should a placeholder element be created when element is stuck? + */ +export const stickyMonitor = (el, stickyTop, insertPlaceholder = true) => { if (!el) return; if (typeof CSS === 'undefined' || !(CSS.supports('(position: -webkit-sticky) or (position: sticky)'))) return; @@ -37,3 +48,13 @@ export default (el, stickyTop, insertPlaceholder = true) => { passive: true, }); }; + +/** + * Polyfill the `position: sticky` behavior. + * + * - If the current environment supports `position: sticky`, do nothing. + * - Can receive an iterable element list (NodeList, jQuery collection, etc.) or single HTMLElement. + */ +export const polyfillSticky = (el) => { + StickyFill.add(el); +}; -- cgit v1.2.1 From 2af2140ceeb2e8dff7c15a23195f5050a11a6ab4 Mon Sep 17 00:00:00 2001 From: Pirate Praveen Date: Tue, 5 Jun 2018 07:26:13 +0200 Subject: update net-ssh 4 -> 5, remove rbnacl, rbnacl-libsodium (not needed) --- Gemfile | 5 ++--- Gemfile.lock | 12 ++++-------- Gemfile.rails5.lock | 12 ++++-------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Gemfile b/Gemfile index 90fa659fe78..91c26f6c5a4 100644 --- a/Gemfile +++ b/Gemfile @@ -404,13 +404,12 @@ gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' # SSH host key support -gem 'net-ssh', '~> 4.2.0' +gem 'net-ssh', '~> 5.0' gem 'sshkey', '~> 1.9.0' # Required for ED25519 SSH host key support group :ed25519 do - gem 'rbnacl-libsodium' - gem 'rbnacl', '~> 4.0' + gem 'ed25519', '~> 1.2' gem 'bcrypt_pbkdf', '~> 1.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 2daaa3b516e..0cd037569f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -177,6 +177,7 @@ GEM json-jwt (~> 1.6) dropzonejs-rails (0.7.2) rails (> 3.1) + ed25519 (1.2.4) email_reply_trimmer (0.1.6) email_spec (2.2.0) htmlentities (~> 4.3.3) @@ -506,7 +507,7 @@ GEM mustermann (~> 1.0.0) mysql2 (0.4.10) net-ldap (0.16.0) - net-ssh (4.2.0) + net-ssh (5.0.1) netrc (0.11.0) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) @@ -692,10 +693,6 @@ GEM ffi (>= 0.5.0, < 2) rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) - rbnacl (4.0.2) - ffi - rbnacl-libsodium (1.0.11) - rbnacl (>= 3.0.1) rdoc (6.0.4) re2 (1.1.1) recaptcha (3.0.0) @@ -1012,6 +1009,7 @@ DEPENDENCIES doorkeeper (~> 4.3) doorkeeper-openid_connect (~> 1.3) dropzonejs-rails (~> 0.7.1) + ed25519 (~> 1.2) email_reply_trimmer (~> 0.1) email_spec (~> 2.2.0) factory_bot_rails (~> 4.8.2) @@ -1082,7 +1080,7 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.10) net-ldap - net-ssh (~> 4.2.0) + net-ssh (~> 5.0) nokogiri (~> 1.8.2) oauth2 (~> 1.4) octokit (~> 4.9) @@ -1124,8 +1122,6 @@ DEPENDENCIES rainbow (~> 2.2) raindrops (~> 0.18) rblineprof (~> 0.3.6) - rbnacl (~> 4.0) - rbnacl-libsodium rdoc (~> 6.0) re2 (~> 1.1.1) recaptcha (~> 3.0) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 14ea3e4519c..bee13dd1d53 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -179,6 +179,7 @@ GEM json-jwt (~> 1.6) dropzonejs-rails (0.7.4) rails (> 3.1) + ed25519 (1.2.4) email_reply_trimmer (0.1.10) email_spec (2.2.0) htmlentities (~> 4.3.3) @@ -505,7 +506,7 @@ GEM mustermann (~> 1.0.0) mysql2 (0.4.10) net-ldap (0.16.1) - net-ssh (4.2.0) + net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) nokogiri (1.8.2) @@ -696,10 +697,6 @@ GEM ffi (>= 0.5.0, < 2) rblineprof (0.3.7) debugger-ruby_core_source (~> 1.3) - rbnacl (4.0.2) - ffi - rbnacl-libsodium (1.0.16) - rbnacl (>= 3.0.1) rdoc (6.0.4) re2 (1.1.1) recaptcha (3.4.0) @@ -1017,6 +1014,7 @@ DEPENDENCIES doorkeeper (~> 4.3) doorkeeper-openid_connect (~> 1.3) dropzonejs-rails (~> 0.7.1) + ed25519 (~> 1.2) email_reply_trimmer (~> 0.1) email_spec (~> 2.2.0) factory_bot_rails (~> 4.8.2) @@ -1087,7 +1085,7 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.10) net-ldap - net-ssh (~> 4.2.0) + net-ssh (~> 5.0) nokogiri (~> 1.8.2) oauth2 (~> 1.4) octokit (~> 4.9) @@ -1130,8 +1128,6 @@ DEPENDENCIES rainbow (~> 2.2) raindrops (~> 0.18) rblineprof (~> 0.3.6) - rbnacl (~> 4.0) - rbnacl-libsodium rdoc (~> 6.0) re2 (~> 1.1.1) recaptcha (~> 3.0) -- cgit v1.2.1 From c46db1390b3d503f78209f3ed1f064652b6a97e4 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Wed, 6 Jun 2018 03:26:22 -0400 Subject: Update NGINX error rate to a percentage --- config/prometheus/additional_metrics.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml index 13732384953..c994bad7865 100644 --- a/config/prometheus/additional_metrics.yml +++ b/config/prometheus/additional_metrics.yml @@ -29,14 +29,14 @@ label: Pod average unit: ms - title: "HTTP Error Rate" - y_label: "HTTP 500 Errors / Sec" + y_label: "HTTP Errors" required_metrics: - nginx_upstream_responses_total weight: 1 queries: - - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))' - label: HTTP Errors - unit: "errors / sec" + - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100' + label: 5xx Errors + unit: "%" - group: Response metrics (HA Proxy) priority: 10 metrics: -- cgit v1.2.1 From 8a70bbad566865bd35744673d5dda38ca0c90d96 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 6 Jun 2018 08:50:38 +0100 Subject: Fix scss_lint --- app/assets/stylesheets/framework/variables.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 0702272e3ed..0581f8baf66 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -777,4 +777,4 @@ Prometheus */ $prometheus-table-row-highlight-color: $theme-gray-100; -$priority-label-empty-state-width: 114px; \ No newline at end of file +$priority-label-empty-state-width: 114px; -- cgit v1.2.1 From 3ed66d4abde28c9f586342fe8e6481360825b823 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Tue, 5 Jun 2018 19:12:26 +0100 Subject: Adds #human_import_status_name to make it comply with ProjectImportState#human_status_name --- app/models/project.rb | 6 ++++++ .../47208-human-import-status-name-not-working.yml | 5 +++++ spec/models/project_spec.rb | 25 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 changelogs/unreleased/47208-human-import-status-name-not-working.yml diff --git a/app/models/project.rb b/app/models/project.rb index a094dbcb747..3ae445ed200 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -674,6 +674,12 @@ class Project < ActiveRecord::Base end end + def human_import_status_name + ensure_import_state + + import_state.human_status_name + end + def import_schedule ensure_import_state(force: true) diff --git a/changelogs/unreleased/47208-human-import-status-name-not-working.yml b/changelogs/unreleased/47208-human-import-status-name-not-working.yml new file mode 100644 index 00000000000..e1f603f988e --- /dev/null +++ b/changelogs/unreleased/47208-human-import-status-name-not-working.yml @@ -0,0 +1,5 @@ +--- +title: Showing project import_status in a humanized form no longer gives an error +merge_request: 19470 +author: +type: fixed diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a76452a808..59d1671b036 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1693,6 +1693,31 @@ describe Project do end end + describe '#human_import_status_name' do + context 'when import_state exists' do + it 'returns the humanized status name' do + project = create(:project) + create(:import_state, :started, project: project) + + expect(project.human_import_status_name).to eq("started") + end + end + + context 'when import_state was not created yet' do + let(:project) { create(:project, :import_started) } + + it 'ensures import_state is created and returns humanized status name' do + expect do + project.human_import_status_name + end.to change { ProjectImportState.count }.from(0).to(1) + end + + it 'returns humanized status name' do + expect(project.human_import_status_name).to eq("started") + end + end + end + describe 'Project import job' do let(:project) { create(:project, import_url: generate(:url)) } -- cgit v1.2.1 From 64854ae395818c81b357d43fe076af7b81a3bb19 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 03:42:12 -0500 Subject: re-order autodevops options --- .../projects/settings/ci_cd/_autodevops_form.html.haml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index bbabb98dafe..f2e42848c11 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -17,13 +17,6 @@ %br = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted } - .form-check - = form.radio_button :enabled, 'false', class: 'form-check-input' - = form.label :enabled_false, class: 'form-check-label' do - %strong= s_('CICD|Disable Auto DevOps') - %br - = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted } - .form-check = form.radio_button :enabled, '', class: 'form-check-input' = form.label :enabled_, class: 'form-check-label' do @@ -31,6 +24,13 @@ %br = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } + .form-check + = form.radio_button :enabled, 'false', class: 'form-check-input' + = form.label :enabled_false, class: 'form-check-label' do + %strong= s_('CICD|Disable Auto DevOps') + %br + = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted } + = form.label :domain, class:"prepend-top-10" do = _('Domain') = form.text_field :domain, class: 'form-control', placeholder: 'domain.com' -- cgit v1.2.1 From c346e6474e1ef1740ec00d06d60ccaa5cf83f5bc Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 04:14:53 -0500 Subject: re-order and re-style autodevops form to match design --- app/assets/stylesheets/pages/settings_ci_cd.scss | 15 +++++ .../settings/ci_cd/_autodevops_form.html.haml | 64 ++++++++++++---------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss index a355e2dee24..4366a348f69 100644 --- a/app/assets/stylesheets/pages/settings_ci_cd.scss +++ b/app/assets/stylesheets/pages/settings_ci_cd.scss @@ -16,3 +16,18 @@ .registry-placeholder { min-height: 60px; } + +.auto-devops-settings { + .card, + .card-body { + border-radius: $card-border-radius; + } + + .card { + margin-bottom: $gl-vert-padding; + } + + .card-body { + padding: $gl-padding $gl-padding-24; + } +} diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index f2e42848c11..92ee5fb95d5 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -2,7 +2,7 @@ .col-lg-12 = form_for @project, url: project_settings_ci_cd_path(@project) do |f| = form_errors(@project) - %fieldset.builds-feature + %fieldset.builds-feature.auto-devops-settings .form-group - message = auto_devops_warning_message(@project) - ci_file_formatted = '.gitlab-ci.yml'.html_safe @@ -10,34 +10,42 @@ %p.settings-message.text-center = message.html_safe = f.fields_for :auto_devops_attributes, @auto_devops do |form| - .form-check - = form.radio_button :enabled, 'true', class: 'form-check-input' - = form.label :enabled_true, class: 'form-check-label' do - %strong= s_('CICD|Enable Auto DevOps') - %br - = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted } + .card + .card-body + .form-check + = form.radio_button :enabled, 'true', class: 'form-check-input' + = form.label :enabled_true, class: 'form-check-label' do + %strong= s_('CICD|Enable Auto DevOps') + .form-text.text-muted + = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted } - .form-check - = form.radio_button :enabled, '', class: 'form-check-input' - = form.label :enabled_, class: 'form-check-label' do - %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" } - %br - = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } + .card + .card-body + .form-check + = form.radio_button :enabled, '', class: 'form-check-input' + = form.label :enabled_, class: 'form-check-label' do + %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" } + .form-text.text-muted + = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } - .form-check - = form.radio_button :enabled, 'false', class: 'form-check-input' - = form.label :enabled_false, class: 'form-check-label' do - %strong= s_('CICD|Disable Auto DevOps') - %br - = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted } - - = form.label :domain, class:"prepend-top-10" do - = _('Domain') - = form.text_field :domain, class: 'form-control', placeholder: 'domain.com' - .form-text.text-muted - = s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.') - - if cluster_ingress_ip = cluster_ingress_ip(@project) - = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "#{cluster_ingress_ip}.nip.io".html_safe } - = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank' + .card + .card-body.bg-light + = form.label :domain do + %strong= _('Domain') + = form.text_field :domain, class: 'form-control', placeholder: 'domain.com' + .form-text.text-muted + = s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.') + - if cluster_ingress_ip = cluster_ingress_ip(@project) + = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "#{cluster_ingress_ip}.nip.io".html_safe } + = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank' + + .card + .card-body + .form-check + = form.radio_button :enabled, 'false', class: 'form-check-input' + = form.label :enabled_false, class: 'form-check-label' do + %strong= s_('CICD|Disable Auto DevOps') + .form-text.text-muted + = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted } = f.submit 'Save changes', class: "btn btn-success prepend-top-15" -- cgit v1.2.1 From 2c11cab38c85a15b6f8f950d8c614fe601e51933 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 04:21:45 -0500 Subject: update wording of domain input subtext --- app/views/projects/settings/ci_cd/_autodevops_form.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 92ee5fb95d5..96399433e07 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -1,4 +1,4 @@ -.row.prepend-top-default +.row .col-lg-12 = form_for @project, url: project_settings_ci_cd_path(@project) do |f| = form_errors(@project) @@ -34,7 +34,7 @@ %strong= _('Domain') = form.text_field :domain, class: 'form-control', placeholder: 'domain.com' .form-text.text-muted - = s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.') + = s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.') - if cluster_ingress_ip = cluster_ingress_ip(@project) = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "#{cluster_ingress_ip}.nip.io".html_safe } = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank' -- cgit v1.2.1 From cb564d5831cba9ca8403440a5cbe02d968260da1 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 15 May 2018 14:47:04 +0200 Subject: use build image with picturemagick --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1679ae378c9..e366538d907 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6" +image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29" .dedicated-runner: &dedicated-runner retry: 1 -- cgit v1.2.1 From d2256300e559e836884fea57210266dae764f13e Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 17 May 2018 19:00:32 +0200 Subject: hint the allowed image formats on favicon upload --- app/helpers/favicon_helper.rb | 7 +++++++ app/uploaders/favicon_uploader.rb | 4 +++- app/views/admin/appearances/_form.html.haml | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 app/helpers/favicon_helper.rb diff --git a/app/helpers/favicon_helper.rb b/app/helpers/favicon_helper.rb new file mode 100644 index 00000000000..3a5342a8d9d --- /dev/null +++ b/app/helpers/favicon_helper.rb @@ -0,0 +1,7 @@ +module FaviconHelper + def favicon_extension_whitelist + FaviconUploader::EXTENSION_WHITELIST + .map { |extension| "'.#{extension}'"} + .to_sentence + end +end diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index aa4c78da7a6..09afc63a5aa 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -1,4 +1,6 @@ class FaviconUploader < AttachmentUploader + EXTENSION_WHITELIST = %w[png ico].freeze + include CarrierWave::MiniMagick version :favicon_main do @@ -11,7 +13,7 @@ class FaviconUploader < AttachmentUploader end def extension_whitelist - %w[png ico] + EXTENSION_WHITELIST end private diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 81979f7b331..ac92b043074 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -70,7 +70,9 @@ = f.hidden_field :favicon_cache = f.file_field :favicon, class: '' .hint - Maximum file size is 1MB. The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px. + Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}. + %br + The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px. .form-actions = f.submit 'Save', class: 'btn btn-save append-right-10' -- cgit v1.2.1 From 29598f6e6d8ebbe9c1b64018fcb925655ccceb67 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 22 May 2018 18:37:35 +0200 Subject: find assets in test for CI and local test Due to the change in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14583/diffs we can't use the same method to access assets in a CI and the local test environment anymore. --- spec/models/project_services/jira_service_spec.rb | 3 ++- spec/services/system_note_service_spec.rb | 3 ++- spec/support/helpers/assets_helpers.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 spec/support/helpers/assets_helpers.rb diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 50bdb80ff92..c3b4eb17a5c 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe JiraService do include Gitlab::Routing + include AssetsHelpers describe '#options' do let(:service) do @@ -164,7 +165,7 @@ describe JiraService do it "creates Remote Link reference in JIRA for comment" do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) - favicon_path = "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" + favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}" # Creates comment expect(WebMock).to have_requested(:post, @comment_url) diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index e2ee421921c..57d081cffb3 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe SystemNoteService do include Gitlab::Routing include RepoHelpers + include AssetsHelpers set(:group) { create(:group) } set(:project) { create(:project, :repository, group: group) } @@ -769,7 +770,7 @@ describe SystemNoteService do end describe "new reference" do - let(:favicon_path) { "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" } + let(:favicon_path) { "http://localhost/assets/#{find_asset('favicon.png').digest_path}" } before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) diff --git a/spec/support/helpers/assets_helpers.rb b/spec/support/helpers/assets_helpers.rb new file mode 100644 index 00000000000..09bbf451671 --- /dev/null +++ b/spec/support/helpers/assets_helpers.rb @@ -0,0 +1,15 @@ +module AssetsHelpers + # In a CI environment the assets are not compiled, as there is a CI job + # `compile-assets` that compiles them in the prepare stage for all following + # specs. + # Locally the assets are precompiled dynamically. + # + # Sprockets doesn't provide one method to access an asset for both cases. + def find_asset(asset_name) + if ENV['CI'] + Sprockets::Railtie.build_environment(Rails.application, true)[asset_name] + else + Rails.application.assets.find_asset(asset_name) + end + end +end -- cgit v1.2.1 From 6b72c2ff34e43a7bc1269c2459057e8817ef3595 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 5 Jun 2018 17:32:22 +0200 Subject: move favicon admin section up --- app/views/admin/appearances/_form.html.haml | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index ac92b043074..94db374040c 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -18,6 +18,25 @@ .hint Maximum file size is 1MB. Pages are optimized for a 28px tall header logo + %fieldset.app_logo + %legend + Favicon: + .form-group.row + = f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label' + .col-sm-10 + - if @appearance.favicon? + = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview' + - if @appearance.persisted? + %br + = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" + %hr + = f.hidden_field :favicon_cache + = f.file_field :favicon, class: '' + .hint + Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}. + %br + The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px. + %fieldset.sign-in %legend Sign in/Sign up pages: @@ -55,25 +74,6 @@ .hint Guidelines parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}. - %fieldset.app_logo - %legend - Favicon: - .form-group - = f.label :favicon, 'Favicon', class: 'control-label' - .col-sm-10 - - if @appearance.favicon? - = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview' - - if @appearance.persisted? - %br - = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo" - %hr - = f.hidden_field :favicon_cache - = f.file_field :favicon, class: '' - .hint - Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}. - %br - The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px. - .form-actions = f.submit 'Save', class: 'btn btn-save append-right-10' - if @appearance.persisted? -- cgit v1.2.1 From 5e78ac2a4f3b907679114193d971dcdf7b29dcc2 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 6 Jun 2018 10:16:13 +0200 Subject: document custom favicon --- doc/administration/index.md | 1 + doc/customization/favicon.md | 16 ++++++++++++++++ doc/customization/favicon/appearance.png | Bin 0 -> 52245 bytes doc/customization/favicon/custom_favicon.png | Bin 0 -> 60083 bytes 4 files changed, 17 insertions(+) create mode 100644 doc/customization/favicon.md create mode 100644 doc/customization/favicon/appearance.png create mode 100644 doc/customization/favicon/custom_favicon.png diff --git a/doc/administration/index.md b/doc/administration/index.md index df935095e61..0e65f9a9963 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -49,6 +49,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. #### Customizing GitLab's appearance - [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers. +- [Favicon](../customization/favicon.md): Change the default favicon to your own logo. - [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description. - [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page. - ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project. diff --git a/doc/customization/favicon.md b/doc/customization/favicon.md new file mode 100644 index 00000000000..45a18159b5e --- /dev/null +++ b/doc/customization/favicon.md @@ -0,0 +1,16 @@ +# Changing the favicon + +> [Introduced][ce-14497] in GitLab 11.0. + +[ce-14497]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14497 + +Navigate to the **Admin** area and go to the **Appearance** page. + +Upload the custom favicon (**Favicon**) in the section **Favicon**. + +![appearance](favicon/appearance.png) + +After saving the page, the new favicon will be shown in the browser. The main +favicon as well as the CI status icons will show the custom icon: + +![custom_favicon](favicon/custom_favicon.png) diff --git a/doc/customization/favicon/appearance.png b/doc/customization/favicon/appearance.png new file mode 100644 index 00000000000..6c41a05fc1f Binary files /dev/null and b/doc/customization/favicon/appearance.png differ diff --git a/doc/customization/favicon/custom_favicon.png b/doc/customization/favicon/custom_favicon.png new file mode 100644 index 00000000000..fa1b8827a36 Binary files /dev/null and b/doc/customization/favicon/custom_favicon.png differ -- cgit v1.2.1 From 366e1331692900300df42e9c38fc17bd46b7ca1c Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 6 Jun 2018 10:23:44 +0200 Subject: add changelog for custom favicon --- changelogs/unreleased/feature-customizable-favicon.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/feature-customizable-favicon.yml diff --git a/changelogs/unreleased/feature-customizable-favicon.yml b/changelogs/unreleased/feature-customizable-favicon.yml new file mode 100644 index 00000000000..0e5afc17c9e --- /dev/null +++ b/changelogs/unreleased/feature-customizable-favicon.yml @@ -0,0 +1,5 @@ +--- +title: Allow changing the default favicon to a custom icon. +merge_request: 14497 +author: Alexis Reigel +type: added -- cgit v1.2.1 From d9c67a709be457457373ca87b015e846044bfff6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 04:44:42 -0500 Subject: show/hide the extra auto devops settings when appropriate --- .../javascripts/pages/projects/settings/ci_cd/show/index.js | 13 +++++++++++++ .../projects/settings/ci_cd/_autodevops_form.html.haml | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 6c2a785c0af..6d227177cb4 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -22,4 +22,17 @@ document.addEventListener('DOMContentLoaded', () => { errorBox: variableListEl.querySelector('.js-ci-variable-error-box'), saveEndpoint: variableListEl.dataset.saveEndpoint, }); + + // hide extra auto devops settings based on data-attributes + const autoDevOpsSettings = document.querySelectorAll('input[data-hide-extra-settings]'); + const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); + + autoDevOpsSettings.forEach(input => { + input.addEventListener('click', () => + autoDevOpsExtraSettings.classList.toggle( + 'hidden', + input.dataset.hideExtraSettings === 'true', + ), + ); + }); }); diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 96399433e07..4c25635be6f 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -13,7 +13,7 @@ .card .card-body .form-check - = form.radio_button :enabled, 'true', class: 'form-check-input' + = form.radio_button :enabled, 'true', class: 'form-check-input', data: { hide_extra_settings: false } = form.label :enabled_true, class: 'form-check-label' do %strong= s_('CICD|Enable Auto DevOps') .form-text.text-muted @@ -22,13 +22,13 @@ .card .card-body .form-check - = form.radio_button :enabled, '', class: 'form-check-input' + = form.radio_button :enabled, '', class: 'form-check-input', data: { hide_extra_settings: false } = form.label :enabled_, class: 'form-check-label' do %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" } .form-text.text-muted = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } - .card + .card.js-extra-settings{ class: form.object.enabled == false ? 'hidden' : nil } .card-body.bg-light = form.label :domain do %strong= _('Domain') @@ -42,7 +42,7 @@ .card .card-body .form-check - = form.radio_button :enabled, 'false', class: 'form-check-input' + = form.radio_button :enabled, 'false', class: 'form-check-input', data: { hide_extra_settings: true } = form.label :enabled_false, class: 'form-check-label' do %strong= s_('CICD|Disable Auto DevOps') .form-text.text-muted -- cgit v1.2.1 From 7830d406be6c9935e8a4c8a863ef1282163b6b19 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:07:32 -0500 Subject: add deploy strategy radio buttons --- app/controllers/projects/settings/ci_cd_controller.rb | 2 +- .../projects/settings/ci_cd/_autodevops_form.html.haml | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 1d850baf012..fb3f6eec2bd 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -41,7 +41,7 @@ module Projects :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_human_readable, :build_coverage_regex, :public_builds, :auto_cancel_pending_pipelines, :ci_config_path, - auto_devops_attributes: [:id, :domain, :enabled] + auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy] ) end diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 4c25635be6f..037c976f57a 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -38,7 +38,20 @@ - if cluster_ingress_ip = cluster_ingress_ip(@project) = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "#{cluster_ingress_ip}.nip.io".html_safe } = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank' - + + %label.prepend-top-10 + %strong= s_('CICD|Deployment strategy') + %p.settings-message.text-center + = s_('CICD|Deployment strategy needs a domain name to work correctly.') + .form-check + = form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input' + = form.label :deploy_strategy_continuous, class: 'form-check-label' do + %strong= s_('CICD|Continuous deployment to production') + .form-check + = form.radio_button :deploy_strategy, 'manual', class: 'form-check-input' + = form.label :deploy_strategy_manual, class: 'form-check-label' do + %strong= s_('CICD|Automatic deployment to staging, manual deployment to production') + .card .card-body .form-check -- cgit v1.2.1 From e2ff570f275da36eddc77b6ee60d075df10dbfc1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:11:16 -0500 Subject: add help links to the deployment strategy options --- app/views/projects/settings/ci_cd/_autodevops_form.html.haml | 2 ++ app/views/projects/settings/ci_cd/show.html.haml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 037c976f57a..c4677715104 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -47,10 +47,12 @@ = form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input' = form.label :deploy_strategy_continuous, class: 'form-check-label' do %strong= s_('CICD|Continuous deployment to production') + = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank' .form-check = form.radio_button :deploy_strategy, 'manual', class: 'form-check-input' = form.label :deploy_strategy_manual, class: 'form-check-label' do %strong= s_('CICD|Automatic deployment to staging, manual deployment to production') + = link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank' .card .card-body diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index ed17bd4f7dc..0b52cf6a4b4 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -16,12 +16,12 @@ .settings-content = render 'form' -%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) } +%section.settings#autodevops-settings.no-animate{ class: ('expanded' if true) } .settings-header %h4 = s_('CICD|Auto DevOps') %button.btn.btn-default.js-settings-toggle{ type: 'button' } - = expanded ? _('Collapse') : _('Expand') + = true ? _('Collapse') : _('Expand') %p = s_('CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.') = link_to s_('CICD|Learn more about Auto DevOps'), help_page_path('topics/autodevops/index.md') -- cgit v1.2.1 From c99332a83a61e79f03e05f578cac016756beec31 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:16:00 -0500 Subject: add CHANGELOG.md entry for !19172 --- .../unreleased/38542-application-control-panel-in-settings-page.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/38542-application-control-panel-in-settings-page.yml diff --git a/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml b/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml new file mode 100644 index 00000000000..0654456ea45 --- /dev/null +++ b/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml @@ -0,0 +1,5 @@ +--- +title: Add deploy strategies to the Auto DevOps settings +merge_request: 19172 +author: +type: added -- cgit v1.2.1 From c604c43782c5f2ac915f934d566f0502ae41f6d4 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:29:52 -0500 Subject: fix broken specs --- app/views/projects/settings/ci_cd/_autodevops_form.html.haml | 4 ++-- .../projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index c4677715104..ceb3597b5de 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -7,7 +7,7 @@ - message = auto_devops_warning_message(@project) - ci_file_formatted = '.gitlab-ci.yml'.html_safe - if message - %p.settings-message.text-center + %p.auto-devops-warning-message.settings-message.text-center = message.html_safe = f.fields_for :auto_devops_attributes, @auto_devops do |form| .card @@ -28,7 +28,7 @@ .form-text.text-muted = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } - .card.js-extra-settings{ class: form.object.enabled == false ? 'hidden' : nil } + .card.js-extra-settings{ class: form.object&.enabled == false ? 'hidden' : nil } .card-body.bg-light = form.label :domain do %strong= _('Domain') diff --git a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb index d15391911c1..cb1b9e6f5fb 100644 --- a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb +++ b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb @@ -12,7 +12,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name and a') expect(rendered).to have_link('Kubernetes cluster') end @@ -26,7 +26,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a') expect(rendered).to have_link('Kubernetes cluster') end @@ -42,7 +42,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name to work correctly.') end end @@ -55,7 +55,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'does not show warning message' do render - expect(rendered).not_to have_css('.settings-message') + expect(rendered).not_to have_css('.auto-devops-warning-message') end end end -- cgit v1.2.1 From 41c17bdb572b9c2875facba5ff6d003e4cb6f23b Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:51:15 -0500 Subject: revert accidental commit --- app/views/projects/settings/ci_cd/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 0b52cf6a4b4..ed17bd4f7dc 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -16,12 +16,12 @@ .settings-content = render 'form' -%section.settings#autodevops-settings.no-animate{ class: ('expanded' if true) } +%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 = s_('CICD|Auto DevOps') %button.btn.btn-default.js-settings-toggle{ type: 'button' } - = true ? _('Collapse') : _('Expand') + = expanded ? _('Collapse') : _('Expand') %p = s_('CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.') = link_to s_('CICD|Learn more about Auto DevOps'), help_page_path('topics/autodevops/index.md') -- cgit v1.2.1 From 57e6a98ce41363150d24b96ee53c748189b936b4 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Tue, 5 Jun 2018 17:02:04 +0100 Subject: Simplify issuable finder queries We had `item_project_ids` to help make slow queries on the dashboard faster, but this isn't necessary any more - the queries are plenty fast, and we forbid searching the dashboard without filters. --- app/controllers/concerns/issuable_collections.rb | 6 ------ app/finders/issuable_finder.rb | 5 +---- app/finders/issues_finder.rb | 4 ---- app/finders/merge_requests_finder.rb | 4 ---- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index ca1b80a36a0..98a8939a4d0 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -95,12 +95,6 @@ module IssuableCollections elsif @group @filter_params[:group_id] = @group.id @filter_params[:include_subgroups] = true - else - # TODO: this filter ignore issues/mr created in public or - # internal repos where you are not a member. Enable this filter - # or improve current implementation to filter only issues you - # created or assigned or mentioned - # @filter_params[:authorized_only] = true end @filter_params.permit(finder_type.valid_params) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index c6ef79ce15e..3649e0fe560 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -159,10 +159,7 @@ class IssuableFinder finder_options = { include_subgroups: params[:include_subgroups], only_owned: true } GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute else - opts = { current_user: current_user } - opts[:project_ids_relation] = item_project_ids(items) if items - - ProjectsFinder.new(opts).execute + ProjectsFinder.new(current_user: current_user).execute end @projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil) diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 3626670d141..24a6b9349a0 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -136,8 +136,4 @@ class IssuesFinder < IssuableFinder items end end - - def item_project_ids(items) - items&.reorder(nil)&.select(:project_id) - end end diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index e2240e5e0d8..8d84ed4bdfb 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -56,8 +56,4 @@ class MergeRequestsFinder < IssuableFinder items.where(target_branch: target_branch) end - - def item_project_ids(items) - items&.reorder(nil)&.select(:target_project_id) - end end -- cgit v1.2.1 From 52e62f718f56852daa541553acf28ec75d8d5ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 31 May 2018 14:00:36 +0200 Subject: Reduce CE/EE diff in app/views/admin/groups/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/group.rb | 9 +++++++-- app/views/admin/groups/_form.html.haml | 7 +++++++ app/views/admin/groups/_group.html.haml | 3 +++ app/views/admin/groups/show.html.haml | 8 +++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8fb77a7869d..aeb50fb9bb7 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -141,13 +141,14 @@ class Group < Namespace ) end - def add_user(user, access_level, current_user: nil, expires_at: nil) + def add_user(user, access_level, current_user: nil, expires_at: nil, ldap: false) GroupMember.add_user( self, user, access_level, current_user: current_user, - expires_at: expires_at + expires_at: expires_at, + ldap: ldap ) end @@ -195,6 +196,10 @@ class Group < Namespace owners.include?(user) && owners.size == 1 end + def ldap_synced? + false + end + def post_create_hook Gitlab::AppLogger.info("Group \"#{name}\" was created") diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index dc4dccc9e0d..c8008771236 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -2,6 +2,9 @@ = form_errors(@group) = render 'shared/group_form', f: f + = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group + = render_if_exists 'admin/namespace_plan', f: f + .form-group.row.group-description-holder = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2' .col-sm-10 @@ -15,6 +18,8 @@ = render 'groups/group_admin_settings', f: f + = render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f + - if @group.new_record? .form-group.row .offset-sm-2.col-sm-10 @@ -28,3 +33,5 @@ .form-actions = f.submit 'Save changes', class: "btn btn-save" = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel" + += render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index e7c70a6f187..3f96988c203 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -1,3 +1,4 @@ +- group = local_assigns.fetch(:group) - css_class = 'no-description' if group.description.blank? %li.group-row{ class: css_class } @@ -8,6 +9,8 @@ %span.badge.badge-pill = storage_counter(group.storage_size) + = render_if_exists 'admin/namespace_plan_badge', namespace: group + %span = icon('bookmark') = number_with_delimiter(group.projects.count) diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 6d75ccd5add..a40f98ad24f 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -40,6 +40,8 @@ %strong = @group.created_at.to_s(:medium) + = render_if_exists 'admin/namespace_plan_info', namespace: @group + %li %span.light Storage: %strong= storage_counter(@group.storage_size) @@ -58,6 +60,10 @@ = group_lfs_status(@group) = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + = render_if_exists 'namespaces/shared_runner_status', namespace: @group + + = render_if_exists 'ldap_group_links/ldap_group_links_show', group: @group + .card .card-header %h3.card-title @@ -104,7 +110,7 @@ = form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do %div - = users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all) + = users_select_tag(:user_ids, multiple: true, email_user: true, skip_ldap: @group.ldap_synced?, scope: :all) .prepend-top-10 = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr -- cgit v1.2.1 From 282338d8f5fbef1b3bead07e9ca7b339bd781b85 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 6 Jun 2018 16:52:53 +0100 Subject: UX review --- app/assets/stylesheets/pages/labels.scss | 2 +- app/views/groups/labels/index.html.haml | 2 -- app/views/shared/_label.html.haml | 15 ++++++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 712e881de6f..27b436eb224 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -58,7 +58,7 @@ } .color-label { - padding: 0 $grid-size; + padding: $gl-padding-4 $grid-size; line-height: 16px; border-radius: $label-border-radius; color: $white-light; diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index e6b20385792..9c31bbee295 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -16,8 +16,6 @@ .top-area.adjust .nav-text = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence } - - if can_admin_label - = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.') .labels-container.prepend-top-5 .other-labels diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 658d546a4a4..4725951b22d 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -14,13 +14,14 @@ - if @project %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - - if can?(current_user, :admin_label, @project) - %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), - dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') } - = sprite_icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') } - = sprite_icon('star') + - if can?(current_user, :admin_label, label) + - if @project + %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + dom_id: dom_id(label), type: label.type } } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') } + = sprite_icon('star-o') + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') } + = sprite_icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action', aria_label: 'Edit label' do = sprite_icon('pencil') -- cgit v1.2.1 From 07b390f2e4d28f13433981bd5e6d0c5bd82ba72e Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Tue, 5 Jun 2018 09:46:29 -0500 Subject: Fix board dropdown being cropped --- app/assets/stylesheets/pages/boards.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 1c3d312f7ac..b2416a3d5bc 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -282,9 +282,6 @@ box-shadow: 0 1px 2px $issue-boards-card-shadow; list-style: none; - // as a fallback, hide overflow content so that dragging and dropping still works - overflow: hidden; - &:not(:last-child) { margin-bottom: 5px; } -- cgit v1.2.1 From f104f9eddfb26627d76bcee5328f49659afa1dad Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 6 Jun 2018 11:50:23 -0500 Subject: Fix alert-danger class colors for text and background --- app/assets/stylesheets/bootstrap_migration.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index 3785aaa43f0..79f580546c3 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -251,3 +251,13 @@ table { pre code { white-space: pre-wrap; } + +.alert-danger { + background-color: $red-500; + border-color: $red-500; + color: $white-light; + + h4 { + color: $white-light; + } +} -- cgit v1.2.1 From 9738b0a42bdfca5b8fbf0a0ef2ade4d46be38c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 6 Jun 2018 18:58:26 +0200 Subject: Make deploy_strategy zero based --- app/models/project_auto_devops.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index d5e00f84cf7..3c468831e91 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -2,8 +2,8 @@ class ProjectAutoDevops < ActiveRecord::Base belongs_to :project enum deploy_strategy: { - manual: 1, - continuous: 2 + manual: 0, + continuous: 1 } scope :enabled, -> { where(enabled: true) } -- cgit v1.2.1 From d680b678bd39085000fe8e3c097c16859228aced Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 6 Jun 2018 17:58:44 +0100 Subject: UX review --- app/assets/stylesheets/pages/labels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 27b436eb224..3c74c5ed2b4 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -77,7 +77,7 @@ margin-bottom: 5px; display: flex; justify-content: space-between; - padding: 8px 8px 8px $gl-padding; + padding: $gl-padding; border-radius: $border-radius-default; &.sortable-ghost { -- cgit v1.2.1 From 78ae23045bae8a75775a11675afd5836fb8f318b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 6 Jun 2018 19:09:46 +0200 Subject: Reverse logic of manual and continuous deploy strategies --- app/models/project_auto_devops.rb | 2 +- spec/models/project_auto_devops_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index 3c468831e91..ed0428615a2 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -26,7 +26,7 @@ class ProjectAutoDevops < ActiveRecord::Base value: domain.presence || instance_domain) end - if continuous? + if manual? variables.append(key: 'STAGING_ENABLED', value: 1) variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 1) end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 37cd1f571e5..040e76e28fc 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -69,11 +69,11 @@ describe ProjectAutoDevops do end end - context 'when deploy_strategy is continuous' do + context 'when deploy_strategy is manual' do let(:domain) { 'example.com' } before do - auto_devops.deploy_strategy = 'continuous' + auto_devops.deploy_strategy = 'manual' end it do -- cgit v1.2.1 From 44be58836c826dfc0fbfa6d58641d34f84e292fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 6 Jun 2018 19:14:39 +0200 Subject: Use add_column_with_default in migration --- ...80601213245_add_deploy_strategy_to_project_auto_devops.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb index d5ea25f02d4..6f50d428965 100644 --- a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb +++ b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb @@ -7,9 +7,13 @@ class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration # Set this constant to true if this migration requires downtime. DOWNTIME = false - def change - change_table :project_auto_devops do |t| - t.integer :deploy_strategy, null: false, default: 0 - end + disable_ddl_transaction! + + def up + add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false + end + + def down + remove_column :project_auto_devops, :deploy_strategy end end -- cgit v1.2.1 From a30b51fa9400d0fee06532fa99a1fa14e694b2c4 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 6 Jun 2018 18:18:21 +0100 Subject: perf improvement --- app/views/groups/labels/index.html.haml | 2 +- app/views/shared/_label.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 9c31bbee295..db7eaff6658 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -22,7 +22,7 @@ - if can_admin_label %h5{ class: ('hide' if hide) } Labels %ul.content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: true } + = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false } = paginate @labels, theme: 'gitlab' - else = render 'shared/empty_states/labels' diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 4725951b22d..c34d63ba5c3 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -2,7 +2,7 @@ - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] - use_label_priority = local_assigns.fetch(:use_label_priority, false) -- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority? : false) +- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false) - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) - show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) -- cgit v1.2.1 From 15fe6a6219c5c7eea4da1aa340ee99c4ad910596 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 6 Jun 2018 13:12:24 -0500 Subject: Fix history icon misaligned --- app/assets/stylesheets/framework/filters.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 0ee5748952a..551a7e852ae 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -299,6 +299,7 @@ height: 14px; width: 14px; vertical-align: middle; + margin-bottom: 4px; } .dropdown-toggle-text { -- cgit v1.2.1 From 9e5841a0ae2a57496b8479ffb67d3a7f1ed74e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 6 Jun 2018 19:43:50 +0200 Subject: Improve the Runtime::API classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- qa/qa.rb | 6 ++- qa/qa/runtime/api.rb | 82 ---------------------------------- qa/qa/runtime/api/client.rb | 39 ++++++++++++++++ qa/qa/runtime/api/request.rb | 43 ++++++++++++++++++ qa/qa/specs/features/api/users_spec.rb | 2 +- qa/spec/runtime/api/client_spec.rb | 30 +++++++++++++ qa/spec/runtime/api/request_spec.rb | 44 ++++++++++++++++++ qa/spec/runtime/api_client_spec.rb | 30 ------------- qa/spec/runtime/api_request_spec.rb | 42 ----------------- 9 files changed, 162 insertions(+), 156 deletions(-) delete mode 100644 qa/qa/runtime/api.rb create mode 100644 qa/qa/runtime/api/client.rb create mode 100644 qa/qa/runtime/api/request.rb create mode 100644 qa/spec/runtime/api/client_spec.rb create mode 100644 qa/spec/runtime/api/request_spec.rb delete mode 100644 qa/spec/runtime/api_client_spec.rb diff --git a/qa/qa.rb b/qa/qa.rb index 7f2da05dd63..503379823f4 100644 --- a/qa/qa.rb +++ b/qa/qa.rb @@ -12,7 +12,11 @@ module QA autoload :Browser, 'qa/runtime/browser' autoload :Env, 'qa/runtime/env' autoload :Address, 'qa/runtime/address' - autoload :API, 'qa/runtime/api' + + module API + autoload :Client, 'qa/runtime/api/client' + autoload :Request, 'qa/runtime/api/request' + end module Key autoload :Base, 'qa/runtime/key/base' diff --git a/qa/qa/runtime/api.rb b/qa/qa/runtime/api.rb deleted file mode 100644 index e2a096b971d..00000000000 --- a/qa/qa/runtime/api.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'airborne' - -module QA - module Runtime - module API - class Client - attr_reader :address - - def initialize(address = :gitlab) - @address = address - end - - def personal_access_token - @personal_access_token ||= get_personal_access_token - end - - def get_personal_access_token - # you can set the environment variable PERSONAL_ACCESS_TOKEN - # to use a specific access token rather than create one from the UI - if Runtime::Env.personal_access_token - Runtime::Env.personal_access_token - else - create_personal_access_token - end - end - - private - - def create_personal_access_token - Runtime::Browser.visit(@address, Page::Main::Login) do - Page::Main::Login.act { sign_in_using_credentials } - Factory::Resource::PersonalAccessToken.fabricate!.access_token - end - end - end - - class Request - API_VERSION = 'v4'.freeze - - def initialize(api_client, path, personal_access_token: nil) - personal_access_token ||= api_client.personal_access_token - request_path = request_path(path, personal_access_token: personal_access_token) - @session_address = Runtime::Address.new(api_client.address, request_path) - end - - def url - @session_address.address - end - - # Prepend a request path with the path to the API - # - # path - Path to append - # - # Examples - # - # >> request_path('/issues') - # => "/api/v4/issues" - # - # >> request_path('/issues', personal_access_token: 'sometoken) - # => "/api/v4/issues?private_token=..." - # - # Returns the relative path to the requested API resource - def request_path(path, version: API_VERSION, personal_access_token: nil, oauth_access_token: nil) - full_path = File.join('/api', version, path) - - if oauth_access_token - query_string = "access_token=#{oauth_access_token}" - elsif personal_access_token - query_string = "private_token=#{personal_access_token}" - end - - if query_string - full_path << (path.include?('?') ? '&' : '?') - full_path << query_string - end - - full_path - end - end - end - end -end diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb new file mode 100644 index 00000000000..02015e23ad8 --- /dev/null +++ b/qa/qa/runtime/api/client.rb @@ -0,0 +1,39 @@ +require 'airborne' + +module QA + module Runtime + module API + class Client + attr_reader :address + + def initialize(address = :gitlab, personal_access_token: nil) + @address = address + @personal_access_token = personal_access_token + end + + def personal_access_token + @personal_access_token ||= get_personal_access_token + end + + def get_personal_access_token + # you can set the environment variable PERSONAL_ACCESS_TOKEN + # to use a specific access token rather than create one from the UI + if Runtime::Env.personal_access_token + Runtime::Env.personal_access_token + else + create_personal_access_token + end + end + + private + + def create_personal_access_token + Runtime::Browser.visit(@address, Page::Main::Login) do + Page::Main::Login.act { sign_in_using_credentials } + Factory::Resource::PersonalAccessToken.fabricate!.access_token + end + end + end + end + end +end diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb new file mode 100644 index 00000000000..c33ada0de7a --- /dev/null +++ b/qa/qa/runtime/api/request.rb @@ -0,0 +1,43 @@ +module QA + module Runtime + module API + class Request + API_VERSION = 'v4'.freeze + + def initialize(api_client, path, **query_string) + query_string[:private_token] ||= api_client.personal_access_token unless query_string[:oauth_access_token] + request_path = request_path(path, **query_string) + @session_address = Runtime::Address.new(api_client.address, request_path) + end + + def url + @session_address.address + end + + # Prepend a request path with the path to the API + # + # path - Path to append + # + # Examples + # + # >> request_path('/issues') + # => "/api/v4/issues" + # + # >> request_path('/issues', private_token: 'sometoken) + # => "/api/v4/issues?private_token=..." + # + # Returns the relative path to the requested API resource + def request_path(path, version: API_VERSION, **query_string) + full_path = File.join('/api', version, path) + + if query_string.any? + full_path << (path.include?('?') ? '&' : '?') + full_path << query_string.map { |k, v| "#{k}=#{CGI.escape(v)}" }.join('&') + end + + full_path + end + end + end + end +end diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb index 38f4c497183..0aecf89e1b7 100644 --- a/qa/qa/specs/features/api/users_spec.rb +++ b/qa/qa/specs/features/api/users_spec.rb @@ -31,7 +31,7 @@ module QA end scenario 'submit request with an invalid token' do - request = Runtime::API::Request.new(@api_client, '/users', personal_access_token: 'invalid') + request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid') get request.url diff --git a/qa/spec/runtime/api/client_spec.rb b/qa/spec/runtime/api/client_spec.rb new file mode 100644 index 00000000000..d497d8839b8 --- /dev/null +++ b/qa/spec/runtime/api/client_spec.rb @@ -0,0 +1,30 @@ +describe QA::Runtime::API::Client do + include Support::StubENV + + describe 'initialization' do + it 'defaults to :gitlab address' do + expect(described_class.new.address).to eq :gitlab + end + + it 'uses specified address' do + client = described_class.new('http:///example.com') + + expect(client.address).to eq 'http:///example.com' + end + end + + describe '#get_personal_access_token' do + it 'returns specified token from env' do + stub_env('PERSONAL_ACCESS_TOKEN', 'a_token') + + expect(described_class.new.get_personal_access_token).to eq 'a_token' + end + + it 'returns a created token' do + allow_any_instance_of(described_class) + .to receive(:create_personal_access_token).and_return('created_token') + + expect(described_class.new.get_personal_access_token).to eq 'created_token' + end + end +end diff --git a/qa/spec/runtime/api/request_spec.rb b/qa/spec/runtime/api/request_spec.rb new file mode 100644 index 00000000000..80e3149f32d --- /dev/null +++ b/qa/spec/runtime/api/request_spec.rb @@ -0,0 +1,44 @@ +describe QA::Runtime::API::Request do + include Support::StubENV + + before do + stub_env('PERSONAL_ACCESS_TOKEN', 'a_token') + end + + let(:client) { QA::Runtime::API::Client.new('http://example.com') } + let(:request) { described_class.new(client, '/users') } + + describe '#url' do + it 'returns the full api request url' do + expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token' + end + end + + describe '#request_path' do + it 'prepends the api path' do + expect(request.request_path('/users')).to eq '/api/v4/users' + end + + it 'adds the personal access token' do + expect(request.request_path('/users', private_token: 'token')) + .to eq '/api/v4/users?private_token=token' + end + + it 'adds the oauth access token' do + expect(request.request_path('/users', access_token: 'otoken')) + .to eq '/api/v4/users?access_token=otoken' + end + + it 'respects query parameters' do + expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1' + expect(request.request_path('/users', private_token: 'token', foo: 'bar/baz')) + .to eq '/api/v4/users?private_token=token&foo=bar%2Fbaz' + expect(request.request_path('/users?page=1', private_token: 'token', foo: 'bar/baz')) + .to eq '/api/v4/users?page=1&private_token=token&foo=bar%2Fbaz' + end + + it 'uses a different api version' do + expect(request.request_path('/users', version: 'other_version')).to eq '/api/other_version/users' + end + end +end diff --git a/qa/spec/runtime/api_client_spec.rb b/qa/spec/runtime/api_client_spec.rb deleted file mode 100644 index d497d8839b8..00000000000 --- a/qa/spec/runtime/api_client_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe QA::Runtime::API::Client do - include Support::StubENV - - describe 'initialization' do - it 'defaults to :gitlab address' do - expect(described_class.new.address).to eq :gitlab - end - - it 'uses specified address' do - client = described_class.new('http:///example.com') - - expect(client.address).to eq 'http:///example.com' - end - end - - describe '#get_personal_access_token' do - it 'returns specified token from env' do - stub_env('PERSONAL_ACCESS_TOKEN', 'a_token') - - expect(described_class.new.get_personal_access_token).to eq 'a_token' - end - - it 'returns a created token' do - allow_any_instance_of(described_class) - .to receive(:create_personal_access_token).and_return('created_token') - - expect(described_class.new.get_personal_access_token).to eq 'created_token' - end - end -end diff --git a/qa/spec/runtime/api_request_spec.rb b/qa/spec/runtime/api_request_spec.rb index 8cf4b040c24..e69de29bb2d 100644 --- a/qa/spec/runtime/api_request_spec.rb +++ b/qa/spec/runtime/api_request_spec.rb @@ -1,42 +0,0 @@ -describe QA::Runtime::API::Request do - include Support::StubENV - - before do - stub_env('PERSONAL_ACCESS_TOKEN', 'a_token') - end - - let(:client) { QA::Runtime::API::Client.new('http://example.com') } - let(:request) { described_class.new(client, '/users') } - - describe '#url' do - it 'returns the full api request url' do - expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token' - end - end - - describe '#request_path' do - it 'prepends the api path' do - expect(request.request_path('/users')).to eq '/api/v4/users' - end - - it 'adds the personal access token' do - expect(request.request_path('/users', personal_access_token: 'token')) - .to eq '/api/v4/users?private_token=token' - end - - it 'adds the oauth access token' do - expect(request.request_path('/users', oauth_access_token: 'otoken')) - .to eq '/api/v4/users?access_token=otoken' - end - - it 'respects query parameters' do - expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1' - expect(request.request_path('/users?page=1', personal_access_token: 'token')) - .to eq '/api/v4/users?page=1&private_token=token' - end - - it 'uses a different api version' do - expect(request.request_path('/users', version: 'other_version')).to eq '/api/other_version/users' - end - end -end -- cgit v1.2.1 From dfb6965b79df788776d9c18baf24fa11e847851a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 6 Jun 2018 19:44:35 +0200 Subject: Automate the basic API tests in a QA scenario MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- qa/qa/specs/features/api/basics_spec.rb | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 qa/qa/specs/features/api/basics_spec.rb diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb new file mode 100644 index 00000000000..1d7f9d6a03c --- /dev/null +++ b/qa/qa/specs/features/api/basics_spec.rb @@ -0,0 +1,61 @@ +require 'securerandom' + +module QA + feature 'API basics', :core do + before(:context) do + @api_client = Runtime::API::Client.new(:gitlab) + end + + let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" } + let(:sanitized_project_path) { CGI.escape("#{Runtime::User.name}/#{project_name}") } + + scenario 'user creates a project with a file and deletes them afterwards' do + create_project_request = Runtime::API::Request.new(@api_client, '/projects') + post create_project_request.url, path: project_name, name: project_name + + expect_status(201) + expect(json_body).to match( + a_hash_including(name: project_name, path: project_name) + ) + + create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md") + post create_file_request.url, branch: 'master', content: 'Hello world', commit_message: 'Add README.md' + + expect_status(201) + expect(json_body).to match( + a_hash_including(branch: 'master', file_path: 'README.md') + ) + + get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: 'master') + get get_file_request.url + + expect_status(200) + expect(json_body).to match( + a_hash_including( + ref: 'master', + file_path: 'README.md', file_name: 'README.md', + encoding: 'base64', content: 'SGVsbG8gd29ybGQ=' + ) + ) + + delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: 'master', commit_message: 'Remove README.md') + delete delete_file_request.url + + expect_status(204) + + get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree") + get get_tree_request.url + + expect_status(200) + expect(json_body).to eq([]) + + delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}") + delete delete_project_request.url + + expect_status(202) + expect(json_body).to match( + a_hash_including(message: '202 Accepted') + ) + end + end +end -- cgit v1.2.1 From 0665a8f730e19e180f2b4d240ca7e93408d61b12 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 31 May 2018 20:32:36 +0200 Subject: Enable mapping to nil in enums Enum in Rails 5 does not map nil values - IOW nil value remains nil, even if there is a key with nil value in the enum definition. This commit overrides the underlying Enum methods so nil value is still mapped. This solution is far from being ideal: it uses dynamic definition of methods which introduces more magic/confusion into the codebase. It would be better to get rid of the nil value in enums. --- app/models/ci/pipeline.rb | 16 +++------------- app/models/commit_status.rb | 10 ++-------- app/models/concerns/enum_with_nil.rb | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 app/models/concerns/enum_with_nil.rb diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index eecd86349e4..0878356e87a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -8,6 +8,7 @@ module Ci include Gitlab::OptimisticLocking include Gitlab::Utils::StrongMemoize include AtomicInternalId + include EnumWithNil belongs_to :project, inverse_of: :pipelines belongs_to :user @@ -54,7 +55,7 @@ module Ci after_create :keep_around_commits, unless: :importing? - enum source: { + enum_with_nil source: { unknown: nil, push: 1, web: 2, @@ -64,7 +65,7 @@ module Ci external: 6 } - enum config_source: { + enum_with_nil config_source: { unknown_source: nil, repository_source: 1, auto_devops_source: 2 @@ -599,17 +600,6 @@ module Ci @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a end - # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. - # They always return `false`. - # These methods overwrite autogenerated ones to return correct results. - def unknown? - Gitlab.rails5? ? source.nil? : super - end - - def unknown_source? - Gitlab.rails5? ? config_source.nil? : super - end - private def ci_yaml_from_repo diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index a7d05722287..97516079b66 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -3,6 +3,7 @@ class CommitStatus < ActiveRecord::Base include Importable include AfterCommitQueue include Presentable + include EnumWithNil self.table_name = 'ci_builds' @@ -39,7 +40,7 @@ class CommitStatus < ActiveRecord::Base scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } scope :after_stage, -> (index) { where('stage_idx > ?', index) } - enum failure_reason: { + enum_with_nil failure_reason: { unknown_failure: nil, script_failure: 1, api_failure: 2, @@ -190,11 +191,4 @@ class CommitStatus < ActiveRecord::Base v =~ /\d+/ ? v.to_i : v end end - - # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. - # They always return `false`. - # This method overwrites the autogenerated one to return correct result. - def unknown_failure? - Gitlab.rails5? ? failure_reason.nil? : super - end end diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb new file mode 100644 index 00000000000..6b37903da20 --- /dev/null +++ b/app/models/concerns/enum_with_nil.rb @@ -0,0 +1,33 @@ +module EnumWithNil + extend ActiveSupport::Concern + + included do + def self.enum_with_nil(definitions) + # use original `enum` to auto-define all methods + enum(definitions) + + # override auto-defined methods only for the + # key which uses nil value + definitions.each do |name, values| + next unless key_with_nil = values.key(nil) + + # E.g. for enum_with_nil failure_reason: { unknown_failure: nil } + # this overrides auto-generated method `unknown_failure?` + define_method("#{key_with_nil}?") do + Gitlab.rails5? ? self[name].nil? : super() + end + + # E.g. for enum_with_nil failure_reason: { unknown_failure: nil } + # this overrides auto-generated method `failure_reason` + define_method(name) do + orig = super() + + return orig unless Gitlab.rails5? + return orig unless orig.nil? + + self.class.public_send(name.to_s.pluralize).key(nil) # rubocop:disable GitlabSecurity/PublicSend + end + end + end + end +end -- cgit v1.2.1 From dd4e6bb9262c9ab0f1f91913e1ac103a1dc82b7e Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Wed, 6 Jun 2018 15:01:47 -0400 Subject: Update vendored ADO template with adding of license management --- vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 27e8245185e..0d58a00482a 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -10,6 +10,7 @@ # Test jobs may be disabled by setting environment variables: # * test: TEST_DISABLED # * code_quality: CODE_QUALITY_DISABLED +# * license_management: LICENSE_MANAGEMENT_DISABLED # * performance: PERFORMANCE_DISABLED # * sast: SAST_DISABLED # * dependency_scanning: DEPENDENCY_SCANNING_DISABLED @@ -108,6 +109,22 @@ code_quality: variables: - $CODE_QUALITY_DISABLED +license_management: + image: docker:stable + variables: + DOCKER_DRIVER: overlay2 + allow_failure: true + services: + - docker:stable-dind + script: + - setup_docker + - license_management + artifacts: + paths: [gl-license-management-report.json] + except: + variables: + - $LICENSE_MANAGEMENT_DISABLED + performance: stage: performance image: docker:stable @@ -462,6 +479,18 @@ rollout 100%: "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code } + function license_management() { + if echo $GITLAB_FEATURES |grep license_management > /dev/null ; then + # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" + LICENSE_MANAGEMENT_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') + + docker run --volume "$PWD:/code" \ + "registry.gitlab.com/gitlab-org/security-products/license-management:$LICENSE_MANAGEMENT_VERSION" analyze /code + else + echo "License management is not available in your subscription" + fi + } + function sast() { case "$CI_SERVER_VERSION" in *-ee) -- cgit v1.2.1 From 119b128ec8415a074a73b73a7878717779c6e0f3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 6 Jun 2018 20:39:50 +0100 Subject: Fix spec --- app/views/shared/_label.html.haml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index c34d63ba5c3..5eec7b02b54 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -14,14 +14,13 @@ - if @project %li.inline .label-badge.label-badge-gray= label.model_name.human.capitalize - - if can?(current_user, :admin_label, label) - - if @project - %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), - dom_id: dom_id(label), type: label.type } } - %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') } - = sprite_icon('star-o') - %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') } - = sprite_icon('star') + - if can?(current_user, :admin_label, @project) + %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label), + dom_id: dom_id(label), type: label.type } } + %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') } + = sprite_icon('star-o') + %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') } + = sprite_icon('star') %li.inline = link_to edit_label_path(label), class: 'btn btn-transparent label-action', aria_label: 'Edit label' do = sprite_icon('pencil') -- cgit v1.2.1 From 90545b552ba666c6b7dc800e7ed3b64f5619837a Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Wed, 6 Jun 2018 12:52:06 -0700 Subject: 47432 Add label-light class to Kubernetes creation form labels --- app/views/projects/clusters/user/_form.html.haml | 12 ++++++------ app/views/projects/clusters/user/_show.html.haml | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml index 2e92524ce8f..db57da99ec7 100644 --- a/app/views/projects/clusters/user/_form.html.haml +++ b/app/views/projects/clusters/user/_form.html.haml @@ -1,27 +1,27 @@ = form_for @cluster, url: user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field| = form_errors(@cluster) .form-group - = field.label :name, s_('ClusterIntegration|Kubernetes cluster name') + = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light' = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name') .form-group - = field.label :environment_scope, s_('ClusterIntegration|Environment scope') + = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light' = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field| .form-group - = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL') + = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light' = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL') .form-group - = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate') + = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light' = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)') .form-group - = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token') + = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light' = platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off' .form-group - = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)') + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light' = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace') .form-group diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml index 77d7a055474..4d117f435dc 100644 --- a/app/views/projects/clusters/user/_show.html.haml +++ b/app/views/projects/clusters/user/_show.html.haml @@ -1,20 +1,20 @@ = form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| = form_errors(@cluster) .form-group - = field.label :name, s_('ClusterIntegration|Kubernetes cluster name') + = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light' = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name') = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field| .form-group - = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL') + = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light' = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL') .form-group - = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate') + = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light' = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)') .form-group - = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token') + = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light' .input-group = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off' %span.input-group-append.clipboard-addon @@ -23,7 +23,7 @@ = s_('ClusterIntegration|Show') .form-group - = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)') + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light' = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace') .form-group -- cgit v1.2.1 From 86d39016a26f73e82437d38fcf723677b97c1c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Wed, 6 Jun 2018 21:07:31 +0200 Subject: Moving rev-list lfs options to Lfschanges --- lib/gitlab/git/lfs_changes.rb | 4 +++- lib/gitlab/git/rev_list.rb | 4 ++-- spec/lib/gitlab/git/rev_list_spec.rb | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb index b9e5cf258f4..320b2ad007b 100644 --- a/lib/gitlab/git/lfs_changes.rb +++ b/lib/gitlab/git/lfs_changes.rb @@ -39,7 +39,9 @@ module Gitlab end def git_all_pointers - rev_list.all_objects(require_path: true) do |object_ids| + params = { options: ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"], require_path: true } + + rev_list.all_objects(params) do |object_ids| Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids) end end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index 79544ccf13d..33b641a090b 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -37,9 +37,9 @@ module Gitlab get_objects(opts, &lazy_block) end - def all_objects(require_path: nil, &lazy_block) + def all_objects(options: [], require_path: nil, &lazy_block) get_objects(including: :all, - options: ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"], + options: options, require_path: require_path, &lazy_block) end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 70e90659b0f..95dc47e2a00 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -88,7 +88,7 @@ describe Gitlab::Git::RevList do context '#all_objects' do it 'fetches list of all pushed objects using rev-list' do - stub_popen_rev_list('--all', '--objects', '--filter=blob:limit=200', output: "sha1\nsha2") + stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2") expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2]) end -- cgit v1.2.1 From be2486152fd9c38619a3aa65241c0643bde39a71 Mon Sep 17 00:00:00 2001 From: Sam Beckham Date: Wed, 6 Jun 2018 19:58:22 +0000 Subject: Resolve "Project homepage > Plus dropdown on mobile screens" --- app/assets/stylesheets/framework/tables.scss | 1 + app/views/projects/tree/_tree_header.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index a10bd1544c5..10c23f6c407 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -1,5 +1,6 @@ .table-holder { margin: 0; + overflow: auto; } table { diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 4fc1a284693..9d196075bf1 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -6,7 +6,7 @@ = render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true - if on_top_of_branch? - - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown' } + - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown', 'data-boundary': 'window' } - else - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } -- cgit v1.2.1 From 5d3abdf9a7c0260c708a46e2fd232b0490940f80 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jun 2018 00:47:53 -0700 Subject: Log response body to production_json.log when a controller responds with a 422 status We have a number of import errors occurring with 422 errors, and it's hard to determine why they are happening. This change will surface the errors in the log lines. Relates to #47365 --- app/controllers/application_controller.rb | 4 ++ changelogs/unreleased/sh-log-422-responses.yml | 6 +++ config/initializers/lograge.rb | 1 + spec/controllers/application_controller_spec.rb | 58 +++++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 changelogs/unreleased/sh-log-422-responses.yml diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bc60a0a02e8..041837c5410 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -91,6 +91,10 @@ class ApplicationController < ActionController::Base payload[:user_id] = logged_user.try(:id) payload[:username] = logged_user.try(:username) end + + if response.status == 422 && response.body.present? && response.content_type == 'application/json'.freeze + payload[:response] = response.body + end end # Controllers such as GitHttpController may use alternative methods diff --git a/changelogs/unreleased/sh-log-422-responses.yml b/changelogs/unreleased/sh-log-422-responses.yml new file mode 100644 index 00000000000..c7dfdbb703b --- /dev/null +++ b/changelogs/unreleased/sh-log-422-responses.yml @@ -0,0 +1,6 @@ +--- +title: Log response body to production_json.log when a controller responds with a + 422 status +merge_request: +author: +type: other diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb index 114c1cb512f..1cf8a24e98c 100644 --- a/config/initializers/lograge.rb +++ b/config/initializers/lograge.rb @@ -27,6 +27,7 @@ unless Sidekiq.server? gitaly_calls = Gitlab::GitalyClient.get_request_count payload[:gitaly_calls] = gitaly_calls if gitaly_calls > 0 + payload[:response] = event.payload[:response] if event.payload[:response] payload end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 683c57c96f8..773bf25ed44 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'spec_helper' describe ApplicationController do @@ -478,6 +479,63 @@ describe ApplicationController do end end + describe '#append_info_to_payload' do + controller(described_class) do + attr_reader :last_payload + + def index + render text: 'authenticated' + end + + def append_info_to_payload(payload) + super + + @last_payload = payload + end + end + + it 'does not log errors with a 200 response' do + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + + context '422 errors' do + it 'logs a response with a string' do + response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload[:response]).to eq('Hello world') + end + + it 'logs a response with an array' do + body = ['I want', 'my hat back'] + response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload[:response]).to eq(body) + end + + it 'does not log a string with an empty body' do + response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + + it 'does not log an HTML body' do + response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + end + end + describe '#access_denied' do controller(described_class) do def index -- cgit v1.2.1 From 22fb44f538994b1aba5e0f4871de3cf75cafee72 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Wed, 6 Jun 2018 15:20:25 -0500 Subject: Add changelog for bootstrap upgrade --- changelogs/unreleased/bootstrap-changelog.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/bootstrap-changelog.yml diff --git a/changelogs/unreleased/bootstrap-changelog.yml b/changelogs/unreleased/bootstrap-changelog.yml new file mode 100644 index 00000000000..fd58447769d --- /dev/null +++ b/changelogs/unreleased/bootstrap-changelog.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade GitLab from Bootstrap 3 to 4 +merge_request: +author: +type: other -- cgit v1.2.1 From 854c9636ec6aabd8941b31f0f2aa4e89c9c072ce Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Wed, 30 May 2018 12:12:42 +0200 Subject: Enforce UTF-8 encoding on user input in LogrageWithTimestamp formatter and filter out file content from logs --- .../45505-lograge_formatter_encoding.yml | 6 ++++++ config/application.rb | 2 ++ .../formatters/lograge_with_timestamp.rb | 17 +++++++++++++++++ spec/requests/api/commits_spec.rb | 22 ++++++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 changelogs/unreleased/45505-lograge_formatter_encoding.yml diff --git a/changelogs/unreleased/45505-lograge_formatter_encoding.yml b/changelogs/unreleased/45505-lograge_formatter_encoding.yml new file mode 100644 index 00000000000..02f4c152966 --- /dev/null +++ b/changelogs/unreleased/45505-lograge_formatter_encoding.yml @@ -0,0 +1,6 @@ +--- +title: Enforce UTF-8 encoding on user input in LogrageWithTimestamp formatter and + filter out file content from logs +merge_request: +author: +type: fixed diff --git a/config/application.rb b/config/application.rb index 1b575f1325d..d379d611074 100644 --- a/config/application.rb +++ b/config/application.rb @@ -70,6 +70,7 @@ module Gitlab # - Webhook URLs (:hook) # - Sentry DSN (:sentry_dsn) # - Deploy keys (:key) + # - File content from Web Editor (:content) config.filter_parameters += [/token$/, /password/, /secret/] config.filter_parameters += %i( certificate @@ -81,6 +82,7 @@ module Gitlab sentry_dsn trace variables + content ) # Enable escaping HTML in JSON. diff --git a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb index 1e1fdabca93..0014ce2689b 100644 --- a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb +++ b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb @@ -2,8 +2,12 @@ module Gitlab module GrapeLogging module Formatters class LogrageWithTimestamp + include Gitlab::EncodingHelper + def call(severity, datetime, _, data) time = data.delete :time + data[:params] = utf8_encode_values(data[:params]) if data.has_key?(:params) + attributes = { time: datetime.utc.iso8601(3), severity: severity, @@ -13,6 +17,19 @@ module Gitlab }.merge(data) ::Lograge.formatter.call(attributes) + "\n" end + + private + + def utf8_encode_values(data) + case data + when Hash + data.merge(data) { |k, v| utf8_encode_values(v) } + when Array + data.map { |v| utf8_encode_values(v) } + when String + encode_utf8(data) + end + end end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 8ad19e3f0f5..7e3277c4cab 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -247,6 +247,19 @@ describe API::Commits do ] } end + let!(:valid_utf8_c_params) do + { + branch: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 🦊' + } + ] + } + end it 'a new file in project repo' do post api(url, user), valid_c_params @@ -257,6 +270,15 @@ describe API::Commits do expect(json_response['committer_email']).to eq(user.email) end + it 'a new file with utf8 chars in project repo' do + post api(url, user), valid_utf8_c_params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['title']).to eq(message) + expect(json_response['committer_name']).to eq(user.name) + expect(json_response['committer_email']).to eq(user.email) + end + it 'returns a 400 bad request if file exists' do post api(url, user), invalid_c_params -- cgit v1.2.1 From c6e26e24f1382392a2eb9cc462316e2646ca6a57 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 6 Jun 2018 21:30:18 +0100 Subject: Update CHANGELOG.md for 10.8.4 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec92829f7d1..0d843c3f318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.8.4 (2018-06-06) + +- No changes. + ## 10.8.3 (2018-05-30) ### Fixed (4 changes) -- cgit v1.2.1 From ff582303a8340fdd4d7fc3c7fb32b07c4c2270fa Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 15:36:38 -0500 Subject: add custom card classes for autodevops settings --- app/assets/stylesheets/pages/settings_ci_cd.scss | 14 ++++---------- .../projects/settings/ci_cd/_autodevops_form.html.haml | 8 ++++---- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss index 4366a348f69..777fdb3581e 100644 --- a/app/assets/stylesheets/pages/settings_ci_cd.scss +++ b/app/assets/stylesheets/pages/settings_ci_cd.scss @@ -17,17 +17,11 @@ min-height: 60px; } -.auto-devops-settings { - .card, - .card-body { - border-radius: $card-border-radius; - } +.auto-devops-card { + margin-bottom: $gl-vert-padding; - .card { - margin-bottom: $gl-vert-padding; - } - - .card-body { + > .card-body { + border-radius: $card-border-radius; padding: $gl-padding $gl-padding-24; } } diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index ceb3597b5de..12a60400a42 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -10,7 +10,7 @@ %p.auto-devops-warning-message.settings-message.text-center = message.html_safe = f.fields_for :auto_devops_attributes, @auto_devops do |form| - .card + .card.auto-devops-card .card-body .form-check = form.radio_button :enabled, 'true', class: 'form-check-input', data: { hide_extra_settings: false } @@ -19,7 +19,7 @@ .form-text.text-muted = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted } - .card + .card.auto-devops-card .card-body .form-check = form.radio_button :enabled, '', class: 'form-check-input', data: { hide_extra_settings: false } @@ -28,7 +28,7 @@ .form-text.text-muted = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted } - .card.js-extra-settings{ class: form.object&.enabled == false ? 'hidden' : nil } + .card.auto-devops-card.js-extra-settings{ class: form.object&.enabled == false ? 'hidden' : nil } .card-body.bg-light = form.label :domain do %strong= _('Domain') @@ -54,7 +54,7 @@ %strong= s_('CICD|Automatic deployment to staging, manual deployment to production') = link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank' - .card + .card.auto-devops-card .card-body .form-check = form.radio_button :enabled, 'false', class: 'form-check-input', data: { hide_extra_settings: true } -- cgit v1.2.1 From af261c1b314058c3aefef3377b9f7f8a20701481 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 15:48:54 -0500 Subject: attach a single event handler to detect radio button changes instead of one per input --- .../pages/projects/settings/ci_cd/show/index.js | 16 ++++++++-------- .../projects/settings/ci_cd/_autodevops_form.html.haml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 6d227177cb4..152f76ff7af 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -24,15 +24,15 @@ document.addEventListener('DOMContentLoaded', () => { }); // hide extra auto devops settings based on data-attributes - const autoDevOpsSettings = document.querySelectorAll('input[data-hide-extra-settings]'); + const autoDevOpsSettings = document.querySelector('.js-auto-devops-settings'); const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); - autoDevOpsSettings.forEach(input => { - input.addEventListener('click', () => - autoDevOpsExtraSettings.classList.toggle( - 'hidden', - input.dataset.hideExtraSettings === 'true', - ), - ); + autoDevOpsSettings.addEventListener('click', event => { + const targetData = event.target && event.target.dataset; + if (targetData.hideExtraSettings === 'true') { + autoDevOpsExtraSettings.classList.add('hidden'); + } else if (targetData.hideExtraSettings === 'false') { + autoDevOpsExtraSettings.classList.remove('hidden'); + } }); }); diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 12a60400a42..c3e10a93456 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -2,7 +2,7 @@ .col-lg-12 = form_for @project, url: project_settings_ci_cd_path(@project) do |f| = form_errors(@project) - %fieldset.builds-feature.auto-devops-settings + %fieldset.builds-feature.js-auto-devops-settings .form-group - message = auto_devops_warning_message(@project) - ci_file_formatted = '.gitlab-ci.yml'.html_safe -- cgit v1.2.1 From 1d81d15787762f8d5b7fa38e0b48201cff268385 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 6 Jun 2018 14:36:35 -0700 Subject: Fix styling of delete user button in dropdown --- app/views/admin/users/_user.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 5e176b61d68..b2163ee85fa 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -38,7 +38,7 @@ %li.divider - if user.can_be_removed? %li - %button.delete-user-button.btn.btn-danger{ data: { toggle: 'modal', + %button.delete-user-button.btn.text-danger{ data: { toggle: 'modal', target: '#delete-user-modal', delete_user_url: admin_user_path(user), block_user_url: block_admin_user_path(user), @@ -47,7 +47,7 @@ = s_('AdminUsers|Delete user') %li - %button.delete-user-button.btn.btn-danger{ data: { toggle: 'modal', + %button.delete-user-button.btn.text-danger{ data: { toggle: 'modal', target: '#delete-user-modal', delete_user_url: admin_user_path(user, hard_delete: true), block_user_url: block_admin_user_path(user), -- cgit v1.2.1 From 0cb4bb1af8fb5b5c71a9b547114563ccec355979 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 16:45:23 -0500 Subject: rely on presense or absense of data attribute instead of coerced boolean strings --- .../javascripts/pages/projects/settings/ci_cd/show/index.js | 11 ++++++----- app/views/projects/settings/ci_cd/_autodevops_form.html.haml | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 152f76ff7af..37ef77c8e43 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -28,11 +28,12 @@ document.addEventListener('DOMContentLoaded', () => { const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); autoDevOpsSettings.addEventListener('click', event => { - const targetData = event.target && event.target.dataset; - if (targetData.hideExtraSettings === 'true') { - autoDevOpsExtraSettings.classList.add('hidden'); - } else if (targetData.hideExtraSettings === 'false') { - autoDevOpsExtraSettings.classList.remove('hidden'); + const target = event.target; + if (target.classList.contains('js-toggle-extra-settings')) { + autoDevOpsExtraSettings.classList.toggle( + 'hidden', + !!(target.dataset && target.dataset.hideExtraSettings), + ); } }); }); diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index c3e10a93456..4359362bb05 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -13,7 +13,7 @@ .card.auto-devops-card .card-body .form-check - = form.radio_button :enabled, 'true', class: 'form-check-input', data: { hide_extra_settings: false } + = form.radio_button :enabled, 'true', class: 'form-check-input js-toggle-extra-settings' = form.label :enabled_true, class: 'form-check-label' do %strong= s_('CICD|Enable Auto DevOps') .form-text.text-muted @@ -22,7 +22,7 @@ .card.auto-devops-card .card-body .form-check - = form.radio_button :enabled, '', class: 'form-check-input', data: { hide_extra_settings: false } + = form.radio_button :enabled, '', class: 'form-check-input js-toggle-extra-settings' = form.label :enabled_, class: 'form-check-label' do %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" } .form-text.text-muted @@ -57,7 +57,7 @@ .card.auto-devops-card .card-body .form-check - = form.radio_button :enabled, 'false', class: 'form-check-input', data: { hide_extra_settings: true } + = form.radio_button :enabled, 'false', class: 'form-check-input js-toggle-extra-settings', data: { hide_extra_settings: true } = form.label :enabled_false, class: 'form-check-label' do %strong= s_('CICD|Disable Auto DevOps') .form-text.text-muted -- cgit v1.2.1 From dde7b97d0b2e562bf8e02e5dcf9e9b71fb601f79 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jun 2018 14:39:21 -0700 Subject: Bump grape-path-helpers to solve a bug in GrapePathHelpers::NamedRouteMatcher If you include this helper with a class that also implements `method_missing`, this would result in AbstractController::DoubleRenderError. See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19487#note_79096902. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 334895351ac..2e776656d7a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -359,7 +359,7 @@ GEM grape-entity (0.7.1) activesupport (>= 4.0) multi_json (>= 1.3.2) - grape-path-helpers (1.0.2) + grape-path-helpers (1.0.4) activesupport (~> 4) grape (~> 1.0) rake (~> 12) -- cgit v1.2.1 From 8eb3eb861b14a06c09f0c46ac24515c992921aed Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Wed, 6 Jun 2018 13:03:57 -0700 Subject: Fix styling of Remove Kubernetes cluster integration section --- app/views/projects/clusters/_advanced_settings.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/clusters/_advanced_settings.html.haml b/app/views/projects/clusters/_advanced_settings.html.haml index e9bdc54364b..243e8cd9ba0 100644 --- a/app/views/projects/clusters/_advanced_settings.html.haml +++ b/app/views/projects/clusters/_advanced_settings.html.haml @@ -7,8 +7,8 @@ - link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer') = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke } - .card.form-group - %label.text-danger + .sub-section.form-group + %h4.text-danger = s_('ClusterIntegration|Remove Kubernetes cluster integration') %p = s_("ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster.") -- cgit v1.2.1 From 8374d5a9c6923d1660e60e3a6d15be1ed1b69d12 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 6 Jun 2018 17:55:18 -0500 Subject: Fix group member information not collapsing in a single line --- app/views/projects/project_members/_groups.html.haml | 2 +- app/views/shared/members/_group.html.haml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/projects/project_members/_groups.html.haml b/app/views/projects/project_members/_groups.html.haml index 128f52ff648..3f05e06b0c6 100644 --- a/app/views/projects/project_members/_groups.html.haml +++ b/app/views/projects/project_members/_groups.html.haml @@ -3,5 +3,5 @@ Groups with access to %strong= @project.name %span.badge.badge-pill= group_links.size - %ul.content-list + %ul.content-list.members-list = render partial: 'shared/members/group', collection: group_links, as: :group_link diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml index 67b8843a27f..d0b492b43f3 100644 --- a/app/views/shared/members/_group.html.haml +++ b/app/views/shared/members/_group.html.haml @@ -5,16 +5,16 @@ %li.member.group_member{ id: dom_id } %span.list-item-name = group_icon(group, class: "avatar s40", alt: '') - %strong - = link_to group.full_name, group_path(group) - .cgray - Given access #{time_ago_with_tooltip(group_link.created_at)} - - if group_link.expires? - · - %span{ class: ('text-warning' if group_link.expires_soon?) } - Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)} + .user-info + = link_to group.full_name, group_path(group), class: 'member' + .cgray + Given access #{time_ago_with_tooltip(group_link.created_at)} + - if group_link.expires? + · + %span{ class: ('text-warning' if group_link.expires_soon?) } + Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)} .controls.member-controls - = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form' do + = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form form-group row append-right-5' do = hidden_field_tag "group_link[group_access]", group_link.group_access .member-form-control.dropdown.append-right-5 %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", -- cgit v1.2.1 From 19adcaf1bf36828a66e519e3c87b3f6a65eae261 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Thu, 7 Jun 2018 00:39:49 -0400 Subject: Update docs --- doc/user/project/integrations/prometheus_library/nginx_ingress.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md index 590b1c4275a..a1db79538a4 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -14,7 +14,7 @@ GitLab has support for automatically detecting and monitoring the Kubernetes NGI | ---- | ----- | | Throughput (req/sec) | sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code) | | Latency (ms) | avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}) | -| HTTP Error Rate (HTTP Errors / sec) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) | +| HTTP Error Rate (%) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100 | ## Configuring NGINX ingress monitoring -- cgit v1.2.1 From 164dbc6b77558e29fd9a1553048d314457dc6990 Mon Sep 17 00:00:00 2001 From: Annabel Gray Date: Thu, 7 Jun 2018 07:37:02 +0000 Subject: Resolve "Introduce new navigation themes in GitLab 11.0" --- app/assets/stylesheets/framework/gitlab_theme.scss | 162 ++++++++++++++------- app/assets/stylesheets/framework/header.scss | 2 + app/assets/stylesheets/framework/variables.scss | 32 ++++ .../stylesheets/pages/profiles/preferences.scss | 75 +++++----- app/views/layouts/devise.html.haml | 2 +- app/views/layouts/devise_empty.html.haml | 2 +- app/views/profiles/preferences/show.html.haml | 8 +- .../unreleased/43597-new-navigation-themes.yml | 5 + config/gitlab.yml.example | 11 +- doc/development/fe_guide/style_guide_scss.md | 10 +- lib/gitlab/themes.rb | 15 +- spec/helpers/preferences_helper_spec.rb | 6 +- spec/lib/gitlab/themes_spec.rb | 8 +- 13 files changed, 216 insertions(+), 122 deletions(-) create mode 100644 changelogs/unreleased/43597-new-navigation-themes.yml diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss index 14dd3879bdc..b40d02f381a 100644 --- a/app/assets/stylesheets/framework/gitlab_theme.scss +++ b/app/assets/stylesheets/framework/gitlab_theme.scss @@ -3,26 +3,26 @@ */ @mixin gitlab-theme( - $color-100, - $color-200, - $color-500, - $color-700, - $color-800, - $color-900, + $location-badge-color, + $search-and-nav-links, + $active-tab-border, + $border-and-box-shadow, + $sidebar-text, + $nav-svg-color, $color-alternate ) { // Header .navbar-gitlab { - background-color: $color-900; + background-color: $nav-svg-color; .navbar-collapse { - color: $color-200; + color: $search-and-nav-links; } .container-fluid { .navbar-toggler { - border-left: 1px solid lighten($color-700, 10%); + border-left: 1px solid lighten($border-and-box-shadow, 10%); } } @@ -31,40 +31,40 @@ > li { > a:hover, > a:focus { - background-color: rgba($color-200, 0.2); + background-color: rgba($search-and-nav-links, 0.2); } &.active > a, &.dropdown.show > a { - color: $color-900; + color: $nav-svg-color; background-color: $color-alternate; } &.line-separator { - border-left: 1px solid rgba($color-200, 0.2); + border-left: 1px solid rgba($search-and-nav-links, 0.2); } } } .navbar-sub-nav { - color: $color-200; + color: $search-and-nav-links; } .nav { > li { - color: $color-200; + color: $search-and-nav-links; > a { &.header-user-dropdown-toggle { .header-user-avatar { - border-color: $color-200; + border-color: $search-and-nav-links; } } &:hover, &:focus { @include media-breakpoint-up(sm) { - background-color: rgba($color-200, 0.2); + background-color: rgba($search-and-nav-links, 0.2); } svg { @@ -75,12 +75,12 @@ &.active > a, &.dropdown.show > a { - color: $color-900; + color: $nav-svg-color; background-color: $color-alternate; &:hover { svg { - fill: $color-900; + fill: $nav-svg-color; } } } @@ -88,7 +88,7 @@ .impersonated-user, .impersonated-user:hover { svg { - fill: $color-900; + fill: $nav-svg-color; } } } @@ -99,34 +99,34 @@ > a { &:hover, &:focus { - background-color: rgba($color-200, 0.2); + background-color: rgba($search-and-nav-links, 0.2); } } } .search { form { - background-color: rgba($color-200, 0.2); + background-color: rgba($search-and-nav-links, 0.2); &:hover { - background-color: rgba($color-200, 0.3); + background-color: rgba($search-and-nav-links, 0.3); } } .location-badge { - color: $color-100; - background-color: rgba($color-200, 0.1); - border-right: 1px solid $color-800; + color: $location-badge-color; + background-color: rgba($search-and-nav-links, 0.1); + border-right: 1px solid $sidebar-text; } .search-input::placeholder { - color: rgba($color-200, 0.8); + color: rgba($search-and-nav-links, 0.8); } .search-input-wrap { .search-icon, .clear-icon { - fill: rgba($color-200, 0.8); + fill: rgba($search-and-nav-links, 0.8); } } @@ -141,38 +141,34 @@ .search-input-wrap { .search-icon { - fill: rgba($color-200, 0.8); + fill: rgba($search-and-nav-links, 0.8); } } } } - .btn-sign-in { - background-color: $color-100; - color: $color-900; - } // Sidebar .nav-sidebar li.active { - box-shadow: inset 4px 0 0 $color-700; + box-shadow: inset 4px 0 0 $border-and-box-shadow; > a { - color: $color-800; + color: $sidebar-text; } svg { - fill: $color-800; + fill: $sidebar-text; } } .sidebar-top-level-items > li.active .badge.badge-pill { - color: $color-800; + color: $sidebar-text; } .nav-links li { &.active a, a.active { - border-bottom: 2px solid $color-500; + border-bottom: 2px solid $active-tab-border; .badge.badge-pill { font-weight: $gl-font-weight-bold; @@ -181,27 +177,27 @@ } .branch-header-title { - color: $color-700; + color: $border-and-box-shadow; } .ide-file-list .file.file-active { - color: $color-700; + color: $border-and-box-shadow; } .ide-sidebar-link { &.active { - color: $color-700; - box-shadow: inset 3px 0 $color-700; + color: $border-and-box-shadow; + box-shadow: inset 3px 0 $border-and-box-shadow; &.is-right { - box-shadow: inset -3px 0 $color-700; + box-shadow: inset -3px 0 $border-and-box-shadow; } } } } body { - &.ui_indigo { + &.ui-indigo { @include gitlab-theme( $indigo-100, $indigo-200, @@ -213,19 +209,19 @@ body { ); } - &.ui_dark { + &.ui-light-indigo { @include gitlab-theme( - $theme-gray-100, - $theme-gray-200, - $theme-gray-500, - $theme-gray-700, - $theme-gray-800, - $theme-gray-900, + $indigo-100, + $indigo-200, + $indigo-500, + $indigo-500, + $indigo-700, + $indigo-700, $white-light ); } - &.ui_blue { + &.ui-blue { @include gitlab-theme( $theme-blue-100, $theme-blue-200, @@ -237,7 +233,19 @@ body { ); } - &.ui_green { + &.ui-light-blue { + @include gitlab-theme( + $theme-light-blue-100, + $theme-light-blue-200, + $theme-light-blue-500, + $theme-light-blue-500, + $theme-light-blue-700, + $theme-light-blue-700, + $white-light + ); + } + + &.ui-green { @include gitlab-theme( $theme-green-100, $theme-green-200, @@ -249,7 +257,55 @@ body { ); } - &.ui_light { + &.ui-light-green { + @include gitlab-theme( + $theme-green-100, + $theme-green-200, + $theme-green-500, + $theme-green-500, + $theme-light-green-700, + $theme-light-green-700, + $white-light + ); + } + + &.ui-red { + @include gitlab-theme( + $theme-red-100, + $theme-red-200, + $theme-red-500, + $theme-red-700, + $theme-red-800, + $theme-red-900, + $white-light + ); + } + + &.ui-light-red { + @include gitlab-theme( + $theme-light-red-100, + $theme-light-red-200, + $theme-light-red-500, + $theme-light-red-500, + $theme-light-red-700, + $theme-light-red-700, + $white-light + ); + } + + &.ui-dark { + @include gitlab-theme( + $theme-gray-100, + $theme-gray-200, + $theme-gray-500, + $theme-gray-700, + $theme-gray-800, + $theme-gray-900, + $white-light + ); + } + + &.ui-light { @include gitlab-theme( $theme-gray-900, $theme-gray-700, diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 094134b63b0..f2ac77819d5 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -437,6 +437,8 @@ } .btn-sign-in { + background-color: $indigo-100; + color: $indigo-900; margin-top: 3px; font-weight: $gl-font-weight-bold; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 946223cfff0..e6e74d55f64 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -117,6 +117,15 @@ $theme-blue-800: #25496e; $theme-blue-900: #1a3652; $theme-blue-950: #0f2235; +$theme-light-blue-50: #f2f7fc; +$theme-light-blue-100: #ebf1f7; +$theme-light-blue-200: #c9dcf2; +$theme-light-blue-300: #83abd4; +$theme-light-blue-400: #4d86bf; +$theme-light-blue-500: #367cc2; +$theme-light-blue-600: #3771ab; +$theme-light-blue-700: #2261a1; + $theme-green-50: #f2faf6; $theme-green-100: #e4f3ea; $theme-green-200: #c0dfcd; @@ -129,6 +138,29 @@ $theme-green-800: #145d33; $theme-green-900: #0d4524; $theme-green-950: #072d16; +$theme-light-green-700: #156b39; + +$theme-red-50: #fcf4f2; +$theme-red-100: #fae9e6; +$theme-red-200: #ebcac5; +$theme-red-300: #d99b91; +$theme-red-400: #b0655a; +$theme-red-500: #ad4a3b; +$theme-red-600: #9e4133; +$theme-red-700: #912f20; +$theme-red-800: #78291d; +$theme-red-900: #691a16; +$theme-red-950: #36140f; + +$theme-light-red-50: #fff6f5; +$theme-light-red-100: #fae2de; +$theme-light-red-200: #f7d5d0; +$theme-light-red-300: #d9796a; +$theme-light-red-400: #cf604e; +$theme-light-red-500: #c24b38; +$theme-light-red-600: #b03927; +$theme-light-red-700: #a62e21; + $black: #000; $black-transparent: rgba(0, 0, 0, 0.3); $almost-black: #242424; diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index 68d40b56133..babe81cb0f7 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -1,25 +1,3 @@ -@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) { - .one { - background-color: $color-1; - border-top-left-radius: $border-radius-default; - } - - .two { - background-color: $color-2; - border-top-right-radius: $border-radius-default; - } - - .three { - background-color: $color-3; - border-bottom-left-radius: $border-radius-default; - } - - .four { - background-color: $color-4; - border-bottom-right-radius: $border-radius-default; - } -} - .multi-file-editor-options { label { margin-right: 20px; @@ -38,44 +16,61 @@ .application-theme { label { - margin-right: 20px; + margin: 0 $gl-padding $gl-padding 0; text-align: center; } .preview { font-size: 0; - margin-bottom: 10px; + height: 48px; + border-radius: 4px; + min-width: 135px; + margin-bottom: $gl-padding-8; + + &.ui-indigo { + background-color: $indigo-900; + } + + &.ui-light-indigo { + background-color: $indigo-700; + } - &.indigo { - @include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500); + &.ui-blue { + background-color: $theme-blue-900; } - &.dark { - @include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600); + &.ui-light-blue { + background-color: $theme-light-blue-700; } - &.light { - @include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100); + &.ui-green { + background-color: $theme-green-900; } - &.blue { - @include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500); + &.ui-light-green { + background-color: $theme-light-green-700; } - &.green { - @include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500); + &.ui-red { + background-color: $theme-red-900; + } + + &.ui-light-red { + background-color: $theme-light-red-700; + } + + &.ui-dark { + background-color: $theme-gray-900; + } + + &.ui-light { + background-color: $theme-gray-200; } } .preview-row { display: block; } - - .quadrant { - display: inline-block; - height: 50px; - width: 80px; - } } .syntax-theme { diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 82ca7252424..81f35615555 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,7 +1,7 @@ !!! 5 %html.devise-layout-html{ class: system_message_class } = render "layouts/head" - %body.ui_indigo.login-page.application.navless{ data: { page: body_data_page } } + %body.ui-indigo.login-page.application.navless{ data: { page: body_data_page } } .page-wrap = render "layouts/header/empty" .login-page-broadcast diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml index adf90cb7667..52805e0da73 100644 --- a/app/views/layouts/devise_empty.html.haml +++ b/app/views/layouts/devise_empty.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en", class: system_message_class } = render "layouts/head" - %body.ui_indigo.login-page.application.navless + %body.ui-indigo.login-page.application.navless = render "layouts/header/empty" = render "layouts/broadcast" .container.navless-container diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index e63e7772ba3..8f1078bd41d 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -9,13 +9,7 @@ .col-lg-8.application-theme - Gitlab::Themes.each do |theme| = label_tag do - .preview{ class: theme.name.downcase } - .preview-row - .quadrant.one - .quadrant.two - .preview-row - .quadrant.three - .quadrant.four + .preview{ class: theme.css_class } = f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id = theme.name diff --git a/changelogs/unreleased/43597-new-navigation-themes.yml b/changelogs/unreleased/43597-new-navigation-themes.yml new file mode 100644 index 00000000000..de703e46b3c --- /dev/null +++ b/changelogs/unreleased/43597-new-navigation-themes.yml @@ -0,0 +1,5 @@ +--- +title: Add additional theme color options +merge_request: +author: +type: changed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 7eb44b8059e..489dc8840e5 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -78,10 +78,15 @@ production: &base # username_changing_enabled: false # default: true - User can change her username/namespace ## Default theme ID ## 1 - Indigo - ## 2 - Dark - ## 3 - Light - ## 4 - Blue + ## 2 - Light Indigo + ## 3 - Blue + ## 4 - Light Blue ## 5 - Green + ## 6 - Light Green + ## 7 - Red + ## 8 - Light Red + ## 9 - Dark + ## 10 - Light # default_theme: 1 # default: 1 ## Automatic issue closing diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md index 655d94793dd..48eb6d0a7d6 100644 --- a/doc/development/fe_guide/style_guide_scss.md +++ b/doc/development/fe_guide/style_guide_scss.md @@ -216,12 +216,12 @@ If you want a line or set of lines to be ignored by the linter, you can use `// scss-lint:disable RuleName` ([more info][disabling-linters]): ```scss -// This lint rule is disabled because the class name comes from a gem. -// scss-lint:disable SelectorFormat -.ui_indigo { - background-color: #333; +// This lint rule is disabled because it is supported only in Chrome/Safari +// scss-lint:disable PropertySpelling +body { + text-decoration-skip: ink; } -// scss-lint:enable SelectorFormat +// scss-lint:enable PropertySpelling ``` Make sure a comment is added on the line above the `disable` rule, otherwise the diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb index d43eff5ba4a..9277b57f46f 100644 --- a/lib/gitlab/themes.rb +++ b/lib/gitlab/themes.rb @@ -12,11 +12,16 @@ module Gitlab # All available Themes THEMES = [ - Theme.new(1, 'Indigo', 'ui_indigo'), - Theme.new(2, 'Dark', 'ui_dark'), - Theme.new(3, 'Light', 'ui_light'), - Theme.new(4, 'Blue', 'ui_blue'), - Theme.new(5, 'Green', 'ui_green') + Theme.new(1, 'Indigo', 'ui-indigo'), + Theme.new(2, 'Light Indigo', 'ui-light-indigo'), + Theme.new(3, 'Blue', 'ui-blue'), + Theme.new(4, 'Light Blue', 'ui-light-blue'), + Theme.new(5, 'Green', 'ui-green'), + Theme.new(6, 'Light Green', 'ui-light-green'), + Theme.new(7, 'Red', 'ui-red'), + Theme.new(8, 'Light Red', 'ui-light-red'), + Theme.new(9, 'Dark', 'ui-dark'), + Theme.new(10, 'Light', 'ui-light') ].freeze # Convenience method to get a space-separated String of all the theme diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index c9d2ec8a4ae..9940656fb68 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -31,9 +31,9 @@ describe PreferencesHelper do describe '#user_application_theme' do context 'with a user' do it "returns user's theme's css_class" do - stub_user(theme_id: 3) + stub_user(theme_id: 10) - expect(helper.user_application_theme).to eq 'ui_light' + expect(helper.user_application_theme).to eq 'ui-light' end it 'returns the default when id is invalid' do @@ -41,7 +41,7 @@ describe PreferencesHelper do allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(1) - expect(helper.user_application_theme).to eq 'ui_indigo' + expect(helper.user_application_theme).to eq 'ui-indigo' end end diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index ecacea6bb35..af2f4568017 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -5,16 +5,16 @@ describe Gitlab::Themes, lib: true do it 'returns a space-separated list of class names' do css = described_class.body_classes - expect(css).to include('ui_indigo') - expect(css).to include(' ui_dark ') - expect(css).to include(' ui_blue') + expect(css).to include('ui-indigo') + expect(css).to include('ui-dark ') + expect(css).to include('ui-blue') end end describe '.by_id' do it 'returns a Theme by its ID' do expect(described_class.by_id(1).name).to eq 'Indigo' - expect(described_class.by_id(3).name).to eq 'Light' + expect(described_class.by_id(10).name).to eq 'Light' end end -- cgit v1.2.1 From 8b31e8c7b2ba8c6947f54994be6c6e7b5af409f6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 7 Jun 2018 09:49:45 +0200 Subject: Change EKS article yaml frontmatter --- doc/user/project/clusters/eks_and_gitlab/index.md | 11 +++++------ doc/user/project/clusters/index.md | 4 ++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md index f50615729dd..bd4d15dddcb 100644 --- a/doc/user/project/clusters/eks_and_gitlab/index.md +++ b/doc/user/project/clusters/eks_and_gitlab/index.md @@ -1,14 +1,13 @@ --- -redirect_from: 'https://docs.gitlab.com/ee/user/project/clusters/eks_and_gitlab/index.html' +author: Joshua Lambert +author_gitlab: joshlambert +level: intermediate +article_type: tutorial +date: 2018-06-05 --- # Connecting and deploying to an Amazon EKS cluster -> **[Article Type](../../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || -> **Level:** intermediate || -> **Author:** [Joshua Lambert](https://gitlab.com/joshlambert) || -> **Publication date:** 2018-06-05 - ## Introduction In this tutorial, we will show how easy it is to integrate an [Amazon EKS](https://aws.amazon.com/eks/) cluster with GitLab, and begin deploying applications. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 1efbe20b84f..e44ac6a3d01 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -398,6 +398,10 @@ containers. To use this integration, you should deploy to Kubernetes using the deployment variables above, ensuring any pods you create are labelled with `app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest! +## Read more + +- [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md) + [permissions]: ../../permissions.md [ee]: https://about.gitlab.com/products/ [Auto DevOps]: ../../../topics/autodevops/index.md -- cgit v1.2.1 From d3659575469bb8a7a5f6bd4ecb86de88d5c2672c Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Thu, 7 Jun 2018 09:55:31 +0200 Subject: Fix default wait argument in QA Pipelines spec --- qa/qa/page/project/pipeline/show.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index de849b3eee8..babc0079f3f 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -24,10 +24,10 @@ module QA::Page end end - def has_build?(name, status: :success, wait:) + def has_build?(name, status: :success, wait: nil) within('.pipeline-graph') do within('.ci-job-component', text: name) do - has_selector?(".ci-status-icon-#{status}", wait: wait) + has_selector?(".ci-status-icon-#{status}", { wait: wait }.compact) end end end -- cgit v1.2.1 From f27ed4f741d7c403de28177cbfa9a3f6978b23e5 Mon Sep 17 00:00:00 2001 From: Gilbert Roulot Date: Tue, 5 Jun 2018 17:33:02 +0200 Subject: Add documentation --- doc/topics/autodevops/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index fec575f263f..478b9c9c0c6 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -498,6 +498,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | | `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `code_quality` job. If the variable is present, the job will not be created. | +| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. | | `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `container_scanning` job. If the variable is present, the job will not be created. | -- cgit v1.2.1 From 69bf190c2a33484cf085409c4d7ed3975d0bfd33 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 7 Jun 2018 10:58:30 +0200 Subject: Correct docs that mirror push is in Core --- doc/workflow/repository_mirroring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md index aaddbe4fbf5..8c4e6ea8eab 100644 --- a/doc/workflow/repository_mirroring.md +++ b/doc/workflow/repository_mirroring.md @@ -228,7 +228,7 @@ backoff period. If the mirror fails (eg: branch diverged from upstream), the project's backoff period will be penalized each time it fails up to a maximum amount of time. -## Pushing to a remote repository **[STARTER]** +## Pushing to a remote repository >[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise Edition 8.7. [Moved to GitLab Community Edition][ce-18715] in 10.8. -- cgit v1.2.1 From fc099269b1e3ff6254648d8c0ad2d52455495b98 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 7 Jun 2018 11:12:51 +0200 Subject: Add artifacts expiry time for GitLab.com --- doc/user/gitlab_com/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 0c1cd113686..d054561d5f3 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -60,6 +60,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md). | Setting | GitLab.com | Default | | ----------- | ----------------- | ------------- | | Artifacts maximum size | 1G | 100M | +| Artifacts [expiry time](../../ci/yaml/README.md#artifacts-expire_in) | kept forever | deleted after 30 days unless otherwise specified | ## Repository size limit -- cgit v1.2.1 From c03386c3914feca56802e6f99bbd0fd08d269472 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Tue, 5 Jun 2018 18:31:32 +0100 Subject: Force Postgres to avoid trigram indexes when in a group When filtering issues with a search string in a group, we observed on GitLab.com that Postgres was using an inefficient query plan, preferring the (global) trigram indexes on description and title, rather than using a filter on the restricted set of issues within the group. Change the callers of the IssuableFinder to use a CTE in this case to fence the rest of the query from the LIKE filters, so that the optimiser is forced to perform the filter in the order we prefer. This will only force the use of a CTE when: 1. The use_cte_for_search params is truthy. 2. We are using Postgres. 3. We have passed the `search` param. The third item is important - searching issues using the search box does not use the finder in this way, but contructs a query and appends `full_search` to that. For some reason, this query does not suffer from the same issue. Currenly, we only pass this param when filtering issuables (issues or MRs) in a group context. --- app/controllers/concerns/issuable_collections.rb | 1 + app/finders/issuable_finder.rb | 34 ++++++++++++++++++---- .../46648-timeout-searching-group-issues.yml | 5 ++++ 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/46648-timeout-searching-group-issues.yml diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 98a8939a4d0..2ef2ee76855 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -95,6 +95,7 @@ module IssuableCollections elsif @group @filter_params[:group_id] = @group.id @filter_params[:include_subgroups] = true + @filter_params[:use_cte_for_search] = true end @filter_params.permit(finder_type.valid_params) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 3649e0fe560..5d5f72c4d86 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -23,6 +23,7 @@ # created_before: datetime # updated_after: datetime # updated_before: datetime +# use_cte_for_search: boolean # class IssuableFinder prepend FinderWithCrossProjectAccess @@ -54,6 +55,7 @@ class IssuableFinder sort state include_subgroups + use_cte_for_search ] end @@ -74,19 +76,21 @@ class IssuableFinder items = init_collection items = filter_items(items) - # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far - items = by_project(items) + # This has to be last as we may use a CTE as an optimization fence by + # passing the use_cte_for_search param + # https://www.postgresql.org/docs/current/static/queries-with.html + items = by_search(items) sort(items) end def filter_items(items) + items = by_project(items) items = by_scope(items) items = by_created_at(items) items = by_updated_at(items) items = by_state(items) items = by_group(items) - items = by_search(items) items = by_assignee(items) items = by_author(items) items = by_non_archived(items) @@ -107,7 +111,6 @@ class IssuableFinder # def count_by_state count_params = params.merge(state: nil, sort: nil) - labels_count = label_names.any? ? label_names.count : 1 finder = self.class.new(current_user, count_params) counts = Hash.new(0) @@ -116,6 +119,11 @@ class IssuableFinder # per issuable, so we have to count those in Ruby - which is bad, but still # better than performing multiple queries. # + # This does not apply when we are using a CTE for the search, as the labels + # GROUP BY is inside the subquery in that case, so we set labels_count to 1. + labels_count = label_names.any? ? label_names.count : 1 + labels_count = 1 if use_cte_for_search? + finder.execute.reorder(nil).group(:state).count.each do |key, value| counts[Array(key).last.to_sym] += value / labels_count end @@ -326,8 +334,24 @@ class IssuableFinder items end + def use_cte_for_search? + return false unless search + return false unless Gitlab::Database.postgresql? + + params[:use_cte_for_search] + end + def by_search(items) - search ? items.full_search(search) : items + return items unless search + + if use_cte_for_search? + cte = Gitlab::SQL::RecursiveCTE.new(klass.table_name) + cte << items + + items = klass.with(cte.to_arel).from(klass.table_name) + end + + items.full_search(search) end def by_iids(items) diff --git a/changelogs/unreleased/46648-timeout-searching-group-issues.yml b/changelogs/unreleased/46648-timeout-searching-group-issues.yml new file mode 100644 index 00000000000..54401edf5cc --- /dev/null +++ b/changelogs/unreleased/46648-timeout-searching-group-issues.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of group issues filtering on GitLab.com +merge_request: 19429 +author: +type: performance -- cgit v1.2.1 From 5484ad78691ab45f79f8b2cf8192cbca8fe7c143 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 6 Jun 2018 19:51:33 +0300 Subject: Enable hashed storage for new projects by default in development --- db/fixtures/development/04_project.rb | 4 ++++ db/fixtures/development/08_settings.rb | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 db/fixtures/development/08_settings.rb diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index 213c8bca639..51e69879c79 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -67,6 +67,10 @@ Sidekiq::Testing.inline! do skip_disk_validation: true } + if i % 2 == 0 + params[:storage_version] = Project::LATEST_STORAGE_VERSION + end + project = Projects::CreateService.new(User.first, params).execute # Seed-Fu runs this entire fixture in a transaction, so the `after_commit` # hook won't run until after the fixture is loaded. That is too late diff --git a/db/fixtures/development/08_settings.rb b/db/fixtures/development/08_settings.rb new file mode 100644 index 00000000000..141465c06cf --- /dev/null +++ b/db/fixtures/development/08_settings.rb @@ -0,0 +1,7 @@ +# We want to enable hashed storage for every new project in development +# Details https://gitlab.com/gitlab-org/gitlab-ce/issues/46241 +Gitlab::Seeder.quiet do + ApplicationSetting.create_from_defaults unless ApplicationSetting.current_without_cache + ApplicationSetting.current_without_cache.update!(hashed_storage_enabled: true) + print '.' +end -- cgit v1.2.1 From dbbcffa186874915198600b2145f0eda79d47205 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jun 2018 13:27:07 +0300 Subject: Pass request to oauth creation during cluster app install Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/clusters/applications_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb index 4d758402850..a5c82caa897 100644 --- a/app/controllers/projects/clusters/applications_controller.rb +++ b/app/controllers/projects/clusters/applications_controller.rb @@ -42,6 +42,6 @@ class Projects::Clusters::ApplicationsController < Projects::ApplicationControll owner: current_user } - Applications::CreateService.new(current_user, oauth_application_params).execute + Applications::CreateService.new(current_user, oauth_application_params).execute(request) end end -- cgit v1.2.1 From 0cae8fd3a9548044c4bb6678de701404c974a235 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 7 Jun 2018 11:41:44 +0100 Subject: Update to GitLab Workhorse v4.3.1 --- GITLAB_WORKHORSE_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 80895903a15..f77856a6f1a 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -4.3.0 +4.3.1 -- cgit v1.2.1 From d18002efeda8ff10abe856e90cb1fa9de755974f Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 6 Jun 2018 21:37:04 +0200 Subject: Use same gem versions for rails5 as for rails4 where possible --- Gemfile.lock | 1 - Gemfile.rails5.lock | 236 +++++++++++++++-------------- changelogs/unreleased/rails5-fix-47370.yml | 5 + 3 files changed, 128 insertions(+), 114 deletions(-) create mode 100644 changelogs/unreleased/rails5-fix-47370.yml diff --git a/Gemfile.lock b/Gemfile.lock index 20fe5daa58e..a7ce46ac45a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -452,7 +452,6 @@ GEM kgio (2.10.0) knapsack (1.16.0) rake - timecop (>= 0.1.0) kubeclient (3.1.0) http (~> 2.2.2) recursive-open-struct (~> 1.0, >= 1.0.4) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index bee13dd1d53..d9a89de32cb 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -3,7 +3,7 @@ GEM specs: RedCloth (4.3.2) abstract_type (0.0.7) - ace-rails-ap (4.1.4) + ace-rails-ap (4.1.2) actioncable (5.0.7) actionpack (= 5.0.7) nio4r (>= 1.2, < 3.0) @@ -54,12 +54,12 @@ GEM akismet (2.0.0) allocations (1.0.5) arel (7.1.4) - asana (0.6.3) + asana (0.6.0) faraday (~> 0.9) faraday_middleware (~> 0.9) faraday_middleware-multi_json (~> 0.0) oauth2 (~> 1.0) - asciidoctor (1.5.6.1) + asciidoctor (1.5.6.2) asciidoctor-plantuml (0.0.8) asciidoctor (~> 1.5) asset_sync (2.4.0) @@ -71,8 +71,8 @@ GEM atomic (1.1.99) attr_encrypted (3.1.0) encryptor (~> 3.0.0) - attr_required (1.0.1) - awesome_print (1.2.0) + attr_required (1.0.0) + awesome_print (1.8.0) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -88,12 +88,12 @@ GEM erubis (>= 2.6.6) rack (>= 0.9.0) bindata (2.4.3) - binding_of_caller (0.7.3) + binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) blankslate (2.1.2.4) bootstrap_form (2.7.0) brakeman (4.2.1) - browser (2.5.3) + browser (2.2.0) builder (3.2.3) bullet (5.5.1) activesupport (>= 3.0.0) @@ -102,32 +102,33 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (9.0.6) - capybara (2.18.0) + capybara (2.15.1) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - xpath (>= 2.0, < 4.0) - capybara-screenshot (1.0.18) + xpath (~> 2.0) + capybara-screenshot (1.0.14) capybara (>= 1.0, < 3) launchy - carrierwave (1.2.2) + carrierwave (1.2.1) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) - charlock_holmes (0.7.5) + cause (0.1) + charlock_holmes (0.7.6) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) chronic_duration (0.10.6) numerizer (~> 0.1.1) - chunky_png (1.3.10) + chunky_png (1.3.5) citrus (3.0.2) - coderay (1.1.2) + coderay (1.1.1) coercible (1.0.0) descendants_tracker (~> 0.0.1) - commonmarker (0.17.9) + commonmarker (0.17.8) ruby-enum (~> 0.5) concord (0.1.5) adamantium (~> 0.2.0) @@ -140,11 +141,11 @@ GEM safe_yaml (~> 1.0.0) crass (1.0.4) creole (0.5.0) - css_parser (1.6.0) + css_parser (1.5.0) addressable - daemons (1.2.6) + daemons (1.2.3) database_cleaner (1.5.3) - debug_inspector (0.0.3) + debug_inspector (0.0.2) debugger-ruby_core_source (1.3.8) deckar01-task_list (2.0.0) html-pipeline @@ -154,33 +155,33 @@ GEM activerecord (>= 3.2.0, < 5.2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - device_detector (1.0.1) - devise (4.4.1) + device_detector (1.0.0) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.2) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) - devise-two-factor (3.0.2) - activesupport (< 5.2) + devise-two-factor (3.0.0) + activesupport attr_encrypted (>= 1.3, < 4, != 2) devise (~> 4.0) - railties (< 5.2) + railties rotp (~> 2.0) diff-lcs (1.3) diffy (3.1.0) docile (1.1.5) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) - doorkeeper (4.3.1) + doorkeeper (4.3.2) railties (>= 4.2) - doorkeeper-openid_connect (1.3.0) + doorkeeper-openid_connect (1.4.0) doorkeeper (~> 4.3) json-jwt (~> 1.6) - dropzonejs-rails (0.7.4) + dropzonejs-rails (0.7.2) rails (> 3.1) ed25519 (1.2.4) - email_reply_trimmer (0.1.10) + email_reply_trimmer (0.1.6) email_spec (2.2.0) htmlentities (~> 4.3.3) launchy (~> 2.1) @@ -189,11 +190,11 @@ GEM equalizer (0.0.11) erubis (2.7.0) escape_utils (1.1.1) - et-orbi (1.0.9) + et-orbi (1.0.3) tzinfo - eventmachine (1.2.5) - excon (0.60.0) - execjs (2.7.0) + eventmachine (1.0.8) + excon (0.62.0) + execjs (2.6.0) expression_parser (0.9.0) factory_bot (4.8.2) activesupport (>= 3.0.0) @@ -209,8 +210,8 @@ GEM multi_json fast_blank (1.0.0) fast_gettext (1.6.0) - ffaker (2.8.1) - ffi (1.9.23) + ffaker (2.4.0) + ffi (1.9.18) flay (2.10.0) erubis (~> 2.7.0) path_expander (~> 1.0) @@ -248,13 +249,13 @@ GEM fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) - fog-local (0.5.0) - fog-core (>= 1.27, < 3.0) - fog-openstack (0.1.24) - fog-core (~> 1.40) + fog-local (0.3.1) + fog-core (~> 1.27) + fog-openstack (0.1.21) + fog-core (>= 1.40) fog-json (>= 1.0) ipaddress (>= 0.8) - fog-rackspace (0.1.5) + fog-rackspace (0.1.1) fog-core (>= 1.35) fog-json (>= 1.0) fog-xml (>= 0.1) @@ -262,8 +263,8 @@ GEM fog-xml (0.1.3) fog-core nokogiri (>= 1.5.11, < 2.0.0) - font-awesome-rails (4.7.0.3) - railties (>= 3.2, < 5.2) + font-awesome-rails (4.7.0.1) + railties (>= 3.2, < 5.1) foreman (0.84.0) thor (~> 0.19.1) formatador (0.2.5) @@ -274,7 +275,7 @@ GEM rugged (~> 0.21) gemojione (3.3.0) json - get_process_mem (0.2.1) + get_process_mem (0.2.0) gettext (3.2.9) locale (>= 2.0.5) text (>= 1.3.0) @@ -350,9 +351,9 @@ GEM multi_json (~> 1.11) os (~> 0.9) signet (~> 0.7) - gpgme (2.0.16) - mini_portile2 (~> 2.3) - grape (1.0.2) + gpgme (2.0.13) + mini_portile2 (~> 2.1) + grape (1.0.3) activesupport builder mustermann-grape (~> 1.0.0) @@ -368,6 +369,10 @@ GEM rake grape_logging (1.7.0) grape + graphiql-rails (1.4.10) + railties + sprockets-rails + graphql (1.8.1) grpc (1.11.0) google-protobuf (~> 3.1) googleapis-common-protos-types (~> 1.0.0) @@ -380,23 +385,23 @@ GEM rake (>= 10, < 13) rubocop (>= 0.49.0) sysexits (~> 1.1) - hamlit (2.6.2) + hamlit (2.6.1) temple (~> 0.7.6) thor tilt - hashdiff (0.3.7) + hashdiff (0.3.4) hashie (3.5.7) hashie-forbidden_attributes (0.1.1) hashie (>= 3.0) health_check (2.6.0) rails (>= 4.0) - hipchat (1.5.4) + hipchat (1.5.2) httparty mimemagic html-pipeline (2.7.1) activesupport (>= 2) nokogiri (>= 1.4) - html2text (0.2.1) + html2text (0.2.0) nokogiri (~> 1.6) htmlentities (4.3.4) http (2.2.2) @@ -416,9 +421,11 @@ GEM concurrent-ruby (~> 1.0) icalendar (2.4.1) ice_nine (0.11.2) - influxdb (0.5.3) + influxdb (0.2.3) + cause + json ipaddress (0.8.3) - jira-ruby (1.5.0) + jira-ruby (1.4.1) activesupport multipart-post oauth (~> 0.5, >= 0.5.0) @@ -433,30 +440,30 @@ GEM json-schema (2.8.0) addressable (>= 2.4) jwt (1.5.6) - kaminari (1.1.1) + kaminari (1.0.1) activesupport (>= 4.1.0) - kaminari-actionview (= 1.1.1) - kaminari-activerecord (= 1.1.1) - kaminari-core (= 1.1.1) - kaminari-actionview (1.1.1) + kaminari-actionview (= 1.0.1) + kaminari-activerecord (= 1.0.1) + kaminari-core (= 1.0.1) + kaminari-actionview (1.0.1) actionview - kaminari-core (= 1.1.1) - kaminari-activerecord (1.1.1) + kaminari-core (= 1.0.1) + kaminari-activerecord (1.0.1) activerecord - kaminari-core (= 1.1.1) - kaminari-core (1.1.1) - kgio (2.11.2) + kaminari-core (= 1.0.1) + kaminari-core (1.0.1) + kgio (2.10.0) knapsack (1.16.0) rake - kubeclient (3.1.1) + kubeclient (3.1.0) http (~> 2.2.2) recursive-open-struct (~> 1.0, >= 1.0.4) rest-client (~> 2.0) launchy (2.4.3) addressable (~> 2.3) - letter_opener (1.6.0) + letter_opener (1.4.1) launchy (~> 2.2) - letter_opener_web (1.3.3) + letter_opener_web (1.3.0) actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) @@ -475,7 +482,7 @@ GEM logging (2.2.2) little-plugger (~> 1.1) multi_json (~> 1.10) - lograge (0.9.0) + lograge (0.10.0) actionpack (>= 4) activesupport (>= 4) railties (>= 4) @@ -489,11 +496,11 @@ GEM memoist (0.16.0) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) - method_source (0.9.0) + method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) - mimemagic (0.3.2) + mimemagic (0.3.0) mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.7.0) @@ -505,7 +512,7 @@ GEM mustermann-grape (1.0.0) mustermann (~> 1.0.0) mysql2 (0.4.10) - net-ldap (0.16.1) + net-ldap (0.16.0) net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) @@ -560,7 +567,7 @@ GEM omniauth-oauth2 (1.5.0) oauth2 (~> 1.1) omniauth (~> 1.2) - omniauth-oauth2-generic (0.2.4) + omniauth-oauth2-generic (0.2.2) omniauth-oauth2 (~> 1.0) omniauth-saml (1.10.0) omniauth (~> 1.3, >= 1.3.2) @@ -579,7 +586,7 @@ GEM orm_adapter (0.5.0) os (0.9.6) parallel (1.12.1) - parser (2.5.0.5) + parser (2.5.1.0) ast (~> 2.4.0) parslet (1.5.0) blankslate (~> 2.0) @@ -615,9 +622,9 @@ GEM json (>= 1.6.0) posix-spawn (0.3.13) powerpack (0.1.1) - premailer (1.11.1) + premailer (1.10.4) addressable - css_parser (>= 1.6.0) + css_parser (>= 1.4.10) htmlentities (>= 4.0.0) premailer-rails (1.9.7) actionmailer (>= 3, < 6) @@ -628,14 +635,15 @@ GEM unparser procto (0.0.3) prometheus-client-mmap (0.9.3) - pry (0.11.3) + pry (0.10.4) coderay (~> 1.1.0) - method_source (~> 0.9.0) - pry-byebug (3.4.3) - byebug (>= 9.0, < 9.1) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-byebug (3.4.2) + byebug (~> 9.0) pry (~> 0.10) - pry-rails (0.3.6) - pry (>= 0.10.4) + pry-rails (0.3.5) + pry (>= 0.9.10) public_suffix (3.0.2) pyu-ruby-sasl (0.0.3.3) rack (2.0.5) @@ -652,7 +660,7 @@ GEM rack (>= 1.1) rack-protection (2.0.1) rack - rack-proxy (0.6.4) + rack-proxy (0.6.0) rack rack-test (0.6.3) rack (>= 1.0) @@ -690,16 +698,16 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake - raindrops (0.19.0) + raindrops (0.18.0) rake (12.3.1) - rb-fsevent (0.10.3) + rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - rblineprof (0.3.7) + rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) rdoc (6.0.4) re2 (1.1.1) - recaptcha (3.4.0) + recaptcha (3.0.0) json recursive-open-struct (1.1.0) redcarpet (3.4.0) @@ -726,8 +734,7 @@ GEM declarative (< 0.1.0) declarative-option (< 0.2.0) uber (< 0.2.0) - request_store (1.4.0) - rack (>= 1.4) + request_store (1.3.1) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) @@ -736,11 +743,11 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.1) - rinku (2.0.4) + rinku (2.0.0) rotp (2.1.2) rouge (3.1.1) - rqrcode (0.10.1) - chunky_png (~> 1.0) + rqrcode (0.7.0) + chunky_png rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) rspec (3.6.0) @@ -761,7 +768,7 @@ GEM proc_to_ast rspec (>= 2.13, < 4) unparser - rspec-rails (3.6.1) + rspec-rails (3.6.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -769,7 +776,7 @@ GEM rspec-expectations (~> 3.6.0) rspec-mocks (~> 3.6.0) rspec-support (~> 3.6.0) - rspec-retry (0.4.6) + rspec-retry (0.4.5) rspec-core rspec-set (0.1.3) rspec-support (3.6.0) @@ -787,7 +794,7 @@ GEM unicode-display_width (~> 1.0, >= 1.0.1) rubocop-gitlab-security (0.1.1) rubocop (>= 0.51) - rubocop-rspec (1.22.2) + rubocop-rspec (1.22.1) rubocop (>= 0.52.1) ruby-enum (0.7.2) i18n @@ -797,14 +804,14 @@ GEM ruby-progressbar (1.9.0) ruby-saml (1.7.2) nokogiri (>= 1.5.10) - ruby_parser (3.11.0) - sexp_processor (~> 4.9) + ruby_parser (3.9.0) + sexp_processor (~> 4.1) rubyntlm (0.6.2) - rubypants (0.7.0) + rubypants (0.2.0) rubyzip (1.2.1) - rufus-scheduler (3.4.2) + rufus-scheduler (3.4.0) et-orbi (~> 1.0) - rugged (0.27.0) + rugged (0.27.1) safe_yaml (1.0.4) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -813,7 +820,7 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.0.7) + sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) @@ -829,7 +836,7 @@ GEM seed-fu (2.3.7) activerecord (>= 3.1) activesupport (>= 3.1) - select2-rails (3.5.10) + select2-rails (3.5.9.3) thor (~> 0.14) selenium-webdriver (3.12.0) childprocess (~> 0.5) @@ -837,17 +844,17 @@ GEM sentry-raven (2.7.2) faraday (>= 0.7.6, < 1.0) settingslogic (2.0.9) - sexp_processor (4.10.1) + sexp_processor (4.9.0) sham_rack (1.3.6) rack shoulda-matchers (3.1.2) activesupport (>= 4.0.0) - sidekiq (5.1.1) + sidekiq (5.1.3) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) redis (>= 3.3.5, < 5) - sidekiq-cron (0.6.3) + sidekiq-cron (0.6.0) rufus-scheduler (>= 3.3.0) sidekiq (>= 4.2.1) sidekiq-limit_fetch (3.4.0) @@ -857,14 +864,15 @@ GEM faraday (~> 0.9) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simple_po_parser (1.1.3) + simple_po_parser (1.1.2) simplecov (0.14.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) + simplecov-html (0.10.0) slack-notifier (1.5.1) - spring (2.0.2) + slop (3.6.0) + spring (2.0.1) activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) @@ -877,7 +885,7 @@ GEM sprockets (>= 3.0.0) sqlite3 (1.3.13) sshkey (1.9.0) - stackprof (0.2.11) + stackprof (0.2.10) state_machines (0.5.0) state_machines-activemodel (0.5.1) activemodel (>= 4.1, < 6.0) @@ -886,19 +894,19 @@ GEM activerecord (>= 4.1, < 6.0) state_machines-activemodel (>= 0.5.0) stringex (2.8.4) - sys-filesystem (1.1.9) + sys-filesystem (1.1.6) ffi sysexits (1.2.0) temple (0.7.7) test-prof (0.2.5) text (1.3.1) - thin (1.7.2) + thin (1.7.0) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.8) + tilt (2.0.6) timecop (0.8.1) timfel-krb5-auth (0.8.3) toml (0.1.2) @@ -918,7 +926,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.5) - unicode-display_width (1.3.0) + unicode-display_width (1.3.2) unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) @@ -935,7 +943,7 @@ GEM parser (>= 2.3.1.2, < 2.6) procto (~> 0.0.2) url_safe_base64 (0.2.2) - validates_hostname (1.0.8) + validates_hostname (1.0.6) activerecord (>= 3.0) activesupport (>= 3.0) version_sorter (2.1.0) @@ -951,7 +959,7 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - webpack-rails (0.9.11) + webpack-rails (0.9.10) railties (>= 3.2.0) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) @@ -962,8 +970,8 @@ GEM rinku with_env (1.1.0) xml-simple (1.1.5) - xpath (3.0.0) - nokogiri (~> 1.8) + xpath (2.1.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -1056,6 +1064,8 @@ DEPENDENCIES grape-entity (~> 0.7.1) grape-path-helpers (~> 1.0) grape_logging (~> 1.7) + graphiql-rails (~> 1.4.10) + graphql (~> 1.8.0) grpc (~> 1.11.0) haml_lint (~> 0.26.0) hamlit (~> 2.6.1) diff --git a/changelogs/unreleased/rails5-fix-47370.yml b/changelogs/unreleased/rails5-fix-47370.yml new file mode 100644 index 00000000000..90c19593b7d --- /dev/null +++ b/changelogs/unreleased/rails5-fix-47370.yml @@ -0,0 +1,5 @@ +--- +title: Use same gem versions for rails5 as for rails4 where possible +merge_request: 19498 +author: Jasper Maes +type: fixed -- cgit v1.2.1 From a2ccadb8b6ad76132bb28251937401d0666829ec Mon Sep 17 00:00:00 2001 From: bikebilly Date: Thu, 7 Jun 2018 13:09:23 +0200 Subject: Change 'project ID' into 'project' --- app/views/projects/clusters/gcp/_form.html.haml | 2 +- locale/gitlab.pot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml index ca7a6d5a886..59c4eeec17a 100644 --- a/app/views/projects/clusters/gcp/_form.html.haml +++ b/app/views/projects/clusters/gcp/_form.html.haml @@ -15,7 +15,7 @@ = field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field| .form-group - = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project ID') + = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project') .js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } } = provider_gcp_field.hidden_field :gcp_project_id .dropdown diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f63f2a89aa9..43afb140051 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1086,7 +1086,7 @@ msgstr "" msgid "ClusterIntegration|GitLab Runner" msgstr "" -msgid "ClusterIntegration|Google Cloud Platform project ID" +msgid "ClusterIntegration|Google Cloud Platform project" msgstr "" msgid "ClusterIntegration|Google Kubernetes Engine" -- cgit v1.2.1 From 1b1d51bb30777065c8f854afcab3e4d865d8dc60 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jun 2018 14:22:29 +0300 Subject: Make request argument required for Applications::CreateService Signed-off-by: Dmitriy Zaporozhets --- app/services/applications/create_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb index e67af929954..94a434b95dd 100644 --- a/app/services/applications/create_service.rb +++ b/app/services/applications/create_service.rb @@ -5,7 +5,7 @@ module Applications @params = params.except(:ip_address) end - def execute(request = nil) + def execute(request) Doorkeeper::Application.create(@params) end end -- cgit v1.2.1 From e6d8faf6fa59815cfe5b43e2a92ddabcae03c8ca Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Thu, 7 Jun 2018 13:26:29 +0200 Subject: Update doc for maintaining project visibility during Github import --- doc/user/project/import/github.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index cad85881c4d..fcd6192e82f 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -29,7 +29,9 @@ The following aspects of a project are imported: * Regular issue and pull request comments References to pull requests and issues are preserved (GitLab.com & 8.7+), and -each imported repository defaults to `private` but [can be made public](../settings/index.md#sharing-and-permissions), as needed. +each imported repository maintains visibility level unless that [visibility +level is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects), +in which case it defaults to the default project visibility. ## How it works -- cgit v1.2.1 From 0206476ae2a2d659fd0fb42338050253a9a91439 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 7 Jun 2018 12:14:27 +0100 Subject: Fix some N+1s when calculating notification recipients First N+1: we may have loaded a user's notification settings already, but not have loaded their sources. Because we're iterating through, we'd potentially load sources that are completely unrelated, just because they belong to this user. Second N+1: we do a separate query for each user who could be subscribed to or unsubcribed from the target. It's actually more efficient in this case to get all subscriptions at once, as we will need to check most of them. We can fix both by the slightly unpleasant means of checking IDs manually, rather than object equality. --- app/models/notification_recipient.rb | 2 +- app/models/user.rb | 5 ++- .../n-plus-one-notification-recipients.yml | 5 +++ .../notification_recipient_service_spec.rb | 36 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/n-plus-one-notification-recipients.yml create mode 100644 spec/services/notification_recipient_service_spec.rb diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index 2c3580bbdc6..79458bd048a 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -64,7 +64,7 @@ class NotificationRecipient return false unless @target return false unless @target.respond_to?(:subscriptions) - subscription = @target.subscriptions.find_by_user_id(@user.id) + subscription = @target.subscriptions.find { |subscription| subscription.user_id == @user.id } subscription && !subscription.subscribed end diff --git a/app/models/user.rb b/app/models/user.rb index e219ab800ad..8e0dc91b2a7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1038,7 +1038,10 @@ class User < ActiveRecord::Base def notification_settings_for(source) if notification_settings.loaded? - notification_settings.find { |notification| notification.source == source } + notification_settings.find do |notification| + notification.source_type == source.class.base_class.name && + notification.source_id == source.id + end else notification_settings.find_or_initialize_by(source: source) end diff --git a/changelogs/unreleased/n-plus-one-notification-recipients.yml b/changelogs/unreleased/n-plus-one-notification-recipients.yml new file mode 100644 index 00000000000..91c31e4c930 --- /dev/null +++ b/changelogs/unreleased/n-plus-one-notification-recipients.yml @@ -0,0 +1,5 @@ +--- +title: Fix some sources of excessive query counts when calculating notification recipients +merge_request: +author: +type: performance diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb new file mode 100644 index 00000000000..340d4585e0c --- /dev/null +++ b/spec/services/notification_recipient_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe NotificationRecipientService do + let(:service) { described_class } + let(:assignee) { create(:user) } + let(:project) { create(:project, :public) } + let(:other_projects) { create_list(:project, 5, :public) } + + describe '#build_new_note_recipients' do + let(:issue) { create(:issue, project: project, assignees: [assignee]) } + let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) } + + def create_watcher + watcher = create(:user) + create(:notification_setting, project: project, user: watcher, level: :watch) + + other_projects.each do |other_project| + create(:notification_setting, project: other_project, user: watcher, level: :watch) + end + end + + it 'avoids N+1 queries' do + create_watcher + + service.build_new_note_recipients(note) + + control_count = ActiveRecord::QueryRecorder.new do + service.build_new_note_recipients(note) + end + + create_watcher + + expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count) + end + end +end -- cgit v1.2.1 From 6dfb0b4b3194519b5000bfba3e736f48d58d5255 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 6 Jun 2018 11:49:34 +0200 Subject: Consistently use tar lookup function --- lib/backup/files.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/backup/files.rb b/lib/backup/files.rb index d769a3ee7b0..e287aa1e392 100644 --- a/lib/backup/files.rb +++ b/lib/backup/files.rb @@ -29,10 +29,10 @@ module Backup raise Backup::Error, 'Backup failed' end - run_pipeline!([%W(tar --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + run_pipeline!([%W(#{tar} --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) FileUtils.rm_rf(@backup_files_dir) else - run_pipeline!([%W(tar --exclude=lost+found -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + run_pipeline!([%W(#{tar} --exclude=lost+found -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) end end @@ -43,7 +43,12 @@ module Backup end def tar - system(*%w[gtar --version], out: '/dev/null') ? 'gtar' : 'tar' + if system(*%w[gtar --version], out: '/dev/null') + # It looks like we can get GNU tar by running 'gtar' + 'gtar' + else + 'tar' + end end def backup_existing_files_dir -- cgit v1.2.1 From 95dde46be57c8e1eacfa7fcd0bdfd299c635af6b Mon Sep 17 00:00:00 2001 From: Fabio Busatto Date: Thu, 7 Jun 2018 12:09:23 +0000 Subject: Resolve "Update docs to reflect the new place of the Kubernetes page" --- doc/ci/environments.md | 4 ++-- doc/user/project/clusters/eks_and_gitlab/index.md | 2 +- doc/user/project/clusters/index.md | 4 ++-- doc/user/project/integrations/prometheus.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 36fd8affa5b..8ea2e0a81dc 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -114,7 +114,7 @@ Let's now see how that information is exposed within GitLab. ## Viewing the current status of an environment -The environment list under your project's **Pipelines ➔ Environments**, is +The environment list under your project's **Operations > Environments**, is where you can find information of the last deployment status of an environment. Here's how the Environments page looks so far. @@ -167,7 +167,7 @@ that works. You can't control everything, so sometimes things go wrong. When that unfortunate time comes GitLab has you covered. Simply by clicking the **Rollback** button that can be found in the deployments page -(**Pipelines ➔ Environments ➔ `environment name`**) you can relaunch the +(**Operations > Environments > `environment name`**) you can relaunch the job with the commit associated with it. >**Note:** diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md index bd4d15dddcb..2d8fdf0d1da 100644 --- a/doc/user/project/clusters/eks_and_gitlab/index.md +++ b/doc/user/project/clusters/eks_and_gitlab/index.md @@ -38,7 +38,7 @@ Give the project a name, and then select `Create project`. ## Connecting the EKS cluster -From the left side bar, hover over `CI/CD` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`. +From the left side bar, hover over `Operations` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`. A few details from the EKS cluster will be required to connect it to GitLab. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index e44ac6a3d01..1e909e9f5f7 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -39,7 +39,7 @@ Before proceeding, make sure the following requirements are met: If all of the above requirements are met, you can proceed to create and add a new Kubernetes cluster that will be hosted on GKE to your project: -1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Navigate to your project's **Operations > Kubernetes** page. 1. Click on **Add Kubernetes cluster**. 1. Click on **Create with GKE**. 1. Connect your Google account if you haven't done already by clicking the @@ -70,7 +70,7 @@ You need Maintainer [permissions] and above to access the Kubernetes page. To add an existing Kubernetes cluster to your project: -1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Navigate to your project's **Operations > Kubernetes** page. 1. Click on **Add Kubernetes cluster**. 1. Click on **Add an existing Kubernetes cluster** and fill in the details: - **Kubernetes cluster name** (required) - The name you wish to give the cluster. diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index fa7e504c4aa..f687027e8c8 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -30,7 +30,7 @@ GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cl Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click. -1. Go to the `CI/CD > Kubernetes` page, to view your connected clusters +1. Go to the `Operations > Kubernetes` page, to view your connected clusters 1. Select the cluster you would like to deploy Prometheus to 1. Click the **Install** button to deploy Prometheus to the cluster -- cgit v1.2.1 From 93c7976ae26370a6d65c3d5038323911592c20ce Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Thu, 7 Jun 2018 12:24:57 +0000 Subject: Restore navigation theme order --- lib/gitlab/themes.rb | 16 ++++++++-------- spec/helpers/preferences_helper_spec.rb | 2 +- spec/lib/gitlab/themes_spec.rb | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb index 9277b57f46f..694b01b272c 100644 --- a/lib/gitlab/themes.rb +++ b/lib/gitlab/themes.rb @@ -13,15 +13,15 @@ module Gitlab # All available Themes THEMES = [ Theme.new(1, 'Indigo', 'ui-indigo'), - Theme.new(2, 'Light Indigo', 'ui-light-indigo'), - Theme.new(3, 'Blue', 'ui-blue'), - Theme.new(4, 'Light Blue', 'ui-light-blue'), + Theme.new(6, 'Light Indigo', 'ui-light-indigo'), + Theme.new(4, 'Blue', 'ui-blue'), + Theme.new(7, 'Light Blue', 'ui-light-blue'), Theme.new(5, 'Green', 'ui-green'), - Theme.new(6, 'Light Green', 'ui-light-green'), - Theme.new(7, 'Red', 'ui-red'), - Theme.new(8, 'Light Red', 'ui-light-red'), - Theme.new(9, 'Dark', 'ui-dark'), - Theme.new(10, 'Light', 'ui-light') + Theme.new(8, 'Light Green', 'ui-light-green'), + Theme.new(9, 'Red', 'ui-red'), + Theme.new(10, 'Light Red', 'ui-light-red'), + Theme.new(2, 'Dark', 'ui-dark'), + Theme.new(3, 'Light', 'ui-light') ].freeze # Convenience method to get a space-separated String of all the theme diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 9940656fb68..363ebc88afd 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -31,7 +31,7 @@ describe PreferencesHelper do describe '#user_application_theme' do context 'with a user' do it "returns user's theme's css_class" do - stub_user(theme_id: 10) + stub_user(theme_id: 3) expect(helper.user_application_theme).to eq 'ui-light' end diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index af2f4568017..a8213988f70 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Themes, lib: true do css = described_class.body_classes expect(css).to include('ui-indigo') - expect(css).to include('ui-dark ') + expect(css).to include('ui-dark') expect(css).to include('ui-blue') end end @@ -14,7 +14,7 @@ describe Gitlab::Themes, lib: true do describe '.by_id' do it 'returns a Theme by its ID' do expect(described_class.by_id(1).name).to eq 'Indigo' - expect(described_class.by_id(10).name).to eq 'Light' + expect(described_class.by_id(3).name).to eq 'Light' end end -- cgit v1.2.1 From 59a8c79f0598d4afafbebeb7e3786a03e555f85d Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Mon, 4 Jun 2018 15:15:54 +0200 Subject: Use RestoreCustomHooks RPC in restore rake task --- Gemfile | 2 +- Gemfile.lock | 4 ++-- lib/backup/repository.rb | 33 ++++++++++++++++++-------- lib/gitlab/gitaly_client/repository_service.rb | 22 +++++++++++++++++ 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index 339761bf8fa..e7745371d74 100644 --- a/Gemfile +++ b/Gemfile @@ -418,7 +418,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.101.0', require: 'gitaly' gem 'grpc', '~> 1.11.0' # Locked until https://github.com/google/protobuf/issues/4210 is closed diff --git a/Gemfile.lock b/Gemfile.lock index ff9e81bdc9f..108b2ec4bec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -283,7 +283,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.100.0) + gitaly-proto (0.101.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -1039,7 +1039,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.100.0) + gitaly-proto (~> 0.101.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 1b1c83d9fb3..0119c5d6851 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -112,18 +112,31 @@ module Backup end end + def local_restore_custom_hooks(project, dir) + path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + path_to_repo(project) + end + cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) + output, status = Gitlab::Popen.popen(cmd) + unless status.zero? + progress_warn(project, cmd.join(' '), output) + end + end + + def gitaly_restore_custom_hooks(project, dir) + custom_hooks_path = path_to_tars(project, dir) + Gitlab::GitalyClient::RepositoryService.new(project.repository) + .restore_custom_hooks(custom_hooks_path) + end + def restore_custom_hooks(project) - # TODO: Need to find a way to do this for gitaly - # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 in_path(path_to_tars(project)) do |dir| - path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - path_to_repo(project) - end - cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) - - output, status = Gitlab::Popen.popen(cmd) - unless status.zero? - progress_warn(project, cmd.join(' '), output) + gitaly_migrate(:restore_custom_hooks) do |is_enabled| + if is_enabled + local_restore_custom_hooks(project, dir) + else + gitaly_restore_custom_hooks(project, dir) + end end end end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index ee01f5a5bd9..7323cb65c29 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -235,6 +235,28 @@ module Gitlab ) end + def restore_custom_hooks(custom_hooks_path) + request = Gitaly::RestoreCustomHooksRequest.new(repository: @gitaly_repo) + enum = Enumerator.new do |y| + File.open(custom_hooks_path, 'rb') do |f| + while data = f.read(MAX_MSG_SIZE) + request.data = data + + y.yield request + request = Gitaly::RestoreCustomHooksRequest.new + end + end + end + + GitalyClient.call( + @storage, + :repository_service, + :restore_custom_hooks, + enum, + timeout: GitalyClient.default_timeout + ) + end + def create_from_snapshot(http_url, http_auth) request = Gitaly::CreateRepositoryFromSnapshotRequest.new( repository: @gitaly_repo, -- cgit v1.2.1 From e68b541aa4657eab5d6a3f059655d56a958647a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 14:55:19 +0200 Subject: Change database deploy strategy default to continuous --- db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb index 6f50d428965..0f6bd0dd581 100644 --- a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb +++ b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb @@ -10,7 +10,7 @@ class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration disable_ddl_transaction! def up - add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false + add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 1, allow_null: false end def down -- cgit v1.2.1 From 616dd00abbd09e6ef550e2eb877ae9fe2b28812b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 14:56:48 +0200 Subject: Add trait for manual deploy strategy --- spec/factories/project_auto_devops.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index a59087cd7eb..189d097d5e6 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -4,5 +4,9 @@ FactoryBot.define do enabled true domain "example.com" deploy_strategy :continuous + + trait :manual do + deploy_strategy :manual + end end end -- cgit v1.2.1 From 637b90db6edbd13d15289bf0d8d2c442d05442f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 14:58:09 +0200 Subject: Add spec for continuous deploy strategy --- spec/models/project_auto_devops_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 040e76e28fc..f9ee9fe98bd 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -82,6 +82,19 @@ describe ProjectAutoDevops do end end + context 'when deploy_strategy is continuous' do + let(:domain) { 'example.com' } + + before do + auto_devops.deploy_strategy = 'continuous' + end + + it do + expect(auto_devops.predefined_variables.map { |var| var[:key] }) + .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") + end + end + def domain_variable { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end -- cgit v1.2.1 From e79476679b250a662ee2116257961daef1596fb1 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Thu, 7 Jun 2018 13:16:14 +0000 Subject: Docs: article - Deploy SB apps to CF with GitLab CI/CD --- .../img/cloud_foundry_secret_variables.png | Bin 0 -> 49735 bytes .../img/create_from_template.png | Bin 0 -> 82121 bytes .../deploy_spring_boot_to_cloud_foundry/index.md | 142 +++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png create mode 100644 doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png create mode 100644 doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png new file mode 100644 index 00000000000..5b5d91ec07a Binary files /dev/null and b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png differ diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png new file mode 100644 index 00000000000..f3761632556 Binary files /dev/null and b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png differ diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md new file mode 100644 index 00000000000..b88761be56b --- /dev/null +++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md @@ -0,0 +1,142 @@ +--- +author: Dylan Griffith +author_gitlab: DylanGriffith +level: intermediary +article_type: tutorial +date: 2018-06-07 +description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD" +--- + +# Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD + +## Introduction + +In this article, we'll demonstrate how to deploy a [Spring +Boot](https://projects.spring.io/spring-boot/) application to [Cloud +Foundry (CF)](https://www.cloudfoundry.org/) with GitLab CI/CD using the [Continuous +Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-deployment) +method. + +All the code for this project can be found in this [GitLab +repo](https://gitlab.com/gitlab-examples/spring-gitlab-cf-deploy-demo). + +In case you're interested in deploying Spring Boot applications to Kubernetes +using GitLab CI/CD, read through the blog post [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/). + +## Requirements + +_We assume you are familiar with Java, GitLab, Cloud Foundry, and GitLab CI/CD._ + +To follow along with this tutorial you will need the following: + +- An account on [Pivotal Web Services (PWS)](https://run.pivotal.io/) or any + other Cloud Foundry instance +- An account on GitLab + +NOTE: **Note:** +You will need to replace the `api.run.pivotal.io` URL in the all below +commands with the [API +URL](https://docs.cloudfoundry.org/running/cf-api-endpoint.html) of your CF +instance if you're not deploying to PWS. + +## Create your project + +To create your Spring Boot application you can use the Spring template in +GitLab when creating a new project: + +![New Project From Template](img/create_from_template.png) + +## Configure the deployment to Cloud Foundry + +To deploy to Cloud Foundry we need to add a `manifest.yml` file. This +is the configuration for the CF CLI we will use to deploy the application. We +will create this in the root directory of our project with the following +content: + +```yaml +--- +applications: +- name: gitlab-hello-world + random-route: true + memory: 1G + path: target/demo-0.0.1-SNAPSHOT.jar +``` + +## Configure GitLab CI/CD to deploy your application + +Now we need to add the the GitLab CI/CD configuration file +([`.gitlab-ci.yml`](../../yaml/README.md)) to our +project's root. This is how GitLab figures out what commands need to be run whenever +code is pushed to our repository. We will add the following `.gitlab-ci.yml` +file to the root directory of the repository, GitLab will detect it +automatically and run the steps defined once we push our code: + +```yaml +image: java:8 + +stages: + - build + - deploy + +build: + stage: build + script: ./mvnw package + artifacts: + paths: + - target/demo-0.0.1-SNAPSHOT.jar + +production: + stage: deploy + script: + - curl --location "https://cli.run.pivotal.io/stable?release=linux64-binary&source=github" | tar zx + - ./cf login -u $CF_USERNAME -p $CF_PASSWORD -a api.run.pivotal.io + - ./cf push + only: + - master +``` + +We've used the `java:8` [docker +image](../../docker/using_docker_images.md) to build +our application as it provides the up-to-date Java 8 JDK on [Docker +Hub](https://hub.docker.com/). We've also added the [`only` +clause](../../yaml/README.md#only-and-except-simplified) +to ensure our deployments only happen when we push to the master branch. + +Now, since the steps defined in `.gitlab-ci.yml` require credentials to login +to CF, you'll need to add your CF credentials as [environment +variables](../../variables/README.md#predefined-variables-environment-variables) +on GitLab CI/CD. To set the environment variables, navigate to your project's +**Settings > CI/CD** and expand **Secret Variables**. Name the variables +`CF_USERNAME` and `CF_PASSWORD` and set them to the correct values. + +![Secret Variable Settings in GitLab](img/cloud_foundry_secret_variables.png) + +Once set up, GitLab CI/CD will deploy your app to CF at every push to your +repository's deafult branch. To see the build logs or watch your builds running +live, navigate to **CI/CD > Pipelines**. + +CAUTION: **Caution:** +It is considered best practice for security to create a separate deploy +user for your application and add its credentials to GitLab instead of using +a developer's credentials. + +To start a manual deployment in GitLab go to **CI/CD > Pipelines** then click +on **Run Pipeline**. Once the app is finished deploying it will display the URL +of your application in the logs for the `production` job like: + +```shell +requested state: started +instances: 1/1 +usage: 1G x 1 instances +urls: gitlab-hello-world-undissembling-hotchpot.cfapps.io +last uploaded: Mon Nov 6 10:02:25 UTC 2017 +stack: cflinuxfs2 +buildpack: client-certificate-mapper=1.2.0_RELEASE container-security-provider=1.8.0_RELEASE java-buildpack=v4.5-offline-https://github.com/cloudfoundry/java-buildpack.git#ffeefb9 java-main java-opts jvmkill-agent=1.10.0_RELEASE open-jdk-like-jre=1.8.0_1... + + state since cpu memory disk details +#0 running 2017-11-06 09:03:22 PM 120.4% 291.9M of 1G 137.6M of 1G +``` + +You can then visit your deployed application (for this example, +https://gitlab-hello-world-undissembling-hotchpot.cfapps.io/) and you should +see the "Spring is here!" message. -- cgit v1.2.1 From 435e661a2ec060c4b7349b26b506243c27e8cc30 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 15:17:44 +0200 Subject: Introduce new keep-alive API entrypoint for CI job --- lib/api/runner.rb | 16 +++++++++++ spec/requests/api/runner_spec.rb | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/lib/api/runner.rb b/lib/api/runner.rb index db502697a19..ac62b83ba4a 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -140,6 +140,22 @@ module API end end + desc 'Marks job as live' do + http_codes [[200, 'Request accepted']] + end + params do + requires :id, type: Integer, desc: %q(Job's ID) + optional :token, type: String, desc: %q(Job's authentication token) + end + post '/:id/keep-alive' do + job = authenticate_job! + + job.touch if job.running? && job.needs_touch? + + status 200 + header 'Job-Status', job.status + end + desc 'Appends a patch to the job trace' do http_codes [[202, 'Trace was patched'], [400, 'Missing Content-Range header'], diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c981a10ac38..c93612d7ada 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -849,6 +849,68 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end + describe 'POST /api/v4/jobs/:id/keep-alive' do + let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } + let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } + let(:update_interval) { 30.seconds } + + it 'returns correct response' do + keep_alive_job + + expect(response.status).to eq 200 + expect(response.header).to have_key 'Job-Status' + end + + it 'updates updated_at value' do + expect { keep_alive_job }.to change { job.updated_at } + end + + context 'when project for the build has been deleted' do + let(:job) do + create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) do |job| + job.project.update(pending_delete: true) + end + end + + it 'responds with forbidden' do + keep_alive_job + + expect(response.status).to eq(403) + end + end + + context 'when job has been canceled' do + before do + job.cancel + end + + it 'returns job-status=canceled header' do + keep_alive_job + + expect(response.status).to eq 200 + expect(response.header['Job-Status']).to eq('canceled') + end + end + + context 'when job has been errased' do + let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } + + it 'rresponds with forbidden' do + keep_alive_job + + expect(response.status).to eq 403 + end + end + + def keep_alive_job(token = job.token, **params) + new_params = params.merge(token: token) + Timecop.travel(job.updated_at + update_interval) do + post api("/jobs/#{job.id}/keep-alive"), new_params + job.reload + end + end + end + describe 'PATCH /api/v4/jobs/:id/trace' do let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } -- cgit v1.2.1 From 135dc2c5b29fc8fe2563617882711364160e729c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 7 Jun 2018 15:08:43 +0200 Subject: Port the Auto DevOps docs from EE --- .../autodevops/img/autodevops_domain_variables.png | Bin 0 -> 8456 bytes .../img/autodevops_multiple_clusters.png | Bin 0 -> 12619 bytes doc/topics/autodevops/index.md | 133 ++++++++++++++++----- 3 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 doc/topics/autodevops/img/autodevops_domain_variables.png create mode 100644 doc/topics/autodevops/img/autodevops_multiple_clusters.png diff --git a/doc/topics/autodevops/img/autodevops_domain_variables.png b/doc/topics/autodevops/img/autodevops_domain_variables.png new file mode 100644 index 00000000000..b6f8864796f Binary files /dev/null and b/doc/topics/autodevops/img/autodevops_domain_variables.png differ diff --git a/doc/topics/autodevops/img/autodevops_multiple_clusters.png b/doc/topics/autodevops/img/autodevops_multiple_clusters.png new file mode 100644 index 00000000000..f4d101ca921 Binary files /dev/null and b/doc/topics/autodevops/img/autodevops_multiple_clusters.png differ diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 478b9c9c0c6..4bcc7fc6512 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -41,6 +41,7 @@ project in an easy and automatic way: 1. [Auto Code Quality](#auto-code-quality) 1. [Auto SAST (Static Application Security Testing)](#auto-sast) 1. [Auto Dependency Scanning](#auto-dependency-scanning) +1. [Auto License Management](#auto-license-management) 1. [Auto Container Scanning](#auto-container-scanning) 1. [Auto Review Apps](#auto-review-apps) 1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast) @@ -62,7 +63,7 @@ Auto DevOps provides great defaults for all the stages; you can, however, For an overview on the creation of Auto DevOps, read the blog post [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/). -## Prerequisites +## Requirements TIP: **Tip:** For self-hosted installations, the easiest way to make use of Auto DevOps is to @@ -112,25 +113,26 @@ NOTE: **Note:** If you do not have Kubernetes or Prometheus installed, then Auto Review Apps, Auto Deploy, and Auto Monitoring will be silently skipped. -### Auto DevOps base domain +## Auto DevOps base domain The Auto DevOps base domain is required if you want to make use of [Auto -Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It is defined -either under the project's CI/CD settings while -[enabling Auto DevOps](#enabling-auto-devops) or in instance-wide settings in -the CI/CD section. -It can also be set at the project or group level as a variable, `AUTO_DEVOPS_DOMAIN`. +Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It can be defined +in three places: -A wildcard DNS A record matching the base domain is required, for example, +- either under the project's CI/CD settings while [enabling Auto DevOps](#enabling-auto-devops) +- or in instance-wide settings in the **admin area > Settings** under the "Continuous Integration and Delivery" section +- or at the project or group level as a variable: `AUTO_DEVOPS_DOMAIN` (required if you want to use [multiple clusters](#using-multiple-kubernetes-clusters)) + +A wildcard DNS A record matching the base domain(s) is required, for example, given a base domain of `example.com`, you'd need a DNS entry like: ``` *.example.com 3600 A 1.2.3.4 ``` -where `example.com` is the domain name under which the deployed apps will be served, +In this case, `example.com` is the domain name under which the deployed apps will be served, and `1.2.3.4` is the IP address of your load balancer; generally NGINX -([see prerequisites](#prerequisites)). How to set up the DNS record is beyond +([see requirements](#requirements)). How to set up the DNS record is beyond the scope of this document; you should check with your DNS provider. Alternatively you can use free public services like [xip.io](http://xip.io) or @@ -146,6 +148,56 @@ If GitLab is installed using the [GitLab Omnibus Helm Chart], there are two options: provide a static IP, or have one assigned. For more information see the relevant docs on the [network prerequisites](../../install/kubernetes/gitlab_omnibus.md#networking-prerequisites). +## Using multiple Kubernetes clusters **[PREMIUM]** + +When using Auto DevOps, you may want to deploy different environments to +different Kubernetes clusters. This is possible due to the 1:1 connection that +[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters). + +In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml) +(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know: + +- `review/` (every environment starting with `review/`) +- `staging` +- `production` + +Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so +except for the environment scope, they would also need to have a different +domain they would be deployed to. This is why you need to define a separate +`AUTO_DEVOPS_DOMAIN` variable for all the above +[based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables). + +The following table is an example of how the three different clusters would +be configured. + +| Cluster name | Cluster environment scope | `AUTO_DEVOPS_DOMAIN` variable value | Variable environment scope | Notes | +| ------------ | -------------- | ----------------------------- | ------------- | ------ | +| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. | +| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). | +| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production). | + +To add a different cluster for each environment: + +1. Navigate to your project's **Operations > Kubernetes** and create the Kubernetes clusters + with their respective environment scope as described from the table above. + + ![Auto DevOps multiple clusters](img/autodevops_multiple_clusters.png) + +1. After the clusters are created, navigate to each one and install Helm Tiller + and Ingress. +1. Make sure you have [configured your DNS](#auto-devops-base-domain) with the + specified Auto DevOps domains. +1. Navigate to your project's **Settings > CI/CD > Variables** and add + the `AUTO_DEVOPS_DOMAIN` variables with their respective environment + scope. + + ![Auto DevOps domain variables](img/autodevops_domain_variables.png) + +Now that all is configured, you can test your setup by creating a merge request +and verifying that your app is deployed as a review app in the Kubernetes +cluster with the `review/*` environment scope. Similarly, you can check the +other environments. + ## Quick start If you are using GitLab.com, see our [quick start guide](quick_start_guide.md) @@ -154,13 +206,13 @@ Google Cloud. ## Enabling Auto DevOps -If you haven't done already, read the [prerequisites](#prerequisites) to make +If you haven't done already, read the [requirements](#requirements) to make full use of Auto DevOps. If this is your fist time, we recommend you follow the -[quick start guide](#quick-start). +[quick start guide](quick_start_guide.md). To enable Auto DevOps to your project: -1. Check that your project doesn't have a `.gitlab-ci.yml`, and remove it otherwise +1. Check that your project doesn't have a `.gitlab-ci.yml`, or remove it otherwise 1. Go to your project's **Settings > CI/CD > General pipelines settings** and find the Auto DevOps section 1. Select "Enable Auto DevOps" @@ -230,7 +282,7 @@ In GitLab Starter, differences between the source and target branches are also [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html). -### Auto SAST +### Auto SAST **[ULTIMATE]** > Introduced in [GitLab Ultimate][ee] 10.3. @@ -241,9 +293,9 @@ report is created, it's uploaded as an artifact which you can later download and check out. In GitLab Ultimate, any security warnings are also -[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html). +[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/sast.html). -### Auto Dependency Scanning +### Auto Dependency Scanning **[ULTIMATE]** > Introduced in [GitLab Ultimate][ee] 10.7. @@ -254,7 +306,20 @@ report is created, it's uploaded as an artifact which you can later download and check out. In GitLab Ultimate, any security warnings are also -[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dependency_scanning.html). +[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dependency_scanning.html). + +### Auto License Management **[ULTIMATE]** + +> Introduced in [GitLab Ultimate][ee] 11.0. + +License Management uses the +[License Management Docker image](https://gitlab.com/gitlab-org/security-products/license_management) +to search the project dependencies for their license. Once the +report is created, it's uploaded as an artifact which you can later download and +check out. + +In GitLab Ultimate, any licenses are also +[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/license_management.html). ### Auto Container Scanning @@ -267,13 +332,13 @@ created, it's uploaded as an artifact which you can later download and check out. In GitLab Ultimate, any security warnings are also -[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html). +[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/container_scanning.html). ### Auto Review Apps NOTE: **Note:** This is an optional step, since many projects do not have a Kubernetes cluster -available. If the [prerequisites](#prerequisites) are not met, the job will +available. If the [requirements](#requirements) are not met, the job will silently be skipped. CAUTION: **Caution:** @@ -295,7 +360,7 @@ up in the merge request widget for easy discovery. When the branch is deleted, for example after the merge request is merged, the Review App will automatically be deleted. -### Auto DAST +### Auto DAST **[ULTIMATE]** > Introduced in [GitLab Ultimate][ee] 10.4. @@ -306,9 +371,9 @@ issues. Once the report is created, it's uploaded as an artifact which you can later download and check out. In GitLab Ultimate, any security warnings are also -[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html). +[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dast.html). -### Auto Browser Performance Testing +### Auto Browser Performance Testing **[PREMIUM]** > Introduced in [GitLab Premium][ee] 10.4. @@ -320,13 +385,14 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h /direction ``` -In GitLab Premium, performance differences between the source and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html). +In GitLab Premium, performance differences between the source +and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html). ### Auto Deploy NOTE: **Note:** This is an optional step, since many projects do not have a Kubernetes cluster -available. If the [prerequisites](#prerequisites) are not met, the job will +available. If the [requirements](#requirements) are not met, the job will silently be skipped. CAUTION: **Caution:** @@ -363,7 +429,7 @@ executed somewhere else, it cannot be accessed again. ### Auto Monitoring NOTE: **Note:** -Check the [prerequisites](#prerequisites) for Auto Monitoring to make this stage +Check the [requirements](#requirements) for Auto Monitoring to make this stage work. Once your application is deployed, Auto Monitoring makes it possible to monitor @@ -437,7 +503,7 @@ repo or by specifying a project variable: file in it, Auto DevOps will detect the chart and use it instead of the [default one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app). This can be a great way to control exactly how your application is deployed. -- **Project variable** - Create a [variable](../../ci/variables/README.md#variables) +- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables) `AUTO_DEVOPS_CHART` with the URL of a custom chart to use. ### Customizing `.gitlab-ci.yml` @@ -493,22 +559,23 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. | | `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-variables-environment-variables). Set it to use a custom database name. | | `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` | +| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.| +| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).| | `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | | `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). | | `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | -| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `code_quality` job. If the variable is present, the job will not be created. | -| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. | +| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | | `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | -| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `container_scanning` job. If the variable is present, the job will not be created. | +| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. | | `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. | | `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. | | `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. | TIP: **Tip:** Set up the replica variables using a -[project variable](../../ci/variables/README.md#variables) +[project variable](../../ci/variables/README.md#secret-variables) and scale your application by just redeploying it! CAUTION: **Caution:** @@ -583,7 +650,7 @@ staging environment and deploy to production manually. For this scenario, the `STAGING_ENABLED` environment variable was introduced. If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to -`1` as a variable), then the application will be automatically deployed +`1` as a secret variable), then the application will be automatically deployed to a `staging` environment, and a `production_manual` job will be created for you when you're ready to manually deploy to production. @@ -596,7 +663,7 @@ A [canary environment](https://docs.gitlab.com/ee/user/project/canary_deployment before any changes are deployed to production. If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to -`1` as a variable) then two manual jobs will be created: +`1` as a secret variable) then two manual jobs will be created: - `canary` which will deploy the application to the canary environment - `production_manual` which is to be used by you when you're ready to manually @@ -612,7 +679,7 @@ This will allow you to first check how the app is behaving, and later manually increasing the rollout up to 100%. If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set -`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a variable), then instead of the +`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the standard `production` job, 4 different [manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph) will be created: -- cgit v1.2.1 From 03a0236410014328550831153d8c47c9805d0946 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Thu, 7 Jun 2018 14:22:02 +0100 Subject: index article --- doc/ci/examples/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index de60cd27cd1..aa31e172641 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -19,7 +19,9 @@ There's also a collection of repositories with [example projects](https://gitlab - [How to test and deploy Laravel/PHP applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md) - **Ruby**: [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) - **Python**: [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) -- **Java**: [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) +- **Java**: + - [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md) + - [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) - **Scala**: [Test a Scala application](test-scala-application.md) - **Clojure**: [Test a Clojure application](test-clojure-application.md) - **Elixir**: -- cgit v1.2.1 From 6fafc63117a9382ee088e364e83431ebd1b63a4a Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 15:31:14 +0200 Subject: Add CHANGELOG entry --- changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml diff --git a/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml new file mode 100644 index 00000000000..f3deade754c --- /dev/null +++ b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml @@ -0,0 +1,5 @@ +--- +title: Introduce new keep-alive API entrypoint for CI job +merge_request: 19543 +author: +type: added -- cgit v1.2.1 From 02a2ca6ae4ab6f2b64ff00f5750a68e9371a4fb7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 14:47:09 +0100 Subject: Fixes IDE button on merge requests not working with relative URL config Closes #46438 --- app/assets/javascripts/lib/utils/url_utility.js | 2 +- .../unreleased/ide-url-util-relative-url-fix.yml | 6 ++++++ spec/javascripts/lib/utils/url_utility_spec.js | 25 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/ide-url-util-relative-url-fix.yml create mode 100644 spec/javascripts/lib/utils/url_utility_spec.js diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index dd17544b656..061fb72ffef 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -87,7 +87,7 @@ export function redirectTo(url) { export function webIDEUrl(route = undefined) { let returnUrl = `${gon.relative_url_root}/-/ide/`; if (route) { - returnUrl += `project${route}`; + returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root}`), '')}`; } return returnUrl; } diff --git a/changelogs/unreleased/ide-url-util-relative-url-fix.yml b/changelogs/unreleased/ide-url-util-relative-url-fix.yml new file mode 100644 index 00000000000..9f0f4a0f7be --- /dev/null +++ b/changelogs/unreleased/ide-url-util-relative-url-fix.yml @@ -0,0 +1,6 @@ +--- +title: Fixes Web IDE button on merge requests when GitLab is installed with relative + URL +merge_request: +author: +type: fixed diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js new file mode 100644 index 00000000000..8ed4950ee09 --- /dev/null +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -0,0 +1,25 @@ +import { webIDEUrl } from '~/lib/utils/url_utility'; + +describe('URL utility', () => { + describe('webIDEUrl', () => { + describe('without relative_url_root', () => { + it('returns IDE path with route', () => { + expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + '/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', + ); + }); + }); + + describe('with relative_url_root', () => { + beforeEach(() => { + gon.relative_url_root = '/gitlab'; + }); + + it('returns IDE path with route', () => { + expect(webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + '/gitlab/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', + ); + }); + }); + }); +}); -- cgit v1.2.1 From 132db99a5b98daf7629f416ebb4ff817010cc0a9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 15:11:10 +0100 Subject: fixed karma --- app/assets/javascripts/lib/utils/url_utility.js | 4 ++-- spec/javascripts/lib/utils/url_utility_spec.js | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 061fb72ffef..72b72f4247d 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -85,9 +85,9 @@ export function redirectTo(url) { } export function webIDEUrl(route = undefined) { - let returnUrl = `${gon.relative_url_root}/-/ide/`; + let returnUrl = `${gon.relative_url_root || ''}/-/ide/`; if (route) { - returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root}`), '')}`; + returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root || ''}`), '')}`; } return returnUrl; } diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js index 8ed4950ee09..c7f4092911c 100644 --- a/spec/javascripts/lib/utils/url_utility_spec.js +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -2,6 +2,10 @@ import { webIDEUrl } from '~/lib/utils/url_utility'; describe('URL utility', () => { describe('webIDEUrl', () => { + afterEach(() => { + gon.relative_url_root = ''; + }); + describe('without relative_url_root', () => { it('returns IDE path with route', () => { expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( -- cgit v1.2.1 From e3a59a931aae7601da370addce5ae37fd2888516 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 7 Jun 2018 11:59:55 +0100 Subject: Fix text colour in performance bar request selector --- app/assets/stylesheets/performance_bar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss index 06ef58531d7..8cdf2275551 100644 --- a/app/assets/stylesheets/performance_bar.scss +++ b/app/assets/stylesheets/performance_bar.scss @@ -15,6 +15,7 @@ color: $perf-bar-text; select { + color: $perf-bar-text; width: 200px; } -- cgit v1.2.1 From cb6a16a2c6b15bbd51c02e74a6086ae33e83d3c8 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 15:53:14 +0100 Subject: fixed more specs --- .../vue_mr_widget/components/mr_widget_header_spec.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 9b9c9656979..3d36e46d863 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -12,6 +12,7 @@ describe('MRWidgetHeader', () => { afterEach(() => { vm.$destroy(); + gon.relative_url_root = ''; }); describe('computed', () => { @@ -145,7 +146,16 @@ describe('MRWidgetHeader', () => { const button = vm.$el.querySelector('.js-web-ide'); expect(button.textContent.trim()).toEqual('Web IDE'); - expect(button.getAttribute('href')).toEqual('undefined/-/ide/projectabc'); + expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); + }); + + it('renders web ide button with relative URL', () => { + gon.relative_url_root = '/gitlab'; + + const button = vm.$el.querySelector('.js-web-ide'); + + expect(button.textContent.trim()).toEqual('Web IDE'); + expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); }); it('renders download dropdown with links', () => { -- cgit v1.2.1 From 0b4312c676ec5d7d07cf866a80dda5da2dbe1b9e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jun 2018 15:16:04 -0500 Subject: Update rspec to 3.7 --- Gemfile | 2 +- Gemfile.lock | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Gemfile b/Gemfile index 339761bf8fa..6d7259490fb 100644 --- a/Gemfile +++ b/Gemfile @@ -332,7 +332,7 @@ group :development, :test do gem 'database_cleaner', '~> 1.5.0' gem 'factory_bot_rails', '~> 4.8.2' - gem 'rspec-rails', '~> 3.6.0' + gem 'rspec-rails', '~> 3.7.0' gem 'rspec-retry', '~> 0.4.5' gem 'rspec_profiling', '~> 0.0.5' gem 'rspec-set', '~> 0.1.3' diff --git a/Gemfile.lock b/Gemfile.lock index ff9e81bdc9f..0a3c3c461b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -742,36 +742,36 @@ GEM chunky_png rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) - rspec (3.6.0) - rspec-core (~> 3.6.0) - rspec-expectations (~> 3.6.0) - rspec-mocks (~> 3.6.0) - rspec-core (3.6.0) - rspec-support (~> 3.6.0) - rspec-expectations (3.6.0) + rspec (3.7.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-core (3.7.1) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-mocks (3.6.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) + rspec-support (~> 3.7.0) rspec-parameterized (0.4.0) binding_of_caller parser proc_to_ast rspec (>= 2.13, < 4) unparser - rspec-rails (3.6.0) + rspec-rails (3.7.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.6.0) - rspec-expectations (~> 3.6.0) - rspec-mocks (~> 3.6.0) - rspec-support (~> 3.6.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-support (~> 3.7.0) rspec-retry (0.4.5) rspec-core rspec-set (0.1.3) - rspec-support (3.6.0) + rspec-support (3.7.1) rspec_profiling (0.0.5) activerecord pg @@ -1140,7 +1140,7 @@ DEPENDENCIES rouge (~> 3.1) rqrcode-rails3 (~> 0.1.7) rspec-parameterized - rspec-rails (~> 3.6.0) + rspec-rails (~> 3.7.0) rspec-retry (~> 0.4.5) rspec-set (~> 0.1.3) rspec_profiling (~> 0.0.5) -- cgit v1.2.1 From 6d165c740cecf6aff4c7ec0bbb962e7964c15f1b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jun 2018 16:18:06 -0500 Subject: Make all uses of `fixture_file_upload` use relative paths --- spec/controllers/boards/issues_controller_spec.rb | 2 +- spec/controllers/groups/avatars_controller_spec.rb | 2 +- spec/controllers/import/gitlab_projects_controller_spec.rb | 2 +- spec/controllers/import/google_code_controller_spec.rb | 2 +- spec/controllers/profiles/avatars_controller_spec.rb | 2 +- spec/controllers/projects/avatars_controller_spec.rb | 2 +- spec/controllers/projects_controller_spec.rb | 4 ++-- spec/controllers/uploads_controller_spec.rb | 14 +++++++------- spec/factories/lfs_objects.rb | 2 +- spec/factories/notes.rb | 4 ++-- spec/features/commits_spec.rb | 2 +- .../merge_request/user_sees_mini_pipeline_graph_spec.rb | 4 ++-- spec/features/projects/jobs_spec.rb | 2 +- spec/features/projects/pages_spec.rb | 4 ++-- spec/lib/gitlab/import_export/avatar_saver_spec.rb | 2 +- spec/lib/gitlab/import_export/uploads_saver_spec.rb | 2 +- spec/models/ci/job_artifact_spec.rb | 4 ++-- spec/requests/api/projects_spec.rb | 4 ++-- spec/requests/api/runner_spec.rb | 4 ++-- spec/requests/api/users_spec.rb | 2 +- spec/requests/openid_connect_spec.rb | 2 +- spec/services/projects/fork_service_spec.rb | 2 +- .../projects/gitlab_projects_import_service_spec.rb | 2 +- spec/services/projects/participants_service_spec.rb | 2 +- spec/services/projects/update_pages_service_spec.rb | 10 +++++----- spec/services/upload_service_spec.rb | 10 +++++----- spec/support/rspec.rb | 2 ++ .../controllers/uploads_actions_shared_examples.rb | 4 ++-- spec/uploaders/attachment_uploader_spec.rb | 2 +- spec/uploaders/file_mover_spec.rb | 2 +- spec/uploaders/file_uploader_spec.rb | 2 +- spec/uploaders/job_artifact_uploader_spec.rb | 2 +- spec/uploaders/namespace_file_uploader_spec.rb | 2 +- spec/uploaders/object_storage_spec.rb | 2 +- spec/uploaders/personal_file_uploader_spec.rb | 2 +- spec/uploaders/records_uploads_spec.rb | 2 +- spec/uploaders/uploader_helper_spec.rb | 2 +- 37 files changed, 60 insertions(+), 58 deletions(-) diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index dcb0faffbd4..e47ff8661a2 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -18,7 +18,7 @@ describe Boards::IssuesController do end describe 'GET index', :request_store do - let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) } + let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join('spec/fixtures/dk.png'))) } context 'with invalid board id' do it 'returns a not found 404 response' do diff --git a/spec/controllers/groups/avatars_controller_spec.rb b/spec/controllers/groups/avatars_controller_spec.rb index 506aeee7d2a..7feecd0c380 100644 --- a/spec/controllers/groups/avatars_controller_spec.rb +++ b/spec/controllers/groups/avatars_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Groups::AvatarsController do let(:user) { create(:user) } - let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } before do group.add_owner(user) diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb index 8759d3c0b97..d624659bce9 100644 --- a/spec/controllers/import/gitlab_projects_controller_spec.rb +++ b/spec/controllers/import/gitlab_projects_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Import::GitlabProjectsController do set(:namespace) { create(:namespace) } set(:user) { namespace.owner } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } before do sign_in(user) diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb index 4241db6e771..0763492d88a 100644 --- a/spec/controllers/import/google_code_controller_spec.rb +++ b/spec/controllers/import/google_code_controller_spec.rb @@ -4,7 +4,7 @@ describe Import::GoogleCodeController do include ImportSpecHelper let(:user) { create(:user) } - let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') } + let(:dump_file) { fixture_file_upload('spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') } before do sign_in(user) diff --git a/spec/controllers/profiles/avatars_controller_spec.rb b/spec/controllers/profiles/avatars_controller_spec.rb index 4fa0462ccdf..909709e1103 100644 --- a/spec/controllers/profiles/avatars_controller_spec.rb +++ b/spec/controllers/profiles/avatars_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Profiles::AvatarsController do - let(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png")) } + let(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png")) } before do sign_in(user) diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb index 6a41c4d23ea..acfa2730d94 100644 --- a/spec/controllers/projects/avatars_controller_spec.rb +++ b/spec/controllers/projects/avatars_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::AvatarsController do - let(:project) { create(:project, :repository, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 994da3cd159..5bd22ea803c 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -6,8 +6,8 @@ describe ProjectsController do let(:project) { create(:project) } let(:public_project) { create(:project, :public) } let(:user) { create(:user) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } describe 'GET new' do context 'with an authenticated user' do diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 376b229ffc9..5892daa1a94 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -6,13 +6,13 @@ shared_examples 'content not cached without revalidation' do end describe UploadsController do - let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } describe 'POST create' do let(:model) { 'personal_snippet' } let(:snippet) { create(:personal_snippet, :public) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } context 'when a user does not have permissions to upload a file' do it "returns 401 when the user is not logged in" do @@ -205,7 +205,7 @@ describe UploadsController do end context "when viewing a project avatar" do - let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:project) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } context "when the project is public" do before do @@ -314,7 +314,7 @@ describe UploadsController do end context "when viewing a group avatar" do - let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } context "when the group is public" do context "when not signed in" do @@ -521,7 +521,7 @@ describe UploadsController do context 'Appearance' do context 'when viewing a custom header logo' do - let!(:appearance) { create :appearance, header_logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let!(:appearance) { create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } context 'when not signed in' do it 'responds with status 200' do @@ -541,7 +541,7 @@ describe UploadsController do end context 'when viewing a custom logo' do - let!(:appearance) { create :appearance, logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let!(:appearance) { create :appearance, logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } context 'when not signed in' do it 'responds with status 200' do diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index eaf3a4ed497..c909471bb55 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -7,7 +7,7 @@ FactoryBot.define do end trait :with_file do - file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } + file { fixture_file_upload("spec/fixtures/dk.png", "`/png") } end # The uniqueness constraint means we can't use the correct OID for all LFS diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 40f3fa7d69b..9fdc3e616a6 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -130,11 +130,11 @@ FactoryBot.define do end trait :with_attachment do - attachment { fixture_file_upload(Rails.root.join( "spec/fixtures/dk.png"), "image/png") } + attachment { fixture_file_upload("spec/fixtures/dk.png", "image/png") } end trait :with_svg_attachment do - attachment { fixture_file_upload(Rails.root.join("spec/fixtures/unsanitized.svg"), "image/svg+xml") } + attachment { fixture_file_upload("spec/fixtures/unsanitized.svg", "image/svg+xml") } end transient do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 62a2ec55b00..87fa3f60826 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -47,7 +47,7 @@ describe 'Commits' do context 'commit status is Ci Build' do let!(:build) { create(:ci_build, pipeline: pipeline) } - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:artifacts_file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } context 'when logged as developer' do before do diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb index fd1629746ef..d3104b448e0 100644 --- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb +++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb @@ -23,8 +23,8 @@ describe 'Merge request < User sees mini pipeline graph', :js do end context 'as json' do - let(:artifacts_file1) { fixture_file_upload(Rails.root.join('spec/fixtures/banana_sample.gif'), 'image/gif') } - let(:artifacts_file2) { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') } + let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') } before do create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file1) diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 9d1c4cbad8b..d2aaf60e72c 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -11,7 +11,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do let(:job2) { create(:ci_build) } let(:artifacts_file) do - fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') end before do diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index bdd49f731c7..a2899ec0f48 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -314,8 +314,8 @@ feature 'Pages' do project: project, pipeline: pipeline, ref: 'HEAD', - legacy_artifacts_file: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip')), - legacy_artifacts_metadata: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip.meta')) + legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')), + legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta')) ) end diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index f40d4bc2d08..2223f163177 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::AvatarSaver do let(:shared) { project.import_export_shared } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:project_with_avatar) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:project_with_avatar) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } let(:project) { create(:project) } before do diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb index 1304d8fabfc..095687fa89d 100644 --- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::UploadsSaver do describe 'bundle a project Git repo' do let(:export_path) { "#{Dir.tmpdir}/uploads_saver_spec" } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } let(:shared) { project.import_export_shared } before do diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index a3e119cbc27..efddd4e7662 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -76,12 +76,12 @@ describe Ci::JobArtifact do context 'updating the artifact file' do it 'updates the artifact size' do - artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) + artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png')) expect(artifact.size).to eq(1062) end it 'updates the project statistics' do - expect { artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) } + expect { artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png')) } .to change { artifact.project.statistics.reload.build_artifacts_size } .by(1062 - 106365) end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 9b7c3205c1f..99103039f77 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -518,7 +518,7 @@ describe API::Projects do end it 'uploads avatar for project a project' do - project = attributes_for(:project, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')) + project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif')) post api('/projects', user), project @@ -777,7 +777,7 @@ describe API::Projects do end it "uploads the file and returns its info" do - post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload("spec/fixtures/dk.png", "image/png") expect(response).to have_gitlab_http_status(201) expect(json_response['alt']).to eq("dk") diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c981a10ac38..9b167a5ed23 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1055,8 +1055,8 @@ describe API::Runner, :clean_gitlab_redis_shared_state do let(:jwt_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt_token } } let(:headers_with_token) { headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.token) } - let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } - let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } + let(:file_upload) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } + let(:file_upload2) { fixture_file_upload('spec/fixtures/dk.png', 'image/gif') } before do stub_artifacts_object_storage diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 05637eb0729..3377d67b644 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -512,7 +512,7 @@ describe API::Users do end it 'updates user with avatar' do - put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + put api("/users/#{user.id}", admin), { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } user.reload diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index be286c490fe..bcb8d6c2bfc 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -61,7 +61,7 @@ describe 'OpenID Connect requests' do email: private_email.email, public_email: public_email.email, website_url: 'https://example.com', - avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png") + avatar: fixture_file_upload('spec/fixtures/dk.png') ) end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index a93f6f1ddc2..c15f5120b8a 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -8,7 +8,7 @@ describe Projects::ForkService do before do @from_user = create(:user) @from_namespace = @from_user.namespace - avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + avatar = fixture_file_upload("spec/fixtures/dk.png", "image/png") @from_project = create(:project, :repository, creator_id: @from_user.id, diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb index ee1a886f5d6..0a898e9b89b 100644 --- a/spec/services/projects/gitlab_projects_import_service_spec.rb +++ b/spec/services/projects/gitlab_projects_import_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::GitlabProjectsImportService do set(:namespace) { create(:namespace) } let(:path) { 'test-path' } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } let(:overwrite) { false } let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } } subject { described_class.new(namespace.owner, import_params) } diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb index 0d18ceb8ff8..6040f9100f8 100644 --- a/spec/services/projects/participants_service_spec.rb +++ b/spec/services/projects/participants_service_spec.rb @@ -4,7 +4,7 @@ describe Projects::ParticipantsService do describe '#groups' do describe 'avatar_url' do let(:project) { create(:project, :public) } - let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/dk.png')) } + let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png')) } let(:user) { create(:user) } let!(:group_member) { create(:group_member, group: group, user: user) } diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 347ac13828c..35a9b744b10 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -4,11 +4,11 @@ describe Projects::UpdatePagesService do set(:project) { create(:project, :repository) } set(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) } set(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') } - let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') } + let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') } let(:extension) { 'zip' } - let(:file) { fixture_file_upload(Rails.root + "spec/fixtures/pages.#{extension}") } - let(:empty_file) { fixture_file_upload(Rails.root + "spec/fixtures/pages_empty.#{extension}") } + let(:file) { fixture_file_upload("spec/fixtures/pages.#{extension}") } + let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.#{extension}") } let(:metadata) do filename = Rails.root + "spec/fixtures/pages.#{extension}.meta" fixture_file_upload(filename) if File.exist?(filename) @@ -196,8 +196,8 @@ describe Projects::UpdatePagesService do let(:metadata) { spy('metadata') } before do - file = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip') - metafile = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') + file = fixture_file_upload('spec/fixtures/pages.zip') + metafile = fixture_file_upload('spec/fixtures/pages.zip.meta') build.update_attributes(legacy_artifacts_file: file) build.update_attributes(legacy_artifacts_metadata: metafile) diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb index 24f3a5c5ff0..9b232a52efa 100644 --- a/spec/services/upload_service_spec.rb +++ b/spec/services/upload_service_spec.rb @@ -9,7 +9,7 @@ describe UploadService do context 'for valid gif file' do before do - gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + gif = fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') @link_to_file = upload_file(@project, gif) end @@ -21,7 +21,7 @@ describe UploadService do context 'for valid png file' do before do - png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', + png = fixture_file_upload('spec/fixtures/dk.png', 'image/png') @link_to_file = upload_file(@project, png) end @@ -34,7 +34,7 @@ describe UploadService do context 'for valid jpg file' do before do - jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') + jpg = fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') @link_to_file = upload_file(@project, jpg) end @@ -46,7 +46,7 @@ describe UploadService do context 'for txt file' do before do - txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') + txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') @link_to_file = upload_file(@project, txt) end @@ -58,7 +58,7 @@ describe UploadService do context 'for too large a file' do before do - txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') + txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') allow(txt).to receive(:size) { 1000.megabytes.to_i } @link_to_file = upload_file(@project, txt) end diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index dffab22d8b5..54b8df7aa19 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -9,4 +9,6 @@ RSpec.configure do |config| config.include StubConfiguration config.include StubObjectStorage config.include StubENV + + config.fixture_path = Rails.root if defined?(Rails) end diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index ea7dbade171..bbbad86dcd5 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -1,7 +1,7 @@ shared_examples 'handle uploads' do let(:user) { create(:user) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } let(:secret) { FileUploader.generate_secret } let(:uploader_class) { FileUploader } diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb index d302c14efb9..a9415854d25 100644 --- a/spec/uploaders/attachment_uploader_spec.rb +++ b/spec/uploaders/attachment_uploader_spec.rb @@ -26,7 +26,7 @@ describe AttachmentUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt'))) stub_uploads_object_storage end diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb index 68b7e24776d..de29d0c943f 100644 --- a/spec/uploaders/file_mover_spec.rb +++ b/spec/uploaders/file_mover_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe FileMover do let(:filename) { 'banana_sample.gif' } - let(:file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) } + let(:file) { fixture_file_upload(File.join('spec', 'fixtures', filename)) } let(:temp_file_path) { File.join('uploads/-/system/temp', 'secret55', filename) } let(:temp_description) do diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb index db2810bbe1d..59013a02938 100644 --- a/spec/uploaders/file_uploader_spec.rb +++ b/spec/uploaders/file_uploader_spec.rb @@ -89,7 +89,7 @@ describe FileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))) + uploader.store!(fixture_file_upload('spec/fixtures/dk.png')) stub_uploads_object_storage end diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb index 42036d67f3d..179c94ec3bc 100644 --- a/spec/uploaders/job_artifact_uploader_spec.rb +++ b/spec/uploaders/job_artifact_uploader_spec.rb @@ -81,7 +81,7 @@ describe JobArtifactUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/trace/sample_trace'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/trace/sample_trace'))) stub_artifacts_object_storage end diff --git a/spec/uploaders/namespace_file_uploader_spec.rb b/spec/uploaders/namespace_file_uploader_spec.rb index a8ba01d70b8..71fe2c353c0 100644 --- a/spec/uploaders/namespace_file_uploader_spec.rb +++ b/spec/uploaders/namespace_file_uploader_spec.rb @@ -28,7 +28,7 @@ describe NamespaceFileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt'))) stub_uploads_object_storage end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 4503288e410..0bc5b6751b3 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -571,7 +571,7 @@ describe ObjectStorage do context 'when local file is used' do context 'when valid file is used' do let(:uploaded_file) do - fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') + fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') end it "properly caches the file" do diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb index c70521d90dc..7700b14ce6b 100644 --- a/spec/uploaders/personal_file_uploader_spec.rb +++ b/spec/uploaders/personal_file_uploader_spec.rb @@ -45,7 +45,7 @@ describe PersonalFileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) stub_uploads_object_storage end diff --git a/spec/uploaders/records_uploads_spec.rb b/spec/uploaders/records_uploads_spec.rb index 9a3e5d83e01..3592a11360d 100644 --- a/spec/uploaders/records_uploads_spec.rb +++ b/spec/uploaders/records_uploads_spec.rb @@ -16,7 +16,7 @@ describe RecordsUploads do end def upload_fixture(filename) - fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + fixture_file_upload(File.join('spec', 'fixtures', filename)) end describe 'callbacks' do diff --git a/spec/uploaders/uploader_helper_spec.rb b/spec/uploaders/uploader_helper_spec.rb index c47f09adb6d..33da93cc9d0 100644 --- a/spec/uploaders/uploader_helper_spec.rb +++ b/spec/uploaders/uploader_helper_spec.rb @@ -12,7 +12,7 @@ describe UploaderHelper do end def upload_fixture(filename) - fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + fixture_file_upload(File.join('spec', 'fixtures', filename)) end describe '#image_or_video?' do -- cgit v1.2.1 From b38c75b3dc2dc655377a1b4070cb6f5242594dbe Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 6 Jun 2018 16:26:00 -0500 Subject: Correct more usages of non-relative `fixture_file_upload` paths --- spec/features/projects/jobs/permissions_spec.rb | 3 +-- spec/helpers/emails_helper_spec.rb | 4 +--- spec/helpers/groups_helper_spec.rb | 8 ++++---- spec/requests/api/project_import_spec.rb | 2 +- spec/services/projects/update_pages_service_spec.rb | 2 +- spec/uploaders/gitlab_uploader_spec.rb | 2 +- spec/uploaders/job_artifact_uploader_spec.rb | 8 +++----- spec/uploaders/legacy_artifact_uploader_spec.rb | 3 +-- 8 files changed, 13 insertions(+), 19 deletions(-) diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index 31abadf9bd6..e9588daf37d 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -88,8 +88,7 @@ describe 'Project Jobs Permissions' do describe 'artifacts page' do context 'when recent job has artifacts available' do before do - artifacts = Rails.root.join('spec/fixtures/ci_build_artifacts.zip') - archive = fixture_file_upload(artifacts, 'application/zip') + archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip') job.update_attributes(legacy_artifacts_file: archive) end diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index 2390c1f3e5d..139387e0b24 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -47,9 +47,7 @@ describe EmailsHelper do describe '#header_logo' do context 'there is a brand item with a logo' do it 'returns the brand header logo' do - appearance = create :appearance, header_logo: fixture_file_upload( - Rails.root.join('spec/fixtures/dk.png') - ) + appearance = create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png') expect(header_logo).to eq( %{Dk} diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index b48c252acd3..6c94bd4e504 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -4,9 +4,9 @@ describe GroupsHelper do include ApplicationHelper describe 'group_icon' do - avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') - it 'returns an url for the avatar' do + avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif') + group = create(:group) group.avatar = fixture_file_upload(avatar_file_path) group.save! @@ -17,9 +17,9 @@ describe GroupsHelper do end describe 'group_icon_url' do - avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') - it 'returns an url for the avatar' do + avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif') + group = create(:group) group.avatar = fixture_file_upload(avatar_file_path) group.save! diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index f8c64f063af..97dffdc9233 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::ProjectImport do let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } let(:user) { create(:user) } - let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:file) { File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } let(:namespace) { create(:group) } before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 35a9b744b10..1bffeee6790 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -10,7 +10,7 @@ describe Projects::UpdatePagesService do let(:file) { fixture_file_upload("spec/fixtures/pages.#{extension}") } let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.#{extension}") } let(:metadata) do - filename = Rails.root + "spec/fixtures/pages.#{extension}.meta" + filename = "spec/fixtures/pages.#{extension}.meta" fixture_file_upload(filename) if File.exist?(filename) end diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb index 4fba122cce1..362f89424d4 100644 --- a/spec/uploaders/gitlab_uploader_spec.rb +++ b/spec/uploaders/gitlab_uploader_spec.rb @@ -62,7 +62,7 @@ describe GitlabUploader do expect(FileUtils).to receive(:mv).with(anything, /^#{subject.work_dir}/).and_call_original expect(FileUtils).to receive(:mv).with(/^#{subject.work_dir}/, /#{subject.cache_dir}/).and_call_original - fixture = Rails.root.join('spec', 'fixtures', 'rails_sample.jpg') + fixture = File.join('spec', 'fixtures', 'rails_sample.jpg') subject.cache!(fixture_file_upload(fixture)) expect(subject.file.path).to match(/#{subject.cache_dir}/) diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb index 179c94ec3bc..026e4356ed6 100644 --- a/spec/uploaders/job_artifact_uploader_spec.rb +++ b/spec/uploaders/job_artifact_uploader_spec.rb @@ -29,8 +29,7 @@ describe JobArtifactUploader do context 'when trace is stored in File storage' do context 'when file exists' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/trace/sample_trace'), 'text/plain') + fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain') end before do @@ -63,8 +62,7 @@ describe JobArtifactUploader do context 'file is stored in valid local_path' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip') + fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip') end before do @@ -81,7 +79,7 @@ describe JobArtifactUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(File.join('spec/fixtures/trace/sample_trace'))) + uploader.store!(fixture_file_upload('spec/fixtures/trace/sample_trace')) stub_artifacts_object_storage end diff --git a/spec/uploaders/legacy_artifact_uploader_spec.rb b/spec/uploaders/legacy_artifact_uploader_spec.rb index eeb6fd90c9d..0589563b502 100644 --- a/spec/uploaders/legacy_artifact_uploader_spec.rb +++ b/spec/uploaders/legacy_artifact_uploader_spec.rb @@ -44,8 +44,7 @@ describe LegacyArtifactUploader do context 'file is stored in valid path' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip') + fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip') end before do -- cgit v1.2.1 From 9f76632d80b5f1d9a47faa95f537e579b6ac41d6 Mon Sep 17 00:00:00 2001 From: Balasankar C Date: Thu, 7 Jun 2018 15:01:20 +0000 Subject: Add installation type to usage ping data --- INSTALLATION_TYPE | 1 + lib/gitlab.rb | 1 + lib/gitlab/usage_data.rb | 1 + spec/lib/gitlab/usage_data_spec.rb | 2 ++ 4 files changed, 5 insertions(+) create mode 100644 INSTALLATION_TYPE diff --git a/INSTALLATION_TYPE b/INSTALLATION_TYPE new file mode 100644 index 00000000000..5a18cd2fbf6 --- /dev/null +++ b/INSTALLATION_TYPE @@ -0,0 +1 @@ +source diff --git a/lib/gitlab.rb b/lib/gitlab.rb index a129746e2c6..b9a148f35bf 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -33,6 +33,7 @@ module Gitlab APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))} SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z} VERSION = File.read(root.join("VERSION")).strip.freeze + INSTALLATION_TYPE = File.read(root.join("INSTALLATION_TYPE")).strip.freeze def self.com? # Check `gl_subdomain?` as well to keep parity with gitlab.com diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index e294f3c4ebc..59a222b086c 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -21,6 +21,7 @@ module Gitlab uuid: Gitlab::CurrentSettings.uuid, hostname: Gitlab.config.gitlab.host, version: Gitlab::VERSION, + installation_type: Gitlab::INSTALLATION_TYPE, active_user_count: User.active.count, recorded_at: Time.now, mattermost_enabled: Gitlab.config.mattermost.enabled, diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index a716e6f5434..22d921716aa 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -32,6 +32,7 @@ describe Gitlab::UsageData do mattermost_enabled edition version + installation_type uuid hostname signup @@ -156,6 +157,7 @@ describe Gitlab::UsageData do it "gathers license data" do expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid) expect(subject[:version]).to eq(Gitlab::VERSION) + expect(subject[:installation_type]).to eq(Gitlab::INSTALLATION_TYPE) expect(subject[:active_user_count]).to eq(User.active.count) expect(subject[:recorded_at]).to be_a(Time) end -- cgit v1.2.1 From 1bcdd25ce24f2b43f5c24f5a296ff8f81c7a0759 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 7 Jun 2018 06:14:27 -0700 Subject: Avoid sequential scans loading schema_migrations table when loading application settings This was causing significant performance problems in production, and this commit reverts to the original behavior. --- lib/gitlab/current_settings.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 6cf7aa1bf0d..591a985a868 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -24,7 +24,20 @@ module Gitlab private def ensure_application_settings! + cached_application_settings || uncached_application_settings + end + + def cached_application_settings return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true' + + begin + ::ApplicationSetting.cached + rescue ::Redis::BaseError, ::Errno::ENOENT, ::Errno::EADDRNOTAVAIL + # In case Redis isn't running or the Redis UNIX socket file is not available + end + end + + def uncached_application_settings return fake_application_settings unless connect_to_db? current_settings = ::ApplicationSetting.current -- cgit v1.2.1 From 6afe6fa6bcb3bdb09bd49ba638a37af2a8c7a6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 7 Jun 2018 17:23:38 +0200 Subject: Make Gitlab::CurrentSettings.current_application_settings return cached settings early if they exist without issuing any DB query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/current_settings.rb | 6 +- spec/lib/gitlab/current_settings_spec.rb | 143 +++++++++++++++++++------------ 2 files changed, 92 insertions(+), 57 deletions(-) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 591a985a868..3cf35f499cd 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -32,8 +32,10 @@ module Gitlab begin ::ApplicationSetting.cached - rescue ::Redis::BaseError, ::Errno::ENOENT, ::Errno::EADDRNOTAVAIL - # In case Redis isn't running or the Redis UNIX socket file is not available + rescue + # In case Redis isn't running + # or the Redis UNIX socket file is not available + # or the DB is not running (we use migrations in the cache key) end end diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index 19028495f52..55490f37ac7 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -5,6 +5,13 @@ describe Gitlab::CurrentSettings do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end + shared_context 'with settings in cache' do + before do + create(:application_setting) + described_class.current_application_settings # warm the cache + end + end + describe '#current_application_settings', :use_clean_rails_memory_store_caching do it 'allows keys to be called directly' do db_settings = create(:application_setting, @@ -31,16 +38,29 @@ describe Gitlab::CurrentSettings do end context 'with DB unavailable' do - before do - # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues - # during the initialization phase of the test suite, so instead let's mock the internals of it - allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) + context 'and settings in cache' do + include_context 'with settings in cache' + + it 'fetches the settings from cache without issuing any query' do + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).not_to receive(:current) + context 'and no settings in cache' do + before do + # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues + # during the initialization phase of the test suite, so instead let's mock the internals of it + allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) + expect(ApplicationSetting).not_to receive(:current) + end - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + it 'returns an in-memory ApplicationSetting object' do + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end + + it 'does not issue any query' do + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end end @@ -52,73 +72,86 @@ describe Gitlab::CurrentSettings do ar_wrapped_defaults.slice(*::ApplicationSetting.defaults.keys) end - before do - # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues - # during the initialization phase of the test suite, so instead let's mock the internals of it - allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true) - allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true) - end + context 'and settings in cache' do + include_context 'with settings in cache' - it 'creates default ApplicationSettings if none are present' do - settings = described_class.current_application_settings - - expect(settings).to be_a(ApplicationSetting) - expect(settings).to be_persisted - expect(settings).to have_attributes(settings_from_defaults) + it 'fetches the settings from cache' do + # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues + # during the initialization phase of the test suite, so instead let's mock the internals of it + expect(ActiveRecord::Base.connection).not_to receive(:active?) + expect(ActiveRecord::Base.connection).not_to receive(:cached_table_exists?) + expect(ActiveRecord::Migrator).not_to receive(:needs_migration?) + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end - context 'with migrations pending' do + context 'and no settings in cache' do before do - expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true) + allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true) + allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true) end - it 'returns an in-memory ApplicationSetting object' do + it 'creates default ApplicationSettings if none are present' do settings = described_class.current_application_settings - expect(settings).to be_a(Gitlab::FakeApplicationSettings) - expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled) - expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled) + expect(settings).to be_a(ApplicationSetting) + expect(settings).to be_persisted + expect(settings).to have_attributes(settings_from_defaults) end - it 'uses the existing database settings and falls back to defaults' do - db_settings = create(:application_setting, - home_page_url: 'http://mydomain.com', - signup_enabled: false) - settings = described_class.current_application_settings - app_defaults = ApplicationSetting.last - - expect(settings).to be_a(Gitlab::FakeApplicationSettings) - expect(settings.home_page_url).to eq(db_settings.home_page_url) - expect(settings.signup_enabled?).to be_falsey - expect(settings.signup_enabled).to be_falsey - - # Check that unspecified values use the defaults - settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key } - settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) } + context 'with migrations pending' do + before do + expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true) + end + + it 'returns an in-memory ApplicationSetting object' do + settings = described_class.current_application_settings + + expect(settings).to be_a(Gitlab::FakeApplicationSettings) + expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled) + expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled) + end + + it 'uses the existing database settings and falls back to defaults' do + db_settings = create(:application_setting, + home_page_url: 'http://mydomain.com', + signup_enabled: false) + settings = described_class.current_application_settings + app_defaults = ApplicationSetting.last + + expect(settings).to be_a(Gitlab::FakeApplicationSettings) + expect(settings.home_page_url).to eq(db_settings.home_page_url) + expect(settings.signup_enabled?).to be_falsey + expect(settings.signup_enabled).to be_falsey + + # Check that unspecified values use the defaults + settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key } + settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) } + end end - end - context 'when ApplicationSettings.current is present' do - it 'returns the existing application settings' do - expect(ApplicationSetting).to receive(:current).and_return(:current_settings) + context 'when ApplicationSettings.current is present' do + it 'returns the existing application settings' do + expect(ApplicationSetting).to receive(:current).and_return(:current_settings) - expect(described_class.current_application_settings).to eq(:current_settings) + expect(described_class.current_application_settings).to eq(:current_settings) + end end - end - context 'when the application_settings table does not exists' do - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid) + context 'when the application_settings table does not exists' do + it 'returns an in-memory ApplicationSetting object' do + expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid) - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end end - end - context 'when the application_settings table is not fully migrated' do - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError) + context 'when the application_settings table is not fully migrated' do + it 'returns an in-memory ApplicationSetting object' do + expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError) - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end end end end -- cgit v1.2.1 From 36c337647591d964b7ef1e1fc61fc64a930fb6f4 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 7 Jun 2018 15:40:44 +0000 Subject: Resolve "Hashed Storage: Make possible to migrate single project" --- app/workers/storage_migrator_worker.rb | 25 +------- .../46922-hashed-storage-single-project.yml | 5 ++ doc/administration/raketasks/storage.md | 45 +++++++------ lib/gitlab/hashed_storage/migrator.rb | 57 ++++++++++++++++ lib/gitlab/hashed_storage/rake_helper.rb | 14 +++- lib/tasks/gitlab/storage.rake | 21 +++++- spec/lib/gitlab/hashed_storage/migrator_spec.rb | 75 ++++++++++++++++++++++ spec/tasks/gitlab/storage_rake_spec.rb | 45 ++++++++++--- spec/workers/storage_migrator_worker_spec.rb | 25 +++----- 9 files changed, 243 insertions(+), 69 deletions(-) create mode 100644 changelogs/unreleased/46922-hashed-storage-single-project.yml create mode 100644 lib/gitlab/hashed_storage/migrator.rb create mode 100644 spec/lib/gitlab/hashed_storage/migrator_spec.rb diff --git a/app/workers/storage_migrator_worker.rb b/app/workers/storage_migrator_worker.rb index f92421a667d..0aff0c4c7c6 100644 --- a/app/workers/storage_migrator_worker.rb +++ b/app/workers/storage_migrator_worker.rb @@ -1,29 +1,8 @@ class StorageMigratorWorker include ApplicationWorker - BATCH_SIZE = 100 - def perform(start, finish) - projects = build_relation(start, finish) - - projects.with_route.find_each(batch_size: BATCH_SIZE) do |project| - Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..." - - begin - project.migrate_to_hashed_storage! - rescue => err - Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}") - end - end - end - - def build_relation(start, finish) - relation = Project - table = Project.arel_table - - relation = relation.where(table[:id].gteq(start)) if start - relation = relation.where(table[:id].lteq(finish)) if finish - - relation + migrator = Gitlab::HashedStorage::Migrator.new + migrator.bulk_migrate(start, finish) end end diff --git a/changelogs/unreleased/46922-hashed-storage-single-project.yml b/changelogs/unreleased/46922-hashed-storage-single-project.yml new file mode 100644 index 00000000000..c293238a5a4 --- /dev/null +++ b/changelogs/unreleased/46922-hashed-storage-single-project.yml @@ -0,0 +1,5 @@ +--- +title: 'Hashed Storage: migration rake task now can be executed to specific project' +merge_request: 19268 +author: +type: changed diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md index cfd601b8866..7ad38abe4f5 100644 --- a/doc/administration/raketasks/storage.md +++ b/doc/administration/raketasks/storage.md @@ -17,13 +17,21 @@ This task will schedule all your existing projects and attachments associated wi **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:migrate_to_hashed +sudo gitlab-rake gitlab:storage:migrate_to_hashed ``` **Source Installation** ```bash -rake gitlab:storage:migrate_to_hashed +sudo -u git -H bundle exec rake gitlab:storage:migrate_to_hashed RAILS_ENV=production +``` + +They both also accept a range as environment variable: + +```bash +# to migrate any non migrated project from ID 20 to 50. +export ID_FROM=20 +export ID_TO=50 ``` You can monitor the progress in the _Admin > Monitoring > Background jobs_ screen. @@ -44,13 +52,13 @@ To have a simple summary of projects using **Legacy** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:legacy_projects +sudo gitlab-rake gitlab:storage:legacy_projects ``` **Source Installation** ```bash -rake gitlab:storage:legacy_projects +sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production ``` ------ @@ -60,13 +68,13 @@ To list projects using **Legacy** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:list_legacy_projects +sudo gitlab-rake gitlab:storage:list_legacy_projects ``` **Source Installation** ```bash -rake gitlab:storage:list_legacy_projects +sudo -u git -H bundle exec rake gitlab:storage:list_legacy_projects RAILS_ENV=production ``` @@ -77,13 +85,13 @@ To have a simple summary of projects using **Hashed** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:hashed_projects +sudo gitlab-rake gitlab:storage:hashed_projects ``` **Source Installation** ```bash -rake gitlab:storage:hashed_projects +sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production ``` ------ @@ -93,14 +101,13 @@ To list projects using **Hashed** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:list_hashed_projects +sudo gitlab-rake gitlab:storage:list_hashed_projects ``` **Source Installation** ```bash -rake gitlab:storage:list_hashed_projects - +sudo -u git -H bundle exec rake gitlab:storage:list_hashed_projects RAILS_ENV=production ``` ## List attachments on Legacy storage @@ -110,13 +117,13 @@ To have a simple summary of project attachments using **Legacy** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:legacy_attachments +sudo gitlab-rake gitlab:storage:legacy_attachments ``` **Source Installation** ```bash -rake gitlab:storage:legacy_attachments +sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production ``` ------ @@ -126,13 +133,13 @@ To list project attachments using **Legacy** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:list_legacy_attachments +sudo gitlab-rake gitlab:storage:list_legacy_attachments ``` **Source Installation** ```bash -rake gitlab:storage:list_legacy_attachments +sudo -u git -H bundle exec rake gitlab:storage:list_legacy_attachments RAILS_ENV=production ``` ## List attachments on Hashed storage @@ -142,13 +149,13 @@ To have a simple summary of project attachments using **Hashed** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:hashed_attachments +sudo gitlab-rake gitlab:storage:hashed_attachments ``` **Source Installation** ```bash -rake gitlab:storage:hashed_attachments +sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production ``` ------ @@ -158,13 +165,13 @@ To list project attachments using **Hashed** storage: **Omnibus Installation** ```bash -gitlab-rake gitlab:storage:list_hashed_attachments +sudo gitlab-rake gitlab:storage:list_hashed_attachments ``` **Source Installation** ```bash -rake gitlab:storage:list_hashed_attachments +sudo -u git -H bundle exec rake gitlab:storage:list_hashed_attachments RAILS_ENV=production ``` [storage-types]: ../repository_storage_types.md diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb new file mode 100644 index 00000000000..9251ed654cd --- /dev/null +++ b/lib/gitlab/hashed_storage/migrator.rb @@ -0,0 +1,57 @@ +module Gitlab + module HashedStorage + # Hashed Storage Migrator + # + # This is responsible for scheduling and flagging projects + # to be migrated from Legacy to Hashed storage, either one by one or in bulk. + class Migrator + BATCH_SIZE = 100 + + # Schedule a range of projects to be bulk migrated with #bulk_migrate asynchronously + # + # @param [Object] start first project id for the range + # @param [Object] finish last project id for the range + def bulk_schedule(start, finish) + StorageMigratorWorker.perform_async(start, finish) + end + + # Start migration of projects from specified range + # + # Flagging a project to be migrated is a synchronous action, + # but the migration runs through async jobs + # + # @param [Object] start first project id for the range + # @param [Object] finish last project id for the range + def bulk_migrate(start, finish) + projects = build_relation(start, finish) + + projects.with_route.find_each(batch_size: BATCH_SIZE) do |project| + migrate(project) + end + end + + # Flag a project to me migrated + # + # @param [Object] project that will be migrated + def migrate(project) + Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..." + + project.migrate_to_hashed_storage! + rescue => err + Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}") + end + + private + + def build_relation(start, finish) + relation = Project + table = Project.arel_table + + relation = relation.where(table[:id].gteq(start)) if start + relation = relation.where(table[:id].lteq(finish)) if finish + + relation + end + end + end +end diff --git a/lib/gitlab/hashed_storage/rake_helper.rb b/lib/gitlab/hashed_storage/rake_helper.rb index 8aba42ccfce..303b05e6a9a 100644 --- a/lib/gitlab/hashed_storage/rake_helper.rb +++ b/lib/gitlab/hashed_storage/rake_helper.rb @@ -9,8 +9,20 @@ module Gitlab ENV.fetch('LIMIT', 500).to_i end + def self.range_from + ENV['ID_FROM'] + end + + def self.range_to + ENV['ID_TO'] + end + + def self.range_single_item? + !range_from.nil? && range_from == range_to + end + def self.project_id_batches(&block) - Project.with_unmigrated_storage.in_batches(of: batch_size, start: ENV['ID_FROM'], finish: ENV['ID_TO']) do |relation| # rubocop: disable Cop/InBatches + Project.with_unmigrated_storage.in_batches(of: batch_size, start: range_from, finish: range_to) do |relation| # rubocop: disable Cop/InBatches ids = relation.pluck(:id) yield ids.min, ids.max diff --git a/lib/tasks/gitlab/storage.rake b/lib/tasks/gitlab/storage.rake index 68d6f9d7cb1..f539b1df955 100644 --- a/lib/tasks/gitlab/storage.rake +++ b/lib/tasks/gitlab/storage.rake @@ -2,9 +2,26 @@ namespace :gitlab do namespace :storage do desc 'GitLab | Storage | Migrate existing projects to Hashed Storage' task migrate_to_hashed: :environment do - legacy_projects_count = Project.with_unmigrated_storage.count + storage_migrator = Gitlab::HashedStorage::Migrator.new helper = Gitlab::HashedStorage::RakeHelper + if helper.range_single_item? + project = Project.with_unmigrated_storage.find_by(id: helper.range_from) + + unless project + puts "There are no projects requiring storage migration with ID=#{helper.range_from}" + + next + end + + puts "Enqueueing storage migration of #{project.full_path} (ID=#{project.id})..." + storage_migrator.migrate(project) + + next + end + + legacy_projects_count = Project.with_unmigrated_storage.count + if legacy_projects_count == 0 puts 'There are no projects requiring storage migration. Nothing to do!' @@ -14,7 +31,7 @@ namespace :gitlab do print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{helper.batch_size}" helper.project_id_batches do |start, finish| - StorageMigratorWorker.perform_async(start, finish) + storage_migrator.bulk_schedule(start, finish) print '.' end diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb new file mode 100644 index 00000000000..813ae43b4d3 --- /dev/null +++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe Gitlab::HashedStorage::Migrator do + describe '#bulk_schedule' do + it 'schedules job to StorageMigratorWorker' do + Sidekiq::Testing.fake! do + expect { subject.bulk_schedule(1, 5) }.to change(StorageMigratorWorker.jobs, :size).by(1) + end + end + end + + describe '#bulk_migrate' do + let(:projects) { create_list(:project, 2, :legacy_storage) } + let(:ids) { projects.map(&:id) } + + it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do + Sidekiq::Testing.fake! do + expect { subject.bulk_migrate(ids.min, ids.max) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(2) + end + end + + it 'sets projects as read only' do + allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice + subject.bulk_migrate(ids.min, ids.max) + + projects.each do |project| + expect(project.reload.repository_read_only?).to be_truthy + end + end + + it 'rescues and log exceptions' do + allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) + expect { subject.bulk_migrate(ids.min, ids.max) }.not_to raise_error + end + + it 'delegates each project in specified range to #migrate' do + projects.each do |project| + expect(subject).to receive(:migrate).with(project) + end + + subject.bulk_migrate(ids.min, ids.max) + end + end + + describe '#migrate' do + let(:project) { create(:project, :legacy_storage, :empty_repo) } + + it 'enqueues job to ProjectMigrateHashedStorageWorker' do + Sidekiq::Testing.fake! do + expect { subject.migrate(project) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1) + end + end + + it 'rescues and log exceptions' do + allow(project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) + + expect { subject.migrate(project) }.not_to raise_error + end + + it 'sets project as read only' do + allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async) + subject.migrate(project) + + expect(project.reload.repository_read_only?).to be_truthy + end + + it 'migrate project' do + Sidekiq::Testing.inline! do + subject.migrate(project) + end + + expect(project.reload.hashed_storage?(:attachments)).to be_truthy + end + end +end diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb index 35e451b2f9a..233076ad6fa 100644 --- a/spec/tasks/gitlab/storage_rake_spec.rb +++ b/spec/tasks/gitlab/storage_rake_spec.rb @@ -1,6 +1,6 @@ require 'rake_helper' -describe 'gitlab:storage:*' do +describe 'rake gitlab:storage:*' do before do Rake.application.rake_require 'tasks/gitlab/storage' @@ -44,16 +44,18 @@ describe 'gitlab:storage:*' do end describe 'gitlab:storage:migrate_to_hashed' do + let(:task) { 'gitlab:storage:migrate_to_hashed' } + context '0 legacy projects' do it 'does nothing' do expect(StorageMigratorWorker).not_to receive(:perform_async) - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end context '3 legacy projects' do - let(:projects) { create_list(:project, 3, storage_version: 0) } + let(:projects) { create_list(:project, 3, :legacy_storage) } context 'in batches of 1' do before do @@ -65,7 +67,7 @@ describe 'gitlab:storage:*' do expect(StorageMigratorWorker).to receive(:perform_async).with(project.id, project.id) end - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end @@ -80,23 +82,48 @@ describe 'gitlab:storage:*' do expect(StorageMigratorWorker).to receive(:perform_async).with(first, last) end - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end end + + context 'with same id in range' do + it 'displays message when project cant be found' do + stub_env('ID_FROM', 99999) + stub_env('ID_TO', 99999) + + expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=99999/).to_stdout + end + + it 'displays a message when project exists but its already migrated' do + project = create(:project) + stub_env('ID_FROM', project.id) + stub_env('ID_TO', project.id) + + expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=#{project.id}/).to_stdout + end + + it 'enqueues migration when project can be found' do + project = create(:project, :legacy_storage) + stub_env('ID_FROM', project.id) + stub_env('ID_TO', project.id) + + expect { run_rake_task(task) }.to output(/Enqueueing storage migration .* \(ID=#{project.id}\)/).to_stdout + end + end end describe 'gitlab:storage:legacy_projects' do it_behaves_like 'rake entities summary', 'projects', 'Legacy' do let(:task) { 'gitlab:storage:legacy_projects' } - let(:create_collection) { create_list(:project, 3, storage_version: 0) } + let(:create_collection) { create_list(:project, 3, :legacy_storage) } end end describe 'gitlab:storage:list_legacy_projects' do it_behaves_like 'rake listing entities', 'projects', 'Legacy' do let(:task) { 'gitlab:storage:list_legacy_projects' } - let(:create_collection) { create_list(:project, 3, storage_version: 0) } + let(:create_collection) { create_list(:project, 3, :legacy_storage) } end end @@ -133,7 +160,7 @@ describe 'gitlab:storage:*' do describe 'gitlab:storage:hashed_attachments' do it_behaves_like 'rake entities summary', 'attachments', 'Hashed' do let(:task) { 'gitlab:storage:hashed_attachments' } - let(:project) { create(:project, storage_version: 2) } + let(:project) { create(:project) } let(:create_collection) { create_list(:upload, 3, model: project) } end end @@ -141,7 +168,7 @@ describe 'gitlab:storage:*' do describe 'gitlab:storage:list_hashed_attachments' do it_behaves_like 'rake listing entities', 'attachments', 'Hashed' do let(:task) { 'gitlab:storage:list_hashed_attachments' } - let(:project) { create(:project, storage_version: 2) } + let(:project) { create(:project) } let(:create_collection) { create_list(:upload, 3, model: project) } end end diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb index ff625164142..815432aacce 100644 --- a/spec/workers/storage_migrator_worker_spec.rb +++ b/spec/workers/storage_migrator_worker_spec.rb @@ -2,29 +2,24 @@ require 'spec_helper' describe StorageMigratorWorker do subject(:worker) { described_class.new } - let(:projects) { create_list(:project, 2, :legacy_storage) } + let(:projects) { create_list(:project, 2, :legacy_storage, :empty_repo) } + let(:ids) { projects.map(&:id) } describe '#perform' do - let(:ids) { projects.map(&:id) } + it 'delegates to MigratorService' do + expect_any_instance_of(Gitlab::HashedStorage::Migrator).to receive(:bulk_migrate).with(5, 10) - it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do - expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice - - worker.perform(ids.min, ids.max) + worker.perform(5, 10) end - it 'sets projects as read only' do - allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice - worker.perform(ids.min, ids.max) + it 'migrates projects in the specified range' do + Sidekiq::Testing.inline! do + worker.perform(ids.min, ids.max) + end projects.each do |project| - expect(project.reload.repository_read_only?).to be_truthy + expect(project.reload.hashed_storage?(:attachments)).to be_truthy end end - - it 'rescues and log exceptions' do - allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) - expect { worker.perform(ids.min, ids.max) }.not_to raise_error - end end end -- cgit v1.2.1 From ea2bd9cb4f8ec9652eea8122f75f059f4a71c9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Thu, 7 Jun 2018 15:49:01 +0000 Subject: Add git filter flag only if it is supported --- lib/gitlab/git/lfs_changes.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb index 320b2ad007b..b230289e7bf 100644 --- a/lib/gitlab/git/lfs_changes.rb +++ b/lib/gitlab/git/lfs_changes.rb @@ -39,7 +39,11 @@ module Gitlab end def git_all_pointers - params = { options: ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"], require_path: true } + params = { require_path: true } + + if Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0') + params[:options] = ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"] + end rev_list.all_objects(params) do |object_ids| Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids) -- cgit v1.2.1 From ce3c9776a97734ce282b7e85875a7cc457554c0f Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Thu, 7 Jun 2018 08:50:17 -0700 Subject: Fix mobile navbar --- app/assets/stylesheets/framework/header.scss | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index f2ac77819d5..6fbc624dee4 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -139,6 +139,8 @@ } .nav { + flex-wrap: nowrap; + > li:not(.d-none) a { @include media-breakpoint-down(xs) { margin-left: 0; @@ -158,11 +160,12 @@ } .navbar-toggler { + position: relative; right: -10px; border-radius: 0; min-width: 45px; padding: 0; - margin-right: -7px; + margin: $gl-padding-8 -7px $gl-padding-8 0; font-size: 14px; text-align: center; color: currentColor; @@ -186,6 +189,7 @@ display: -webkit-flex; display: flex; padding-right: 10px; + flex-direction: row; } li { @@ -290,6 +294,10 @@ margin: 8px; } } + + .dropdown-menu { + position: absolute; + } } .navbar-sub-nav { -- cgit v1.2.1 From dd7a59bf94849f578ebc8f65aa7acbd0ed7c8f8e Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 7 Jun 2018 21:38:27 +0530 Subject: Add timeframe helper methods --- .../javascripts/lib/utils/datetime_utility.js | 77 ++++++++++++++-------- spec/javascripts/datetime_utility_spec.js | 23 ++++--- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 0ff23bbb061..f850e818bdf 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -269,6 +269,17 @@ export const totalDaysInMonth = date => { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); }; +/** + * Returns number of days in a quarter from provided + * months array. + * + * @param {Array} quarter + */ +export const totalDaysInQuarter = quarter => quarter.reduce( + (acc, month) => acc + totalDaysInMonth(month), + 0, +); + /** * Returns list of Dates referring to Sundays of the month * based on provided date @@ -309,42 +320,27 @@ export const getSundays = date => { }; /** - * Returns list of Dates representing a timeframe of Months from month of provided date (inclusive) - * up to provided length - * - * For eg; - * If current month is January 2018 and `length` provided is `6` - * Then this method will return list of Date objects as follows; - * - * [ October 2017, November 2017, December 2017, January 2018, February 2018, March 2018 ] - * - * If current month is March 2018 and `length` provided is `3` - * Then this method will return list of Date objects as follows; - * - * [ February 2018, March 2018, April 2018 ] + * Returns list of Dates representing a timeframe of months from startDate and length * + * @param {Date} startDate * @param {Number} length - * @param {Date} date */ -export const getTimeframeWindow = (length, date) => { - if (!length) { +export const getTimeframeWindowFrom = (startDate, length) => { + if (!(startDate instanceof Date) || !length) { return []; } - const currentDate = date instanceof Date ? date : new Date(); - const currentMonthIndex = Math.floor(length / 2); - const timeframe = []; - - // Move date object backward to the first month of timeframe - currentDate.setDate(1); - currentDate.setMonth(currentDate.getMonth() - currentMonthIndex); - - // Iterate and update date for the size of length + // Iterate and set date for the size of length // and push date reference to timeframe list - for (let i = 0; i < length; i += 1) { - timeframe.push(new Date(currentDate.getTime())); - currentDate.setMonth(currentDate.getMonth() + 1); - } + const timeframe = new Array(length) + .fill() + .map( + (val, i) => new Date( + startDate.getFullYear(), + startDate.getMonth() + i, + 1, + ), + ); // Change date of last timeframe item to last date of the month timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1])); @@ -352,6 +348,29 @@ export const getTimeframeWindow = (length, date) => { return timeframe; }; +/** + * Returns count of day within current quarter from provided date + * and array of months for the quarter + * + * Eg; + * If date is 15 Feb 2018 + * and quarter is [Jan, Feb, Mar] + * + * Then 15th Feb is 46th day of the quarter + * Where 31 (days in Jan) + 15 (date of Feb). + * + * @param {Date} date + * @param {Array} quarter + */ +export const dayInQuarter = (date, quarter) => quarter.reduce((acc, month) => { + if (date.getMonth() > month.getMonth()) { + return acc + totalDaysInMonth(month); + } else if (date.getMonth() === month.getMonth()) { + return acc + date.getDate(); + } + return acc + 0; +}, 0); + window.gl = window.gl || {}; window.gl.utils = { ...(window.gl.utils || {}), diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js index a8d09202154..e224ed46d18 100644 --- a/spec/javascripts/datetime_utility_spec.js +++ b/spec/javascripts/datetime_utility_spec.js @@ -149,23 +149,22 @@ describe('getSundays', () => { }); }); -describe('getTimeframeWindow', () => { - it('returns array of dates representing a timeframe based on provided length and date', () => { - const date = new Date(2018, 0, 1); +describe('getTimeframeWindowFrom', () => { + it('returns array of date objects upto provided length start with provided startDate', () => { + const startDate = new Date(2018, 0, 1); const mockTimeframe = [ - new Date(2017, 9, 1), - new Date(2017, 10, 1), - new Date(2017, 11, 1), new Date(2018, 0, 1), new Date(2018, 1, 1), - new Date(2018, 2, 31), + new Date(2018, 2, 1), + new Date(2018, 3, 1), + new Date(2018, 4, 31), ]; - const timeframe = datetimeUtility.getTimeframeWindow(6, date); - - expect(timeframe.length).toBe(6); + const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5); + expect(timeframe.length).toBe(5); timeframe.forEach((timeframeItem, index) => { - expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBeTruthy(); - expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBeTruthy(); + console.log(timeframeItem); + expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true); + expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true); expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy(); }); }); -- cgit v1.2.1 From 2bc1835597446a1240cfb364d1943feb4080e675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 18:11:08 +0200 Subject: Use 0 continuous default for deploy strategy --- app/models/project_auto_devops.rb | 4 ++-- .../20180601213245_add_deploy_strategy_to_project_auto_devops.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index ed0428615a2..a6f728e478e 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -2,8 +2,8 @@ class ProjectAutoDevops < ActiveRecord::Base belongs_to :project enum deploy_strategy: { - manual: 0, - continuous: 1 + continuous: 0, + manual: 1 } scope :enabled, -> { where(enabled: true) } diff --git a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb index 0f6bd0dd581..6f50d428965 100644 --- a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb +++ b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb @@ -10,7 +10,7 @@ class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration disable_ddl_transaction! def up - add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 1, allow_null: false + add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false end def down -- cgit v1.2.1 From cb2a00fd6a31866f31201ea79d180547ecb58f4d Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Thu, 7 Jun 2018 11:14:34 -0500 Subject: Fix height of color inputs --- app/assets/stylesheets/bootstrap_migration.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index 79f580546c3..0c5b6171223 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -261,3 +261,7 @@ pre code { color: $white-light; } } + +input[type=color].form-control { + height: $input-height; +} -- cgit v1.2.1 From a6a900256e194c28d8342db9c2d67df784877c14 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 17:27:26 +0200 Subject: Change update entrypoint instead of adding new keep-alive one --- lib/api/helpers/runner.rb | 5 +++ lib/api/runner.rb | 22 ++--------- spec/requests/api/runner_spec.rb | 84 +++++++++++++--------------------------- 3 files changed, 35 insertions(+), 76 deletions(-) diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index 35ac0b4cbca..61eb88d3331 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -59,6 +59,11 @@ module API def max_artifacts_size Gitlab::CurrentSettings.max_artifacts_size.megabytes.to_i end + + def job_forbidden!(job, reason) + header 'Job-Status', job.status + forbidden!(reason) + end end end end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index ac62b83ba4a..dc102259ca8 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -125,7 +125,7 @@ module API end put '/:id' do job = authenticate_job! - forbidden!('Job is not running') unless job.running? + job_forbidden!(job, 'Job is not running') unless job.running? job.trace.set(params[:trace]) if params[:trace] @@ -133,6 +133,8 @@ module API project: job.project.full_path) case params[:state].to_s + when 'running' + job.touch if job.needs_touch? when 'success' job.success! when 'failed' @@ -140,22 +142,6 @@ module API end end - desc 'Marks job as live' do - http_codes [[200, 'Request accepted']] - end - params do - requires :id, type: Integer, desc: %q(Job's ID) - optional :token, type: String, desc: %q(Job's authentication token) - end - post '/:id/keep-alive' do - job = authenticate_job! - - job.touch if job.running? && job.needs_touch? - - status 200 - header 'Job-Status', job.status - end - desc 'Appends a patch to the job trace' do http_codes [[202, 'Trace was patched'], [400, 'Missing Content-Range header'], @@ -168,7 +154,7 @@ module API end patch '/:id/trace' do job = authenticate_job! - forbidden!('Job is not running') unless job.running? + job_forbidden!(job, 'Job is not running') unless job.running? error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range') content_range = request.headers['Content-Range'] diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c93612d7ada..57d238ff79b 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -816,6 +816,18 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(job.reload.trace.raw).to eq 'BUILD TRACE' end + + context 'when running state is sent' do + it 'updates update_at value' do + expect { update_job_after_time }.to change { job.reload.updated_at } + end + end + + context 'when other state is sent' do + it "doesn't update update_at value" do + expect { update_job_after_time(20.minutes, state: 'success') }.not_to change { job.reload.updated_at } + end + end end context 'when job has been erased' do @@ -838,6 +850,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do update_job(state: 'success', trace: 'BUILD TRACE UPDATED') expect(response).to have_gitlab_http_status(403) + expect(response.header['Job-Status']).to eq 'failed' expect(job.trace.raw).to eq 'Job failed' expect(job).to be_failed end @@ -847,66 +860,10 @@ describe API::Runner, :clean_gitlab_redis_shared_state do new_params = params.merge(token: token) put api("/jobs/#{job.id}"), new_params end - end - - describe 'POST /api/v4/jobs/:id/keep-alive' do - let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } - let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } - let(:update_interval) { 30.seconds } - - it 'returns correct response' do - keep_alive_job - - expect(response.status).to eq 200 - expect(response.header).to have_key 'Job-Status' - end - - it 'updates updated_at value' do - expect { keep_alive_job }.to change { job.updated_at } - end - - context 'when project for the build has been deleted' do - let(:job) do - create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) do |job| - job.project.update(pending_delete: true) - end - end - - it 'responds with forbidden' do - keep_alive_job - - expect(response.status).to eq(403) - end - end - context 'when job has been canceled' do - before do - job.cancel - end - - it 'returns job-status=canceled header' do - keep_alive_job - - expect(response.status).to eq 200 - expect(response.header['Job-Status']).to eq('canceled') - end - end - - context 'when job has been errased' do - let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } - - it 'rresponds with forbidden' do - keep_alive_job - - expect(response.status).to eq 403 - end - end - - def keep_alive_job(token = job.token, **params) - new_params = params.merge(token: token) + def update_job_after_time(update_interval = 20.minutes, state = 'running') Timecop.travel(job.updated_at + update_interval) do - post api("/jobs/#{job.id}/keep-alive"), new_params - job.reload + update_job(job.token, state: state) end end end @@ -1041,6 +998,17 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end end + + context 'when the job is canceled' do + before do + job.cancel + patch_the_trace + end + + it 'receives status in header' do + expect(response.header['Job-Status']).to eq 'canceled' + end + end end context 'when Runner makes a force-patch' do -- cgit v1.2.1 From 6139c013adb1877451cdbf254d0ea61cc3a571b0 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 17:28:38 +0200 Subject: Update CHANGELOG --- changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml index f3deade754c..0789fc34f27 100644 --- a/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml +++ b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml @@ -1,5 +1,5 @@ --- -title: Introduce new keep-alive API entrypoint for CI job +title: Make CI job update entrypoint to work as keep-alive endpoint merge_request: 19543 author: -type: added +type: changed -- cgit v1.2.1 From f60da4145d9107aaf5d4b618e567b689784a2bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 16:47:54 +0000 Subject: Add deployment strategy docs --- doc/topics/autodevops/index.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 4bcc7fc6512..522cecbcac7 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -234,6 +234,15 @@ in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that all the projects that haven't explicitly set an option will have Auto DevOps enabled by default. +### Deployment Strategy + +You can change the deployment strategy used by Auto DevOps from within the UI. + +The available options are: + +* Continuous deployment to production +* Automatic deployment to staging, manual deployment to production + ## Stages of Auto DevOps The following sections describe the stages of Auto DevOps. Read them carefully -- cgit v1.2.1 From c5d4f10b802c1223de130c5f95f6323140b8ff13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Thu, 7 Jun 2018 12:09:45 -0500 Subject: Pass the --in-commit-order arg to `git-rev-list` @jamedjo has found a good improvement in the performance of the LFS integrity check by passing this arg --- .../add-new-arg-to-git-rev-list-call.yml | 5 +++++ lib/gitlab/git/lfs_changes.rb | 26 +++++++++++++++++----- lib/gitlab/git/rev_list.rb | 3 ++- 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml diff --git a/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml b/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml new file mode 100644 index 00000000000..86680b6b117 --- /dev/null +++ b/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of LFS integrity check +merge_request: 19494 +author: +type: performance diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb index b230289e7bf..f3cc388ea41 100644 --- a/lib/gitlab/git/lfs_changes.rb +++ b/lib/gitlab/git/lfs_changes.rb @@ -30,7 +30,7 @@ module Gitlab def git_new_pointers(object_limit, not_in) @new_pointers ||= begin - rev_list.new_objects(not_in: not_in, require_path: true) do |object_ids| + rev_list.new_objects(rev_list_params(not_in: not_in)) do |object_ids| object_ids = object_ids.take(object_limit) if object_limit Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids) @@ -39,13 +39,12 @@ module Gitlab end def git_all_pointers - params = { require_path: true } - - if Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0') + params = {} + if rev_list_supports_new_options? params[:options] = ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"] end - rev_list.all_objects(params) do |object_ids| + rev_list.all_objects(rev_list_params(params)) do |object_ids| Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids) end end @@ -53,6 +52,23 @@ module Gitlab def rev_list Gitlab::Git::RevList.new(@repository, newrev: @newrev) end + + # We're passing the `--in-commit-order` arg to ensure we don't wait + # for git to traverse all commits before returning pointers. + # This is required in order to improve the performance of LFS integrity check + def rev_list_params(params = {}) + params[:options] ||= [] + params[:options] << "--in-commit-order" if rev_list_supports_new_options? + params[:require_path] = true + + params + end + + def rev_list_supports_new_options? + return @option_supported if defined?(@option_supported) + + @option_supported = Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0') + end end end end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index 33b641a090b..4e661eceffb 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -27,9 +27,10 @@ module Gitlab # # When given a block it will yield objects as a lazy enumerator so # the caller can limit work done instead of processing megabytes of data - def new_objects(require_path: nil, not_in: nil, &lazy_block) + def new_objects(options: [], require_path: nil, not_in: nil, &lazy_block) opts = { including: newrev, + options: options, excluding: not_in.nil? ? :all : not_in, require_path: require_path } -- cgit v1.2.1 From 56983fafbcd6d5d93c51d076b5b785c6635158d9 Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Wed, 6 Jun 2018 13:13:03 -0700 Subject: Update readonly inputs to use gitlab colors --- app/assets/stylesheets/framework/buttons.scss | 4 ++++ app/assets/stylesheets/framework/variables.scss | 14 +++++++++----- app/assets/stylesheets/pages/search.scss | 3 ++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 0115f542c88..88b174491dd 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -497,6 +497,10 @@ fieldset[disabled] .btn, } } +[readonly] { + cursor: default; +} + .btn-no-padding { padding: 0; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index dd7374c503e..730a0192de0 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -173,11 +173,6 @@ $border-gray-normal: darken($gray-normal, $darken-border-factor); $border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor); $border-gray-dark: darken($white-normal, $darken-border-factor); -/* - * Override Bootstrap 4 variables - */ -$secondary: $gray-light; - /* * UI elements */ @@ -810,3 +805,12 @@ Prometheus $prometheus-table-row-highlight-color: $theme-gray-100; $priority-label-empty-state-width: 114px; + +/* + * Override Bootstrap 4 variables + */ + +$secondary: $gray-light; +$input-disabled-bg: $gray-light; +$input-border-color: $theme-gray-200; +$input-color: $gl-text-color; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index a35c4ff7c80..5f15795a8e3 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -18,7 +18,8 @@ .file-finder-input:hover, .issuable-search-form:hover, .search-text-input:hover, -.form-control:hover { +.form-control:hover, +:not[readonly] { border-color: lighten($dropdown-input-focus-border, 20%); box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%); } -- cgit v1.2.1 From e4027a746cdf474671319d236f12ed3676ef3d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 17:25:16 +0000 Subject: Add option descriptions --- doc/topics/autodevops/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 522cecbcac7..1211529b8f7 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -241,7 +241,9 @@ You can change the deployment strategy used by Auto DevOps from within the UI. The available options are: * Continuous deployment to production + * This enables Auto Deploy - https://docs.gitlab.com/ee/topics/autodevops/#auto-deploy * Automatic deployment to staging, manual deployment to production + * The user is responsible for deploying to staging and production - https://docs.gitlab.com/ce/ci/environments.html#manually-deploying-to-environments ## Stages of Auto DevOps -- cgit v1.2.1 From 6ca8668f13b0c88ebc9eaad9eca20161fc925e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 17:26:19 +0000 Subject: Link option descriptions --- doc/topics/autodevops/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 1211529b8f7..cf85187a570 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -241,9 +241,9 @@ You can change the deployment strategy used by Auto DevOps from within the UI. The available options are: * Continuous deployment to production - * This enables Auto Deploy - https://docs.gitlab.com/ee/topics/autodevops/#auto-deploy + * This enables [Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/#auto-deploy) * Automatic deployment to staging, manual deployment to production - * The user is responsible for deploying to staging and production - https://docs.gitlab.com/ce/ci/environments.html#manually-deploying-to-environments + * The user is responsible for [deploying to staging and production](https://docs.gitlab.com/ce/ci/environments.html#manually-deploying-to-environments) ## Stages of Auto DevOps -- cgit v1.2.1 From 19ac6badbf9484aa488cf9c15453c030a185104e Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Thu, 7 Jun 2018 11:01:33 -0700 Subject: Update QA deploy key clone test to wait for job traces Otherwise if the completion status loaded before the trace, the test will fail. --- qa/qa/specs/features/project/deploy_key_clone_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb index 442ac312b4d..2fa5c3a1a4c 100644 --- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb +++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb @@ -92,7 +92,9 @@ module QA Page::Project::Pipeline::Show.act { go_to_first_job } Page::Project::Job::Show.perform do |job| - job.wait(reload: false) { job.completed? } + job.wait(reload: false) do + job.completed? && !job.has_css?('.js-build-refresh') + end expect(job.passed?).to be_truthy, "Job status did not become \"passed\"." expect(job.output).to include(sha1sum) -- cgit v1.2.1 From d64ab8df18db156ff44fb3c9f4ecef529c1c28e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lu=C3=ADs?= Date: Thu, 7 Jun 2018 18:06:31 +0000 Subject: Resolve "Show `failure_reason` and improve failed jobs tab in pipeline detail view" --- app/assets/stylesheets/framework/tables.scss | 84 ++++++++++++++++++++- app/assets/stylesheets/pages/builds.scss | 16 ++-- app/assets/stylesheets/pages/pipelines.scss | 86 ++++++++++++++++++++-- app/views/projects/pipelines/_with_tabs.html.haml | 44 ++++++++--- .../unreleased/44267-improve-failed-jobs-tab.yml | 5 ++ spec/features/projects/pipelines/pipeline_spec.rb | 10 +++ 6 files changed, 220 insertions(+), 25 deletions(-) create mode 100644 changelogs/unreleased/44267-improve-failed-jobs-tab.yml diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 10c23f6c407..6e1758d7677 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -39,6 +39,11 @@ table { &.wide { width: 55%; } + + &.table-th-transparent { + background: none; + color: $gl-text-color-secondary; + } } td { @@ -46,9 +51,86 @@ table { } } } + + &.responsive-table { + @include media-breakpoint-down(sm) { + thead { + display: none; + } + + td { + display: block; + color: $gl-text-color-secondary; + } + + tbody td.responsive-table-cell { + padding: $gl-padding 0; + width: 100%; + display: flex; + text-align: right; + align-items: center; + justify-content: space-between; + + &[data-column]::before { + content: attr(data-column); + display: block; + text-align: left; + padding-right: $gl-padding; + color: $gl-text-color-secondary; + } + + &:not([data-column]) { + flex-direction: row-reverse; + } + } + + tr.responsive-table-border-start, + tr.responsive-table-border-end { + display: block; + border: solid $gl-text-color-quaternary; + padding-left: 0; + padding-right: 0; + + > td { + border-color: $gl-text-color-quaternary; + + &, + &:last-child { + padding-left: $gl-padding; + padding-right: $gl-padding; + } + } + } + + tr.responsive-table-border-start { + border-width: 1px 1px 0; + border-radius: $border-radius-default $border-radius-default 0 0; + padding-top: 0; + padding-bottom: 0; + + > td:first-child { + border-top: 0; // always have the top border + } + + > td:last-child { + border-bottom: 1px solid $gl-text-color-quaternary; + } + } + + tr.responsive-table-border-end { + border-width: 0 1px 1px; + border-radius: 0 0 $border-radius-default $border-radius-default; + margin-bottom: 2 * $gl-padding; + + > :last-child { + border-bottom: 0; + } + } + } + } } -.responsive-table { +.responsive-table:not(table) { @include media-breakpoint-down(sm) { th { width: 100%; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 9213ccd4cdf..f030189af06 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -12,26 +12,22 @@ @keyframes blinking-dots { 0% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light, 0.2), - 24px 0 0 0 rgba($white-light, 0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2); } 25% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light, 2), - 24px 0 0 0 rgba($white-light, 0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 2), 24px 0 0 0 rgba($white-light, 0.2); } 75% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light, 0.2), - 24px 0 0 0 rgba($white-light, 1); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 1); } 100% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light, 0.2), - 24px 0 0 0 rgba($white-light, 0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2); } } @@ -71,6 +67,10 @@ .bash { display: block; } + + &.build-trace-rounded { + border-radius: $border-radius-base; + } } .top-bar { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index f85f66b9c0b..30428fd198d 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -321,18 +321,17 @@ } .build-failures { + th { + border-top: 0; + } + .build-state { padding: 20px 2px; .build-name { - float: right; font-weight: $gl-font-weight-normal; } - .ci-status-icon-failed svg { - vertical-align: middle; - } - .stage { color: $gl-text-color-secondary; font-weight: $gl-font-weight-normal; @@ -344,6 +343,81 @@ border: 0; line-height: initial; } + + .build-trace-row td { + border-top: 0; + border-bottom-width: 1px; + border-bottom-style: solid; + padding-top: 0; + } + + .build-trace { + width: 100%; + text-align: left; + margin-top: $gl-padding; + } + + .build-name { + width: 196px; + + a { + font-weight: $gl-font-weight-bold; + color: $gl-text-color; + text-decoration: none; + + &:focus, + &:hover { + text-decoration: underline; + } + } + } + + .build-actions { + width: 70px; + text-align: right; + } + + .build-stage { + width: 140px; + } + + .ci-status-icon-failed { + padding: 10px 0 10px 12px; + width: 12px + 24px; // padding-left + svg width + } + + .build-icon svg { + width: 24px; + height: 24px; + vertical-align: middle; + } + + .build-state, + .build-trace-row { + > td:last-child { + padding-right: 0; + } + } + + @include media-breakpoint-down(sm) { + td:empty { + display: none; + } + + .ci-table { + margin-top: 2 * $gl-padding; + } + + .build-trace-container { + padding-top: $gl-padding; + padding-bottom: $gl-padding; + } + + .build-trace { + margin-bottom: 0; + margin-top: 0; + } + } } .pipeline-tab-content { @@ -929,7 +1003,7 @@ button.mini-pipeline-graph-dropdown-toggle { &.dropdown-menu { transform: translate(-80%, 0); - @media(min-width: map-get($grid-breakpoints, md)) { + @media (min-width: map-get($grid-breakpoints, md)) { transform: translate(-50%, 0); right: auto; left: 50%; diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 118391aac64..951f80b378d 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -1,7 +1,7 @@ .tabs-holder %ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator.nav.nav-tabs %li.js-pipeline-tab-link - = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do + = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do = _("Pipeline") %li.js-builds-tab-link = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do @@ -43,12 +43,36 @@ = render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage - if @pipeline.failed_builds.present? - #js-tab-failures.build-failures.tab-pane - - @pipeline.failed_builds.each_with_index do |build, index| - .build-state - %span.ci-status-icon-failed= custom_icon('icon_status_failed') - %span.stage - = build.stage.titleize - %span.build-name - = link_to build.name, pipeline_job_url(pipeline, build) - %pre.build-log= build_summary(build, skip: index >= 10) + #js-tab-failures.build-failures.tab-pane.build-page + %table.table.responsive-table.ci-table.responsive-table-sm-rounded + %thead + %th.table-th-transparent + %th.table-th-transparent= _("Name") + %th.table-th-transparent= _("Stage") + %th.table-th-transparent= _("Failure") + + %tbody + - @pipeline.failed_builds.each_with_index do |build, index| + - job = build.present(current_user: current_user) + %tr.build-state.responsive-table-border-start + %td.responsive-table-cell.ci-status-icon-failed{ data: { column: "Status"} } + .d-none.d-md-block.build-icon + = custom_icon("icon_status_#{build.status}") + .d-md-none.build-badge + = render "ci/status/badge", link: false, status: job.detailed_status(current_user) + %td.responsive-table-cell.build-name{ data: { column: _("Name")} } + = link_to build.name, pipeline_job_url(pipeline, build) + %td.responsive-table-cell.build-stage{ data: { column: _("Stage")} } + = build.stage.titleize + %td.responsive-table-cell.build-failure{ data: { column: _("Failure")} } + = build.present.callout_failure_message + %td.responsive-table-cell.build-actions + = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do + = icon('repeat') + %tr.build-trace-row.responsive-table-border-end + %td + %td.responsive-table-cell.build-trace-container{ colspan: 4 } + %pre.build-trace.build-trace-rounded + %code.bash.js-build-output + = build_summary(build) + diff --git a/changelogs/unreleased/44267-improve-failed-jobs-tab.yml b/changelogs/unreleased/44267-improve-failed-jobs-tab.yml new file mode 100644 index 00000000000..9743704e23d --- /dev/null +++ b/changelogs/unreleased/44267-improve-failed-jobs-tab.yml @@ -0,0 +1,5 @@ +--- +title: Improve Failed Jobs tab in the Pipeline detail page +merge_request: +author: +type: changed diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 35776a5f23b..ecc7cf84138 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -344,6 +344,16 @@ describe 'Pipeline', :js do it 'shows build failure logs' do expect(page).to have_content('4 examples, 1 failure') end + + it 'shows the failure reason' do + expect(page).to have_content('There is an unknown failure, please try again') + end + + it 'shows retry button for failed build' do + page.within(find('.build-failures', match: :first)) do + expect(page).to have_link('Retry') + end + end end context 'when missing build logs' do -- cgit v1.2.1 From 5370c442dfc0f1009b557cea9ce4dafbfe821569 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Thu, 7 Jun 2018 18:09:14 +0000 Subject: Resolve "Automatically provide a Deploy Token to projects when Auto DevOps is enabled" --- app/models/project_auto_devops.rb | 21 +++++ ...ide-deploy-token-when-autodevops-is-enabled.yml | 5 ++ doc/topics/autodevops/index.md | 10 +++ spec/factories/project_auto_devops.rb | 4 + spec/models/project_auto_devops_spec.rb | 93 ++++++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index ed6c1eddbc1..c6c990dfa00 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -6,6 +6,8 @@ class ProjectAutoDevops < ActiveRecord::Base validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true } + after_save :create_gitlab_deploy_token, if: :needs_to_create_deploy_token? + def instance_domain Gitlab::CurrentSettings.auto_devops_domain end @@ -22,4 +24,23 @@ class ProjectAutoDevops < ActiveRecord::Base end end end + + private + + def create_gitlab_deploy_token + project.deploy_tokens.create!( + name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME, + read_registry: true + ) + end + + def needs_to_create_deploy_token? + auto_devops_enabled? && + !project.public? && + !project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present? + end + + def auto_devops_enabled? + Gitlab::CurrentSettings.auto_devops_enabled? || enabled? + end end diff --git a/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml b/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml new file mode 100644 index 00000000000..6974be07716 --- /dev/null +++ b/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml @@ -0,0 +1,5 @@ +--- +title: Automatize Deploy Token creation for Auto Devops +merge_request: 19507 +author: +type: added diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 4bcc7fc6512..e2edee42717 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -426,6 +426,15 @@ no longer be valid as soon as the deployment job finishes. This means that Kubernetes can run the application, but in case it should be restarted or executed somewhere else, it cannot be accessed again. +> [Introduced][ce-19507] in GitLab 11.0. + +For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token) +will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token +can be used for permanent access to the registry. + +Note: **Note** +When the GitLab Deploy Token has been manually revoked, it won't be automatically created. + ### Auto Monitoring NOTE: **Note:** @@ -809,3 +818,4 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/ [Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml [GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md [ee]: https://about.gitlab.com/products/ +[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507 diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 5ce1988c76f..0e8b507f9ce 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -3,5 +3,9 @@ FactoryBot.define do project enabled true domain "example.com" + + trait :disabled do + enabled false + end end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7545c0797e9..4778bf4052b 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -71,4 +71,97 @@ describe ProjectAutoDevops do { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end end + + describe '#set_gitlab_deploy_token' do + let(:auto_devops) { build(:project_auto_devops, project: project) } + + context 'when the project is public' do + let(:project) { create(:project, :repository, :public) } + + it 'should not create a gitlab deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project is internal' do + let(:project) { create(:project, :repository, :internal) } + + it 'should create a gitlab deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when the project is private' do + let(:project) { create(:project, :repository, :private) } + + it 'should create a gitlab deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is enabled at project level' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should create a deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is enabled at instancel level' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) } + + it 'should create a deploy token' do + allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true) + + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is disabled' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project already has an active gitlab-deploy-token' do + let(:project) { create(:project, :repository, :internal) } + let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project already has a revoked gitlab-deploy-token' do + let(:project) { create(:project, :repository, :internal) } + let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + end end -- cgit v1.2.1 From afe5d7d56ee771dac6e4a97d23e69d678c03da2d Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Mon, 28 May 2018 15:43:46 -0300 Subject: Apply notification settings level of groups to all child objects --- app/models/group.rb | 20 +++ app/models/notification_recipient.rb | 29 ++++- changelogs/unreleased/issue_44230.yml | 5 + doc/workflow/notifications.md | 9 +- spec/models/group_spec.rb | 24 ++++ spec/models/notification_recipient_spec.rb | 44 +++++++ spec/services/notification_service_spec.rb | 188 ++++++++++++++++++++++++----- 7 files changed, 286 insertions(+), 33 deletions(-) create mode 100644 changelogs/unreleased/issue_44230.yml diff --git a/app/models/group.rb b/app/models/group.rb index 8fb77a7869d..1bd718d0a92 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,6 +11,7 @@ class Group < Namespace include GroupDescendant include TokenAuthenticatable include WithUploads + include Gitlab::Utils::StrongMemoize has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent alias_method :members, :group_members @@ -26,7 +27,11 @@ class Group < Namespace has_many :milestones has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :shared_projects, through: :project_group_links, source: :project + + # Overridden on another method + # Left here just to be dependent: :destroy has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent + has_many :labels, class_name: 'GroupLabel' has_many :variables, class_name: 'Ci::GroupVariable' has_many :custom_attributes, class_name: 'GroupCustomAttribute' @@ -88,6 +93,15 @@ class Group < Namespace end end + # Overrides notification_settings has_many association + # This allows to apply notification settings from parent groups + # to child groups and projects. + def notification_settings + source_type = self.class.base_class.name + + NotificationSetting.where(source_type: source_type, source_id: self_and_ancestors_ids) + end + def to_reference(_from = nil, full: nil) "#{self.class.reference_prefix}#{full_path}" end @@ -220,6 +234,12 @@ class Group < Namespace members_with_parents.pluck(:user_id) end + def self_and_ancestors_ids + strong_memoize(:self_and_ancestors_ids) do + self_and_ancestors.pluck(:id) + end + end + def members_with_parents # Avoids an unnecessary SELECT when the group has no parents source_ids = diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index 2c3580bbdc6..ee497579e60 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -1,4 +1,6 @@ class NotificationRecipient + include Gitlab::Utils::StrongMemoize + attr_reader :user, :type, :reason def initialize(user, type, **opts) unless NotificationSetting.levels.key?(type) || type == :subscription @@ -142,10 +144,33 @@ class NotificationRecipient return project_setting unless project_setting.nil? || project_setting.global? - group_setting = @group && user.notification_settings_for(@group) + group_setting = closest_non_global_group_notification_settting - return group_setting unless group_setting.nil? || group_setting.global? + return group_setting unless group_setting.nil? user.global_notification_setting end + + # Returns the notificaton_setting of the lowest group in hierarchy with non global level + def closest_non_global_group_notification_settting + return unless @group + return if indexed_group_notification_settings.empty? + + notification_setting = nil + + @group.self_and_ancestors_ids.each do |id| + notification_setting = indexed_group_notification_settings[id] + break if notification_setting + end + + notification_setting + end + + def indexed_group_notification_settings + strong_memoize(:indexed_group_notification_settings) do + @group.notification_settings.where(user_id: user.id) + .where.not(level: NotificationSetting.levels[:global]) + .index_by(&:source_id) + end + end end diff --git a/changelogs/unreleased/issue_44230.yml b/changelogs/unreleased/issue_44230.yml new file mode 100644 index 00000000000..2c6dba6c0fb --- /dev/null +++ b/changelogs/unreleased/issue_44230.yml @@ -0,0 +1,5 @@ +--- +title: Apply notification settings level of groups to all child objects +merge_request: +author: +type: changed diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md index fc592b99860..edb0c6bdc30 100644 --- a/doc/workflow/notifications.md +++ b/doc/workflow/notifications.md @@ -34,9 +34,14 @@ anything that is set at Global Settings. ![notification settings](img/notification_group_settings.png) -Group Settings are taking precedence over Global Settings but are on a level below Project Settings. +Group Settings are taking precedence over Global Settings but are on a level below Project or Subgroup Settings: + +``` +Group < Subgroup < Project +``` + This means that you can set a different level of notifications per group while still being able -to have a finer level setting per project. +to have a finer level setting per project or subgroup. Organization like this is suitable for users that belong to different groups but don't have the same need for being notified for every group they are member of. These settings can be configured on group page under the name of the group. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown. diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index f83b52e8975..ff35be4a8e3 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -67,6 +67,30 @@ describe Group do end end + describe '#notification_settings', :nested_groups do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:sub_group) { create(:group, parent_id: group.id) } + + before do + group.add_developer(user) + sub_group.add_developer(user) + end + + it 'also gets notification settings from parent groups' do + expect(sub_group.notification_settings.size).to eq(2) + expect(sub_group.notification_settings).to include(group.notification_settings.first) + end + + context 'when sub group is deleted' do + it 'does not delete parent notification settings' do + expect do + sub_group.destroy + end.to change { NotificationSetting.count }.by(-1) + end + end + end + describe '#visibility_level_allowed_by_parent' do let(:parent) { create(:group, :internal) } let(:sub_group) { build(:group, parent_id: parent.id) } diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb index eda0e1da835..13fe47799ed 100644 --- a/spec/models/notification_recipient_spec.rb +++ b/spec/models/notification_recipient_spec.rb @@ -13,4 +13,48 @@ describe NotificationRecipient do expect(recipient.has_access?).to be_falsy end + + context '#notification_setting' do + context 'for child groups', :nested_groups do + let!(:moved_group) { create(:group) } + let(:group) { create(:group) } + let(:sub_group_1) { create(:group, parent: group) } + let(:sub_group_2) { create(:group, parent: sub_group_1) } + let(:project) { create(:project, namespace: moved_group) } + + before do + sub_group_2.add_owner(user) + moved_group.add_owner(user) + Groups::TransferService.new(moved_group, user).execute(sub_group_2) + + moved_group.reload + end + + context 'when notification setting is global' do + before do + user.notification_settings_for(group).global! + user.notification_settings_for(sub_group_1).mention! + user.notification_settings_for(sub_group_2).global! + user.notification_settings_for(moved_group).global! + end + + it 'considers notification setting from the first parent without global setting' do + expect(subject.notification_setting.source).to eq(sub_group_1) + end + end + + context 'when notification setting is not global' do + before do + user.notification_settings_for(group).global! + user.notification_settings_for(sub_group_1).mention! + user.notification_settings_for(sub_group_2).watch! + user.notification_settings_for(moved_group).disabled! + end + + it 'considers notification setting from lowest group member in hierarchy' do + expect(subject.notification_setting.source).to eq(moved_group) + end + end + end + end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 831678b949d..0eadc83bfe3 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -59,6 +59,20 @@ describe NotificationService, :mailer do should_email(participant) end + + context 'for subgroups', :nested_groups do + before do + build_group(project) + end + + it 'emails the participant' do + create(:note_on_issue, noteable: issuable, project_id: project.id, note: 'anything', author: @pg_participant) + + notification_trigger + + should_email_nested_group_user(@pg_participant) + end + end end shared_examples 'participating by assignee notification' do @@ -239,34 +253,56 @@ describe NotificationService, :mailer do end describe 'new note on issue in project that belongs to a group' do - let(:group) { create(:group) } - before do note.project.namespace_id = group.id - note.project.group.add_user(@u_watcher, GroupMember::MASTER) - note.project.group.add_user(@u_custom_global, GroupMember::MASTER) + group.add_user(@u_watcher, GroupMember::MASTER) + group.add_user(@u_custom_global, GroupMember::MASTER) note.project.save @u_watcher.notification_settings_for(note.project).participating! - @u_watcher.notification_settings_for(note.project.group).global! + @u_watcher.notification_settings_for(group).global! update_custom_notification(:new_note, @u_custom_global) reset_delivered_emails! end - it do - notification.new_note(note) + shared_examples 'new note notifications' do + it do + notification.new_note(note) + + should_email(note.noteable.author) + should_email(note.noteable.assignees.first) + should_email(@u_mentioned) + should_email(@u_custom_global) + should_not_email(@u_guest_custom) + should_not_email(@u_guest_watcher) + should_not_email(@u_watcher) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + should_not_email(@u_lazy_participant) + end + end - should_email(note.noteable.author) - should_email(note.noteable.assignees.first) - should_email(@u_mentioned) - should_email(@u_custom_global) - should_not_email(@u_guest_custom) - should_not_email(@u_guest_watcher) - should_not_email(@u_watcher) - should_not_email(note.author) - should_not_email(@u_participating) - should_not_email(@u_disabled) - should_not_email(@u_lazy_participant) + let(:group) { create(:group) } + + it_behaves_like 'new note notifications' + + context 'which is a subgroup', :nested_groups do + let!(:parent) { create(:group) } + let!(:group) { create(:group, parent: parent) } + + it_behaves_like 'new note notifications' + + it 'overrides child objects with global level' do + user = create(:user) + parent.add_developer(user) + user.notification_settings_for(parent).watch! + reset_delivered_emails! + + notification.new_note(note) + + should_email(user) + end end end end @@ -301,6 +337,31 @@ describe NotificationService, :mailer do should_email(member) should_email(admin) end + + context 'on project that belongs to subgroup', :nested_groups do + let(:group_reporter) { create(:user) } + let(:group_guest) { create(:user) } + let(:parent_group) { create(:group) } + let(:child_group) { create(:group, parent: parent_group) } + let(:project) { create(:project, namespace: child_group) } + + context 'when user is group guest member' do + before do + parent_group.add_reporter(group_reporter) + parent_group.add_guest(group_guest) + group_guest.notification_settings_for(parent_group).watch! + group_reporter.notification_settings_for(parent_group).watch! + reset_delivered_emails! + end + + it 'does not email guest user' do + notification.new_note(note) + + should_email(group_reporter) + should_not_email(group_guest) + end + end + end end context 'issue note mention' do @@ -311,6 +372,7 @@ describe NotificationService, :mailer do before do build_team(note.project) + build_group(note.project) note.project.add_master(note.author) add_users_with_subscription(note.project, issue) reset_delivered_emails! @@ -336,10 +398,20 @@ describe NotificationService, :mailer do should_email(@u_guest_watcher) should_email(note.noteable.author) should_email(note.noteable.assignees.first) - should_not_email(note.author) + should_email_nested_group_user(@pg_watcher) should_email(@u_mentioned) - should_not_email(@u_disabled) should_email(@u_not_mentioned) + should_not_email(note.author) + should_not_email(@u_disabled) + should_not_email_nested_group_user(@pg_disabled) + end + + it 'notifies parent group members with mention level', :nested_groups do + note = create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: "@#{@pg_mention.username}") + + notification.new_note(note) + + should_email_nested_group_user(@pg_mention) end it 'filters out "mentioned in" notes' do @@ -352,17 +424,18 @@ describe NotificationService, :mailer do end context 'project snippet note' do - let(:project) { create(:project, :public) } + let!(:project) { create(:project, :public) } let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } - let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') } + let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: project.id, note: '@all mentioned') } before do - build_team(note.project) + build_team(project) + build_group(project) # make sure these users can read the project snippet! project.add_guest(@u_guest_watcher) project.add_guest(@u_guest_custom) - + add_member_for_parent_group(@pg_watcher, project) note.project.add_master(note.author) reset_delivered_emails! end @@ -370,7 +443,6 @@ describe NotificationService, :mailer do describe '#new_note' do it 'notifies the team members' do notification.new_note(note) - # Notify all team members note.project.team.members.each do |member| # User with disabled notification should not be notified @@ -449,6 +521,7 @@ describe NotificationService, :mailer do before do build_team(note.project) + build_group(project) reset_delivered_emails! allow(note.noteable).to receive(:author).and_return(@u_committer) update_custom_notification(:new_note, @u_guest_custom, resource: project) @@ -463,11 +536,13 @@ describe NotificationService, :mailer do should_email(@u_guest_custom) should_email(@u_committer) should_email(@u_watcher) + should_email_nested_group_user(@pg_watcher) should_not_email(@u_mentioned) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) end it do @@ -478,10 +553,12 @@ describe NotificationService, :mailer do should_email(@u_committer) should_email(@u_watcher) should_email(@u_mentioned) + should_email_nested_group_user(@pg_watcher) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) end it do @@ -548,10 +625,13 @@ describe NotificationService, :mailer do should_email(@g_global_watcher) should_email(@g_watcher) should_email(@unsubscribed_mentioned) + should_email_nested_group_user(@pg_watcher) should_not_email(@u_mentioned) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) + should_not_email_nested_group_user(@pg_mention) end it do @@ -1922,19 +2002,69 @@ describe NotificationService, :mailer do # Users in the project's group but not part of project's team # with different notification settings def build_group(project) - group = create(:group, :public) - project.group = group + group = create_nested_group + project.update(namespace_id: group.id) # Group member: global=disabled, group=watch - @g_watcher = create_user_with_notification(:watch, 'group_watcher', project.group) + @g_watcher ||= create_user_with_notification(:watch, 'group_watcher', project.group) @g_watcher.notification_settings_for(nil).disabled! # Group member: global=watch, group=global - @g_global_watcher = create_global_setting_for(create(:user), :watch) + @g_global_watcher ||= create_global_setting_for(create(:user), :watch) group.add_users([@g_watcher, @g_global_watcher], :master) + group end + # Creates a nested group only if supported + # to avoid errors on MySQL + def create_nested_group + if Group.supports_nested_groups? + parent_group = create(:group, :public) + child_group = create(:group, :public, parent: parent_group) + + # Parent group member: global=disabled, parent_group=watch, child_group=global + @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group) + @pg_watcher.notification_settings_for(nil).disabled! + + # Parent group member: global=global, parent_group=disabled, child_group=global + @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group) + @pg_disabled.notification_settings_for(nil).global! + + # Parent group member: global=global, parent_group=mention, child_group=global + @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group) + @pg_mention.notification_settings_for(nil).global! + + # Parent group member: global=global, parent_group=participating, child_group=global + @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group) + @pg_mention.notification_settings_for(nil).global! + + child_group + else + create(:group, :public) + end + end + + def add_member_for_parent_group(user, project) + return unless Group.supports_nested_groups? + + project.reload + + project.group.parent.add_master(user) + end + + def should_email_nested_group_user(user, times: 1, recipients: email_recipients) + return unless Group.supports_nested_groups? + + should_email(user, times: 1, recipients: email_recipients) + end + + def should_not_email_nested_group_user(user, recipients: email_recipients) + return unless Group.supports_nested_groups? + + should_not_email(user, recipients: email_recipients) + end + def add_users_with_subscription(project, issuable) @subscriber = create :user @unsubscriber = create :user -- cgit v1.2.1 From 91254c11b102ceeb79e787a693616ecf8ecb6ba9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 19:49:47 +0100 Subject: Allow IDE pipeline panel to be resizable Closes #47538 --- app/assets/javascripts/ide/components/panes/right.vue | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue index aafd6a15a78..dd7fc8f1e01 100644 --- a/app/assets/javascripts/ide/components/panes/right.vue +++ b/app/assets/javascripts/ide/components/panes/right.vue @@ -5,6 +5,7 @@ import Icon from '../../../vue_shared/components/icon.vue'; import { rightSidebarViews } from '../../constants'; import PipelinesList from '../pipelines/list.vue'; import JobsDetail from '../jobs/detail.vue'; +import ResizablePanel from '../resizable_panel.vue'; export default { directives: { @@ -14,6 +15,7 @@ export default { Icon, PipelinesList, JobsDetail, + ResizablePanel, }, computed: { ...mapState(['rightPane']), @@ -40,12 +42,16 @@ export default {
-
-
+
- diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js index 71f49319c36..6dcd4aaec43 100644 --- a/app/assets/javascripts/boards/components/new_list_dropdown.js +++ b/app/assets/javascripts/boards/components/new_list_dropdown.js @@ -56,6 +56,7 @@ gl.issueBoards.newListDropdownInit = () => { filterable: true, selectable: true, multiSelect: true, + containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content', clicked (options) { const { e } = options; const label = options.selectedObj; diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 29ab13b8e0b..cdad8d238e3 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -7,6 +7,7 @@ import Vue from 'vue'; import Flash from '~/flash'; import { __ } from '~/locale'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import FilteredSearchBoards from './filtered_search_boards'; import eventHub from './eventhub'; @@ -15,7 +16,6 @@ import './models/issue'; import './models/list'; import './models/milestone'; import './models/project'; -import './models/assignee'; import './stores/boards_store'; import ModalStore from './stores/modal_store'; import BoardService from './services/board_service'; diff --git a/app/assets/javascripts/boards/models/assignee.js b/app/assets/javascripts/boards/models/assignee.js deleted file mode 100644 index 05dd449e4fd..00000000000 --- a/app/assets/javascripts/boards/models/assignee.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable no-unused-vars */ - -class ListAssignee { - constructor(user, defaultAvatar) { - this.id = user.id; - this.name = user.name; - this.username = user.username; - this.avatar = user.avatar_url || defaultAvatar; - } -} - -window.ListAssignee = ListAssignee; diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index 7144f4190e7..a79dd62e2e4 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -1,12 +1,14 @@ /* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len, no-unused-vars */ /* global ListIssue */ -/* global ListLabel */ + +import ListLabel from '~/vue_shared/models/label'; +import ListAssignee from '~/vue_shared/models/assignee'; import queryData from '../utils/query_data'; const PER_PAGE = 20; class List { - constructor (obj, defaultAvatar) { + constructor(obj, defaultAvatar) { this.id = obj.id; this._uid = this.guid(); this.position = obj.position; @@ -24,6 +26,9 @@ class List { if (obj.label) { this.label = new ListLabel(obj.label); + } else if (obj.user) { + this.assignee = new ListAssignee(obj.user); + this.title = this.assignee.name; } if (this.type !== 'blank' && this.id) { @@ -34,14 +39,25 @@ class List { } guid() { - const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); + const s4 = () => + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; } - save () { + save() { + const entity = this.label || this.assignee; + let entityType = ''; + if (this.label) { + entityType = 'label_id'; + } else { + entityType = 'assignee_id'; + } + return gl.boardService.createList(this.label.id) .then(res => res.data) - .then((data) => { + .then(data => { this.id = data.id; this.type = data.list_type; this.position = data.position; @@ -50,25 +66,23 @@ class List { }); } - destroy () { + destroy() { const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this); gl.issueBoards.BoardsStore.state.lists.splice(index, 1); gl.issueBoards.BoardsStore.updateNewListDropdown(this.id); - gl.boardService.destroyList(this.id) - .catch(() => { - // TODO: handle request error - }); + gl.boardService.destroyList(this.id).catch(() => { + // TODO: handle request error + }); } - update () { - gl.boardService.updateList(this.id, this.position) - .catch(() => { - // TODO: handle request error - }); + update() { + gl.boardService.updateList(this.id, this.position).catch(() => { + // TODO: handle request error + }); } - nextPage () { + nextPage() { if (this.issuesSize > this.issues.length) { if (this.issues.length / PER_PAGE >= 1) { this.page += 1; @@ -78,7 +92,7 @@ class List { } } - getIssues (emptyIssues = true) { + getIssues(emptyIssues = true) { const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page }); if (this.label && data.label_name) { @@ -89,7 +103,8 @@ class List { this.loading = true; } - return gl.boardService.getIssuesForList(this.id, data) + return gl.boardService + .getIssuesForList(this.id, data) .then(res => res.data) .then((data) => { this.loading = false; @@ -103,11 +118,12 @@ class List { }); } - newIssue (issue) { + newIssue(issue) { this.addIssue(issue, null, 0); this.issuesSize += 1; - return gl.boardService.newIssue(this.id, issue) + return gl.boardService + .newIssue(this.id, issue) .then(res => res.data) .then((data) => { issue.id = data.id; @@ -123,13 +139,13 @@ class List { }); } - createIssues (data) { - data.forEach((issueObj) => { + createIssues(data) { + data.forEach(issueObj => { this.addIssue(new ListIssue(issueObj, this.defaultAvatar)); }); } - addIssue (issue, listFrom, newIndex) { + addIssue(issue, listFrom, newIndex) { let moveBeforeId = null; let moveAfterId = null; @@ -152,6 +168,13 @@ class List { issue.addLabel(this.label); } + if (this.assignee) { + if (listFrom && listFrom.type === 'assignee') { + issue.removeAssignee(listFrom.assignee); + } + issue.addAssignee(this.assignee); + } + if (listFrom) { this.issuesSize += 1; @@ -160,29 +183,29 @@ class List { } } - moveIssue (issue, oldIndex, newIndex, moveBeforeId, moveAfterId) { + moveIssue(issue, oldIndex, newIndex, moveBeforeId, moveAfterId) { this.issues.splice(oldIndex, 1); this.issues.splice(newIndex, 0, issue); - gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId) - .catch(() => { - // TODO: handle request error - }); + gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => { + // TODO: handle request error + }); } updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) { - gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId) + gl.boardService + .moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId) .catch(() => { // TODO: handle request error }); } - findIssue (id) { + findIssue(id) { return this.issues.find(issue => issue.id === id); } - removeIssue (removeIssue) { - this.issues = this.issues.filter((issue) => { + removeIssue(removeIssue) { + this.issues = this.issues.filter(issue => { const matchesRemove = removeIssue.id === issue.id; if (matchesRemove) { diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index 7c90597f77c..029b0971f2c 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -30,11 +30,13 @@ export default class BoardService { return axios.post(this.listsEndpointGenerate, {}); } - createList(labelId) { + createList(entityId, entityType) { + const list = { + [entityType]: entityId, + }; + return axios.post(this.listsEndpoint, { - list: { - label_id: labelId, - }, + list, }); } diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 20e78edf2a2..7dc83843e9b 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -103,8 +103,15 @@ gl.issueBoards.BoardsStore = { const listLabels = issueLists.map(listIssue => listIssue.label); if (!issueTo) { - // Add to new lists issues if it doesn't already exist - listTo.addIssue(issue, listFrom, newIndex); + // Check if target list assignee is already present in this issue + if ((listTo.type === 'assignee' && listFrom.type === 'assignee') && + issue.findAssignee(listTo.assignee)) { + const targetIssue = listTo.findIssue(issue.id); + targetIssue.removeAssignee(listFrom.assignee); + } else { + // Add to new lists issues if it doesn't already exist + listTo.addIssue(issue, listFrom, newIndex); + } } else { listTo.updateIssueLabel(issue, listFrom); issueTo.removeLabel(listFrom.label); @@ -115,7 +122,11 @@ gl.issueBoards.BoardsStore = { list.removeIssue(issue); }); issue.removeLabels(listLabels); - } else { + } else if (listTo.type === 'backlog' && listFrom.type === 'assignee') { + issue.removeAssignee(listFrom.assignee); + listFrom.removeIssue(issue); + } else if ((listTo.type !== 'label' && listFrom.type === 'assignee') || + (listTo.type !== 'assignee' && listFrom.type === 'label')) { listFrom.removeIssue(issue); } }, @@ -126,11 +137,12 @@ gl.issueBoards.BoardsStore = { list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId); }, findList (key, val, type = 'label') { - return this.state.lists.filter((list) => { - const byType = type ? list['type'] === type : true; + const filteredList = this.state.lists.filter((list) => { + const byType = type ? (list.type === type) || (list.type === 'assignee') : true; return list[key] === val && byType; - })[0]; + }); + return filteredList[0]; }, updateFiltersUrl () { history.pushState(null, null, `?${this.filter.path}`); diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 746a06b7c4f..7fbba7e27cb 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -602,7 +602,11 @@ GitLabDropdown = (function() { var selector; selector = '.dropdown-content'; if (this.dropdown.find(".dropdown-toggle-page").length) { - selector = ".dropdown-page-one .dropdown-content"; + if (this.options.containerSelector) { + selector = this.options.containerSelector; + } else { + selector = '.dropdown-page-one .dropdown-content'; + } } return $(selector, this.dropdown).empty(); diff --git a/app/assets/javascripts/vue_shared/models/assignee.js b/app/assets/javascripts/vue_shared/models/assignee.js new file mode 100644 index 00000000000..4a29b0d0581 --- /dev/null +++ b/app/assets/javascripts/vue_shared/models/assignee.js @@ -0,0 +1,13 @@ +export default class ListAssignee { + constructor(obj, defaultAvatar) { + this.id = obj.id; + this.name = obj.name; + this.username = obj.username; + this.avatar = obj.avatar_url || obj.avatar || defaultAvatar; + this.path = obj.path; + this.state = obj.state; + this.webUrl = obj.web_url || obj.webUrl; + } +} + +window.ListAssignee = ListAssignee; diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb index 381fd4d7508..e8b5934f2a9 100644 --- a/app/controllers/boards/lists_controller.rb +++ b/app/controllers/boards/lists_controller.rb @@ -56,8 +56,12 @@ module Boards private + def list_creation_attrs + %i[label_id] + end + def list_params - params.require(:list).permit(:label_id) + params.require(:list).permit(list_creation_attrs) end def move_params @@ -65,11 +69,15 @@ module Boards end def serialize_as_json(resource) - resource.as_json( + resource.as_json(serialization_attrs) + end + + def serialization_attrs + { only: [:id, :list_type, :position], methods: [:title], label: true - ) + } end end end diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index 067aff408df..2a656c0d31c 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -3,17 +3,29 @@ class GroupMembersFinder @group = group end - def execute + def execute(include_descendants: false) group_members = @group.members + wheres = [] - return group_members unless @group.parent + return group_members unless @group.parent || include_descendants - parents_members = GroupMember.non_request - .where(source_id: @group.ancestors.select(:id)) - .where.not(user_id: @group.users.select(:id)) + wheres << "members.id IN (#{group_members.select(:id).to_sql})" - wheres = ["members.id IN (#{group_members.select(:id).to_sql})"] - wheres << "members.id IN (#{parents_members.select(:id).to_sql})" + if @group.parent + parents_members = GroupMember.non_request + .where(source_id: @group.ancestors.select(:id)) + .where.not(user_id: @group.users.select(:id)) + + wheres << "members.id IN (#{parents_members.select(:id).to_sql})" + end + + if include_descendants + descendant_members = GroupMember.non_request + .where(source_id: @group.descendants.select(:id)) + .where.not(user_id: @group.users.select(:id)) + + wheres << "members.id IN (#{descendant_members.select(:id).to_sql})" + end GroupMember.where(wheres.join(' OR ')) end diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 4734d97b8c7..4c893ae2de6 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -7,12 +7,12 @@ class MembersFinder @group = project.group end - def execute + def execute(include_descendants: false) project_members = project.project_members project_members = project_members.non_invite unless can?(current_user, :admin_project, project) if group - group_members = GroupMembersFinder.new(group).execute + group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants) group_members = group_members.non_invite union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) diff --git a/app/models/list.rb b/app/models/list.rb index 5daf35ef845..4edcfa78835 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -2,17 +2,27 @@ class List < ActiveRecord::Base belongs_to :board belongs_to :label - enum list_type: { backlog: 0, label: 1, closed: 2 } + enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3 } validates :board, :list_type, presence: true validates :label, :position, presence: true, if: :label? validates :label_id, uniqueness: { scope: :board_id }, if: :label? - validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :label? + validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :movable? before_destroy :can_be_destroyed - scope :destroyable, -> { where(list_type: list_types[:label]) } - scope :movable, -> { where(list_type: list_types[:label]) } + scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) } + scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) } + + class << self + def destroyable_types + [:label] + end + + def movable_types + [:label] + end + end def destroyable? label? diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb index 5a961ac89e4..b1dbe73cdf7 100644 --- a/app/services/boards/issues/list_service.rb +++ b/app/services/boards/issues/list_service.rb @@ -3,13 +3,18 @@ module Boards class ListService < Boards::BaseService def execute issues = IssuesFinder.new(current_user, filter_params).execute - issues = without_board_labels(issues) unless movable_list? || closed_list? - issues = with_list_label(issues) if movable_list? + issues = filter(issues) issues.order_by_position_and_priority end private + def filter(issues) + issues = without_board_labels(issues) unless list&.movable? || list&.closed? + issues = with_list_label(issues) if list&.label? + issues + end + def board @board ||= parent.boards.find(params[:board_id]) end @@ -20,18 +25,6 @@ module Boards @list = board.lists.find(params[:id]) if params.key?(:id) end - def movable_list? - return @movable_list if defined?(@movable_list) - - @movable_list = list.present? && list.movable? - end - - def closed_list? - return @closed_list if defined?(@closed_list) - - @closed_list = list.present? && list.closed? - end - def filter_params set_parent set_state diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb index 3ceab209f3f..ee3112c7571 100644 --- a/app/services/boards/issues/move_service.rb +++ b/app/services/boards/issues/move_service.rb @@ -3,7 +3,7 @@ module Boards class MoveService < Boards::BaseService def execute(issue) return false unless can?(current_user, :update_issue, issue) - return false if issue_params.empty? + return false if issue_params(issue).empty? update(issue) end @@ -28,10 +28,10 @@ module Boards end def update(issue) - ::Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue) + ::Issues::UpdateService.new(issue.project, current_user, issue_params(issue)).execute(issue) end - def issue_params + def issue_params(issue) attrs = {} if move_between_lists? diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb index 02f1c709374..6fd9885d4f3 100644 --- a/app/services/boards/lists/create_service.rb +++ b/app/services/boards/lists/create_service.rb @@ -1,16 +1,28 @@ module Boards module Lists class CreateService < Boards::BaseService + include Gitlab::Utils::StrongMemoize + def execute(board) List.transaction do - label = available_labels_for(board).find(params[:label_id]) + target = target(board) position = next_position(board) - create_list(board, label, position) + create_list(board, type, target, position) end end private + def type + :label + end + + def target(board) + strong_memoize(:target) do + available_labels_for(board).find(params[:label_id]) + end + end + def available_labels_for(board) options = { include_ancestor_groups: true } @@ -28,8 +40,8 @@ module Boards max_position.nil? ? 0 : max_position.succ end - def create_list(board, label, position) - board.lists.create(label: label, list_type: :label, position: position) + def create_list(board, type, target, position) + board.lists.create(type => target, list_type: type, position: position) end end end diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml index 67476a3f573..76843ce7cc0 100644 --- a/app/views/shared/boards/components/_board.html.haml +++ b/app/views/shared/boards/components/_board.html.haml @@ -1,4 +1,4 @@ -.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded }', +.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded, "board-type-assignee": list.type === "assignee" }', ":data-id" => "list.id" } .board-inner %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" } @@ -7,10 +7,18 @@ ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }", "aria-hidden": "true" } + %a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" } + -# haml-lint:disable AltText + %img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" } + %span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"", - ":title" => '(list.label ? list.label.description : "")', data: { container: "body" } } + ":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } } {{ list.title }} + %span.board-title-sub-text.prepend-left-5.has-tooltip{ "v-if": "list.type === \"assignee\"", + ":title" => '(list.assignee && list.assignee.username || "")' } + @{{ list.assignee.username }} + %span.has-tooltip{ "v-if": "list.type === \"label\"", ":title" => '(list.label ? list.label.description : "")', data: { container: "body", placement: "bottom" }, diff --git a/app/views/shared/issuable/_board_create_list_dropdown.html.haml b/app/views/shared/issuable/_board_create_list_dropdown.html.haml new file mode 100644 index 00000000000..23b2e1b91e5 --- /dev/null +++ b/app/views/shared/issuable/_board_create_list_dropdown.html.haml @@ -0,0 +1,8 @@ +.dropdown.prepend-left-10#js-add-list + %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data } + Add list + .dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable.js-tab-container-labels + = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" } + - if can?(current_user, :admin_label, board.parent) + = render partial: "shared/issuable/label_page_create" + = dropdown_loading diff --git a/app/views/shared/issuable/_label_page_create.html.haml b/app/views/shared/issuable/_label_page_create.html.haml index 9f4021802df..55edaa7eda4 100644 --- a/app/views/shared/issuable/_label_page_create.html.haml +++ b/app/views/shared/issuable/_label_page_create.html.haml @@ -1,6 +1,7 @@ +- show_close = local_assigns.fetch(:show_close, true) - subject = @project || @group .dropdown-page-two.dropdown-new-label - = dropdown_title(create_label_title(subject), options: { back: true }) + = dropdown_title(create_label_title(subject), options: { back: true, close: show_close }) = dropdown_content do .dropdown-labels-error.js-label-error %input#new_label_name.default-dropdown-input{ type: "text", placeholder: _('Name new label') } diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml index 2bd922bca2b..aa4a5f0e0d3 100644 --- a/app/views/shared/issuable/_label_page_default.html.haml +++ b/app/views/shared/issuable/_label_page_default.html.haml @@ -1,15 +1,18 @@ - title = local_assigns.fetch(:title, _('Assign labels')) +- content_title = local_assigns.fetch(:content_title, _('Create lists from labels. Issues with that label appear in that list.')) +- show_title = local_assigns.fetch(:show_title, true) - show_create = local_assigns.fetch(:show_create, true) - show_footer = local_assigns.fetch(:show_footer, true) - filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search') - show_boards_content = local_assigns.fetch(:show_boards_content, false) - subject = @project || @group .dropdown-page-one - = dropdown_title(title) + - if show_title + = dropdown_title(title) - if show_boards_content .issue-board-dropdown-content %p - = _('Create lists from labels. Issues with that label appear in that list.') + = content_title = dropdown_filter(filter_placeholder) = dropdown_content - if current_board_parent && show_footer diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 644f7c4dd28..ef9ea2194ee 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -104,14 +104,7 @@ .filter-dropdown-container - if type == :boards - if can?(current_user, :admin_list, board.parent) - .dropdown.prepend-left-10#js-add-list - %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data } - Add list - .dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable - = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" } - - if can?(current_user, :admin_label, board.parent) - = render partial: "shared/issuable/label_page_create" - = dropdown_loading + = render_if_exists 'shared/issuable/board_create_list_dropdown', board: board - if @project #js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } } - elsif type != :boards_modal diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index e414345ac23..f6e0dee28c6 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -150,7 +150,7 @@ describe 'Issue Boards', :js do click_button 'Add list' wait_for_requests - find('.dropdown-menu-close').click + find('.js-new-board-list').click page.within(find('.board:nth-child(2)')) do accept_confirm { find('.board-delete').click } diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 4625a50b8d9..2cb3ae08b0e 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -143,6 +143,9 @@ describe 'New/edit issue', :js do click_link label.title click_link label2.title end + + find('.js-issuable-form-dropdown.js-label-select').click + page.within '.js-label-select' do expect(page).to have_content label.title end diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index 9f285e28535..63e15b365a4 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -29,4 +29,16 @@ describe GroupMembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member3, member4]) end + + it 'returns members for descendant groups if requested', :nested_groups do + member1 = group.add_master(user2) + member2 = group.add_master(user1) + nested_group.add_master(user2) + member3 = nested_group.add_master(user3) + member4 = nested_group.add_master(user4) + + result = described_class.new(group).execute(include_descendants: true) + + expect(result.to_a).to match_array([member1, member2, member3, member4]) + end end diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 7bb1f45322e..2fc5299b0f4 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -19,4 +19,16 @@ describe MembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member2, member3]) end + + it 'includes nested group members if asked', :nested_groups do + project = create(:project, namespace: group) + nested_group.request_access(user1) + member1 = group.add_master(user2) + member2 = nested_group.add_master(user3) + member3 = project.add_master(user4) + + result = described_class.new(project, user2).execute(include_descendants: true) + + expect(result.to_a).to match_array([member1, member2, member3]) + end end diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json index 05922df6b81..b76ec115293 100644 --- a/spec/fixtures/api/schemas/list.json +++ b/spec/fixtures/api/schemas/list.json @@ -37,5 +37,5 @@ "title": { "type": "string" }, "position": { "type": ["integer", "null"] } }, - "additionalProperties": false + "additionalProperties": true } diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js index 9b4db774b63..ad263791cd4 100644 --- a/spec/javascripts/boards/board_card_spec.js +++ b/spec/javascripts/boards/board_card_spec.js @@ -5,10 +5,10 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import '~/boards/models/assignee'; import eventHub from '~/boards/eventhub'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/list'; import '~/boards/stores/boards_store'; import boardCard from '~/boards/components/board_card.vue'; diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index 46fa10e1789..3f5ed4f3d07 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -7,9 +7,9 @@ import axios from '~/lib/utils/axios_utils'; import Cookies from 'js-cookie'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index abeef272c68..05acf903933 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -5,9 +5,9 @@ import Vue from 'vue'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/stores/boards_store'; import '~/boards/components/issue_card_inner'; import { listObj } from './mock_data'; diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js index d90f9a41231..db68096e3bd 100644 --- a/spec/javascripts/boards/issue_spec.js +++ b/spec/javascripts/boards/issue_spec.js @@ -3,9 +3,9 @@ import Vue from 'vue'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index d5d1139de15..ac8bbb8f2a8 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -6,9 +6,9 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import _ from 'underscore'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js index 797693a21aa..a234c81fadf 100644 --- a/spec/javascripts/boards/modal_store_spec.js +++ b/spec/javascripts/boards/modal_store_spec.js @@ -1,9 +1,9 @@ /* global ListIssue */ import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import Store from '~/boards/stores/modal_store'; describe('Modal store', () => { -- cgit v1.2.1 From 0e794561bd5d45e48581cc03102b85f4aa17a0ab Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 7 Jun 2018 16:08:48 -0500 Subject: Fix markdown code highlighting in mr discussion --- app/assets/stylesheets/bootstrap_migration.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index 79f580546c3..34042cf4256 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -107,7 +107,7 @@ code { background-color: $red-100; border-radius: 3px; - .code & { + .code > & { background-color: inherit; padding: unset; } -- cgit v1.2.1 From d52a64bb16215fb4b866691cc5a593a7667666aa Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 4 Jun 2018 16:10:13 -0500 Subject: update monaco-editor dependency --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index be3a6e4c9f6..c859203646f 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "jszip-utils": "^0.0.2", "katex": "^0.8.3", "marked": "^0.3.12", - "monaco-editor": "0.10.0", + "monaco-editor": "0.13.1", "mousetrap": "^1.4.6", "pikaday": "^1.6.1", "popper.js": "^1.14.3", diff --git a/yarn.lock b/yarn.lock index 418fa4a2216..e8170e244b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5647,9 +5647,9 @@ moment@2.x, moment@^2.18.1: version "2.19.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" -monaco-editor@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.10.0.tgz#6604932585fe9c1f993f000a503d0d20fbe5896a" +monaco-editor@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.13.1.tgz#6b9ce20e4d1c945042d256825eb133cb23315a52" mousetrap@^1.4.6: version "1.4.6" -- cgit v1.2.1 From 41aa6bd8776570b491747b95c146255c0b58a870 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 16:20:39 -0500 Subject: update webpack and webpack-cli to latest --- package.json | 4 +- yarn.lock | 1066 ++++++++++++---------------------------------------------- 2 files changed, 217 insertions(+), 853 deletions(-) diff --git a/package.json b/package.json index c859203646f..c2f0df0e6d4 100644 --- a/package.json +++ b/package.json @@ -94,9 +94,9 @@ "vue-template-compiler": "^2.5.16", "vue-virtual-scroll-list": "^1.2.5", "vuex": "^3.0.1", - "webpack": "^4.7.0", + "webpack": "^4.11.1", "webpack-bundle-analyzer": "^2.11.1", - "webpack-cli": "^2.1.2", + "webpack-cli": "^3.0.2", "webpack-stats-plugin": "^0.2.1", "worker-loader": "^2.0.0" }, diff --git a/yarn.lock b/yarn.lock index e8170e244b6..b10466bbf29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -82,13 +82,6 @@ version "1.23.0" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.23.0.tgz#42047aeedcc06bc12d417ed1efadad1749af9670" -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" - dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" - "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" @@ -111,6 +104,141 @@ source-map "^0.5.6" vue-template-es2015-compiler "^1.6.0" +"@webassemblyjs/ast@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.10.tgz#7f1e81149ca4e103c9e7cc321ea0dcb83a392512" + dependencies: + "@webassemblyjs/helper-module-context" "1.5.10" + "@webassemblyjs/helper-wasm-bytecode" "1.5.10" + "@webassemblyjs/wast-parser" "1.5.10" + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/floating-point-hex-parser@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.10.tgz#ae48705fd58927df62023f114520b8215330ff86" + +"@webassemblyjs/helper-api-error@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.10.tgz#0baf9453ce2fd8db58f0fdb4fb2852557c71d5a7" + +"@webassemblyjs/helper-buffer@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.10.tgz#abee4284161e9cd6ba7619785ca277bfcb8052ce" + dependencies: + debug "^3.1.0" + +"@webassemblyjs/helper-code-frame@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.10.tgz#4e23c05431665f16322104580af7c06253d4b4e0" + dependencies: + "@webassemblyjs/wast-printer" "1.5.10" + +"@webassemblyjs/helper-fsm@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.10.tgz#490bab613ea255a9272b764826d3cc9d15170676" + +"@webassemblyjs/helper-module-context@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.10.tgz#6fca93585228bf33e6da076d0a1373db1fdd6580" + dependencies: + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.10.tgz#90f6da93c7a186bfb2f587de442982ff533c4b44" + +"@webassemblyjs/helper-wasm-section@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.10.tgz#d64292a19f7f357c49719461065efdf7ec975d66" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-buffer" "1.5.10" + "@webassemblyjs/helper-wasm-bytecode" "1.5.10" + "@webassemblyjs/wasm-gen" "1.5.10" + debug "^3.1.0" + +"@webassemblyjs/ieee754@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.10.tgz#257cad440dd6c8a339402d31e035ba2e38e9c245" + dependencies: + ieee754 "^1.1.11" + +"@webassemblyjs/leb128@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.10.tgz#a8e4fe5f4b16daadb241fcc44d9735e9f27b05a3" + dependencies: + leb "^0.3.0" + +"@webassemblyjs/utf8@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.10.tgz#0b3b6bc86b7619c5dc7b2789db6665aa35689983" + +"@webassemblyjs/wasm-edit@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.10.tgz#0fe80f19e57f669eab1caa8c1faf9690b259d5b9" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-buffer" "1.5.10" + "@webassemblyjs/helper-wasm-bytecode" "1.5.10" + "@webassemblyjs/helper-wasm-section" "1.5.10" + "@webassemblyjs/wasm-gen" "1.5.10" + "@webassemblyjs/wasm-opt" "1.5.10" + "@webassemblyjs/wasm-parser" "1.5.10" + "@webassemblyjs/wast-printer" "1.5.10" + debug "^3.1.0" + +"@webassemblyjs/wasm-gen@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.10.tgz#8b29ddd3651259408ae5d5c816a011fb3f3f3584" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-wasm-bytecode" "1.5.10" + "@webassemblyjs/ieee754" "1.5.10" + "@webassemblyjs/leb128" "1.5.10" + "@webassemblyjs/utf8" "1.5.10" + +"@webassemblyjs/wasm-opt@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.10.tgz#569e45ab1b2bf0a7706cdf6d1b51d1188e9e4c7b" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-buffer" "1.5.10" + "@webassemblyjs/wasm-gen" "1.5.10" + "@webassemblyjs/wasm-parser" "1.5.10" + debug "^3.1.0" + +"@webassemblyjs/wasm-parser@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.10.tgz#3e1017e49f833f46b840db7cf9d194d4f00037ff" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-api-error" "1.5.10" + "@webassemblyjs/helper-wasm-bytecode" "1.5.10" + "@webassemblyjs/ieee754" "1.5.10" + "@webassemblyjs/leb128" "1.5.10" + "@webassemblyjs/wasm-parser" "1.5.10" + +"@webassemblyjs/wast-parser@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.10.tgz#1a3235926483c985a00ee8ebca856ffda9544934" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/floating-point-hex-parser" "1.5.10" + "@webassemblyjs/helper-api-error" "1.5.10" + "@webassemblyjs/helper-code-frame" "1.5.10" + "@webassemblyjs/helper-fsm" "1.5.10" + long "^3.2.0" + mamacro "^0.0.3" + +"@webassemblyjs/wast-printer@1.5.10": + version "1.5.10" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.10.tgz#adb38831ba45efd0a5c7971b666e179b64f68bba" + dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/wast-parser" "1.5.10" + long "^3.2.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -225,10 +353,6 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -255,14 +379,6 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" - -any-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" - anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -318,10 +434,6 @@ arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -405,11 +517,7 @@ assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" -ast-types@0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" - -ast-types@0.11.3, ast-types@0.x.x: +ast-types@0.x.x: version "0.11.3" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" @@ -421,11 +529,11 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@1.x, async@^1.4.0, async@^1.5.0, async@^1.5.2: +async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.0.0, async@^2.1.4, async@^2.6.0: +async@^2.0.0, async@^2.1.4: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" dependencies: @@ -706,10 +814,6 @@ babel-plugin-syntax-async-generators@^6.5.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" -babel-plugin-syntax-class-constructor-call@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" - babel-plugin-syntax-class-properties@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" @@ -726,14 +830,6 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" -babel-plugin-syntax-export-extensions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" - -babel-plugin-syntax-flow@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" - babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" @@ -758,14 +854,6 @@ babel-plugin-transform-async-to-generator@^6.24.1: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-class-constructor-call@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" - dependencies: - babel-plugin-syntax-class-constructor-call "^6.18.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-plugin-transform-class-properties@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" @@ -968,20 +1056,6 @@ babel-plugin-transform-exponentiation-operator@^6.24.1: babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-export-extensions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" - dependencies: - babel-plugin-syntax-export-extensions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-flow-strip-types@^6.8.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" - dependencies: - babel-plugin-syntax-flow "^6.18.0" - babel-runtime "^6.22.0" - babel-plugin-transform-object-rest-spread@^6.22.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" @@ -1002,7 +1076,7 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" -babel-preset-es2015@^6.24.1, babel-preset-es2015@^6.9.0: +babel-preset-es2015@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" dependencies: @@ -1052,14 +1126,6 @@ babel-preset-latest@^6.24.1: babel-preset-es2016 "^6.24.1" babel-preset-es2017 "^6.24.1" -babel-preset-stage-1@^6.5.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" - dependencies: - babel-plugin-transform-class-constructor-call "^6.24.1" - babel-plugin-transform-export-extensions "^6.22.0" - babel-preset-stage-2 "^6.24.1" - babel-preset-stage-2@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" @@ -1079,7 +1145,7 @@ babel-preset-stage-3@^6.24.1: babel-plugin-transform-exponentiation-operator "^6.24.1" babel-plugin-transform-object-rest-spread "^6.22.0" -babel-register@^6.26.0, babel-register@^6.9.0: +babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" dependencies: @@ -1131,11 +1197,11 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26 lodash "^4.17.4" to-fast-properties "^1.0.3" -babylon@7.0.0-beta.44, babylon@^7.0.0-beta.30: +babylon@7.0.0-beta.44: version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" -babylon@^6.17.3, babylon@^6.18.0: +babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -1207,10 +1273,6 @@ binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" -binaryextensions@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935" - bitsyntax@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" @@ -1518,10 +1580,6 @@ cacheable-request@^2.1.1: normalize-url "2.0.1" responselike "1.0.2" -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -1587,7 +1645,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1597,7 +1655,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1605,14 +1663,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" - dependencies: - ansi-styles "~1.0.0" - has-color "~0.1.0" - strip-ansi "~0.1.0" - chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" @@ -1708,35 +1758,12 @@ cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - -cli-table@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" - dependencies: - colors "1.0.3" - -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" - cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -1765,40 +1792,16 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - clone-response@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" dependencies: mimic-response "^1.0.0" -clone-stats@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - -clone@^1.0.0, clone@^1.0.2: +clone@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - -cloneable-readable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" - dependencies: - inherits "^2.0.1" - process-nextick-args "^1.0.6" - through2 "^2.0.1" - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -1856,11 +1859,7 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" -colors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" - -colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: +colors@^1.1.0, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -2357,10 +2356,6 @@ dagre-layout@^0.8.0: graphlib "^2.1.1" lodash "^4.17.4" -dargs@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -2371,10 +2366,6 @@ data-uri-to-buffer@1: version "1.2.0" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" -date-fns@^1.27.2: - version "1.29.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" - date-format@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" @@ -2383,10 +2374,6 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -dateformat@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" - de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" @@ -2427,7 +2414,7 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" -decompress-response@^3.2.0, decompress-response@^3.3.0: +decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" dependencies: @@ -2437,10 +2424,6 @@ deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" -deep-extend@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" - deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -2543,10 +2526,6 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-conflict@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -2565,7 +2544,7 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" -diff@^3.3.1, diff@^3.4.0, diff@^3.5.0: +diff@^3.4.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -2696,15 +2675,11 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -editions@^1.3.3: - version "1.3.4" - resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -ejs@^2.5.7, ejs@^2.5.9: +ejs@^2.5.7: version "2.5.9" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5" @@ -2712,10 +2687,6 @@ electron-to-chromium@^1.2.7: version "1.3.3" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e" -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2809,10 +2780,6 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -envinfo@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-4.4.2.tgz#472c49f3a8b9bca73962641ce7cb692bf623cd1c" - errno@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" @@ -2831,19 +2798,6 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -error-ex@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" - dependencies: - is-arrayish "^0.2.1" - -error@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" - dependencies: - string-template "~0.2.1" - xtend "~4.0.0" - es-abstract@^1.7.0: version "1.10.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" @@ -3048,7 +3002,7 @@ esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" @@ -3134,10 +3088,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - expand-braces@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" @@ -3177,12 +3127,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - dependencies: - homedir-polyfill "^1.0.1" - exports-loader@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5" @@ -3281,16 +3225,6 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" -fast-glob@^2.0.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.1.tgz#686c2345be88f3741e174add0be6f2e5b6078889" - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.1" - micromatch "^3.1.10" - fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3315,13 +3249,6 @@ faye-websocket@~0.11.0: dependencies: websocket-driver ">=0.5.1" -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3417,12 +3344,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" - dependencies: - readable-stream "^2.0.2" - flat-cache@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" @@ -3436,10 +3357,6 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -flow-parser@^0.*: - version "0.66.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.66.0.tgz#be583fefb01192aa5164415d31a6241b35718983" - flush-write-stream@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" @@ -3644,26 +3561,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gh-got@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/gh-got/-/gh-got-6.0.0.tgz#d74353004c6ec466647520a10bd46f7299d268d0" - dependencies: - got "^7.0.0" - is-plain-obj "^1.1.0" - -github-username@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/github-username/-/github-username-4.1.0.tgz#cbe280041883206da4212ae9e4b5f169c30bf417" - dependencies: - gh-got "^6.0.0" - -glob-all@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-all/-/glob-all-3.1.0.tgz#8913ddfb5ee1ac7812656241b03d5217c64b02ab" - dependencies: - glob "^7.0.5" - yargs "~1.2.6" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -3684,10 +3581,6 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -3698,7 +3591,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -3715,23 +3608,9 @@ global-dirs@^0.1.0: dependencies: ini "^1.3.4" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" +global-modules-path@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb" globals@^11.0.1, globals@^11.1.0: version "11.5.0" @@ -3773,18 +3652,6 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -globby@^8.0.0, globby@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - fast-glob "^2.0.2" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -3807,26 +3674,7 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -got@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.0.3, got@^8.2.0: +got@^8.0.3: version "8.3.0" resolved "https://registry.yarnpkg.com/got/-/got-8.3.0.tgz#6ba26e75f8a6cc4c6b3eb1fe7ce4fec7abac8533" dependencies: @@ -3858,12 +3706,6 @@ graphlib@^2.1.1: dependencies: lodash "^4.11.1" -grouped-queue@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-0.3.3.tgz#c167d2a5319c5a0e0964ef6a25b7c2df8996c85c" - dependencies: - lodash "^4.17.2" - gzip-size@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c" @@ -3928,10 +3770,6 @@ has-binary2@~1.0.2: dependencies: isarray "2.0.1" -has-color@~0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - has-cors@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" @@ -4067,12 +3905,6 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" -homedir-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - dependencies: - parse-passwd "^1.0.0" - hosted-git-info@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" @@ -4203,6 +4035,10 @@ icss-utils@^2.1.0: dependencies: postcss "^6.0.1" +ieee754@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -4251,10 +4087,6 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -4309,7 +4141,7 @@ inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^5.1.0, inquirer@^5.2.0: +inquirer@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" dependencies: @@ -4333,7 +4165,7 @@ internal-ip@1.2.0: dependencies: meow "^3.3.0" -interpret@^1.0.0, interpret@^1.0.4: +interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" @@ -4553,12 +4385,6 @@ is-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" -is-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" - dependencies: - symbol-observable "^0.2.2" - is-odd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" @@ -4581,7 +4407,7 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: +is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -4627,12 +4453,6 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-scoped@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-1.0.0.tgz#449ca98299e713038256289ecb2b540dc437cb30" - dependencies: - scoped-regex "^1.0.0" - is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4655,7 +4475,7 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4675,7 +4495,7 @@ isarray@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" -isbinaryfile@^3.0.0, isbinaryfile@^3.0.2: +isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -4783,14 +4603,6 @@ istanbul@^0.4.5: which "^1.1.1" wordwrap "^1.0.0" -istextorbinary@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53" - dependencies: - binaryextensions "2" - editions "^1.3.3" - textextensions "2" - isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" @@ -4854,46 +4666,6 @@ jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" -jscodeshift@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.4.1.tgz#da91a1c2eccfa03a3387a21d39948e251ced444a" - dependencies: - async "^1.5.0" - babel-plugin-transform-flow-strip-types "^6.8.0" - babel-preset-es2015 "^6.9.0" - babel-preset-stage-1 "^6.5.0" - babel-register "^6.9.0" - babylon "^6.17.3" - colors "^1.1.2" - flow-parser "^0.*" - lodash "^4.13.1" - micromatch "^2.3.7" - node-dir "0.1.8" - nomnom "^1.8.1" - recast "^0.12.5" - temp "^0.8.1" - write-file-atomic "^1.2.0" - -jscodeshift@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.5.0.tgz#bdb7b6cc20dd62c16aa728c3fa2d2fe66ca7c748" - dependencies: - babel-plugin-transform-flow-strip-types "^6.8.0" - babel-preset-es2015 "^6.9.0" - babel-preset-stage-1 "^6.5.0" - babel-register "^6.9.0" - babylon "^7.0.0-beta.30" - colors "^1.1.2" - flow-parser "^0.*" - lodash "^4.13.1" - micromatch "^2.3.7" - neo-async "^2.5.0" - node-dir "0.1.8" - nomnom "^1.8.1" - recast "^0.14.1" - temp "^0.8.1" - write-file-atomic "^1.2.0" - jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -4910,7 +4682,7 @@ json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" -json-parse-better-errors@^1.0.1: +json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -5108,6 +4880,10 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +leb@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -5137,54 +4913,6 @@ lie@~3.1.0: dependencies: immediate "~3.0.5" -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - -listr-update-renderer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -listr@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d" - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-observable "^0.2.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.4.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.4.2" - stream-to-observable "^0.2.0" - strip-ansi "^3.0.1" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -5204,15 +4932,6 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" @@ -5272,29 +4991,16 @@ lodash@4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: +lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - dependencies: - chalk "^1.0.0" - -log-symbols@^2.1.0, log-symbols@^2.2.0: +log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: chalk "^2.0.1" -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - log4js@^2.3.9: version "2.5.3" resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1" @@ -5330,6 +5036,10 @@ loglevelnext@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e" +long@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -5391,12 +5101,16 @@ mailgun-js@^0.7.0: q "~1.4.0" tsscmp "~1.0.0" -make-dir@^1.0.0, make-dir@^1.1.0: +make-dir@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" dependencies: pify "^3.0.0" +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5438,30 +5152,6 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -mem-fs-editor@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-4.0.1.tgz#27e6b59df91b37248e9be2145b1bea84695103ed" - dependencies: - commondir "^1.0.1" - deep-extend "^0.5.1" - ejs "^2.5.9" - glob "^7.0.3" - globby "^8.0.0" - isbinaryfile "^3.0.2" - mkdirp "^0.5.0" - multimatch "^2.0.0" - rimraf "^2.2.8" - through2 "^2.0.0" - vinyl "^2.0.1" - -mem-fs@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc" - dependencies: - through2 "^2.0.0" - vinyl "^1.1.0" - vinyl-file "^2.0.0" - mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" @@ -5504,15 +5194,11 @@ merge-source-map@^1.1.0: dependencies: source-map "^0.6.1" -merge2@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5, micromatch@^2.3.7: +micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -5530,7 +5216,7 @@ micromatch@^2.1.5, micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: +micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: @@ -5603,10 +5289,6 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de" - minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -5637,7 +5319,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -5685,15 +5367,6 @@ multicast-dns@^6.0.1: dns-packet "^1.0.1" thunky "^0.1.0" -multimatch@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -5739,10 +5412,6 @@ nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" -node-dir@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d" - node-forge@0.6.33: version "0.6.33" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" @@ -5859,13 +5528,6 @@ nodemon@^1.17.3: undefsafe "^2.0.2" update-notifier "^2.3.0" -nomnom@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" - dependencies: - chalk "~0.4.0" - underscore "~1.6.0" - nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -6011,10 +5673,6 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -6049,15 +5707,6 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - original@>=0.0.5: version "1.0.0" resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" @@ -6091,20 +5740,10 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - p-cancelable@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - dependencies: - p-reduce "^1.0.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -6113,10 +5752,6 @@ p-is-promise@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" -p-lazy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-lazy/-/p-lazy-1.0.0.tgz#ec53c802f2ee3ac28f166cc82d0b2b02de27a835" - p-limit@^1.0.0, p-limit@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" @@ -6133,16 +5768,6 @@ p-map@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - dependencies: - p-finally "^1.0.0" - p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" @@ -6223,17 +5848,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -6342,7 +5956,7 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" -pify@^2.0.0, pify@^2.3.0: +pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -6672,25 +6286,21 @@ prettier@1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" -prettier@^1.11.1, prettier@^1.5.3: +prettier@^1.11.1: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" -pretty-bytes@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" - prismjs@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365" optionalDependencies: clipboard "^1.5.5" -private@^0.1.6, private@^0.1.8, private@~0.1.5: +private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" -process-nextick-args@^1.0.6, process-nextick-args@~1.0.6: +process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -6898,13 +6508,6 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -read-chunk@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-2.1.0.tgz#6a04c0928005ed9d42e1a6ac5600e19cbc7ff655" - dependencies: - pify "^3.0.0" - safe-buffer "^5.1.1" - read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -6919,13 +6522,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -6942,14 +6538,6 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - "readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071" @@ -7003,31 +6591,6 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" -recast@^0.12.5: - version "0.12.9" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.12.9.tgz#e8e52bdb9691af462ccbd7c15d5a5113647a15f1" - dependencies: - ast-types "0.10.1" - core-js "^2.4.1" - esprima "~4.0.0" - private "~0.1.5" - source-map "~0.6.1" - -recast@^0.14.1: - version "0.14.7" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" - dependencies: - ast-types "0.11.3" - esprima "~4.0.0" - private "~0.1.5" - source-map "~0.6.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -7155,14 +6718,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - request@2.75.x: version "2.75.0" resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" @@ -7281,13 +6836,6 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" -resolve-dir@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" @@ -7304,7 +6852,7 @@ resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.6, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0: +resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0: version "1.7.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" dependencies: @@ -7316,13 +6864,6 @@ responselike@1.0.2: dependencies: lowercase-keys "^1.0.0" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7346,10 +6887,6 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2. dependencies: glob "^7.0.5" -rimraf@~2.2.6: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" @@ -7357,7 +6894,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -run-async@^2.0.0, run-async@^2.2.0: +run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: @@ -7379,7 +6916,7 @@ rx-lite@*, rx-lite@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" -rxjs@^5.4.2, rxjs@^5.5.2: +rxjs@^5.5.2: version "5.5.10" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045" dependencies: @@ -7418,10 +6955,6 @@ schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4 ajv "^6.1.0" ajv-keywords "^3.1.0" -scoped-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -7565,14 +7098,6 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -shelljs@^0.8.0: - version "0.8.1" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.1.tgz#729e038c413a2254c4078b95ed46e0397154a9f1" - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -7587,20 +7112,12 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - slice-ansi@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" dependencies: is-fullwidth-code-point "^2.0.0" -slide@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - smart-buffer@^1.0.13, smart-buffer@^1.0.4: version "1.1.15" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" @@ -7937,12 +7454,6 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" -stream-to-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" - dependencies: - any-observable "^0.2.0" - streamroller@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" @@ -7956,10 +7467,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -8007,17 +7514,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" - -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" - dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8085,10 +7581,6 @@ symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" -symbol-observable@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" - table@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" @@ -8129,13 +7621,6 @@ tar@^2.2.1: fstream "^1.0.2" inherits "2" -temp@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" - dependencies: - os-tmpdir "^1.0.0" - rimraf "~2.2.6" - term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" @@ -8152,14 +7637,10 @@ test-exclude@^4.2.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -text-table@^0.2.0, text-table@~0.2.0: +text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" -textextensions@2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286" - three-orbit-controls@^82.1.0: version "82.1.0" resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4" @@ -8172,7 +7653,7 @@ three@^0.84.0: version "0.84.0" resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918" -through2@^2.0.0, through2@^2.0.1: +through2@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" dependencies: @@ -8381,10 +7862,6 @@ underscore@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4" -underscore@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" - underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" @@ -8441,10 +7918,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -untildify@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.2.tgz#7f1f302055b3fea0f3e81dc78eb36766cb65e3f1" - unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" @@ -8561,9 +8034,9 @@ uws@~9.14.0: version "9.14.0" resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95" -v8-compile-cache@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4" +v8-compile-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" validate-npm-package-license@^3.0.1: version "3.0.1" @@ -8592,36 +8065,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vinyl-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a" - dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - strip-bom-stream "^2.0.0" - vinyl "^1.1.0" - -vinyl@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - visibilityjs@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/visibilityjs/-/visibilityjs-1.2.4.tgz#bff8663da62c8c10ad4ee5ae6a1ae6fac4259d63" @@ -8715,12 +8158,6 @@ wbuf@^1.1.0, wbuf@^1.7.2: dependencies: minimalistic-assert "^1.0.0" -webpack-addons@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/webpack-addons/-/webpack-addons-1.1.5.tgz#2b178dfe873fb6e75e40a819fa5c26e4a9bc837a" - dependencies: - jscodeshift "^0.4.0" - webpack-bundle-analyzer@^2.11.1: version "2.11.1" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.1.tgz#b9fbfb6a32c0a8c1c3237223e90890796b950ab9" @@ -8738,36 +8175,21 @@ webpack-bundle-analyzer@^2.11.1: opener "^1.4.3" ws "^4.0.0" -webpack-cli@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-2.1.2.tgz#9c9a4b90584f7b8acaf591238ef0667e04c817f6" +webpack-cli@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.2.tgz#e48c5662aff8ed5aac3db5f82f51d7f32e50459e" dependencies: - chalk "^2.3.2" + chalk "^2.4.1" cross-spawn "^6.0.5" - diff "^3.5.0" enhanced-resolve "^4.0.0" - envinfo "^4.4.2" - glob-all "^3.1.0" - global-modules "^1.0.0" - got "^8.2.0" + global-modules-path "^2.1.0" import-local "^1.0.0" - inquirer "^5.1.0" - interpret "^1.0.4" - jscodeshift "^0.5.0" - listr "^0.13.0" + inquirer "^5.2.0" + interpret "^1.1.0" loader-utils "^1.1.0" - lodash "^4.17.5" - log-symbols "^2.2.0" - mkdirp "^0.5.1" - p-each-series "^1.0.0" - p-lazy "^1.0.0" - prettier "^1.5.3" - supports-color "^5.3.0" - v8-compile-cache "^1.1.2" - webpack-addons "^1.1.5" + supports-color "^5.4.0" + v8-compile-cache "^2.0.0" yargs "^11.1.0" - yeoman-environment "^2.0.0" - yeoman-generator "^2.0.4" webpack-dev-middleware@3.1.3: version "3.1.3" @@ -8846,10 +8268,15 @@ webpack-stats-plugin@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595" -webpack@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.7.0.tgz#a04f68dab86d5545fd0277d07ffc44e4078154c9" +webpack@^4.11.1: + version "4.11.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.11.1.tgz#1aa0b936f7ae93a52cf38d2ad0d0f46dcf3c2723" dependencies: + "@webassemblyjs/ast" "1.5.10" + "@webassemblyjs/helper-module-context" "1.5.10" + "@webassemblyjs/wasm-edit" "1.5.10" + "@webassemblyjs/wasm-opt" "1.5.10" + "@webassemblyjs/wasm-parser" "1.5.10" acorn "^5.0.0" acorn-dynamic-import "^3.0.0" ajv "^6.1.0" @@ -8857,6 +8284,7 @@ webpack@^4.7.0: chrome-trace-event "^0.1.1" enhanced-resolve "^4.0.0" eslint-scope "^3.7.1" + json-parse-better-errors "^1.0.2" loader-runner "^2.3.0" loader-utils "^1.1.0" memory-fs "~0.4.1" @@ -8892,7 +8320,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@^1.1.1, which@^1.2.1, which@^1.2.14, which@^1.2.9: +which@^1.1.1, which@^1.2.1, which@^1.2.9: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: @@ -8951,14 +8379,6 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^1.2.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - slide "^1.1.5" - write-file-atomic@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" @@ -9001,7 +8421,7 @@ xregexp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -9057,12 +8477,6 @@ yargs@^11.1.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@~1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b" - dependencies: - minimist "^0.1.0" - yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" @@ -9075,53 +8489,3 @@ yargs@~3.10.0: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - -yeoman-environment@^2.0.0, yeoman-environment@^2.0.5: - version "2.1.1" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.1.1.tgz#10a045f7fc4397873764882eae055a33e56ee1c5" - dependencies: - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^3.1.0" - diff "^3.3.1" - escape-string-regexp "^1.0.2" - globby "^8.0.1" - grouped-queue "^0.3.3" - inquirer "^5.2.0" - is-scoped "^1.0.0" - lodash "^4.17.10" - log-symbols "^2.1.0" - mem-fs "^1.1.0" - strip-ansi "^4.0.0" - text-table "^0.2.0" - untildify "^3.0.2" - -yeoman-generator@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-2.0.5.tgz#57b0b3474701293cc9ec965288f3400b00887c81" - dependencies: - async "^2.6.0" - chalk "^2.3.0" - cli-table "^0.3.1" - cross-spawn "^6.0.5" - dargs "^5.1.0" - dateformat "^3.0.3" - debug "^3.1.0" - detect-conflict "^1.0.0" - error "^7.0.2" - find-up "^2.1.0" - github-username "^4.0.0" - istextorbinary "^2.2.1" - lodash "^4.17.10" - make-dir "^1.1.0" - mem-fs-editor "^4.0.0" - minimist "^1.2.0" - pretty-bytes "^4.0.2" - read-chunk "^2.1.0" - read-pkg-up "^3.0.0" - rimraf "^2.6.2" - run-async "^2.0.0" - shelljs "^0.8.0" - text-table "^0.2.0" - through2 "^2.0.0" - yeoman-environment "^2.0.5" -- cgit v1.2.1 From 6d96b9dd6be8d27d3030e7a6de08ec3ba419643e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 03:25:13 -0500 Subject: add monaco-editor-webpack-plugin and update webpack config --- .../javascripts/ide/components/repo_editor.vue | 10 ++---- config/webpack.config.js | 36 +++------------------- package.json | 1 + yarn.lock | 4 +++ 4 files changed, 12 insertions(+), 39 deletions(-) diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 93453989c08..5af0f142d5e 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -1,10 +1,9 @@