Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-03-30 12:08:15 +00:00
parent e820415cea
commit bfa1adf977
112 changed files with 1347 additions and 230 deletions

View File

@ -556,7 +556,6 @@ Layout/ArgumentAlignment:
- 'app/models/packages/cleanup/policy.rb'
- 'app/models/packages/conan/metadatum.rb'
- 'app/models/packages/debian/file_entry.rb'
- 'app/models/packages/debian/file_metadatum.rb'
- 'app/models/packages/package.rb'
- 'app/models/packages/rpm/metadatum.rb'
- 'app/models/pages_domain.rb'
@ -665,7 +664,6 @@ Layout/ArgumentAlignment:
- 'app/validators/feature_flag_user_xids_validator.rb'
- 'app/workers/gitlab/github_import/stage/import_protected_branches_worker.rb'
- 'app/workers/gitlab/jira_import/stuck_jira_import_jobs_worker.rb'
- 'app/workers/packages/debian/process_package_file_worker.rb'
- 'app/workers/repository_update_remote_mirror_worker.rb'
- 'app/workers/run_pipeline_schedule_worker.rb'
- 'app/workers/stuck_export_jobs_worker.rb'

View File

@ -186,7 +186,6 @@ Layout/FirstHashElementIndentation:
- 'spec/controllers/projects/web_ide_terminals_controller_spec.rb'
- 'spec/controllers/projects_controller_spec.rb'
- 'spec/factories/ci/builds.rb'
- 'spec/factories/packages/debian/file_metadatum.rb'
- 'spec/frontend/fixtures/autocomplete_sources.rb'
- 'spec/graphql/types/ci/detailed_status_type_spec.rb'
- 'spec/helpers/groups/observability_helper_spec.rb'

View File

@ -3534,7 +3534,6 @@ Layout/LineLength:
- 'spec/factories/keys.rb'
- 'spec/factories/namespaces.rb'
- 'spec/factories/notes.rb'
- 'spec/factories/packages/debian/file_metadatum.rb'
- 'spec/factories/packages/package_files.rb'
- 'spec/factories/project_members.rb'
- 'spec/factories/projects.rb'
@ -4667,7 +4666,6 @@ Layout/LineLength:
- 'spec/models/packages/composer/metadatum_spec.rb'
- 'spec/models/packages/conan/metadatum_spec.rb'
- 'spec/models/packages/debian/file_entry_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
- 'spec/models/packages/debian/publication_spec.rb'
- 'spec/models/packages/dependency_link_spec.rb'
- 'spec/models/packages/dependency_spec.rb'

View File

@ -84,7 +84,6 @@ Lint/SymbolConversion:
- 'spec/controllers/jira_connect/branches_controller_spec.rb'
- 'spec/factories/ci/reports/codequality_degradations.rb'
- 'spec/factories/evidences.rb'
- 'spec/factories/packages/debian/file_metadatum.rb'
- 'spec/factories/packages/helm/file_metadatum.rb'
- 'spec/factories/packages/npm/metadata.rb'
- 'spec/features/file_uploads/graphql_add_design_spec.rb'
@ -140,7 +139,6 @@ Lint/SymbolConversion:
- 'spec/lib/service_ping/devops_report_spec.rb'
- 'spec/models/integrations/prometheus_spec.rb'
- 'spec/models/merge_request_diff_commit_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
- 'spec/models/packages/helm/file_metadatum_spec.rb'
- 'spec/models/packages/npm/metadatum_spec.rb'
- 'spec/presenters/packages/npm/package_presenter_spec.rb'

View File

@ -374,7 +374,6 @@ Lint/UnusedBlockArgument:
- 'spec/models/concerns/each_batch_spec.rb'
- 'spec/models/container_repository_spec.rb'
- 'spec/models/network/graph_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
- 'spec/requests/api/ci/pipeline_schedules_spec.rb'
- 'spec/requests/api/graphql/gitlab_schema_spec.rb'
- 'spec/requests/api/internal/container_registry/migration_spec.rb'

View File

@ -54,7 +54,6 @@ Naming/HeredocDelimiterNaming:
- 'rubocop/cop/gitlab/predicate_memoization.rb'
- 'spec/controllers/projects/pipelines_controller_spec.rb'
- 'spec/deprecation_toolkit_env.rb'
- 'spec/factories/packages/debian/file_metadatum.rb'
- 'spec/features/projects/commit/user_comments_on_commit_spec.rb'
- 'spec/features/task_lists_spec.rb'
- 'spec/initializers/100_patch_omniauth_oauth2_spec.rb'

View File

@ -2303,7 +2303,6 @@ RSpec/ContextWording:
- 'spec/models/notification_setting_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/packages/conan/file_metadatum_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
- 'spec/models/packages/dependency_link_spec.rb'
- 'spec/models/packages/dependency_spec.rb'
- 'spec/models/packages/package_file_spec.rb'

View File

@ -5264,7 +5264,6 @@ RSpec/MissingFeatureCategory:
- 'spec/models/packages/conan/file_metadatum_spec.rb'
- 'spec/models/packages/conan/metadatum_spec.rb'
- 'spec/models/packages/debian/file_entry_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
- 'spec/models/packages/debian/group_architecture_spec.rb'
- 'spec/models/packages/debian/group_component_file_spec.rb'
- 'spec/models/packages/debian/group_component_spec.rb'

View File

@ -285,7 +285,6 @@ Style/ClassAndModuleChildren:
- 'app/models/packages/build_info.rb'
- 'app/models/packages/conan/file_metadatum.rb'
- 'app/models/packages/conan/metadatum.rb'
- 'app/models/packages/debian/file_metadatum.rb'
- 'app/models/packages/debian/group_architecture.rb'
- 'app/models/packages/debian/group_component.rb'
- 'app/models/packages/debian/group_component_file.rb'

View File

@ -1 +1 @@
c9c3f444b7d9e54f706552e199f7b9239b0027c5
140ee5c9aa5f4fb7dd4a0017677788ee88ce7149

View File

