Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-29 15:12:58 +00:00
parent d2d9859ef4
commit 7f0fd430c2
195 changed files with 3274 additions and 960 deletions

View File

@ -2568,6 +2568,7 @@ Gitlab/BoundedContexts:
- 'ee/app/graphql/resolvers/vulnerabilities_grade_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerabilities_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb'
- 'ee/app/graphql/resolvers/vulnerability_filterable.rb'
- 'ee/app/graphql/subscriptions/ai_completion_response.rb'
- 'ee/app/graphql/types/access_levels/group_type.rb'
- 'ee/app/graphql/types/admin/cloud_licenses/current_license_type.rb'

View File

@ -193,19 +193,6 @@ Layout/ArgumentAlignment:
- 'lib/api/ci/resource_groups.rb'
- 'lib/api/ci/runner.rb'
- 'lib/api/ci/runners.rb'
- 'lib/api/ci/triggers.rb'
- 'lib/api/commit_statuses.rb'
- 'lib/api/commits.rb'
- 'lib/api/concerns/packages/debian_distribution_endpoints.rb'
- 'lib/api/concerns/packages/npm_endpoints.rb'
- 'lib/api/container_repositories.rb'
- 'lib/api/dependency_proxy.rb'
- 'lib/api/deploy_keys.rb'
- 'lib/api/deploy_tokens.rb'
- 'lib/api/deployments.rb'
- 'lib/api/entities/application.rb'
- 'lib/api/entities/application_statistics.rb'
- 'lib/api/entities/branch.rb'
- 'lib/api/entities/npm_package.rb'
- 'lib/api/entities/nuget/dependency_group.rb'
- 'lib/api/entities/nuget/package_metadata.rb'
@ -219,19 +206,6 @@ Layout/ArgumentAlignment:
- 'lib/api/entities/pull_mirror.rb'
- 'lib/api/entities/release.rb'
- 'lib/api/entities/resource_access_token.rb'
- 'lib/api/merge_requests.rb'
- 'lib/api/metrics/dashboard/annotations.rb'
- 'lib/api/metrics/user_starred_dashboards.rb'
- 'lib/api/milestone_responses.rb'
- 'lib/api/notes.rb'
- 'lib/api/nuget_project_packages.rb'
- 'lib/api/pages.rb'
- 'lib/api/pages_domains.rb'
- 'lib/api/pagination_params.rb'
- 'lib/api/personal_access_tokens.rb'
- 'lib/api/project_container_repositories.rb'
- 'lib/api/project_export.rb'
- 'lib/api/project_import.rb'
- 'lib/api/tags.rb'
- 'lib/api/terraform/state.rb'
- 'lib/api/topics.rb'

View File

@ -1,14 +0,0 @@
---
# Cop supports --autocorrect.
Layout/MultilineOperationIndentation:
Exclude:
- 'app/policies/project_policy.rb'
- 'app/serializers/deploy_keys/deploy_key_entity.rb'
- 'app/services/ci/create_downstream_pipeline_service.rb'
- 'app/services/git/branch_hooks_service.rb'
- 'app/services/groups/transfer_service.rb'
- 'app/services/issues/update_service.rb'
- 'app/services/labels/promote_service.rb'
- 'app/services/labels/transfer_service.rb'
- 'app/services/members/approve_access_request_service.rb'
- 'app/services/webauthn/authenticate_service.rb'

View File

@ -21,18 +21,6 @@ Lint/AmbiguousOperatorPrecedence:
- 'ee/app/services/geo/registry_consistency_service.rb'
- 'ee/app/services/vulnerabilities/create_service.rb'
- 'ee/lib/gitlab/expiring_subscription_message.rb'
- 'lib/banzai/filter/references/user_reference_filter.rb'
- 'lib/banzai/filter_array.rb'
- 'lib/extracts_ref.rb'
- 'lib/gitlab/chaos.rb'
- 'lib/gitlab/ci/config/normalizer/number_strategy.rb'
- 'lib/gitlab/console.rb'
- 'lib/gitlab/database/background_migration/batch_metrics.rb'
- 'lib/gitlab/database/background_migration/batched_migration.rb'
- 'lib/gitlab/database/migrations/background_migration_helpers.rb'
- 'lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb'
- 'lib/gitlab/database/postgres_hll/buckets.rb'
- 'lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb'
- 'spec/lib/gitlab/conan_token_spec.rb'
- 'spec/lib/gitlab/database/background_migration/batched_job_spec.rb'
- 'spec/lib/gitlab/database/batch_count_spec.rb'

View File

@ -1 +1 @@
v17.1.0-rc6
v17.1.0-rc7

11
Gemfile
View File

@ -140,7 +140,7 @@ gem 'rack-cors', '~> 2.0.1', require: 'rack/cors' # rubocop:todo Gemfile/Missing
gem 'graphql', '~> 2.3.3', feature_category: :api
gem 'graphql-docs', '~> 4.0.0', group: [:development, :test], feature_category: :api
gem 'graphiql-rails', '~> 1.8.0', feature_category: :api
gem 'apollo_upload_server', '~> 2.1.5', feature_category: :api
gem 'apollo_upload_server', '~> 2.1.6', feature_category: :api
gem 'graphlient', '~> 0.6.0', feature_category: :importers # Used by BulkImport feature (group::import)
# Generate Fake data
@ -536,7 +536,7 @@ group :test do
gem 'capybara', '~> 3.40' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'capybara-screenshot', '~> 1.0.26' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'selenium-webdriver', '~> 4.20', '>= 4.20.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'selenium-webdriver', '~> 4.21', '>= 4.21.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'graphlyte', '~> 1.0.0' # rubocop:todo Gemfile/MissingFeatureCategory
@ -682,9 +682,10 @@ gem 'telesignenterprise', '~> 2.2' # rubocop:todo Gemfile/MissingFeatureCategory
# BufferedIO patch
# Updating this version will require updating scripts/allowed_warnings.txt
gem 'net-protocol', '~> 0.1.3' # rubocop:todo Gemfile/MissingFeatureCategory
# Lock this until we make DNS rebinding work with the updated net-http:
# https://gitlab.com/gitlab-org/gitlab/-/issues/413528
gem 'net-http', '= 0.1.1' # rubocop:todo Gemfile/MissingFeatureCategory
# This is locked to 0.4.1 because we patch Net::HTTP#connect in
# gems/gitlab-http/lib/net_http/connect_patch.rb.
gem 'net-http', '= 0.4.1', feature_category: :shared
gem 'duo_api', '~> 1.3' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -21,7 +21,7 @@
{"name":"aliyun-sdk","version":"0.8.0","platform":"ruby","checksum":"65915d3f9b528082253d1f9ad0e4d13d6b552933fe49251c68c6915cd4d75b9d"},
{"name":"amatch","version":"0.4.1","platform":"ruby","checksum":"d3ff15226a2e627c72802e94579db829e5e10c96cf89d329494caec5889145f7"},
{"name":"android_key_attestation","version":"0.3.0","platform":"ruby","checksum":"467eb01a99d2bb48ef9cf24cc13712669d7056cba5a52d009554ff037560570b"},
{"name":"apollo_upload_server","version":"2.1.5","platform":"ruby","checksum":"0f66bea96bdf7ce8b7278712ebafc8a26b82864ea6541213b58d9b3f673413a5"},
{"name":"apollo_upload_server","version":"2.1.6","platform":"ruby","checksum":"dcec4072258e6518b0b82e03b485efbddde946813543c14184fc81952d6bcdb2"},
{"name":"app_store_connect","version":"0.29.0","platform":"ruby","checksum":"01d7a923825a4221892099acb5a72f86f6ee7d8aa95815d3c459ba6816ea430f"},
{"name":"arr-pm","version":"0.0.12","platform":"ruby","checksum":"fdff482f75239239201f4d667d93424412639aad0b3b0ad4d827e7c637e0ad39"},
{"name":"asciidoctor","version":"2.0.18","platform":"ruby","checksum":"bbd1e1d16deed8db94bf9624b9f4474fac32d9ca7225d377f076c08d9adde387"},
@ -395,7 +395,7 @@
{"name":"nap","version":"1.1.0","platform":"ruby","checksum":"949691660f9d041d75be611bb2a8d2fd559c467537deac241f4097d9b5eea576"},
{"name":"neighbor","version":"0.3.2","platform":"ruby","checksum":"b795bbcc24b1b9ae82d9f7e97a3461b0b3607d24a85a7acbed776bd498e7eba8"},
{"name":"nenv","version":"0.3.0","platform":"ruby","checksum":"d9de6d8fb7072228463bf61843159419c969edb34b3cef51832b516ae7972765"},
{"name":"net-http","version":"0.1.1","platform":"ruby","checksum":"75a4e109b6f9af32fad0e98a6180c47aceb415927ca3bd70c8fc3e7dbbabbe86"},
{"name":"net-http","version":"0.4.1","platform":"ruby","checksum":"a96efc5ea18bcb9715e24dda4159d10f67ff0345c8a980d04630028055b2c282"},
{"name":"net-http-persistent","version":"4.0.1","platform":"ruby","checksum":"2752f4cce05fd1c45e0537c6f3a98fa5a4899efd5f88e63c104ed5f05cbddef9"},
{"name":"net-imap","version":"0.3.4","platform":"ruby","checksum":"a82a59e2a429433dc54cae5a8b2979ffe49da8c66085740811bfa337dc3729b5"},
{"name":"net-ldap","version":"0.17.1","platform":"ruby","checksum":"52571b55f9157120833ac1667f2969ce0139251811d0a9b64657c1c135069cf9"},
@ -617,7 +617,7 @@
{"name":"sawyer","version":"0.9.2","platform":"ruby","checksum":"fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca"},
{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"4.20.1","platform":"ruby","checksum":"560ca00d45bed16d661089da674290ce81564949888daa1f8659fe77fd39a2ac"},
{"name":"selenium-webdriver","version":"4.21.1","platform":"ruby","checksum":"c30b64014532fc5156c60797985f839f36adbe60ff4653e7112b008dc1c83263"},
{"name":"semver_dialects","version":"2.0.2","platform":"ruby","checksum":"60059c9f416f931b5212d862fad2879d6b9affb8e0b9afb0d91b793639c116fe"},
{"name":"sentry-rails","version":"5.17.3","platform":"ruby","checksum":"017771c42d739c0ad2213a581ca9d005cf543227bc13662cd1ca9909f2429459"},
{"name":"sentry-ruby","version":"5.17.3","platform":"ruby","checksum":"61791a4b0bb0f95cd87aceeaa1efa6d4ab34d64236c9d5df820478adfe2fbbfc"},

View File

@ -48,6 +48,7 @@ PATH
concurrent-ruby (~> 1.2)
httparty (~> 0.21.0)
ipaddress (~> 0.8.3)
net-http (= 0.4.1)
railties (~> 7)
PATH
@ -278,7 +279,7 @@ GEM
mize
tins (~> 1.0)
android_key_attestation (0.3.0)
apollo_upload_server (2.1.5)
apollo_upload_server (2.1.6)
actionpack (>= 6.1.6)
graphql (>= 1.8)
app_store_connect (0.29.0)
@ -1115,8 +1116,7 @@ GEM
neighbor (0.3.2)
activerecord (>= 6.1)
nenv (0.3.0)
net-http (0.1.1)
net-protocol
net-http (0.4.1)
uri
net-http-persistent (4.0.1)
connection_pool (~> 2.2)
@ -1643,7 +1643,7 @@ GEM
seed-fu (2.3.7)
activerecord (>= 3.1)
activesupport (>= 3.1)
selenium-webdriver (4.20.1)
selenium-webdriver (4.21.1)
base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
@ -1912,7 +1912,7 @@ DEPENDENCIES
acts-as-taggable-on (~> 10.0)
addressable (~> 2.8)
akismet (~> 3.0)
apollo_upload_server (~> 2.1.5)
apollo_upload_server (~> 2.1.6)
app_store_connect
arr-pm (~> 0.0.12)
asciidoctor (~> 2.0.18)
@ -2095,7 +2095,7 @@ DEPENDENCIES
minitest (~> 5.11.0)
multi_json (~> 1.14.1)
neighbor (~> 0.3.2)
net-http (= 0.1.1)
net-http (= 0.4.1)
net-ldap (~> 0.17.1)
net-ntp
net-protocol (~> 0.1.3)
@ -2203,7 +2203,7 @@ DEPENDENCIES
sanitize (~> 6.0.2)
sd_notify (~> 0.1.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 4.20, >= 4.20.1)
selenium-webdriver (~> 4.21, >= 4.21.1)
semver_dialects (~> 2.0, >= 2.0.2)
sentry-rails (~> 5.17.3)
sentry-ruby (~> 5.17.3)

