diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-22 18:09:56 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-22 18:09:56 +0300 |
commit | 5f5f492fe278f3322e9533b617522321e2ccafcc (patch) | |
tree | 2f9f65c206d555d44034d386f4a03e0835059b04 | |
parent | 4b074c5f634f8e1e550107f9e8237f07878ca0e8 (diff) |
Add latest changes from gitlab-org/gitlab@master
65 files changed, 674 insertions, 167 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 781f69d811b..4933792c6cb 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -a7bc2f86b507daaaf9f18e0ea189b062d6149720 +c2c12e3152bfc6c899b5ad08974659ac7b450232 diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue index f16a547e441..86c7b4c7a6e 100644 --- a/app/assets/javascripts/badges/components/badge_list.vue +++ b/app/assets/javascripts/badges/components/badge_list.vue @@ -1,5 +1,5 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlLoadingIcon, GlBadge } from '@gitlab/ui'; import { mapState } from 'vuex'; import { GROUP_BADGE } from '../constants'; import BadgeListRow from './badge_list_row.vue'; @@ -9,6 +9,7 @@ export default { components: { BadgeListRow, GlLoadingIcon, + GlBadge, }, computed: { ...mapState(['badges', 'isLoading', 'kind']), @@ -26,7 +27,7 @@ export default { <div class="card"> <div class="card-header"> {{ s__('Badges|Your badges') }} - <span v-show="!isLoading" class="badge badge-pill">{{ badges.length }}</span> + <gl-badge v-show="!isLoading" size="sm">{{ badges.length }}</gl-badge> </div> <gl-loading-icon v-show="isLoading" size="lg" class="card-body" /> <div v-if="hasNoBadges" class="card-body"> diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue index af7c391ab70..7bc1eb5d652 100644 --- a/app/assets/javascripts/deploy_keys/components/action_btn.vue +++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue @@ -1,10 +1,10 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; import eventHub from '../eventhub'; export default { components: { - GlLoadingIcon, + GlButton, }, props: { deployKey: { @@ -15,10 +15,20 @@ export default { type: String, required: true, }, - btnCssClass: { + category: { type: String, required: false, - default: 'btn-default', + default: 'tertiary', + }, + variant: { + type: String, + required: false, + default: 'default', + }, + icon: { + type: String, + required: false, + default: '', }, }, data() { @@ -39,13 +49,14 @@ export default { </script> <template> - <button - :class="[{ disabled: isLoading }, btnCssClass]" - :disabled="isLoading" + <gl-button + :category="category" + :variant="variant" + :icon="icon" + :loading="isLoading" class="btn" @click="doAction" > <slot></slot> - <gl-loading-icon v-if="isLoading" :inline="true" /> - </button> + </gl-button> </template> diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index 425cca13ae8..5ea29a7503b 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -6,10 +6,12 @@ import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue'; import eventHub from '../eventhub'; import DeployKeysService from '../service'; import DeployKeysStore from '../store'; +import ConfirmModal from './confirm_modal.vue'; import KeysPanel from './keys_panel.vue'; export default { components: { + ConfirmModal, KeysPanel, NavigationTabs, GlLoadingIcon, @@ -30,6 +32,9 @@ export default { currentTab: 'enabled_keys', isLoading: false, store: new DeployKeysStore(), + removeKey: () => {}, + cancel: () => {}, + confirmModalVisible: false, }; }, scopes: { @@ -61,16 +66,16 @@ export default { this.service = new DeployKeysService(this.endpoint); eventHub.$on('enable.key', this.enableKey); - eventHub.$on('remove.key', this.disableKey); - eventHub.$on('disable.key', this.disableKey); + eventHub.$on('remove.key', this.confirmRemoveKey); + eventHub.$on('disable.key', this.confirmRemoveKey); }, mounted() { this.fetchKeys(); }, beforeDestroy() { eventHub.$off('enable.key', this.enableKey); - eventHub.$off('remove.key', this.disableKey); - eventHub.$off('disable.key', this.disableKey); + eventHub.$off('remove.key', this.confirmRemoveKey); + eventHub.$off('disable.key', this.confirmRemoveKey); }, methods: { onChangeTab(tab) { @@ -97,19 +102,20 @@ export default { .then(this.fetchKeys) .catch(() => new Flash(s__('DeployKeys|Error enabling deploy key'))); }, - disableKey(deployKey, callback) { - if ( - // eslint-disable-next-line no-alert - window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?')) - ) { + confirmRemoveKey(deployKey, callback) { + const hideModal = () => { + this.confirmModalVisible = false; + callback?.(); + }; + this.removeKey = () => { this.service .disableKey(deployKey.id) .then(this.fetchKeys) - .then(callback) + .then(hideModal) .catch(() => new Flash(s__('DeployKeys|Error removing deploy key'))); - } else { - callback(); - } + }; + this.cancel = hideModal; + this.confirmModalVisible = true; }, }, }; @@ -117,6 +123,7 @@ export default { <template> <div class="gl-mb-3 deploy-keys"> + <confirm-modal :visible="confirmModalVisible" @remove="removeKey" @cancel="cancel" /> <gl-loading-icon v-if="isLoading && !hasKeys" :label="s__('DeployKeys|Loading deploy keys')" @@ -124,8 +131,12 @@ export default { /> <template v-else-if="hasKeys"> <div class="top-area scrolling-tabs-container inner-page-scroll-tabs"> - <div class="fade-left"><gl-icon name="chevron-lg-left" :size="12" /></div> - <div class="fade-right"><gl-icon name="chevron-lg-right" :size="12" /></div> + <div class="fade-left"> + <gl-icon name="chevron-lg-left" :size="12" /> + </div> + <div class="fade-right"> + <gl-icon name="chevron-lg-right" :size="12" /> + </div> <navigation-tabs :tabs="tabs" scope="deployKeys" @onChangeTab="onChangeTab" /> </div> diff --git a/app/assets/javascripts/deploy_keys/components/confirm_modal.vue b/app/assets/javascripts/deploy_keys/components/confirm_modal.vue new file mode 100644 index 00000000000..1932435c42a --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/confirm_modal.vue @@ -0,0 +1,46 @@ +<script> +import { GlModal } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { + GlModal, + }, + props: { + visible: { + type: Boolean, + required: false, + default: false, + }, + }, + i18n: { + body: __( + 'Are you sure you want to remove this deploy key? If anything is still using this key, it will stop working.', + ), + }, + modalOptions: { + title: __('Do you want to remove this deploy key?'), + actionPrimary: { + text: __('Remove deploy key'), + attributes: [{ variant: 'danger' }], + }, + actionSecondary: { + text: __('Cancel'), + attributes: [{ category: 'tertiary' }], + }, + static: true, + modalId: 'confirm-remove-deploy-key', + }, +}; +</script> +<template> + <gl-modal + v-bind="$options.modalOptions" + :visible="visible" + @primary="$emit('remove')" + @secondary="$emit('cancel')" + @hidden="$emit('cancel')" + > + {{ $options.i18n.body }} + </gl-modal> +</template> diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue index e70ca18bb71..fb55e86df1f 100644 --- a/app/assets/javascripts/deploy_keys/components/key.vue +++ b/app/assets/javascripts/deploy_keys/components/key.vue @@ -1,5 +1,5 @@ <script> -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlIcon, GlLink, GlTooltipDirective, GlButton } from '@gitlab/ui'; import { head, tail } from 'lodash'; import { s__, sprintf } from '~/locale'; import timeagoMixin from '~/vue_shared/mixins/timeago'; @@ -9,7 +9,9 @@ import actionBtn from './action_btn.vue'; export default { components: { actionBtn, + GlButton, GlIcon, + GlLink, }, directives: { GlTooltip: GlTooltipDirective, @@ -123,15 +125,15 @@ export default { <div role="rowheader" class="table-mobile-header">{{ s__('DeployKeys|Project usage') }}</div> <div class="table-mobile-content deploy-project-list"> <template v-if="projects.length > 0"> - <a + <gl-link v-gl-tooltip :title="projectTooltipTitle(firstProject)" class="label deploy-project-label" > <span> {{ firstProject.project.full_name }} </span> <gl-icon :name="firstProject.can_push ? 'lock-open' : 'lock'" /> - </a> - <a + </gl-link> + <gl-link v-if="isExpandable" v-gl-tooltip :title="restProjectsTooltip" @@ -139,8 +141,8 @@ export default { @click="toggleExpanded" > <span>{{ restProjectsLabel }}</span> - </a> - <a + </gl-link> + <gl-link v-for="deployKeysProject in restProjects" v-else-if="isExpanded" :key="deployKeysProject.project.full_path" @@ -151,7 +153,7 @@ export default { > <span> {{ deployKeysProject.project.full_name }} </span> <gl-icon :name="deployKeysProject.can_push ? 'lock-open' : 'lock'" /> - </a> + </gl-link> </template> <span v-else class="text-secondary">{{ __('None') }}</span> </div> @@ -166,41 +168,43 @@ export default { </div> <div class="table-section section-15 table-button-footer deploy-key-actions"> <div class="btn-group table-action-buttons"> - <action-btn v-if="!isEnabled" :deploy-key="deployKey" type="enable"> + <action-btn v-if="!isEnabled" :deploy-key="deployKey" type="enable" category="secondary"> {{ __('Enable') }} </action-btn> - <a + <gl-button v-if="deployKey.can_edit" v-gl-tooltip :href="editDeployKeyPath" :title="__('Edit')" - class="btn btn-default text-secondary" + :aria-label="__('Edit')" data-container="body" - > - <gl-icon name="pencil" /> - </a> + icon="pencil" + category="secondary" + /> <action-btn v-if="isRemovable" v-gl-tooltip :deploy-key="deployKey" :title="__('Remove')" - btn-css-class="btn-danger" + :aria-label="__('Remove')" + category="primary" + variant="danger" + icon="remove" type="remove" data-container="body" - > - <gl-icon name="remove" /> - </action-btn> + /> <action-btn v-else-if="isEnabled" v-gl-tooltip :deploy-key="deployKey" :title="__('Disable')" - btn-css-class="btn-warning" + :aria-label="__('Disable')" type="disable" data-container="body" - > - <gl-icon name="cancel" /> - </action-btn> + icon="cancel" + category="primary" + variant="danger" + /> </div> </div> </div> diff --git a/app/assets/javascripts/ensure_data.js b/app/assets/javascripts/ensure_data.js index 5b4d1afc9d0..69c81c35bd4 100644 --- a/app/assets/javascripts/ensure_data.js +++ b/app/assets/javascripts/ensure_data.js @@ -3,8 +3,8 @@ import { GlEmptyState } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; import { __ } from '~/locale'; -const ERROR_FETCHING_DATA_HEADER = __('Could not get the data properly'); -const ERROR_FETCHING_DATA_DESCRIPTION = __( +export const ERROR_FETCHING_DATA_HEADER = __('Could not get the data properly'); +export const ERROR_FETCHING_DATA_DESCRIPTION = __( 'Please try and refresh the page. If the problem persists please contact support.', ); diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index c720476f3bf..1630f0d689c 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -18,11 +18,21 @@ export const fetchPolicies = { }; export default (resolvers = {}, config = {}) => { - let uri = `${gon.relative_url_root || ''}/api/graphql`; + const { + assumeImmutableResults, + baseUrl, + batchMax = 10, + cacheConfig, + fetchPolicy = fetchPolicies.CACHE_FIRST, + typeDefs, + path = '/api/graphql', + useGet = false, + } = config; + let uri = `${gon.relative_url_root || ''}${path}`; - if (config.baseUrl) { + if (baseUrl) { // Prepend baseUrl and ensure that `///` are replaced with `/` - uri = `${config.baseUrl}${uri}`.replace(/\/{3,}/g, '/'); + uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/'); } const httpOptions = { @@ -34,7 +44,7 @@ export default (resolvers = {}, config = {}) => { // We set to `same-origin` which is default value in modern browsers. // See https://github.com/whatwg/fetch/pull/585 for more information. credentials: 'same-origin', - batchMax: config.batchMax || 10, + batchMax, }; const requestCounterLink = new ApolloLink((operation, forward) => { @@ -50,7 +60,7 @@ export default (resolvers = {}, config = {}) => { const uploadsLink = ApolloLink.split( (operation) => operation.getContext().hasUpload || operation.getContext().isSingleRequest, createUploadLink(httpOptions), - config.useGet ? createHttpLink(httpOptions) : new BatchHttpLink(httpOptions), + useGet ? createHttpLink(httpOptions) : new BatchHttpLink(httpOptions), ); const performanceBarLink = new ApolloLink((operation, forward) => { @@ -74,7 +84,7 @@ export default (resolvers = {}, config = {}) => { }); return new ApolloClient({ - typeDefs: config.typeDefs, + typeDefs, link: ApolloLink.from([ requestCounterLink, performanceBarLink, @@ -83,14 +93,14 @@ export default (resolvers = {}, config = {}) => { uploadsLink, ]), cache: new InMemoryCache({ - ...config.cacheConfig, - freezeResults: config.assumeImmutableResults, + ...cacheConfig, + freezeResults: assumeImmutableResults, }), resolvers, - assumeImmutableResults: config.assumeImmutableResults, + assumeImmutableResults, defaultOptions: { query: { - fetchPolicy: config.fetchPolicy || fetchPolicies.CACHE_FIRST, + fetchPolicy, }, }, }); diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue index c9ac9da0501..09ef98ec411 100644 --- a/app/assets/javascripts/members/components/table/members_table.vue +++ b/app/assets/javascripts/members/components/table/members_table.vue @@ -134,6 +134,9 @@ export default { show-empty :tbody-tr-attr="tbodyTrAttr" > + <template #head()="{ label }"> + {{ label }} + </template> <template #cell(account)="{ item: member }"> <members-table-cell #default="{ memberType, isCurrentUser }" :member="member"> <member-avatar diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue new file mode 100644 index 00000000000..73cd8742a27 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue @@ -0,0 +1,64 @@ +<script> +import { + GlDropdown, + GlDropdownItem, + GlDropdownSectionHeader, + GlSprintf, + GlTooltipDirective, +} from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + i18n: { + artifacts: __('Artifacts'), + downloadArtifact: __('Download %{name} artifact'), + artifactSectionHeader: __('Download artifacts'), + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + components: { + GlDropdown, + GlDropdownItem, + GlDropdownSectionHeader, + GlSprintf, + }, + props: { + artifacts: { + type: Array, + required: true, + }, + }, +}; +</script> +<template> + <gl-dropdown + v-gl-tooltip + :title="$options.i18n.artifacts" + :text="$options.i18n.artifacts" + :aria-label="$options.i18n.artifacts" + icon="ellipsis_v" + data-testid="pipeline-multi-actions-dropdown" + right + lazy + text-sr-only + no-caret + > + <gl-dropdown-section-header>{{ + $options.i18n.artifactSectionHeader + }}</gl-dropdown-section-header> + + <gl-dropdown-item + v-for="(artifact, i) in artifacts" + :key="i" + :href="artifact.path" + rel="nofollow" + download + data-testid="artifact-item" + > + <gl-sprintf :message="$options.i18n.downloadArtifact"> + <template #name>{{ artifact.name }}</template> + </gl-sprintf> + </gl-dropdown-item> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue index 81eeead2171..a0f8e5272cf 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue @@ -2,7 +2,7 @@ import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui'; import { __ } from '~/locale'; import eventHub from '../../event_hub'; -import PipelinesArtifactsComponent from './pipelines_artifacts.vue'; +import PipelineMultiActions from './pipeline_multi_actions.vue'; import PipelinesManualActions from './pipelines_manual_actions.vue'; export default { @@ -16,8 +16,8 @@ export default { }, components: { GlButton, + PipelineMultiActions, PipelinesManualActions, - PipelinesArtifactsComponent, }, props: { pipeline: { @@ -80,11 +80,6 @@ export default { <div class="btn-group"> <pipelines-manual-actions v-if="actions.length > 0" :actions="actions" /> - <pipelines-artifacts-component - v-if="pipeline.details.artifacts.length" - :artifacts="pipeline.details.artifacts" - /> - <gl-button v-if="pipeline.flags.retryable" v-gl-tooltip.hover @@ -114,6 +109,11 @@ export default { class="js-pipelines-cancel-button" @click="handleCancelClick" /> + + <pipeline-multi-actions + v-if="pipeline.details.artifacts.length" + :artifacts="pipeline.details.artifacts" + /> </div> </div> </template> diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 14fc2cd5a00..1df7b9ed165 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -20,7 +20,7 @@ class Projects::BlameController < Projects::ApplicationController environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true - @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last + @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last @blame = Gitlab::Blame.new(@blob, @commit) @blame = Gitlab::View::Presenter::Factory.new(@blame, project: @project, path: @path).fabricate! diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b9b0c2b802c..2b2b4bfb65a 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -214,7 +214,7 @@ class Projects::BlobController < Projects::ApplicationController def show_html environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true - @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last + @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last @last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true) @code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index be1bd37e341..863715429ff 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -167,7 +167,7 @@ class Projects::CommitController < Projects::ApplicationController @diffs = commit.diffs(opts) @notes_count = commit.notes.count - @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last + @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 0db9940529a..28a87f83451 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -136,7 +136,7 @@ class Projects::CompareController < Projects::ApplicationController if compare environment_params = source_project.repository.branch_exists?(head_ref) ? { ref: head_ref } : { commit: compare.commit } environment_params[:find_latest] = true - @environment = Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last + @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last end end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index 94a256fed3d..a3b8a132427 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -65,6 +65,12 @@ module Types description: 'Indicates the job is active.' field :coverage, GraphQL::FLOAT_TYPE, null: true, description: 'Coverage level of the job.' + field :created_by_tag, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Whether the job was created by a tag.' + field :manual_job, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Whether the job has a manual action.' + field :triggered, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Whether the job was triggered.' def pipeline Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find @@ -123,6 +129,18 @@ module Types def coverage object&.coverage end + + def created_by_tag + object.tag? + end + + def manual_job + object.try(:action?) + end + + def triggered + object.try(:trigger_request) + end end end end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 09f91f350bd..33698897083 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -98,6 +98,14 @@ module AvatarsHelper end end + def avatar_without_link(resource, options = {}) + if resource.is_a?(User) + user_avatar_without_link(options.merge(user: resource)) + elsif resource.is_a?(Group) + group_icon(resource, options.merge(class: 'avatar')) + end + end + private def avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path:) @@ -136,9 +144,10 @@ module AvatarsHelper def source_identicon(source, options = {}) bg_key = (source.id % 7) + 1 + size_class = "s#{options[:size]}" if options[:size] options[:class] = - [*options[:class], "identicon bg#{bg_key}"].join(' ') + [*options[:class], "identicon bg#{bg_key}", size_class].compact.join(' ') content_tag(:div, class: options[:class].strip) do source.name[0, 1].upcase diff --git a/app/models/service.rb b/app/models/service.rb index 9867081ce1b..7782b016b52 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -416,6 +416,14 @@ class Service < ApplicationRecord project_id.present? end + def group_level? + group_id.present? + end + + def instance_level? + instance? + end + def parent project || group end diff --git a/changelogs/unreleased/300874-follow-up-from-trying-to-open-ci-cd-settings-on-gitlab-project-fai.yml b/changelogs/unreleased/300874-follow-up-from-trying-to-open-ci-cd-settings-on-gitlab-project-fai.yml new file mode 100644 index 00000000000..07c15343d09 --- /dev/null +++ b/changelogs/unreleased/300874-follow-up-from-trying-to-open-ci-cd-settings-on-gitlab-project-fai.yml @@ -0,0 +1,5 @@ +--- +title: Add generic avatar method for users and groups +merge_request: 59758 +author: +type: fixed diff --git a/changelogs/unreleased/326079-add-group-level-and-instance-level-helpers-to-service-model.yml b/changelogs/unreleased/326079-add-group-level-and-instance-level-helpers-to-service-model.yml new file mode 100644 index 00000000000..f5202dd33c4 --- /dev/null +++ b/changelogs/unreleased/326079-add-group-level-and-instance-level-helpers-to-service-model.yml @@ -0,0 +1,5 @@ +--- +title: Add group_level? and instance_level? helpers to Service model +merge_request: 59838 +author: Amit Patel @amit.savani +type: other diff --git a/changelogs/unreleased/afontaine-alert-users-when-deleting-deploy-keys.yml b/changelogs/unreleased/afontaine-alert-users-when-deleting-deploy-keys.yml new file mode 100644 index 00000000000..c02a4e88348 --- /dev/null +++ b/changelogs/unreleased/afontaine-alert-users-when-deleting-deploy-keys.yml @@ -0,0 +1,5 @@ +--- +title: Use GlModal for Confirmation of Deploy Key Delete +merge_request: 59697 +author: +type: changed diff --git a/changelogs/unreleased/dz-api-filter-projects-by-topic.yml b/changelogs/unreleased/dz-api-filter-projects-by-topic.yml new file mode 100644 index 00000000000..6001563c04c --- /dev/null +++ b/changelogs/unreleased/dz-api-filter-projects-by-topic.yml @@ -0,0 +1,5 @@ +--- +title: Search projects by topic via API +merge_request: 59900 +author: +type: added diff --git a/changelogs/unreleased/gitlab-ui-integration-883-upgrade-bootstrap-vue-2-15-0.yml b/changelogs/unreleased/gitlab-ui-integration-883-upgrade-bootstrap-vue-2-15-0.yml new file mode 100644 index 00000000000..0278a78c78b --- /dev/null +++ b/changelogs/unreleased/gitlab-ui-integration-883-upgrade-bootstrap-vue-2-15-0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Bootstrap to v4.5.3 +merge_request: 59501 +author: +type: other diff --git a/changelogs/unreleased/gl-badge-badges.yml b/changelogs/unreleased/gl-badge-badges.yml new file mode 100644 index 00000000000..1a437fd3e8d --- /dev/null +++ b/changelogs/unreleased/gl-badge-badges.yml @@ -0,0 +1,5 @@ +--- +title: Move badge to vue component in project badges +merge_request: 58045 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/pb-add-missing-data-to-ci-job-type.yml b/changelogs/unreleased/pb-add-missing-data-to-ci-job-type.yml new file mode 100644 index 00000000000..c5bd7a7210e --- /dev/null +++ b/changelogs/unreleased/pb-add-missing-data-to-ci-job-type.yml @@ -0,0 +1,5 @@ +--- +title: Add missing data to CiJob type +merge_request: 59805 +author: +type: added diff --git a/changelogs/unreleased/pb-move-artifacts-to-ellipsis-dropdown.yml b/changelogs/unreleased/pb-move-artifacts-to-ellipsis-dropdown.yml new file mode 100644 index 00000000000..7ef3617ad85 --- /dev/null +++ b/changelogs/unreleased/pb-move-artifacts-to-ellipsis-dropdown.yml @@ -0,0 +1,5 @@ +--- +title: Change artifacts download button to a vertical ellipsis menu +merge_request: 59667 +author: +type: changed diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md index 22cd6ca097c..693c22f925c 100644 --- a/doc/administration/incoming_email.md +++ b/doc/administration/incoming_email.md @@ -4,7 +4,7 @@ group: Project Management info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Incoming email +# Incoming email **(FREE SELF)** GitLab has several features based on receiving incoming emails: diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 1cb82fbb0d8..0fd45a3c37c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -7075,10 +7075,12 @@ Represents the total number of issues and their weights for a particular day. | <a id="cijobcommitpath"></a>`commitPath` | [`String`](#string) | Path to the commit that triggered the job. | | <a id="cijobcoverage"></a>`coverage` | [`Float`](#float) | Coverage level of the job. | | <a id="cijobcreatedat"></a>`createdAt` | [`Time!`](#time) | When the job was created. | +| <a id="cijobcreatedbytag"></a>`createdByTag` | [`Boolean!`](#boolean) | Whether the job was created by a tag. | | <a id="cijobdetailedstatus"></a>`detailedStatus` | [`DetailedStatus`](#detailedstatus) | Detailed status of the job. | | <a id="cijobduration"></a>`duration` | [`Int`](#int) | Duration of the job in seconds. | | <a id="cijobfinishedat"></a>`finishedAt` | [`Time`](#time) | When a job has finished running. | | <a id="cijobid"></a>`id` | [`JobID`](#jobid) | ID of the job. | +| <a id="cijobmanualjob"></a>`manualJob` | [`Boolean`](#boolean) | Whether the job has a manual action. | | <a id="cijobname"></a>`name` | [`String`](#string) | Name of the job. | | <a id="cijobneeds"></a>`needs` | [`CiBuildNeedConnection`](#cibuildneedconnection) | References to builds that must complete before the jobs run. | | <a id="cijobpipeline"></a>`pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. | @@ -7094,6 +7096,7 @@ Represents the total number of issues and their weights for a particular day. | <a id="cijobstartedat"></a>`startedAt` | [`Time`](#time) | When the job was started. | | <a id="cijobstatus"></a>`status` | [`CiJobStatus`](#cijobstatus) | Status of the job. | | <a id="cijobtags"></a>`tags` | [`[String!]`](#string) | Tags for the current job. | +| <a id="cijobtriggered"></a>`triggered` | [`Boolean`](#boolean) | Whether the job was triggered. | ### `CiJobArtifact` diff --git a/doc/api/projects.md b/doc/api/projects.md index d9aabfbc337..2961cb725b3 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -59,6 +59,7 @@ GET /projects | `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. | | `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. | | `statistics` | boolean | **{dotted-circle}** No | Include project statistics. | +| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `tag_list` attribute. | | `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. | | `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). | | `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ | diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md index e87b4197269..8ce6db8b88a 100644 --- a/doc/development/fe_guide/graphql.md +++ b/doc/development/fe_guide/graphql.md @@ -118,6 +118,8 @@ To distinguish queries from mutations and fragments, the following naming conven - `add_user.mutation.graphql` for mutations; - `basic_user.fragment.graphql` for fragments. +If you are using queries for the [CustomersDot GraphQL endpoint](https://gitlab.com/gitlab-org/gitlab/-/blob/be78ccd832fd40315c5e63bb48ee1596ae146f56/app/controllers/customers_dot/proxy_controller.rb), end the filename with `.customer.query.graphql`, `.customer.mutation.graphql`, or `.customer.fragment.graphql`. + ### Fragments [Fragments](https://graphql.org/learn/queries/#fragments) are a way to make your complex GraphQL queries more readable and re-usable. Here is an example of GraphQL fragment: diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md index c07291d61f2..1ffc86b40d5 100644 --- a/doc/development/snowplow/index.md +++ b/doc/development/snowplow/index.md @@ -114,13 +114,13 @@ The current method provides several attributes that are sent on each click event | category* | label | action | property** | value | |-------------|------------------|-----------------------|----------|:-----:| -| [root:index] | main_navigation | click_navigation_link | `[link_label]` | - | -| [groups:boards:show] | toggle_swimlanes | click_toggle_button | - | `[is_active]` | -| [projects:registry:index] | registry_delete | click_button | - | - | -| [projects:registry:index] | registry_delete | confirm_deletion | - | - | -| [projects:blob:show] | congratulate_first_pipeline | click_button | `[human_access]` | - | -| [projects:clusters:new] | chart_options | generate_link | `[chart_link]` | - | -| [projects:clusters:new] | chart_options | click_add_label_button | `[label_id]` | - | +| `[root:index]` | `main_navigation` | `click_navigation_link` | `[link_label]` | - | +| `[groups:boards:show]` | `toggle_swimlanes` | `click_toggle_button` | - | `[is_active]` | +| `[projects:registry:index]` | `registry_delete` | `click_button` | - | - | +| `[projects:registry:index]` | `registry_delete` | `confirm_deletion` | - | - | +| `[projects:blob:show]` | `congratulate_first_pipeline` | `click_button` | `[human_access]` | - | +| `[projects:clusters:new]` | `chart_options` | `generate_link` | `[chart_link]` | - | +| `[projects:clusters:new]` | `chart_options` | `click_add_label_button` | `[label_id]` | - | _* It's ok to omit the category, and use the default._<br> _** Property is usually the best place for variable strings._ diff --git a/doc/development/usage_ping/index.md b/doc/development/usage_ping/index.md index bf423d68700..6a6a2cdd5d8 100644 --- a/doc/development/usage_ping/index.md +++ b/doc/development/usage_ping/index.md @@ -913,9 +913,9 @@ On GitLab.com, the Product Intelligence team regularly monitors Usage Ping. They To set up Usage Ping locally, you must: -1. [Set up local repositories]#(set-up-local-repositories) -1. [Test local setup](#test-local-setup) -1. (Optional) [Test Prometheus-based usage ping](#test-prometheus-based-usage-ping) +1. [Set up local repositories](#set-up-local-repositories). +1. [Test local setup](#test-local-setup). +1. (Optional) [Test Prometheus-based usage ping](#test-prometheus-based-usage-ping). #### Set up local repositories diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md index 276009ac200..0c7a87d3087 100644 --- a/doc/operations/incident_management/alerts.md +++ b/doc/operations/incident_management/alerts.md @@ -167,22 +167,19 @@ difficult to track who is investigating and working on it. Assigning alerts ease To assign an alert: -1. To display the list of current alerts, navigate to **Operations > Alerts**: - - ![Alert List View Assignee(s)](img/alert_list_assignees_v13_1.png) +1. To display the list of current alerts, navigate to **Operations > Alerts**. -1. Select your desired alert to display its **Alert Details View**: +1. Select your desired alert to display its details. ![Alert Details View Assignee(s)](img/alert_details_assignees_v13_1.png) 1. If the right sidebar is not expanded, select **{angle-double-right}** **Expand sidebar** to expand it. + 1. In the right sidebar, locate the **Assignee**, and then select **Edit**. From the dropdown menu, select each user you want to assign to the alert. GitLab creates a [to-do item](../../user/todos.md) for each user. - ![Alert Details View Assignee(s)](img/alert_todo_assignees_v13_1.png) - After completing their portion of investigating or fixing the alert, users can unassign themselves from the alert. To remove an assignee, select **Edit** next to the **Assignee** dropdown menu and deselect the user from the list of assignees, or select **Unassigned**. @@ -203,8 +200,6 @@ add a to-do item: Select the **To-Do List** **{todo-done}** in the navigation bar to view your current to-do list. -![Alert Details Added to do](img/alert_detail_added_todo_v13_1.png) - ## Link runbooks to alerts > Runbook URLs [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39315) in GitLab 13.3. diff --git a/doc/operations/incident_management/img/alert_detail_added_todo_v13_1.png b/doc/operations/incident_management/img/alert_detail_added_todo_v13_1.png Binary files differdeleted file mode 100644 index ae874706895..00000000000 --- a/doc/operations/incident_management/img/alert_detail_added_todo_v13_1.png +++ /dev/null diff --git a/doc/operations/incident_management/img/alert_details_assignees_v13_1.png b/doc/operations/incident_management/img/alert_details_assignees_v13_1.png Binary files differindex dab4eac384a..29cdba2c9ab 100644 --- a/doc/operations/incident_management/img/alert_details_assignees_v13_1.png +++ b/doc/operations/incident_management/img/alert_details_assignees_v13_1.png diff --git a/doc/operations/incident_management/img/alert_list_assignees_v13_1.png b/doc/operations/incident_management/img/alert_list_assignees_v13_1.png Binary files differdeleted file mode 100644 index db1e0d8dcb7..00000000000 --- a/doc/operations/incident_management/img/alert_list_assignees_v13_1.png +++ /dev/null diff --git a/doc/operations/incident_management/img/alert_todo_assignees_v13_1.png b/doc/operations/incident_management/img/alert_todo_assignees_v13_1.png Binary files differdeleted file mode 100644 index 637f8be5d25..00000000000 --- a/doc/operations/incident_management/img/alert_todo_assignees_v13_1.png +++ /dev/null diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 87213f72534..50743f7438e 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -207,7 +207,7 @@ To use SSH with GitLab, copy your public key to your GitLab account. 1. Sign in to GitLab. 1. In the top right corner, select your avatar. -1. Select **Settings**. +1. Select **Preferences**. 1. From the left sidebar, select **SSH Keys**. 1. In the **Key** box, paste the contents of your public key. If you manually copied the key, make sure you copy the entire key, diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md index 4d890e249e7..e64c7b4f3f0 100644 --- a/doc/user/profile/notifications.md +++ b/doc/user/profile/notifications.md @@ -43,7 +43,7 @@ You can tune the notifications you receive by combining your notification settin To edit your notification settings: -1. Click on your profile picture and select **Settings**. +1. Click on your profile picture and select **Preferences**. 1. Click **Notifications** in the left sidebar. 1. Edit the desired notification settings. Edited settings are automatically saved and enabled. @@ -85,7 +85,7 @@ You can select a notification level for each project to help you closely monitor To select a notification level for a project, use either of these methods: -1. Click on your profile picture and select **Settings**. +1. Click on your profile picture and select **Preferences**. 1. Click **Notifications** in the left sidebar. 1. Locate the project in the **Projects** section. 1. Select the desired [notification level](#notification-levels). @@ -109,7 +109,7 @@ You can select a notification level and email address for each group. To select a notification level for a group, use either of these methods: -1. Click on your profile picture and select **Settings**. +1. Click on your profile picture and select **Preferences**. 1. Click **Notifications** in the left sidebar. 1. Locate the project in the **Groups** section. 1. Select the desired [notification level](#notification-levels). @@ -126,7 +126,7 @@ To select a notification level for a group, use either of these methods: You can select an email address to receive notifications for each group you belong to. This could be useful, for example, if you work freelance, and want to keep email about clients' projects separate. -1. Click on your profile picture and select **Settings**. +1. Click on your profile picture and select **Preferences**. 1. Click **Notifications** in the left sidebar. 1. Locate the project in the **Groups** section. 1. Select the desired email address. diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md index a45c3d26f1a..bf082cca93a 100644 --- a/doc/user/project/deploy_keys/index.md +++ b/doc/user/project/deploy_keys/index.md @@ -151,6 +151,24 @@ Adding a public deploy key does not immediately expose any repository to it. Pub deploy keys enable access from other systems, but access is not given to any project until a project maintainer chooses to make use of it. +## How to disable deploy keys + +[Project maintainers and owners](../../permissions.md#project-members-permissions) +can remove or disable a deploy key for a project repository: + +1. Navigate to the project's **Settings > Repository** page. +1. Expand the **Deploy keys** section. +1. Select the **{remove}** or **{cancel}** button. + +NOTE: +If anything relies on the removed deploy key, it will stop working once removed. + +If the key is **publicly accessible**, it will be removed from the project, but still available under **Publicly accessible deploy keys**. + +If the key is **privately accessible** and only in use by this project, it will deleted. + +If the key is **privately accessible** and in use by other projects, it will be removed from the project, but still available under **Privately accesible deploy keys**. + ## Troubleshooting ### Deploy key cannot push to a protected branch diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md index 2a841400852..915d049b7c9 100644 --- a/doc/user/project/issues/managing_issues.md +++ b/doc/user/project/issues/managing_issues.md @@ -74,8 +74,9 @@ To visit the issue tracker for all projects in your group: 1. Go to the group dashboard. 1. In the left sidebar, select **Issues**. 1. In the top-right, select the **Select project to create issue** button. -1. Select the project you'd like to create an issue for. The button now appears as **New issue in <selected project>**. -1. Select **New issue in <selected project>**. +1. Select the project you'd like to create an issue for. The button now reflects the selected + project. +1. Select the button to create an issue in the selected project. ![Select project to create issue](img/select_project_from_group_level_issue_tracker_v13_11.png) diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md index fd5045f2e93..0cb5b5d993e 100644 --- a/doc/user/project/labels.md +++ b/doc/user/project/labels.md @@ -4,7 +4,7 @@ group: Project Management info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Labels +# Labels **(FREE)** As your count of issues, merge requests, and epics grows in GitLab, it's more and more challenging to keep track of those items. Especially as your organization grows from just a few people to diff --git a/fixtures/lib/gitlab/graphql/queries/plans.customer.mutation.graphql b/fixtures/lib/gitlab/graphql/queries/plans.customer.mutation.graphql new file mode 100644 index 00000000000..71ad42c3303 --- /dev/null +++ b/fixtures/lib/gitlab/graphql/queries/plans.customer.mutation.graphql @@ -0,0 +1,5 @@ +mutation updatePlans($tags: [PlanTag!]) { + plans(planTags: $tags) { + name + } +} diff --git a/fixtures/lib/gitlab/graphql/queries/plans.customer.query.graphql b/fixtures/lib/gitlab/graphql/queries/plans.customer.query.graphql new file mode 100644 index 00000000000..cbc705ee81b --- /dev/null +++ b/fixtures/lib/gitlab/graphql/queries/plans.customer.query.graphql @@ -0,0 +1,5 @@ +query getPlans($tags: [PlanTag!]) { + plans(planTags: $tags) { + name + } +} diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 1387aeed2e7..92018adadb1 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -592,18 +592,26 @@ module API def project_finder_params_ce finder_params = project_finder_params_visibility_ce + + finder_params.merge!( + params + .slice(:search, + :custom_attributes, + :last_activity_after, + :last_activity_before, + :repository_storage) + .symbolize_keys + .compact + ) + finder_params[:with_issues_enabled] = true if params[:with_issues_enabled].present? finder_params[:with_merge_requests_enabled] = true if params[:with_merge_requests_enabled].present? finder_params[:without_deleted] = true - finder_params[:search] = params[:search] if params[:search] finder_params[:search_namespaces] = true if params[:search_namespaces].present? finder_params[:user] = params.delete(:user) if params[:user] - finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes] finder_params[:id_after] = sanitize_id_param(params[:id_after]) if params[:id_after] finder_params[:id_before] = sanitize_id_param(params[:id_before]) if params[:id_before] - finder_params[:last_activity_after] = params[:last_activity_after] if params[:last_activity_after] - finder_params[:last_activity_before] = params[:last_activity_before] if params[:last_activity_before] - finder_params[:repository_storage] = params[:repository_storage] if params[:repository_storage] + finder_params[:tag] = params[:topic] if params[:topic].present? finder_params end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 491afe3b9a3..27c9ea5e0cb 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -117,6 +117,7 @@ module API optional :last_activity_after, type: DateTime, desc: 'Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ' optional :last_activity_before, type: DateTime, desc: 'Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ' optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins' + optional :topic, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of topics. Limit results to projects having all topics' use :optional_filter_params_ee end diff --git a/lib/gitlab/graphql/queries.rb b/lib/gitlab/graphql/queries.rb index 74f55abccbc..5d3a9245427 100644 --- a/lib/gitlab/graphql/queries.rb +++ b/lib/gitlab/graphql/queries.rb @@ -264,7 +264,7 @@ module Gitlab definitions = [] ::Find.find(root.to_s) do |path| - definitions << Definition.new(path, fragments) if query?(path) + definitions << Definition.new(path, fragments) if query_for_gitlab_schema?(path) end definitions @@ -288,10 +288,11 @@ module Gitlab @known_failures.fetch('filenames', []).any? { |known_failure| path.to_s.ends_with?(known_failure) } end - def self.query?(path) + def self.query_for_gitlab_schema?(path) path.ends_with?('.graphql') && !path.ends_with?('.fragment.graphql') && - !path.ends_with?('typedefs.graphql') + !path.ends_with?('typedefs.graphql') && + !/.*\.customer\.(query|mutation)\.graphql$/.match?(path) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 865a6413e20..a21f161df98 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4265,6 +4265,9 @@ msgstr "" msgid "Are you sure you want to remove the license?" msgstr "" +msgid "Are you sure you want to remove this deploy key? If anything is still using this key, it will stop working." +msgstr "" + msgid "Are you sure you want to remove this identity?" msgstr "" @@ -10816,9 +10819,6 @@ msgstr "" msgid "DeployKeys|Read access only" msgstr "" -msgid "DeployKeys|You are going to remove this deploy key. Are you sure?" -msgstr "" - msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})" msgstr "" @@ -11483,6 +11483,9 @@ msgstr "" msgid "Do not display offers from third parties within GitLab" msgstr "" +msgid "Do you want to remove this deploy key?" +msgstr "" + msgid "Dockerfile" msgstr "" @@ -26466,6 +26469,9 @@ msgstr "" msgid "Remove child epic from an epic" msgstr "" +msgid "Remove deploy key" +msgstr "" + msgid "Remove description history" msgstr "" diff --git a/package.json b/package.json index f16ece96f19..5c86a0c7839 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "1.189.0", "@gitlab/tributejs": "1.0.0", - "@gitlab/ui": "29.7.3", + "@gitlab/ui": "29.8.1", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-4", "@rails/ujs": "^6.0.3-4", @@ -70,14 +70,14 @@ "axios": "^0.20.0", "babel-loader": "^8.2.2", "babel-plugin-lodash": "^3.3.4", - "bootstrap": "4.4.1", + "bootstrap": "4.5.3", "cache-loader": "^4.1.0", "clipboard": "^1.7.1", "codemirror": "^5.48.4", "codesandbox-api": "0.0.23", "compression-webpack-plugin": "^5.0.2", "copy-webpack-plugin": "^5.1.2", - "core-js": "^3.10.2", + "core-js": "^3.11.0", "cron-validator": "^1.1.1", "cropper": "^2.3.0", "css-loader": "^2.1.1", diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb index 6218578cac6..5a36290214d 100644 --- a/spec/features/projects/deploy_keys_spec.rb +++ b/spec/features/projects/deploy_keys_spec.rb @@ -22,7 +22,8 @@ RSpec.describe 'Project deploy keys', :js do page.within(find('.qa-deploy-keys-settings')) do expect(page).to have_selector('.deploy-key', count: 1) - accept_confirm { find('[data-testid="remove-icon"]').click } + click_button 'Remove' + click_button 'Remove deploy key' wait_for_requests diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 0ffb6d14cb3..eafac1c4e68 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -457,20 +457,20 @@ RSpec.describe 'Pipelines', :js do visit_project_pipelines end - it 'has artifacts' do - expect(page).to have_selector('.build-artifacts') + it 'has artifacts dropdown' do + expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]') end it 'has artifacts download dropdown' do - find('.js-pipeline-dropdown-download').click + find('[data-testid="pipeline-multi-actions-dropdown"]').click expect(page).to have_link(with_artifacts.file_type) end it 'has download attribute on download links' do - find('.js-pipeline-dropdown-download').click + find('[data-testid="pipeline-multi-actions-dropdown"]').click expect(page).to have_selector('a', text: 'Download') - page.all('.build-artifacts a', text: 'Download').each do |link| + page.all('[data-testid="artifact-item"]', text: 'Download').each do |link| expect(link[:download]).to eq '' end end @@ -488,7 +488,7 @@ RSpec.describe 'Pipelines', :js do visit_project_pipelines end - it { expect(page).not_to have_selector('.build-artifacts') } + it { expect(page).not_to have_selector('[data-testid="artifact-item"]') } end context 'without artifacts' do @@ -503,7 +503,7 @@ RSpec.describe 'Pipelines', :js do visit_project_pipelines end - it { expect(page).not_to have_selector('.build-artifacts') } + it { expect(page).not_to have_selector('[data-testid="artifact-item"]') } end context 'with trace artifact' do @@ -514,7 +514,7 @@ RSpec.describe 'Pipelines', :js do end it 'does not show trace artifact as artifacts' do - expect(page).not_to have_selector('.build-artifacts') + expect(page).not_to have_selector('[data-testid="artifact-item"]') end end end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index ea26fe502a5..f420a8a76b9 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -117,7 +117,8 @@ RSpec.describe 'Projects > Settings > Repository settings' do project.deploy_keys << private_deploy_key visit project_settings_repository_path(project) - accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('[data-testid="remove-icon"]').click } + click_button 'Remove' + click_button 'Remove deploy key' expect(page).not_to have_content(private_deploy_key.title) end diff --git a/spec/frontend/__helpers__/mock_apollo_helper.js b/spec/frontend/__helpers__/mock_apollo_helper.js index bd97a06071a..520d6c72541 100644 --- a/spec/frontend/__helpers__/mock_apollo_helper.js +++ b/spec/frontend/__helpers__/mock_apollo_helper.js @@ -1,5 +1,5 @@ import { InMemoryCache } from 'apollo-cache-inmemory'; -import { createMockClient } from 'mock-apollo-client'; +import { createMockClient as createMockApolloClient } from 'mock-apollo-client'; import VueApollo from 'vue-apollo'; const defaultCacheOptions = { @@ -7,13 +7,13 @@ const defaultCacheOptions = { addTypename: false, }; -export default (handlers = [], resolvers = {}, cacheOptions = {}) => { +export function createMockClient(handlers = [], resolvers = {}, cacheOptions = {}) { const cache = new InMemoryCache({ ...defaultCacheOptions, ...cacheOptions, }); - const mockClient = createMockClient({ cache, resolvers }); + const mockClient = createMockApolloClient({ cache, resolvers }); if (Array.isArray(handlers)) { handlers.forEach(([query, value]) => mockClient.setRequestHandler(query, value)); @@ -21,7 +21,12 @@ export default (handlers = [], resolvers = {}, cacheOptions = {}) => { throw new Error('You should pass an array of handlers to mock Apollo client'); } + return mockClient; +} + +export default function createMockApollo(handlers, resolvers, cacheOptions) { + const mockClient = createMockClient(handlers, resolvers, cacheOptions); const apolloProvider = new VueApollo({ defaultClient: mockClient }); return apolloProvider; -}; +} diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap index 9a89e3430b4..35c02911e27 100644 --- a/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap +++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`content_editor/components/toolbar_button displays tertiary, small button with a provided label and icon 1`] = ` -"<b-button-stub event=\\"click\\" routertag=\\"a\\" size=\\"sm\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-mx-2 gl-button btn-default-tertiary btn-icon\\"> +"<b-button-stub size=\\"sm\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-mx-2 gl-button btn-default-tertiary btn-icon\\"> <!----> <gl-icon-stub name=\\"bold\\" size=\\"16\\" class=\\"gl-button-icon\\"></gl-icon-stub> <!----> diff --git a/spec/frontend/deploy_keys/components/action_btn_spec.js b/spec/frontend/deploy_keys/components/action_btn_spec.js index 21281ff15b1..307a0b6d8b0 100644 --- a/spec/frontend/deploy_keys/components/action_btn_spec.js +++ b/spec/frontend/deploy_keys/components/action_btn_spec.js @@ -1,4 +1,4 @@ -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import actionBtn from '~/deploy_keys/components/action_btn.vue'; import eventHub from '~/deploy_keys/eventhub'; @@ -8,13 +8,16 @@ describe('Deploy keys action btn', () => { const deployKey = data.enabled_keys[0]; let wrapper; - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + const findButton = () => wrapper.findComponent(GlButton); beforeEach(() => { wrapper = shallowMount(actionBtn, { propsData: { deployKey, type: 'enable', + category: 'primary', + variant: 'confirm', + icon: 'edit', }, slots: { default: 'Enable', @@ -26,10 +29,18 @@ describe('Deploy keys action btn', () => { expect(wrapper.text()).toBe('Enable'); }); + it('passes the button props on', () => { + expect(findButton().props()).toMatchObject({ + category: 'primary', + variant: 'confirm', + icon: 'edit', + }); + }); + it('sends eventHub event with btn type', () => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - wrapper.trigger('click'); + findButton().vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, expect.anything()); @@ -37,18 +48,10 @@ describe('Deploy keys action btn', () => { }); it('shows loading spinner after click', () => { - wrapper.trigger('click'); - - return wrapper.vm.$nextTick().then(() => { - expect(findLoadingIcon().exists()).toBe(true); - }); - }); - - it('disables button after click', () => { - wrapper.trigger('click'); + findButton().vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { - expect(wrapper.attributes('disabled')).toBe('disabled'); + expect(findButton().props('loading')).toBe(true); }); }); }); diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js index b48e0424580..a72b2b00776 100644 --- a/spec/frontend/deploy_keys/components/app_spec.js +++ b/spec/frontend/deploy_keys/components/app_spec.js @@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import { TEST_HOST } from 'spec/test_constants'; import deployKeysApp from '~/deploy_keys/components/app.vue'; +import ConfirmModal from '~/deploy_keys/components/confirm_modal.vue'; import eventHub from '~/deploy_keys/eventhub'; import axios from '~/lib/utils/axios_utils'; @@ -36,6 +37,7 @@ describe('Deploy keys app component', () => { const findLoadingIcon = () => wrapper.find('.gl-spinner'); const findKeyPanels = () => wrapper.findAll('.deploy-keys .gl-tabs-nav li'); + const findModal = () => wrapper.findComponent(ConfirmModal); it('renders loading icon while waiting for request', () => { mock.onGet(TEST_ENDPOINT).reply(() => new Promise()); @@ -94,11 +96,16 @@ describe('Deploy keys app component', () => { const key = data.public_keys[0]; return mountComponent() .then(() => { - jest.spyOn(window, 'confirm').mockReturnValue(true); jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {}); jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve()); - eventHub.$emit('disable.key', key); + eventHub.$emit('disable.key', key, () => {}); + + return wrapper.vm.$nextTick(); + }) + .then(() => { + expect(findModal().props('visible')).toBe(true); + findModal().vm.$emit('remove'); return wrapper.vm.$nextTick(); }) @@ -112,11 +119,16 @@ describe('Deploy keys app component', () => { const key = data.public_keys[0]; return mountComponent() .then(() => { - jest.spyOn(window, 'confirm').mockReturnValue(true); jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {}); jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve()); - eventHub.$emit('remove.key', key); + eventHub.$emit('remove.key', key, () => {}); + + return wrapper.vm.$nextTick(); + }) + .then(() => { + expect(findModal().props('visible')).toBe(true); + findModal().vm.$emit('remove'); return wrapper.vm.$nextTick(); }) diff --git a/spec/frontend/deploy_keys/components/confirm_modal_spec.js b/spec/frontend/deploy_keys/components/confirm_modal_spec.js new file mode 100644 index 00000000000..42cc2b377a7 --- /dev/null +++ b/spec/frontend/deploy_keys/components/confirm_modal_spec.js @@ -0,0 +1,28 @@ +import { GlModal } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import ConfirmModal from '~/deploy_keys/components/confirm_modal.vue'; + +describe('~/deploy_keys/components/confirm_modal.vue', () => { + let wrapper; + let modal; + + beforeEach(() => { + wrapper = mount(ConfirmModal, { propsData: { modalId: 'test', visible: true } }); + modal = extendedWrapper(wrapper.findComponent(GlModal)); + }); + + it('emits a remove event if the primary button is clicked', () => { + modal.findByText('Remove deploy key').trigger('click'); + expect(wrapper.emitted('remove')).toEqual([[]]); + }); + + it('emits a cancel event if the secondary button is clicked', () => { + modal.findByText('Cancel').trigger('click'); + expect(wrapper.emitted('cancel')).toEqual([[]]); + }); + + it('displays the warning about removing the deploy key', () => { + expect(modal.text()).toContain('Are you sure you want to remove this deploy key?'); + }); +}); diff --git a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap index bea27c8877d..9f49cb4007a 100644 --- a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap +++ b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap @@ -24,7 +24,9 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] = role="columnheader" scope="col" > - Jira display name + <div> + Jira display name + </div> </th> <th aria-colindex="2" @@ -32,14 +34,18 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] = class="" role="columnheader" scope="col" - /> + > + <div /> + </th> <th aria-colindex="3" class="" role="columnheader" scope="col" > - GitLab username + <div> + GitLab username + </div> </th> </tr> </thead> diff --git a/spec/frontend/pipelines/pipeline_multi_actions_spec.js b/spec/frontend/pipelines/pipeline_multi_actions_spec.js new file mode 100644 index 00000000000..bb110a8924f --- /dev/null +++ b/spec/frontend/pipelines/pipeline_multi_actions_spec.js @@ -0,0 +1,67 @@ +import { GlDropdown, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import PipelineMultiActions from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue'; + +describe('Pipeline Multi Actions Dropdown', () => { + let wrapper; + + const artifactItemTestId = 'artifact-item'; + + const defaultProps = { + artifacts: [ + { + name: 'job my-artifact', + path: '/download/path', + }, + { + name: 'job-2 my-artifact-2', + path: '/download/path-two', + }, + ], + }; + + const createComponent = (props = defaultProps) => { + wrapper = extendedWrapper( + shallowMount(PipelineMultiActions, { + propsData: { + ...defaultProps, + ...props, + }, + stubs: { + GlSprintf, + }, + }), + ); + }; + + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findAllArtifactItems = () => wrapper.findAllByTestId(artifactItemTestId); + const findFirstArtifactItem = () => wrapper.findByTestId(artifactItemTestId); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('should render the dropdown', () => { + expect(findDropdown().exists()).toBe(true); + }); + + describe('Artifacts', () => { + it('should render all the provided artifacts', () => { + expect(findAllArtifactItems()).toHaveLength(defaultProps.artifacts.length); + }); + + it('should render the correct artifact name and path', () => { + expect(findFirstArtifactItem().attributes('href')).toBe(defaultProps.artifacts[0].path); + + expect(findFirstArtifactItem().text()).toBe( + `Download ${defaultProps.artifacts[0].name} artifact`, + ); + }); + }); +}); diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index 787e2174070..832295db005 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -15,10 +15,12 @@ RSpec.describe Types::Ci::JobType do commitPath coverage created_at + created_by_tag detailedStatus duration finished_at id + manual_job name needs pipeline @@ -34,6 +36,7 @@ RSpec.describe Types::Ci::JobType do started_at status tags + triggered ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index 120dbe7cb49..c4366cffd7f 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -409,4 +409,33 @@ RSpec.describe AvatarsHelper do end end end + + describe '#avatar_without_link' do + let(:options) { { size: 32 } } + + subject { helper.avatar_without_link(resource, options) } + + context 'with users' do + let(:resource) { user } + + it 'displays user avatar' do + is_expected.to eq tag( + :img, + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 32), + data: { container: 'body' }, + class: 'avatar s32 has-tooltip', + title: user.name + ) + end + end + + context 'with groups' do + let(:resource) { build_stubbed(:group, name: 'foo') } + + it 'displays group avatar' do + is_expected.to match(%r{<div class="avatar identicon bg\d+ s32">F</div>}) + end + end + end end diff --git a/spec/lib/gitlab/graphql/queries_spec.rb b/spec/lib/gitlab/graphql/queries_spec.rb index a140a283c1b..a1cd2cdb2de 100644 --- a/spec/lib/gitlab/graphql/queries_spec.rb +++ b/spec/lib/gitlab/graphql/queries_spec.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'spec_helper' require 'fast_spec_helper' require "test_prof/recipes/rspec/let_it_be" @@ -124,6 +125,18 @@ RSpec.describe Gitlab::Graphql::Queries do expect(described_class.find(path)).to be_empty end + it 'ignores customer.query.graphql' do + path = root / 'plans.customer.query.graphql' + + expect(described_class.find(path)).to be_empty + end + + it 'ignores customer.mutation.graphql' do + path = root / 'plans.customer.mutation.graphql' + + expect(described_class.find(path)).to be_empty + end + it 'finds all query definitions under a root directory' do found = described_class.find(root) @@ -137,7 +150,9 @@ RSpec.describe Gitlab::Graphql::Queries do expect(found).not_to include( definition_of(root / 'typedefs.graphql'), - definition_of(root / 'author.fragment.graphql') + definition_of(root / 'author.fragment.graphql'), + definition_of(root / 'plans.customer.query.graphql'), + definition_of(root / 'plans.customer.mutation.graphql') ) end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index d8eb4ebc432..5d2cfcb7611 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -212,6 +212,26 @@ RSpec.describe Service do end end + describe '#group_level?' do + it 'is true when service has a group' do + expect(build(:service, group: group)).to be_group_level + end + + it 'is false when service has no group' do + expect(build(:service, group: nil)).not_to be_group_level + end + end + + describe '#instance_level?' do + it 'is true when service has instance-level integration' do + expect(build(:service, :instance)).to be_instance_level + end + + it 'is false when service does not have instance-level integration' do + expect(build(:service, instance: false)).not_to be_instance_level + end + end + describe '.find_or_initialize_non_project_specific_integration' do let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) } let!(:service2) { create(:jira_service) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index b0ecb711283..a368d66ab11 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -223,6 +223,52 @@ RSpec.describe API::Projects do expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count') end + context 'filter by topic (column tag_list)' do + before do + project.update!(tag_list: %w(ruby javascript)) + end + + it 'returns no projects' do + get api('/projects', user), params: { topic: 'foo' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_empty + end + + it 'returns matching project for a single topic' do + get api('/projects', user), params: { topic: 'ruby' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to contain_exactly a_hash_including('id' => project.id) + end + + it 'returns matching project for multiple topics' do + get api('/projects', user), params: { topic: 'ruby, javascript' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to contain_exactly a_hash_including('id' => project.id) + end + + it 'returns no projects if project match only some topic' do + get api('/projects', user), params: { topic: 'ruby, foo' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_empty + end + + it 'ignores topic if it is empty' do + get api('/projects', user), params: { topic: '' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_present + end + end + context 'and with_issues_enabled=true' do it 'only returns projects with issues enabled' do project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) diff --git a/yarn.lock b/yarn.lock index 184a5037a13..a8e560490a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,14 +907,14 @@ resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== -"@gitlab/ui@29.7.3": - version "29.7.3" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.7.3.tgz#f7762cc6a20fc8f1c6403822f8cbef821f2438dd" - integrity sha512-2pU7t+kFB4ndq3ZW8PzZ26LEj+vAd8AaJLvBmBDba1hEx5KlH4B9U30lGl+UYzgjY3yoe68eObK26vJjJ1rb0g== +"@gitlab/ui@29.8.1": + version "29.8.1" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.8.1.tgz#9bac442e0579e7eb5610a3e08c481bb272514559" + integrity sha512-Pn1n4K2MR42YoQI+8i9Hs5ljtb2TQWhx1ectEsft/xMLS8TTm8okM553sM9Z7kf1coizFKILQOmz+E5RwcnOsQ== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" - bootstrap-vue "2.14.0" + bootstrap-vue "2.15.0" copy-to-clipboard "^3.0.8" dompurify "^2.2.7" echarts "^4.9.0" @@ -2480,21 +2480,21 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" -bootstrap-vue@2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.14.0.tgz#de88b607627431980b707e6f069f13ef8cc897bd" - integrity sha512-sqbS7iHYCZEj/dDx4Yaze99HcX6bZjO4bSWZ0xSgJwtWQlbfB2VDJ9Qjzjp9XI8TT32wYNGAMpnXpYjQvv5qyQ== +bootstrap-vue@2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.15.0.tgz#0dfc12c054496c0f10efed510da1def41697cf3c" + integrity sha512-ncxWkDG0mKFVot314wWKJELi+ESO7k6ngV//qvJFs9iVzlFI8Hx3rBVbpcPW2vrJ+0vitH8N2SOwn4fdQ3frMQ== dependencies: "@nuxt/opencollective" "^0.3.0" - bootstrap ">=4.4.1 <5.0.0" + bootstrap ">=4.5.0 <5.0.0" popper.js "^1.16.1" portal-vue "^2.1.7" vue-functional-data-merge "^3.1.0" -bootstrap@4.4.1, "bootstrap@>=4.4.1 <5.0.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01" - integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA== +bootstrap@4.5.3, "bootstrap@>=4.5.0 <5.0.0": + version "4.5.3" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6" + integrity sha512-o9ppKQioXGqhw8Z7mah6KdTYpNQY//tipnkxppWhPbiSWdD+1raYsnhwEZjkTHYbGee4cVQ0Rx65EhOY/HNLcQ== boxen@^4.2.0: version "4.2.0" @@ -3387,10 +3387,10 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-js@^3.1.3, core-js@^3.10.2: - version "3.10.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.2.tgz#17cb038ce084522a717d873b63f2b3ee532e2cd5" - integrity sha512-W+2oVYeNghuBr3yTzZFQ5rfmjZtYB/Ubg87R5YOmlGrIb+Uw9f7qjUbhsj+/EkXhcV7eOD3jiM4+sgraX3FZUw== +core-js@^3.1.3, core-js@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.11.0.tgz#05dac6aa70c0a4ad842261f8957b961d36eb8926" + integrity sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw== core-js@~2.3.0: version "2.3.0" |