@ -22,7 +22,7 @@ import {
TOKEN_TYPE_WEIGHT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import { AssigneeFilterType } from '~/boards/constants';
import { AssigneeFilterType, GroupByParamType } from 'ee_else_ce/boards/constants';
import { TYPENAME_ITERATION } from '~/graphql_shared/constants';
import eventHub from '../eventhub';
@ -33,6 +33,11 @@ export default {
components: { FilteredSearch },
inject: ['initialFilterParams', 'isApolloBoard'],
props: {
isSwimlanesOn: {
type: Boolean,
required: false,
default: false,
},
tokens: {
type: Array,
required: true,
@ -321,6 +326,7 @@ export default {
release_tag: releaseTag,
confidential,
health_status: healthStatus,
group_by: this.isSwimlanesOn ? GroupByParamType.epic : undefined,
},
(value) => {
if (value || value === false) {

View File

@ -226,12 +226,10 @@ export default {
}
this.cancel();
if (!this.isApolloBoard) {
const param = getParameterByName('group_by')
? `?group_by=${getParameterByName('group_by')}`
: '';
updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` });
}
const param = getParameterByName('group_by')
? `?group_by=${getParameterByName('group_by')}`
: '';
updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` });
} catch {
this.setError({ message: this.$options.i18n.saveErrorMessage });
} finally {

View File

@ -98,6 +98,7 @@ export default {
<issue-board-filtered-search
v-if="isIssueBoard"
:board="board"
:is-swimlanes-on="isSwimlanesOn"
@setFilters="$emit('setFilters', $event)"
/>
<epic-board-filtered-search

View File

@ -52,6 +52,11 @@ export default {
required: false,
default: () => {},
},
isSwimlanesOn: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
tokensCE() {
@ -203,6 +208,7 @@ export default {
data-testid="issue-board-filtered-search"
:tokens="tokens"
:board="board"
:is-swimlanes-on="isSwimlanesOn"
@setFilters="$emit('setFilters', $event)"
/>
</template>

View File

@ -1,6 +1,9 @@
import { __, s__ } from '~/locale';
export const GENERIC_ERROR = __('Something went wrong on our end. Please try again!');
export const LOAD_SINGLE_DIFF_FAILED = s__(
'MergeRequest|Encountered an issue while trying to fetch the single file diff.',
);
export const DIFF_FILE_HEADER = {
optionsDropdownTitle: __('Options'),

View File

@ -50,6 +50,7 @@ import {
TRACKING_SINGLE_FILE_MODE,
TRACKING_MULTIPLE_FILES_MODE,
} from '../constants';
import { LOAD_SINGLE_DIFF_FAILED } from '../i18n';
import eventHub from '../event_hub';
import { isCollapsed } from '../utils/diff_file';
import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews';
@ -590,6 +591,31 @@ export const setCurrentFileHash = ({ commit }, hash) => {
commit(types.SET_CURRENT_DIFF_FILE, hash);
};
export const goToFile = ({ state, commit, dispatch, getters }, { path }) => {
if (!state.viewDiffsFileByFile) {
dispatch('scrollToFile', { path });
} else {
if (!state.treeEntries[path]) return;
const { fileHash } = state.treeEntries[path];
commit(types.SET_CURRENT_DIFF_FILE, fileHash);
if (!getters.isTreePathLoaded(path)) {
document.location.hash = fileHash;
dispatch('fetchFileByFile')
.then(() => {
dispatch('scrollToFile', { path });
})
.catch(() => {
createAlert({
message: LOAD_SINGLE_DIFF_FAILED,
});
});
}
}
};
export const scrollToFile = ({ state, commit, getters }, { path }) => {
if (!state.treeEntries[path]) return;

View File

@ -126,6 +126,26 @@ export const config = {
};
},
},
Group: {
fields: {
projects: {
keyArgs: ['includeSubgroups', 'search'],
},
descendantGroups: {
keyArgs: ['includeSubgroups', 'search'],
},
},
},
ProjectConnection: {
fields: {
nodes: concatPagination(),
},
},
GroupConnection: {
fields: {
nodes: concatPagination(),
},
},
Board: {
fields: {
epics: {

View File

@ -199,7 +199,7 @@ export default {
<p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
</div>
</div>
<gl-form-group v-if="glFeatures.incidentEventTags">
<gl-form-group>
<label class="gl-display-flex gl-align-items-center gl-gap-3" for="timeline-input-tags">
{{ $options.i18n.tagsLabel }}
<timeline-events-tags-popover />

View File

@ -0,0 +1,78 @@
import Vue from 'vue';
import { createApolloProvider } from '@vue/apollo-option';
import { ApolloMutation } from '@vue/apollo-components';
export { ApolloMutation };
const installed = new WeakMap();
function callLifecycle(hookName, ...extraArgs) {
const { GITLAB_INTERNAL_ADDED_MIXINS: addedMixins } = this.$;
if (!addedMixins) {
return [];
}
return addedMixins.map((m) => m[hookName]?.apply(this, extraArgs));
}
function createMixinForLateInit({ install, shouldInstall }) {
return {
created() {
callLifecycle.call(this, 'created');
},
// @vue/compat normalizez lifecycle hook names so there is no error here
destroyed() {
callLifecycle.call(this, 'unmounted');
},
data(...args) {
const extraData = callLifecycle.call(this, 'data', ...args);
if (!extraData.length) {
return {};
}
return Object.assign({}, ...extraData);
},
beforeCreate() {
if (shouldInstall(this)) {
const { mixins } = this.$.appContext;
const globalMixinsBeforeInit = new Set(mixins);
install(this);
this.$.GITLAB_INTERNAL_ADDED_MIXINS = mixins.filter((m) => !globalMixinsBeforeInit.has(m));
callLifecycle.call(this, 'beforeCreate');
}
},
};
}
export default class VueCompatApollo {
constructor(...args) {
// eslint-disable-next-line no-constructor-return
return createApolloProvider(...args);
}
static install() {
Vue.mixin(
createMixinForLateInit({
shouldInstall: (vm) =>
vm.$options.apolloProvider &&
!installed.get(vm.$.appContext.app)?.has(vm.$options.apolloProvider),
install: (vm) => {
const { app } = vm.$.appContext;
const { apolloProvider } = vm.$options;
if (!installed.has(app)) {
installed.set(app, new WeakSet());
}
installed.get(app).add(apolloProvider);
vm.$.appContext.app.use(vm.$options.apolloProvider);
},
}),
);
}
}

View File

@ -43,6 +43,7 @@ const initRefSwitcher = () => {
props: {
projectId,
value: ref,
queryParams: { sort: 'updated_desc' },
},
on: {
input(selectedRef) {

View File

@ -187,6 +187,7 @@ export default {
:roles="pushAccessLevels.roles"
:users="pushAccessLevels.users"
:groups="pushAccessLevels.groups"
data-qa-selector="allowed_to_push_content"
/>
<!-- Allowed to merge -->
@ -197,6 +198,7 @@ export default {
:roles="mergeAccessLevels.roles"
:users="mergeAccessLevels.users"
:groups="mergeAccessLevels.groups"
data-qa-selector="allowed_to_merge_content"
/>
<!-- Force push -->

View File

@ -101,7 +101,13 @@ export default {
<div v-if="statusCheckUrl" class="gl-ml-7 gl-flex-grow-1">{{ statusCheckUrl }}</div>
<div v-for="(item, index) in accessLevels" :key="index" data-testid="access-level">
<div
v-for="(item, index) in accessLevels"
:key="index"
data-testid="access-level"
data-qa-selector="access_level_content"
:data-qa-role="item.accessLevelDescription"
>
<span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span>
{{ item.accessLevelDescription }}
</div>

View File

@ -69,9 +69,14 @@ export default {
<div v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</div>
<gl-button v-gl-modal="$options.modalId" class="gl-mt-5" category="secondary" variant="info">{{
$options.i18n.addBranchRule
}}</gl-button>
<gl-button
v-gl-modal="$options.modalId"
class="gl-mt-5"
data-qa-selector="add_branch_rule_button"
category="secondary"
variant="info"
>{{ $options.i18n.addBranchRule }}</gl-button
>
<gl-modal
:ref="$options.modalId"

View File

@ -153,7 +153,11 @@ export default {
</script>
<template>
<div class="gl-border-b gl-pt-5 gl-pb-5 gl-display-flex gl-justify-content-space-between">
<div
class="gl-border-b gl-pt-5 gl-pb-5 gl-display-flex gl-justify-content-space-between"
data-qa-selector="branch_content"
:data-qa-branch-name="name"
>
<div>
<strong class="gl-font-monospace">{{ name }}</strong>
@ -169,7 +173,7 @@ export default {
<li v-for="(detail, index) in approvalDetails" :key="index">{{ detail }}</li>
</ul>
</div>
<gl-button class="gl-align-self-start" :href="detailsPath">
<gl-button class="gl-align-self-start" data-qa-selector="details_button" :href="detailsPath">
{{ $options.i18n.detailsButtonLabel }}</gl-button
>
</div>

View File

@ -42,6 +42,11 @@ export default {
required: false,
default: '',
},
queryParams: {
type: Object,
required: false,
default: () => {},
},
refType: {
type: String,
required: false,
@ -93,6 +98,7 @@ export default {
matches: (state) => state.matches,
lastQuery: (state) => state.query,
selectedRef: (state) => state.selectedRef,
params: (state) => state.params,
}),
...mapGetters(['isLoading', 'isQueryPossiblyASha']),
i18n() {
@ -186,6 +192,7 @@ export default {
this.debouncedSearch = debounce(this.search, SEARCH_DEBOUNCE_MS);
this.setProjectId(this.projectId);
this.setParams(this.queryParams);
this.$watch(
'enabledRefTypes',
@ -206,6 +213,7 @@ export default {
...mapActions([
'setEnabledRefTypes',
'setUseSymbolicRefNames',
'setParams',
'setProjectId',
'setSelectedRef',
]),

View File

@ -5,6 +5,8 @@ import * as types from './mutation_types';
export const setEnabledRefTypes = ({ commit }, refTypes) =>
commit(types.SET_ENABLED_REF_TYPES, refTypes);
export const setParams = ({ commit }, params) => commit(types.SET_PARAMS, params);
export const setUseSymbolicRefNames = ({ commit }, useSymbolicRefNames) =>
commit(types.SET_USE_SYMBOLIC_REF_NAMES, useSymbolicRefNames);
@ -29,7 +31,7 @@ export const search = ({ state, dispatch, commit }, query) => {
export const searchBranches = ({ commit, state }) => {
commit(types.REQUEST_START);
Api.branches(state.projectId, state.query)
Api.branches(state.projectId, state.query, state.params)
.then((response) => {
commit(types.RECEIVE_BRANCHES_SUCCESS, response);
})

View File

@ -1,5 +1,6 @@
export const SET_ENABLED_REF_TYPES = 'SET_ENABLED_REF_TYPES';
export const SET_USE_SYMBOLIC_REF_NAMES = 'SET_USE_SYMBOLIC_REF_NAMES';
export const SET_PARAMS = 'SET_PARAMS';
export const SET_PROJECT_ID = 'SET_PROJECT_ID';
export const SET_SELECTED_REF = 'SET_SELECTED_REF';

View File

@ -10,6 +10,9 @@ export default {
[types.SET_USE_SYMBOLIC_REF_NAMES](state, useSymbolicRefNames) {
state.useSymbolicRefNames = useSymbolicRefNames;
},
[types.SET_PARAMS](state, params) {
state.params = params;
},
[types.SET_PROJECT_ID](state, projectId) {
state.projectId = projectId;
},

View File

@ -15,5 +15,6 @@ export default () => ({
commits: createRefTypeState(),
},
selectedRef: null,
params: null,
requestCount: 0,
});

View File

@ -137,6 +137,7 @@ export default function setupVueRepositoryList() {
props: {
projectId,
value: ref,
queryParams: { sort: 'updated_desc' },
},
on: {
input(selectedRef) {

View File

@ -14,10 +14,10 @@ import PersistentUserCallout from '~/persistent_user_callout';
import UserNameGroup from './user_name_group.vue';
export default {
feedbackUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391533',
feedbackUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/403059',
i18n: {
newNavigation: {
badgeLabel: s__('NorthstarNavigation|Alpha'),
badgeLabel: s__('NorthstarNavigation|Beta'),
sectionTitle: s__('NorthstarNavigation|Navigation redesign'),
},
setStatus: s__('SetStatusModal|Set status'),

View File

@ -610,10 +610,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
padding: 0;
vertical-align: top;
white-space: normal;
// Fixes subpixel rounding issue https://gitlab.com/gitlab-org/gitlab-foss/issues/53973
// background-color is needed for dark code preference
padding-bottom: 1px;
background-color: $white;
&.parallel {
@ -640,6 +636,14 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
}
}
}
.diff-grid-comments:last-child {
.notes-content {
border-bottom-width: 0;
border-bottom-left-radius: #{$border-radius-default - 1px};
border-bottom-right-radius: #{$border-radius-default - 1px};
}
}
}
.diffs {

View File

@ -10,7 +10,6 @@ class Projects::IncidentsController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:incident_event_tags, project)
end
feature_category :incident_management

View File

@ -66,7 +66,6 @@ class Projects::IssuesController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_frontend_feature_flag(:incident_event_tags, project)
push_frontend_feature_flag(:real_time_issue_due_date, project)
end

View File

@ -125,7 +125,7 @@ module Namespaces
elsif parent_id.nil?
# There is no parent, so we are the root ancestor.
self
elsif traversal_ids.present?
else
Namespace.find_by(id: traversal_ids.first)
end
end

View File

@ -12,6 +12,8 @@ module Packages
EMPTY_FILE_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'.freeze
INCOMING_PACKAGE_NAME = 'incoming'
def self.table_name_prefix
'packages_debian_'
end

View File

@ -1,59 +1,69 @@
# frozen_string_literal: true
class Packages::Debian::FileMetadatum < ApplicationRecord
self.primary_key = :package_file_id
module Packages
module Debian
class FileMetadatum < ApplicationRecord
include UpdatedAtFilterable
belongs_to :package_file, inverse_of: :debian_file_metadatum
self.primary_key = :package_file_id
validates :package_file, presence: true
validate :valid_debian_package_type
belongs_to :package_file, inverse_of: :debian_file_metadatum
enum file_type: {
unknown: 1, source: 2, dsc: 3, deb: 4, udeb: 5, buildinfo: 6, changes: 7, ddeb: 8
}
validates :package_file, presence: true
validate :valid_debian_package_type
validates :file_type, presence: true
validates :file_type, inclusion: { in: %w[unknown] },
if: -> { package_file&.package&.debian_incoming? || package_file&.package&.processing? }
validates :file_type,
inclusion: { in: %w[source dsc deb udeb buildinfo changes ddeb] },
if: -> { package_file&.package&.debian_package? && !package_file&.package&.processing? }
enum file_type: {
unknown: 1, source: 2, dsc: 3, deb: 4, udeb: 5, buildinfo: 6, changes: 7, ddeb: 8
}
validates :component,
presence: true,
format: { with: Gitlab::Regex.debian_component_regex },
if: :requires_component?
validates :component, absence: true, unless: :requires_component?
validates :file_type, presence: true
validates :file_type, inclusion: { in: %w[unknown] },
if: -> { package_file&.package&.debian_incoming? || package_file&.package&.processing? }
validates :file_type,
inclusion: { in: %w[source dsc deb udeb buildinfo changes ddeb] },
if: -> { package_file&.package&.debian_package? && !package_file&.package&.processing? }
validates :architecture,
presence: true,
format: { with: Gitlab::Regex.debian_architecture_regex },
if: :requires_architecture?
validates :architecture, absence: true, unless: :requires_architecture?
validates :component,
presence: true,
format: { with: Gitlab::Regex.debian_component_regex },
if: :requires_component?
validates :component, absence: true, unless: :requires_component?
validates :fields,
presence: true,
json_schema: { filename: "debian_fields" },
if: :requires_fields?
validates :fields, absence: true, unless: :requires_fields?
validates :architecture,
presence: true,
format: { with: Gitlab::Regex.debian_architecture_regex },
if: :requires_architecture?
validates :architecture, absence: true, unless: :requires_architecture?
private
validates :fields,
presence: true,
json_schema: { filename: "debian_fields" },
if: :requires_fields?
validates :fields, absence: true, unless: :requires_fields?
def valid_debian_package_type
return if package_file&.package&.debian?
scope :with_file_type, ->(file_type) do
where(file_type: file_type)
end
errors.add(:package_file, _('Package type must be Debian'))
end
private
def requires_architecture?
deb? || udeb? || ddeb?
end
def valid_debian_package_type
return if package_file&.package&.debian?
def requires_component?
source? || dsc? || requires_architecture? || buildinfo?
end
errors.add(:package_file, _('Package type must be Debian'))
end
def requires_fields?
dsc? || requires_architecture? || buildinfo? || changes?
def requires_architecture?
deb? || udeb? || ddeb?
end
def requires_component?
source? || dsc? || requires_architecture? || buildinfo?
end
def requires_fields?
dsc? || requires_architecture? || buildinfo? || changes?
end
end
end
end

View File

@ -86,7 +86,7 @@ class Packages::Package < ApplicationRecord
validates :name, format: { with: Gitlab::Regex.nuget_package_name_regex }, if: :nuget?
validates :name, format: { with: Gitlab::Regex.terraform_module_package_name_regex }, if: :terraform_module?
validates :name, format: { with: Gitlab::Regex.debian_package_name_regex }, if: :debian_package?
validates :name, inclusion: { in: %w[incoming] }, if: :debian_incoming?
validates :name, inclusion: { in: [Packages::Debian::INCOMING_PACKAGE_NAME] }, if: :debian_incoming?
validates :version, format: { with: Gitlab::Regex.nuget_version_regex }, if: :nuget?
validates :version, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :version, format: { with: Gitlab::Regex.maven_version_regex }, if: -> { version? && maven? }

View File

@ -85,6 +85,13 @@ class Packages::PackageFile < ApplicationRecord
.where(packages_debian_file_metadata: { architecture: architecture_name })
end
scope :with_debian_unknown_since, ->(updated_before) do
file_metadata = Packages::Debian::FileMetadatum.with_file_type(:unknown)
.updated_before(updated_before)
.where('packages_package_files.id = packages_debian_file_metadata.package_file_id')
where('EXISTS (?)', file_metadata.select(1))
end
scope :with_conan_package_reference, ->(conan_package_reference) do
joins(:conan_file_metadatum)
.where(packages_conan_file_metadata: { conan_package_reference: conan_package_reference })

View File

@ -4,7 +4,7 @@ module Packages
module Debian
class FindOrCreateIncomingService < ::Packages::CreatePackageService
def execute
find_or_create_package!(:debian, name: 'incoming', version: nil)
find_or_create_package!(:debian, name: ::Packages::Debian::INCOMING_PACKAGE_NAME, version: nil)
end
end
end

View File

@ -2,5 +2,5 @@ Reassigned Issue <%= @issue.iid %>
<%= url_for([@issue.project, @issue, { only_path: false }]) %>
Assignee changed <%= "from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%>
Assignee changed<%= " from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%>
to <%= "#{@issue.assignees.any? ? @issue.assignee_list : 'Unassigned'}" %>

View File

@ -3,7 +3,7 @@
- show_status_checks = @project.licensed_feature_available?(:external_status_checks)
- show_approvers = @project.licensed_feature_available?(:merge_request_approvers)
%section.settings.no-animate#branch-rules{ class: ('expanded' if expanded) }
%section.settings.no-animate#branch-rules{ class: ('expanded' if expanded), data: { qa_selector: 'branch_rules_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Branch rules')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do

View File

@ -1,3 +1,3 @@
.detail-page-description.py-2.gl-display-flex.gl-align-items-center.gl-flex-wrap{ class: "#{'is-merge-request' if moved_mr_sidebar_enabled? && !fluid_layout}" }
.detail-page-description.gl-pt-2.gl-pb-4.gl-display-flex.gl-align-items-center.gl-flex-wrap{ class: "#{'is-merge-request' if moved_mr_sidebar_enabled? && !fluid_layout}" }
= render 'shared/issuable/status_box', issuable: @merge_request
= merge_request_header(@project, @merge_request)

View File

@ -12,7 +12,7 @@
= c.body do
= _('The source project of this merge request has been removed.')
.detail-page-header.border-bottom-0.pb-0.gl-display-block{ class: "gl-md-display-flex! #{'is-merge-request' if moved_mr_sidebar_enabled? && !fluid_layout}" }
.detail-page-header.border-bottom-0.gl-display-block.gl-pt-5{ class: "gl-md-display-flex! #{'is-merge-request' if moved_mr_sidebar_enabled? && !fluid_layout}" }
.detail-page-header-body
.issuable-meta.gl-display-flex
#js-issuable-header-warnings{ data: { hidden: @merge_request.hidden?.to_s } }

View File

@ -579,6 +579,15 @@
:weight: 1
:idempotent: true
:tags: []
- :name: cronjob:packages_debian_cleanup_dangling_package_files
:worker_name: Packages::Debian::CleanupDanglingPackageFilesWorker
:feature_category: :package_registry
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: cronjob:pages_domain_removal_cron
:worker_name: PagesDomainRemovalCronWorker
:feature_category: :pages

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Packages
module Debian
class CleanupDanglingPackageFilesWorker
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
data_consistency :always
deduplicate :until_executed
idempotent!
feature_category :package_registry
THREE_HOUR = 3.hours.freeze
BATCH_TIMEOUT = 250.seconds.freeze
def perform
return unless Feature.enabled?(:debian_packages)
package_files = Packages::PackageFile.with_debian_unknown_since(THREE_HOUR.ago)
.installable
Packages::MarkPackageFilesForDestructionService.new(package_files)
.execute(batch_deadline: Time.zone.now + BATCH_TIMEOUT)
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e)
end
end
end
end

View File

@ -26,7 +26,8 @@ module Packages
::Packages::Debian::ProcessPackageFileService.new(package_file, distribution_name, component_name).execute
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e, package_file_id: @package_file_id,
distribution_name: @distribution_name, component_name: @component_name)
distribution_name: @distribution_name, component_name: @component_name)
package_file.update_column(:status, :error)
package_file.package.update_column(:status, :error)
end

View File

@ -1,8 +0,0 @@
---
name: incident_event_tags
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107194
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387647
milestone: '15.8'
type: development
group: group::respond
default_enabled: true

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390664
milestone: '15.9'
type: development
group: group::optimize
default_enabled: false
default_enabled: true

View File

@ -682,6 +682,9 @@ Settings.cron_jobs['users_migrate_records_to_ghost_user_in_batches_worker']['job
Settings.cron_jobs['ci_runners_stale_machines_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ci_runners_stale_machines_cleanup_worker']['cron'] ||= '36 4 * * *'
Settings.cron_jobs['ci_runners_stale_machines_cleanup_worker']['job_class'] = 'Ci::Runners::StaleMachinesCleanupCronWorker'
Settings.cron_jobs['cleanup_dangling_debian_package_files_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['cleanup_dangling_debian_package_files_worker']['cron'] ||= '20 21 * * *'
Settings.cron_jobs['cleanup_dangling_debian_package_files_worker']['job_class'] = 'Packages::Debian::CleanupDanglingPackageFilesWorker'
Gitlab.ee do
Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({})

View File

@ -324,6 +324,7 @@ if (USE_VUE3) {
Object.assign(alias, {
vue: '@vue/compat',
vuex: path.join(ROOT_PATH, 'app/assets/javascripts/lib/utils/vue3compat/vuex.js'),
'vue-apollo': path.join(ROOT_PATH, 'app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js'),
});
vueLoaderOptions.compiler = require.resolve('./vue3migration/compiler');

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexPackagesDebianFileMetadataWhenUnknown < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'i_pkgs_deb_file_meta_on_updated_at_package_file_id_when_unknown'
UNKNOWN = 1
def up
add_concurrent_index :packages_debian_file_metadata, [:updated_at, :package_file_id],
where: "file_type = #{UNKNOWN}", name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :packages_debian_file_metadata, name: INDEX_NAME
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddIndexToVulnerabilityFindingSignaturesOnSignatureSha < Gitlab::Database::Migration[2.1]
INDEX_NAME = 'index_vulnerability_finding_signatures_on_signature_sha'
disable_ddl_transaction!
def up
add_concurrent_index :vulnerability_finding_signatures, :signature_sha, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :vulnerability_finding_signatures, name: INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class DropSoftwareLicensesTempIndex < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
TABLE_NAME = :software_licenses
INDEX_NAME = 'tmp_index_for_software_licenses_spdx_identifier_cleanup'
def up
remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
end
def down
add_concurrent_index TABLE_NAME, :spdx_identifier, where: 'spdx_identifier IS NULL', name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
dd965c40fa107f487bda00d71dfdf40aabd013dd2cb05a4d9621cb1aa87d8060

View File

@ -0,0 +1 @@
70c83ed052082e1f37bd46c5abcb0b1b101ea08b719a68d8f39cb7545da97e61

View File

@ -0,0 +1 @@
9e9f4b2ed1a56fb3dd780cd22f82eab741639bb972c524984741d4f8cd799e5c

View File

@ -29059,6 +29059,8 @@ CREATE INDEX i_dast_profiles_tags_on_scanner_profiles_id ON dast_profiles_tags U
CREATE INDEX i_dast_scanner_profiles_tags_on_scanner_profiles_id ON dast_scanner_profiles_tags USING btree (dast_scanner_profile_id);
CREATE INDEX i_pkgs_deb_file_meta_on_updated_at_package_file_id_when_unknown ON packages_debian_file_metadata USING btree (updated_at, package_file_id) WHERE (file_type = 1);
CREATE UNIQUE INDEX i_pm_licenses_on_spdx_identifier ON pm_licenses USING btree (spdx_identifier);
CREATE UNIQUE INDEX i_pm_package_version_licenses_join_ids ON pm_package_version_licenses USING btree (pm_package_version_id, pm_license_id);
@ -32479,6 +32481,8 @@ CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON vulnerability_feedba
CREATE INDEX index_vulnerability_finding_signatures_on_finding_id ON vulnerability_finding_signatures USING btree (finding_id);
CREATE INDEX index_vulnerability_finding_signatures_on_signature_sha ON vulnerability_finding_signatures USING btree (signature_sha);
CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vulnerability_findings_remediations USING btree (vulnerability_remediation_id);
CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
@ -32755,8 +32759,6 @@ CREATE INDEX tmp_index_for_null_member_namespace_id ON members USING btree (memb
CREATE INDEX tmp_index_for_project_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Project'::text));
CREATE INDEX tmp_index_for_software_licenses_spdx_identifier_cleanup ON software_licenses USING btree (spdx_identifier) WHERE (spdx_identifier IS NULL);
CREATE INDEX tmp_index_members_on_state ON members USING btree (state) WHERE (state = 2);
CREATE INDEX tmp_index_migrated_container_registries ON container_repositories USING btree (project_id) WHERE ((migration_state = 'import_done'::text) OR (created_at >= '2022-01-23 00:00:00'::timestamp without time zone));

View File

@ -1,5 +1,5 @@
---
status: accepted
status: implemented
creation-date: "2021-02-08"
authors: [ "@abrandl" ]
coach: "@glopezfernandez"
@ -10,6 +10,11 @@ participating-stages: []
# Database Testing
**Notice:** This blueprints has been partially implemented. We still plan to
iterate on the tooling. The content below is a historical version of the
blueprint, written prior to incorporating database testing into our development
workflow.
We have identified [common themes of reverted migrations](https://gitlab.com/gitlab-org/gitlab/-/issues/233391) and discovered failed migrations breaking in both production and staging even when successfully tested in a developer environment. We have also experienced production incidents even with successful testing in staging. These failures are quite expensive: they can have a significant effect on availability, block deployments, and generate incident escalations. These escalations must be triaged and either reverted or fixed forward. Often, this can take place without the original author's involvement due to time zones and/or the criticality of the escalation. With our increased deployment speeds and stricter uptime requirements, the need for improving database testing is critical, particularly earlier in the development process (shift left).
From a developer's perspective, it is hard, if not unfeasible, to validate a migration on a large enough dataset before it goes into production.

View File

@ -78,7 +78,8 @@ see [epic 3559](https://gitlab.com/groups/gitlab-org/-/epics/3559).
### Allow access to your project with a job token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346298/) in GitLab 15.9. [Deployed behind the `:inbound_ci_scoped_job_token` feature flag](../../user/feature_flags.md), enabled by default.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346298/) in GitLab 15.9. [Deployed behind the `:inbound_ci_scoped_job_token` feature flag](../../user/feature_flags.md), enabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/346298/) in GitLab 15.10.
Create an **inbound** allowlist of projects which can access your project through
their `CI_JOB_TOKEN`.

View File

@ -113,10 +113,7 @@ Alternatively:
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8741) in GitLab 15.9 [with a flag](../../administration/feature_flags.md) named `incident_event_tags`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/387647) in GitLab 15.9.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/387647) in GitLab 15.10.
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `incident_event_tags`.
On GitLab.com, this feature is available.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/387647) in GitLab 15.11. Feature flag `incident_event_tags` removed.
[When creating an event using the form](#using-the-form) or editing it,
you can specify incident tags to capture relevant incident timestamps.

View File

@ -35,16 +35,17 @@ the most out of GitLab.
| [Take advantage of Git rebase](https://about.gitlab.com/blog/2022/10/06/take-advantage-of-git-rebase/)| Learn how to use the `rebase` command in your workflow. | |
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF of common Git commands. | |
## Plan your work in projects
## Plan and track your work
Your work takes place in a project, from creating code, to planning,
collaborating, and more.
Create a project to host your code, and plan your work using
issues, epics, and more.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
| [Create a project from a template](https://gitlab.com/projects/new#create_from_template) | Choose a project template and create a project with files to get you started. | |
| [Migrate to GitLab](../user/project/import/index.md) | If you are coming to GitLab from another platform, you can import or convert your projects. | |
| [Run an agile iteration](agile_sprint.md) | Use group, projects, and iterations to run an agile development iteration. |
| [Epics and Issue Boards](https://www.youtube.com/watch?v=I1bFIAQBHB8) | Find out how to use epics and issue boards for project management. | |
## Use CI/CD pipelines

View File

@ -4,7 +4,7 @@ group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Value Streams Dashboard **(ULTIMATE)**
# Value Streams Dashboard (Beta) **(ULTIMATE)**
> Introduced in GitLab 15.8 as a Closed [Beta](../../policy/alpha-beta-support.md#beta-features) feature [with a flag](../../administration/feature_flags.md) named `group_analytics_dashboards_page`. Disabled by default.

View File

@ -137,7 +137,7 @@ Create the group you want to import to and connect the source GitLab instance:
1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your source GitLab instance.
1. Select **Connect instance**.
### Select the groups to import
### Select the groups and projects to import
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385689) in GitLab 15.8, option to import groups with or without projects.
@ -220,12 +220,15 @@ Group items that are migrated to the destination GitLab instance include:
- Already exists in the destination GitLab instance.
- Has a public email in the source GitLab instance that matches a confirmed email in the destination GitLab instance.
### Migrated project items (beta)
### Migrated project items (Beta)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4 [with a flag](../../feature_flags.md) named `bulk_import_projects`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/339941) in GitLab 15.6.
> - `bulk_import_projects` feature flag [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/339941) in GitLab 15.10.
If you choose to migrate projects when you [select groups to migrate](#select-the-groups-and-projects-to-import),
project items are migrated with the projects.
The project items that are migrated depends on the version of GitLab you use on the destination. To determine if a
specific project item is migrated:

View File

@ -124,21 +124,50 @@ You can install a package from a GitLab project or instance:
- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
- **Project-level**: Use when you have few npm packages and they are not in the same GitLab group.
### Authenticate to the Package Registry
You must authenticate to the Package Registry to install a package from a private project.
No authentication is needed if the project is public.
To authenticate with `npm`:
```shell
npm config set -- //your_domain_name/:_authToken=your_token
```
With npm version 7 or earlier, use the full URL to the endpoint.
If you're installing:
- From the instance level:
```shell
npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
```
From the project level:
```shell
npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
```
In these examples:
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_project_id` is your project ID, found on the project's home page.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
NOTE:
Starting with npm version 8, you can [use a URI fragment instead of a full URL](https://docs.npmjs.com/cli/v8/configuring-npm/npmrc?v=true#auth-related-configuration)
in the `_authToken` parameter. However, [group-level endpoints](https://gitlab.com/gitlab-org/gitlab/-/issues/299834)
are not supported.
### Install from the instance level
WARNING:
To install a package from the instance level, the package must have been published following the scoped [naming convention](#naming-convention).
1. Authenticate to the Package Registry
If you would like to install a package from a private project, you would have to authenticate to the Package Registry. Skip this step if the project is not private.
```shell
npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
```
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
1. [Authenticate to the Package Registry](#authenticate-to-the-package-registry).
1. Set the registry
@ -158,17 +187,7 @@ To install a package from the instance level, the package must have been publish
### Install from the project level
1. Authenticate to the Package Registry
If you would like to install a package from a private project, you would have to authenticate to the Package Registry. Skip this step if the project is not private.
```shell
npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
```
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_project_id` is your project ID, found on the project's home page.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
1. [Authenticate to the Package Registry](#authenticate-to-the-package-registry).
1. Set the registry

View File

@ -4,7 +4,7 @@ group: Product Analytics
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Product analytics **(ULTIMATE)**
# Product analytics (Alpha) **(ULTIMATE)**
> - Introduced in GitLab 15.4 as an [Alpha](../../policy/alpha-beta-support.md#alpha-features) feature [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
> - `cube_api_proxy` revised to only reference the [Product Analytics API](../../api/product_analytics.md) in GitLab 15.6.

View File

@ -43,6 +43,7 @@ module.exports = (path, options = {}) => {
// Library wrappers
'^vuex$': '<rootDir>/app/assets/javascripts/lib/utils/vue3compat/vuex.js',
'^vue-apollo$': '<rootDir>/app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js',
});
Object.assign(globals, {
'vue-jest': {

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Sidebars
module Groups
module SuperSidebarMenus
class AnalyzeMenu < ::Sidebars::Menu
override :title
def title
s_('Navigation|Analyze')
end
override :sprite_icon
def sprite_icon
'chart'
end
override :configure_menu_items
def configure_menu_items
[
:cycle_analytics,
:ci_cd_analytics,
:contribution_analytics,
:devops_adoption,
:insights,
:issues_analytics,
:productivity_analytics,
:repository_analytics
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
module Sidebars
module Groups
module SuperSidebarMenus
class BuildMenu < ::Sidebars::Menu
override :title
def title
s_('Navigation|Build')
end
override :sprite_icon
def sprite_icon
'rocket'
end
override :configure_menu_items
def configure_menu_items
[
:runners
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Sidebars
module Groups
module SuperSidebarMenus
class ManageMenu < ::Sidebars::Menu
override :title
def title
s_('Navigation|Manage')
end
override :sprite_icon
def sprite_icon
'users'
end
override :configure_menu_items
def configure_menu_items
[
:activity,
:members,
:labels,
:milestones,
:iterations
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Sidebars
module Groups
module SuperSidebarMenus
class MonitorMenu < ::Sidebars::Menu
override :title
def title
s_('Navigation|Monitor')
end
override :sprite_icon
def sprite_icon
'monitor'
end
override :configure_menu_items
def configure_menu_items
[
:explore,
:datasources
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end
end

View File

@ -6,13 +6,23 @@ module Sidebars
class OperationsMenu < ::Sidebars::Menu
override :title
def title
_('Operations')
s_('Navigation|Operate')
end
override :sprite_icon
def sprite_icon
'deployments'
end
override :configure_menu_items
def configure_menu_items
[
:dependency_proxy,
:packages_registry,
:container_registry,
:group_kubernetes_clusters
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end

View File

@ -6,13 +6,25 @@ module Sidebars
class PlanMenu < ::Sidebars::Menu
override :title
def title
_('Plan')
s_('Navigation|Plan')
end
override :sprite_icon
def sprite_icon
'planning'
end
override :configure_menu_items
def configure_menu_items
[
:issue_boards,
:epic_boards,
:roadmap,
:group_wiki,
:crm_contacts,
:crm_organizations
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Sidebars
module Groups
module SuperSidebarMenus
class SecureMenu < ::Sidebars::Menu
override :title
def title
s_('Navigation|Secure')
end
override :sprite_icon
def sprite_icon
'shield'
end
override :configure_menu_items
def configure_menu_items
[
:audit_events,
:security_dashboard,
:vulnerability_report,
:compliance,
:scan_policies
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end
end

View File

@ -13,15 +13,16 @@ module Sidebars
@menus = []
add_menu(Sidebars::StaticMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::ManageMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::PlanMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::BuildMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::SecureMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::OperationsMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::MonitorMenu.new(context))
add_menu(Sidebars::Groups::SuperSidebarMenus::AnalyzeMenu.new(context))
pick_from_old_menus(old_menus)
insert_menu_before(
Sidebars::Groups::Menus::ObservabilityMenu,
Sidebars::Groups::SuperSidebarMenus::OperationsMenu.new(context)
)
insert_menu_before(
Sidebars::Groups::Menus::SettingsMenu,
Sidebars::UncategorizedMenu.new(context)

View File

@ -38,9 +38,9 @@ module Sidebars
{ controller: [:user, :gcp] }
end
override :pick_into_super_sidebar?
def pick_into_super_sidebar?
true
override :serialize_as_menu_item_args
def serialize_as_menu_item_args
nil
end
private
@ -57,6 +57,7 @@ module Sidebars
::Sidebars::MenuItem.new(
title: _('Metrics'),
link: project_metrics_dashboard_path(context.project),
super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
active_routes: { path: 'metrics_dashboard#show' },
container_html_options: { class: 'shortcuts-metrics' },
item_id: :metrics
@ -71,6 +72,7 @@ module Sidebars
::Sidebars::MenuItem.new(
title: _('Error Tracking'),
link: project_error_tracking_index_path(context.project),
super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
active_routes: { controller: :error_tracking },
item_id: :error_tracking
)
@ -84,6 +86,7 @@ module Sidebars
::Sidebars::MenuItem.new(
title: _('Alerts'),
link: project_alert_management_index_path(context.project),
super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
active_routes: { controller: :alert_management },
item_id: :alert_management
)
@ -97,6 +100,7 @@ module Sidebars
::Sidebars::MenuItem.new(
title: _('Incidents'),
link: project_incidents_path(context.project),
super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
active_routes: { controller: [:incidents, :incident_management] },
item_id: :incidents
)

View File

@ -13,6 +13,18 @@ module Sidebars
def sprite_icon
'monitor'
end
override :configure_menu_items
def configure_menu_items
[
:metrics,
:error_tracking,
:alert_management,
:incidents,
:on_call_schedules,
:escalation_policies
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
end
end

View File

@ -27313,6 +27313,9 @@ msgstr ""
msgid "MergeRequest|Compare %{target} and %{source}"
msgstr ""
msgid "MergeRequest|Encountered an issue while trying to fetch the single file diff."
msgstr ""
msgid "MergeRequest|Error dismissing suggestion popover. Please try again."
msgstr ""
@ -29225,6 +29228,9 @@ msgstr ""
msgid "NorthstarNavigation|Alpha"
msgstr ""
msgid "NorthstarNavigation|Beta"
msgstr ""
msgid "NorthstarNavigation|Could not update the new navigation preference. Please try again later."
msgstr ""

View File

@ -98,6 +98,8 @@
"@tiptap/pm": "^2.0.0-beta.220",
"@tiptap/suggestion": "^2.0.0-beta.220",
"@tiptap/vue-2": "2.0.0-beta.220",
"@vue/apollo-components": "^4.0.0-beta.4",
"@vue/apollo-option": "^4.0.0-beta.4",
"apollo-upload-client": "15.0.0",
"apollo3-cache-persist": "^0.14.1",
"autosize": "^5.0.1",

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
module QA
module Page
module Project
module Settings
class BranchRules < Page::Base
view 'app/assets/javascripts/projects/settings/repository/branch_rules/app.vue' do
element :add_branch_rule_button
end
view 'app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue' do
element :branch_content
element :details_button
end
def click_add_branch_rule
click_element(:add_branch_rule_button)
click_button('Create protected branch')
end
def navigate_to_branch_rules_details(branch_name)
within_element(:branch_content, branch_name: branch_name) do
click_element(:details_button)
end
end
end
end
end
end
end
QA::Page::Project::Settings::BranchRules.prepend_mod_with('Page::Project::Settings::BranchRules', namespace: QA)

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
module QA
module Page
module Project
module Settings
class BranchRulesDetails < Page::Base
view 'app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue' do
element :allowed_to_push_content
element :allowed_to_merge_content
end
view 'app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue' do
element :access_level_content
end
def has_allowed_to_push?(role)
within_element(:allowed_to_push_content) do
has_element?(:access_level_content, role: role)
end
end
def has_allowed_to_merge?(role)
within_element(:allowed_to_merge_content) do
has_element?(:access_level_content, role: role)
end
end
end
end
end
end
end
QA::Page::Project::Settings::BranchRulesDetails.prepend_mod_with( # rubocop:disable Cop/InjectEnterpriseEditionModule
'Page::Project::Settings::BranchRulesDetails', namespace: QA)

View File

@ -27,6 +27,10 @@ module QA
element :protected_tag_settings_content
end
view 'app/views/projects/branch_rules/_show.html.haml' do
element :branch_rules_content
end
def expand_deploy_tokens(&block)
expand_content(:deploy_tokens_settings_content) do
Settings::DeployTokens.perform(&block)
@ -57,6 +61,10 @@ module QA
end
end
def expand_branch_rules
expand_content(:branch_rules_content)
end
def expand_default_branch(&block)
within('#branch-defaults-settings') do
find('.btn-default').click do

View File

@ -26,6 +26,9 @@ module QA
file.name = "text-#{SecureRandom.hex(8)}.txt"
file.content = 'New file'
end
rescue StandardError => e
QA::Runtime::Logger.error("Full failure message: #{e.message}")
raise
end.not_to raise_error
end
@ -42,6 +45,9 @@ module QA
commit.commit_message = 'Add new file'
commit.add_files([{ file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' }])
end
rescue StandardError => e
QA::Runtime::Logger.error("Full failure message: #{e.message}")
raise
end.not_to raise_error
end
end

View File

@ -24,6 +24,9 @@ module QA
file.name = "text-#{SecureRandom.hex(8)}.txt"
file.content = 'New file'
end
rescue StandardError => e
QA::Runtime::Logger.error("Full failure message: #{e.message}")
raise
end.not_to raise_error
end
@ -37,6 +40,9 @@ module QA
commit.commit_message = 'Add new file'
commit.add_files([{ file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' }])
end
rescue StandardError => e
QA::Runtime::Logger.error("Full failure message: #{e.message}")
raise
end.not_to raise_error
end
end

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Create' do
describe 'Branch Rules Overview', product_group: :source_code, feature_flag: {
name: 'branch_rules',
scope: :project
} do
let(:branch_name) { 'new-branch' }
let(:allowed_to_push_role) { Resource::ProtectedBranch::Roles::NO_ONE }
let(:allowed_to_merge_role) { Resource::ProtectedBranch::Roles::MAINTAINERS }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'branch-rule-project'
project.initialize_with_readme = true
end
end
before do
Runtime::Feature.enable(:branch_rules, project: project)
Flow::Login.sign_in
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.branch = branch_name
commit.start_branch = project.default_branch
commit.commit_message = 'First commit'
commit.add_files([{ file_path: 'new_file.rb', content: '# new content' }])
end
end
after do
Runtime::Feature.disable(:branch_rules, project: project)
end
it 'adds a new branch rule', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/397587' do
project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings)
Page::Project::Settings::Repository.perform(&:expand_branch_rules)
Page::Project::Settings::BranchRules.perform(&:click_add_branch_rule)
Page::Project::Settings::ProtectedBranches.perform do |settings|
settings.select_branch(branch_name)
settings.select_allowed_to_push(roles: allowed_to_push_role)
settings.select_allowed_to_merge(roles: allowed_to_merge_role)
settings.protect_branch
end
Page::Project::Settings::Repository.perform(&:expand_branch_rules)
Page::Project::Settings::BranchRules.perform do |rules|
expect(rules).to have_content(branch_name)
rules.navigate_to_branch_rules_details(branch_name)
end
Page::Project::Settings::BranchRulesDetails.perform do |details|
aggregate_failures 'branch rules details' do
expect(details).to have_allowed_to_push(allowed_to_push_role[:description])
expect(details).to have_allowed_to_merge(allowed_to_merge_role[:description])
end
end
end
end
end
end

View File

@ -6,7 +6,7 @@ FactoryBot.define do
file_type { 'deb' }
component { 'main' }
architecture { 'amd64' }
fields { { 'a': 'b' } }
fields { { 'a' => 'b' } }
trait(:unknown) do
file_type { 'unknown' }
@ -32,19 +32,20 @@ FactoryBot.define do
'Source' => package_file.package.name,
'Binary' => 'sample-dev, libsample0, sample-udeb, sample-ddeb',
'Architecture' => 'any',
'Version': package_file.package.version,
'Version' => package_file.package.version,
'Maintainer' => "#{FFaker::Name.name} <#{FFaker::Internet.email}>",
'Homepage' => FFaker::Internet.http_url,
'Standards-Version' => '4.5.0',
'Build-Depends' => 'debhelper-compat (= 13)',
'Package-List' => <<~EOF.rstrip,
libsample0 deb libs optional arch=any',
'sample-ddeb deb libs optional arch=any',
sample-dev deb libdevel optional arch=any',
sample-udeb udeb libs optional arch=any',
EOF
'Package-List' => <<~PACKAGELIST.rstrip,
libsample0 deb libs optional arch=any
sample-ddeb deb libs optional arch=any
sample-dev deb libdevel optional arch=any
sample-udeb udeb libs optional arch=any
PACKAGELIST
'Checksums-Sha1' => "\n4a9cb2a7c77a68dc0fe54ba8ecef133a7c949e9d 964 sample_1.2.3~alpha2.tar.xz",
'Checksums-Sha256' => "\nc9d05185ca158bb804977fa9d7b922e8a0f644a2da41f99d2787dd61b1e2e2c5 964 sample_1.2.3~alpha2.tar.xz",
'Checksums-Sha256' =>
"\nc9d05185ca158bb804977fa9d7b922e8a0f644a2da41f99d2787dd61b1e2e2c5 964 sample_1.2.3~alpha2.tar.xz",
'Files' => "\nadc69e57cda38d9bb7c8d59cacfb6869 964 sample_1.2.3~alpha2.tar.xz"
}
end
@ -56,22 +57,22 @@ FactoryBot.define do
architecture { 'amd64' }
fields do
{
'Package' => 'libsample0',
'Source' => package_file.package.name,
'Version' => package_file.package.version,
'Architecture' => 'amd64',
'Maintainer' => "#{FFaker::Name.name} <#{FFaker::Internet.email}>",
'Installed-Size' => '7',
'Section' => 'libs',
'Priority' => 'optional',
'Multi-Arch' => 'same',
'Homepage' => FFaker::Internet.http_url,
'Description' => <<~EOF.rstrip
Some mostly empty lib
Used in GitLab tests.
'Package' => 'libsample0',
'Source' => package_file.package.name,
'Version' => package_file.package.version,
'Architecture' => 'amd64',
'Maintainer' => "#{FFaker::Name.name} <#{FFaker::Internet.email}>",
'Installed-Size' => '7',
'Section' => 'libs',
'Priority' => 'optional',
'Multi-Arch' => 'same',
'Homepage' => FFaker::Internet.http_url,
'Description' => <<~DESCRIPTION.rstrip
Some mostly empty lib
Used in GitLab tests.
Testing another paragraph.
EOF
Testing another paragraph.
DESCRIPTION
}
end
end
@ -93,12 +94,12 @@ FactoryBot.define do
'Priority' => 'optional',
'Multi-Arch' => 'same',
'Homepage' => FFaker::Internet.http_url,
'Description' => <<~EOF.rstrip
'Description' => <<~DESCRIPTION.rstrip
Some mostly empty development files
Used in GitLab tests.
Testing another paragraph.
EOF
DESCRIPTION
}
end
end
@ -107,28 +108,28 @@ FactoryBot.define do
file_type { 'udeb' }
component { 'main' }
architecture { 'amd64' }
fields { { 'a': 'b' } }
fields { { 'a' => 'b' } }
end
trait(:ddeb) do
file_type { 'ddeb' }
component { 'main' }
architecture { 'amd64' }
fields { { 'a': 'b' } }
fields { { 'a' => 'b' } }
end
trait(:buildinfo) do
file_type { 'buildinfo' }
component { 'main' }
architecture { nil }
fields { { 'Architecture': 'amd64 source' } }
fields { { 'Architecture' => 'amd64 source' } }
end
trait(:changes) do
file_type { 'changes' }
component { nil }
architecture { nil }
fields { { 'Architecture': 'source amd64' } }
fields { { 'Architecture' => 'source amd64' } }
end
end
end

View File

@ -215,6 +215,7 @@ FactoryBot.define do
end
trait(:keep) do
# do not override attributes
end
end

View File

@ -43,9 +43,7 @@ RSpec.describe 'Incident timeline events', :js, feature_category: :incident_mana
expect(page).to have_content(s_('Incident|No timeline items have been added yet.'))
end
it 'submits event data on save with feature flag on' do
stub_feature_flags(incident_event_tags: true)
it 'submits event data on save' do
# Add event
click_button(s_('Incident|Add new timeline event'))

View File

@ -294,7 +294,9 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
end
end
context 'with valid username/password' do
# Freeze time to prevent failures when time between code being entered and
# validated greater than otp_allowed_drift
context 'with valid username/password', :freeze_time do
let(:user) { create(:user, :two_factor) }
before do
@ -321,8 +323,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
end
context 'using one-time code' do
it 'allows login with valid code',
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/402322' do
it 'allows login with valid code' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
.and increment(:user_two_factor_authenticated_counter)
@ -348,8 +349,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
expect(page).to have_content('Invalid two-factor code')
end
it 'allows login with invalid code, then valid code',
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/402322' do
it 'allows login with invalid code, then valid code' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
.and increment(:user_two_factor_authenticated_counter)
@ -363,8 +363,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
expect(page).to have_current_path root_path, ignore_query: true
end
it 'triggers ActiveSession.cleanup for the user',
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/402322' do
it 'triggers ActiveSession.cleanup for the user' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
.and increment(:user_two_factor_authenticated_counter)
@ -421,8 +420,10 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
end
end
context 'when two factor authentication is required' do
it 'shows 2FA prompt after OAuth login', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/402615' do
# Freeze time to prevent failures when time between code being entered and
# validated greater than otp_allowed_drift
context 'when two factor authentication is required', :freeze_time do
it 'shows 2FA prompt after OAuth login' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
.and increment(:user_two_factor_authenticated_counter)
@ -613,23 +614,21 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions, feature_category: :system_
end
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
freeze_time do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
it 'redirects to two-factor configuration page', :freeze_time do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
gitlab_sign_in(user)
gitlab_sign_in(user)
expect(page).to have_current_path profile_two_factor_auth_path, ignore_query: true
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable '\
'Two-Factor Authentication for your account. '\
'You can leave Group 1 and leave Group 2. '\
'You need to do this '\
'before '\
"#{(Time.zone.now + 2.days).strftime("%a, %d %b %Y %H:%M:%S %z")}"
)
end
expect(page).to have_current_path profile_two_factor_auth_path, ignore_query: true
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable '\
'Two-Factor Authentication for your account. '\
'You can leave Group 1 and leave Group 2. '\
'You need to do this '\
'before '\
"#{(Time.zone.now + 2.days).strftime("%a, %d %b %Y %H:%M:%S %z")}"
)
end
it 'allows skipping two-factor configuration', :js do

View File

@ -10,6 +10,7 @@ import {
INLINE_DIFF_VIEW_TYPE,
PARALLEL_DIFF_VIEW_TYPE,
} from '~/diffs/constants';
import { LOAD_SINGLE_DIFF_FAILED } from '~/diffs/i18n';
import * as diffActions from '~/diffs/store/actions';
import * as types from '~/diffs/store/mutation_types';
import * as utils from '~/diffs/store/utils';
@ -994,6 +995,87 @@ describe('DiffsStoreActions', () => {
});
});
describe('goToFile', () => {
const getters = {};
const file = { path: 'path' };
const fileHash = 'test';
let state;
let dispatch;
let commit;
beforeEach(() => {
getters.isTreePathLoaded = () => false;
state = {
viewDiffsFileByFile: true,
treeEntries: {
path: {
fileHash,
},
},
};
commit = jest.fn();
dispatch = jest.fn().mockResolvedValue();
});
it('immediately defers to scrollToFile if the app is not in file-by-file mode', () => {
state.viewDiffsFileByFile = false;
diffActions.goToFile({ state, dispatch }, file);
expect(dispatch).toHaveBeenCalledWith('scrollToFile', file);
});
describe('when the app is in fileByFile mode', () => {
it('commits SET_CURRENT_DIFF_FILE', () => {
diffActions.goToFile({ state, commit, dispatch, getters }, file);
expect(commit).toHaveBeenCalledWith(types.SET_CURRENT_DIFF_FILE, fileHash);
});
it('does nothing more if the path has already been loaded', () => {
getters.isTreePathLoaded = () => true;
diffActions.goToFile({ state, dispatch, getters, commit }, file);
expect(commit).toHaveBeenCalledWith(types.SET_CURRENT_DIFF_FILE, fileHash);
expect(dispatch).toHaveBeenCalledTimes(0);
});
describe('when the tree entry has not been loaded', () => {
it('updates location hash', () => {
diffActions.goToFile({ state, commit, getters, dispatch }, file);
expect(document.location.hash).toBe('#test');
});
it('loads the file and then scrolls to it', async () => {
diffActions.goToFile({ state, commit, getters, dispatch }, file);
// Wait for the fetchFileByFile dispatch to return, to trigger scrollToFile
await waitForPromises();
expect(dispatch).toHaveBeenCalledWith('fetchFileByFile');
expect(dispatch).toHaveBeenCalledWith('scrollToFile', file);
expect(dispatch).toHaveBeenCalledTimes(2);
});
it('shows an alert when there was an error fetching the file', async () => {
dispatch = jest.fn().mockRejectedValue();
diffActions.goToFile({ state, commit, getters, dispatch }, file);
// Wait for the fetchFileByFile dispatch to return, to trigger the catch
await waitForPromises();
expect(createAlert).toHaveBeenCalledTimes(1);
expect(createAlert).toHaveBeenCalledWith({
message: expect.stringMatching(LOAD_SINGLE_DIFF_FAILED),
});
});
});
});
});
describe('scrollToFile', () => {
let commit;
const getters = { isVirtualScrollingEnabled: false };

View File

@ -86,7 +86,6 @@ describe('Create Timeline events', () => {
provide: {
fullPath: 'group/project',
issuableId: '1',
glFeatures: { incidentEventTags: true },
},
apolloProvider,
});

View File

@ -113,17 +113,7 @@ describe('Timeline events form', () => {
]);
});
describe('with incident_event_tag feature flag enabled', () => {
beforeEach(() => {
mountComponent(
{},
{},
{
incidentEventTags: true,
},
);
});
describe('Event Tags', () => {
describe('event tags listbox', () => {
it('should render option list from provided array', () => {
expect(findTagsListbox().props('items')).toEqual(mockTags);

View File

@ -35,6 +35,7 @@ describe('Ref selector component', () => {
const projectId = '8';
const totalBranchesCount = 123;
const totalTagsCount = 456;
const queryParams = { sort: 'updated_desc' };
let wrapper;
let branchesApiCallSpy;
@ -738,4 +739,25 @@ describe('Ref selector component', () => {
expect(lastCallProps.matches).toMatchObject(expectedMatches);
});
});
describe('when queryParam prop is present', () => {
it('passes params to a branches API call', () => {
createComponent({ propsData: { queryParams } });
return waitForRequests().then(() => {
expect(branchesApiCallSpy).toHaveBeenCalledWith(
expect.objectContaining({ params: { per_page: 20, search: '', sort: queryParams.sort } }),
);
});
});
it('does not pass params to tags API call', () => {
createComponent({ propsData: { queryParams } });
return waitForRequests().then(() => {
expect(tagsApiCallSpy).toHaveBeenCalledWith(
expect.objectContaining({ params: { per_page: 20, search: '' } }),
);
});
});
});
});

View File

@ -52,6 +52,13 @@ describe('Ref selector Vuex store actions', () => {
});
});
describe('setParams', () => {
it(`commits ${types.SET_PARAMS} with the provided params`, () => {
const params = { sort: 'updated_asc' };
testAction(actions.setParams, params, state, [{ type: types.SET_PARAMS, payload: params }]);
});
});
describe('search', () => {
it(`commits ${types.SET_QUERY} with the new search query`, () => {
const query = 'hello';

View File

@ -34,6 +34,7 @@ describe('Ref selector Vuex store mutations', () => {
error: null,
},
},
params: null,
selectedRef: null,
requestCount: 0,
});
@ -56,6 +57,15 @@ describe('Ref selector Vuex store mutations', () => {
});
});
describe(`${types.SET_PARAMS}`, () => {
it('sets the additional query params', () => {
const params = { sort: 'updated_desc' };
mutations[types.SET_PARAMS](state, params);
expect(state.params).toBe(params);
});
});
describe(`${types.SET_PROJECT_ID}`, () => {
it('updates the project ID', () => {
const newProjectId = '4';

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::SuperSidebarMenus::AnalyzeMenu, feature_category: :navigation do
subject { described_class.new({}) }
let(:items) { subject.instance_variable_get(:@items) }
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Analyze"))
expect(subject.sprite_icon).to eq("chart")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:cycle_analytics,
:ci_cd_analytics,
:contribution_analytics,
:devops_adoption,
:insights,
:issues_analytics,
:productivity_analytics,
:repository_analytics
])
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::SuperSidebarMenus::BuildMenu, feature_category: :navigation do
subject { described_class.new({}) }
let(:items) { subject.instance_variable_get(:@items) }
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Build"))
expect(subject.sprite_icon).to eq("rocket")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:runners
])
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::SuperSidebarMenus::ManageMenu, feature_category: :navigation do
subject { described_class.new({}) }
let(:items) { subject.instance_variable_get(:@items) }
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Manage"))
expect(subject.sprite_icon).to eq("users")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:activity,
:members,
:labels,
:milestones,
:iterations
])
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::SuperSidebarMenus::MonitorMenu, feature_category: :navigation do
subject { described_class.new({}) }
let(:items) { subject.instance_variable_get(:@items) }
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Monitor"))
expect(subject.sprite_icon).to eq("monitor")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:explore,
:datasources
])
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::SuperSidebarMenus::OperationsMenu, feature_category: :navigation do
subject { described_class.new({}) }
let(:items) { subject.instance_variable_get(:@items) }
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Operate"))
expect(subject.sprite_icon).to eq("deployments")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:dependency_proxy,
:packages_registry,
:container_registry,
:group_kubernetes_clusters
])
end
end

Some files were not shown because too many files have changed in this diff Show More