View File

@ -1,5 +1,5 @@
<script>
import { GlForm, GlFormFields, GlButton, GlLink, GlAlert } from '@gitlab/ui';
import { GlForm, GlFormFields, GlButton, GlLink, GlAlert, GlSprintf } from '@gitlab/ui';
import { formValidators } from '@gitlab/ui/dist/utils';
import { __, s__, sprintf } from '~/locale';
import { slugify } from '~/lib/utils/text_utility';
@ -7,26 +7,36 @@ import VisibilityLevelRadioButtons from '~/visibility_level/components/visibilit
import { GROUP_VISIBILITY_LEVEL_DESCRIPTIONS } from '~/visibility_level/constants';
import { restrictedVisibilityLevelsMessage } from '~/visibility_level/utils';
import { helpPagePath } from '~/helpers/help_page_helper';
import { FORM_FIELD_NAME, FORM_FIELD_PATH, FORM_FIELD_VISIBILITY_LEVEL } from '../constants';
import HelpPageLink from '~/vue_shared/components/help_page_link/help_page_link.vue';
import {
FORM_FIELD_NAME,
FORM_FIELD_PATH,
FORM_FIELD_ID,
FORM_FIELD_VISIBILITY_LEVEL,
} from '../constants';
import GroupPathField from './group_path_field.vue';
export default {
name: 'NewGroupForm',
name: 'NewEditForm',
components: {
GlForm,
GlFormFields,
GlButton,
GlLink,
GlAlert,
GlSprintf,
GroupPathField,
VisibilityLevelRadioButtons,
HelpPageLink,
},
i18n: {
cancel: __('Cancel'),
submitButtonText: __('Create group'),
warningForUsingDotInName: s__(
'Groups|Your group name must not contain a period if you intend to use SCIM integration, as it can lead to errors.',
),
warningForChangingUrl: s__(
'Groups|Changing group URL can have unintended side effects. %{linkStart}Learn more%{linkEnd}.',
),
},
GROUP_VISIBILITY_LEVEL_DESCRIPTIONS,
formId: 'organization-new-group-form',
@ -39,6 +49,11 @@ export default {
type: String,
required: true,
},
submitButtonText: {
type: String,
required: false,
default: __('Create group'),
},
cancelPath: {
type: String,
required: true,
@ -66,7 +81,7 @@ export default {
},
data() {
return {
hasPathBeenManuallySet: false,
hasPathBeenManuallySet: this.initialFormValues[FORM_FIELD_PATH],
isPathLoading: false,
formValues: this.initialFormValues,
};
@ -114,6 +129,20 @@ export default {
: null,
},
},
...(this.isEditing
? {
[FORM_FIELD_ID]: {
label: s__('Groups|Group ID'),
groupAttrs: {
class: 'gl-w-full',
},
inputAttrs: {
class: 'gl-md-form-input-lg',
disabled: true,
},
},
}
: {}),
[FORM_FIELD_VISIBILITY_LEVEL]: {
label: __('Visibility level'),
labelDescription: {
@ -132,6 +161,9 @@ export default {
},
};
},
isEditing() {
return this.initialFormValues[FORM_FIELD_ID];
},
},
watch: {
[`formValues.${FORM_FIELD_NAME}`](newName) {
@ -184,6 +216,22 @@ export default {
@loading-change="onPathLoading"
/>
</template>
<template v-if="isEditing" #after(path)>
<gl-alert
class="gl-mb-5"
:dismissible="false"
variant="warning"
data-testid="changing-url-alert"
>
<gl-sprintf :message="$options.i18n.warningForChangingUrl">
<template #link="{ content }">
<help-page-link href="user/group/manage" anchor="change-a-groups-path">{{
content
}}</help-page-link>
</template>
</gl-sprintf>
</gl-alert>
</template>
<template #input(visibilityLevel)="{ value, input }">
<visibility-level-radio-buttons
:checked="value"
@ -207,7 +255,7 @@ export default {
:loading="loading"
class="js-no-auto-disable"
data-testid="submit-button"
>{{ $options.i18n.submitButtonText }}</gl-button
>{{ submitButtonText }}</gl-button
>
<gl-button :href="cancelPath">{{ $options.i18n.cancel }}</gl-button>
</div>

View File

@ -72,4 +72,5 @@ export const OVERVIEW_TABS_ARCHIVED_PROJECTS_SORTING_ITEMS = [
export const FORM_FIELD_NAME = 'name';
export const FORM_FIELD_PATH = 'path';
export const FORM_FIELD_ID = 'id';
export const FORM_FIELD_VISIBILITY_LEVEL = 'visibilityLevel';

View File

@ -1,5 +1,5 @@
<script>
import { GlAccordion, GlAccordionItem, GlIcon } from '@gitlab/ui';
import { GlAccordion, GlAccordionItem, GlIcon, GlLink } from '@gitlab/ui';
import { BULK_IMPORT_STATIC_ITEMS } from '~/import/constants';
import { STATUSES } from '../constants';
@ -11,9 +11,15 @@ export default {
GlAccordion,
GlAccordionItem,
GlIcon,
GlLink,
},
props: {
failuresHref: {
type: String,
required: false,
default: '',
},
stats: {
type: Object,
required: false,
@ -63,7 +69,7 @@ export default {
<template>
<gl-accordion :header-level="3">
<gl-accordion-item :title="__('Details')">
<gl-accordion-item :title="__('View details')">
<ul class="gl-p-0 gl-mb-3 gl-list-none gl-font-sm">
<li v-for="key in Object.keys(stats)" :key="key" data-testid="import-stat-item">
<div class="gl-display-flex gl-w-28 gl-align-items-center">
@ -73,6 +79,9 @@ export default {
</div>
</li>
</ul>
<gl-link v-if="failuresHref" :href="failuresHref"
>{{ s__('Import|Show errors') }} &gt;</gl-link
>
</gl-accordion-item>
</gl-accordion>
</template>

View File

@ -1,6 +1,5 @@
<script>
import { GlAccordion, GlAccordionItem, GlBadge, GlIcon, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
import { STATISTIC_ITEMS } from '~/import/constants';
import { STATUSES, STATUS_ICON_MAP } from '../constants';
@ -99,9 +98,6 @@ export default {
},
STATISTIC_ITEMS,
i18n: {
detailsLink: s__('Import|See failures'),
},
};
</script>
@ -129,9 +125,9 @@ export default {
</div>
</li>
</ul>
<gl-link v-if="showDetails" :href="detailsPathForProject">{{
$options.i18n.detailsLink
}}</gl-link>
<gl-link v-if="showDetails" :href="detailsPathForProject"
>{{ s__('Import|Show errors') }} &gt;</gl-link
>
</gl-accordion-item>
</gl-accordion>
</div>

View File

@ -26,5 +26,5 @@ export default {
};
</script>
<template>
<gl-link :href="historyPathWithId">{{ __('View details') }}</gl-link>
<gl-link :href="historyPathWithId">{{ s__('BulkImport|Migration details') }} &gt;</gl-link>
</template>

View File

@ -8,28 +8,17 @@ export default {
GlLink,
},
inject: {
detailsPath: {
default: undefined,
},
},
props: {
id: {
type: Number,
required: false,
default: null,
},
entityId: {
type: Number,
required: false,
default: null,
},
hasFailures: {
type: Boolean,
required: false,
default: false,
},
failuresHref: {
type: String,
required: false,
default: '',
},
status: {
type: String,
required: true,
@ -48,20 +37,6 @@ export default {
return STATUS_ICON_MAP[this.status];
},
showDetails() {
return Boolean(this.detailsPathWithId) && this.hasFailures;
},
detailsPathWithId() {
if (!this.id || !this.entityId || !this.detailsPath) {
return null;
}
return this.detailsPath
.replace(':id', encodeURIComponent(this.id))
.replace(':entity_id', encodeURIComponent(this.entityId));
},
},
};
</script>
@ -72,8 +47,8 @@ export default {
{{ mappedStatus.text }}
</gl-badge>
<div v-if="showDetails" class="gl-mt-2">
<gl-link :href="detailsPathWithId">{{ s__('Import|See failures') }}</gl-link>
<div v-if="failuresHref" class="gl-mt-2">
<gl-link :href="failuresHref">{{ s__('Import|Show errors') }} &gt;</gl-link>
</div>
</div>
</template>

View File

@ -637,7 +637,6 @@ export default {
gitlabLogo: window.gon.gitlab_logo,
PAGE_SIZES,
permissionsHelpPath: helpPagePath('user/permissions', { anchor: 'group-members-permissions' }),
betaFeatureHelpPath: helpPagePath('policy/experiment-beta-support', { anchor: 'beta-features' }),
popoverOptions: { title: __('What is listed here?') },
i18n,
LOCAL_STORAGE_KEY: 'gl-bulk-imports-status-page-size-v1',
@ -803,23 +802,6 @@ export default {
data-testid="import-projects-warning"
/>
</span>
<span class="gl-ml-3">
<gl-icon name="information-o" :size="12" class="gl-text-blue-600" />
<gl-sprintf
:message="
s__(
'BulkImport|Importing projects is a %{docsLinkStart}Beta%{docsLinkEnd} feature.',
)
"
>
<template #docsLink="{ content }"
><gl-link :href="$options.betaFeatureHelpPath" target="_blank">{{
content
}}</gl-link></template
>
</gl-sprintf>
</span>
</div>
<gl-table
ref="table"

View File

@ -38,8 +38,9 @@ function parseDatasetToProps(data) {
jiraIssueTransitionAutomatic,
jiraIssueTransitionId,
artifactRegistryPath,
personalAccessTokensPath,
workloadIdentityFederationPath,
workloadIdentityFederationProjectNumber,
workloadIdentityPoolId,
wlifIssuer,
redirectTo,
upgradeSlackUrl,
@ -74,7 +75,6 @@ function parseDatasetToProps(data) {
testPath,
resetPath,
formPath,
personalAccessTokensPath,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
@ -95,6 +95,8 @@ function parseDatasetToProps(data) {
googleArtifactManagementProps: {
artifactRegistryPath,
workloadIdentityFederationPath,
workloadIdentityFederationProjectNumber,
workloadIdentityPoolId,
},
learnMorePath,
aboutPricingUrl,

View File

@ -1,8 +1,12 @@
<script>
import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
import UserDate from '~/vue_shared/components/user_date.vue';
export default {
components: { UserDate },
components: { UserDate, GlIcon },
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
member: {
type: Object,
@ -21,17 +25,35 @@ export default {
</script>
<template>
<div>
<div v-if="userCreated">
<strong>{{ s__('Members|User created') }}:</strong>
<div class="gl-display-flex gl-flex-direction-column gl-gap-2">
<div v-if="userCreated" class="gl-display-flex gl-gap-3">
<gl-icon
ref="userCreated"
v-gl-tooltip.${userCreated}
class="gl-ml-2 gl-mr-n2 gl-text-gray-500"
name="assignee"
:title="s__('Members|User created')"
/>
<user-date :date="userCreated" />
</div>
<div v-if="member.createdAt">
<strong>{{ s__('Members|Access granted') }}:</strong>
<div v-if="member.createdAt" class="gl-display-flex gl-gap-3">
<gl-icon
ref="memberCreatedAt"
v-gl-tooltip.${memberCreatedAt}
class="gl-text-gray-500"
name="check"
:title="s__('Members|Access granted')"
/>
<user-date :date="member.createdAt" />
</div>
<div v-if="lastActivity">
<strong>{{ s__('Members|Last activity') }}:</strong>
<div v-if="lastActivity" class="gl-display-flex gl-gap-3">
<gl-icon
ref="lastActivity"
v-gl-tooltip.${lastActivity}
class="gl-text-gray-500"
name="hourglass"
:title="s__('Members|Last activity')"
/>
<user-date :date="lastActivity" />
</div>
</div>

View File

@ -1,11 +1,19 @@
<script>
import { GlEmptyState, GlPagination, GlLoadingIcon } from '@gitlab/ui';
import { GlEmptyState, GlPagination, GlLoadingIcon, GlFilteredSearchToken } from '@gitlab/ui';
import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-activity-md.svg?url';
import { DEFAULT_PER_PAGE } from '~/api';
import { __, s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { createAlert } from '~/alert';
import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import ContributionEvents from '~/contribution_events/components/contribution_events.vue';
import {
CONTRIBUTION_TYPE_FILTER_TYPE,
RECENT_SEARCHES_STORAGE_KEY,
FILTERED_SEARCH_NAMESPACE,
convertTokensToFilter,
} from '../filters';
export default {
name: 'OrganizationsActivityApp',
@ -14,8 +22,10 @@ export default {
eventsErrorMessage: s__(
'Organization|An error occurred loading the activity. Please refresh the page to try again.',
),
contributionType: __('Contribution type'),
},
components: {
FilteredSearch,
ContributionEvents,
GlEmptyState,
GlPagination,
@ -26,12 +36,21 @@ export default {
type: String,
required: true,
},
organizationActivityEventTypes: {
type: Array,
required: true,
},
organizationActivityAllEvent: {
type: String,
required: true,
},
},
data() {
return {
events: [],
eventsLoading: false,
page: 1,
eventFilter: this.organizationActivityAllEvent,
hasNextPage: false,
};
},
@ -46,6 +65,19 @@ export default {
prevPage() {
return this.page - 1;
},
availableTokens() {
return [
{
title: this.$options.i18n.contributionType,
icon: 'comparison',
type: CONTRIBUTION_TYPE_FILTER_TYPE,
token: GlFilteredSearchToken,
unique: true,
operators: OPERATORS_IS,
options: this.organizationActivityEventTypes,
},
];
},
},
async mounted() {
this.fetchEvents();
@ -55,6 +87,10 @@ export default {
// Offset is starts at 0, but pages start at page 1. We need to use -1 logic to generate offset
return (page - 1) * DEFAULT_PER_PAGE;
},
onSearchFilter(tokens) {
this.eventFilter = convertTokensToFilter(tokens) || this.organizationActivityAllEvent;
this.fetchEvents();
},
async fetchEvents(page = 1) {
this.eventsLoading = true;
@ -62,7 +98,11 @@ export default {
const {
data: { events, has_next_page: hasNextPage },
} = await axios.get(this.organizationActivityPath, {
params: { offset: this.calculateOffset(page), limit: DEFAULT_PER_PAGE },
params: {
offset: this.calculateOffset(page),
limit: DEFAULT_PER_PAGE,
event_filter: this.eventFilter,
},
});
this.hasNextPage = hasNextPage;
@ -76,26 +116,39 @@ export default {
},
},
EMPTY_STATE_SVG_URL,
RECENT_SEARCHES_STORAGE_KEY,
FILTERED_SEARCH_NAMESPACE,
};
</script>
<template>
<div v-if="!showEmptyState">
<contribution-events :events="events" />
<gl-loading-icon v-if="eventsLoading" size="md" class="gl-mb-3" />
<gl-pagination
v-else
:value="page"
:prev-page="prevPage"
:next-page="nextPage"
align="center"
class="gl-w-full"
@input="fetchEvents"
<div>
<filtered-search
:recent-searches-storage-key="$options.RECENT_SEARCHES_STORAGE_KEY"
:namespace="$options.FILTERED_SEARCH_NAMESPACE"
:tokens="availableTokens"
terms-as-tokens
@onFilter="onSearchFilter"
/>
<template v-if="!showEmptyState">
<contribution-events :events="events" />
<gl-loading-icon v-if="eventsLoading" size="md" class="gl-mb-3" />
<gl-pagination
v-else
:value="page"
:prev-page="prevPage"
:next-page="nextPage"
align="center"
class="gl-w-full"
@input="fetchEvents"
/>
</template>
<gl-empty-state
v-else-if="showEmptyState"
:title="$options.i18n.emptyStateTitle"
:svg-path="$options.EMPTY_STATE_SVG_URL"
/>
</div>
<gl-empty-state
v-else-if="showEmptyState"
:title="$options.i18n.emptyStateTitle"
:svg-path="$options.EMPTY_STATE_SVG_URL"
/>
</template>

View File

@ -0,0 +1,16 @@
import { processFilters } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
export const CONTRIBUTION_TYPE_FILTER_TYPE = 'contribution_type';
export const RECENT_SEARCHES_STORAGE_KEY = 'recent-organizations-activity-filter-search';
export const FILTERED_SEARCH_NAMESPACE = 'organizations-activity-filter-search';
export const convertTokensToFilter = (tokens) => {
const processedFilters = processFilters(tokens);
if (!processedFilters[CONTRIBUTION_TYPE_FILTER_TYPE]) {
return null;
}
return processedFilters[CONTRIBUTION_TYPE_FILTER_TYPE][0]?.value;
};

View File

@ -8,7 +8,11 @@ export const initOrganizationsActivity = () => {
const {
dataset: { appData },
} = el;
const { organizationActivityPath } = convertObjectPropsToCamelCase(JSON.parse(appData));
const {
organizationActivityPath,
organizationActivityEventTypes,
organizationActivityAllEvent,
} = convertObjectPropsToCamelCase(JSON.parse(appData));
return new Vue({
el,
@ -17,6 +21,8 @@ export const initOrganizationsActivity = () => {
return createElement(OrganizationsActivityApp, {
props: {
organizationActivityPath,
organizationActivityEventTypes,
organizationActivityAllEvent,
},
});
},

View File

@ -1,14 +1,26 @@
<script>
import { GlSprintf } from '@gitlab/ui';
import NewEditForm from '~/groups/components/new_edit_form.vue';
import { __ } from '~/locale';
export default {
name: 'OrganizationGroupsEditApp',
components: { GlSprintf },
components: { GlSprintf, NewEditForm },
i18n: {
pageTitle: __('Edit group: %{group_name}'),
submitButtonText: __('Save changes'),
},
inject: ['group'],
inject: [
'group',
'basePath',
'groupsAndProjectsOrganizationPath',
'groupsOrganizationPath',
'availableVisibilityLevels',
'restrictedVisibilityLevels',
'defaultVisibilityLevel',
'pathMaxlength',
'pathPattern',
],
};
</script>
@ -19,5 +31,16 @@ export default {
<template #group_name>{{ group.fullName }}</template>
</gl-sprintf>
</h1>
<new-edit-form
:loading="false"
:base-path="basePath"
:path-maxlength="pathMaxlength"
:path-pattern="pathPattern"
:submit-button-text="$options.i18n.submitButtonText"
:cancel-path="groupsAndProjectsOrganizationPath"
:available-visibility-levels="availableVisibilityLevels"
:restricted-visibility-levels="restrictedVisibilityLevels"
:initial-form-values="group"
/>
</div>
</template>

View File

@ -11,13 +11,31 @@ export const initOrganizationsGroupsEdit = () => {
const {
dataset: { appData },
} = el;
const { group } = convertObjectPropsToCamelCase(JSON.parse(appData), { deep: true });
const {
group,
basePath,
groupsAndProjectsOrganizationPath,
groupsOrganizationPath,
availableVisibilityLevels,
restrictedVisibilityLevels,
defaultVisibilityLevel,
pathMaxlength,
pathPattern,
} = convertObjectPropsToCamelCase(JSON.parse(appData), { deep: true });
return new Vue({
el,
name: 'OrganizationGroupsEditRoot',
provide: {
group,
basePath,
groupsAndProjectsOrganizationPath,
groupsOrganizationPath,
availableVisibilityLevels,
restrictedVisibilityLevels,
defaultVisibilityLevel,
pathMaxlength,
pathPattern,
},
render(createElement) {
return createElement(App);

View File

@ -3,7 +3,7 @@ import { GlSprintf, GlLink } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import axios from '~/lib/utils/axios_utils';
import NewGroupForm from '~/groups/components/new_group_form.vue';
import NewEditForm from '~/groups/components/new_edit_form.vue';
import { FORM_FIELD_NAME, FORM_FIELD_PATH, FORM_FIELD_VISIBILITY_LEVEL } from '~/groups/constants';
import { VISIBILITY_LEVELS_INTEGER_TO_STRING } from '~/visibility_level/constants';
import { createAlert } from '~/alert';
@ -29,13 +29,12 @@ export default {
components: {
GlLink,
GlSprintf,
NewGroupForm,
NewEditForm,
},
inject: [
'basePath',
'groupsAndProjectsOrganizationPath',
'groupsOrganizationPath',
'mattermostEnabled',
'availableVisibilityLevels',
'restrictedVisibilityLevels',
'defaultVisibilityLevel',
@ -106,7 +105,7 @@ export default {
</template>
</gl-sprintf>
</p>
<new-group-form
<new-edit-form
:loading="loading"
:base-path="basePath"
:path-maxlength="pathMaxlength"

View File

@ -17,7 +17,6 @@ export const initOrganizationsGroupsNew = () => {
basePath,
groupsAndProjectsOrganizationPath,
groupsOrganizationPath,
mattermostEnabled,
availableVisibilityLevels,
restrictedVisibilityLevels,
defaultVisibilityLevel,
@ -37,7 +36,6 @@ export const initOrganizationsGroupsNew = () => {
basePath,
groupsAndProjectsOrganizationPath,
groupsOrganizationPath,
mattermostEnabled,
availableVisibilityLevels,
restrictedVisibilityLevels,
defaultVisibilityLevel,

View File

@ -24,7 +24,7 @@ import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { isImporting } from '../utils';
import { isFailed, isImporting } from '../utils';
import { DEFAULT_ERROR } from '../utils/error_messages';
const DEFAULT_PER_PAGE = 20;
@ -59,7 +59,14 @@ export default {
GlTooltip,
},
inject: ['realtimeChangesPath'],
inject: {
detailsPath: {
default: undefined,
},
realtimeChangesPath: {
default: undefined,
},
},
props: {
id: {
@ -211,6 +218,28 @@ export default {
return !isEmpty(item.stats);
},
showFailuresLinkInStatus(item) {
if (isFailed(item.status)) {
return true;
}
// Import has failures but no stats
if (item.has_failures && !this.hasStats(item)) {
return true;
}
return false;
},
failuresLinkHref(item) {
if (!item.has_failures) {
return '';
}
return this.detailsPath
.replace(':id', encodeURIComponent(item.bulk_import_id))
.replace(':entity_id', encodeURIComponent(item.id));
},
getEntityTooltip(item) {
switch (item.entity_type) {
case WORKSPACE_PROJECT:
@ -238,7 +267,7 @@ export default {
<div>
<h1 class="gl-font-size-h1 gl-my-0 gl-py-4 gl-display-flex gl-align-items-center gl-gap-3">
<img :src="$options.gitlabLogo" :alt="__('GitLab Logo')" class="gl-w-6 gl-h-6" />
<span>{{ s__('BulkImport|Direct transfer history') }}</span>
<span>{{ s__('BulkImport|Migration history') }}</span>
</h1>
<gl-loading-icon v-if="loading" size="lg" class="gl-mt-5" />
@ -272,13 +301,13 @@ export default {
<template #cell(status)="{ value, item }">
<div>
<import-status
:id="item.bulk_import_id"
:entity-id="item.id"
:has-failures="item.has_failures"
:failures-href="showFailuresLinkInStatus(item) ? failuresLinkHref(item) : null"
:status="value"
/>
<import-stats
v-if="hasStats(item)"
:failures-href="failuresLinkHref(item)"
:stats="item.stats"
:stats-mapping="$options.BULK_IMPORT_STATIC_ITEMS"
:status="value"

View File

@ -1,5 +1,9 @@
import { STATUSES } from '~/import_entities/constants';
export function isFailed(status) {
return status === STATUSES.FAILED;
}
export function isImporting(status) {
return [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.CREATED, STATUSES.STARTED].includes(
status,

View File

@ -0,0 +1,5 @@
mutation removeBlobs($projectPath: ID!, $blobOids: [String!]!) {
projectBlobsRemove(input: { projectPath: $projectPath, blobOids: $blobOids }) {
errors
}
}

View File

@ -1,12 +1,20 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import RemoveBlobs from '~/projects/settings/repository/maintenance/remove_blobs.vue';
export default function mountRepositoryMaintenance() {
const removeBlobsEl = document.querySelector('.js-maintenance-remove-blobs');
if (!removeBlobsEl) return false;
const { projectPath, housekeepingPath } = removeBlobsEl.dataset;
return new Vue({
el: removeBlobsEl,
apolloProvider: new VueApollo({
defaultClient: createDefaultClient(),
}),
provide: { projectPath, housekeepingPath },
render(createElement) {
return createElement(RemoveBlobs);
},

View File

@ -1,9 +1,14 @@
<script>
import { GlButton, GlDrawer, GlLink, GlFormTextarea, GlModal } from '@gitlab/ui';
import { GlButton, GlDrawer, GlLink, GlFormTextarea, GlModal, GlFormInput } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import { helpPagePath } from '~/helpers/help_page_helper';
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
import { getContentWrapperHeight } from '~/lib/utils/dom_utils';
import { s__ } from '~/locale';
import { createAlert, VARIANT_WARNING } from '~/alert';
import removeBlobsMutation from './graphql/mutations/remove_blobs.mutation.graphql';
export const BLOB_OID_LENGTH = 40;
const i18n = {
removeBlobs: s__('ProjectMaintenance|Remove blobs'),
@ -18,22 +23,52 @@ const i18n = {
modalContent: s__(
'ProjectMaintenance|Removing blobs by ID cannot be undone. Are you sure you want to continue?',
),
modalConfirm: s__('ProjectMaintenance|Enter the following to confirm:'),
removeBlobsError: s__('ProjectMaintenance|Something went wrong while removing blobs.'),
successAlertTitle: s__('ProjectMaintenance|Blobs removed'),
successAlertContent: s__(
'ProjectMaintenance|Run housekeeping to remove old versions from repository.',
),
successAlertButtonText: s__('ProjectMaintenance|Go to housekeeping'),
};
export default {
i18n,
DRAWER_Z_INDEX,
removeBlobsHelpLink: helpPagePath('/user/project/repository/reducing_the_repo_size_using_git'),
removeBlobsHelpLink: helpPagePath('/user/project/repository/reducing_the_repo_size_using_git', {
anchor: 'repository-cleanup',
}),
modalCancel: { text: i18n.modalCancelText },
modalPrimary: { text: i18n.modalPrimaryText, attributes: { variant: 'danger' } },
components: { GlButton, GlDrawer, GlLink, GlFormTextarea, GlModal },
components: { GlButton, GlDrawer, GlLink, GlFormTextarea, GlModal, GlFormInput },
inject: { projectPath: { default: '' }, housekeepingPath: { default: '' } },
data() {
return { isDrawerOpen: false, blobIDs: null, showConfirmationModal: false };
return {
isDrawerOpen: false,
blobIDs: null,
showConfirmationModal: false,
confirmInput: null,
isLoading: false,
};
},
computed: {
getDrawerHeaderHeight() {
return getContentWrapperHeight();
},
blobOids() {
return this.blobIDs?.split('\n') || [];
},
isValid() {
return this.blobOids.length && this.blobOids.every((s) => s.length >= BLOB_OID_LENGTH);
},
modalPrimary() {
return {
text: i18n.modalPrimaryText,
attributes: { variant: 'danger', disabled: !this.isConfirmEnabled },
};
},
isConfirmEnabled() {
return this.confirmInput === this.projectPath;
},
},
methods: {
openDrawer() {
@ -47,8 +82,47 @@ export default {
this.showConfirmationModal = true;
},
removeBlobsConfirm() {
// TODO (follow-up MR): submit mutation + show alert/toast...
this.closeDrawer();
this.isLoading = true;
this.$apollo
.mutate({
mutation: removeBlobsMutation,
variables: {
blobOids: this.blobOids,
projectPath: this.projectPath,
},
})
.then(({ data: { projectBlobsRemove: { errors } = {} } = {} }) => {
this.isLoading = false;
if (errors?.length) {
this.handleError();
return;
}
this.closeDrawer();
this.generateSuccessAlert();
})
.catch(() => {
this.isLoading = false;
this.handleError();
});
},
generateSuccessAlert() {
createAlert({
title: i18n.successAlertTitle,
message: i18n.successAlertContent,
variant: VARIANT_WARNING,
primaryButton: {
text: i18n.successAlertButtonText,
clickHandler: () => this.navigateToHousekeeping(),
},
});
},
navigateToHousekeeping() {
visitUrl(this.housekeepingPath);
},
handleError() {
createAlert({ message: i18n.removeBlobsError, captureError: true });
},
},
};
@ -56,9 +130,14 @@ export default {
<template>
<div>
<gl-button class="gl-mb-6" data-testid="drawer-trigger" @click="openDrawer">{{
$options.i18n.removeBlobs
}}</gl-button>
<gl-button
class="gl-mb-6"
category="secondary"
variant="danger"
data-testid="drawer-trigger"
@click="openDrawer"
>{{ $options.i18n.removeBlobs }}</gl-button
>
<gl-drawer
:header-height="getDrawerHeaderHeight"
@ -82,6 +161,7 @@ export default {
id="blobs"
v-model.trim="blobIDs"
class="!gl-font-monospace gl-mb-3"
:disabled="isLoading"
autofocus
/>
@ -90,7 +170,8 @@ export default {
<gl-button
data-testid="remove-blobs"
variant="danger"
:disabled="!blobIDs"
:disabled="!isValid"
:loading="isLoading"
@click="removeBlobs"
>{{ $options.i18n.removeBlobs }}</gl-button
>
@ -102,10 +183,15 @@ export default {
:title="$options.i18n.removeBlobs"
modal-id="remove-blobs-confirmation-modal"
:action-cancel="$options.modalCancel"
:action-primary="$options.modalPrimary"
:action-primary="modalPrimary"
@primary="removeBlobsConfirm"
>
{{ $options.i18n.modalContent }}
<p>{{ $options.i18n.modalContent }}</p>
<p class="gl-mb-0">{{ $options.i18n.modalConfirm }}</p>
<code>{{ projectPath }}</code>
<gl-form-input v-model="confirmInput" class="gl-mt-2 gl-max-w-34" />
</gl-modal>
</div>
</template>

View File

@ -463,6 +463,13 @@ export default {
</section>
<section :class="workItemBodyClass">
<work-item-loading v-if="workItemLoading" />
<gl-empty-state
v-if="error"
:title="$options.i18n.fetchErrorTitle"
:description="error"
:svg-path="noAccessSvgPath"
:svg-height="null"
/>
<template v-else>
<div class="gl-sm-display-none! gl-display-flex">
<gl-button
@ -644,13 +651,6 @@ export default {
@has-notes="updateHasNotes"
@openReportAbuse="openReportAbuseDrawer"
/>
<gl-empty-state
v-if="error"
:title="$options.i18n.fetchErrorTitle"
:description="error"
:svg-path="noAccessSvgPath"
:svg-height="null"
/>
</div>
</template>
</section>

View File

@ -52,23 +52,14 @@ module Organizations
def organization_groups_new_app_data(organization)
{
base_path: root_url,
groups_and_projects_organization_path:
groups_and_projects_organization_path(organization, { display: 'groups' }),
groups_organization_path: groups_organization_path(organization),
mattermost_enabled: Gitlab.config.mattermost.enabled,
available_visibility_levels: available_visibility_levels(Group),
restricted_visibility_levels: restricted_visibility_levels,
default_visibility_level: default_group_visibility,
path_maxlength: ::Namespace::URL_MAX_LENGTH,
path_pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS
}.to_json
default_visibility_level: default_group_visibility
}.merge(shared_organization_groups_app_data(organization)).to_json
end
def organization_groups_edit_app_data(group)
def organization_groups_edit_app_data(organization, group)
{
group: group.slice(:full_name)
}.to_json
group: group.slice(:id, :full_name, :name, :visibility_level, :path)
}.merge(shared_organization_groups_app_data(organization)).to_json
end
def admin_organizations_index_app_data
@ -85,7 +76,9 @@ module Organizations
def organization_activity_app_data(organization)
{
organization_activity_path: activity_organization_path(organization, { format: :json })
organization_activity_path: activity_organization_path(organization, { format: :json }),
organization_activity_event_types: organization_activity_event_types,
organization_activity_all_event: EventFilter::ALL
}.to_json
end
@ -119,6 +112,19 @@ module Organizations
}
end
def shared_organization_groups_app_data(organization)
{
base_path: root_url,
groups_and_projects_organization_path:
groups_and_projects_organization_path(organization, { display: 'groups' }),
groups_organization_path: groups_organization_path(organization),
available_visibility_levels: available_visibility_levels(Group),
restricted_visibility_levels: restricted_visibility_levels,
path_maxlength: ::Namespace::URL_MAX_LENGTH,
path_pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS
}
end
# See UsersHelper#admin_users_paths for inspiration to this method
def organizations_users_paths
{
@ -133,5 +139,40 @@ module Organizations
def association_counts(organization)
Organizations::OrganizationAssociationCounter.new(organization: organization, current_user: current_user).execute
end
def organization_activity_event_types
[
{
title: _('Comments'),
value: EventFilter::COMMENTS
},
{
title: _('Designs'),
value: EventFilter::DESIGNS
},
{
title: _('Issue events'),
value: EventFilter::ISSUE
},
{
title: _('Merge events'),
value: EventFilter::MERGED
},
{
title: _('Push events'),
value: EventFilter::PUSH
},
{
title: _('Team'),
value: EventFilter::TEAM
},
{
title: _('Wiki'),
value: EventFilter::WIKI
}
]
end
end
end
Organizations::OrganizationHelper.prepend_mod_with('Organizations::OrganizationHelper')

View File

@ -53,6 +53,24 @@ module VisibilityLevelHelper
!form_model.visibility_level_allowed?(level)
end
def disallowed_visibility_level_by_parent?(form_model, level)
return false unless form_model.respond_to?(:visibility_level_allowed_by_parent?)
!form_model.visibility_level_allowed_by_parent?(level)
end
def disallowed_visibility_level_by_projects?(form_model, level)
return false unless form_model.respond_to?(:visibility_level_allowed_by_projects?)
!form_model.visibility_level_allowed_by_projects?(level)
end
def disallowed_visibility_level_by_sub_groups?(form_model, level)
return false unless form_model.respond_to?(:visibility_level_allowed_by_sub_groups?)
!form_model.visibility_level_allowed_by_sub_groups?(level)
end
# Visibility level can be restricted in two ways:
#
# 1. The group permissions (e.g. a subgroup is private, which requires
@ -69,6 +87,10 @@ module VisibilityLevelHelper
[requested_level, max_allowed_visibility_level(form_model)].min
end
def all_visibility_levels
Gitlab::VisibilityLevel.values
end
def available_visibility_levels(form_model)
Gitlab::VisibilityLevel.values.reject do |level|
disallowed_visibility_level?(form_model, level) ||
@ -76,6 +98,15 @@ module VisibilityLevelHelper
end
end
def disabled_visibility_level?(form_model, level)
disallowed_visibility_level?(form_model, level) ||
restricted_visibility_level?(level)
end
def restricted_visibility_level?(level)
restricted_visibility_levels.include?(level)
end
def snippets_selected_visibility_level(visibility_levels, selected)
visibility_levels.find { |level| level == selected } || visibility_levels.min
end

View File

@ -17,6 +17,7 @@ module Members
validates :new_access_level, presence: true
validates :user, presence: true
validates :member_namespace, presence: true
validates :metadata, json_schema: { filename: "members_approval_request_metadata" }
end
end

View File

@ -14,6 +14,8 @@ class NamespaceSetting < ApplicationRecord
cascading_attr :toggle_security_policy_custom_ci
cascading_attr :math_rendering_limits_enabled
scope :for_namespaces, ->(namespaces) { where(namespace: namespaces) }
belongs_to :namespace, inverse_of: :namespace_settings
enum jobs_to_be_done: { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5 }, _suffix: true

View File

@ -47,6 +47,9 @@ class Project < ApplicationRecord
include CrossDatabaseIgnoredTables
include UseSqlFunctionForPrimaryKeyLookups
include Importable
include SafelyChangeColumnDefault
columns_changing_default :organization_id
ignore_column :emails_disabled, remove_with: '16.3', remove_after: '2023-08-22'

View File

@ -80,8 +80,8 @@ class ProjectPolicy < BasePolicy
condition(:container_registry_disabled) do
if user.is_a?(DeployToken)
(!user.read_registry? && !user.write_registry?) ||
user.revoked? ||
!project.container_registry_enabled?
user.revoked? ||
!project.container_registry_enabled?
else
!access_allowed_to?(:container_registry)
end
@ -1103,7 +1103,7 @@ class ProjectPolicy < BasePolicy
false
when ProjectFeature::PRIVATE
can?(:read_all_resources) ||
can?(:read_all_organization_resources) ||
can?(:read_all_organization_resources) ||
team_access_level >= ProjectFeature.required_minimum_access_level(feature)
else
true

View File

@ -5,7 +5,7 @@ module DeployKeys
expose :deploy_keys_projects, using: DeployKeysProjectEntity do |deploy_key|
deploy_key.deploy_keys_projects.select do |deploy_key_project|
!deploy_key_project.project&.pending_delete? &&
(allowed_to_read_project?(deploy_key_project.project) || options[:user].can_admin_all_resources?)
(allowed_to_read_project?(deploy_key_project.project) || options[:user].can_admin_all_resources?)
end
end

View File

@ -121,7 +121,7 @@ module Ci
def can_create_downstream_pipeline?(target_ref)
can?(current_user, :update_pipeline, project) &&
can?(current_user, :create_pipeline, downstream_project) &&
can_update_branch?(target_ref)
can_update_branch?(target_ref)
end
def can_update_branch?(target_ref)

View File

@ -50,7 +50,7 @@ module Git
strong_memoize(:commits_count) do
next threshold_commits.count if
strong_memoized?(:threshold_commits) &&
threshold_commits.count <= PROCESS_COMMIT_LIMIT
threshold_commits.count <= PROCESS_COMMIT_LIMIT
if creating_default_branch?
project.repository.commit_count_for_ref(ref)

View File

@ -147,7 +147,7 @@ module Groups
def transfer_to_subgroup?
@new_parent_group && \
@group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id)
@group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id)
end
def valid_policies?

View File

@ -100,8 +100,8 @@ module Issues
target_project = params.delete(:target_project)
return unless target_project &&
issue.can_move?(current_user, target_project) &&
target_project != issue.project
issue.can_move?(current_user, target_project) &&
target_project != issue.project
update(issue)
Issues::MoveService.new(container: project, current_user: current_user).execute(issue, target_project)

View File

@ -7,7 +7,7 @@ module Labels
# rubocop: disable CodeReuse/ActiveRecord
def execute(label)
return unless project.group &&
label.is_a?(ProjectLabel)
label.is_a?(ProjectLabel)
ProjectLabel.transaction do
# use the existing group label if it exists

View File

@ -17,7 +17,7 @@ module Labels
# rubocop: disable CodeReuse/ActiveRecord
link_ids = group_labels_applied_to_issues.pluck("label_links.id") +
group_labels_applied_to_merge_requests.pluck("label_links.id")
group_labels_applied_to_merge_requests.pluck("label_links.id")
# rubocop: disable CodeReuse/ActiveRecord
Label.transaction do

View File

@ -25,7 +25,7 @@ module Members
raise Gitlab::Access::AccessDeniedError unless can_approve_access_requester?(access_requester)
if approving_member_with_owner_access_level?(access_requester) &&
cannot_assign_owner_responsibilities_to_member_in_project?(access_requester)
cannot_assign_owner_responsibilities_to_member_in_project?(access_requester)
raise Gitlab::Access::AccessDeniedError
end
end

View File

@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"expires_at": {
"type": "string",
"oneOf": [
{
"format": "date"
},
{
"pattern": "^$"
}
]
},
"member_role_id": {
"type": [
"number",
"null"
]
}
},
"additionalProperties": true
}

View File

@ -25,6 +25,8 @@
.settings-content
= render 'account_and_limit'
= render_if_exists 'admin/application_settings/ai_powered'
%section.settings.as-import-export.no-animate#js-import-export-settings{ class: ('expanded' if expanded_by_default?), data: { testid: 'admin-import-export-settings' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
@ -120,6 +122,5 @@
= render_if_exists 'admin/application_settings/add_license'
= render 'admin/application_settings/jira_connect'
= render 'admin/application_settings/slack'
= render_if_exists 'admin/application_settings/ai_powered'
= render 'admin/application_settings/security_txt', expanded: expanded_by_default?
= render_if_exists 'admin/application_settings/analytics'

View File

@ -1,6 +1,6 @@
- add_to_breadcrumbs _('New group'), new_group_path
- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
- add_to_breadcrumbs s_('BulkImport|Direct transfer history'), history_import_bulk_imports_path
- add_to_breadcrumbs s_('BulkImport|Migration history'), history_import_bulk_imports_path
- add_to_breadcrumbs @bulk_import.id, history_import_bulk_import_path(@bulk_import.id)
- page_title format(s_('Import|Failures for %{id}'), id: @bulk_import_entity.full_path_with_fallback)

View File

@ -1,9 +1,10 @@
- add_to_breadcrumbs _('New group'), new_group_path
- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
- if params[:id].present?
- add_to_breadcrumbs s_('BulkImport|Direct transfer history'), history_import_bulk_imports_path
- breadcrumb_title params[:id]
- page_title s_('BulkImport|Direct transfer history')
- if @bulk_import.present?
- add_to_breadcrumbs s_('BulkImport|Migration history'), history_import_bulk_imports_path
- breadcrumb_title @bulk_import.id
- page_title s_('BulkImport|Migration history')
- add_page_specific_style 'page_bundles/import'
#import-history-mount-element{ data: { id: params[:id], details_path: failures_import_bulk_import_path(':id', ':entity_id'), realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }
#import-history-mount-element{ data: { id: @bulk_import&.id, details_path: failures_import_bulk_import_path(':id', ':entity_id'), realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }

View File

@ -2,4 +2,4 @@
- add_to_breadcrumbs _('Groups and projects'), groups_and_projects_organization_path(@organization, { display: 'groups' })
- add_to_breadcrumbs @group.name, group_path(@group)
#js-organizations-groups-edit{ data: { app_data: organization_groups_edit_app_data(@group) } }
#js-organizations-groups-edit{ data: { app_data: organization_groups_edit_app_data(@organization, @group) } }

View File

@ -1,8 +1,8 @@
%h5.gl-heading-4= s_('ProjectMaintenance|Remove blobs')
%p.gl-text-secondary
= s_('ProjectMaintenance|Provide a list of blob object IDs to be removed.')
= link_to s_('ProjectMaintenance|How do I get a list of object IDs?'), help_page_path('user/project/repository/reducing_the_repo_size_using_git')
= link_to s_('ProjectMaintenance|How do I get a list of object IDs?'), help_page_path('user/project/repository/reducing_the_repo_size_using_git', anchor: 'repository-cleanup')
%p.gl-text-secondary
= s_('ProjectMaintenance|Housekeeping will need to be triggered manually afterwards to remove old versions of the file.')
.js-maintenance-remove-blobs
.js-maintenance-remove-blobs{ data: { project_path: @project.full_path, housekeeping_path: edit_project_path(@project, anchor: 'js-project-advanced-settings') } }

View File

@ -8,12 +8,12 @@
%p.gl-text-secondary.gl-pb-3
= s_('ProjectMaintenance|Manage repository storage and cleanup.')
.settings-content
= render Pajamas::AlertComponent.new(variant: :default, alert_options: { class: 'gl-mb-5' }, dismissible: false) do |c|
= render Pajamas::AlertComponent.new(variant: :danger, alert_options: { class: 'gl-mb-5' }, dismissible: false) do |c|
- c.with_body do
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
- docs_link_start = link_start % { url: help_page_path('user/project/settings/import_export') }
- link_end = '</a>'.html_safe
= s_('ProjectMaintenance| To ensure that a full backup is available in case changes need to be restored, you should make an %{docs_link_start}export of the project%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end }
= s_('ProjectMaintenance|To ensure that a full backup is available in case changes need to be restored, you should make an %{docs_link_start}export of the project%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end }
- if current_user.can?(:owner_access, @project) && Feature.enabled?(:rewrite_history_ui, @project)
= render "projects/maintenance/remove_blobs"

View File

@ -1,17 +1,30 @@
- available_visibility_levels = available_visibility_levels(form_model)
- selected_level = snippets_selected_visibility_level(available_visibility_levels, selected_level)
- all_visibility_levels.each do |level|
- disabled_visibility_level_icon_with_popover = capture do
- if disabled_visibility_level?(form_model, level)
- popover_content = capture do
- if restricted_visibility_level?(level)
= s_('VisibilityLevel|This visibility level has been restricted by your administrator.')
- elsif disallowed_visibility_level_by_parent?(form_model, level)
= s_('VisibilityLevel|This visibility level is not allowed because the parent group has a more restrictive visibility level.')
- elsif disallowed_visibility_level_by_projects?(form_model, level) || disallowed_visibility_level_by_sub_groups?(form_model, level)
- learn_more_link_start = '<a href="https://docs.gitlab.com/ee/user/public_access" target="_blank" rel="noopener noreferrer">'.html_safe # rubocop:disable Gitlab/DocUrl -- Not referencing this rails application; it is referencing another doc
- learn_more_link_end = '</a>'.html_safe
= s_('VisibilityLevel|This visibility level is not allowed because a child of %{group_name} has a less restrictive visibility level. %{learn_more_link_start}Learn more%{learn_more_link_end}.').html_safe % { group_name: form_model.name, learn_more_link_start: learn_more_link_start, learn_more_link_end: learn_more_link_end }
- available_visibility_levels.each do |level|
%span{
data: {
testid: 'visibility-level-not-allowed-popover',
container: 'body',
content: popover_content,
html: 'true',
title: _('Visibility level not allowed'),
toggle: 'popover',
triggers: 'hover' }
}
= sprite_icon('lock')
= form.gitlab_ui_radio_component model_method, level,
"#{visibility_level_icon(level)} #{visibility_level_label(level)}".html_safe,
help_text: '<span class="option-description">%{visibility_level_description}</span><span class="option-disabled-reason"></span>'.html_safe % { visibility_level_description: visibility_level_description(level, form_model)},
radio_options: { checked: (selected_level == level), data: { track_label: "blank_project", track_action: "activate_form_input", track_property: "#{model_method}_#{level}", track_value: "" } },
"#{visibility_level_icon(level)} #{visibility_level_label(level)} #{disabled_visibility_level_icon_with_popover}".html_safe,
help_text: '<span class="option-description text-muted">%{visibility_level_description}</span><span class="option-disabled-reason">%{option_disabled_reason}</span>'.html_safe % { visibility_level_description: visibility_level_description(level, form_model), option_disabled_reason: 'Not allowed by administrators' },
radio_options: { checked: (selected_level == level), disabled: disabled_visibility_level?(form_model, level), data: { track_label: "blank_project", track_action: "activate_form_input", track_property: "#{model_method}_#{level}", track_value: "" } },
label_options: { class: 'js-visibility-level-radio' }
.text-muted
- if all_visibility_levels_restricted?
= _('Visibility settings have been disabled by the administrator.')
- elsif multiple_visibility_levels_restricted?
= _('Other visibility settings have been disabled by the administrator.')

View File

@ -1,8 +0,0 @@
---
name: product_analytics_dashboards
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115177
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/398653
milestone: '15.11'
type: development
group: group::product analytics
default_enabled: true

View File

@ -517,6 +517,8 @@
- 1
- - ml_experiment_tracking_associate_ml_candidate_to_package
- 1
- - namespaces_cascade_duo_features_enabled
- 1
- - namespaces_free_user_cap_group_over_limit_notification
- 1
- - namespaces_process_sync_events

View File

@ -7,4 +7,5 @@ feature_categories:
description: Contains periodically snapshotted database record counts.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62797
milestone: '14.0'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
exempt_from_sharding: true # Cell local table storing instance level-aggregations

View File

@ -0,0 +1,9 @@
---
migration_job_name: BackfillBoardsEpicUserPreferencesGroupId
description: Backfills sharding key `boards_epic_user_preferences.group_id` from `epics`.
feature_category: portfolio_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153684
milestone: '17.1'
queued_migration_version: 20240521094917
finalize_after: '2024-06-22'
finalized_by: # version of the migration that finalized this BBM

View File

@ -19,3 +19,4 @@ desired_sharding_key:
table: epics
sharding_key: group_id
belongs_to: epic
desired_sharding_key_migration_job_name: BackfillBoardsEpicUserPreferencesGroupId

View File

@ -7,4 +7,5 @@ feature_categories:
description: Contains data for calculating DevOps score.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/26dde5f55f1dac2e6bea4f7e1dfa51c72dc756cb
milestone: '9.3'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
exempt_from_sharding: true # Cell local table storing instance DevOps metrics calculated from service ping

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
class IncreaseQuantityForGitlabcomDuoProTrials < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
milestone '17.1'
class AddOn < MigrationRecord
self.table_name = :subscription_add_ons
enum name: {
code_suggestions: 1
}
end
def up
return unless Gitlab.com?
AddOn.reset_column_information
duo_pro_addon_id = AddOn.find_by(name: "code_suggestions")&.id
return unless duo_pro_addon_id
today = Date.current
update_column_in_batches(:subscription_add_on_purchases, :quantity, 100) do |table, query|
query.where(table[:subscription_add_on_id].eq(duo_pro_addon_id))
.where(table[:trial].eq(true))
.where(table[:expires_on].gteq(today))
end
end
def down
# no-op
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddEarlyAccessProgramParticipantToNamespaceSettings < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :namespace_settings, :early_access_program_participant, :boolean, null: false, default: false
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddMetadataToMemberApprovals < Gitlab::Database::Migration[2.2]
milestone '17.1'
enable_lock_retries!
def change
add_column :member_approvals, :metadata, :jsonb, default: {}, null: false
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddGroupIdToBoardsEpicUserPreferences < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :boards_epic_user_preferences, :group_id, :bigint
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class IndexBoardsEpicUserPreferencesOnGroupId < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
INDEX_NAME = 'index_boards_epic_user_preferences_on_group_id'
def up
add_concurrent_index :boards_epic_user_preferences, :group_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :boards_epic_user_preferences, INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddBoardsEpicUserPreferencesGroupIdFk < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :boards_epic_user_preferences, :namespaces, column: :group_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :boards_epic_user_preferences, column: :group_id
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class AddBoardsEpicUserPreferencesGroupIdTrigger < Gitlab::Database::Migration[2.2]
milestone '17.1'
def up
install_sharding_key_assignment_trigger(
table: :boards_epic_user_preferences,
sharding_key: :group_id,
parent_table: :epics,
parent_sharding_key: :group_id,
foreign_key: :epic_id
)
end
def down
remove_sharding_key_assignment_trigger(
table: :boards_epic_user_preferences,
sharding_key: :group_id,
parent_table: :epics,
parent_sharding_key: :group_id,
foreign_key: :epic_id
)
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class QueueBackfillBoardsEpicUserPreferencesGroupId < Gitlab::Database::Migration[2.2]
milestone '17.1'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillBoardsEpicUserPreferencesGroupId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:boards_epic_user_preferences,
:id,
:group_id,
:epics,
:group_id,
:epic_id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(
MIGRATION,
:boards_epic_user_preferences,
:id,
[
:group_id,
:epics,
:group_id,
:epic_id
]
)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ChangeProjectsOrganizationIdDefault < Gitlab::Database::Migration[2.2]
milestone '17.1'
enable_lock_retries!
def up
change_column_default(:projects, :organization_id, nil)
end
def down
change_column_default(:projects, :organization_id, 1)
end
end

View File

@ -0,0 +1 @@
79e99af8399b46b5804cdffd949583e6ffb312ee55edf94f2b0e2bb92afe8085

View File

@ -0,0 +1 @@
209ef8ffed9a59acdd813b3feaa259231ec812733e58341dedd0a8b941d8a658

View File

@ -0,0 +1 @@
cd61987ea28b86a0c4280c8e0c743ecef2e1c6a45842281b4c7814ebe6e87057

View File

@ -0,0 +1 @@
46c5066752efaa024617a45dd76455f7ae478965067d12dc0022e8c29b90f596

View File

@ -0,0 +1 @@
5a3f2851042d532a5b792a862cc8d6d8b359c02e26b73bbd0d17e0a1936df7f0

View File

@ -0,0 +1 @@
3b31f18988a16c8d61eac4f669b8ae222a01b1e0f49716056737d735e3b81490

View File

@ -0,0 +1 @@
beef1073839bd762120d6340f2f04de3836536d213abf5a07a21db1992498234

View File

@ -0,0 +1 @@
dfac2d41003a3a1971656c47a2e32fac23692b2cfcc371b28bb5246a04dff2ab

View File

@ -0,0 +1 @@
e9e8c4e5fe7108a7e82866d7a38330262af3090fabbb380a6d8aab3e5d03d5fe

View File

@ -243,7 +243,7 @@ CREATE TABLE projects (
suggestion_commit_message character varying(255),
project_namespace_id bigint,
hidden boolean DEFAULT false NOT NULL,
organization_id bigint DEFAULT 1
organization_id bigint
);
CREATE FUNCTION find_projects_by_id(projects_id bigint) RETURNS projects
@ -865,6 +865,22 @@ RETURN NEW;
END
$$;
CREATE FUNCTION trigger_8a38ce2327de() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."group_id" IS NULL THEN
SELECT "group_id"
INTO NEW."group_id"
FROM "epics"
WHERE "epics"."id" = NEW."epic_id";
END IF;
RETURN NEW;
END
$$;
CREATE FUNCTION trigger_8ac78f164b2d() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -5928,7 +5944,8 @@ CREATE TABLE boards_epic_user_preferences (
board_id bigint NOT NULL,
user_id bigint NOT NULL,
epic_id bigint NOT NULL,
collapsed boolean DEFAULT false NOT NULL
collapsed boolean DEFAULT false NOT NULL,
group_id bigint
);
CREATE SEQUENCE boards_epic_user_preferences_id_seq
@ -11247,7 +11264,8 @@ CREATE TABLE member_approvals (
old_access_level integer,
status smallint DEFAULT 0 NOT NULL,
user_id bigint NOT NULL,
member_role_id bigint
member_role_id bigint,
metadata jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE member_approvals_id_seq
@ -12280,6 +12298,7 @@ CREATE TABLE namespace_settings (
lock_duo_features_enabled boolean DEFAULT false NOT NULL,
disable_personal_access_tokens boolean DEFAULT false NOT NULL,
enable_auto_assign_gitlab_duo_pro_seats boolean DEFAULT false NOT NULL,
early_access_program_participant boolean DEFAULT false NOT NULL,
remove_dormant_members boolean DEFAULT false NOT NULL,
remove_dormant_members_period integer DEFAULT 90 NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
@ -25027,6 +25046,8 @@ CREATE UNIQUE INDEX index_boards_epic_user_preferences_on_board_user_epic_unique
CREATE INDEX index_boards_epic_user_preferences_on_epic_id ON boards_epic_user_preferences USING btree (epic_id);
CREATE INDEX index_boards_epic_user_preferences_on_group_id ON boards_epic_user_preferences USING btree (group_id);
CREATE INDEX index_boards_epic_user_preferences_on_user_id ON boards_epic_user_preferences USING btree (user_id);
CREATE INDEX index_boards_on_group_id ON boards USING btree (group_id);
@ -30275,6 +30296,8 @@ CREATE TRIGGER trigger_56d49f4ed623 BEFORE INSERT OR UPDATE ON workspace_variabl
CREATE TRIGGER trigger_7a8b08eed782 BEFORE INSERT OR UPDATE ON boards_epic_board_positions FOR EACH ROW EXECUTE FUNCTION trigger_7a8b08eed782();
CREATE TRIGGER trigger_8a38ce2327de BEFORE INSERT OR UPDATE ON boards_epic_user_preferences FOR EACH ROW EXECUTE FUNCTION trigger_8a38ce2327de();
CREATE TRIGGER trigger_8ac78f164b2d BEFORE INSERT OR UPDATE ON design_management_repositories FOR EACH ROW EXECUTE FUNCTION trigger_8ac78f164b2d();
CREATE TRIGGER trigger_8e66b994e8f0 BEFORE INSERT OR UPDATE ON audit_events_streaming_event_type_filters FOR EACH ROW EXECUTE FUNCTION trigger_8e66b994e8f0();
@ -31346,6 +31369,9 @@ ALTER TABLE ONLY environments
ALTER TABLE p_ci_builds
ADD CONSTRAINT fk_d3130c9a7f FOREIGN KEY (commit_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards_epic_user_preferences
ADD CONSTRAINT fk_d32c3d693c FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_d4e29af7d7 FOREIGN KEY (source_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;

View File

@ -83,14 +83,21 @@ For example, if the backup directory name is `1714053314_2024_04_25_17.0.0-pre`,
}
```
## Limitations
## Known issues
- The tool has only been tested on the [1K architecture](../reference_architectures/1k_users.md)
and therefore only recommended to be used on relevant environments.
- The initial version doesn't use the [copy strategy](backup_gitlab.md#backup-strategy-option),
so as long as there is nothing changing existing files while you perform the backup, you should be fine.
For that reason, you need to either put the GitLab instance into [Maintenance Mode](../maintenance_mode/index.md),
or ensure your instance is not being used by restricting traffic to the servers.
When working with `gitlab-backup-cli`, you might encounter the following issues.
See [issue 428520](https://gitlab.com/gitlab-org/gitlab/-/issues/428520) where an alternative to the copy strategy is discussed.
### Architecture compatibility
If you use the `gitlab-backup-cli` tool on architectures other than the [1K architecture](../reference_architectures/1k_users.md), you might experience issues. This tool is supported only on 1K architecture and is recommended only for relevant environments.
### Backup strategy
Changes to existing files during backup might cause issues on the GitLab instance. This issue occurs because the tool's initial version does not use the [copy strategy](backup_gitlab.md#backup-strategy-option).
A workaround of this issue, is either to:
- Transition the GitLab instance into [Maintenance Mode](../maintenance_mode/index.md).
- Restrict traffic to the servers during backup to preserve instance resources.
We're investigating an alternative to the copy strategy, see [issue 428520](https://gitlab.com/gitlab-org/gitlab/-/issues/428520).

View File

@ -175,7 +175,7 @@ including:
- Snippets
- [Group wikis](../../user/project/wiki/group.md)
- Project-level Secure Files ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121142) in GitLab 16.1)
- Merge request diffs (Helm chart installation only)
- External merge request diffs (GitLab Helm chart only. [Issue 438777](https://gitlab.com/gitlab-org/gitlab/-/issues/438777) proposes to backup local external MR diffs with the Linux package)
Backups do not include:

View File

@ -1007,7 +1007,7 @@ running [Prometheus](../monitoring/prometheus/index.md):
],
},
{
'job_name': 'node',
'job_name': 'static-node',
'static_configs' => [
'targets' => ['1.1.1.1:9100', '1.1.1.2:9100', '1.1.1.3:9100', '1.1.1.4:9100', '1.1.1.5:9100'],
],

View File

@ -0,0 +1,54 @@
---
stage: AI-Powered
group: Custom Models
description: Configure your GitLab instance with Switchboard.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Configure your GitLab Duo features
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
To configure your GitLab instance to access the available large language models (LLMs) in your infrastructure:
1. Configure the self-hosted model.
1. Configure the AI-powered features to use specific self-hosted models.
## Configure the self-hosted model
Prerequisites:
- You must be an administrator.
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **AI-Powered Features**.
- If the **AI-Powered Features** menu item is not available, synchronize your subscription after purchase:
1. On the left sidebar, select **Subscription**.
1. In **Subscription details**, to the right of **Last sync**, select synchronize subscription (**{retry}**).
1. Select **Models**.
1. Set your model details by selecting **New Self-Hosted Model**. Complete the fields:
- **Name the deployment (must be unique):** Enter the model name, for example, Mistral.
- **Choose the model from the Model dropdown list:** Only GitLab-approved models are listed here.
- **Endpoint:** The self-hosted model endpoint, for example, the server hosting the model.
- **API token (if needed):** Optional. Complete if you need an API key to access the model.
- Select **Create Self-Hosted Model** to save the model details.
## Configure the features to use specific models
Prerequisites:
- You must be an administrator.
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **AI-Powered Features**.
- If the **AI-Powered Features** menu item is not available, synchronize your subscription after purchase:
1. On the left sidebar, select **Subscription**.
1. In **Subscription details**, to the right of **Last sync**, select synchronize subscription (**{retry}**).
1. Select **Features**.
1. Set your feature model by selecting **Edit** for the feature you want to set. For example, **Code Generation**.
1. Select the Model Provider for the feature:
- Select **Self-Hosted Model** in the list.
- Choose the self-hosted model you would like to use, for example, Mistral.
- Select **Save Changes** to set the feature to use this specific model.

View File

@ -0,0 +1,61 @@
---
stage: AI-Powered
group: Custom Models
description: Get started with Self-Hosted AI Models.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Deploy a self-hosted large language model
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
Deploying a self-hosted large language model (LLM) allows customers to:
- Manage the end-to-end transmission of requests to enterprise-hosted LLM backends for GitLab Duo features.
- Keep all of these requests within their enterprise network, ensuring no calls to external architecture.
Self-hosted models serve sophisticated customers capable of managing their own LLM infrastructure. GitLab provides the option to connect supported models to LLM features. Model-specific prompts and GitLab Duo feature support is provided by the self-hosted models feature. For more information about this offering, see the [subscription page](../../subscriptions/self_managed/index.md).
## Advantages
- Choice of GitLab-approved LLM models.
- Ability to keep all data and request/response logs within their own domain.
- Ability to select specific GitLab Duo Features for their users.
- Non-reliance on the GitLab shared AI Gateway.
## Self Hosted models vs the default GitLab AI Vendor architecture
```mermaid
sequenceDiagram
actor User
participant GitLab
participant AIGateway as AI Gateway
participant SelfHostedModel as Self Hosted Model
participant GitLabAIVendor as GitLab AI Vendor
User ->> GitLab: Send request
GitLab ->> GitLab: Check if self-hosted model is configured
alt Self-hosted model configured
GitLab ->> AIGateway: Create prompt and send request
AIGateway ->> SelfHostedModel: Perform API request to AI model
SelfHostedModel -->> AIGateway: Respond to the prompt
AIGateway -->> GitLab: Forward AI response
else
GitLab ->> AIGateway: Create prompt and send request
AIGateway ->> GitLabAIVendor: Perform API request to AI model
GitLabAIVendor -->> AIGateway: Respond to the prompt
AIGateway -->> GitLab: Forward AI response
end
GitLab -->> User: Forward AI response
```
With AI self-hosted models, your GitLab instance, AI Gateway, and self-hosted AI model are fully isolated within your own environment. This setup ensures complete privacy and high security for using AI features, with no reliance on public services.
## Get started
To deploy a self-hosted large language model:
1. [Install your self-hosted model deployment infrastructure](../../administration/self_hosted_models/install_infrastructure.md) and connect it to your GitLab instance.
1. [Configure your self-hosted model deployment](../../administration/self_hosted_models/configure_duo_features.md) using instance and group level settings.

View File

@ -0,0 +1,152 @@
---
stage: AI-Powered
group: Custom Models
description: Setup your Self-Hosted Model Deployment infrastructure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Set up your self-hosted model deployment infrastructure
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
By self-hosting the model, AI gateway, and GitLab instance, there are no calls to external architecture, ensuring maximum levels of security.
To set up your self-hosted model deployment infrastructure:
1. Install the large language model (LLM) serving infrastructure.
1. Install the GitLab AI Gateway.
## Step 1: Install LLM serving infrastructure
Install one of the following GitLab-approved LLM models:
- [Mistral-7B-v0.1](https://huggingface.co/mistralai/Mistral-7B-v0.1).
- [Mixtral-8x7B-instruct](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1).
- [Mixtral 8x22B](https://huggingface.co/mistral-community/Mixtral-8x22B-v0.1).
- [CodeGemma 7B IT](https://huggingface.co/google/codegemma-7b-it).
- [CodeGemma 2B](https://huggingface.co/google/codegemma-2b).
### Recommended serving architectures
For Mistral, you should use one of the following architectures:
- [vLLM](https://docs.vllm.ai/en/stable/)
- [TensorRT-LLM](https://docs.mistral.ai/deployment/self-deployment/overview/)
## Step 2: Install the GitLab AI Gateway
### Install using Docker
The GitLab AI Gateway Docker image contains all necessary code and dependencies in a single container.
Find the GitLab official Docker image at:
- [AI Gateway Docker image on Container Registry](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/container_registry/).
- [Release process for self-hosted AI Gateway](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/blob/main/docs/release.md).
WARNING:
Docker for Windows is not officially supported. There are known issues with volume
permissions, and potentially other unknown issues. If you are trying to run on Docker
for Windows, see the [getting help page](https://about.gitlab.com/get-help/) for links
to community resources (such as IRC or forums) to seek help from other users.
#### Set up the volumes location
Create a directory where the logs will reside on the Docker host. It can be under your user's home directory (for example
`~/gitlab-agw`), or in a directory like `/srv/gitlab-agw`. To create that directory, run:
```shell
sudo mkdir -p /srv/gitlab-agw
```
If you're running Docker with a user other than `root`, ensure appropriate
permissions have been granted to that directory.
#### Find the AI Gateway Release
In a production environment, you should pin your deployment to a specific
GitLab AI Gateway release. Find the release to use in [GitLab AI Gateway Releases](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/releases), for example: `7d5f58e1` where `7d5f58e1` is the AI Gateway released version.
To pin your deployment to the latest stable release, use the `latest` tag to run the latest stable release:
```shell
docker run -p 5000:500 registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:latest`
```
NOTE:
We do not yet support multi-arch image, only `linux/amd64`. If you try to run this on Apple chip, adding `--platform linux/amd64` to the `docker run` command will help.
#### Prerequisites
To use the GitLab Docker images:
- You must [install Docker](https://docs.docker.com/engine/install/#server).
- You should use a valid hostname accessible within your network. Do not use `localhost`.
#### Install using Docker Engine
1. For the AI Gateway to know where the GitLab instance is located so it can access the API, set the environment variable `AIGW_GITLAB_API_URL`.
For example, run:
```shell
AIGW_GITLAB_API_URL=https://YOUR_GITLAB_DOMAIN/api/v4/
```
1. For the GitLab instance to know where AI Gateway is located so it can access the gateway, set the environment variable `AI_GATEWAY_URL`
inside your GitLab instance environment variables.
For example, run:
```shell
AI_GATEWAY_URL=https://YOUR_AI_GATEWAY_DOMAIN
```
1. After you've set up the environment variables, run the image. For example:
```shell
docker run -p 5000:500 registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:latest
```
This command downloads and starts a AI Gateway container, and
[publishes ports](https://docs.docker.com/network/#published-ports) needed to
access SSH, HTTP and HTTPS.
1. Track the initialization process:
```shell
sudo docker logs -f gitlab-aigw
```
After starting the container, visit `gitlab-aigw.example.com`. It might take
a while before the Docker container starts to respond to queries.
#### Upgrade
To upgrade the AI Gateway, download the newest Docker image tag.
1. Stop the running container:
```shell
sudo docker stop gitlab-aigw
```
1. Remove the existing container:
```shell
sudo docker rm gitlab-aigw
```
1. Pull the new image:
```shell
docker run -p 5000:500 registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:latest
```
1. Ensure that the environment variables are all set correctly
### Alternative installation methods
For information on alternative ways to install the AI Gateway, see [issue 463773](https://gitlab.com/gitlab-org/gitlab/-/issues/463773).

View File

@ -39,18 +39,12 @@ To enable the export of
## Enable migration of groups and projects by direct transfer
DETAILS:
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383268) in GitLab 15.8.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/461326) in GitLab 17.1.
WARNING:
In GitLab 16.1 and earlier, you should **not** use direct transfer with [scheduled scan execution policies](../../user/application_security/policies/scan-execution-policies.md). If using direct transfer, first upgrade to GitLab 16.2 and ensure security policy bots are enabled in the projects you are enforcing.
WARNING:
This feature is in [beta](../../policy/experiment-beta-support.md#beta) and subject to change without notice.
This feature is not ready for production use.
Migration of groups and projects by direct transfer is disabled by default.
To enable migration of groups and projects by direct transfer:

View File

@ -15,10 +15,6 @@ DETAILS:
With the group migration by direct transfer API, you can start and view the progress of migrations initiated with
[group migration by direct transfer](../user/group/import/index.md).
WARNING:
Migrating projects with this API is in [beta](../policy/experiment-beta-support.md#beta). This feature is not
ready for production use.
## Prerequisites
For information on prerequisites for group migration by direct transfer API, see
@ -31,7 +27,7 @@ prerequisites for [migrating groups by direct transfer](../user/group/import/ind
Use this endpoint to start a new group or project migration. Specify:
- `entities[group_entity]` to migrate a group.
- `entities[project_entity]` to migrate a project. (**Status:** Beta)
- `entities[project_entity]` to migrate a project.
```plaintext
POST /bulk_imports

View File

@ -16,6 +16,12 @@ DETAILS:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/415581) in GitLab 16.2 [with a flag](../administration/feature_flags.md) named `code_suggestions_completion_api`. Disabled by default. This feature is an experiment.
> - Requirement to generate a JWT before calling this endpoint was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127863) in GitLab 16.3.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/416371) in GitLab 16.8. [Feature flag `code_suggestions_completion_api`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138174) removed.
> - `context` and `user_instruction` attributes [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/462750) in GitLab 17.1 [with a flag](../administration/feature_flags.md) named `code_suggestions_context`. Disabled by default.
FLAG:
The availability of the `context` and `user_instruction` attributes is controlled by a feature flag.
For more information, see the history.
These attributes are available for testing, but are not ready for production use.
```plaintext
POST /code_suggestions/completions
@ -31,12 +37,14 @@ Requests to this endpoint are proxied to the
Parameters:
| Attribute | Type | Required | Description |
|----------------|---------|----------|-------------|
| `current_file` | hash | yes | Attributes of file for which code suggestions are being generated. See [File attributes](#file-attributes) for a list of strings this attribute accepts. |
| `intent` | string | no | The intent of the completion request. Options: `completion` or `generation`. |
| `stream` | boolean | no | Whether to stream the response as smaller chunks as they are ready (if applicable). Default: `false`. |
| `project_path` | string | no | The path of the project. |
| Attribute | Type | Required | Description |
|--------------------|---------|----------|-------------|
| `current_file` | hash | yes | Attributes of file for which Code Suggestions are being generated. See [File attributes](#file-attributes) for a list of strings this attribute accepts. |
| `intent` | string | no | The intent of the completion request. This can be either `completion` or `generation`. |
| `stream` | boolean | no | Whether to stream the response as smaller chunks as they are ready (if applicable). Default: `false`. |
| `project_path` | string | no | The path of the project. |
| `context` | array | no | Additional context to be used for Code Suggestions. See [Context attributes](#context-attributes) for a list of parameters this attribute accepts. |
| `user_instruction` | string | no | A user's instructions for Code Suggestions. |
### File attributes
@ -46,6 +54,14 @@ The `current_file` attribute accepts the following strings:
- `content_above_cursor` - The content of the file above the current cursor position. Required.
- `content_below_cursor` - The content of the file below the current cursor position. Optional.
### Context attributes
The `context` attribute accepts a list of elements with the following attributes:
- `type` - The type of the context element. This can be either `file` or `snippet`.
- `name` - The name of the context element. A name of the file or a code snippet.
- `content` - The content of the context element. The body of the file or a function.
Example request:
```shell

View File

@ -961,6 +961,20 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="queryselfmanagedaddoneligibleusersaddontype"></a>`addOnType` | [`GitlabSubscriptionsAddOnType!`](#gitlabsubscriptionsaddontype) | Type of add on to filter the eligible users by. |
| <a id="queryselfmanagedaddoneligibleuserssearch"></a>`search` | [`String`](#string) | Search the user list. |
### `Query.selfManagedUsersQueuedForRolePromotion`
Fields related to users within a self-managed instance that are pending role promotion approval.
DETAILS:
**Introduced** in GitLab 17.1.
**Status**: Experiment.
Returns [`UsersQueuedForRolePromotionConnection`](#usersqueuedforrolepromotionconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#pagination-arguments):
`before: String`, `after: String`, `first: Int`, and `last: Int`.
### `Query.snippets`
Find Snippets visible to the current user.
@ -1134,7 +1148,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="queryvulnerabilitieshasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have remediations. |
| <a id="queryvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="queryvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="queryvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="queryvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="queryvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="queryvulnerabilitiesreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="queryvulnerabilitiesscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@ -9472,7 +9486,7 @@ Input type: `VerifiedNamespaceCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationverifiednamespacecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationverifiednamespacecreatenamespacepath"></a>`namespacePath` | [`ID!`](#id) | Root namespace path. |
| <a id="mutationverifiednamespacecreateverificationlevel"></a>`verificationLevel` | [`String!`](#string) | Verification level used to indicate the verification for namespace given by Gitlab. |
| <a id="mutationverifiednamespacecreateverificationlevel"></a>`verificationLevel` | [`CiCatalogResourceVerificationLevel!`](#cicatalogresourceverificationlevel) | Verification level used to indicate the verification for namespace given by Gitlab. |
#### Fields
@ -15334,6 +15348,30 @@ The edge type for [`UserCore`](#usercore).
| <a id="usercoreedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="usercoreedgenode"></a>`node` | [`UserCore`](#usercore) | The item at the end of the edge. |
#### `UsersQueuedForRolePromotionConnection`
The connection type for [`UsersQueuedForRolePromotion`](#usersqueuedforrolepromotion).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="usersqueuedforrolepromotionconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. |
| <a id="usersqueuedforrolepromotionconnectionedges"></a>`edges` | [`[UsersQueuedForRolePromotionEdge]`](#usersqueuedforrolepromotionedge) | A list of edges. |
| <a id="usersqueuedforrolepromotionconnectionnodes"></a>`nodes` | [`[UsersQueuedForRolePromotion]`](#usersqueuedforrolepromotion) | A list of nodes. |
| <a id="usersqueuedforrolepromotionconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `UsersQueuedForRolePromotionEdge`
The edge type for [`UsersQueuedForRolePromotion`](#usersqueuedforrolepromotion).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="usersqueuedforrolepromotionedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="usersqueuedforrolepromotionedgenode"></a>`node` | [`UsersQueuedForRolePromotion`](#usersqueuedforrolepromotion) | The item at the end of the edge. |
#### `ValueStreamConnection`
The connection type for [`ValueStream`](#valuestream).
@ -22597,7 +22635,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="groupvulnerabilitieshasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have remediations. |
| <a id="groupvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="groupvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="groupvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="groupvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="groupvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="groupvulnerabilitiesreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="groupvulnerabilitiesscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@ -22653,7 +22691,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| <a id="groupvulnerabilityseveritiescounthasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have remediations. |
| <a id="groupvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
| <a id="groupvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="groupvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="groupvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="groupvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="groupvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="groupvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@ -23289,7 +23327,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| <a id="instancesecuritydashboardvulnerabilityseveritiescounthasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have remediations. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@ -28823,7 +28861,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="projectvulnerabilitieshasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have remediations. |
| <a id="projectvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="projectvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="projectvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="projectvulnerabilitiesowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="projectvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="projectvulnerabilitiesreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="projectvulnerabilitiesscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@ -28866,7 +28904,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| <a id="projectvulnerabilityseveritiescounthasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have remediations. |
| <a id="projectvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
| <a id="projectvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="projectvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. |
| <a id="projectvulnerabilityseveritiescountowasptopten"></a>`owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 category. Wildcard value "NONE" also supported when feature flag `owasp_top_10_null_filtering` is enabled. "NONE" wildcard cannot be combined with other OWASP top 10 values. |
| <a id="projectvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="projectvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="projectvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@ -31308,6 +31346,17 @@ fields relate to interactions between the two entities.
| <a id="userstatusmessage"></a>`message` | [`String`](#string) | User status message. |
| <a id="userstatusmessagehtml"></a>`messageHtml` | [`String`](#string) | HTML of the user status message. |
### `UsersQueuedForRolePromotion`
Represents a Pending Member Approval Queued for Role Promotion.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="usersqueuedforrolepromotionnewaccesslevel"></a>`newAccessLevel` | [`AccessLevel`](#accesslevel) | Highest New GitLab::Access level requested for the member. |
| <a id="usersqueuedforrolepromotionuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member approval object. |
### `ValueStream`
#### Fields
@ -34522,6 +34571,8 @@ Possible filter types for remote development cluster agents in a namespace.
| Value | Description |
| ----- | ----------- |
| <a id="namespaceclusteragentfilteravailable"></a>`AVAILABLE` | Cluster agents in the namespace that can be used for hosting workspaces. |
| <a id="namespaceclusteragentfilterdirectly_mapped"></a>`DIRECTLY_MAPPED` | Cluster agents that are directly mapped to the given namespace. |
| <a id="namespaceclusteragentfilterunmapped"></a>`UNMAPPED` | Cluster agents within a namespace that are not directly mapped to it. |
### `NamespaceProjectSort`
@ -35536,6 +35587,7 @@ OwaspTop10 category of the vulnerability.
| <a id="vulnerabilityowasptop10a8_2021"></a>`A8_2021` | A8:2021-Software and Data Integrity Failures, OWASP top 10 category. |
| <a id="vulnerabilityowasptop10a9_2017"></a>`A9_2017` | A9:2017-Using Components with Known Vulnerabilities, OWASP top 10 category. |
| <a id="vulnerabilityowasptop10a9_2021"></a>`A9_2021` | A9:2021-Security Logging and Monitoring Failures, OWASP top 10 category. |
| <a id="vulnerabilityowasptop10none"></a>`NONE` | No OWASP top 10 category. |
### `VulnerabilityReportType`

View File

@ -14,11 +14,7 @@ DETAILS:
> - `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
> - `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
> - `product_analytics_dashboards` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/398653) by default in GitLab 16.11.
FLAG:
On self-managed GitLab and GitLab Dedicated, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flag](../administration/feature_flags.md) named `cube_api_proxy`.
On GitLab.com, this feature is available.
This feature is not ready for production use.
> - Feature flag `product_analytics_dashboards` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454059) in GitLab 17.1.
NOTE:
Make sure to define the `cube_api_base_url` and `cube_api_key` application settings first using [the API](settings.md).

View File

@ -851,7 +851,7 @@ Administrators can monitor usage of these API clients by
### Rust
- [`gitlab` crate](https://crates.io/crates/gitlab)
- [`gitlab` crate](https://crates.io/crates/gitlab/)
### Swift

View File

@ -0,0 +1,40 @@
---
status: proposed
creation-date: "2024-05-27"
authors: [ "@bsandlin" ]
coach: "@ntepluhina"
approvers: [ "@dhershkovitch", "@marknuzzo" ]
owning-stage: "~devops::verify"
participating-stages: []
---
<!-- Blueprints often contain forward-looking statements -->
<!-- vale gitlab.FutureTense = NO -->
# Pipeline Mini Graph
This blueprint serves as living documentation for the Pipeline Mini Graph. The Pipeline Mini Graph is used in various places throughout the platform to communicate to users the status of the relevant pipeline. Users are able to re-run jobs directly from the component or drilldown into said jobs and linked pipelines for further investigation.
## Motivation
While the Pipeline Mini Graph primarily functions via REST, we are updating the component to support GraphQL and subsequently migrating all instances of this component to GraphQL. This documentation will serve as the SSOT for this refactor. Developers have expressed difficulty contributing to this component as the two APIs co-exist, so we need to make this code easier to update while also supporting both REST and GraphQL. This refactor lives behind a feature flag called `ci_graphql_pipeline_mini_graph`.
### Goals
- Improved maintainability
- Backwards compatibility
- Improved query performance
- Deprecation of REST support
### Non-Goals
- Redesign of the Pipeline Mini Graph UI
## Proposal
To break down implementation, we are taking the following steps:
1. Separate the REST version and the GraphQL version of the component into 2 directories called `pipeline_mini_graph` and `legacy_pipeline_mini_graph`. This way, developers can contribute with more ease and we can easily remove the REST version once all apps are using GraphQL.
1. Finish updating the newer component to fully support GraphQL by adding a query for the stage dropdown.
1. Optimize GraphQL query structure to be more performant.
1. Roll out `ci_graphql_pipeline_mini_graph` to globally enable GraphQL instances of the component.

View File

@ -225,7 +225,7 @@ Some key details about runners:
- You can use the [`tags` keyword](../runners/configure_runners.md#control-jobs-that-a-runner-can-run)
for finer control, and associate runners with specific jobs. For example, you can use a tag for jobs that
require dedicated, more powerful, or specific hardware.
- GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/runner_autoscale).
- GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/runner_autoscale/).
Use autoscaling to provision runners only when needed and scale down when not needed.
### TeamCity build features & plugins

View File

@ -86,7 +86,7 @@ like `:hover` and others ([Chrome](https://developer.chrome.com/docs/devtools/cs
- Account for states dependent on data size ([empty](https://design.gitlab.com/patterns/empty-states),
some data, and lots of data).
- Account for states dependent on user role, user preferences, and subscription.
- Consider animations and transitions, and follow their [guidelines](https://design.gitlab.com/product-foundations/motion/).
- Consider animations and transitions, and follow their [guidelines](https://design.gitlab.com/brand-design/motion).
### Responsive

View File

@ -188,5 +188,5 @@ flowchart LR;
## Data Privacy
GitLab only receives event counts or similarly aggregated information from self-managed instances. User identifiers for individual events on the SaaS version of GitLab are [pseudonymized](https://metrics.gitlab.com/identifiers).
GitLab only receives event counts or similarly aggregated information from self-managed instances. User identifiers for individual events on the SaaS version of GitLab are [pseudonymized](https://metrics.gitlab.com/identifiers/).
An exact description on what kind of data is being collected through the Internal Analytics system is given in our [handbook](https://handbook.gitlab.com/handbook/legal/privacy/customer-product-usage-information/).

View File

@ -142,7 +142,7 @@ track_event(
Any frontend tracking call automatically passes the values `user.id`, `namespace.id`, and `project.id` from the current context of the page.
If you need to pass any further properties, such as `extra`, `context`, `label`, `property`, and `value`, you can use the [deprecated snowplow implementation](https://docs.gitlab.com/16.4/ee/development/internal_analytics/snowplow/implementation.html). In this case, let us know about your specific use-case in our [feedback issue for Internal Events](https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/690).
If you need to pass any further properties, such as `extra`, `context`, `label`, `property`, and `value`, you can use the [deprecated snowplow implementation](https://archives.docs.gitlab.com/16.4/ee/development/internal_analytics/snowplow/implementation.html). In this case, let us know about your specific use-case in our [feedback issue for Internal Events](https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/690).
#### Vue components

View File

@ -729,7 +729,7 @@ We must add [the VPC IP Address Range (CIDR)](https://docs.aws.amazon.com/elasti
### Proxy Protocol
If Proxy protocol is enabled in the [load balancer](#load-balancer) we created earlier, we must also [enable](https://docs.gitlab.com/omnibus/settings/nginx.md#configuring-the-proxy-protocol) this on the `gitlab.rb` file.
If Proxy protocol is enabled in the [load balancer](#load-balancer) we created earlier, we must also [enable](https://docs.gitlab.com/omnibus/settings/nginx.html#configuring-the-proxy-protocol) this on the `gitlab.rb` file.
1. Edit `/etc/gitlab/gitlab.rb`:

View File

@ -53,7 +53,7 @@ If the highest number stable branch is unclear, check the [GitLab blog](https://
| [RubyGems](#3-rubygems) | `3.5.x` | A specific RubyGems version is not required, but you should update to benefit from some known performance improvements. |
| [Go](#4-go) | `1.20.x` | From GitLab 16.4, Go 1.20 or later is required. |
| [Git](#git) | `2.42.x` | From GitLab 16.5, Git 2.42.x and later is required. You should use the [Git version provided by Gitaly](#git). |
| [Node.js](#5-node) | `18.17.x` | From GitLab 16.3, Node.js 18.17 or later is required. |
| [Node.js](#5-node) | `20.13.x` | From GitLab 17.0, Node.js 20.13 or later is required. |
## GitLab directory structure
@ -266,8 +266,8 @@ GitLab requires the use of Node to compile JavaScript
assets, and Yarn to manage JavaScript dependencies. The current minimum
requirements for these are:
- `node` 18.x releases (v18.17.0 or later).
[Other LTS versions of Node.js](https://github.com/nodejs/release#release-schedule) might be able to build assets, but we only guarantee Node.js 18.x.
- `node` 20.x releases (v20.13.0 or later).
[Other LTS versions of Node.js](https://github.com/nodejs/release#release-schedule) might be able to build assets, but we only guarantee Node.js 20.x.
- `yarn` = v1.22.x (Yarn 2 is not supported yet)
In many distributions,
@ -275,8 +275,8 @@ the versions provided by the official package repositories are out of date, so
we must install through the following commands:
```shell
# install node v18.x
curl --location "https://deb.nodesource.com/setup_18.x" | sudo bash -
# install node v20.x
curl --location "https://deb.nodesource.com/setup_20.x" | sudo bash -
sudo apt-get install -y nodejs
npm install --global yarn

View File

@ -424,4 +424,4 @@ accDescr: Sequence of actions that happen when a user authenticates to GitLab th
For help and support around your GitLab Mattermost deployment, see:
- [Troubleshooting Mattermost issues](https://docs.mattermost.com/install/troubleshooting.html).
- [Mattermost GitLab Issues Support Handbook](https://docs.mattermost.com/process/support.html?highlight=omnibus#gitlab-issues).
- [Mattermost forum](https://forum.mattermost.com/search?q=gitlab).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1249,7 +1249,7 @@ Do not override the `reports.html.destination` or `reports.html.outputLocation`
### Python projects
Extra care needs to be taken when using the [`PIP_EXTRA_INDEX_URL`](https://pipenv.pypa.io/en/latest/cli/#envvar-PIP_EXTRA_INDEX_URL)
Extra care needs to be taken when using the [`PIP_EXTRA_INDEX_URL`](https://pipenv.pypa.io/en/latest/indexes.html)
environment variable due to a possible exploit documented by [CVE-2018-20225](https://nvd.nist.gov/vuln/detail/CVE-2018-20225):
> An issue was discovered in pip (all versions) because it installs the version with the highest version number, even if the user had

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