Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-05-22 00:13:47 +00:00
parent 31de60f0ac
commit 074caf94a9
56 changed files with 779 additions and 432 deletions

View File

@ -25,6 +25,7 @@ workflow:
--chart-sha "${GITLAB_HELM_CHART_REF}" \
--admin-token "${GITLAB_QA_ADMIN_ACCESS_TOKEN}" \
--retry 1 \
--resource-preset high \
--ci \
${EXTRA_DEPLOY_VALUES}
- export QA_GITLAB_URL="http://gitlab.${GITLAB_DOMAIN}"
@ -66,6 +67,7 @@ workflow:
- |
bundle exec orchestrator create deployment "${DEPLOYMENT_TYPE}" \
--chart-sha "${GITLAB_HELM_CHART_REF}" \
--resource-preset high \
--ci \
--print-deploy-args \
$(cat $CI_PROJECT_DIR/EXTRA_DEPLOY_VALUES)

View File

@ -6,7 +6,7 @@
/assign @gitlab-org/secure/static-analysis/reaction-rotation
Note: If the rules release includes support for a new file type, the engineer(s) on reaction rotation will refine and schedule the for an upcoming milestone.
Note: If the rules release includes support for a new file type, the engineer(s) on reaction rotation will refine and schedule the for an upcoming milestone.
## Actions for ~"group::static analysis" team member
@ -22,9 +22,9 @@ Release the new Semgrep SAST ruleset version X.Y.Z by going through the sequence
```
1. [ ] If support for a new file type is required:
1. [ ] Add new extension to https://gitlab.com/gitlab-org/security-products/analyzers/semgrep/-/blob/main/plugin/plugin.go.
1. [ ] Update [SAST components template](https://gitlab.com/components/sast/-/blob/main/templates/sast.yml) to add support for new file type
1. [ ] Update the SAST templates [SAST.latest.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml)
1. [ ] Update [SAST components template](https://gitlab.com/components/sast/-/blob/main/templates/sast.yml) to add support for new file type.
1. [ ] Update the SAST templates [SAST.latest.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml).
and [SAST.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml) to support new file types.
1. [ ] Update the [SAST documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/application_security/sast/_index.md)
1. [ ] Update the [SAST documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/application_security/sast/_index.md).
/label ~"group::static analysis" ~"devops::application security testing" ~"section::sec" ~"Category:SAST" ~"type::feature" ~"workflow::refinement"

View File

@ -97,5 +97,3 @@ dismiss_two_factor_auth_recovery_settings_check;:
file: app/helpers/users/callouts_helper.rb
all_visibility_levels_restricted?:
file: app/helpers/visibility_level_helper.rb
link_to_wiki_page:
file: app/helpers/wiki_helper.rb

View File

@ -3128,7 +3128,6 @@ RSpec/NamedSubject:
- 'spec/services/user_agent_detail_service_spec.rb'
- 'spec/services/users/activity_service_spec.rb'
- 'spec/services/users/approve_service_spec.rb'
- 'spec/services/users/assigned_issues_count_service_spec.rb'
- 'spec/services/users/keys_count_service_spec.rb'
- 'spec/services/users/reject_service_spec.rb'
- 'spec/services/vs_code/settings/create_or_update_service_spec.rb'

View File

@ -648,7 +648,6 @@ Style/InlineDisableAnnotation:
- 'app/services/todos/destroy/unauthorized_features_service.rb'
- 'app/services/topics/merge_service.rb'
- 'app/services/user_project_access_changed_service.rb'
- 'app/services/users/assigned_issues_count_service.rb'
- 'app/services/users/batch_status_cleaner_service.rb'
- 'app/services/users/destroy_service.rb'
- 'app/services/users/last_push_event_service.rb'

View File

@ -3,6 +3,7 @@ import { GlToggle, GlAlert } from '@gitlab/ui';
import { s__ } from '~/locale';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import getProjectPagesDeployments from '../queries/get_project_pages_deployments.graphql';
import LiveBlock from './live_block.vue';
import PagesDeployment from './deployment.vue';
import LoadMoreDeployments from './load_more_deployments.vue';
@ -11,6 +12,7 @@ export default {
components: {
CrudComponent,
LoadMoreDeployments,
LiveBlock,
PagesDeployment,
GlToggle,
GlAlert,
@ -57,6 +59,9 @@ export default {
loadedParallelDeploymentsCount() {
return this.parallelDeployments?.nodes.length || 0;
},
newestDeployment() {
return this.primaryDeployments?.nodes[0] || null;
},
},
apollo: {
primaryDeployments: {
@ -160,6 +165,8 @@ export default {
{{ $options.i18n.loadErrorMessage }}
</gl-alert>
<live-block :is-loading="!newestDeployment && $apollo.loading" :deployment="newestDeployment" />
<crud-component
:title="$options.i18n.title"
:is-loading="!primaryDeployments && $apollo.loading"

View File

@ -0,0 +1,100 @@
<script>
import { GlButton, GlSkeletonLoader } from '@gitlab/ui';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { SHORT_DATE_FORMAT_WITH_TIME } from '~/vue_shared/constants';
import { s__ } from '~/locale';
import { joinPaths } from '~/lib/utils/url_utility';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
export default {
name: 'PrimaryDeployment',
components: {
GlButton,
GlSkeletonLoader,
TimeAgo,
CrudComponent,
},
i18n: {
createdLabel: s__('Pages|Created'),
deployJobLabel: s__('Pages|Deploy job'),
lastUpdatedLabel: s__('Pages|Last updated'),
liveSite: s__('Pages|Your Pages site is live at'),
buttonLabel: s__('Pages|Visit site'),
},
static: {
SHORT_DATE_FORMAT_WITH_TIME,
},
inject: ['projectFullPath', 'primaryDomain'],
props: {
deployment: {
type: Object,
required: false,
default: () => ({}),
},
isLoading: {
type: Boolean,
required: true,
},
},
computed: {
domainName() {
return this.primaryDomain || this.deployment.url;
},
ciBuildUrl() {
return joinPaths(
gon.relative_url_root || '/',
this.projectFullPath,
'/-/jobs/',
`${this.deployment.ciBuildId}`,
);
},
},
};
</script>
<template>
<crud-component header-class="gl-rounded-lg gl-border-b-0 gl-gap-y-3" body-class="gl-hidden">
<template v-if="isLoading" #title>
<gl-skeleton-loader v-if="isLoading" :width="400" :lines="2" />
</template>
<template v-else #title>
<span data-testid="live-heading">
{{ $options.i18n.liveSite }}
<a
v-if="deployment.active"
:href="domainName"
target="_blank"
data-testid="live-heading-link"
>
{{ domainName }}
</a>
<span v-else class="gl-text-subtle">
{{ domainName }}
</span>
🎉
</span>
</template>
<template v-if="!isLoading" #description>
{{ $options.i18n.deployJobLabel }}
<a :href="ciBuildUrl" data-testid="deploy-job-number">{{ deployment.ciBuildId }}</a>
<template v-if="deployment.updatedAt">
<span aria-hidden="true">·</span>
{{ $options.i18n.lastUpdatedLabel }}
<time-ago :time="deployment.updatedAt" data-testid="last-updated-date" />
</template>
</template>
<template v-if="!isLoading" #actions>
<gl-button
icon="external-link"
:href="domainName"
target="_blank"
data-testid="visit-site-url"
>
{{ $options.i18n.buttonLabel }}
</gl-button>
</template>
</crud-component>
</template>

View File

@ -27,6 +27,7 @@ export default function initPages() {
apolloProvider,
provide: {
projectFullPath: el.dataset.fullPath,
primaryDomain: el.dataset.primaryDomain,
},
render(createElement) {
return createElement(PagesEdit, {});

View File

@ -44,11 +44,6 @@ export default {
await this.$nextTick();
initIssuablePopovers([this.$refs.reference.$el]);
},
methods: {
truncateText(text, length = 50) {
return text.length > length ? `${text.substring(0, length - 3)}...` : text;
},
},
};
</script>
<template>
@ -65,7 +60,7 @@ export default {
:data-group-path="group"
>
<gl-intersperse separator="">
<span>{{ truncateText(data.title) }}</span>
<span>{{ data.title }}</span>
<span> ({{ data.reference }}</span>
<span v-if="data.state === 'closed'"> - {{ __('closed') }}</span>
<span v-if="data.state === 'merged'"> - {{ __('merged') }}</span>

View File

@ -266,13 +266,16 @@ export default {
},
mounted() {
const DEFAULT_TEMPLATE_NAME = 'default';
if (this.isCreateFlow) {
const templateName = !this.isNewWorkItemRoute
? DEFAULT_TEMPLATE_NAME
: this.$route.query[paramName] ||
this.$route.query[oldParamNameFromPreWorkItems] ||
DEFAULT_TEMPLATE_NAME;
const templateNameFromRoute =
this.$route.query[paramName] || this.$route.query[oldParamNameFromPreWorkItems];
const templateName = !this.isNewWorkItemRoute
? DEFAULT_TEMPLATE_NAME
: templateNameFromRoute || DEFAULT_TEMPLATE_NAME;
// Ensure that template is set during Create Flow only if any of the following is true:;
// - Template name is present in URL.
// - Description is empty.
if (this.isCreateFlow && (templateNameFromRoute || this.descriptionText.trim() === '')) {
this.selectedTemplate = {
name: templateName,
projectId: null,
@ -301,10 +304,6 @@ export default {
}
if (this.isEditing && this.createFlow) {
this.startEditing();
// Reset edit state as the description
// can also be populated from localStorage
// when creating a new work item.
this.wasEdited = false;
}
},
error() {
@ -332,17 +331,6 @@ export default {
if (this.descriptionTemplate === this.descriptionText) {
return;
}
if (this.createFlow && !this.wasEdited && hasContent && this.appliedTemplate === '') {
// If the template was fetched on component mount
// while in create flow, we may also have populated
// the description from localStorage. In this case,
// we need avoid showing the warning on first load.
// while also setting appliedTemplate to the current
// template such that reset is possible.
this.appliedTemplate = this.descriptionTemplate;
this.wasEdited = true;
return;
}
if (!isUnchangedTemplate && (isDirty || hasContent)) {
this.showTemplateApplyWarning = true;
@ -366,9 +354,12 @@ export default {
this.isEditing = true;
this.wasEdited = true;
this.descriptionText = this.createFlow
? this.workItemDescription?.description
: getDraft(this.autosaveKey) || this.workItemDescription?.description;
const draftDescription = getDraft(this.autosaveKey) || '';
if (draftDescription.trim() !== '') {
this.descriptionText = draftDescription;
} else {
this.descriptionText = this.workItemDescription?.description;
}
this.initialDescriptionText = this.descriptionText;
await this.$nextTick();

View File

@ -1,3 +1,4 @@
@use 'functions';
@import 'page_bundles/mixins_and_variables_and_functions';
$base-font-size: 16px;
@ -13,3 +14,9 @@ $app-vertical-breakpoint: md;
$diff-file-border-width: 1px;
$diff-file-header-padding-y: $gl-spacing-scale-3;
$diff-file-line-number-z-index: 1;
$diff-file-hunk-header-z-index: functions.more-than($diff-file-line-number-z-index);
$diff-file-highlighted-line-number-z-index: functions.more-than($diff-file-hunk-header-z-index);
$diff-file-header-z-index: functions.more-than($diff-file-highlighted-line-number-z-index);

View File

@ -0,0 +1,3 @@
@function more-than($z-index) {
@return $z-index + 1;
}

View File

@ -7,19 +7,20 @@
}
.rd-diff-file {
$header-padding: constants.$diff-file-header-padding-y * 2;
// top/bottom padding + toggle button size, excluding borders
$file-header-height: calc($header-padding + $gl-button-small-size);
--rd-diff-file-header-height: #{$file-header-height};
--rd-diff-file-border-radius: #{calc($gl-border-radius-base - 1px)};
}
.rd-diff-file[data-virtual]:not([data-collapsed]) {
$header-padding: constants.$diff-file-header-padding-y * 2;
// top/bottom padding + toggle button size, excluding borders
$file-header-height: calc($header-padding + $gl-button-small-size);
// header top/bottom borders + body border
$total-borders: constants.$diff-file-border-width * 3;
content-visibility: var(--rd-content-visibility-auto, auto);
contain-intrinsic-size:
auto 0 auto
calc(#{$file-header-height} + #{$total-borders} + (#{constants.$code-row-height-target} * var(--total-rows)));
calc(var(--rd-diff-file-header-height) + #{$total-borders} + (#{constants.$code-row-height-target} * var(--total-rows)));
}
// content-visibility: auto; applies paint containment, which means you can not draw outside a diff file
@ -32,7 +33,7 @@
position: sticky;
// 1px offset to hide top border
top: calc(var(--rd-app-sticky-top, 1px) - 1px);
z-index: 3;
z-index: constants.$diff-file-header-z-index;
container-type: scroll-state;
}

View File

@ -17,13 +17,13 @@
// stylelint-disable-next-line gitlab/no-gl-class
:root.gl-dark .rd-text-view-root {
// disable side borders when light theme is used in dark mode
--rd-disable-border-colors: var(--code-light-theme);
--rd-disable-extended-borders: var(--code-light-theme);
}
// stylelint-disable-next-line gitlab/no-gl-class
:root:not(.gl-dark) .rd-text-view-root {
// disable side borders when light theme is used in dark mode
--rd-disable-border-colors: var(--code-dark-theme);
--rd-disable-extended-borders: var(--code-dark-theme);
}
.rd-text-view-root,
@ -51,8 +51,7 @@
.rd-hunk-header {
position: relative;
// overlap line numbers
z-index: 2;
z-index: constants.$diff-file-hunk-header-z-index;
// this is used when a hunk header doesn't have any text, only expand buttons
min-height: calc(1em * $code-line-height);
background-color: var(--code-diff-hunk-header-background-color, $gray-50);
@ -82,7 +81,7 @@
}
.rd-hunk-header-parallel,
.rd-hunk-lines-parallel, {
.rd-hunk-lines-parallel {
grid-template-columns: 50px 1fr 50px 1fr;
}
@ -129,11 +128,30 @@
padding: 0 10px 0 5px;
text-align: right;
background-color: var(--code-line-nubmer-background-color, $white);
scroll-margin-top: var(--rd-diff-file-header-height);
z-index: constants.$diff-file-line-number-z-index;
}
.rd-hunk-lines:has(.rd-line-number:target) {
--rd-disable-extended-borders: 1;
position: relative;
background-color: var(--code-highlighted-line-background-color);
border-radius: 0 0 var(--rd-row-bottom-right-radius) var(--rd-row-bottom-left-radius);
outline: 1px solid var(--code-highlighted-line-border-color);
z-index: constants.$diff-file-highlighted-line-number-z-index;
}
.rd-hunk-lines:has(.rd-line-number:target) .rd-line-number,
.rd-hunk-lines:has(.rd-line-number:target) .rd-line-content {
// handle empty cells
--code-line-nubmer-background-color: var(--code-highlighted-line-background-color);
--rd-line-background-color: var(--code-highlighted-line-background-color);
}
// child combinator improves performance of the selector
// duplicate .rd-hunk-lines-inline selector to override highlighted color
.rd-hunk-lines-parallel:hover > .rd-line-number:not(:empty),
.rd-hunk-lines-inline:hover > .rd-line-number {
.rd-hunk-lines-inline.rd-hunk-lines-inline:hover > .rd-line-number {
--rd-line-border-color: var(--code-line-nubmer-hover-border-color, #{$gl-color-purple-200});
--rd-adjacent-line-border-color: var(--rd-line-border-color);
--rd-line-background-color: var(--code-line-nubmer-hover-background-color, #{$gl-color-purple-100});
@ -197,12 +215,12 @@
}
.rd-line-number:first-child {
box-shadow: var(--rd-disable-border-colors, -1px 0 var(--rd-line-border-color));
box-shadow: var(--rd-disable-extended-borders, -1px 0 var(--rd-line-border-color));
border-bottom-left-radius: var(--rd-row-bottom-left-radius);
}
.rd-line-content:last-child {
box-shadow: var(--rd-disable-border-colors, 1px 0 var(--rd-line-border-color));
box-shadow: var(--rd-disable-extended-borders, 1px 0 var(--rd-line-border-color));
border-bottom-right-radius: var(--rd-row-bottom-right-radius);
}

View File

@ -92,6 +92,9 @@ $white-gc-bg: #eaf2f5;
--code-meta-diff-background-color: #{$gl-color-neutral-10};
--code-meta-diff-color: #{$gl-color-alpha-dark-24};
--code-highlighted-line-background-color: #{$gl-color-blue-50};
--code-highlighted-line-border-color: #{$gl-color-blue-300};
// Line numbers
.file-line-blame {
@include line-link($gl-color-neutral-1000, 'git');

View File

@ -165,6 +165,9 @@ $dark-il: #de935f;
--code-diff-expand-button-hover-background-color: #{$gl-color-neutral-300};
--code-diff-expand-button-hover-color: #{$gl-color-neutral-0};
--code-highlighted-line-background-color: #{$gl-color-orange-800};
--code-highlighted-line-border-color: #{$gl-color-orange-500};
// Highlight.js theme overrides (https://gitlab.com/gitlab-org/gitlab/-/issues/365167)
// We should be able to remove the overrides once the upstream issue is fixed (https://github.com/sourcegraph/sourcegraph/issues/23251)
@include hljs-override('title\\.class', $dark-nc);

View File

@ -138,6 +138,9 @@ $monokai-gh: #75715e;
--code-diff-expand-button-hover-background-color: #{$gl-color-neutral-300};
--code-diff-expand-button-hover-color: #{$gl-color-neutral-0};
--code-highlighted-line-background-color: #{$gl-color-orange-800};
--code-highlighted-line-border-color: #{$gl-color-orange-500};
// Highlight.js theme overrides (https://gitlab.com/gitlab-org/gitlab/-/issues/365167)
// We should be able to remove the overrides once the upstream issue is fixed (https://github.com/sourcegraph/sourcegraph/issues/23251)
@include hljs-override('string', $monokai-s);

View File

@ -37,6 +37,9 @@ $none-code-mark: #d3e3f4;
--code-new-diff-background-color: #{$gl-color-neutral-50};
--code-new-inline-diff-background-color: #{rgba(0,0,0,0.1)};
--code-highlighted-line-background-color: #{$gl-color-blue-50};
--code-highlighted-line-border-color: #{$gl-color-blue-300};
// Highlight.js theme overrides (https://gitlab.com/gitlab-org/gitlab/-/issues/365167)
// We should be able to remove the overrides once the upstream issue is fixed (https://github.com/sourcegraph/sourcegraph/issues/23251)
&.blob-viewer {

View File

@ -141,6 +141,9 @@ $solarized-dark-il: #2aa198;
--code-diff-expand-button-hover-background-color: #{lighten($solarized-dark-pre-bg, 20%)};
--code-diff-expand-button-hover-color: #{$gl-color-neutral-0};
--code-highlighted-line-background-color: #{$gl-color-orange-800};
--code-highlighted-line-border-color: #{$gl-color-orange-500};
// Highlight.js theme overrides (https://gitlab.com/gitlab-org/gitlab/-/issues/365167)
// We should be able to remove the overrides once the upstream issue is fixed (https://github.com/sourcegraph/sourcegraph/issues/23251)
@include hljs-override('string', $solarized-dark-s);

View File

@ -139,6 +139,9 @@ $solarized-light-il: #2aa198;
--code-diff-hunk-header-background-color: #{$solarized-light-matchline-bg};
--code-diff-hunk-header-color: #{$gl-color-alpha-dark-24};
--code-highlighted-line-background-color: #{$solarized-light-hll-bg};
--code-highlighted-line-border-color: #{darken($solarized-light-hll-bg, 15%)};
// Highlight.js theme overrides (https://gitlab.com/gitlab-org/gitlab/-/issues/365167)
// We should be able to remove the overrides once the upstream issue is fixed (https://github.com/sourcegraph/sourcegraph/issues/23251)
@include hljs-override('keyword', $solarized-light-k);

View File

@ -135,20 +135,6 @@ module IssuablesHelper
html.html_safe
end
def assigned_issuables_count(issuable_type)
case issuable_type
when :issues
::Users::AssignedIssuesCountService.new(
current_user: current_user,
max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT
).count
when :merge_requests
current_user.assigned_open_merge_requests_count
else
raise ArgumentError, "invalid issuable `#{issuable_type}`"
end
end
def issuable_reference(issuable)
@show_full_reference ? issuable.to_reference(full: true) : issuable.to_reference(@group || @project)
end

