diff options
43 files changed, 582 insertions, 88 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b3593df8b13..1ed267cfe5e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -75,11 +75,6 @@ stages: - mysql:5.7 - redis:alpine -.rails5-variables: &rails5-variables - script: - - export RAILS5=${RAILS5} - - export BUNDLE_GEMFILE=${BUNDLE_GEMFILE} - .rails5: &rails5 allow_failure: true only: @@ -139,7 +134,7 @@ stages: - export SCRIPT_NAME="${SCRIPT_NAME:-$CI_JOB_NAME}" - apk add --update openssl - wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/$SCRIPT_NAME - - chmod 755 $SCRIPT_NAME + - chmod 755 $(basename $SCRIPT_NAME) .rake-exec: &rake-exec <<: *dedicated-no-docs-no-db-pull-cache-job @@ -150,7 +145,6 @@ stages: <<: *dedicated-runner <<: *except-docs-and-qa <<: *pull-cache - <<: *rails5-variables stage: test script: - JOB_NAME=( $CI_JOB_NAME ) @@ -929,3 +923,94 @@ no_ee_check: - scripts/no-ee-check only: - //@gitlab-org/gitlab-ce + +# GitLab Review apps +review: + image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base + stage: test + allow_failure: true + before_script: + - gem install gitlab --no-document + variables: + GIT_DEPTH: "1" + HOST_SUFFIX: "$CI_ENVIRONMENT_SLUG" + DOMAIN: "-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN" + GITLAB_HELM_CHART_REF: "master" + script: + - export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION) + - export GITALY_VERSION=$(<GITALY_SERVER_VERSION) + - export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION) + - source ./scripts/review_apps/review-apps.sh + - BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng + - check_kube_domain + - download_gitlab_chart + - ensure_namespace + - install_tiller + - create_secret + - install_external_dns + - deploy + environment: + name: review/$CI_COMMIT_REF_NAME + url: https://gitlab-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN + on_stop: stop_review + only: + refs: + - branches@gitlab-org/gitlab-ce + - branches@gitlab-org/gitlab-ee + kubernetes: active + except: + refs: + - master + - /(^docs[\/-].*|.*-docs$)/ + +stop_review: + <<: *single-script-job + image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base + stage: test + allow_failure: true + cache: {} + dependencies: [] + variables: + SCRIPT_NAME: "review_apps/review-apps.sh" + script: + - source $(basename "${SCRIPT_NAME}") + - delete + - cleanup + when: manual + environment: + name: review/$CI_COMMIT_REF_NAME + action: stop + only: + refs: + - branches@gitlab-org/gitlab-ce + - branches@gitlab-org/gitlab-ee + kubernetes: active + except: + - master + - /(^docs[\/-].*|.*-docs$)/ + +schedule:review_apps_cleanup: + <<: *dedicated-no-docs-pull-cache-job + image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base + stage: build + allow_failure: true + cache: {} + dependencies: [] + before_script: + - gem install gitlab --no-document + variables: + GIT_DEPTH: "1" + script: + - ruby -rrubygems scripts/review_apps/automated_cleanup.rb + environment: + name: review/auto-cleanup + action: stop + only: + refs: + - schedules@gitlab-org/gitlab-ce + - schedules@gitlab-org/gitlab-ee + kubernetes: active + except: + - master + - tags + - /(^docs[\/-].*|.*-docs$)/ diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md index 64b54b171f7..69cf7fe1548 100644 --- a/.gitlab/issue_templates/Security developer workflow.md +++ b/.gitlab/issue_templates/Security developer workflow.md @@ -16,7 +16,6 @@ Set the title to: `[Security] Description of the original issue` - [ ] Add a link to the MR to the [links section](#links) - [ ] Add a link to an EE MR if required - [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**. -- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping. #### Backports @@ -26,7 +25,8 @@ Set the title to: `[Security] Description of the original issue` - [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable) - [ ] Create each MR targetting the security branch `security-X-Y` - [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR -- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager. +- [ ] Add the ~"Merge into Security" label to all of the MRs. +- [ ] Make sure all MRs have a link in the [links section](#links) [secpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script @@ -417,8 +417,7 @@ end gem 'gitaly-proto', '~> 0.118.1', require: 'gitaly' gem 'grpc', '~> 1.15.0' -# Locked until https://github.com/google/protobuf/issues/4210 is closed -gem 'google-protobuf', '= 3.5.1' +gem 'google-protobuf', '~> 3.6' gem 'toml-rb', '~> 1.0.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index a39788bee9f..bf16bef4f32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -303,7 +303,7 @@ GEM mime-types (~> 3.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - google-protobuf (3.5.1) + google-protobuf (3.6.1) googleapis-common-protos-types (1.0.2) google-protobuf (~> 3.0) googleauth (0.6.6) @@ -1005,7 +1005,7 @@ DEPENDENCIES gitlab_omniauth-ldap (~> 2.0.4) gon (~> 6.2) google-api-client (~> 0.23) - google-protobuf (= 3.5.1) + google-protobuf (~> 3.6) gpgme grape (~> 1.1) grape-entity (~> 0.7.1) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 1421edb1d39..81547303ed2 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -306,7 +306,7 @@ GEM mime-types (~> 3.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - google-protobuf (3.5.1) + google-protobuf (3.6.1) googleapis-common-protos-types (1.0.2) google-protobuf (~> 3.0) googleauth (0.6.6) @@ -1014,7 +1014,7 @@ DEPENDENCIES gitlab_omniauth-ldap (~> 2.0.4) gon (~> 6.2) google-api-client (~> 0.23) - google-protobuf (= 3.5.1) + google-protobuf (~> 3.6) gpgme grape (~> 1.1) grape-entity (~> 0.7.1) diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index de4566bb119..05de970e387 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -6,10 +6,12 @@ import Pager from './pager'; import { localTimeAgo } from './lib/utils/datetime_utility'; export default class Activities { - constructor() { - Pager.init(20, true, false, data => data, this.updateTooltips); + constructor(container = '') { + this.container = container; - $('.event-filter-link').on('click', (e) => { + Pager.init(20, true, false, data => data, this.updateTooltips, this.container); + + $('.event-filter-link').on('click', e => { e.preventDefault(); this.toggleFilter(e.currentTarget); this.reloadActivities(); @@ -22,7 +24,7 @@ export default class Activities { reloadActivities() { $('.content_list').html(''); - Pager.init(20, true, false, data => data, this.updateTooltips); + Pager.init(20, true, false, data => data, this.updateTooltips, this.container); } toggleFilter(sender) { diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index cfe4273742f..34e836a570a 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -1,17 +1,30 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; +import { TooltipDirective as Tooltip } from '@gitlab-org/gitlab-ui'; +import { convertPermissionToBoolean } from '~/lib/utils/common_utils'; import Icon from '~/vue_shared/components/icon.vue'; import FileRow from '~/vue_shared/components/file_row.vue'; import FileRowStats from './file_row_stats.vue'; +const treeListStorageKey = 'mr_diff_tree_list'; + export default { + directives: { + Tooltip, + }, components: { Icon, FileRow, }, data() { + const treeListStored = localStorage.getItem(treeListStorageKey); + const renderTreeList = treeListStored !== null ? + convertPermissionToBoolean(treeListStored) : true; + return { search: '', + renderTreeList, + focusSearch: false, }; }, computed: { @@ -20,15 +33,35 @@ export default { filteredTreeList() { const search = this.search.toLowerCase().trim(); - if (search === '') return this.tree; + if (search === '') return this.renderTreeList ? this.tree : this.allBlobs; return this.allBlobs.filter(f => f.name.toLowerCase().indexOf(search) >= 0); }, + rowDisplayTextKey() { + if (this.renderTreeList && this.search.trim() === '') { + return 'name'; + } + + return 'path'; + }, }, methods: { ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']), clearSearch() { this.search = ''; + this.toggleFocusSearch(false); + }, + toggleRenderTreeList(toggle) { + this.renderTreeList = toggle; + localStorage.setItem(treeListStorageKey, this.renderTreeList); + }, + toggleFocusSearch(toggle) { + this.focusSearch = toggle; + }, + blurSearch() { + if (this.search.trim() === '') { + this.toggleFocusSearch(false); + } }, }, FileRowStats, @@ -37,28 +70,67 @@ export default { <template> <div class="tree-list-holder d-flex flex-column"> - <div class="append-bottom-8 position-relative tree-list-search"> - <icon - name="search" - class="position-absolute tree-list-icon" - /> - <input - v-model="search" - :placeholder="s__('MergeRequest|Filter files')" - type="search" - class="form-control" - /> - <button - v-show="search" - :aria-label="__('Clear search')" - type="button" - class="position-absolute tree-list-icon tree-list-clear-icon border-0 p-0" - @click="clearSearch" - > + <div class="append-bottom-8 position-relative tree-list-search d-flex"> + <div class="flex-fill d-flex"> <icon - name="close" + name="search" + class="position-absolute tree-list-icon" + /> + <input + v-model="search" + :placeholder="s__('MergeRequest|Filter files')" + type="search" + class="form-control" + @focus="toggleFocusSearch(true)" + @blur="blurSearch" /> - </button> + <button + v-show="search" + :aria-label="__('Clear search')" + type="button" + class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0" + @click="clearSearch" + > + <icon + name="close" + /> + </button> + </div> + <div + v-show="!focusSearch" + class="btn-group prepend-left-8 tree-list-view-toggle" + > + <button + v-tooltip.hover + :aria-label="__('List view')" + :title="__('List view')" + :class="{ + active: !renderTreeList + }" + class="btn btn-default pt-0 pb-0 d-flex align-items-center" + type="button" + @click="toggleRenderTreeList(false)" + > + <icon + name="hamburger" + /> + </button> + <button + v-tooltip.hover + :aria-label="__('Tree view')" + :title="__('Tree view')" + :class="{ + active: renderTreeList + }" + class="btn btn-default pt-0 pb-0 d-flex align-items-center" + type="button" + @click="toggleRenderTreeList(true)" + > + <icon + name="file-tree" + /> + </button> + </div> </div> <div class="tree-list-scroll" @@ -72,6 +144,8 @@ export default { :hide-extra-on-tree="true" :extra-component="$options.FileRowStats" :show-changed-icon="true" + :display-text-key="rowDisplayTextKey" + :should-truncate-start="true" @toggleTreeOpen="toggleTreeOpen" @clickFile="scrollToFile" /> diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index a29de9ae899..a6ab614df16 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -30,12 +30,12 @@ const createAction = config => ` </a> `; -const createFlashEl = (message, type, isInContentWrapper = false) => ` +const createFlashEl = (message, type, isFixedLayout = false) => ` <div class="flash-${type}" > <div - class="flash-text ${isInContentWrapper ? 'container-fluid container-limited' : ''}" + class="flash-text ${isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''}" > ${_.escape(message)} </div> @@ -69,12 +69,13 @@ const createFlash = function createFlash( addBodyClass = false, ) { const flashContainer = parent.querySelector('.flash-container'); + const navigation = parent.querySelector('.content'); if (!flashContainer) return null; - const isInContentWrapper = flashContainer.parentNode.classList.contains('content-wrapper'); + const isFixedLayout = navigation ? navigation.parentNode.classList.contains('container-limited') : true; - flashContainer.innerHTML = createFlashEl(message, type, isInContentWrapper); + flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout); const flashEl = flashContainer.querySelector(`.flash-${type}`); removeFlashClickListener(flashEl, fadeTransition); diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job_container_item.vue index 81cc0823792..6486b25c8a7 100644 --- a/app/assets/javascripts/jobs/components/job_container_item.vue +++ b/app/assets/javascripts/jobs/components/job_container_item.vue @@ -1,5 +1,4 @@ <script> -import _ from 'underscore'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; import tooltip from '~/vue_shared/directives/tooltip'; @@ -9,11 +8,9 @@ export default { CiIcon, Icon, }, - directives: { tooltip, }, - props: { job: { type: Object, @@ -24,10 +21,9 @@ export default { required: true, }, }, - computed: { tooltipText() { - return `${_.escape(this.job.name)} - ${this.job.status.tooltip}`; + return `${this.job.name} - ${this.job.status.tooltip}`; }, }, }; @@ -36,7 +32,10 @@ export default { <template> <div class="build-job" - :class="{ retried: job.retried, active: isActive }" + :class="{ + retried: job.retried, + active: isActive + }" > <a v-tooltip diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 3b58c54b3f4..386a9b2c740 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -7,14 +7,21 @@ const ENDLESS_SCROLL_BOTTOM_PX = 400; const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; export default { - init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { + init( + limit = 0, + preload = false, + disable = false, + prepareData = $.noop, + callback = $.noop, + container = '', + ) { this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']); this.limit = limit; this.offset = parseInt(getParameterByName('offset'), 10) || this.limit; this.disable = disable; this.prepareData = prepareData; this.callback = callback; - this.loading = $('.loading').first(); + this.loading = $(`${container} .loading`).first(); if (preload) { this.offset = 0; this.getOld(); diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 1de9945baad..04bcb16f036 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -170,7 +170,7 @@ export default class UserTabs { this.loadActivityCalendar('activity'); // eslint-disable-next-line no-new - new Activities(); + new Activities('#activity'); this.loaded.activity = true; } diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue index 36a345130c0..2d89a156117 100644 --- a/app/assets/javascripts/vue_shared/components/file_row.vue +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -34,10 +34,21 @@ export default { required: false, default: false, }, + displayTextKey: { + type: String, + required: false, + default: 'name', + }, + shouldTruncateStart: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { mouseOver: false, + truncateStart: 0, }; }, computed: { @@ -60,6 +71,15 @@ export default { 'is-open': this.file.opened, }; }, + outputText() { + const text = this.file[this.displayTextKey]; + + if (this.truncateStart === 0) { + return text; + } + + return `...${text.substring(this.truncateStart, text.length)}`; + }, }, watch: { 'file.active': function fileActiveWatch(active) { @@ -72,6 +92,15 @@ export default { if (this.hasPathAtCurrentRoute()) { this.scrollIntoView(true); } + + if (this.shouldTruncateStart) { + const { scrollWidth, offsetWidth } = this.$refs.textOutput; + const textOverflow = scrollWidth - offsetWidth; + + if (textOverflow > 0) { + this.truncateStart = Math.ceil(textOverflow / 5) + 3; + } + } }, methods: { toggleTreeOpen(path) { @@ -139,6 +168,7 @@ export default { class="file-row-name-container" > <span + ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated" > @@ -156,7 +186,7 @@ export default { :size="16" class="append-right-5" /> - {{ file.name }} + {{ outputText }} </span> <component :is="extraComponent" @@ -175,6 +205,8 @@ export default { :hide-extra-on-tree="hideExtraOnTree" :extra-component="extraComponent" :show-changed-icon="showChangedIcon" + :display-text-key="displayTextKey" + :should-truncate-start="shouldTruncateStart" @toggleTreeOpen="toggleTreeOpen" @clickFile="clickedFile" /> diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index bf6f66d30ff..f47dfe1b563 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -37,6 +37,7 @@ button { padding-top: 0; + background-color: transparent; } &.active a, diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 8d884ad6891..52c91266ff4 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -1027,8 +1027,12 @@ overflow-x: auto; } -.tree-list-search .form-control { - padding-left: 30px; +.tree-list-search { + flex: 0 0 34px; + + .form-control { + padding-left: 30px; + } } .tree-list-icon { @@ -1063,3 +1067,9 @@ } } } + +.tree-list-view-toggle { + svg { + top: 0; + } +} diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index f573fd399a5..0c313e9e6d3 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,6 +1,15 @@ # frozen_string_literal: true module GroupsHelper + def group_overview_nav_link_paths + %w[ + groups#show + groups#activity + groups#subgroups + analytics#show + ] + end + def group_nav_link_paths %w[groups#projects groups#edit badges#index ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index] end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 43bf852c7ec..b311f5e0617 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ActiveRecord::Base - VERSION = '0.1.34'.freeze + VERSION = '0.1.35'.freeze self.table_name = 'clusters_applications_runners' diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml index 8bd5708d490..2cdaa85bdaa 100644 --- a/app/views/layouts/_flash.html.haml +++ b/app/views/layouts/_flash.html.haml @@ -6,5 +6,5 @@ -# Don't show a flash message if the message is nil - if value %div{ class: "flash-#{key}" } - %div{ class: "#{container_class} #{extra_flash_class}" } + %div{ class: "#{(container_class unless fluid_layout)} #{(extra_flash_class unless @no_container)} #{@content_class}" } %span= value diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 1420b0a4973..1b2a4cd6780 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -6,12 +6,12 @@ .mobile-overlay .alert-wrapper = render "layouts/broadcast" - = render 'layouts/header/read_only_banner' + = render "layouts/header/read_only_banner" = yield :flash_message = render "shared/ping_consent" - unless @hide_breadcrumbs = render "layouts/nav/breadcrumbs" - = render "layouts/flash" + = render "layouts/flash", extra_flash_class: 'limit-container-width' .d-flex %div{ class: "#{(container_class unless @no_container)} #{@content_class}" } .content{ id: "content-body" } diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 4aa22138498..163556f4509 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -12,7 +12,7 @@ = @group.name %ul.sidebar-top-level-items.qa-group-sidebar - if group_sidebar_link?(:overview) - = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do + = nav_link(path: group_overview_nav_link_paths, html_options: { class: 'home' }) do = link_to group_path(@group) do .nav-icon-container = sprite_icon('home') @@ -36,6 +36,16 @@ %span = _('Activity') + = render_if_exists 'groups/sidebar/security_dashboard' + + - if group_sidebar_link?(:contribution_analytics) + = nav_link(path: 'analytics#show') do + = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do + %span + Contribution Analytics + + = render_if_exists "layouts/nav/ee/epic_link", group: @group + - if group_sidebar_link?(:issues) = nav_link(path: issues_sub_menu_items) do = link_to issues_group_path(@group) do @@ -132,4 +142,6 @@ %span = _('CI / CD') + = render_if_exists "groups/ee/settings_nav" + = render 'shared/sidebar_toggle_button' diff --git a/changelogs/unreleased/53013-duplicate-escape.yml b/changelogs/unreleased/53013-duplicate-escape.yml new file mode 100644 index 00000000000..c5ec2322fb5 --- /dev/null +++ b/changelogs/unreleased/53013-duplicate-escape.yml @@ -0,0 +1,5 @@ +--- +title: Remove duplicate escape in job sidebar +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml b/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml new file mode 100644 index 00000000000..0377e10fe9e --- /dev/null +++ b/changelogs/unreleased/53023-endless-scroll-loader-is-visible-on-user-profile-overview-page.yml @@ -0,0 +1,4 @@ +title: Adds container to pager to enable scoping +merge_request: 22529 +? author +type: other diff --git a/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml b/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml new file mode 100644 index 00000000000..2ba52e07324 --- /dev/null +++ b/changelogs/unreleased/gt-add-transparent-background-to-markdown-header-tabs.yml @@ -0,0 +1,5 @@ +--- +title: Add transparent background to markdown header tabs +merge_request: 22565 +author: George Tsiolis +type: fixed diff --git a/changelogs/unreleased/mr-file-list.yml b/changelogs/unreleased/mr-file-list.yml new file mode 100644 index 00000000000..0a2a5e0c1cc --- /dev/null +++ b/changelogs/unreleased/mr-file-list.yml @@ -0,0 +1,5 @@ +--- +title: Switch between tree list & file list in diffs file browser +merge_request: 22191 +author: +type: added diff --git a/changelogs/unreleased/update-runner-chart-to-0-1-35.yml b/changelogs/unreleased/update-runner-chart-to-0-1-35.yml new file mode 100644 index 00000000000..3b8029c8d96 --- /dev/null +++ b/changelogs/unreleased/update-runner-chart-to-0-1-35.yml @@ -0,0 +1,5 @@ +--- +title: Update used version of Runner Helm Chart to 0.1.35 +merge_request: 22541 +author: +type: other diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 2952a98626a..d8345f2d6bd 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -242,6 +242,33 @@ verification requirement. Navigate to `Admin area ➔ Settings` and uncheck **Require users to prove ownership of custom domains** in the Pages section. This setting is enabled by default. +### Access control + +Access control was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) +in GitLab 11.5. It can be configured per-project, and allows access to a Pages +site to be controlled based on a user's membership to that project. + +Access control works by registering the Pages daemon as an OAuth application +with GitLab. Whenever a request to access a private Pages site is made by an +unauthenticated user, the Pages daemon redirects the user to GitLab. If +authentication is successful, the user is redirected back to Pages with a token, +which is persisted in a cookie. The cookies are signed with a secret key, so +tampering can be detected. + +Each request to view a resource in a private site is authenticated by Pages +using that token. For each request it receives, it makes a request to the GitLab +API to check that the user is authorized to read that site. + +Pages access control is currently disabled by default. To enable it, you must: + +1. Enable it in `/etc/gitlab/gitlab.rb` + + ```ruby + gitlab_pages['access_control'] = true + ``` + +1. [Reconfigure GitLab][reconfigure] + ## Activate verbose logging for daemon Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md index 295905a7625..ddff54be575 100644 --- a/doc/administration/pages/source.md +++ b/doc/administration/pages/source.md @@ -391,6 +391,44 @@ the first one with a backslash (\). For example `pages.example.io` would be: server_name ~^.*\.pages\.example\.io$; ``` +## Access control + +Access control was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) +in GitLab 11.5. It can be configured per-project, and allows access to a Pages +site to be controlled based on a user's membership to that project. + +Access control works by registering the Pages daemon as an OAuth application +with GitLab. Whenever a request to access a private Pages site is made by an +unauthenticated user, the Pages daemon redirects the user to GitLab. If +authentication is successful, the user is redirected back to Pages with a token, +which is persisted in a cookie. The cookies are signed with a secret key, so +tampering can be detected. + +Each request to view a resource in a private site is authenticated by Pages +using that token. For each request it receives, it makes a request to the GitLab +API to check that the user is authorized to read that site. + +Pages access control is currently disabled by default. To enable it, you must: + +1. Modify your `config/gitlab.yml` file: + ```yaml + pages: + access_control: true + ``` +1. [Restart GitLab][restart] +1. Create a new [system OAuth application](../../integration/oauth_provider.md#adding-an-application-through-the-profile) + This should be called `GitLab Pages` and have a `Redirect URL` of + `https://projects.example.io/auth`. It does not need to be a "trusted" + application, but it does need the "api" scope. +1. Start the Pages daemon with the following additional arguments: + + ```shell + -auth-client-secret <OAuth code generated by GitLab> \ + -auth-redirect-uri http://projects.example.io/auth \ + -auth-secret <40 random hex characters> \ + -auth-server <URL of the GitLab instance> + ``` + ## Change storage path Follow the steps below to change the default path where GitLab Pages' contents diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index 83e0fa34ad6..2a179bfbbf0 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -312,7 +312,7 @@ We're always looking for contributions that can mitigate these If you think that registration token for a Project was revealed, you should reset them. It's recommended because such token can be used to register another -Runner to thi Project. It may be next used to obtain the values of secret +Runner to the Project. It may be next used to obtain the values of secret variables or clone the project code, that normally may be unavailable for the attacker. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 424e1af7ba3..4b2a6ccc7e4 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -2031,3 +2031,5 @@ CI with various languages. [ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909 [schedules]: ../../user/project/pipelines/schedules.md [variables-expressions]: ../variables/README.md#variables-expressions +[ee]: https://about.gitlab.com/gitlab-ee/ +[gitlab-versions]: https://about.gitlab.com/products/
\ No newline at end of file diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md index 0f1f079bdb4..350593cc813 100644 --- a/doc/development/feature_flags.md +++ b/doc/development/feature_flags.md @@ -112,3 +112,8 @@ feature flag. You can stub a feature flag as follows: ```ruby stub_feature_flags(my_feature_flag: false) ``` + +## Enabling a feature flag + +Check how to [roll out changes using feature flags](rolling_out_changes_using_feature_flags.md). + diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index 6a67aa7f610..f58d79fccf1 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -20,6 +20,7 @@ are very appreciative of the work done by translators and proofreaders! - French - Davy Defaud - [GitLab](https://gitlab.com/DevDef), [Crowdin](https://crowdin.com/profile/DevDef) - German + - Michael Hahnle - [GitLab](https://gitlab.com/mhah), [Crowdin](https://crowdin.com/profile/mhah) - Indonesian - Ahmad Naufal Mukhtar - [GitLab](https://gitlab.com/anaufalm), [Crowdin](https://crowdin.com/profile/anaufalm) - Italian diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index e1d8345f415..783081cec26 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -30,12 +30,12 @@ to learn more. ## Delete merged branches -> [Introduced][ce-6449] in GitLab 8.14. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449) in GitLab 8.14. ![Delete merged branches](img/delete_merged_branches.png) This feature allows merged branches to be deleted in bulk. Only branches that -have been merged and [are not protected][protected] will be deleted as part of +have been merged and [are not protected](../../protected_branches.md) will be deleted as part of this operation. It's particularly useful to clean up old branches that were not deleted @@ -44,7 +44,7 @@ automatically when a merge request was merged. ## Branch filter search box -> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166] in GitLab 11.5. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166) in GitLab 11.5. ![Branch filter search box](img/branch_filter_search_box.png) @@ -57,6 +57,3 @@ Sometimes when you have hundreds of branches you may want a more flexible matchi - `^feature` will only match branch names that begin with 'feature'. - `feature$` will only match branch names that end with 'feature'. - -[ce-6449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449 "Add button to delete all merged branches" -[protected]: ../../protected_branches.md diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index 6fc86925f81..5d9ecd651a0 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -286,7 +286,7 @@ module Gitlab end def patch_name_from_branch(branch_name) - branch_name.parameterize << '.patch' + "#{branch_name.parameterize}.patch" end def patch_url @@ -434,9 +434,11 @@ module Gitlab end def conflicting_files_msg - failed_files.reduce("The conflicts detected were as follows:\n") do |memo, file| - memo << "\n - #{file}" - end + header = "The conflicts detected were as follows:\n" + separator = "\n - " + failed_items = failed_files.join(separator) + + "#{header}#{separator}#{failed_items}" end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 40d45d0dee9..26270595c6a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3599,6 +3599,9 @@ msgstr "" msgid "List available repositories" msgstr "" +msgid "List view" +msgstr "" + msgid "List your Bitbucket Server repositories" msgstr "" @@ -6519,6 +6522,9 @@ msgstr "" msgid "Track time with quick actions" msgstr "" +msgid "Tree view" +msgstr "" + msgid "Trending" msgstr "" diff --git a/package.json b/package.json index 5b0a92ee7a1..086617dc265 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@babel/plugin-syntax-dynamic-import": "^7.0.0", "@babel/plugin-syntax-import-meta": "^7.0.0", "@babel/preset-env": "^7.1.0", - "@gitlab-org/gitlab-svgs": "^1.32.0", + "@gitlab-org/gitlab-svgs": "^1.33.0", "@gitlab-org/gitlab-ui": "^1.8.0", "autosize": "^4.0.0", "axios": "^0.17.1", diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 78293464265..d372bcbdab1 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -47,15 +47,23 @@ function create_secret() { --dry-run -o json | kubectl apply -f - } +function deployExists() { + local namespace="${1}" + local deploy="${2}" + helm status --tiller-namespace "${namespace}" "${deploy}" >/dev/null 2>&1 + return $? +} + function previousDeployFailed() { set +e - echo "Checking for previous deployment of $CI_ENVIRONMENT_SLUG" - deployment_status=$(helm status $CI_ENVIRONMENT_SLUG >/dev/null 2>&1) + deploy="${1}" + echo "Checking for previous deployment of ${deploy}" + deployment_status=$(helm status ${deploy} >/dev/null 2>&1) status=$? # if `status` is `0`, deployment exists, has a status if [ $status -eq 0 ]; then echo "Previous deployment found, checking status" - deployment_status=$(helm status $CI_ENVIRONMENT_SLUG | grep ^STATUS | cut -d' ' -f2) + deployment_status=$(helm status ${deploy} | grep ^STATUS | cut -d' ' -f2) echo "Previous deployment state: $deployment_status" if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then status=0; @@ -113,7 +121,7 @@ function deploy() { fi # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade` - if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previousDeployFailed ; then + if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previousDeployFailed "$CI_ENVIRONMENT_SLUG" ; then echo "Deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG" delete cleanup @@ -149,6 +157,7 @@ HELM_CMD=$(cat << EOF --set gitlab.gitlab-shell.image.tag="v$GITLAB_SHELL_VERSION" \ --set gitlab.unicorn.workhorse.image="$gitlab_workhorse_image_repository" \ --set gitlab.unicorn.workhorse.tag="$CI_COMMIT_REF_NAME" \ + --set nginx-ingress.controller.config.ssl-ciphers="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4" \ --namespace="$KUBE_NAMESPACE" \ --version="$CI_PIPELINE_ID-$CI_JOB_ID" \ "$name" \ @@ -182,3 +191,23 @@ function cleanup() { | xargs kubectl -n "$KUBE_NAMESPACE" delete \ || true } + +function install_external_dns() { + local release_name="dns-gitlab-review-app" + local domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}') + + if ! deployExists "${KUBE_NAMESPACE}" "${release_name}" || previousDeployFailed "${release_name}" ; then + echo "Installing external-dns helm chart" + helm repo update + helm install stable/external-dns \ + -n "${release_name}" \ + --namespace "${KUBE_NAMESPACE}" \ + --set provider="aws" \ + --set aws.secretKey="${REVIEW_APPS_AWS_SECRET_KEY}" \ + --set aws.accessKey="${REVIEW_APPS_AWS_ACCESS_KEY}" \ + --set aws.zoneType="public" \ + --set domainFilters[0]="${domain}" \ + --set txtOwnerId="${KUBE_NAMESPACE}" \ + --set rbac.create="true" + fi +} diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 1ea8a640e17..c3902ecdd17 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -151,9 +151,8 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end it 'renders escaped tooltip name' do - page.within('aside.right-sidebar') do - expect(find('.active.build-job a')['data-original-title']).to eq('<img src=x onerror=alert(document.domain)> - passed') - end + page.find('.active.build-job a').hover + expect(page).to have_content('<img src=x onerror=alert(document.domain)> - passed') end end diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js index 08e25d2004e..fc94d0bab5b 100644 --- a/spec/javascripts/diffs/components/tree_list_spec.js +++ b/spec/javascripts/diffs/components/tree_list_spec.js @@ -53,7 +53,7 @@ describe('Diffs tree list component', () => { fileHash: 'test', key: 'index.js', name: 'index.js', - path: 'index.js', + path: 'app/index.js', removedLines: 0, tempFile: true, type: 'blob', @@ -104,7 +104,55 @@ describe('Diffs tree list component', () => { vm.$el.querySelector('.file-row').click(); - expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'index.js'); + expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'app/index.js'); + }); + + it('renders as file list when renderTreeList is false', done => { + vm.renderTreeList = false; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.file-row').length).toBe(1); + + done(); + }); + }); + + it('renders file paths when renderTreeList is false', done => { + vm.renderTreeList = false; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.file-row').textContent).toContain('app/index.js'); + + done(); + }); + }); + + it('hides render buttons when input is focused', done => { + const focusEvent = new Event('focus'); + + vm.$el.querySelector('.form-control').dispatchEvent(focusEvent); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.tree-list-view-toggle').style.display).toBe('none'); + + done(); + }); + }); + + it('shows render buttons when input is blurred', done => { + const blurEvent = new Event('blur'); + vm.focusSearch = true; + + vm.$nextTick() + .then(() => { + vm.$el.querySelector('.form-control').dispatchEvent(blurEvent); + }) + .then(vm.$nextTick) + .then(() => { + expect(vm.$el.querySelector('.tree-list-view-toggle').style.display).not.toBe('none'); + }) + .then(done) + .catch(done.fail); }); }); @@ -117,4 +165,24 @@ describe('Diffs tree list component', () => { expect(vm.search).toBe(''); }); }); + + describe('toggleRenderTreeList', () => { + it('updates renderTreeList', () => { + expect(vm.renderTreeList).toBe(true); + + vm.toggleRenderTreeList(false); + + expect(vm.renderTreeList).toBe(false); + }); + }); + + describe('toggleFocusSearch', () => { + it('updates focusSearch', () => { + expect(vm.focusSearch).toBe(false); + + vm.toggleFocusSearch(true); + + expect(vm.focusSearch).toBe(true); + }); + }); }); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index ef367fc09fa..f49dee3696d 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -445,6 +445,14 @@ describe('DiffsStoreUtils', () => { fileHash: 'test', }, { + newPath: 'app/test/filepathneedstruncating.js', + deletedFile: false, + newFile: true, + removedLines: 0, + addedLines: 0, + fileHash: 'test', + }, + { newPath: 'package.json', deletedFile: true, newFile: false, @@ -498,6 +506,19 @@ describe('DiffsStoreUtils', () => { type: 'blob', tree: [], }, + { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'app/test/filepathneedstruncating.js', + name: 'filepathneedstruncating.js', + path: 'app/test/filepathneedstruncating.js', + removedLines: 0, + tempFile: true, + type: 'blob', + tree: [], + }, ], }, ], @@ -527,6 +548,7 @@ describe('DiffsStoreUtils', () => { 'app/index.js', 'app/test', 'app/test/index.js', + 'app/test/filepathneedstruncating.js', 'package.json', ]); }); diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js index d7338ee0f66..aecab331ead 100644 --- a/spec/javascripts/flash_spec.js +++ b/spec/javascripts/flash_spec.js @@ -172,7 +172,7 @@ describe('Flash', () => { flash('test'); expect(document.querySelector('.flash-text').className).toBe( - 'flash-text container-fluid container-limited', + 'flash-text container-fluid container-limited limit-container-width', ); }); @@ -180,7 +180,7 @@ describe('Flash', () => { document.querySelector('.content-wrapper').className = 'js-content-wrapper'; flash('test'); - expect(document.querySelector('.flash-text').className.trim()).toBe('flash-text'); + expect(document.querySelector('.flash-text').className.trim()).toContain('flash-text'); }); it('removes element after clicking', () => { diff --git a/spec/javascripts/vue_shared/components/file_row_spec.js b/spec/javascripts/vue_shared/components/file_row_spec.js index 9914c0b70f3..67752c1c455 100644 --- a/spec/javascripts/vue_shared/components/file_row_spec.js +++ b/spec/javascripts/vue_shared/components/file_row_spec.js @@ -71,4 +71,40 @@ describe('RepoFile', () => { expect(vm.$el.querySelector('.file-row-name').style.marginLeft).toBe('32px'); }); + + describe('outputText', () => { + beforeEach(done => { + createComponent({ + file: { + ...file(), + path: 'app/assets/index.js', + }, + level: 0, + }); + + vm.displayTextKey = 'path'; + + vm.$nextTick(done); + }); + + it('returns text if truncateStart is 0', done => { + vm.truncateStart = 0; + + vm.$nextTick(() => { + expect(vm.outputText).toBe('app/assets/index.js'); + + done(); + }); + }); + + it('returns text truncated at start', done => { + vm.truncateStart = 5; + + vm.$nextTick(() => { + expect(vm.outputText).toBe('...ssets/index.js'); + + done(); + }); + }); + }); }); diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 23643d1c4d2..d5fb1a9d010 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -17,7 +17,7 @@ describe Clusters::Applications::Runner do let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') } it 'updates the application version' do - expect(application.reload.version).to eq('0.1.34') + expect(application.reload.version).to eq('0.1.35') end end end @@ -45,7 +45,7 @@ describe Clusters::Applications::Runner do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('runner') expect(subject.chart).to eq('runner/gitlab-runner') - expect(subject.version).to eq('0.1.34') + expect(subject.version).to eq('0.1.35') expect(subject).not_to be_rbac expect(subject.repository).to eq('https://charts.gitlab.io') expect(subject.files).to eq(gitlab_runner.files) @@ -63,7 +63,7 @@ describe Clusters::Applications::Runner do let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') } it 'should be initialized with the locked version' do - expect(subject.version).to eq('0.1.34') + expect(subject.version).to eq('0.1.35') end end end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 71287f28171..71d72ff27e9 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -168,6 +168,8 @@ module TestEnv return end + FileUtils.mkdir_p("tmp/tests/second_storage") unless File.exist?("tmp/tests/second_storage") + spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s Bundler.with_original_env do raise "gitaly spawn failed" unless system(spawn_script) diff --git a/yarn.lock b/yarn.lock index 544bd4a05bd..5da401c1d43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -616,11 +616,16 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.32.0": +"@gitlab-org/gitlab-svgs@^1.23.0": version "1.32.0" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.32.0.tgz#a65ab7724fa7d55be8e5cc9b2dbe3f0757432fd3" integrity sha512-L3o8dFUd2nSkVZBwh2hCJWzNzADJ3dTBZxamND8NLosZK9/ohNhccmsQOZGyMCUHaOzm4vifaaXkAXh04UtMKA== +"@gitlab-org/gitlab-svgs@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.33.0.tgz#068566e8ee00795f6f09f58236f08e1716f9f04a" + integrity sha512-8ajtUHk6gQ1xosL/CO5IzHSFM/t18hx5pfzQ3cd0VuQXcyR6QKGuXTLwbYdmJDYOw1Etoo5DqDWxPEClHyZpiA== + "@gitlab-org/gitlab-ui@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.8.0.tgz#dee33d78f68c91644273dbd51734b796108263ee" |