Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-16 15:14:25 +00:00
parent 678191068e
commit 8abe394ab5
82 changed files with 893 additions and 403 deletions

View File

@ -377,7 +377,6 @@ export default {
'ee/app/assets/javascripts/iterations/components/iteration_report.vue',
'ee/app/assets/javascripts/linked_resources/components/resource_links_block.vue',
'ee/app/assets/javascripts/logs/list/related_issues/related_issues_provider.vue',
'ee/app/assets/javascripts/members/promotion_requests/components/app.vue',
'ee/app/assets/javascripts/merge_requests/components/reviewers/approval_summary.vue',
'ee/app/assets/javascripts/metrics/details/metrics_details.vue',
'ee/app/assets/javascripts/metrics/details/metrics_line_chart.vue',

View File

@ -76,7 +76,7 @@ docs code_quality:
when: always
docs hugo_build:
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/technical-writing/docs-gitlab-com/docs-gitlab-com-builder:hugo-0.142.0
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/technical-writing/docs-gitlab-com/docs-gitlab-com-builder:hugo-0.145.0
extends:
- .default-retry
- .docs:rules:docs-lint

View File

@ -3530,7 +3530,6 @@ Gitlab/BoundedContexts:
- 'ee/lib/bulk_imports/projects/pipelines/push_rule_pipeline.rb'
- 'ee/lib/bulk_imports/projects/pipelines/vulnerabilities_pipeline.rb'
- 'ee/lib/delay.rb'
- 'ee/lib/duo_pro/bulk_user_assignment.rb'
- 'ee/lib/ee/bulk_imports/groups/stage.rb'
- 'ee/lib/ee/bulk_imports/projects/pipelines/issues_pipeline.rb'
- 'ee/lib/ee/bulk_imports/projects/stage.rb'

View File

@ -79,7 +79,6 @@ Layout/LineBreakAfterFinalMixin:
- 'ee/lib/api/dependency_proxy/packages/maven.rb'
- 'ee/lib/api/resource_iteration_events.rb'
- 'ee/lib/api/resource_weight_events.rb'
- 'ee/lib/duo_pro/bulk_user_assignment.rb'
- 'ee/lib/ee/api/entities/project_push_rule.rb'
- 'ee/lib/ee/gitlab/background_migration/create_compliance_standards_adherence.rb'
- 'ee/lib/gitlab/auth/group_saml/identity_linker.rb'

View File

@ -1 +1 @@
d5b6a529fbff95ae2f8479a95ae375abaa497ae1
5603e4787366e8a2b02491ab921364627efc3ed3

View File

@ -1,6 +1,5 @@
<script>
import { GlAlert, GlFormInputGroup, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { generateAgentRegistrationCommand } from '../clusters_util';
@ -8,9 +7,6 @@ import { I18N_AGENT_TOKEN, HELM_VERSION_POLICY_URL } from '../constants';
export default {
i18n: I18N_AGENT_TOKEN,
advancedInstallPath: helpPagePath('user/clusters/agent/install/_index', {
anchor: 'advanced-installation-method',
}),
HELM_VERSION_POLICY_URL,
components: {
GlAlert,
@ -95,17 +91,5 @@ export default {
:modal-id="modalId"
/>
</p>
<p>
<strong>{{ $options.i18n.advancedInstallTitle }}</strong>
</p>
<p>
<gl-sprintf :message="$options.i18n.advancedInstallBody">
<template #link="{ content }">
<gl-link :href="$options.advancedInstallPath" target="_blank"> {{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</div>
</template>

View File

@ -98,18 +98,13 @@ export const I18N_AGENT_TOKEN = {
),
tokenSubtitle: s__('ClusterAgents|The agent uses the token to connect with GitLab.'),
basicInstallTitle: s__('ClusterAgents|Install using Helm (recommended)'),
basicInstallTitle: s__('ClusterAgents|Install using Helm'),
basicInstallBody: s__(
'ClusterAgents|From a terminal, connect to your cluster and run this command. The token is included in the command.',
),
helmVersionText: s__(
'ClusterAgents|Use a Helm version compatible with your Kubernetes version (see %{linkStart}Helm version support policy%{linkEnd}).',
),
advancedInstallTitle: s__('ClusterAgents|Advanced installation methods'),
advancedInstallBody: s__(
'ClusterAgents|%{linkStart}View the documentation%{linkEnd} for advanced installation. Ensure you have your access token available.',
),
};
export const HELM_VERSION_POLICY_URL = 'https://helm.sh/docs/topics/version_skew/';

View File

@ -812,7 +812,7 @@ export default {
class="import-table-bar gl-sticky gl-z-3 gl-flex-col gl-bg-subtle gl-px-5 md:gl-flex md:gl-flex-row md:gl-items-center md:gl-justify-between"
>
<div class="gl-flex gl-w-full gl-items-center gl-gap-4 gl-pb-4">
<span data-test-id="selection-count">
<span data-testid="selection-count">
<gl-sprintf :message="__('%{count} selected')">
<template #count>
{{ selectedGroupsIds.length }}
@ -885,7 +885,7 @@ export default {
/>
</template>
<template #head(importTarget)="data">
<span data-test-id="new-path-col">
<span data-testid="new-path-col">
<span class="gl-mr-2">{{ data.label }}</span
><gl-icon
v-gl-tooltip="s__('BulkImport|Path of the new group.')"

View File

@ -98,6 +98,7 @@ export default {
[constants.ISSUE_NOTEABLE_TYPE]: this.$options.i18n.issue,
[constants.EPIC_NOTEABLE_TYPE]: this.$options.i18n.epic,
[constants.MERGE_REQUEST_NOTEABLE_TYPE]: this.$options.i18n.mergeRequest,
[constants.INCIDENT_NOTEABLE_TYPE]: this.$options.i18n.incident,
};
const noteableTypeKey =

View File

@ -161,6 +161,12 @@ export default {
return this.sortDirection === constants.DESC;
},
noteableType() {
if (this.noteableData.type?.toUpperCase() === constants.INCIDENT_NOTEABLE_TYPE) {
return (
this.noteableData.type.charAt(0).toUpperCase() +
this.noteableData.type.slice(1).toLowerCase()
);
}
return this.noteableData.noteableType;
},
previewNoteId() {

View File

@ -10,6 +10,7 @@ export const COMMENT_FORM = {
wiki: __('Wiki'),
internalComment: __('Add internal note'),
issue: __('issue'),
incident: __('incident'),
startThread: __('Start thread'),
startInternalThread: __('Start internal thread'),
mergeRequest: __('merge request'),

View File

@ -12,6 +12,7 @@ import {
NPM_PACKAGE_MANAGER,
YARN_PACKAGE_MANAGER,
PROJECT_PACKAGE_ENDPOINT_TYPE,
GROUP_PACKAGE_ENDPOINT_TYPE,
INSTANCE_PACKAGE_ENDPOINT_TYPE,
NPM_HELP_PATH,
} from '~/packages_and_registries/package_registry/constants';
@ -26,7 +27,7 @@ export default {
GlSprintf,
GlFormRadioGroup,
},
inject: ['npmInstanceUrl'],
inject: ['npmInstanceUrl', 'npmGroupUrl'],
props: {
packageEntity: {
type: Object,
@ -63,12 +64,18 @@ export default {
return `${instruction} ${this.packageEntity.name}`;
},
npmPath(endpointType) {
const endpointMap = {
[INSTANCE_PACKAGE_ENDPOINT_TYPE]: this.npmInstanceUrl,
[GROUP_PACKAGE_ENDPOINT_TYPE]: this.npmGroupUrl,
};
return endpointMap[endpointType] || this.packageEntity.npmUrl;
},
npmSetupCommand(type, endpointType) {
const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/'));
const npmPathForEndpoint =
endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE
? this.npmInstanceUrl
: this.packageEntity.npmUrl;
const npmPathForEndpoint = this.npmPath(endpointType);
if (type === NPM_PACKAGE_MANAGER) {
return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`;
@ -99,6 +106,7 @@ export default {
],
packageEndpointTypeOptions: [
{ value: INSTANCE_PACKAGE_ENDPOINT_TYPE, text: s__('PackageRegistry|Instance-level') },
{ value: GROUP_PACKAGE_ENDPOINT_TYPE, text: s__('PackageRegistry|Group-level') },
{ value: PROJECT_PACKAGE_ENDPOINT_TYPE, text: s__('PackageRegistry|Project-level') },
],
};

View File

@ -168,6 +168,7 @@ export const NPM_PACKAGE_MANAGER = 'npm';
export const YARN_PACKAGE_MANAGER = 'yarn';
export const PROJECT_PACKAGE_ENDPOINT_TYPE = 'project';
export const GROUP_PACKAGE_ENDPOINT_TYPE = 'group';
export const INSTANCE_PACKAGE_ENDPOINT_TYPE = 'instance';
export const GRAPHQL_PAGE_SIZE = 20;

View File

@ -17,6 +17,7 @@ export default () => {
pageType,
emptyListIllustration,
npmInstanceUrl,
npmGroupUrl,
projectListUrl,
groupListUrl,
settingsPath,
@ -46,6 +47,7 @@ export default () => {
emptyListIllustration,
isGroupPage,
npmInstanceUrl,
npmGroupUrl,
projectListUrl,
groupListUrl,
breadCrumbState,

View File

@ -7,28 +7,29 @@ function getMenuItems(container) {
export const OptionsMenuAdapter = {
clicks: {
toggleOptionsMenu(event) {
const button = event.target.closest('.js-options-button');
const menuContainer = button.parentElement;
toggleOptionsMenu(event, button) {
const menuContainer = this.diffElement.querySelector('[data-options-menu]');
const items = getMenuItems(menuContainer);
if (!this.sink.optionsMenu) {
this.sink.optionsMenu = new Vue({
el: Vue.version.startsWith('2') ? button : menuContainer,
name: 'GlDisclosureDropdown',
render: (createElement = Vue.h) =>
createElement(GlDisclosureDropdown, {
props: {
icon: 'ellipsis_v',
startOpened: true,
noCaret: true,
category: 'tertiary',
size: 'small',
items,
},
}),
});
}
// eslint-disable-next-line no-new
new Vue({
el: Vue.version.startsWith('2') ? button : menuContainer,
name: 'GlDisclosureDropdown',
render(h) {
return h(GlDisclosureDropdown, {
props: {
icon: 'ellipsis_v',
startOpened: true,
noCaret: true,
category: 'tertiary',
size: 'small',
items,
},
attrs: {
'data-options-toggle': true,
},
});
},
});
},
},
};

View File

@ -4,6 +4,7 @@ import { mapState } from 'vuex';
import { __, s__ } from '~/locale';
import getBlobSearchQuery from '~/search/graphql/blob_search_zoekt.query.graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import { logError } from '~/lib/logger';
import { DEFAULT_FETCH_CHUNKS } from '../constants';
import { RECEIVE_NAVIGATION_COUNT } from '../../store/mutation_types';
import EmptyResult from './result_empty.vue';
@ -30,7 +31,6 @@ export default {
return {
hasError: false,
blobSearch: {},
hasResults: true,
};
},
apollo: {
@ -40,30 +40,40 @@ export default {
},
errorPolicy: 'none',
variables() {
return {
search: this.query.search,
groupId: this.query.group_id && `gid://gitlab/Group/${this.query.group_id}`,
projectId: this.query.project_id && `gid://gitlab/Project/${this.query.project_id}`,
const variables = {
search: this.query.search || '',
page: this.currentPage,
chunkCount: DEFAULT_FETCH_CHUNKS,
regex: parseBoolean(this.query.regex),
includeArchived: parseBoolean(this.query.include_archived),
includeForked: parseBoolean(this.query.include_forked),
regex: parseBoolean(this.query?.regex),
includeArchived: parseBoolean(this.query?.include_archived),
includeForked: parseBoolean(this.query?.include_forked),
};
if (this.query?.group_id) {
variables.groupId = `gid://gitlab/Group/${this.query.group_id}`;
}
if (this.query?.project_id) {
variables.projectId = `gid://gitlab/Project/${this.query.project_id}`;
}
return variables;
},
skip() {
return !this.query.search;
},
result({ data }) {
this.hasError = false;
this.blobSearch = data?.blobSearch;
this.hasResults = data?.blobSearch?.files?.length > 0;
this.$store.commit(RECEIVE_NAVIGATION_COUNT, {
key: 'blobs',
count: data?.blobSearch?.matchCount.toString(),
});
},
debounce: 500,
error() {
error(error) {
logError(error);
this.hasError = true;
this.hasResults = false;
},
},
},
@ -75,6 +85,9 @@ export default {
isLoading() {
return this.$apollo.queries.blobSearch.loading;
},
hasResults() {
return this.blobSearch?.files?.length > 0;
},
},
};
</script>
@ -86,7 +99,7 @@ export default {
<status-bar v-if="!isLoading" :blob-search="blobSearch" />
<empty-result v-if="!hasResults && !isLoading" />
<zoekt-blob-results
v-if="hasResults"
v-if="hasResults || isLoading"
:blob-search="blobSearch"
:has-results="hasResults"
:is-loading="isLoading"

View File

@ -632,7 +632,6 @@ export default {
v-if="!restrictedToolBarItems.includes('collapsible-section')"
v-show="!previewMarkdown"
:tag="mdCollapsibleSection"
:prepend="true"
tag-select="Click to expand"
:button-title="__('Add a collapsible section')"
icon="details-block"

View File

@ -25,6 +25,10 @@
z-index: 1;
}
.rd-diff-file-header:has([data-options-toggle] button[aria-expanded='true']) {
z-index: 10;
}
.rd-diff-file[data-collapsed] .rd-diff-file-header {
border-radius: var(--rd-diff-file-border-radius);
}

View File

@ -166,12 +166,3 @@
font-size: $default-icon-size;
line-height: 42px;
}
.settings-message {
padding: 5px;
line-height: 1.3;
color: var(--gray-900, $gray-900);
background-color: var(--orange-50, $orange-50);
border: 1px solid var(--orange-200, $orange-200);
border-radius: $gl-border-radius-base;
}

View File

@ -13,7 +13,7 @@
- view_title = _('View file @ %{commitSha}') % { commitSha: Commit.truncate_sha(@diff_file.content_sha) }
- view_href = project_blob_path(@diff_file.repository.project, helpers.tree_join(@diff_file.content_sha, @diff_file.new_path))
- options_menu_items = [ { "text": "#{view_title}", "href": "#{view_href}" } ].to_json
- options_menu_items = [{ "text": "#{view_title}", "href": "#{view_href}" }].to_json
.rd-diff-file-header{ data: { testid: 'rd-diff-file-header' } }
.rd-diff-file-toggle<
@ -54,8 +54,11 @@
%span>= "&#x2212;".html_safe
%span{ "data-testid" => "js-file-deletion-line" }= @diff_file.removed_lines
.rd-diff-file-options-menu
.js-options-menu
-# <script> here is likely the most effective way to minimize bytes: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182850#note_2387011092
%div{ data: { options_menu: true } }
-# <script> here is likely the most effective way to minimize bytes:
-# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182850#note_2387011092
-# haml-lint:disable InlineJavaScript
%script{ type: "application/json" }
= options_menu_items.html_safe
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'ellipsis_v', button_options: { class: 'js-options-button', data: { click: 'toggleOptionsMenu' }, aria: { label: _('Options') } })
- button_params = { icon: 'ellipsis_v', button_options: { data: { click: 'toggleOptionsMenu' }, aria: { label: _('Options') } } }
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, **button_params)

View File

@ -11,6 +11,12 @@ module PackagesHelper
expose_url("api/#{::API::API.version}/packages/#{registry_type}")
end
def package_registry_group_url(group_id, registry_type = :maven)
group_api_path = expose_path(api_v4_groups_path(id: group_id))
package_registry_group_path = "#{group_api_path}/-/packages/#{registry_type}"
expose_url(package_registry_group_path)
end
def package_registry_project_url(project_id, registry_type = :maven)
project_api_path = api_v4_projects_path(id: project_id)
package_registry_project_path = "#{project_api_path}/packages/#{registry_type}"
@ -91,6 +97,7 @@ module PackagesHelper
full_path: group.full_path,
group_list_url: group_packages_path(group),
page_type: 'groups',
npm_group_url: package_registry_group_url(group.id, :npm),
settings_path: if show_group_package_registry_settings(group)
group_settings_packages_and_registries_path(group)
@ -107,6 +114,7 @@ module PackagesHelper
full_path: project.full_path,
page_type: 'projects',
project_list_url: project_packages_path(project),
npm_group_url: package_registry_group_url(project.group&.id, :npm),
settings_path: if show_package_registry_settings(project)
project_settings_packages_and_registries_path(project, anchor: 'package-registry-settings')

View File

@ -1,10 +0,0 @@
%tr
%td
= link_to matching_tag.name, project_ref_path(@project, matching_tag.name), class: 'ref-name'
- if @project.root_ref?(matching_tag.name)
= gl_badge_tag s_('ProtectedTag|default'), { variant: :info }, { class: 'gl-ml-2' }
%td
- commit = @project.commit(matching_tag.name)
= link_to(commit.short_id, project_commit_path(@project, commit.id), class: 'commit-sha')
= time_ago_with_tooltip(commit.committed_date)

View File

@ -1,25 +0,0 @@
- page_title @protected_ref.name, _("Protected Tags")
.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0.ref-name
= @protected_ref.name
.col-lg-9.edit_protected_tag
%h5 Matching Tags
- if @matching_refs.present?
.table-responsive
%table.table.protected-tags-list
%colgroup
%col{ width: "30%" }
%col{ width: "30%" }
%thead
%tr
%th Tag
%th Last commit
%tbody
- @matching_refs.each do |matching_tag|
= render partial: "projects/protected_tags/shared/matching_tag", object: matching_tag
- else
%p.settings-message.text-center
Couldn't find any matching tags.

View File

@ -39,7 +39,7 @@
title: sprintf(s_("MarkdownEditor|Outdent line (%{modifier_key}[)") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "details-block",
css_class: 'haml-markdown-button gl-mr-2',
data: { "md-tag" => "<details><summary>Click to expand</summary>\n{text}\n</details>", "md-prepend" => true, "md-select" => "Click to expand" },
data: { "md-tag" => "<details><summary>Click to expand</summary>\n{text}\n</details>", "md-select" => "Click to expand" },
title: _("Add a collapsible section") })
.gl-flex.gl-py-2.haml-markdown-button
.gl-border-l.gl-ml-2.gl-pl-3

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128701/
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421373
milestone: '16.4'
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134730
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429027
milestone: '16.6'
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118887
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409025
milestone: '16.0'
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112973
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/405148
milestone: '15.11'
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107836
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/386726
milestone: "15.8"
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134048
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425391
milestone: '16.6'
type: development
group: group::anti-abuse
default_enabled: false
group: group::authorization
default_enabled: false

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86352
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364835
milestone: "15.2"
type: development
group: group::anti-abuse
group: group::authorization
default_enabled: false

View File

@ -8,14 +8,6 @@ description: Nuget package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30994
milestone: '13.1'
gitlab_schema: gitlab_main_cell
desired_sharding_key:
project_id:
references: projects
backfill_via:
parent:
foreign_key: package_id
table: packages_packages
sharding_key: project_id
belongs_to: package
desired_sharding_key_migration_job_name: BackfillPackagesNugetMetadataProjectId
sharding_key:
project_id: projects
table_size: small

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddAutoDuoCodeReviewToProjectSettings < Gitlab::Database::Migration[2.2]
milestone '18.0'
def up
add_column :project_settings, :auto_duo_code_review_enabled, :boolean, default: false, null: false
end
def down
remove_column :project_settings, :auto_duo_code_review_enabled
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddPackagesNugetMetadataProjectIdNotNull < Gitlab::Database::Migration[2.2]
milestone '17.11'
disable_ddl_transaction!
def up
add_not_null_constraint :packages_nuget_metadata, :project_id
end
def down
remove_not_null_constraint :packages_nuget_metadata, :project_id
end
end

View File

@ -0,0 +1 @@
0b3667ac1bfa0848d63548447128e4ad37b312a473a910a352a8c31db4e7c8c7

View File

@ -0,0 +1 @@
e4266035c9251b2e41d552111d4d7ae8451bee938cf684f3313aeb30c852d2a7

View File

@ -19492,6 +19492,7 @@ CREATE TABLE packages_nuget_metadata (
description text,
normalized_version text,
project_id bigint,
CONSTRAINT check_6b272cad10 CHECK ((project_id IS NOT NULL)),
CONSTRAINT check_9973c0cc33 CHECK ((char_length(normalized_version) <= 255)),
CONSTRAINT check_d39a5fe9ee CHECK ((char_length(description) <= 4000)),
CONSTRAINT check_e2fc129ebd CHECK ((char_length(authors) <= 255)),
@ -21421,6 +21422,7 @@ CREATE TABLE project_settings (
extended_prat_expiry_webhooks_execute boolean DEFAULT false NOT NULL,
merge_request_title_regex text,
protect_merge_request_pipelines boolean DEFAULT false NOT NULL,
auto_duo_code_review_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT check_1a30456322 CHECK ((char_length(pages_unique_domain) <= 63)),
CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
CONSTRAINT check_3ca5cbffe6 CHECK ((char_length(issue_branch_template) <= 255)),

View File

@ -17,4 +17,3 @@ swap:
and so on: "Remove 'and so on'. Try to use 'like' and provide examples instead."
in order to: "Remove 'in order' and leave 'to'."
quite: "Remove 'quite', as it's wordy."
you can: "When possible, remove 'you can'. It's often unnecessary."

View File

@ -2,6 +2,7 @@
stage: none
group: unassigned
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
description: Customize your GitLab instance appearance, including logos, favicons, sign-in pages, Progressive Web App settings, system messages, and color themes.
title: GitLab Appearance
---

View File

@ -83,6 +83,8 @@ To configure a self-hosted model:
| Bedrock | `bedrock/<model ID of the model>` | `bedrock/mistral.mixtral-8x7b-instruct-v0:1` |
| Azure OpenAI | `azure/<model ID of the model>` | `azure/gpt-35-turbo` |
- For Amazon Bedrock models, find and copy the model's **Inference profile ID** (based on your `AWS_REGION`), and paste it in the `Model Identifier` field, with the `bedrock/` prefix. For more information, see the [Amazon supported regions and models for inference profiles documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html).
For more information about configuring the model identifier for models deployed through vLLM, see the [vLLM documentation](supported_llm_serving_platforms.md#finding-the-model-name).
1. Select **Create self-hosted model**.

View File

@ -184,7 +184,20 @@ sudo /etc/init.d/gitlab start
```
## Bulk assign users to GitLab Duo Pro
## Bulk assign users to GitLab Duo
You can assign users in bulk to GitLab Duo using a CSV file with the names of the users.
The CSV file must have a header named `username`, followed by the usernames on each subsequent row.
```plaintext
username
user1
user2
user3
user4
```
### GitLab Duo Pro
{{< details >}}
@ -199,8 +212,6 @@ sudo /etc/init.d/gitlab start
{{< /history >}}
The Rake task for bulk user assignment is available in GitLab 16.9 and later. For GitLab 16.8, use the script [`bulk_user_assignment.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/duo_pro/bulk_user_assignment.rb) instead.
To perform bulk user assignment for GitLab Duo Pro, you can use the following Rake task:
```shell
@ -212,27 +223,71 @@ If you prefer to use square brackets in the file path, you can escape them or us
```shell
bundle exec rake duo_pro:bulk_user_assignment\['path/to/your/file.csv'\]
# or
bundle exec rake "duo_pro:bulk_user_assignment['path/to/your/file.csv']"
bundle exec rake "duo_pro:bulk_user_assignment[path/to/your/file.csv]"
```
The CSV file should have the following format:
### GitLab Duo Pro and Enterprise
```plaintext
username
user1
user2
user3
user4
etc..
{{< details >}}
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/187230) in GitLab 18.0.
{{< /history >}}
#### GitLab Self-Managed
This Rake task bulk assigns GitLab Duo Pro or Enterprise seats at the instance level to a list of users from a
CSV file, based on the available purchased add-on.
To perform bulk user assignment for a GitLab Self-Managed instance:
```shell
bundle exec rake gitlab_subscriptions:duo:bulk_user_assignment DUO_BULK_USER_FILE_PATH=path/to/your/file.csv
```
Ensure that the file contains a header named `username`, and each subsequent row represents a username for user assignment.
If you prefer to use square brackets in the file path, you can escape them or use double quotes:
The task might raise the following error messages:
```shell
bundle exec rake gitlab_subscriptions:duo:bulk_user_assignment\['path/to/your/file.csv'\]
# or
bundle exec rake "gitlab_subscriptions:duo:bulk_user_assignment[path/to/your/file.csv]"
```
- `User is not found`: The specified user was not found.
- `ERROR_NO_SEATS_AVAILABLE`: No more seats are available for user assignment.
- `ERROR_INVALID_USER_MEMBERSHIP`: The user is not eligible for assignment due to being inactive, a bot, or a ghost.
#### GitLab.com
GitLab.com administrators can also use this Rake task to bulk assign GitLab Duo Pro or Enterprise seats for GitLab.com
groups, based on the available purchased add-on for that group.
To perform bulk user assignment for a GitLab.com group:
```shell
bundle exec rake gitlab_subscriptions:duo:bulk_user_assignment DUO_BULK_USER_FILE_PATH=path/to/your/file.csv NAMESPACE_ID=<namespace_id>
```
If you prefer to use square brackets in the file path, you can escape them or use double quotes:
```shell
bundle exec rake gitlab_subscriptions:duo:bulk_user_assignment\['path/to/your/file.csv','<namespace_id>'\]
# or
bundle exec rake "gitlab_subscriptions:duo:bulk_user_assignment[path/to/your/file.csv,<namespace_id>]"
```
## Troubleshooting
### Errors during bulk user assignment
When using the Rake task for bulk user assignment, you might encounter the following errors:
- `User is not found`: The specified user was not found. Please ensure the provided username matches an existing user.
- `ERROR_NO_SEATS_AVAILABLE`: No more seats are available for user assignment. Please see how to [view assigned GitLab Duo users](../../subscriptions/subscription-add-ons.md#view-assigned-gitlab-duo-users) to check current seat assignments.
- `ERROR_INVALID_USER_MEMBERSHIP`: The user is not eligible for assignment due to being inactive, a bot, or a ghost. Please ensure the user is active and, if on GitLab.com, a member of the provided namespace.
## Related topics

View File

@ -2674,3 +2674,20 @@ Example response:
...
}
```
## Troubleshooting
### Unexpected `restrict_user_defined_variables` value in response
If you set conflicting values for `restrict_user_defined_variables` and `ci_pipeline_variables_minimum_override_role`,
the response values might differ from what you expect because the `pipeline_variables_minimum_override_role`
setting has higher priority.
For example, if you:
- Set `restrict_user_defined_variables` to `true` and `ci_pipeline_variables_minimum_override_role` to `developer`,
the response returns `restrict_user_defined_variables: false`. Setting `ci_pipeline_variables_minimum_override_role`
to `developer` takes precedence and variables are not restricted.
- Set `restrict_user_defined_variables` to `false` and `ci_pipeline_variables_minimum_override_role` to `maintainer`,
The response returns `restrict_user_defined_variables: true` because setting `ci_pipeline_variables_minimum_override_role`
to `maintainer` takes precedence and variables are restricted.

View File

@ -128,7 +128,7 @@ After purchasing the Duo add-on, existing eligible users can be assigned/un-assi
1. [Duo users management UI](../../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats)
1. [GraphQL endpoint](../../api/graphql/assign_gitlab_duo_seats.md)
1. [Rake task](../../administration/raketasks/user_management.md#bulk-assign-users-to-gitlab-duo-pro)
1. [Rake task](../../administration/raketasks/user_management.md#bulk-assign-users-to-gitlab-duo)
The above methods make use of the [BulkAssignService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/services/gitlab_subscriptions/duo/bulk_assign_service.rb)/[BulkUnassignService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/services/gitlab_subscriptions/duo/bulk_unassign_service.rb), which evaluates eligibility criteria preliminarily before assigning/un-assigning the passed users in a single SQL operation.

View File

@ -2737,7 +2737,6 @@ Instead of:
## you can
When possible, start sentences with an active verb instead of **you can**.
([Vale](../testing/vale.md) rule: [`Wordy.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab_base/Wordy.yml))
For example:

View File

@ -77,7 +77,7 @@ The following directories are created as you go through the installation steps:
- `/home/git/gitlab-shell` - Core add-on component of GitLab. Maintains SSH
cloning and other functionality.
- `/home/git/repositories` - Bare repositories for all projects organized by
namespace. This is where the Git repositories which are pushed/pulled are
namespace. This directory is where the Git repositories which are pushed/pulled are
maintained for all projects. **This area contains critical data for projects.
[Keep a backup](../administration/backup_restore/_index.md).**

View File

@ -158,7 +158,7 @@ Prerequisites:
- To remove users from seats, select **Remove seat**, then **Remove seats** to confirm.
1. To the right of the user, turn on the toggle to assign a GitLab Duo seat.
Administrators of GitLab Self-Managed instances can also use a [Rake task](../administration/raketasks/user_management.md#bulk-assign-users-to-gitlab-duo-pro) to assign or remove seats in bulk.
Administrators of GitLab Self-Managed instances can also use a [Rake task](../administration/raketasks/user_management.md#bulk-assign-users-to-gitlab-duo) to assign or remove seats in bulk.
#### Managing GitLab Duo seats with LDAP configuration

View File

@ -187,15 +187,7 @@ It might take up to 10 minutes for GitLab to start trying to establish a connect
### Install the agent in the cluster
GitLab recommends using Helm to install the agent.
To connect your cluster to GitLab, install the registered agent
in your cluster. You can either:
- [Install the agent with Helm](#install-the-agent-with-helm).
- Or, follow the [advanced installation method](#advanced-installation-method).
If you do not know which one to choose, we recommend starting with Helm.
To connect your cluster to GitLab, [install the registered agent with Helm](#install-the-agent-with-helm).
To install a receptive agent, follow the steps in [GitLab connects to agent (receptive agent)](#option-2-gitlab-connects-to-agent-receptive-agent).
@ -292,10 +284,6 @@ and the domain DNS can't be resolved.
{{< /alert >}}
#### Advanced installation method
GitLab also provides a [KPT package for the agent](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/tree/master/build/deployment/gitlab-agent). This method provides greater flexibility, but is only recommended for advanced users.
## Install multiple agents in your cluster
{{< alert type="note" >}}

View File

@ -125,7 +125,7 @@ After you set up the IAM role, you cannot change the AWS account that's associat
},
"Condition": {
"StringEquals": {
"auth.token.gitlab.com/cc/oidc/<Instance_ID>": "gitlab-cc-<Instance_ID>"
"auth.token.gitlab.com/cc/oidc/<Instance_ID>:aud": "gitlab-cc-<Instance_ID>"
},
}

View File

@ -5,6 +5,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
title: Group access and permissions
---
{{< details >}}
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
{{< /details >}}
Configure your groups to control group permissions and access.
For more information, see also [Sharing projects and groups](../project/members/sharing_projects_groups.md).
@ -360,6 +367,30 @@ LDAP user permissions can be manually overridden by an administrator. To overrid
Now you can edit the user's permissions from the **Members** page.
## Set the default role that can use pipeline variables
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/513117) in GitLab 17.10.
{{< /history >}}
Set the default [minimum role allowed to run a new pipeline with pipeline variables](../../ci/variables/_index.md#restrict-pipeline-variables)
for projects in a group. Alternatively, you can prevent all users using pipeline variables.
Prerequisites:
- You must be a maintainer in the group.
- The group must be the top level group, not a subgroup.
To set the default minimum role:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > CI/CD > Variables**.
1. Under **Default role to use pipeline variables** select a minimum role, or select
**No one allowed** to prevent any user from using pipeline variables.
1. Select **Save changes**.
## Troubleshooting
### Verify if access is blocked by IP restriction

View File

@ -1170,6 +1170,10 @@ in the rendered output.
Embed images using inline or reference links.
To see title text, hover over the image.
The text in square brackets (`[ ]`) becomes the image alt text.
To learn about creating accessible alt text, see
[Accessible images and videos](#accessible-images-and-videos).
<!--
The following examples use HTML to skip the Vale ReferenceLinks test.
Do not change it back to a markdown codeblocks.
@ -1189,18 +1193,18 @@ Inline-style:
<!-- markdownlint-disable proper-names -->
<pre class="highlight"><code>![alt text](img/markdown_logo_v17_11.png "Title Text")
<pre class="highlight"><code>![GitLab logo](img/markdown_logo_v17_11.png "Title Text")
</code></pre>
![alt text](img/markdown_logo_v17_11.png "Title Text")
![GitLab logo](img/markdown_logo_v17_11.png "Title Text")
Reference-style:
<pre class="highlight"><code>![alt text1][logo]
<pre class="highlight"><code>![GitLab logo][logo]
&#91;logo]: img/markdown_logo_v17_11.png "Title Text"
</code></pre>
![alt text](img/markdown_logo_v17_11.png "Title Text")
![GitLab logo](img/markdown_logo_v17_11.png "Title Text")
<!-- markdownlint-enable proper-names -->
@ -1237,12 +1241,12 @@ The value must an integer with a unit of either `px` (default) or `%`.
For example
```markdown
![alt text](img/markdown_logo_v17_11.png "Title Text"){width=100 height=100px}
![GitLab logo](img/markdown_logo_v17_11.png "Title Text"){width=100 height=100px}
![alt text](img/markdown_logo_v17_11.png "Title Text"){width=75%}
![GitLab logo](img/markdown_logo_v17_11.png "Title Text"){width=75%}
```
![alt text](img/markdown_logo_v17_11.png "Title Text"){width=100 height=100px}
![GitLab logo](img/markdown_logo_v17_11.png "Title Text"){width=100 height=100px}
You can also use the `img` HTML tag instead of Markdown and set its `height` and
`width` parameters.

View File

@ -48,9 +48,34 @@ container_scanning:
rules:
- if: $CONTAINER_SCANNING_DISABLED == 'true' || $CONTAINER_SCANNING_DISABLED == '1'
when: never
# The following 3 blocks of rules define whether the job runs in a an *MR pipeline* or a *branch pipeline*
# when an MR exists. If the job has additional rules to observe they should be added in the blocks 1 and 3
# to cover both the *MR pipeline* and the *branch pipeline* workflows.
# 1. Run the job in an *MR pipeline* if MR pipelines for AST are enabled and there's an open merge request.
## When FIPS mode is enabled, use the FIPS compatible image
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_PIPELINE_SOURCE == "merge_request_event" &&
$CI_GITLAB_FIPS_MODE == "true" &&
$CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
variables:
CS_IMAGE_SUFFIX: -fips
## When FIPS mode is not enabled, use the regular image
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_PIPELINE_SOURCE == "merge_request_event"
# 2. Don't run the job in a *branch pipeline* if *MR pipelines* for AST are enabled and there's an open merge request.
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_OPEN_MERGE_REQUESTS
when: never
# 3. Finally, run the job in a *branch pipeline* (When MR pipelines are disabled for AST, or it is enabled but no open MRs exist for the branch).
## When FIPS mode is enabled, use the FIPS compatible image
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true" &&
$CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
variables:
CS_IMAGE_SUFFIX: -fips
## When FIPS mode is not enabled, use the regular image
- if: $CI_COMMIT_BRANCH

View File

@ -26,6 +26,10 @@
# List of available variables: https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-variables
variables:
# Setting this variable affects all Security templates
# (SAST, Dependency Scanning, ...)
AST_ENABLE_MR_PIPELINES: "true"
#
CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:7"
CS_SCHEMA_MODEL: 15
@ -54,24 +58,35 @@ variables:
- if: $CONTAINER_SCANNING_DISABLED == 'true' || $CONTAINER_SCANNING_DISABLED == '1'
when: never
# Add the job to merge request pipelines if there's an open merge request.
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
# The following 3 blocks of rules define whether the job runs in a an *MR pipeline* or a *branch pipeline*
# when an MR exists. If the job has additional rules to observe they should be added in the blocks 1 and 3
# to cover both the *MR pipeline* and the *branch pipeline* workflows.
# 1. Run the job in an *MR pipeline* if MR pipelines for AST are enabled and there's an open merge request.
## When FIPS mode is enabled, use the FIPS compatible image
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_PIPELINE_SOURCE == "merge_request_event" &&
$CI_GITLAB_FIPS_MODE == "true" &&
$CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
variables:
CS_IMAGE_SUFFIX: -fips
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
## When FIPS mode is not enabled, use the regular image
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_PIPELINE_SOURCE == "merge_request_event"
# Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
- if: $CI_OPEN_MERGE_REQUESTS
# 2. Don't run the job in a *branch pipeline* if *MR pipelines* for AST are enabled and there's an open merge request.
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
$CI_OPEN_MERGE_REQUESTS
when: never
# Add the job to branch pipelines.
# 3. Finally, run the job in a *branch pipeline* (When MR pipelines are disabled for AST, or it is enabled but no open MRs exist for the branch).
## When FIPS mode is enabled, use the FIPS compatible image
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true" &&
$CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
variables:
CS_IMAGE_SUFFIX: -fips
## When FIPS mode is not enabled, use the regular image
- if: $CI_COMMIT_BRANCH
container_scanning:

View File

@ -274,7 +274,23 @@ module Gitlab
def max_relative_position
Rails.cache.fetch("import:#{@importable.model_name.plural}:#{@importable.id}:hierarchy_max_issues_relative_position", expires_in: 24.hours) do
::RelativePositioning.mover.context(Issue.in_projects(@importable.root_ancestor.all_projects).first)&.max_relative_position || ::Gitlab::RelativePositioning::START_POSITION
inner_sql = Issue
.select(:id)
.where(::Project.arel_table[:id].eq(Issue.arel_table[:project_id]))
.order(iid: :asc)
.limit(1)
.to_sql
anchor_issue_id = @importable
.root_ancestor
.all_project_ids
.joins("INNER JOIN LATERAL (#{inner_sql}) issues ON TRUE")
.order(Issue.arel_table[:id].asc)
.pick(Issue.arel_table[:id])
::RelativePositioning
.mover
.context(Issue.find_by(id: anchor_issue_id))&.max_relative_position || 0
end
end

View File

@ -8,7 +8,8 @@ namespace :gitlab do
logger.level = Gitlab::Utils.to_boolean(ENV['VERBOSE']) ? Logger::INFO : Logger::WARN
Gitlab::Database::TablesLocker.new(
logger: logger,
dry_run: Gitlab::Utils.to_boolean(ENV['DRY_RUN'], default: false)
dry_run: Gitlab::Utils.to_boolean(ENV['DRY_RUN'], default: false),
include_partitions: Gitlab::Utils.to_boolean(ENV['INCLUDE_PARTITIONS'], default: true)
).lock_writes
end
@ -18,7 +19,8 @@ namespace :gitlab do
logger.level = Gitlab::Utils.to_boolean(ENV['VERBOSE']) ? Logger::INFO : Logger::WARN
Gitlab::Database::TablesLocker.new(
logger: logger,
dry_run: Gitlab::Utils.to_boolean(ENV['DRY_RUN'], default: false)
dry_run: Gitlab::Utils.to_boolean(ENV['DRY_RUN'], default: false),
include_partitions: Gitlab::Utils.to_boolean(ENV['INCLUDE_PARTITIONS'], default: true)
).unlock_writes
end
end

View File

@ -13504,9 +13504,6 @@ msgstr ""
msgid "ClusterAgents|%{agentName} successfully created."
msgstr ""
msgid "ClusterAgents|%{linkStart}View the documentation%{linkEnd} for advanced installation. Ensure you have your access token available."
msgstr ""
msgid "ClusterAgents|%{name} successfully deleted"
msgstr ""
@ -13534,9 +13531,6 @@ msgstr ""
msgid "ClusterAgents|Access tokens"
msgstr ""
msgid "ClusterAgents|Advanced installation methods"
msgstr ""
msgid "ClusterAgents|Agent"
msgstr ""
@ -13729,7 +13723,7 @@ msgstr ""
msgid "ClusterAgents|If you're %{linkStart}bootstrapping the agent with Flux%{linkEnd}, you can close this dialog."
msgstr ""
msgid "ClusterAgents|Install using Helm (recommended)"
msgid "ClusterAgents|Install using Helm"
msgstr ""
msgid "ClusterAgents|Instance wide settings for the GitLab Agent for Kubernetes."
@ -43099,6 +43093,9 @@ msgstr ""
msgid "PackageRegistry|Gradle Kotlin DSL install command"
msgstr ""
msgid "PackageRegistry|Group-level"
msgstr ""
msgid "PackageRegistry|Helm"
msgstr ""
@ -48870,9 +48867,6 @@ msgstr ""
msgid "Protected Tag"
msgstr ""
msgid "Protected Tags"
msgstr ""
msgid "Protected branches"
msgstr ""
@ -49261,9 +49255,6 @@ msgstr ""
msgid "ProtectedTag|What are protected tags?"
msgstr ""
msgid "ProtectedTag|default"
msgstr ""
msgid "Provide Feedback"
msgstr ""

View File

@ -306,7 +306,7 @@
"swagger-cli": "^4.0.4",
"tailwindcss": "^3.4.1",
"timezone-mock": "^1.0.8",
"vite": "^6.2.6",
"vite": "^6.3.0",
"vite-plugin-ruby": "^5.1.1",
"vue-loader-vue3": "npm:vue-loader@17.4.2",
"vue-test-utils-compat": "0.0.14",

View File

@ -180,7 +180,6 @@ spec/frontend/snippets/components/snippet_description_edit_spec.js
spec/frontend/super_sidebar/components/organization_switcher_spec.js
spec/frontend/super_sidebar/components/sidebar_portal_spec.js
spec/frontend/super_sidebar/components/user_menu_spec.js
spec/frontend/todos/components/filtered_search_tokens/project_token_spec.js
spec/frontend/tooltips/index_spec.js
spec/frontend/vue_alerts_spec.js
spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js

View File

@ -211,6 +211,26 @@ begin
rescue Errno::ECONNREFUSED
# Start the mock server if Snowplow Micro is not running
server = Thread.start { Server.new.start }
retry
rescue Errno::ECONNRESET, EOFError
puts <<~TEXT
Error: No events server available!
This is often caused by mismatched hostnames. To resolve this issue, you can do one of:
1) When GDK has a hostname alias, update `config/gitlab.yml` to
use localhost for the snowplow_micro settings. For example:
| --> |
| snowplow_micro: | snowplow_micro: |
| address: 'gdk.test:9090' | address: 'localhost:9090' |
2) Set up Snowplow Micro in your GDK
https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/snowplow_micro.md
TEXT
exit 1
end
reader = TTY::Reader.new
@ -234,8 +254,8 @@ begin
sleep 1
end
rescue Interrupt
server&.exit
rescue Errno::ECONNREFUSED
# Ignore this error, caused by the server being killed before the loop due to working on a child thread
ensure
server&.exit
end

View File

@ -1,7 +1,6 @@
import Vue from 'vue';
import { shallowMount, mount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BlobHeader from '~/blob/components/blob_header.vue';
import DefaultActions from '~/blob/components/blob_header_default_actions.vue';
import BlobFilepath from '~/blob/components/blob_header_filepath.vue';
@ -29,17 +28,15 @@ describe('Blob Header Default Actions', () => {
const findTableContents = () => wrapper.findComponent(TableContents);
const findViewSwitcher = () => wrapper.findComponent(ViewerSwitcher);
const findBlobFilePath = () => wrapper.findComponent(BlobFilepath);
const findRichTextEditorBtn = () =>
wrapper.findComponent('[data-testid="rich-blob-viewer-button"]');
const findSimpleTextEditorBtn = () =>
wrapper.findComponent('[data-testid="simple-blob-viewer-button"]');
const findRichTextEditorBtn = () => wrapper.findByTestId('rich-blob-viewer-button');
const findSimpleTextEditorBtn = () => wrapper.findByTestId('simple-blob-viewer-button');
const findWebIdeLink = () => wrapper.findComponent(WebIdeLink);
async function createComponent({
blobProps = {},
options = {},
propsData = {},
mountFn = shallowMount,
mountFn = shallowMountExtended,
} = {}) {
const userInfoMockResolver = jest.fn().mockResolvedValue({
data: { ...userInfoMock },
@ -215,7 +212,7 @@ describe('Blob Header Default Actions', () => {
[key]: `<span>${slotContent}</span>`,
},
},
mountFn: mount,
mountFn: mountExtended,
});
expect(wrapper.text()).toContain(slotContent);
});

View File

@ -69,10 +69,6 @@ describe('InstallAgentModal', () => {
expect(findHelmExternalLinkIcon().props()).toMatchObject({ name: 'external-link', size: 12 });
});
it('shows advanced agent installation instructions', () => {
expect(wrapper.text()).toContain(I18N_AGENT_TOKEN.advancedInstallTitle);
});
it('shows agent token as an input value', () => {
expect(findInput().props('value')).toBe(agentToken);
});

View File

@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import X509CertificateDetails from '~/commit/components/x509_certificate_details.vue';
import { X509_CERTIFICATE_KEY_IDENTIFIER_TITLE } from '~/commit/constants';
import { x509CertificateDetailsProp } from '../mock_data';
@ -7,7 +7,7 @@ describe('X509 certificate details', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(X509CertificateDetails, {
wrapper = shallowMountExtended(X509CertificateDetails, {
propsData: x509CertificateDetailsProp,
});
};
@ -17,8 +17,8 @@ describe('X509 certificate details', () => {
});
const findTitle = () => wrapper.find('strong');
const findSubjectValues = () => wrapper.findAll("[data-testid='subject-value']");
const findKeyIdentifier = () => wrapper.find("[data-testid='key-identifier']");
const findSubjectValues = () => wrapper.findAllByTestId('subject-value');
const findKeyIdentifier = () => wrapper.findByTestId('key-identifier');
it('renders a title', () => {
expect(findTitle().text()).toBe(x509CertificateDetailsProp.title);

View File

@ -54,8 +54,7 @@ describe('import table', () => {
},
};
const findImportSelectedDropdown = () =>
wrapper.find('[data-testid="import-selected-groups-dropdown"]');
const findImportSelectedDropdown = () => wrapper.findByTestId('import-selected-groups-dropdown');
const findRowImportDropdownAtIndex = (idx) =>
wrapper.findAll('tbody td button').wrappers.filter((w) => w.text() === 'Import with projects')[
idx
@ -64,8 +63,8 @@ describe('import table', () => {
const findTargetNamespaceInput = (rowWrapper) =>
extendedWrapper(rowWrapper).findByTestId('target-namespace-input');
const findPaginationDropdownText = () => findPaginationDropdown().find('button').text();
const findSelectionCount = () => wrapper.find('[data-test-id="selection-count"]');
const findNewPathCol = () => wrapper.find('[data-test-id="new-path-col"]');
const findSelectionCount = () => wrapper.findByTestId('selection-count');
const findNewPathCol = () => wrapper.findByTestId('new-path-col');
const findHistoryLink = () => wrapper.findByTestId('history-link');
const findUnavailableFeaturesWarning = () => wrapper.findByTestId('unavailable-features-alert');
const findImportProjectsWarning = () => wrapper.findByTestId('import-projects-warning');

View File

@ -47,12 +47,12 @@ describe('Incidents List', () => {
const findTableRows = () => wrapper.findAll('table tbody tr');
const findLoader = () => wrapper.findComponent(GlLoadingIcon);
const findTimeAgo = () => wrapper.findAllComponents(TimeAgoTooltip);
const findAssignees = () => wrapper.findAll('[data-testid="incident-assignees"]');
const findCreateIncidentBtn = () => wrapper.find('[data-testid="create-incident-button"]');
const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']");
const findAssignees = () => wrapper.findAllByTestId('incident-assignees');
const findCreateIncidentBtn = () => wrapper.findByTestId('create-incident-button');
const findClosedIcon = () => wrapper.findAllByTestId('incident-closed');
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findSeverity = () => wrapper.findAllComponents(SeverityToken);
const findEscalationStatus = () => wrapper.findAll('[data-testid="incident-escalation-status"]');
const findEscalationStatus = () => wrapper.findAllByTestId('incident-escalation-status');
const findIncidentLink = () => wrapper.findByTestId('incident-link');
function mountComponent({ data = {}, loading = false, provide = {} } = {}) {

View File

@ -852,6 +852,26 @@ describe('init markdown', () => {
expect(textArea.value).toEqual(expectedText);
});
it('wraps collapsible section text correctly', () => {
const initialValue = 'first line\nsecond line';
textArea.value = initialValue;
textArea.setSelectionRange(0, initialValue.length);
const tag = '<details><summary>Click to expand</summary>\n{text}\n</details>';
insertMarkdownText({
textArea,
text: textArea.value,
tag,
blockTag: null,
selected: initialValue,
wrap: true,
});
const expectedText = `<details><summary>Click to expand</summary>\n${initialValue}\n</details>`;
expect(textArea.value).toEqual(expectedText);
});
it.each([{ linePrefix: '> ' }, { linePrefix: ' >' }, { linePrefix: '>' }])(
"removes quotes correctly when line prefix '$linePrefix'",
({ linePrefix }) => {

View File

@ -610,4 +610,43 @@ describe('note_app', () => {
expect(replySpy).toHaveBeenCalledWith('foo');
});
});
describe('noteableType computed property', () => {
const createComponent = (noteableType, type) => {
store = createStore();
return shallowMount(NotesApp, {
propsData: {
...propsData,
noteableData: {
...propsData.noteableData,
noteableType,
type,
},
},
store,
});
};
it('returns the noteableType as is for regular types', () => {
wrapper = createComponent('Issue', 'issue');
expect(wrapper.findComponent(NotesActivityHeader).props('noteableType')).toBe('Issue');
wrapper = createComponent('MergeRequest', 'merge_request');
expect(wrapper.findComponent(NotesActivityHeader).props('noteableType')).toBe('MergeRequest');
});
it('capitalizes the first letter for incident type & does not return noteableType', () => {
wrapper = createComponent('Incident', 'incident');
expect(wrapper.findComponent(NotesActivityHeader).props('noteableType')).toBe('Incident');
wrapper = createComponent('Issue', 'incident');
expect(wrapper.findComponent(NotesActivityHeader).props('noteableType')).toBe('Incident');
});
it('handles empty values gracefully', () => {
wrapper = createComponent('', '');
expect(wrapper.findComponent(NotesActivityHeader).props('noteableType')).toBe('');
expect(wrapper.vm.noteableType).toBe('');
});
});
});

View File

@ -1,7 +1,7 @@
import { GlButton, GlButtonGroup, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
@ -16,7 +16,7 @@ describe('NotificationsDropdown', () => {
let mockAxios;
function createComponent(injectedProperties = {}) {
return mount(NotificationsDropdown, {
return mountExtended(NotificationsDropdown, {
stubs: {
GlButtonGroup,
GlButton,
@ -40,11 +40,10 @@ describe('NotificationsDropdown', () => {
}
const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
const findSplitIcon = () => wrapper.find('[data-testid="notification-split-icon"]');
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findSplitIcon = () => wrapper.findByTestId('notification-split-icon');
const findAllNotificationsDropdownItems = () => wrapper.findAllComponents(GlListboxItem);
const findListboxItemAt = (index) => findAllNotificationsDropdownItems().at(index);
const findListboxItem = (value) => wrapper.find(`[data-testid="listbox-item-${value}"]`);
const findListboxItem = (value) => wrapper.findByTestId(`listbox-item-${value}`);
const findNotificationsModal = () => wrapper.findComponent(CustomNotificationsModal);
const tooltipTitlePrefix = 'Notification setting';
@ -152,7 +151,7 @@ describe('NotificationsDropdown', () => {
initialNotificationLevel: level,
});
const tooltipElement = findByTestId('notification-dropdown');
const tooltipElement = wrapper.findByTestId('notification-dropdown');
const tooltip = getBinding(tooltipElement.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`);

View File

@ -22,7 +22,7 @@ exports[`NpmInstallation renders all the messages 1`] = `
checked="instance"
disabledfield="disabled"
htmlfield="html"
options="[object Object],[object Object]"
options="[object Object],[object Object],[object Object]"
textfield="text"
valuefield="value"
/>

View File

@ -14,6 +14,7 @@ import {
NPM_PACKAGE_MANAGER,
YARN_PACKAGE_MANAGER,
PROJECT_PACKAGE_ENDPOINT_TYPE,
GROUP_PACKAGE_ENDPOINT_TYPE,
INSTANCE_PACKAGE_ENDPOINT_TYPE,
NPM_HELP_PATH,
} from '~/packages_and_registries/package_registry/constants';
@ -36,6 +37,7 @@ describe('NpmInstallation', () => {
wrapper = shallowMountExtended(NpmInstallation, {
provide: {
npmInstanceUrl: 'npmInstanceUrl',
npmGroupUrl: 'npmGroupUrl',
},
propsData: {
packageEntity,
@ -69,6 +71,7 @@ describe('NpmInstallation', () => {
expect(findEndPointTypeSector().props()).toMatchObject({
options: [
{ value: INSTANCE_PACKAGE_ENDPOINT_TYPE, text: 'Instance-level' },
{ value: GROUP_PACKAGE_ENDPOINT_TYPE, text: 'Group-level' },
{ value: PROJECT_PACKAGE_ENDPOINT_TYPE, text: 'Project-level' },
],
});
@ -130,12 +133,22 @@ describe('NpmInstallation', () => {
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
findEndPointTypeSector().vm.$emit('change', GROUP_PACKAGE_ENDPOINT_TYPE);
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo @gitlab-org:registry=npmGroupUrl/ >> .npmrc',
multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
findEndPointTypeSector().vm.$emit('change', INSTANCE_PACKAGE_ENDPOINT_TYPE);
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo @gitlab-org:registry=npmInstanceUrl/ >> .npmrc`,
instruction: 'echo @gitlab-org:registry=npmInstanceUrl/ >> .npmrc',
multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
@ -174,6 +187,10 @@ describe('NpmInstallation', () => {
trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND,
});
findEndPointTypeSector().vm.$emit('change', GROUP_PACKAGE_ENDPOINT_TYPE);
await nextTick();
findEndPointTypeSector().vm.$emit('change', INSTANCE_PACKAGE_ENDPOINT_TYPE);
await nextTick();

View File

@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import PackagePath from '~/packages_and_registries/shared/components/package_path.vue';
@ -6,7 +6,7 @@ describe('PackagePath', () => {
let wrapper;
const mountComponent = (propsData = { path: 'foo' }) => {
wrapper = shallowMount(PackagePath, {
wrapper = shallowMountExtended(PackagePath, {
propsData,
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
@ -21,7 +21,7 @@ describe('PackagePath', () => {
const ELLIPSIS_CHEVRON = 'ellipsis-chevron';
const LEAF_LINK = 'leaf-link';
const findItem = (name) => wrapper.find(`[data-testid="${name}"]`);
const findItem = (name) => wrapper.findByTestId(name);
const findTooltip = (w) => getBinding(w.element, 'gl-tooltip');
describe.each`

View File

@ -1,7 +1,7 @@
import { GlLoadingIcon, GlBadge } from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import NoteHeader from '~/pages/shared/wikis/wiki_notes/components/note_header.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { extendedWrapper, shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('NoteHeader', () => {
let wrapper;
@ -19,6 +19,9 @@ describe('NoteHeader', () => {
propsData,
});
const findAuthorNameLink = () => wrapper.findByTestId('wiki-note-author-name-link');
const findAuthorUsernameLink = () => wrapper.findByTestId('wiki-note-author-username-link');
describe('renders correctly', () => {
const shouldNotDisplayExternalParticipantText = () => {
expect(wrapper.findByText('(external participant)').exists()).toBe(false);
@ -61,11 +64,11 @@ describe('NoteHeader', () => {
});
it('should not render author name link', () => {
expect(wrapper.find('a[data-testid="wiki-note-author-name-link"]').exists()).toBe(false);
expect(findAuthorNameLink().exists()).toBe(false);
});
it('should render author name correclty', async () => {
const authorName = await wrapper.findByTestId('wiki-note-author-name').text();
it('should render author name correclty', () => {
const authorName = wrapper.findByTestId('wiki-note-author-name').text();
expect(authorName).toBe('John Doe');
});
@ -79,47 +82,37 @@ describe('NoteHeader', () => {
});
describe('email participant is not set', () => {
it('should render author name in link', async () => {
const authorNameLink = wrapper.find('a[data-testid="wiki-note-author-name-link"]');
const authorName = authorNameLink.find('[data-testid="wiki-note-author-name"]');
it('should render author name in link', () => {
const authorNameLink = findAuthorNameLink();
const authorName = extendedWrapper(authorNameLink).findByTestId('wiki-note-author-name');
expect(await authorName.text()).toBe('John Doe');
expect(authorName.text()).toBe('John Doe');
});
it('should render author name link with href to author path when author path is set', () => {
const authorNameLink = wrapper.find('a[data-testid="wiki-note-author-name-link"]');
expect(authorNameLink.attributes('href')).toBe('/johndoe');
expect(findAuthorNameLink().attributes('href')).toBe('/johndoe');
});
it('should default to author webUrl for author name link author path is not set', () => {
wrapper = createWrapper({ author: { ...author, path: null } });
const authorNameLink = wrapper.find('a[data-testid="wiki-note-author-name-link"]');
expect(authorNameLink.attributes('href')).toBe('https://example.com/johndoe');
expect(findAuthorNameLink().attributes('href')).toBe('https://example.com/johndoe');
});
it('should render author username correctly', async () => {
const authorUsernameLink = wrapper.find(
'a[data-testid="wiki-note-author-username-link"]',
);
const authorUsername = await authorUsernameLink
.find('[data-testid="wiki-note-username"]')
it('should render author username correctly', () => {
const authorUsernameLink = findAuthorUsernameLink();
const authorUsername = extendedWrapper(authorUsernameLink)
.findByTestId('wiki-note-username')
.text();
expect(authorUsername).toBe('@johndoe');
});
it('should render author username link with href to author path when it is set', () => {
const authorUsernameLink = wrapper.find(
'a[data-testid="wiki-note-author-username-link"]',
);
expect(authorUsernameLink.attributes('href')).toBe('/johndoe');
expect(findAuthorUsernameLink().attributes('href')).toBe('/johndoe');
});
it('should default to author webUrl for author username link author path is not set', () => {
wrapper = createWrapper({ author: { ...author, path: null } });
const authorUsernameLink = wrapper.find(
'a[data-testid="wiki-note-author-username-link"]',
);
expect(authorUsernameLink.attributes('href')).toBe('https://example.com/johndoe');
expect(findAuthorUsernameLink().attributes('href')).toBe('https://example.com/johndoe');
});
it('should note display external participant text', () => {
@ -153,14 +146,14 @@ describe('NoteHeader', () => {
expect(wrapper.findComponent(GlBadge).exists()).toBe(false);
});
it('should render internal note badge correctly when isInternalNote prop is true', async () => {
it('should render internal note badge correctly when isInternalNote prop is true', () => {
wrapper = createWrapper({ isInternalNote: true });
const glBadge = wrapper.findComponent(GlBadge);
expect(glBadge.element.getAttribute('title')).toBe(
'This internal note will always remain confidential',
);
expect(await glBadge.text()).toBe('Internal note');
expect(glBadge.text()).toBe('Internal note');
});
});
@ -182,12 +175,12 @@ describe('NoteHeader', () => {
});
describe('when author username link is hovered', () => {
const hoverUserNameLink = async () => {
await wrapper.findByTestId('wiki-note-author-username-link').trigger('mouseenter');
const hoverUserNameLink = () => {
findAuthorUsernameLink().trigger('mouseenter');
};
const leaveUserNameLink = async () => {
await wrapper.findByTestId('wiki-note-author-username-link').trigger('mouseleave');
const leaveUserNameLink = () => {
findAuthorUsernameLink().trigger('mouseleave');
};
beforeEach(() => {
@ -196,7 +189,7 @@ describe('NoteHeader', () => {
it('should underline author Name link', async () => {
await hoverUserNameLink();
const { classList } = wrapper.findByTestId('wiki-note-author-name-link').element;
const { classList } = findAuthorNameLink().element;
expect(classList.contains('text-underline')).toBe(true);
});
@ -205,7 +198,7 @@ describe('NoteHeader', () => {
await hoverUserNameLink();
await leaveUserNameLink();
const { classList } = wrapper.findByTestId('wiki-note-author-name-link').element;
const { classList } = findAuthorNameLink().element;
expect(classList.contains('text-underline')).toBe(false);
});
});

View File

@ -43,7 +43,7 @@ describe('Pipeline Wizard - wrapper.vue', () => {
wrapper.findAllComponents(WizardStep).wrappers.find((w) => w.isVisible());
const getGlProgressBarWrapper = () => wrapper.getComponent(GlProgressBar);
const findFirstVisibleStep = () =>
wrapper.findAllComponents('[data-testid="step"]').wrappers.find((w) => w.isVisible());
wrapper.findAllByTestId('step').wrappers.find((w) => w.isVisible());
const findFirstInputFieldForTarget = (target) =>
wrapper.find(`[data-input-target="${target}"]`).find('input');

View File

@ -8,11 +8,11 @@ describe('Diff File Options Menu', () => {
<div class="rd-diff-file">
<div class="rd-diff-file-header" data-testid="rd-diff-file-header">
<div class="rd-diff-file-options-menu gl-ml-2">
<div class="js-options-menu">
<div data-options-menu>
<script type="application/json">
[{"text": "${item1.text}", "href": "${item1.path}"}]
</script>
<button class="js-options-button" data-click="toggleOptionsMenu" type="button"></button>
<button data-click="toggleOptionsMenu" type="button"></button>
</div>
</div>
</div>
@ -24,8 +24,8 @@ describe('Diff File Options Menu', () => {
function get(element) {
const elements = {
file: () => document.querySelector('diff-file'),
container: () => get('file').querySelector('.js-options-menu'),
serverButton: () => get('container').querySelector('.js-options-button'),
container: () => get('file').querySelector('[data-options-menu]'),
serverButton: () => get('container').querySelector('[data-click="toggleOptionsMenu"]'),
vueButton: () => get('container').querySelector('[data-testid="base-dropdown-toggle"]'),
menuItems: () =>
get('container').querySelectorAll('[data-testid="disclosure-dropdown-item"]'),

View File

@ -1,4 +1,4 @@
import Vue from 'vue';
import Vue, { nextTick } from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import VueApollo from 'vue-apollo';
@ -12,6 +12,7 @@ import StatusBar from '~/search/results/components/status_bar.vue';
import EmptyResult from '~/search/results/components/result_empty.vue';
import ErrorResult from '~/search/results/components/result_error.vue';
import mutations from '~/search/store/mutations';
import { logError } from '~/lib/logger';
import {
MOCK_QUERY,
mockGetBlobSearchQuery,
@ -22,7 +23,7 @@ import {
Vue.use(Vuex);
Vue.use(VueApollo);
jest.mock('~/alert');
jest.mock('~/lib/logger');
describe('GlobalSearchResultsApp', () => {
let wrapper;
@ -66,7 +67,7 @@ describe('GlobalSearchResultsApp', () => {
describe('when loading results', () => {
beforeEach(async () => {
createComponent({
initialState: { query: { scope: 'blobs' }, searchType: 'zoekt' },
initialState: { query: { scope: 'blobs', search: 'test' }, searchType: 'zoekt' },
queryHandler: mockQueryLoading,
});
jest.advanceTimersByTime(500);
@ -81,14 +82,16 @@ describe('GlobalSearchResultsApp', () => {
describe('when component has load error', () => {
beforeEach(async () => {
createComponent({
initialState: { query: { scope: 'blobs' }, searchType: 'zoekt' },
initialState: { query: { scope: 'blobs', search: 'test' }, searchType: 'zoekt' },
queryHandler: mockQueryError,
});
jest.runOnlyPendingTimers();
jest.advanceTimersByTime(500);
await waitForPromises();
await nextTick();
});
it('renders alert', () => {
it('renders error state', () => {
expect(logError).toHaveBeenCalledWith(new Error('Network error'));
expect(findError().exists()).toBe(true);
expect(findZoektBlobResults().exists()).toBe(false);
});
@ -126,7 +129,7 @@ describe('GlobalSearchResultsApp', () => {
getterSpies.currentScope = jest.fn(() => 'blobs');
createComponent({
initialState: {
query: { scope: 'blobs' },
query: { scope: 'blobs', search: 'test' },
searchType: 'zoekt',
navigation: MOCK_NAVIGATION_DATA,
},

View File

@ -170,14 +170,10 @@ describe('ProjectToken', () => {
}),
},
});
const suggestionTexts = wrapper
.text()
.split('\n')
.map((text) => text.trim())
.filter(Boolean);
todosProjectsResponse.data.projects.nodes.forEach((project, i) => {
expect(suggestionTexts[i]).toBe(project.fullPath);
const childIndex = i + 1;
expect(wrapper.findAll('div').at(childIndex).text()).toBe(project.fullPath);
});
});

View File

@ -23,6 +23,20 @@ RSpec.describe PackagesHelper, feature_category: :package_registry do
end
end
describe '#package_registry_group_url' do
context 'when registry_type is not provided' do
subject { helper.package_registry_group_url(1) }
it { is_expected.to eq("#{base_url}groups/1/-/packages/maven") }
end
context 'when registry_type is provided' do
subject { helper.package_registry_group_url(1, :npm) }
it { is_expected.to eq("#{base_url}groups/1/-/packages/npm") }
end
end
describe '#package_registry_project_url' do
it 'returns maven registry url when registry_type is not provided' do
url = helper.package_registry_project_url(1)

View File

@ -190,6 +190,7 @@ project_setting:
- product_analytics_configurator_connection_string
- code_suggestions
- duo_features_enabled
- auto_duo_code_review_enabled
- require_reauthentication_to_approve
- observability_alerts_enabled
- spp_repository_pipeline_access

View File

@ -0,0 +1,131 @@
# frozen_string_literal: true
require 'spec_helper'
# These shared_contexts and shared_examples are used to test the # CI/CD templates
# powering the Application Security Testing (AST) features.
# There is a lot of repitition across these templates and the setup for these
# specs is expensive.
#
# Usually, each template will have its behavior tested in these 3 different pipelines types:
# - default branch pipeline
# - feature branch pipeline
# - MR pipeline
#
# Additionally, some templates have CI jobs using rules:exists which involves setting up
# a project with a repository that contains specific files. To improve speed and
# efficiency, the setup steps are extracted into shared_context that use let_it_be and
# before(:context). This ensures to create the project only once per scenario.
# Though these contexts assume a particular usage which reduces flexibility.
# Please check existing specs for examples.
RSpec.shared_context 'with CI variables' do |variables|
before do
variables.each do |(key, value)|
create(:ci_variable, project: project, key: key, value: value)
end
end
end
RSpec.shared_context 'with default branch pipeline setup' do
let_it_be(:pipeline_branch) { default_branch }
let_it_be(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch) }
end
RSpec.shared_context 'with feature branch pipeline setup' do
let_it_be(:pipeline_branch) { feature_branch }
let_it_be(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch) }
before(:context) do
project.repository.create_file(
project.creator,
'branch.patch',
'',
message: "Add file to feature branch",
branch_name: pipeline_branch,
# Ensure new branch includes expected project files from default branch.
start_branch_name: default_branch)
end
end
RSpec.shared_context 'with MR pipeline setup' do
let_it_be(:pipeline_branch) { 'mr_branch' }
let_it_be(:service) { MergeRequests::CreatePipelineService.new(project: project, current_user: user) }
let(:pipeline) { service.execute(merge_request).payload }
let_it_be(:merge_request) do
# Ensure MR has at least one commit otherwise MR pipeline won't be triggered.
# This also seems to be required to happen before the MR creation.
project.repository.create_file(
project.creator,
'MR.patch',
'',
message: "Add patch to MR branch",
branch_name: pipeline_branch,
# Ensure new branch includes expected project files from default branch.
start_branch_name: default_branch)
create(:merge_request,
source_project: project,
source_branch: pipeline_branch,
target_project: project,
target_branch: default_branch)
end
end
RSpec.shared_examples 'has expected jobs' do |jobs|
it 'includes jobs', if: jobs.any? do
expect(pipeline.builds.pluck(:name)).to match_array(jobs)
# TODO: Failing for DAST related templates with error:
# "Insufficient permissions for dast_configuration keyword"
# expect(pipeline.errors.full_messages).to be_empty unless ignore_errors
end
it 'includes no jobs', if: jobs.empty? do
expect(pipeline.builds.pluck(:name)).to be_empty
expect(pipeline.errors.full_messages).to match_array(
[sanitize_message(Ci::Pipeline.rules_failure_message)])
end
end
RSpec.shared_examples 'has FIPS compatible jobs' do |variable, jobs|
context 'when CI_GITLAB_FIPS_MODE=false', fips_mode: false do
jobs.each do |job|
it "sets #{variable} to '' for job #{job}" do
build = pipeline.builds.find_by(name: job)
expect(String(build.variables.to_hash[variable])).to eql('')
end
end
end
context 'when CI_GITLAB_FIPS_MODE=true', :fips_mode do
jobs.each do |job|
it "sets #{variable} to '-fips' for job #{job}" do
build = pipeline.builds.find_by(name: job)
expect(String(build.variables.to_hash[variable])).to eql('-fips')
end
end
end
end
RSpec.shared_examples 'has jobs that can be disabled' do |key, disabled_values, jobs|
disabled_values.each do |disabled_value|
context "when #{key} is set to '#{disabled_value}'" do
before do
create(:ci_variable, project: project, key: key, value: disabled_value)
end
include_examples 'has expected jobs', []
end
end
# This ensures we don't accidentally disable jobs when user sets the variable to 'false'.
context "when #{key} is set to 'false'" do
before do
create(:ci_variable, project: project, key: key, value: 'false')
end
include_examples 'has expected jobs', jobs
end
end

View File

@ -14,11 +14,12 @@ RSpec.describe 'gitlab:db:lock_writes', :reestablished_active_record_base, featu
let(:logger) { instance_double(Logger, level: nil) }
let(:dry_run) { false }
let(:verbose) { false }
let(:include_partitions) { true }
before do
allow(Logger).to receive(:new).with($stdout).and_return(logger)
allow(Gitlab::Database::TablesLocker).to receive(:new).with(
logger: logger, dry_run: dry_run
logger: logger, dry_run: dry_run, include_partitions: include_partitions
).and_return(table_locker)
end
@ -44,6 +45,16 @@ RSpec.describe 'gitlab:db:lock_writes', :reestablished_active_record_base, featu
include_examples "call table locker", :lock_writes
end
context 'when environment sets INCLUDE_PARTITIONS to false' do
let(:include_partitions) { false }
before do
stub_env('INCLUDE_PARTITIONS', 'false')
end
include_examples "call table locker", :lock_writes
end
context 'when environment sets DRY_RUN to false' do
let(:dry_run) { false }
@ -96,6 +107,16 @@ RSpec.describe 'gitlab:db:lock_writes', :reestablished_active_record_base, featu
include_examples "call table locker", :unlock_writes
end
context 'when environment sets INCLUDE_PARTITIONS to false' do
let(:include_partitions) { false }
before do
stub_env('INCLUDE_PARTITIONS', 'false')
end
include_examples "call table locker", :unlock_writes
end
context 'when environment sets DRY_RUN to false' do
before do
stub_env('DRY_RUN', 'false')

View File

@ -3,7 +3,9 @@
require 'spec_helper'
RSpec.describe 'groups/packages/index.html.haml', feature_category: :package_registry do
let_it_be(:group) { build(:group) }
include PackagesHelper
let_it_be(:group) { build_stubbed(:group) }
subject { rendered }
@ -16,4 +18,40 @@ RSpec.describe 'groups/packages/index.html.haml', feature_category: :package_reg
expect(rendered).to have_selector('#js-vue-packages-list')
end
it 'sets npm group url' do
render
expect(rendered).to have_selector(
"[data-npm-group-url=\"#{package_registry_group_url(group.id, :npm)}\"]"
)
end
describe 'settings path' do
context 'without permission' do
before do
allow(view).to receive(:show_group_package_registry_settings).and_return(false)
end
it 'sets empty settings path' do
render
expect(rendered).to have_selector('[data-settings-path=""]')
end
end
context 'with permission' do
before do
allow(view).to receive(:show_group_package_registry_settings).and_return(true)
end
it 'sets group settings path' do
render
expect(rendered).to have_selector(
"[data-settings-path=\"#{group_settings_packages_and_registries_path(group)}\"]"
)
end
end
end
end

View File

@ -3,7 +3,10 @@
require 'spec_helper'
RSpec.describe 'projects/packages/packages/index.html.haml', feature_category: :package_registry do
let_it_be(:project) { build(:project) }
include PackagesHelper
let_it_be(:group) { build_stubbed(:group) }
let_it_be(:project) { build(:project, namespace: group, group: group) }
subject { rendered }
@ -16,4 +19,39 @@ RSpec.describe 'projects/packages/packages/index.html.haml', feature_category: :
expect(rendered).to have_selector('#js-vue-packages-list')
end
it 'sets npm group url' do
render
expect(rendered).to have_selector(
"[data-npm-group-url=\"#{package_registry_group_url(project.group&.id, :npm)}\"]"
)
end
describe 'settings path' do
context 'without permission' do
before do
allow(view).to receive(:show_package_registry_settings).and_return(false)
end
it 'sets empty settings path' do
render
expect(rendered).to have_selector('[data-settings-path=""]')
end
end
context 'with permission' do
before do
allow(view).to receive(:show_package_registry_settings).and_return(true)
end
it 'sets project settings path' do
render
expect(rendered).to have_selector(
"[data-settings-path=\"#{project_settings_packages_and_registries_path(project)}#package-registry-settings\"]"
)
end
end
end
end

240
yarn.lock
View File

@ -2426,100 +2426,105 @@
estree-walker "^2.0.2"
picomatch "^2.3.1"
"@rollup/rollup-android-arm-eabi@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.3.tgz#eb1b0a1d75c5f048b8d41eb30188c22292676c02"
integrity sha512-8kq/NjMKkMTGKMPldWihncOl62kgnLYk7cW+/4NCUWfS70/wz4+gQ7rMxMMpZ3dIOP/xw7wKNzIuUnN/H2GfUg==
"@rollup/rollup-android-arm-eabi@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz#d964ee8ce4d18acf9358f96adc408689b6e27fe3"
integrity sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==
"@rollup/rollup-android-arm64@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.3.tgz#850f0962a7a98a698dfc4b7530a3932b486d84c0"
integrity sha512-1PqMHiuRochQ6++SDI7SaRDWJKr/NgAlezBi5nOne6Da6IWJo3hK0TdECBDwd92IUDPG4j/bZmWuwOnomNT8wA==
"@rollup/rollup-android-arm64@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz#9b5e130ecc32a5fc1e96c09ff371743ee71a62d3"
integrity sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==
"@rollup/rollup-darwin-arm64@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.3.tgz#150c4cfacd11ca3fe2904a25bcfd3f948aa8fd39"
integrity sha512-fqbrykX4mGV3DlCDXhF4OaMGcchd2tmLYxVt3On5oOZWVDFfdEoYAV2alzNChl8OzNaeMAGqm1f7gk7eIw/uDg==
"@rollup/rollup-darwin-arm64@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz#ef439182c739b20b3c4398cfc03e3c1249ac8903"
integrity sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==
"@rollup/rollup-darwin-x64@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.3.tgz#27501960a733043c2b0634c884d20cd456d1cdef"
integrity sha512-8Wxrx/KRvMsTyLTbdrMXcVKfpW51cCNW8x7iQD72xSEbjvhCY3b+w83Bea3nQfysTMR7K28esc+ZFITThXm+1w==
"@rollup/rollup-darwin-x64@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz#d7380c1531ab0420ca3be16f17018ef72dd3d504"
integrity sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==
"@rollup/rollup-freebsd-arm64@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.3.tgz#a54caebb98ab71aaf67e826cc9e6a145fb30ffb5"
integrity sha512-lpBmV2qSiELh+ATQPTjQczt5hvbTLsE0c43Rx4bGxN2VpnAZWy77we7OO62LyOSZNY7CzjMoceRPc+Lt4e9J6A==
"@rollup/rollup-freebsd-arm64@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz#cbcbd7248823c6b430ce543c59906dd3c6df0936"
integrity sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==
"@rollup/rollup-freebsd-x64@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.3.tgz#2312f47788b3e334b14edb7eee748e9d545fd856"
integrity sha512-sNPvBIXpgaYcI6mAeH13GZMXFrrw5mdZVI1M9YQPRG2LpjwL8DSxSIflZoh/B5NEuOi53kxsR/S2GKozK1vDXA==
"@rollup/rollup-freebsd-x64@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz#96bf6ff875bab5219c3472c95fa6eb992586a93b"
integrity sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==
"@rollup/rollup-linux-arm-gnueabihf@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.3.tgz#aaaa3f678ab3bcdf8ebda600ed2a9f04fe00d9cc"
integrity sha512-MW6N3AoC61OfE1VgnN5O1OW0gt8VTbhx9s/ZEPLBM11wEdHjeilPzOxVmmsrx5YmejpGPvez8QwGGvMU+pGxpw==
"@rollup/rollup-linux-arm-gnueabihf@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz#d80cd62ce6d40f8e611008d8dbf03b5e6bbf009c"
integrity sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==
"@rollup/rollup-linux-arm-musleabihf@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.3.tgz#ec7c8d98c79091afda6804fdf72d1d202217b9e4"
integrity sha512-2SQkhr5xvatYq0/+H6qyW0zvrQz9LM4lxGkpWURLoQX5+yP8MsERh4uWmxFohOvwCP6l/+wgiHZ1qVwLDc7Qmw==
"@rollup/rollup-linux-arm-musleabihf@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz#75440cfc1e8d0f87a239b4c31dfeaf4719b656b7"
integrity sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==
"@rollup/rollup-linux-arm64-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.3.tgz#df198a61a48db932426eee593f3699aa289e90f5"
integrity sha512-R3JLYt8YoRwKI5shJsovLpcR6pwIMui/MGG/MmxZ1DYI3iRSKI4qcYrvYgDf4Ss2oCR3RL3F3dYK7uAGQgMIuQ==
"@rollup/rollup-linux-arm64-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz#ac527485ecbb619247fb08253ec8c551a0712e7c"
integrity sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==
"@rollup/rollup-linux-arm64-musl@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.3.tgz#97b231d2ca6fdeaa8d0e02de2f1f3896bedf14a3"
integrity sha512-4XQhG8v/t3S7Rxs7rmFUuM6j09hVrTArzONS3fUZ6oBRSN/ps9IPQjVhp62P0W3KhqJdQADo/MRlYRMdgxr/3w==
"@rollup/rollup-linux-arm64-musl@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz#74d2b5cb11cf714cd7d1682e7c8b39140e908552"
integrity sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==
"@rollup/rollup-linux-loongarch64-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.3.tgz#a1149b186e16d009d8fd715285e84ed63ba3cbbc"
integrity sha512-QlW1jCUZ1LHUIYCAK2FciVw1ptHsxzApYVi05q7bz2A8oNE8QxQ85NhM4arLxkAlcnS42t4avJbSfzSQwbIaKg==
"@rollup/rollup-linux-loongarch64-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz#a0a310e51da0b5fea0e944b0abd4be899819aef6"
integrity sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==
"@rollup/rollup-linux-powerpc64le-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.3.tgz#df3c2c25f800bc0bdf5e8cfc00372b5ac761bc5b"
integrity sha512-kMbLToizVeCcN69+nnm20Dh0hrRIAjgaaL+Wh0gWZcNt8e542d2FUGtsyuNsHVNNF3gqTJrpzUGIdwMGLEUM7g==
"@rollup/rollup-linux-powerpc64le-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz#4077e2862b0ac9f61916d6b474d988171bd43b83"
integrity sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==
"@rollup/rollup-linux-riscv64-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.3.tgz#c57b3e2c12969586f3513295cb36da96746edbf6"
integrity sha512-YgD0DnZ3CHtvXRH8rzjVSxwI0kMTr0RQt3o1N92RwxGdx7YejzbBO0ELlSU48DP96u1gYYVWfUhDRyaGNqJqJg==
"@rollup/rollup-linux-riscv64-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz#5812a1a7a2f9581cbe12597307cc7ba3321cf2f3"
integrity sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==
"@rollup/rollup-linux-s390x-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.3.tgz#e6ac0788471a9f7400b358eb5f91292efcc900c4"
integrity sha512-dIOoOz8altjp6UjAi3U9EW99s8nta4gzi52FeI45GlPyrUH4QixUoBMH9VsVjt+9A2RiZBWyjYNHlJ/HmJOBCQ==
"@rollup/rollup-linux-riscv64-musl@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz#973aaaf4adef4531375c36616de4e01647f90039"
integrity sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==
"@rollup/rollup-linux-x64-gnu@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.3.tgz#017bb2808665d69ba55740cae02708ea8cb45885"
integrity sha512-lOyG3aF4FTKrhpzXfMmBXgeKUUXdAWmP2zSNf8HTAXPqZay6QYT26l64hVizBjq+hJx3pl0DTEyvPi9sTA6VGA==
"@rollup/rollup-linux-s390x-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz#9bad59e907ba5bfcf3e9dbd0247dfe583112f70b"
integrity sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==
"@rollup/rollup-linux-x64-musl@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.3.tgz#ac3de953f8e31b08f1528e17f0524af15b2df38c"
integrity sha512-usztyYLu2i+mYzzOjqHZTaRXbUOqw3P6laNUh1zcqxbPH1P2Tz/QdJJCQSnGxCtsRQeuU2bCyraGMtMumC46rw==
"@rollup/rollup-linux-x64-gnu@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz#68b045a720bd9b4d905f462b997590c2190a6de0"
integrity sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==
"@rollup/rollup-win32-arm64-msvc@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.3.tgz#183fb4b849accdf68d430894ada2b88eea95a140"
integrity sha512-ojFOKaz/ZyalIrizdBq2vyc2f0kFbJahEznfZlxdB6pF9Do6++i1zS5Gy6QLf8D7/S57MHrmBLur6AeRYeQXSA==
"@rollup/rollup-linux-x64-musl@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz#8e703e2c2ad19ba7b2cb3d8c3a4ad11d4ee3a282"
integrity sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==
"@rollup/rollup-win32-ia32-msvc@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.3.tgz#3fd1b93867442ecd3d2329b902b111853600cc6c"
integrity sha512-K/V97GMbNa+Da9mGcZqmSl+DlJmWfHXTuI9V8oB2evGsQUtszCl67+OxWjBKpeOnYwox9Jpmt/J6VhpeRCYqow==
"@rollup/rollup-win32-arm64-msvc@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz#c5bee19fa670ff5da5f066be6a58b4568e9c650b"
integrity sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==
"@rollup/rollup-win32-x64-msvc@4.34.3":
version "4.34.3"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.3.tgz#2cd47d213ddd921bab1470a3e31312ee37aac08a"
integrity sha512-CUypcYP31Q8O04myV6NKGzk9GVXslO5EJNfmARNSzLF2A+5rmZUlDJ4et6eoJaZgBT9wrC2p4JZH04Vkic8HdQ==
"@rollup/rollup-win32-ia32-msvc@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz#846e02c17044bd922f6f483a3b4d36aac6e2b921"
integrity sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==
"@rollup/rollup-win32-x64-msvc@4.40.0":
version "4.40.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz#fd92d31a2931483c25677b9c6698106490cbbc76"
integrity sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==
"@rtsao/scc@^1.1.0":
version "1.1.0"
@ -3483,10 +3488,10 @@
dependencies:
"@types/ms" "*"
"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
"@types/estree@*", "@types/estree@1.0.7", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
"@types/events@*":
version "1.2.0"
@ -7880,6 +7885,11 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
fdir@^6.4.3:
version "6.4.3"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72"
integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==
figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@ -13388,32 +13398,33 @@ robust-predicates@^3.0.0:
resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
rollup@^4.30.1:
version "4.34.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.3.tgz#319a3c3065d9f80fef2faef24ef8aba7773e7f77"
integrity sha512-ORCtU0UBJyiAIn9m0llUXJXAswG/68pZptCrqxHG7//Z2DDzAUeyyY5hqf4XrsGlUxscMr9GkQ2QI7KTLqeyPw==
rollup@^4.34.9:
version "4.40.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.40.0.tgz#13742a615f423ccba457554f006873d5a4de1920"
integrity sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==
dependencies:
"@types/estree" "1.0.6"
"@types/estree" "1.0.7"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.34.3"
"@rollup/rollup-android-arm64" "4.34.3"
"@rollup/rollup-darwin-arm64" "4.34.3"
"@rollup/rollup-darwin-x64" "4.34.3"
"@rollup/rollup-freebsd-arm64" "4.34.3"
"@rollup/rollup-freebsd-x64" "4.34.3"
"@rollup/rollup-linux-arm-gnueabihf" "4.34.3"
"@rollup/rollup-linux-arm-musleabihf" "4.34.3"
"@rollup/rollup-linux-arm64-gnu" "4.34.3"
"@rollup/rollup-linux-arm64-musl" "4.34.3"
"@rollup/rollup-linux-loongarch64-gnu" "4.34.3"
"@rollup/rollup-linux-powerpc64le-gnu" "4.34.3"
"@rollup/rollup-linux-riscv64-gnu" "4.34.3"
"@rollup/rollup-linux-s390x-gnu" "4.34.3"
"@rollup/rollup-linux-x64-gnu" "4.34.3"
"@rollup/rollup-linux-x64-musl" "4.34.3"
"@rollup/rollup-win32-arm64-msvc" "4.34.3"
"@rollup/rollup-win32-ia32-msvc" "4.34.3"
"@rollup/rollup-win32-x64-msvc" "4.34.3"
"@rollup/rollup-android-arm-eabi" "4.40.0"
"@rollup/rollup-android-arm64" "4.40.0"
"@rollup/rollup-darwin-arm64" "4.40.0"
"@rollup/rollup-darwin-x64" "4.40.0"
"@rollup/rollup-freebsd-arm64" "4.40.0"
"@rollup/rollup-freebsd-x64" "4.40.0"
"@rollup/rollup-linux-arm-gnueabihf" "4.40.0"
"@rollup/rollup-linux-arm-musleabihf" "4.40.0"
"@rollup/rollup-linux-arm64-gnu" "4.40.0"
"@rollup/rollup-linux-arm64-musl" "4.40.0"
"@rollup/rollup-linux-loongarch64-gnu" "4.40.0"
"@rollup/rollup-linux-powerpc64le-gnu" "4.40.0"
"@rollup/rollup-linux-riscv64-gnu" "4.40.0"
"@rollup/rollup-linux-riscv64-musl" "4.40.0"
"@rollup/rollup-linux-s390x-gnu" "4.40.0"
"@rollup/rollup-linux-x64-gnu" "4.40.0"
"@rollup/rollup-linux-x64-musl" "4.40.0"
"@rollup/rollup-win32-arm64-msvc" "4.40.0"
"@rollup/rollup-win32-ia32-msvc" "4.40.0"
"@rollup/rollup-win32-x64-msvc" "4.40.0"
fsevents "~2.3.2"
rope-sequence@^1.3.0:
@ -14615,6 +14626,14 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==
tinyglobby@^0.2.12:
version "0.2.12"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5"
integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==
dependencies:
fdir "^6.4.3"
picomatch "^4.0.2"
tippy.js@^6.3.7:
version "6.3.7"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
@ -15368,14 +15387,17 @@ vite-plugin-ruby@^5.1.1:
debug "^4.3.4"
fast-glob "^3.3.2"
vite@^6.2.6:
version "6.2.6"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.6.tgz#7f0ccf2fdc0c1eda079ce258508728e2473d3f61"
integrity sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==
vite@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.3.0.tgz#17c86b3a0f4d20b210fe89f22f7b27d5926fcce5"
integrity sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==
dependencies:
esbuild "^0.25.0"
fdir "^6.4.3"
picomatch "^4.0.2"
postcss "^8.5.3"
rollup "^4.30.1"
rollup "^4.34.9"
tinyglobby "^0.2.12"
optionalDependencies:
fsevents "~2.3.3"