View File

@ -151,20 +151,6 @@ module MergeRequestsHelper
end
end
def user_merge_requests_counts
@user_merge_requests_counts ||= begin
assigned_count = assigned_issuables_count(:merge_requests)
review_requested_count = review_requested_merge_requests_count
total_count = assigned_count + review_requested_count
{
assigned: assigned_count,
review_requested: review_requested_count,
total: total_count
}
end
end
def reviewers_label(merge_request, include_value: true)
reviewers = merge_request.reviewers
@ -360,10 +346,6 @@ module MergeRequestsHelper
private
def review_requested_merge_requests_count
current_user.review_requested_open_merge_requests_count
end
def default_suggestion_commit_message(project)
project.suggestion_commit_message.presence || Gitlab::Suggestions::CommitMessage::DEFAULT_SUGGESTION_COMMIT_MESSAGE
end

View File

@ -17,10 +17,6 @@ module WikiHelper
add_to_breadcrumbs(_('Wiki'), wiki_path(page.wiki))
end
def link_to_wiki_page(page, **options)
link_to page.human_title, wiki_page_path(page.wiki, page), **options
end
def wiki_sidebar_toggle_button
render Pajamas::ButtonComponent.new(
icon: 'chevron-double-lg-left',

View File

@ -8,9 +8,6 @@ class DiffNote < Note
include DiffPositionableNote
include Gitlab::Utils::StrongMemoize
# Duplicate ignore_column from Note to make the db:check-migrations job pass
ignore_column :attachment, remove_with: '18.1', remove_after: '2025-05-15'
self.allow_legacy_sti_class = true
def self.noteable_types

View File

@ -29,8 +29,6 @@ class Note < ApplicationRecord
include EachBatch
include Spammable
ignore_column :attachment, remove_with: '18.1', remove_after: '2025-05-15'
cache_markdown_field :note, pipeline: :note, issuable_reference_expansion_enabled: true
redact_field :note

View File

@ -49,8 +49,6 @@ class User < ApplicationRecord
MAX_USERNAME_LENGTH = 255
MIN_USERNAME_LENGTH = 2
MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT = 100
SECONDARY_EMAIL_ATTRIBUTES = [
:commit_email,
:notification_email,

View File

@ -6,6 +6,7 @@ module Users
self.table_name = 'user_callouts'
# NOTE: to avoid false-positive dismissals, use new consecutive enum values for new callout IDs
enum :feature_name, {
gke_cluster_integration: 1,
gcp_signup_offer: 2,

View File

@ -8,6 +8,7 @@ module Users
belongs_to :group
# NOTE: to avoid false-positive dismissals, use new consecutive enum values for new callout IDs
enum :feature_name, {
invite_members_banner: 1,
approaching_seat_count_threshold: 2, # EE-only

View File

@ -8,13 +8,14 @@ module Users
belongs_to :project
# NOTE: to avoid false-positive dismissals, use new consecutive enum values for new callout IDs
enum :feature_name, {
awaiting_members_banner: 1, # EE-only
web_hook_disabled: 2,
# 3 was removed https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129703,
# and cleaned up https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129924, it can be replaced
# and cleaned up https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129924
namespace_storage_pre_enforcement_banner: 4, # EE-only
# 5,6,7 were removed https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118330, they can be replaced
# 5,6,7 were removed https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118330
license_check_deprecation_alert: 8 # EE-only
}

View File

@ -1,63 +0,0 @@
# frozen_string_literal: true
module Users
class AssignedIssuesCountService < ::BaseCountService
def initialize(current_user:, max_limit: User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT)
@current_user = current_user
@max_limit = max_limit
end
def cache_key
['users', @current_user.id, 'max_assigned_open_issues_count']
end
def cache_options
{ force: false, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD }
end
# rubocop: disable CodeReuse/ActiveRecord
def uncached_count
# When a user has many assigned issues, counting them all can be very slow.
# As a workaround, we will short-circuit the counting query once the count reaches some threshold.
#
# Concretely, given a threshold, say 100 (= max_limit),
# iterate through the first 100 issues, sorted by ID desc, assigned to the user using `issue_assignees` table.
# For each issue iterated, use IssuesFinder to check if the issue should be counted.
initializer = IssueAssignee
.select(:issue_id).joins(", LATERAL (#{finder_constraint.to_sql}) as issues")
.where(user_id: @current_user.id)
.order(issue_id: :desc)
.limit(1)
recursive_finder = initializer.where("issue_assignees.issue_id < assigned_issues.issue_id")
cte = <<~SQL
WITH RECURSIVE assigned_issues AS (
(
#{initializer.to_sql}
)
UNION ALL
(
SELECT next_assigned_issue.issue_id
FROM assigned_issues,
LATERAL (
#{recursive_finder.to_sql}
) next_assigned_issue
)
) SELECT COUNT(*) FROM (SELECT * FROM assigned_issues LIMIT #{@max_limit}) issues
SQL
ApplicationRecord.connection.execute(cte).first["count"]
end
# rubocop: enable CodeReuse/ActiveRecord
private
# rubocop: disable CodeReuse/ActiveRecord
def finder_constraint
IssuesFinder.new(@current_user, assignee_id: @current_user.id, state: 'opened', non_archived: true)
.execute
.where("issues.id=issue_assignees.issue_id").limit(1)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end

View File

@ -13,7 +13,10 @@
.tab-content.gl-tab-content.gl-pt-5
.tab-pane.active#overview
#js-pages{ data: { full_path: @project.full_path } }
#js-pages{ data: {
full_path: @project.full_path,
primary_domain: @project.pages_domains.present? ? @project.project_setting.pages_primary_domain : ''
} }
.tab-pane#domains-settings
.gl-flex.gl-flex-col.gl-gap-5

View File

@ -59,7 +59,8 @@ module Database
database_name: database_name,
with_retries: !connection.transaction_open?,
logger: nil,
dry_run: false
dry_run: false,
force: true
)
end
end

View File

@ -23,7 +23,8 @@ module Database
return unless Gitlab::Database.database_mode == Gitlab::Database::MODE_MULTIPLE_DATABASES
return if Feature.disabled?(:monitor_database_locked_tables, type: :ops)
lock_writes_results = ::Gitlab::Database::TablesLocker.new(dry_run: true, include_partitions: false).lock_writes
lock_writes_results = ::Gitlab::Database::TablesLocker.new(
dry_run: true, include_partitions: false).lock_writes
tables_lock_info_per_db = ::Gitlab::Database.database_base_models_with_gitlab_shared.keys.to_h do |db_name, _|
[db_name, INITIAL_DATABASE_RESULT.deep_dup]

View File

@ -62,7 +62,7 @@ users with auditor access have the same [permissions](../user/permissions.md) as
If you are signed in with auditor access, you:
- Have full access to the projects and groups you own.
- Have read-only access to the projects and groups you are not a member of.
- Have read-only access to the projects and groups you are not a member of. Because of a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/542815) this is not supported. Users must have at least the Reporter role for read-only tasks.
- Have [permissions](../user/permissions.md) based on your role to projects and groups you are a member of. For example, if you have the Developer role,
you can push commits or comment on issues.
- Can access the same resources using the GitLab UI or API.

View File

@ -26,7 +26,7 @@ running on.
## strace-parser
[strace-parser](https://gitlab.com/wchandler/strace-parser) is a small tool to analyze
[strace-parser](https://gitlab.com/gitlab-com/support/toolbox/strace-parser) is a small tool to analyze
and summarize raw `strace` data.
## `kubesos`

163
doc/development/callouts.md Normal file
View File

@ -0,0 +1,163 @@
---
stage: none
group: unassigned
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
title: Callouts
---
Callouts are a mechanism for presenting notifications to users. Users can dismiss the notifications, and the notifications can stay dismissed for a predefined duration. Notification dismissal is persistent across page loads and different user devices.
## Callout contexts
**Global context:** Callouts can be displayed to a user regardless of where they are in the application. For example, we can show a notification that reminds the user to have two-factor authentication recovery codes stored in a safe place. Dismissing this type of callout is effective for the particular user across the whole GitLab instance, no matter where they encountered the callout.
**Group and project contexts:** Callouts can also be displayed to a specific user and have a particular context binding, like a group or a project context. For example, group owners can be notified that their group is running out of available seats. Dismissing that callout would be effective for the particular user only in this particular group, while they would still see the same callout in other groups, if applicable.
Regardless of the context, dismissing a callout is only effective for the given user. Other users still see their relevant callouts.
## Callout IDs
Callouts use unique names to identify them, and a unique value to store dismissals data. For example:
```ruby
amazing_alert: 42,
```
Here `amazing_alert` is the callout ID, and `42` is a unique number to be used to register dismissals in the database. Here's how a group callout would be saved:
```plaintext
id | user_id | group_id | feature_name | dismissed_at
----+---------+----------+--------------+-------------------------------
0 | 1 | 4 | 42 | 2025-05-21 00:00:00.000000+00
```
To create a new callout ID, add a new key to the `feature_name` enum in the relevant context type registry file, using a unique name and a sequential value:
- Global context: `app/models/users/callout.rb`. Callouts are dismissed by a user globally. Related notifications would not be displayed anywhere in the GitLab instance for that user.
- Group context: `app/models/users/group_callout.rb`. Callouts are dismissed by a user in a given group. Related notifications are still shown to the user in other groups.
- Project context: `app/models/users/project_callout.rb`. Callouts dismissed by a user in a given project. Related notifications are still shown to the user in other projects.
**NOTE:** do not reuse old enum values, as it may lead to false-positive dismissals. Instead, create a new sequential number.
### Deprecating a callout
When we no longer need a callout, we can remove it from the callout ID enums. But since dismissal records in the DB use the numerical value of the enum, we need to explicitly preserve the deprecated ID from being reused, so that old dismissals don't affect the new callouts. Thus to remove a callout ID:
1. Remove the key/value pair from the enum hash
1. Leave an inline comment, mentioning the deprecated ID and the MR removing the callout
For example:
```diff
- amazing_alert: 42,
+ # 42 removed in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121920
```
## Server-side rendered callouts
This section describes using callouts when they are rendered on the server in `.haml` views, partials, or components.
### Dismissing the callouts on the client side
JavaScript helpers for callouts rely on certain selectors and data attributes to be present on the HTML of the notification, to properly call dismissal API endpoints, and hide the notification in the runtime. The wrapper of the notification needs to have a `.js-persistent-callout` CSS class with the following data-attributes:
```javascript
{
featureId, // Unique callout ID
dismissEndpoint, // Dismiss endpoint, unique for each callout context type
groupId, // optional, required for the group context
projectId, // optional, required for the project context
deferLinks, // optional, allows executing certain action alongside the dismissal
}
```
For the dismissal trigger, the wrapper needs to contain at least one `.js-close` element and optionally `.deferred-link` links (if `deferLinks` is `true`). See `app/assets/javascripts/persistent_user_callout.js` for more details.
#### Defining the dismissal endpoint
For the JS to properly register the dismissal — apart from the `featureId`, we need to provide the `dismissEndpoint` URL, different for each context. Here are path helpers to use for each context:
- Global context: `callouts_path`
- Group context: `group_callouts_path`
- Project context: `project_callouts_path`
### Detecting the dismissal on the server side
Usually before rendering the callout, we check if it has been dismissed. `User` model on the Backend has helpers to detect dismissals in different contexts:
- Global context: `user.dismissed_callout?(feature_name:, ignore_dismissal_earlier_than: nil)`
- Group context: `user.dismissed_callout_for_group?(feature_name:, group:, ignore_dismissal_earlier_than: nil)`
- Project context: `user.dismissed_callout_for_project?(feature_name:, project:, ignore_dismissal_earlier_than: nil)`
**NOTE:** `feature_name` is the Callout ID, described above. In our example, it would be `amazing_alert`
#### Setting expiration for dismissals using `ignore_dismissal_earlier_than` parameter
Some callouts can be displayed once and after the dismissal should never appear again. Others need to pop-up repeatedly, even if dismissed.
Without the `ignore_dismissal_earlier_than` parameter callout dismissals will stay effective indefinitely. Once the user has dismissed the callout, it would stay dismissed.
If we pass `ignore_dismissal_earlier_than` a value, for example, `30.days.ago`, the dismissed callout would re-appear after this duration.
**NOTE:** expired or deprecated dismissals are not automatically removed from the database. This parameter only checks if the callout has been dismissed within the defined period.
### Example usage
Here's an example `.haml` file:
```haml
- return if amazing_alert_callout_dismissed?(group)
= render Pajamas::AlertComponent.new(title: s_('AmazingAlert|Amazing title'),
variant: :warning,
alert_options: { class: 'js-persistent-callout', data: amazing_alert_callout_data(group) }) do |c|
- c.with_body do
= s_('AmazingAlert|This is an amazing alert body.')
```
With a corresponding `.rb` helper:
```ruby
# frozen_string_literal: true
module AmazingAlertHelper
def amazing_alert_callout_dismissed?(group)
user_dismissed_for_group("amazing_alert", group.root_ancestor, 30.days.ago)
end
def amazing_alert_callout_data(group)
{
feature_id: "amazing_alert",
dismiss_endpoint: group_callouts_path,
group_id: group.root_ancestor.id
}
end
end
```
## Client-side rendered callouts
This section describes using callouts when they are rendered on the client in `.vue` components.
### Dismissing the callouts on the client side
For Vue components, we have a `<user-callout-dismisser>` wrapper, that integrates with GraphQL API to simplify dismissing and checking the dismissed state of a callout. Here's an example usage:
```vue
<user-callout-dismisser feature-name="my_user_callout">
<template #default="{ dismiss, shouldShowCallout }">
<my-callout-component
v-if="shouldShowCallout"
@close="dismiss"
/>
</template>
</user-callout-dismisser>
```
See `app/assets/javascripts/vue_shared/components/user_callout_dismisser.vue` for more details.

View File

@ -362,6 +362,9 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/
## 17.11.0
- To avoid a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/537325) that causes a database migration to run for a long time and use excessive CPU,
update to GitLab 17.11.3 or later.
### New encryption secrets
In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9):

View File

@ -62,8 +62,8 @@ your workflow.
The following table lists the GitLab tiers in which each feature is available.
| Feature | In Free & Premium | In Ultimate |
|:-----------------------------------------------------------------------------------------|:-----------------------|:-----------------------|
| Feature | In Free & Premium | In Ultimate |
|:-----------------------------------------------------------------------------------------|:-------------------------------------|:------------|
| Basic scanning with [open-source analyzers](#supported-languages-and-frameworks) | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes |
| Downloadable [SAST JSON report](#download-a-sast-report) | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes |
| Cross-file, cross-function scanning with [GitLab Advanced SAST](gitlab_advanced_sast.md) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes |
@ -96,27 +96,27 @@ The available scanning options depend on the GitLab tier:
For more information about our plans for language support in SAST, see the [category direction page](https://about.gitlab.com/direction/application_security_testing/static-analysis/sast/#language-support).
| Language | Supported by [GitLab Advanced SAST](gitlab_advanced_sast.md) (Ultimate only) | Supported by another [analyzer](analyzers.md) (all tiers) |
|-----------------------------------------|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Apex (Salesforce) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [PMD-Apex](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex) |
| C | {{< icon name="dotted-circle" >}} No; tracked in [epic 14271](https://gitlab.com/groups/gitlab-org/-/epics/14271) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| C++ | {{< icon name="dotted-circle" >}} No; tracked in [epic 14271](https://gitlab.com/groups/gitlab-org/-/epics/14271) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| C# | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Elixir (Phoenix) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Sobelow](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) |
| Go | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Groovy | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) with the find-sec-bugs plugin<sup><b><a href="#spotbugs-footnote">1</a></b></sup> |
| Java | {{< icon name="check-circle" >}} Yes, including Java Server Pages (JSP) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) (including Android) |
| JavaScript, including Node.js and React | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Kotlin | {{< icon name="dotted-circle" >}} No; tracked in [epic 15173](https://gitlab.com/groups/gitlab-org/-/epics/15173) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) (including Android) |
| Objective-C (iOS) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| PHP | {{< icon name="dotted-circle" >}} No; tracked in [epic 14273](https://gitlab.com/groups/gitlab-org/-/epics/14273) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Python | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Ruby, including Ruby on Rails | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Scala | {{< icon name="dotted-circle" >}} No; tracked in [epic 15174](https://gitlab.com/groups/gitlab-org/-/epics/15174) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Swift (iOS) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| TypeScript | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| YAML<sup><b><a href="#yaml-footnote">2</a></b></sup> | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Java Properties | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Language | Supported by [GitLab Advanced SAST](gitlab_advanced_sast.md) (Ultimate only) | Supported by another [analyzer](analyzers.md) (all tiers) |
|------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| Apex (Salesforce) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [PMD-Apex](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex) |
| C | {{< icon name="dotted-circle" >}} No; tracked in [epic 14271](https://gitlab.com/groups/gitlab-org/-/epics/14271) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| C++ | {{< icon name="dotted-circle" >}} No; tracked in [epic 14271](https://gitlab.com/groups/gitlab-org/-/epics/14271) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| C# | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Elixir (Phoenix) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Sobelow](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) |
| Go | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Groovy | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) with the find-sec-bugs plugin<sup><b><a href="#spotbugs-footnote">1</a></b></sup> |
| Java | {{< icon name="check-circle" >}} Yes, including Java Server Pages (JSP) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) (including Android) |
| JavaScript, including Node.js and React | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Kotlin | {{< icon name="dotted-circle" >}} No; tracked in [epic 15173](https://gitlab.com/groups/gitlab-org/-/epics/15173) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) (including Android) |
| Objective-C (iOS) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| PHP | {{< icon name="dotted-circle" >}} No; tracked in [epic 14273](https://gitlab.com/groups/gitlab-org/-/epics/14273) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Python | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Ruby, including Ruby on Rails | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Scala | {{< icon name="dotted-circle" >}} No; tracked in [epic 15174](https://gitlab.com/groups/gitlab-org/-/epics/15174) | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Swift (iOS) | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| TypeScript | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| YAML<sup><b><a href="#yaml-footnote">2</a></b></sup> | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
| Java Properties | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes: [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) with [GitLab-managed rules](rules.md#semgrep-based-analyzer) |
**Footnotes**:
@ -656,11 +656,11 @@ The `ADDITIONAL_CA_CERT_BUNDLE` value can also be configured as a [custom variab
The following are Docker image-related CI/CD variables.
| CI/CD variable | Description |
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| CI/CD variable | Description |
|---------------------------|-------------|
| `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the default images (proxy). Read more about [customizing analyzers](analyzers.md). |
| `SAST_EXCLUDED_ANALYZERS` | Names of default images that should never run. Read more about [customizing analyzers](analyzers.md). |
| `SAST_ANALYZER_IMAGE_TAG` | Override the default version of analyzer image. Read more about [pinning the analyzer image version](#pinning-to-minor-image-version). |
| `SAST_EXCLUDED_ANALYZERS` | Names of default images that should never run. Read more about [customizing analyzers](analyzers.md). |
| `SAST_ANALYZER_IMAGE_TAG` | Override the default version of analyzer image. Read more about [pinning the analyzer image version](#pinning-to-minor-image-version). |
| `SAST_IMAGE_SUFFIX` | Suffix added to the image name. If set to `-fips`, `FIPS-enabled` images are used for scan. See [FIPS-enabled images](#fips-enabled-images) for more details. |
#### Vulnerability filters
@ -780,7 +780,7 @@ The following are Docker image-related CI/CD variables.
patterns), or file or folder paths (for example, `doc,spec`). Parent directories also match patterns.
The post-filter implementation of `SAST_EXCLUDED_PATHS` is available for all SAST analyzers. Some
SAST analyzers such as those with superscript **[2](#sast-excluded-paths-semgrep)** implement `SAST_EXCLUDED_PATHS`
SAST analyzers such as those with [superscript `2`](#sast-excluded-paths-semgrep) implement `SAST_EXCLUDED_PATHS`
as both a pre-filter and post-filter. A pre-filter is more efficient because it reduces the number of files
to be scanned.
@ -812,28 +812,28 @@ The following are Docker image-related CI/CD variables.
Some analyzers can be customized with CI/CD variables.
| CI/CD variable | Analyzer | Description |
|-----------------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `GITLAB_ADVANCED_SAST_ENABLED` | GitLab Advanced SAST | Set to `true` to enable [GitLab Advanced SAST](gitlab_advanced_sast.md) scanning (available in GitLab Ultimate only). Default: `false`. |
| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` scans. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
| `KUBESEC_HELM_OPTIONS` | Kubesec | Additional arguments for the `helm` executable. |
| `COMPILE` | SpotBugs | Set to `false` to disable project compilation and dependency fetching. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
| `SAST_JAVA_VERSION` | SpotBugs | Which Java version to use. [Starting in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/352549), supported versions are `11` and `17` (default). Before GitLab 15.0, supported versions are `8` (default) and `11`. |
| `MAVEN_CLI_OPTS` | SpotBugs | Additional arguments for the `mvn` or `mvnw` executable. |
| `MAVEN_PATH` | SpotBugs | Path to the `mvn` executable. |
| `MAVEN_REPO_PATH` | SpotBugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
| `SBT_PATH` | SpotBugs | Path to the `sbt` executable. |
| `FAIL_NEVER` | SpotBugs | Set to `1` to ignore compilation failure. |
| `SAST_SEMGREP_METRICS` | Semgrep | Set to `"false"` to disable sending anonymized scan metrics to [r2c](https://semgrep.dev). Default: `true`. |
| `SAST_SCANNER_ALLOWED_CLI_OPTS` | Semgrep | CLI options (arguments with value, or flags) that are passed to the underlying security scanner when running scan operation. Only a limited set of [options](#security-scanner-configuration) are accepted. Separate a CLI option and its value using either a blank space or equals (`=`) character. For example: `name1 value1` or `name1=value1`. Multiple options must be separated by blank spaces. For example: `name1 value1 name2 value2`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368565) in GitLab 15.3. |
| `SAST_RULESET_GIT_REFERENCE` | All | Defines a path to a custom ruleset configuration. If a project has a `.gitlab/sast-ruleset.toml` file committed, that local configuration takes precedence and the file from `SAST_RULESET_GIT_REFERENCE` isn't used. This variable is available for the Ultimate tier only.|
| `SECURE_ENABLE_LOCAL_CONFIGURATION` | All | Enables the option to use custom ruleset configuration. If `SECURE_ENABLE_LOCAL_CONFIGURATION` is set to `false`, the project's custom ruleset configuration file at `.gitlab/sast-ruleset.toml` is ignored and the file from `SAST_RULESET_GIT_REFERENCE` or the default configuration takes precedence. |
| CI/CD variable | Analyzer | Description |
|-------------------------------------|----------------------|-------------|
| `GITLAB_ADVANCED_SAST_ENABLED` | GitLab Advanced SAST | Set to `true` to enable [GitLab Advanced SAST](gitlab_advanced_sast.md) scanning (available in GitLab Ultimate only). Default: `false`. |
| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` scans. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
| `KUBESEC_HELM_OPTIONS` | Kubesec | Additional arguments for the `helm` executable. |
| `COMPILE` | SpotBugs | Set to `false` to disable project compilation and dependency fetching. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
| `SAST_JAVA_VERSION` | SpotBugs | Which Java version to use. [Starting in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/352549), supported versions are `11` and `17` (default). Before GitLab 15.0, supported versions are `8` (default) and `11`. |
| `MAVEN_CLI_OPTS` | SpotBugs | Additional arguments for the `mvn` or `mvnw` executable. |
| `MAVEN_PATH` | SpotBugs | Path to the `mvn` executable. |
| `MAVEN_REPO_PATH` | SpotBugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
| `SBT_PATH` | SpotBugs | Path to the `sbt` executable. |
| `FAIL_NEVER` | SpotBugs | Set to `1` to ignore compilation failure. |
| `SAST_SEMGREP_METRICS` | Semgrep | Set to `"false"` to disable sending anonymized scan metrics to [r2c](https://semgrep.dev). Default: `true`. |
| `SAST_SCANNER_ALLOWED_CLI_OPTS` | Semgrep | CLI options (arguments with value, or flags) that are passed to the underlying security scanner when running scan operation. Only a limited set of [options](#security-scanner-configuration) are accepted. Separate a CLI option and its value using either a blank space or equals (`=`) character. For example: `name1 value1` or `name1=value1`. Multiple options must be separated by blank spaces. For example: `name1 value1 name2 value2`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368565) in GitLab 15.3. |
| `SAST_RULESET_GIT_REFERENCE` | All | Defines a path to a custom ruleset configuration. If a project has a `.gitlab/sast-ruleset.toml` file committed, that local configuration takes precedence and the file from `SAST_RULESET_GIT_REFERENCE` isn't used. This variable is available for the Ultimate tier only. |
| `SECURE_ENABLE_LOCAL_CONFIGURATION` | All | Enables the option to use custom ruleset configuration. If `SECURE_ENABLE_LOCAL_CONFIGURATION` is set to `false`, the project's custom ruleset configuration file at `.gitlab/sast-ruleset.toml` is ignored and the file from `SAST_RULESET_GIT_REFERENCE` or the default configuration takes precedence. |
#### Security scanner configuration
@ -1021,13 +1021,13 @@ For details on saving and transporting Docker images as a file, see the Docker d
Support for custom certificate authorities was introduced in the following versions.
| Analyzer | Version |
| -------- | ------- |
| `kubesec` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/kubesec/-/releases/v2.1.0) |
| `pmd-apex` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex/-/releases/v2.1.0) |
| `semgrep` | [v0.0.1](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep/-/releases/v0.0.1) |
| `sobelow` | [v2.2.0](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow/-/releases/v2.2.0) |
| `spotbugs` | [v2.7.1](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs/-/releases/v2.7.1) |
| Analyzer | Version |
|------------|---------|
| `kubesec` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/kubesec/-/releases/v2.1.0) |
| `pmd-apex` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex/-/releases/v2.1.0) |
| `semgrep` | [v0.0.1](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep/-/releases/v0.0.1) |
| `sobelow` | [v2.2.0](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow/-/releases/v2.2.0) |
| `spotbugs` | [v2.7.1](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs/-/releases/v2.7.1) |
### Set SAST CI/CD variables to use local SAST analyzers

View File

@ -12,12 +12,16 @@ module Gitlab
# table_name can include schema name as a prefix. For example: 'gitlab_partitions_static.events_03',
# otherwise, it will default to current used schema, for example 'public'.
def initialize(table_name:, connection:, database_name:, with_retries: true, logger: nil, dry_run: false)
def initialize(
table_name:, connection:, database_name:,
with_retries: true, logger: nil, dry_run: false, force: true
)
@table_name = table_name.to_s
@connection = connection
@database_name = database_name
@logger = logger
@dry_run = dry_run
@force = force
@with_retries = with_retries
@table_name_without_schema = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
@ -36,6 +40,18 @@ module Gitlab
end
def lock_writes
unless force
unless table_exist?
logger&.info "Skipping lock_writes, because #{table_name} does not exist"
return result_hash(action: 'skipped')
end
if table_locked_for_writes?
logger&.info "Skipping lock_writes, because #{table_name} is already locked for writes"
return result_hash(action: 'skipped')
end
end
logger&.info Rainbow("Database: '#{database_name}', Table: '#{table_name}': Lock Writes").yellow
sql_statement = <<~SQL
CREATE OR REPLACE TRIGGER #{write_trigger_name}
@ -55,19 +71,31 @@ module Gitlab
end
def unlock_writes
logger&.info Rainbow("Database: '#{database_name}', Table: '#{table_name}': Allow Writes").green
sql_statement = <<~SQL
DROP TRIGGER IF EXISTS #{write_trigger_name} ON #{table_name};
SQL
if force || table_locked_for_writes?
logger&.info Rainbow("Database: '#{database_name}', Table: '#{table_name}': Allow Writes").green
sql_statement = <<~SQL
DROP TRIGGER IF EXISTS #{write_trigger_name} ON #{table_name};
SQL
result = process_query(sql_statement, 'unlock')
result = process_query(sql_statement, 'unlock')
result_hash(action: result)
result_hash(action: result)
else
logger&.info "Skipping unlock_writes, because #{table_name} is already unlocked for writes"
result_hash(action: 'skipped')
end
end
private
attr_reader :table_name, :connection, :database_name, :logger, :dry_run, :table_name_without_schema, :with_retries
attr_reader :table_name,
:connection,
:database_name,
:logger,
:dry_run,
:force,
:table_name_without_schema,
:with_retries
def table_exist?
where = if table_name.include?('.')

View File

@ -114,7 +114,8 @@ module Gitlab
database_name: database_name,
with_retries: true,
logger: @logger,
dry_run: @dry_run
dry_run: @dry_run,
force: !@dry_run
)
end
end

View File

@ -44114,9 +44114,15 @@ msgstr ""
msgid "Pages|URL"
msgstr ""
msgid "Pages|Visit site"
msgstr ""
msgid "Pages|We did not find any projects with parallel Pages deployments in this namespace."
msgstr ""
msgid "Pages|Your Pages site is live at"
msgstr ""
msgid "Pagination|First"
msgstr ""
@ -53353,6 +53359,9 @@ msgstr ""
msgid "ScanExecutionPolicy|%{rules} every time a pipeline runs for %{scopes} %{branches} %{branchExceptions} %{agents} %{namespaces}"
msgstr ""
msgid "ScanExecutionPolicy|%{rules} every time a pipeline runs for %{scopes} %{branches} %{sources} %{branchExceptions} %{agents} %{namespaces}"
msgstr ""
msgid "ScanExecutionPolicy|%{strategySelector} pipeline file to run from %{projectSelector}"
msgstr ""
@ -55001,6 +55010,9 @@ msgstr ""
msgid "SecurityOrchestration|A scan execution policy exceeds the limit of %{maxScanExecutionPolicySchedules} scheduled rules per policy. Remove or consolidate rules across policies to reduce the total number of rules."
msgstr ""
msgid "SecurityOrchestration|API request"
msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
@ -55127,6 +55139,9 @@ msgstr ""
msgid "SecurityOrchestration|Cannot create an empty policy"
msgstr ""
msgid "SecurityOrchestration|ChatOps command"
msgstr ""
msgid "SecurityOrchestration|Choose a project"
msgstr ""
@ -55145,6 +55160,9 @@ msgstr ""
msgid "SecurityOrchestration|Clear all"
msgstr ""
msgid "SecurityOrchestration|Code push"
msgstr ""
msgid "SecurityOrchestration|Compliance framework has no projects"
msgstr ""
@ -55295,6 +55313,12 @@ msgstr ""
msgid "SecurityOrchestration|Excluded projects"
msgstr ""
msgid "SecurityOrchestration|External event"
msgstr ""
msgid "SecurityOrchestration|External pull request"
msgstr ""
msgid "SecurityOrchestration|Failed to fetch the scope information. Please refresh the page to try again."
msgstr ""
@ -55412,6 +55436,9 @@ msgstr ""
msgid "SecurityOrchestration|Logic error"
msgstr ""
msgid "SecurityOrchestration|Manual pipeline run"
msgstr ""
msgid "SecurityOrchestration|Maximum action limit for scan execution policies will be enabled in 18.0"
msgstr ""
@ -55436,6 +55463,9 @@ msgstr ""
msgid "SecurityOrchestration|Merge request approval policy syntax changes"
msgstr ""
msgid "SecurityOrchestration|Merge request pipeline"
msgstr ""
msgid "SecurityOrchestration|Never"
msgstr ""
@ -55666,6 +55696,9 @@ msgstr ""
msgid "SecurityOrchestration|Schedule to run for %{branchSelector}"
msgstr ""
msgid "SecurityOrchestration|Scheduled pipeline"
msgstr ""
msgid "SecurityOrchestration|Scheduled pipeline execution"
msgstr ""
@ -55726,6 +55759,9 @@ msgstr ""
msgid "SecurityOrchestration|Select suffix"
msgstr ""
msgid "SecurityOrchestration|Select the source"
msgstr ""
msgid "SecurityOrchestration|Send a bot message when the conditions match."
msgstr ""
@ -55873,12 +55909,18 @@ msgstr ""
msgid "SecurityOrchestration|To widen your search, change filters above or select a different security policy project."
msgstr ""
msgid "SecurityOrchestration|Trigger"
msgstr ""
msgid "SecurityOrchestration|Turn off default mode to edit scope."
msgstr ""
msgid "SecurityOrchestration|Type in custom variable"
msgstr ""
msgid "SecurityOrchestration|Unknown source"
msgstr ""
msgid "SecurityOrchestration|Unlink project"
msgstr ""
@ -55966,6 +56008,9 @@ msgstr ""
msgid "SecurityOrchestration|Warn users with a bot comment and contact the following users as security consultants for support: %{approvers}"
msgstr ""
msgid "SecurityOrchestration|Web UI"
msgstr ""
msgid "SecurityOrchestration|What does an example message look like?"
msgstr ""

View File

@ -53,6 +53,14 @@ module Gitlab
desc: "Max number of retries for failed deployment",
default: 0,
type: :numeric
option :resource_preset,
desc: "Kubernetes resource definition preset",
default: Gitlab::Orchestrator::Deployment::ResourcePresets::DEFAULT,
type: :string,
enum: [
Gitlab::Orchestrator::Deployment::ResourcePresets::DEFAULT,
Gitlab::Orchestrator::Deployment::ResourcePresets::HIGH
]
super
end
@ -113,7 +121,8 @@ module Gitlab
:admin_token,
:host_http_port,
:host_ssh_port,
:host_registry_port
:host_registry_port,
:resource_preset
)
installation(name, Orchestrator::Deployment::Configurations::Kind.new(**configuration_args)).create

View File

@ -21,23 +21,27 @@ module Gitlab
fi
SH
def initialize(
namespace:,
ci:,
gitlab_domain:,
admin_password:,
admin_token:,
host_http_port:,
host_ssh_port:,
host_registry_port:
)
super(namespace: namespace, ci: ci, gitlab_domain: gitlab_domain)
# Instance of kind deployment configuration
#
# @param **args [Hash]
# @option args [String] :namespace Namespace used for deployment
# @option args [Boolean] :ci Run for CI environment
# @option args [String] :gitlab_domain Custom gitlab domain
# @option args [String] :admin_password Initial password for admin user
# @option args [String] :admin_token Initial PAT token for admin user
# @option args [Integer] :host_http_port HTTP port for gitlab pages
# @option args [Integer] :host_ssh_port SSH port for gitlab
# @option args [Integer] :host_registry_port Registry port for gitlab
# @option args [String] :resource_preset resource preset name
def initialize(**args)
super(**args.slice(:namespace, :ci, :gitlab_domain))
@admin_password = admin_password
@admin_token = admin_token
@host_http_port = host_http_port
@host_ssh_port = host_ssh_port
@host_registry_port = host_registry_port
@admin_password = args[:admin_password]
@admin_token = args[:admin_token]
@host_http_port = args[:host_http_port]
@host_ssh_port = args[:host_ssh_port]
@host_registry_port = args[:host_registry_port]
@resource_preset = args[:resource_preset]
end
# Run pre-deployment setup
@ -96,7 +100,7 @@ module Gitlab
}
}
}
}
}.deep_merge(ResourcePresets.resource_values(resource_preset))
end
# Gitlab url
@ -108,7 +112,12 @@ module Gitlab
private
attr_reader :admin_password, :admin_token, :host_http_port, :host_ssh_port, :host_registry_port
attr_reader :admin_password,
:admin_token,
:host_http_port,
:host_ssh_port,
:host_registry_port,
:resource_preset
# Token seed script for root user
#

View File

@ -189,7 +189,6 @@ module Gitlab
.deep_merge(license_values)
.deep_merge(env_values)
.deep_merge(configuration.values)
.deep_merge(ResourcePresets.resource_values(ci ? ResourcePresets::HIGH : ResourcePresets::DEFAULT))
.deep_stringify_keys
.to_yaml

View File

@ -59,7 +59,8 @@ RSpec.describe Gitlab::Orchestrator::Commands::Subcommands::Deployment do
admin_token: "ypCa3Dzb23o5nvsixwPA",
host_http_port: 80,
host_ssh_port: 22,
host_registry_port: 5000
host_registry_port: 5000,
resource_preset: "default"
)
expect(installation_instance).to have_received(:create)
end

View File

@ -10,10 +10,13 @@ RSpec.describe Gitlab::Orchestrator::Deployment::Configurations::Kind do
admin_token: "token",
host_http_port: 80,
host_ssh_port: 22,
host_registry_port: 5000
host_registry_port: 5000,
resource_preset: resource_preset
)
end
let(:resource_preset) { "default" }
let(:kubeclient) do
instance_double(Gitlab::Orchestrator::Kubectl::Client, create_resource: "", execute: "", patch: "")
end
@ -146,7 +149,7 @@ RSpec.describe Gitlab::Orchestrator::Deployment::Configurations::Kind do
}
}
}
})
}.deep_merge(Gitlab::Orchestrator::Deployment::ResourcePresets.resource_values(resource_preset)))
end
it "returns correct gitlab url" do

View File

@ -41,16 +41,6 @@ RSpec.describe Gitlab::Orchestrator::Deployment::Installation, :aggregate_failur
)
end
let(:resources_values) do
presets = if ci
Gitlab::Orchestrator::Deployment::ResourcePresets::HIGH
else
Gitlab::Orchestrator::Deployment::ResourcePresets::DEFAULT
end
Gitlab::Orchestrator::Deployment::ResourcePresets.resource_values(presets)
end
let(:expected_values_yml) do
{
global: {
@ -65,7 +55,7 @@ RSpec.describe Gitlab::Orchestrator::Deployment::Installation, :aggregate_failur
license: { secret: "gitlab-license" }
},
**config_values
}.deep_merge(resources_values).deep_stringify_keys.to_yaml
}.deep_stringify_keys.to_yaml
end
before do

View File

@ -0,0 +1,79 @@
import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import LiveBlock from '~/gitlab_pages/components/live_block.vue';
import CrudComponent from '~/vue_shared/components/crud_component.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { primaryDeployment } from '../mock_data';
Vue.use(VueApollo);
describe('PagesLiveBlock', () => {
let wrapper;
const createComponent = ({ provide = {} } = {}) => {
wrapper = mountExtended(LiveBlock, {
propsData: {
deployment: primaryDeployment,
},
provide: {
projectFullPath: 'my-group/my-project',
primaryDomain: null,
...provide,
},
stubs: {
CrudComponent,
TimeAgo,
},
});
};
const findHeading = () => wrapper.findByTestId('live-heading');
const findHeadingLink = () => wrapper.findByTestId('live-heading-link');
const findDeployJobNumber = () => wrapper.findByTestId('deploy-job-number');
const findUpdatedAt = () => wrapper.findComponent(TimeAgo);
const findVisitSite = () => wrapper.findByTestId('visit-site-url');
describe('displays expected data', () => {
beforeEach(() => {
createComponent();
});
it('renders heading', () => {
expect(findHeading().text().replace(/\s+/g, ' ')).toBe(
`Your Pages site is live at ${primaryDeployment.url} 🎉`,
);
expect(findHeadingLink().attributes('href')).toBe(primaryDeployment.url);
});
it('renders deploy job number and link', () => {
expect(findDeployJobNumber().text()).toBe(primaryDeployment.ciBuildId.toString());
expect(findDeployJobNumber().attributes('href')).toBe('/my-group/my-project/-/jobs/123');
});
it('renders updated at', () => {
expect(findUpdatedAt().props('time')).toBe(primaryDeployment.updatedAt);
});
it('renders visit site button', () => {
expect(findVisitSite().text()).toBe('Visit site');
expect(findVisitSite().attributes('href')).toBe(primaryDeployment.url);
});
it('uses primaryDomain when set', () => {
const primaryDomain = 'https://primary.domain';
createComponent({
provide: {
primaryDomain,
},
});
expect(findHeading().text().replace(/\s+/g, ' ')).toBe(
`Your Pages site is live at ${primaryDomain} 🎉`,
);
expect(findHeadingLink().attributes('href')).toBe(primaryDomain);
expect(findVisitSite().attributes('href')).toBe(primaryDomain);
});
});
});

View File

@ -30,17 +30,6 @@ describe('IssuablePresenter', () => {
expect(link.text()).toEqual(expectedText);
});
it('truncates long titles', () => {
const longTitle =
'A quick brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog.';
createWrapper({ data: { ...mockData, title: longTitle } });
const link = findLink();
expect(link.text()).toContain('A quick brown fox jumps over the lazy dog. A qu...');
expect(link.attributes('title')).toBe(longTitle);
});
it(`correctly renders the state if the ${type} is closed`, () => {
createWrapper({ data: { ...mockData, state: 'closed' } });

View File

@ -406,19 +406,22 @@ describe('WorkItemDescription', () => {
// Mimic component mount with a pre-populated description
await createComponent({
editMode: true,
workItemResponseHandler: jest
.fn()
.mockResolvedValue(
workItemByIidResponseFactory({ description: 'Pre-filled description' }),
),
workItemId: newWorkItemId(workItemQueryResponse.data.workItem.workItemType.name),
});
findDescriptionTemplateListbox().vm.$emit('selectTemplate', {
name: 'default',
projectId: 1,
catagory: 'catagory',
});
await nextTick();
await waitForPromises();
expect(findDescriptionTemplateWarning().exists()).toBe(false);
expect(findCancelApplyTemplate().exists()).toBe(false);
expect(findApplyTemplate().exists()).toBe(false);
// No template is selected when description is pre-filled
expect(findDescriptionTemplateListbox().props('template')).toBeNull();
});
it('hides the warning when the cancel button is clicked', async () => {

View File

@ -105,29 +105,6 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
describe '#assigned_issuables_count', feature_category: :team_planning do
context 'when issuable is issues' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, developers: user) }
subject { helper.assigned_issuables_count(:issues) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when assigned issues count is over MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT' do
before do
stub_const('User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT', 2)
end
let_it_be(:issues) { create_list(:issue, 3, project: project, assignees: [user]) }
it { is_expected.to eq 2 }
end
end
end
describe '#issuables_state_counter_text' do
let_it_be(:user) { create(:user) }

View File

@ -120,29 +120,6 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
end
end
describe '#user_merge_requests_counts' do
let(:user) do
double(
assigned_open_merge_requests_count: 1,
review_requested_open_merge_requests_count: 2
)
end
subject { helper.user_merge_requests_counts }
before do
allow(helper).to receive(:current_user).and_return(user)
end
it "returns assigned, review requested and total merge request counts" do
expect(subject).to eq(
assigned: user.assigned_open_merge_requests_count,
review_requested: user.review_requested_open_merge_requests_count,
total: user.assigned_open_merge_requests_count + user.review_requested_open_merge_requests_count
)
end
end
describe '#reviewers_label' do
let(:merge_request) { build_stubbed(:merge_request) }
let(:reviewer1) { build_stubbed(:user, name: 'Jane Doe') }

View File

@ -9,6 +9,7 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
let(:skip_table_creation) { false }
let(:logger) { instance_double(Logger) }
let(:dry_run) { false }
let(:force) { true }
subject(:lock_writes_manager) do
described_class.new(
@ -17,7 +18,8 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
database_name: 'main',
with_retries: true,
logger: logger,
dry_run: dry_run
dry_run: dry_run,
force: force
)
end
@ -125,36 +127,58 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
}
end
it 'replaces the trigger if the table is already locked' do
subject.lock_writes
context 'when running in force mode' do
it 'replaces the trigger if the table is already locked' do
subject.lock_writes
expect(connection).to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect(connection).to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect do
result = subject.lock_writes
expect(result).to eq({ action: "locked", database: "main", dry_run: false, table: test_table })
end.not_to change {
number_of_triggers_on(connection, test_table)
}
end
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
# In tests if we don't add the fake table to the gitlab schema then our test only schema checks will fail.
before do
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).and_call_original
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).with(
non_existent_table
).and_return(:gitlab_main_cell)
expect do
result = subject.lock_writes
expect(result).to eq({ action: "locked", database: "main", dry_run: false, table: test_table })
end.not_to change {
number_of_triggers_on(connection, test_table)
}
end
it 'tries to lock the table anyways, and logs the failure' do
expect(logger).to receive(:warn).with(
/Failed lock_writes, because #{test_table} raised an error. Error:/
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
# In tests if we don't add the fake table to the gitlab schema then our test only schema checks will fail.
before do
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).and_call_original
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).with(
non_existent_table
).and_return(:gitlab_main_cell)
end
it 'tries to lock the table anyways, and logs the failure' do
expect(logger).to receive(:warn).with(
/Failed lock_writes, because #{test_table} raised an error. Error:/
)
expect(connection).to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect do
result = subject.lock_writes
expect(result).to eq({ action: "skipped", database: "main", dry_run: false, table: test_table })
end.not_to change {
number_of_triggers_on(connection, test_table)
}
end
end
end
context 'when running in force mode false' do
let(:force) { false }
it 'skips the operation if the table is already locked for writes' do
subject.lock_writes
expect(logger).to receive(:info).with(
"Skipping lock_writes, because #{test_table} is already locked for writes"
)
expect(connection).to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect(connection).not_to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect do
result = subject.lock_writes
@ -163,6 +187,31 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
number_of_triggers_on(connection, test_table)
}
end
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
# In tests if we don't add the fake table to the gitlab schema then our test only schema checks will fail.
before do
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).and_call_original
allow(Gitlab::Database::GitlabSchema).to receive(:table_schema).with(
non_existent_table
).and_return(:gitlab_main_cell)
end
it 'skips locking table' do
expect(logger).to receive(:info).with("Skipping lock_writes, because #{test_table} does not exist")
expect(connection).not_to receive(:execute).with(/CREATE OR REPLACE TRIGGER/)
expect do
result = subject.lock_writes
expect(result).to eq({ action: "skipped", database: "main", dry_run: false, table: test_table })
end.not_to change {
number_of_triggers_on(connection, test_table)
}
end
end
end
context 'when running in dry_run mode' do
@ -227,15 +276,6 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
end.not_to raise_error
end
it 'idempotently drops the lock even if it does not exist' do
subject.unlock_writes
expect(subject).to receive(:execute_sql_statement)
expect(subject.unlock_writes).to eq(
{ action: "unlocked", database: "main", dry_run: dry_run, table: test_table }
)
end
it 'removes the write protection triggers from the gitlab_main tables on the ci database' do
expect do
subject.unlock_writes
@ -252,14 +292,54 @@ RSpec.describe Gitlab::Database::LockWritesManager, :delete, feature_category: :
subject.unlock_writes
end
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
context 'when running in force mode true' do
it 'idempotently drops the lock even if it does not exist' do
subject.unlock_writes
it 'tries to unlock the table which postgres handles gracefully' do
expect(subject).to receive(:execute_sql_statement).and_call_original
expect(subject).to receive(:execute_sql_statement)
expect(subject.unlock_writes).to eq(
{ action: "unlocked", database: "main", dry_run: dry_run, table: test_table }
)
end
expect(subject.unlock_writes).to eq({ action: "unlocked", database: "main", dry_run: false, table: test_table })
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
it 'tries to unlock the table which postgres handles gracefully' do
expect(subject).to receive(:execute_sql_statement).and_call_original
expect(subject.unlock_writes).to eq(
{ action: "unlocked", database: "main", dry_run: false, table: test_table }
)
end
end
end
context 'when running in force mode false' do
let(:force) { false }
it 'skips unlocking the table if the table was already unlocked for writes' do
subject.unlock_writes
expect(subject).not_to receive(:execute_sql_statement)
expect(subject.unlock_writes).to eq(
{ action: "skipped", database: "main", dry_run: dry_run, table: test_table }
)
end
context 'when table does not exist' do
let(:skip_table_creation) { true }
let(:test_table) { non_existent_table }
it 'skips unlocking table' do
subject.unlock_writes
expect(subject).not_to receive(:execute_sql_statement)
expect(subject.unlock_writes).to eq(
{ action: "skipped", database: "main", dry_run: dry_run, table: test_table }
)
end
end
end

View File

@ -88,7 +88,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: database_name,
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
).once.and_return(lock_writes_manager)
expect(lock_writes_manager).to receive(:lock_writes).once
end
@ -121,7 +122,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: database_name,
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
).once.and_return(lock_writes_manager)
expect(lock_writes_manager).to receive(:unlock_writes)
end
@ -146,7 +148,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: database_name,
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
).once.and_return(lock_writes_manager)
expect(lock_writes_manager).to receive(:lock_writes)
@ -166,7 +169,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: database_name,
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
).once.and_return(lock_writes_manager)
expect(lock_writes_manager).to receive(:unlock_writes)
@ -340,7 +344,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: 'ci',
with_retries: true,
logger: anything,
dry_run: true
dry_run: true,
force: false
).and_return(default_lock_writes_manager)
expect(default_lock_writes_manager).to receive(:lock_writes)
@ -389,7 +394,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: 'geo',
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
)
subject
@ -416,7 +422,8 @@ RSpec.describe Gitlab::Database::TablesLocker, :suppress_gitlab_schemas_validate
database_name: 'sec',
with_retries: true,
logger: anything,
dry_run: anything
dry_run: anything,
force: true
)
subject

View File

@ -1,57 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::AssignedIssuesCountService, :use_clean_rails_memory_store_caching,
feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:max_limit) { 10 }
let(:current_user) { user }
subject { described_class.new(current_user: current_user, max_limit: max_limit) }
it_behaves_like 'a counter caching service'
context 'when user has assigned open issues from archived and closed projects' do
before do
project = create(:project, :public)
archived_project = create(:project, :public, :archived)
create(:issue, project: project, author: user, assignees: [user])
create(:issue, :closed, project: project, author: user, assignees: [user])
create(:issue, project: archived_project, author: user, assignees: [user])
end
it 'count all assigned open issues excluding those from closed or archived projects' do
expect(subject.count).to eq(1)
end
end
context 'when the number of assigned open issues exceeds max_limit' do
let_it_be(:banned_user) { create(:user, :banned) }
let_it_be(:project) { create(:project, developers: user) }
context 'when user is admin', :enable_admin_mode do
let_it_be(:admin) { create(:admin) }
let_it_be(:issues) { create_list(:issue, max_limit + 1, project: project, assignees: [admin]) }
let_it_be(:banned_issue) { create(:issue, project: project, assignees: [admin], author: banned_user) }
let(:current_user) { admin }
it 'returns the max_limit count' do
expect(subject.count).to eq max_limit
end
end
context 'when user is non-admin' do
let_it_be(:issues) { create_list(:issue, max_limit + 1, project: project, assignees: [user]) }
let_it_be(:closed_issue) { create(:issue, :closed, project: project, assignees: [user]) }
let_it_be(:banned_issue) { create(:issue, project: project, assignees: [user], author: banned_user) }
it 'returns the max_limit count' do
expect(subject.count).to eq max_limit
end
end
end
end

View File

@ -93,7 +93,7 @@ RSpec.describe Database::LockTablesWorker, feature_category: :cell do
end
with_them do
it 'tries to lock the table anyways, and logs the failure' do
it 'returns as locked for the previously locked tables on the corresponding database' do
tables.each do |table_name|
lock_table(database_name, table_name)
end