Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-02-06 18:08:07 +00:00
parent edde77d99a
commit e7f151b0c0
37 changed files with 458 additions and 269 deletions

View File

@ -204,6 +204,7 @@ trigger-omnibus-env:
SECURITY_SOURCES=$([[ ! "$CI_PROJECT_NAMESPACE" =~ ^gitlab-org\/security ]] || echo "true")
echo "SECURITY_SOURCES=${SECURITY_SOURCES:-false}" > $BUILD_ENV
echo "OMNIBUS_GITLAB_CACHE_UPDATE=${OMNIBUS_GITLAB_CACHE_UPDATE:-false}" >> $BUILD_ENV
echo "OMNIBUS_GITLAB_CACHE_EDITION=${OMNIBUS_GITLAB_CACHE_EDITION}" >> $BUILD_ENV
for version_file in *_VERSION; do echo "$version_file=$(cat $version_file)" >> $BUILD_ENV; done
echo "OMNIBUS_GITLAB_BUILD_ON_ALL_OS=${OMNIBUS_GITLAB_BUILD_ON_ALL_OS:-false}" >> $BUILD_ENV
ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> $BUILD_ENV

View File

@ -0,0 +1,19 @@
<script>
export default {
name: 'BoardCutLine',
props: {
cutLineText: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="board-cut-line gl-display-flex gl-mb-3 gl-text-red-700 gl-align-items-center">
<span class="gl-px-2 gl-font-sm gl-font-weight-bold" data-testid="cut-line-text">{{
cutLineText
}}</span>
</div>
</template>

View File

@ -29,6 +29,7 @@ import { shouldCloneCard, moveItemVariables } from '../boards_util';
import eventHub from '../eventhub';
import BoardCard from './board_card.vue';
import BoardNewIssue from './board_new_issue.vue';
import BoardCutLine from './board_cut_line.vue';
export default {
draggableItemTypes: DraggableItemTypes,
@ -42,6 +43,7 @@ export default {
components: {
BoardCard,
BoardNewIssue,
BoardCutLine,
BoardNewEpic: () => import('ee_component/boards/components/board_new_epic.vue'),
GlLoadingIcon,
GlIntersectionObserver,
@ -154,6 +156,16 @@ export default {
boardListItems() {
return this.currentList?.[`${this.issuableType}s`].nodes || [];
},
beforeCutLine() {
return this.boardItemsSizeExceedsMax
? this.boardListItems.slice(0, this.list.maxIssueCount)
: this.boardListItems;
},
afterCutLine() {
return this.boardItemsSizeExceedsMax
? this.boardListItems.slice(this.list.maxIssueCount)
: [];
},
listQueryVariables() {
return {
fullPath: this.fullPath,
@ -174,6 +186,11 @@ export default {
issuableType: this.isEpicBoard ? 'epics' : 'issues',
});
},
wipLimitText() {
return sprintf(__('Work in progress limit: %{wipLimit}'), {
wipLimit: this.list.maxIssueCount,
});
},
toggleFormEventPrefix() {
return this.isEpicBoard ? toggleFormEventPrefix.epic : toggleFormEventPrefix.issue;
},
@ -653,7 +670,7 @@ export default {
:data-board="list.id"
:data-board-type="list.listType"
:class="{
'gl-bg-red-100 gl-rounded-bottom-left-base gl-rounded-bottom-right-base': boardItemsSizeExceedsMax,
'gl-bg-red-50 gl-rounded-bottom-left-base gl-rounded-bottom-right-base': boardItemsSizeExceedsMax,
'gl-overflow-hidden': disableScrollingWhenMutationInProgress,
'gl-overflow-y-auto': !disableScrollingWhenMutationInProgress,
}"
@ -664,7 +681,32 @@ export default {
@end="handleDragOnEnd"
>
<board-card
v-for="(item, index) in boardListItems"
v-for="(item, index) in beforeCutLine"
ref="issue"
:key="item.id"
:index="index"
:list="list"
:item="item"
:data-draggable-item-type="$options.draggableItemTypes.card"
:show-work-item-type-icon="!isEpicBoard"
>
<board-card-move-to-position
v-if="showMoveToPosition"
:item="item"
:index="index"
:list="list"
:list-items-length="boardListItems.length"
@moveToPosition="moveToPosition($event, index, item)"
/>
<gl-intersection-observer
v-if="isObservableItem(index)"
data-testid="board-card-gl-io"
@appear="onReachingListBottom"
/>
</board-card>
<board-cut-line v-if="boardItemsSizeExceedsMax" :cut-line-text="wipLimitText" />
<board-card
v-for="(item, index) in afterCutLine"
ref="issue"
:key="item.id"
:index="index"

View File

@ -110,6 +110,9 @@ export default {
itemsCount() {
return this.isEpicBoard ? this.list.metadata.epicsCount : this.boardList?.issuesCount;
},
boardItemsSizeExceedsMax() {
return this.list.maxIssueCount > 0 && this.itemsCount > this.list.maxIssueCount;
},
listAssignee() {
return this.list?.assignee?.username || '';
},
@ -333,6 +336,7 @@ export default {
'gl-h-full': list.collapsed,
'gl-bg-gray-50': isSwimlanesHeader,
'gl-border-t-solid gl-border-4 gl-rounded-top-left-base gl-rounded-top-right-base': isLabelList,
'gl-bg-red-50 gl-rounded-top-left-base gl-rounded-top-right-base': boardItemsSizeExceedsMax,
}"
:style="headerStyle"
class="board-header gl-relative"

View File

@ -26,7 +26,7 @@ export default {
<template>
<div class="item-count text-nowrap">
<span :class="{ 'text-danger': issuesExceedMax }" data-testid="board-items-count">
<span :class="{ 'gl-text-red-700': issuesExceedMax }" data-testid="board-items-count">
{{ itemsSize }}
</span>
<span v-if="isMaxLimitSet" class="max-issue-size">

View File

@ -1,11 +1,12 @@
<script>
import { GlButton, GlAlert } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import Autosave from '~/autosave';
import { s__, __ } from '~/locale';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import markdownEditorEventHub from '~/vue_shared/components/markdown/eventhub';
import { CLEAR_AUTOSAVE_ENTRY_EVENT } from '~/vue_shared/constants';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import {
ADD_DISCUSSION_COMMENT_ERROR,
@ -27,7 +28,7 @@ export default {
},
markdownDocsPath: helpPagePath('user/markdown'),
components: {
MarkdownField,
MarkdownEditor,
GlButton,
GlAlert,
},
@ -78,6 +79,14 @@ export default {
noteUpdateDirty: false,
isLoggedIn: isLoggedIn(),
errorMessage: '',
formFieldProps: {
id: 'design-reply',
name: 'design-reply',
'aria-label': __('Description'),
placeholder: __('Write a comment…'),
'data-testid': 'note-textarea',
class: 'note-textarea js-gfm-input js-autosize markdown-area',
},
};
},
computed: {
@ -92,9 +101,16 @@ export default {
shortDiscussionId() {
return isGid(this.discussionId) ? getIdFromGraphQLId(this.discussionId) : this.discussionId;
},
},
mounted() {
this.focusInput();
autosaveKey() {
if (this.isLoggedIn) {
return [
s__('DesignManagement|Discussion'),
getIdFromGraphQLId(this.noteableId),
this.shortDiscussionId,
].join('/');
}
return '';
},
},
beforeDestroy() {
/**
@ -104,9 +120,7 @@ export default {
* so we're safe to clear autosave data here conditionally.
*/
this.$nextTick(() => {
if (!this.noteUpdateDirty) {
this.autosaveDiscussion?.reset();
}
markdownEditorEventHub.$emit(CLEAR_AUTOSAVE_ENTRY_EVENT, this.autosaveKey);
});
},
methods: {
@ -181,20 +195,7 @@ export default {
}
this.$emit('cancel-form');
this.autosaveDiscussion.reset();
},
focusInput() {
this.$refs.textarea.focus();
this.initAutosaveComment();
},
initAutosaveComment() {
if (this.isLoggedIn) {
this.autosaveDiscussion = new Autosave(this.$refs.textarea, [
s__('DesignManagement|Discussion'),
getIdFromGraphQLId(this.noteableId),
this.shortDiscussionId,
]);
}
markdownEditorEventHub.$emit(CLEAR_AUTOSAVE_ENTRY_EVENT, this.autosaveKey);
},
},
};
@ -207,31 +208,19 @@ export default {
{{ errorMessage }}
</gl-alert>
</div>
<markdown-field
:markdown-preview-path="markdownPreviewPath"
:enable-autocomplete="true"
:textarea-value="noteText"
<markdown-editor
v-model="noteText"
autofocus
:markdown-docs-path="$options.markdownDocsPath"
class="bordered-box"
>
<template #textarea>
<textarea
ref="textarea"
v-model.trim="noteText"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
data-testid="note-textarea"
:aria-label="__('Description')"
:placeholder="__('Write a comment…')"
@input="handleInput"
@keydown.meta.enter="submitForm"
@keydown.ctrl.enter="submitForm"
@keyup.esc.stop="cancelComment"
>
</textarea>
</template>
</markdown-field>
:render-markdown-path="markdownPreviewPath"
:enable-autocomplete="true"
:supports-quick-actions="false"
:form-field-props="formFieldProps"
@input="handleInput"
@keydown.meta.enter="submitForm"
@keydown.ctrl.enter="submitForm"
@keydown.esc.stop="cancelComment"
/>
<slot name="resolve-checkbox"></slot>
<div class="note-form-actions gl-display-flex gl-mt-4!">
<gl-button

View File

@ -276,9 +276,6 @@ export default {
},
openCommentForm(annotationCoordinates) {
this.annotationCoordinates = annotationCoordinates;
if (this.$refs.newDiscussionForm) {
this.$refs.newDiscussionForm.focusInput();
}
},
closeCommentForm(data) {
this.annotationCoordinates = null;

View File

@ -15,7 +15,7 @@ import { __ } from '~/locale';
import MilestoneCombobox from '~/milestones/components/milestone_combobox.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import { putCreateReleaseNotification } from '~/releases/release_notification_service';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import AssetLinksForm from './asset_links_form.vue';
import ConfirmDeleteModal from './confirm_delete_modal.vue';
import TagField from './tag_field.vue';
@ -31,11 +31,22 @@ export default {
GlLink,
GlSprintf,
ConfirmDeleteModal,
MarkdownField,
MarkdownEditor,
AssetLinksForm,
MilestoneCombobox,
TagField,
},
data() {
return {
formFieldProps: {
id: 'release-notes',
name: 'release-notes',
class: 'note-textarea js-gfm-input js-autosize markdown-area',
'aria-label': __('Release notes'),
placeholder: __('Write your release notes or drag your files here…'),
},
};
},
computed: {
...mapState('editNew', [
'isExistingRelease',
@ -71,7 +82,7 @@ export default {
},
releaseNotes: {
get() {
return this.$store.state.editNew.release.description;
return this.$store.state.editNew.release.description || this.formattedReleaseNotes;
},
set(notes) {
this.updateReleaseNotes(notes);
@ -220,25 +231,13 @@ export default {
</gl-form-group>
<gl-form-group :label="__('Release notes')" data-testid="release-notes">
<div class="common-note-form">
<markdown-field
:can-attach-file="true"
:markdown-preview-path="markdownPreviewPath"
<markdown-editor
v-model="releaseNotes"
:render-markdown-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:add-spacing-classes="false"
:textarea-value="formattedReleaseNotes"
>
<template #textarea>
<textarea
id="release-notes"
v-model="releaseNotes"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
:aria-label="__('Release notes')"
:placeholder="__('Write your release notes or drag your files here…')"
></textarea>
</template>
</markdown-field>
:supports-quick-actions="false"
:form-field-props="formFieldProps"
/>
</div>
</gl-form-group>
<gl-form-group v-if="!isExistingRelease">

View File

@ -242,3 +242,12 @@
height: 100px;
}
}
.board-cut-line {
&::before, &::after {
content: '';
height: 1px;
flex: 1;
border-top: 1px dashed $red-700;
}
}

View File

@ -30,16 +30,16 @@ module CascadingNamespaceSettingAttribute
# similar to Rails' `attr_accessor`, defines convenience methods such as
# a reader, writer, and validators.
#
# Example: `cascading_attr :delayed_project_removal`
# Example: `cascading_attr :toggle_security_policy_custom_ci`
#
# Public methods defined:
# - `delayed_project_removal`
# - `delayed_project_removal=`
# - `delayed_project_removal_locked?`
# - `delayed_project_removal_locked_by_ancestor?`
# - `delayed_project_removal_locked_by_application_setting?`
# - `delayed_project_removal?` (only defined for boolean attributes)
# - `delayed_project_removal_locked_ancestor` - Returns locked namespace settings object (only namespace_id)
# - `toggle_security_policy_custom_ci`
# - `toggle_security_policy_custom_ci=`
# - `toggle_security_policy_custom_ci_locked?`
# - `toggle_security_policy_custom_ci_locked_by_ancestor?`
# - `toggle_security_policy_custom_ci_locked_by_application_setting?`
# - `toggle_security_policy_custom_ci?` (only defined for boolean attributes)
# - `toggle_security_policy_custom_ci_locked_ancestor` - Returns locked namespace settings object (only namespace_id)
#
# Defined validators ensure attribute value cannot be updated if locked by
# an ancestor or application settings.

View File

@ -8,9 +8,11 @@ module Integrations
field :token,
type: :password,
description: -> { _('The Slack token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: ''
placeholder: '',
required: true
def self.title
'Slack slash commands'

View File

@ -8,8 +8,8 @@ class NamespaceSetting < ApplicationRecord
ignore_column :project_import_level, remove_with: '16.10', remove_after: '2024-02-22'
ignore_column :third_party_ai_features_enabled, remove_with: '16.10', remove_after: '2024-02-22'
ignore_column %i[delayed_project_removal lock_delayed_project_removal], remove_with: '16.10', remove_after: '2024-02-22'
cascading_attr :delayed_project_removal
cascading_attr :toggle_security_policy_custom_ci
cascading_attr :toggle_security_policies_policy_scope
@ -40,8 +40,6 @@ class NamespaceSetting < ApplicationRecord
NAMESPACE_SETTINGS_PARAMS = %i[
default_branch_name
delayed_project_removal
lock_delayed_project_removal
resource_access_token_creation_allowed
prevent_sharing_groups_outside_hierarchy
new_user_signups_cap

View File

@ -1,8 +0,0 @@
---
name: oidc_issuer_url
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135049
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429855
milestone: '16.6'
type: development
group: group::pipeline security
default_enabled: false

View File

@ -0,0 +1,27 @@
- title: "Deprecate `fmt` job in Terraform Module CI/CD template"
# The milestones for the deprecation announcement, and the removal.
removal_milestone: "17.0"
announcement_milestone: "16.9"
# Change breaking_change to false if needed.
breaking_change: true
# The stage and GitLab username of the person reporting the change,
# and a link to the deprecation issue
reporter: timofurrer
stage: deploy
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/440249
body: | # (required) Don't change this line.
The `fmt` job in the Terraform Module CI/CD templates is deprecated and will be removed in GitLab 17.0.
This affects the following templates:
- `Terraform-Module.gitlab-ci.yml`
- `Terraform/Module-Base.gitlab-ci.yml`
You can manually add back a Terraform `fmt` job to your pipeline using:
```yaml
fmt:
image: hashicorp/terraform
script: terraform fmt -chdir "$TF_ROOT" -check -diff -recursive
```
You can also use the `fmt` template from the [OpenTofu CI/CD component](https://gitlab.com/components/opentofu).

View File

@ -18,7 +18,7 @@ and the following external authentication and authorization providers:
and 389 Server.
- [Google Secure LDAP](ldap/google_secure_ldap.md)
- [SAML for GitLab.com groups](../../user/group/saml_sso/index.md)
- [Smartcard](smartcard.md)
- [Smart card](smartcard.md)
NOTE:
UltraAuth has removed their software which supports OmniAuth integration. We have therefore removed all references to UltraAuth integration.
@ -32,7 +32,7 @@ For more information, see the links shown on this page for each external provide
|-------------------------------------------------|-----------------------------------------|------------------------------------|
| **User Provisioning** | SCIM<br>SAML <sup>1</sup> | LDAP <sup>1</sup><br>SAML <sup>1</sup><br>[OmniAuth Providers](../../integration/omniauth.md#supported-providers) <sup>1</sup><br>SCIM |
| **User Detail Updating** (not group management) | Not Available | LDAP Sync |
| **Authentication** | SAML at top-level group (1 provider) | LDAP (multiple providers)<br>Generic OAuth 2.0<br>SAML (only 1 permitted per unique provider)<br>Kerberos<br>JWT<br>Smartcard<br>[OmniAuth Providers](../../integration/omniauth.md#supported-providers) (only 1 permitted per unique provider) |
| **Authentication** | SAML at top-level group (1 provider) | LDAP (multiple providers)<br>Generic OAuth 2.0<br>SAML (only 1 permitted per unique provider)<br>Kerberos<br>JWT<br>Smart card<br>[OmniAuth Providers](../../integration/omniauth.md#supported-providers) (only 1 permitted per unique provider) |
| **Provider-to-GitLab Role Sync** | SAML Group Sync | LDAP Group Sync<br>SAML Group Sync ([GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/285150) and later) |
| **User Removal** | SCIM (remove user from top-level group) | LDAP (remove user from groups and block from the instance)<br>SCIM |

View File

@ -4,22 +4,22 @@ group: Authentication
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
---
# Smartcard authentication
# Smart card authentication
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
GitLab supports authentication using smartcards.
GitLab supports authentication using smart cards.
## Existing password authentication
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33669) in GitLab 12.6.
By default, existing users can continue to sign in with a username and password when smartcard
By default, existing users can continue to sign in with a username and password when smart card
authentication is enabled.
To force existing users to use only smartcard authentication,
To force existing users to use only smart card authentication,
[disable username and password authentication](../settings/sign_in_restrictions.md#password-authentication-enabled).
## Authentication methods
@ -34,12 +34,11 @@ GitLab supports two authentication methods:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/726) in GitLab 11.6 as an experimental feature.
WARNING:
Smartcard authentication against local databases may change or be removed completely in future
releases.
Smart card authentication against local databases may change or be removed completely in future releases.
Smartcards with X.509 certificates can be used to authenticate with GitLab.
Smart cards with X.509 certificates can be used to authenticate with GitLab.
To use a smartcard with an X.509 certificate to authenticate against a local
To use a smart card with an X.509 certificate to authenticate against a local
database with GitLab, `CN` and `emailAddress` must be defined in the
certificate. For example:
@ -60,14 +59,14 @@ Certificate:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8605) in GitLab 12.3.
Smartcards with X.509 certificates using SAN extensions can be used to authenticate
Smart cards with X.509 certificates using SAN extensions can be used to authenticate
with GitLab.
NOTE:
This is an experimental feature. Smartcard authentication against local databases may
This is an experimental feature. Smart card authentication against local databases may
change or be removed completely in future releases.
To use a smartcard with an X.509 certificate to authenticate against a local
To use a smart card with an X.509 certificate to authenticate against a local
database with GitLab, in:
- GitLab 12.4 and later, at least one of the `subjectAltName` (SAN) extensions
@ -101,7 +100,7 @@ Certificate:
### Authentication against an LDAP server
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7693) in GitLab 11.8 as an experimental feature. Smartcard authentication against an LDAP server may change or be removed completely in the future.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7693) in GitLab 11.8 as an experimental feature. Smart card authentication against an LDAP server may change or be removed completely in the future.
GitLab implements a standard way of certificate matching following
[RFC4523](https://www.rfc-editor.org/rfc/rfc4523). It uses the
@ -116,14 +115,14 @@ Active Directory doesn't support the `certificateExactMatch` matching rule so
[it is not supported at this time](https://gitlab.com/gitlab-org/gitlab/-/issues/327491). For
more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/328074).
## Configure GitLab for smartcard authentication
## Configure GitLab for smart card authentication
For Linux package installations:
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
# Allow smartcard authentication
# Allow smart card authentication
gitlab_rails['smartcard_enabled'] = true
# Path to a file containing a CA certificate
@ -215,9 +214,9 @@ For self-compiled installations:
1. Edit `config/gitlab.yml`:
```yaml
## Smartcard authentication settings
## Smart card authentication settings
smartcard:
# Allow smartcard authentication
# Allow smart card authentication
enabled: true
# Path to a file containing a CA certificate
@ -251,7 +250,7 @@ For Linux package installations:
For self-compiled installations:
1. Add the `san_extensions` line to `config/gitlab.yml` within the smartcard section:
1. Add the `san_extensions` line to `config/gitlab.yml` within the smart card section:
```yaml
smartcard:
@ -276,7 +275,7 @@ For Linux package installations:
gitlab_rails['ldap_servers'] = YAML.load <<-EOS
main:
# snip...
# Enable smartcard authentication against the LDAP server. Valid values
# Enable smart card authentication against the LDAP server. Valid values
# are "false", "optional", and "required".
smartcard_auth: optional
EOS
@ -295,7 +294,7 @@ For self-compiled installations:
servers:
main:
# snip...
# Enable smartcard authentication against the LDAP server. Valid values
# Enable smart card authentication against the LDAP server. Valid values
# are "false", "optional", and "required".
smartcard_auth: optional
```
@ -303,7 +302,7 @@ For self-compiled installations:
1. Save the file and [restart](../restart_gitlab.md#self-compiled-installations)
GitLab for the changes to take effect.
### Require browser session with smartcard sign-in for Git access
### Require browser session with smart card sign-in for Git access
For Linux package installations:
@ -321,19 +320,19 @@ For self-compiled installations:
1. Edit `config/gitlab.yml`:
```yaml
## Smartcard authentication settings
## Smart card authentication settings
smartcard:
# snip...
# Browser session with smartcard sign-in is required for Git access
# Browser session with smart card sign-in is required for Git access
required_for_git_access: true
```
1. Save the file and [restart](../restart_gitlab.md#self-compiled-installations)
GitLab for the changes to take effect.
## Passwords for users created via smartcard authentication
## Passwords for users created via smart card authentication
The [Generated passwords for users created through integrated authentication](../../security/passwords_for_integrated_authentication_methods.md) guide provides an overview of how GitLab generates and sets passwords for users created via smartcard authentication.
The [Generated passwords for users created through integrated authentication](../../security/passwords_for_integrated_authentication_methods.md) guide provides an overview of how GitLab generates and sets passwords for users created via smart card authentication.
<!-- ## Troubleshooting

View File

@ -774,7 +774,7 @@ DRIs:
| Leadership | Mark Nuzzo |
| Product | Dov Hershkovitch |
| Engineering | Fabio Pitino |
| UX | Kevin Comoli (interim), Sunjung Park |
| UX | Sunjung Park |
Domain experts:

View File

@ -103,6 +103,9 @@ To find a domain expert:
NOTE:
Reviewer roulette is an internal tool for use on GitLab.com, and not available for use on customer installations.
NOTE:
Until %16.11, GitLab is running [an experiment](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/377) to remove hungriness and busy indicators.
The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for
each area of the codebase that your merge request seems to touch. It makes
**recommendations** for developer reviewers and you should override it if you think someone else is a better
@ -140,7 +143,7 @@ page, with these behaviors:
not counted. These MRs are usually backports, and maintainers or reviewers usually
do not need much time reviewing them.
- Team members whose Slack or [GitLab status](../user/profile/index.md#set-your-current-status) emoji
- 'Hungriness' for reviews: Team members whose Slack or [GitLab status](../user/profile/index.md#set-your-current-status) emoji
is 🔵 `:large_blue_circle:` are more likely to be picked. This applies to both reviewers and trainee maintainers.
- Reviewers with 🔵 `:large_blue_circle:` are two times as likely to be picked as other reviewers.
- [Trainee maintainers](https://handbook.gitlab.com/handbook/engineering/workflow/code-review/#trainee-maintainer) with 🔵 `:large_blue_circle:` are three times as likely to be picked as other reviewers.

View File

@ -148,10 +148,12 @@ class Ci::PipelineCreatedEvent < Gitlab::EventStore::Event
end
```
The schema is validated immediately when we initialize the event object so we can ensure that
publishers follow the contract with the subscribers.
The schema, which must be a valid [JSON schema](https://json-schema.org/specification), is validated
by the [`JSONSchemer`](https://github.com/davishmcclurg/json_schemer) gem. The validation happens
immediately when you initialize the event object to ensure that publishers follow the contract
with the subscribers.
We recommend using optional properties as much as possible, which require fewer rollouts for schema changes.
You should use optional properties as much as possible, which require fewer rollouts for schema changes.
However, `required` properties could be used for unique identifiers of the event's subject. For example:
- `pipeline_id` can be a required property for a `Ci::PipelineCreatedEvent`.
@ -375,6 +377,21 @@ it 'publishes a ProjectCreatedEvent with project id and namespace id' do
end
```
When you publish multiple events, you can also check for non-published events.
```ruby
it 'publishes a ProjectCreatedEvent with project id and namespace id' do
# The project ID is generated when `create_project`
# is called in the `expect` block.
expected_data = { project_id: kind_of(Numeric), namespace_id: group_id }
expect { create_project(user, name: 'Project', path: 'project', namespace_id: group_id) }
.to publish_event(Projects::ProjectCreatedEvent)
.with(expected_data)
.and not_publish_event(Projects::ProjectDeletedEvent)
end
```
### Testing the subscriber
The subscriber must ensure that a published event can be consumed correctly. For this purpose

View File

@ -141,7 +141,7 @@ To help you migrate your data to GitLab Dedicated, you can choose from the follo
The following GitLab application features are not available:
- LDAP, Smartcard, or Kerberos authentication
- LDAP, smart card, or Kerberos authentication
- Multiple login providers
- GitLab Pages
- FortiAuthenticator, or FortiToken 2FA

View File

@ -578,6 +578,34 @@ These fields (`architectureName`, `ipAddress`, `platformName`, `revision`, `vers
<div class="deprecation breaking-change" data-milestone="17.0">
### Deprecate `fmt` job in Terraform Module CI/CD template
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.9</span>
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/440249).
</div>
The `fmt` job in the Terraform Module CI/CD templates is deprecated and will be removed in GitLab 17.0.
This affects the following templates:
- `Terraform-Module.gitlab-ci.yml`
- `Terraform/Module-Base.gitlab-ci.yml`
You can manually add back a Terraform `fmt` job to your pipeline using:
```yaml
fmt:
image: hashicorp/terraform
script: terraform fmt -chdir "$TF_ROOT" -check -diff -recursive
```
You can also use the `fmt` template from the [OpenTofu CI/CD component](https://gitlab.com/components/opentofu).
</div>
<div class="deprecation breaking-change" data-milestone="17.0">
### Deprecate `message` field from Vulnerability Management features
<div class="deprecation-notes">

View File

@ -28,7 +28,7 @@ SAML Group Sync only manages a group if that group has one or more SAML group li
You must configure the SAML group links before you configure SAML Group Sync.
When SAML is enabled, users with the Maintainer or Owner role see a new menu
When SAML is enabled, users with the Owner role see a new menu
item in group **Settings > SAML Group Links**.
- You can configure one or more **SAML Group Links** to map a SAML identity

View File

@ -438,7 +438,7 @@ DETAILS:
> - Moved to GitLab Premium in 13.9.
You can set a work in progress (WIP) limit for each issue list on an issue board. When a limit is
set, the list's header shows the number of issues in the list and the soft limit of issues.
set, the list's header shows the number of issues in the list and the soft limit of issues. A line in the list separates items within the limit from those in excess of the limit.
You cannot set a WIP limit on the default lists (**Open** and **Closed**).
Examples:
@ -446,7 +446,7 @@ Examples:
- When you have a list with four issues and a limit of five, the header shows **4/5**.
If you exceed the limit, the current number of issues is shown in red.
- You have a list with five issues with a limit of five. When you move another issue to that list,
the list's header displays **6/5**, with the six shown in red.
the list's header displays **6/5**, with the six shown in red. The work in progress line is shown before the sixth issue.
Prerequisites:

View File

@ -11,25 +11,19 @@ DETAILS:
**Offering:** SaaS, self-managed
> - **Merge when pipeline succeeds** and **Add to merge train when pipeline succeeds** [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/409530) to **Auto-merge** in GitLab 16.0 [with a flag](../../../administration/feature_flags.md) named `auto_merge_labels_mr_widget`. Enabled by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120922) in GitLab 16.0. Feature flag `auto_merge_labels_mr_widget` removed.
If you review a merge request and it's ready to merge, but the pipeline hasn't
completed yet, you can set it to auto-merge. You don't
have to remember later to merge the work manually:
If the content of a merge request is ready to merge, use **Set to auto-merge** on
the merge request. You don't have to remember later to merge the work manually. If set,
a merge request auto-merges when all these conditions are met:
- The merge request pipeline must complete successfully.
- All required approvals must be given.
![Auto-merge is ready](img/auto_merge_ready_v16_0.png)
NOTE:
[In GitLab 16.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/359057), **Merge when pipeline succeeds** and **Add to merge train when pipeline succeeds** are renamed **Set to auto-merge**.
If the pipeline succeeds, the merge request is merged. If the pipeline fails, the
author can either retry any failed jobs, or push new commits to fix the failure:
- If a retried job succeeds on the second try, the merge request is merged.
- If new commits are added to the merge request, GitLab cancels the request
to ensure the new changes are reviewed before merge.
- If new commits are added to the target branch of the merge request and
fast-forward only merge request is configured, GitLab cancels the request
to prevent merge conflicts.
The [merge when checks pass](#merge-when-checks-pass) feature, available in
GitLab 16.9 and later, adds more checks to the auto-merge process.
## Auto-merge a merge request
@ -57,6 +51,42 @@ If a new comment is added to the merge request after you select **Auto-merge**,
but before the pipeline completes, GitLab blocks the merge until you
resolve all existing threads.
### Merge when pipeline succeeds
If the pipeline succeeds, the merge request is merged. If the pipeline fails, the
author can either retry any failed jobs, or push new commits to fix the failure:
- If a retried job succeeds on the second try, the merge request is merged.
- If new commits are added to the merge request, GitLab cancels the request
to ensure the new changes are reviewed before merge.
- If new commits are added to the target branch of the merge request and
fast-forward only merge request is configured, GitLab cancels the request
to prevent merge conflicts.
### Merge when checks pass
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** SaaS
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10874) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `merge_when_checks_pass` and `additional_merge_when_checks_ready`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/412995) in GitLab 16.9.
FLAG:
On self-managed GitLab, by default this feature is not available. To enable the feature,
an administrator can [enable the feature flags](../../../administration/feature_flags.md)
named `merge_when_checks_pass` and `additional_merge_when_checks_ready`.
On GitLab.com, this feature is available.
In GitLab 16.9 and later, **Merge when checks pass** adds more checks to the auto-merge
process. When set to auto-merge, all of these checks must pass for a merge request to merge:
- The merge request pipeline must complete successfully.
- All required approvals must be given.
- The merge request must not be a **Draft**.
- All discussions must be resolved.
- All blocking merge requests must be merged or closed.
## Cancel an auto-merge
If a merge request is set to auto-merge, you can cancel it.
@ -110,8 +140,6 @@ despite a newer but failed branch pipeline.
### Allow merge after skipped pipelines
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211482) in GitLab 13.1.
When the **Pipelines must succeed** checkbox is checked,
[skipped pipelines](../../../ci/pipelines/index.md#skip-a-pipeline) prevent
merge requests from being merged.

View File

@ -30,20 +30,8 @@ module API
end
SLASH_COMMAND_INTEGRATIONS = {
'mattermost-slash-commands' => [
{
name: :token,
type: String,
desc: 'The Mattermost token'
}
],
'slack-slash-commands' => [
{
name: :token,
type: String,
desc: 'The Slack token'
}
]
'mattermost-slash-commands' => ::Integrations::MattermostSlashCommands.api_fields,
'slack-slash-commands' => ::Integrations::SlackSlashCommands.api_fields
}.freeze
helpers do

View File

@ -26,7 +26,7 @@ module Gitlab
def reserved_claims
super.merge({
iss: Feature.enabled?(:oidc_issuer_url) ? Gitlab.config.gitlab.url : Settings.gitlab.base_url,
iss: Gitlab.config.gitlab.url,
sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
aud: aud,
wlif: wlif

View File

@ -49471,6 +49471,9 @@ msgstr ""
msgid "The Slack notifications integration is deprecated and will be removed in a future release. To continue to receive notifications from Slack, use the GitLab for Slack app instead. %{learn_more_link_start}Learn more%{link_end}."
msgstr ""
msgid "The Slack token."
msgstr ""
msgid "The Snowplow cookie domain."
msgstr ""
@ -55944,6 +55947,9 @@ msgstr ""
msgid "Work in progress limit"
msgstr ""
msgid "Work in progress limit: %{wipLimit}"
msgstr ""
msgid "Work item parent removed successfully"
msgstr ""

View File

@ -7,6 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import createComponent from 'jest/boards/board_list_helper';
import { ESC_KEY_CODE } from '~/lib/utils/keycodes';
import BoardCard from '~/boards/components/board_card.vue';
import BoardCutLine from '~/boards/components/board_cut_line.vue';
import eventHub from '~/boards/eventhub';
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
import listIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
@ -22,6 +23,8 @@ describe('Board list component', () => {
const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
const findBoardListCount = () => wrapper.find('.board-list-count');
const maxIssueCountWarningClass = '.gl-bg-red-50';
const triggerInfiniteScroll = () => findIntersectionObserver().vm.$emit('appear');
const startDrag = (
@ -143,34 +146,48 @@ describe('Board list component', () => {
describe('max issue count warning', () => {
describe('when issue count exceeds max issue count', () => {
it('sets background to gl-bg-red-100', async () => {
wrapper = createComponent({ listProps: { issuesCount: 4, maxIssueCount: 3 } });
beforeEach(async () => {
wrapper = createComponent({ listProps: { issuesCount: 4, maxIssueCount: 2 } });
await waitForPromises();
const block = wrapper.find('.gl-bg-red-100');
});
it('sets background to warning color', () => {
const block = wrapper.find(maxIssueCountWarningClass);
expect(block.exists()).toBe(true);
expect(block.attributes('class')).toContain(
'gl-rounded-bottom-left-base gl-rounded-bottom-right-base',
);
});
it('shows cut line', () => {
const cutline = wrapper.findComponent(BoardCutLine);
expect(cutline.exists()).toBe(true);
expect(cutline.props('cutLineText')).toEqual('Work in progress limit: 2');
});
});
describe('when list issue count does NOT exceed list max issue count', () => {
it('does not sets background to gl-bg-red-100', async () => {
beforeEach(async () => {
wrapper = createComponent({ list: { issuesCount: 2, maxIssueCount: 3 } });
await waitForPromises();
expect(wrapper.find('.gl-bg-red-100').exists()).toBe(false);
});
it('does not sets background to warning color', () => {
expect(wrapper.find(maxIssueCountWarningClass).exists()).toBe(false);
});
it('does not show cut line', () => {
expect(wrapper.findComponent(BoardCutLine).exists()).toBe(false);
});
});
describe('when list max issue count is 0', () => {
it('does not sets background to gl-bg-red-100', async () => {
beforeEach(async () => {
wrapper = createComponent({ list: { maxIssueCount: 0 } });
await waitForPromises();
expect(wrapper.find('.gl-bg-red-100').exists()).toBe(false);
});
it('does not sets background to warning color', () => {
expect(wrapper.find(maxIssueCountWarningClass).exists()).toBe(false);
});
it('does not show cut line', () => {
expect(wrapper.findComponent(BoardCutLine).exists()).toBe(false);
});
});
});

View File

@ -0,0 +1,27 @@
import { shallowMount } from '@vue/test-utils';
import BoardCutLine from '~/boards/components/board_cut_line.vue';
describe('BoardCutLine', () => {
let wrapper;
const cutLineText = 'Work in progress limit: 3';
const createComponent = (props) => {
wrapper = shallowMount(BoardCutLine, { propsData: props });
};
describe('when cut line is shown', () => {
beforeEach(() => {
createComponent({ cutLineText });
});
it('contains cut line text in the template', () => {
expect(wrapper.find('[data-testid="cut-line-text"]').text()).toContain(
`Work in progress limit: 3`,
);
});
it('does not contain other text in the template', () => {
expect(wrapper.find('[data-testid="cut-line-text"]').text()).not.toContain(`unexpected`);
});
});
});

View File

@ -2,19 +2,17 @@ import { shallowMount } from '@vue/test-utils';
import IssueCount from '~/boards/components/item_count.vue';
describe('IssueCount', () => {
let vm;
let wrapper;
let maxIssueCount;
let itemsSize;
const createComponent = (props) => {
vm = shallowMount(IssueCount, { propsData: props });
wrapper = shallowMount(IssueCount, { propsData: props });
};
afterEach(() => {
maxIssueCount = 0;
itemsSize = 0;
if (vm) vm.destroy();
});
describe('when maxIssueCount is zero', () => {
@ -25,11 +23,11 @@ describe('IssueCount', () => {
});
it('contains issueSize in the template', () => {
expect(vm.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
expect(wrapper.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
});
it('does not contains maxIssueCount in the template', () => {
expect(vm.find('.max-issue-size').exists()).toBe(false);
expect(wrapper.find('.max-issue-size').exists()).toBe(false);
});
});
@ -42,15 +40,15 @@ describe('IssueCount', () => {
});
it('contains issueSize in the template', () => {
expect(vm.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
expect(wrapper.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
});
it('contains maxIssueCount in the template', () => {
expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
expect(wrapper.find('.max-issue-size').text()).toContain(String(maxIssueCount));
});
it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
expect(vm.classes('.text-danger')).toBe(false);
it('does not have red text when issueSize is less than maxIssueCount', () => {
expect(wrapper.classes('.gl-text-red-700')).toBe(false);
});
});
@ -63,15 +61,15 @@ describe('IssueCount', () => {
});
it('contains issueSize in the template', () => {
expect(vm.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
expect(wrapper.find('[data-testid="board-items-count"]').text()).toEqual(String(itemsSize));
});
it('contains maxIssueCount in the template', () => {
expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
expect(wrapper.find('.max-issue-size').text()).toContain(String(maxIssueCount));
});
it('has text-danger class', () => {
expect(vm.find('.text-danger').text()).toEqual(String(itemsSize));
it('has red text', () => {
expect(wrapper.find('.gl-text-red-700').text()).toEqual(String(itemsSize));
});
});
});

View File

@ -2,7 +2,8 @@ import { GlAlert } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import Autosave from '~/autosave';
import markdownEditorEventHub from '~/vue_shared/components/markdown/eventhub';
import { CLEAR_AUTOSAVE_ENTRY_EVENT } from '~/vue_shared/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
@ -94,6 +95,12 @@ describe('Design reply form component', () => {
expect(findTextarea().element).toEqual(document.activeElement);
});
it('allows switching to rich text', () => {
createComponent();
expect(wrapper.text()).toContain('Switch to rich text editing');
});
it('renders "Attach a file or image" button in markdown toolbar', () => {
createComponent();
@ -118,23 +125,6 @@ describe('Design reply form component', () => {
expect(findSubmitButton().html()).toMatchSnapshot();
});
it.each`
discussionId | shortDiscussionId
${undefined} | ${'new'}
${'gid://gitlab/DiffDiscussion/123'} | ${123}
`(
'initializes autosave support on discussion with proper key',
({ discussionId, shortDiscussionId }) => {
createComponent({ props: { discussionId } });
expect(Autosave).toHaveBeenCalledWith(expect.any(Element), [
'Discussion',
6,
shortDiscussionId,
]);
},
);
describe('when form has no text', () => {
beforeEach(() => {
createComponent();
@ -155,7 +145,7 @@ describe('Design reply form component', () => {
});
it('emits cancelForm event on pressing escape button on textarea', () => {
findTextarea().trigger('keyup.esc');
findTextarea().trigger('keydown.esc');
expect(wrapper.emitted('cancel-form')).toHaveLength(1);
});
@ -261,7 +251,7 @@ describe('Design reply form component', () => {
it('emits cancelForm event on Escape key if text was not changed', () => {
createComponent();
findTextarea().trigger('keyup.esc');
findTextarea().trigger('keydown.esc');
expect(wrapper.emitted('cancel-form')).toHaveLength(1);
});
@ -271,7 +261,7 @@ describe('Design reply form component', () => {
findTextarea().setValue(mockComment);
findTextarea().trigger('keyup.esc');
findTextarea().trigger('keydown.esc');
expect(confirmAction).toHaveBeenCalled();
});
@ -282,7 +272,7 @@ describe('Design reply form component', () => {
createComponent({ props: { value: mockComment } });
findTextarea().setValue('Comment changed');
findTextarea().trigger('keyup.esc');
findTextarea().trigger('keydown.esc');
expect(confirmAction).toHaveBeenCalled();
@ -296,7 +286,7 @@ describe('Design reply form component', () => {
createComponent({ props: { value: mockComment } });
findTextarea().setValue('Comment changed');
findTextarea().trigger('keyup.esc');
findTextarea().trigger('keydown.esc');
expect(confirmAction).toHaveBeenCalled();
await waitForPromises();
@ -306,11 +296,12 @@ describe('Design reply form component', () => {
});
describe('when component is destroyed', () => {
it('calls autosave.reset', async () => {
const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
it('clears autosave entry', async () => {
const clearAutosaveSpy = jest.fn();
markdownEditorEventHub.$on(CLEAR_AUTOSAVE_ENTRY_EVENT, clearAutosaveSpy);
createComponent();
await wrapper.destroy();
expect(autosaveResetSpy).toHaveBeenCalled();
expect(clearAutosaveSpy).toHaveBeenCalled();
});
});
});

View File

@ -37,7 +37,6 @@ import { mockCreateImageNoteDiffResponse } from '../../mock_data/apollo_mock';
jest.mock('~/alert');
jest.mock('~/api.js');
const focusInput = jest.fn();
const mockCacheObject = {
readQuery: jest.fn().mockReturnValue(mockProject),
writeQuery: jest.fn(),
@ -51,9 +50,6 @@ const mockPageLayoutElement = {
};
const DesignReplyForm = {
template: '<div><textarea ref="textarea"></textarea></div>',
methods: {
focusInput,
},
};
const mockDesignNoDiscussions = {
...design,
@ -219,22 +215,6 @@ describe('Design management design index page', () => {
expect(findDesignReplyForm().exists()).toBe(true);
});
it('keeps new discussion form focused', () => {
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
},
},
);
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
expect(focusInput).toHaveBeenCalled();
});
it('sends a update and closes the form when mutation is completed', async () => {
createComponent(
{ loading: false },

View File

@ -16,7 +16,6 @@ import { putCreateReleaseNotification } from '~/releases/release_notification_se
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
import ConfirmDeleteModal from '~/releases/components/confirm_delete_modal.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { ValidationResult } from '~/lib/utils/ref_validator';
const originalRelease = originalOneReleaseForEditingQueryResponse.data.project.release;
@ -41,6 +40,7 @@ describe('Release edit/new component', () => {
release,
isExistingRelease: true,
projectPath,
markdownPreviewPath: 'path/to/markdown/preview',
markdownDocsPath: 'path/to/markdown/docs',
releasesPagePath,
projectId: '8',
@ -54,6 +54,7 @@ describe('Release edit/new component', () => {
saveRelease: jest.fn(),
addEmptyAssetLink: jest.fn(),
deleteRelease: jest.fn(),
updateReleaseNotes: jest.fn(),
};
getters = {
@ -173,15 +174,14 @@ describe('Release edit/new component', () => {
expect(wrapper.find('#release-notes').element.value).toBe(release.description);
});
it('sets the preview text to be the formatted release notes', () => {
const notes = getters.formattedReleaseNotes();
expect(wrapper.findComponent(MarkdownField).props('textareaValue')).toBe(notes);
});
it('renders the "Save changes" button as type="submit"', () => {
expect(findSubmitButton().attributes('type')).toBe('submit');
});
it('allows switching to rich text editor', () => {
expect(wrapper.html()).toContain('Switch to rich text editing');
});
it('calls saveRelease when the form is submitted', () => {
findForm().trigger('submit');

View File

@ -43,7 +43,7 @@ RSpec.describe NamespacesHelper, feature_category: :groups_and_projects do
end
describe '#cascading_namespace_settings_popover_data' do
attribute = :delayed_project_removal
attribute = :toggle_security_policy_custom_ci
subject do
helper.cascading_namespace_settings_popover_data(
@ -94,7 +94,7 @@ RSpec.describe NamespacesHelper, feature_category: :groups_and_projects do
end
describe '#cascading_namespace_setting_locked?' do
let(:attribute) { :delayed_project_removal }
let(:attribute) { :toggle_security_policy_custom_ci }
context 'when `group` argument is `nil`' do
it 'returns `false`' do
@ -110,13 +110,13 @@ RSpec.describe NamespacesHelper, feature_category: :groups_and_projects do
context 'when `*_locked?` method does exist' do
before do
allow(admin_group.namespace_settings).to receive(:delayed_project_removal_locked?).and_return(true)
allow(admin_group.namespace_settings).to receive(:toggle_security_policy_custom_ci_locked?).and_return(true)
end
it 'calls corresponding `*_locked?` method' do
helper.cascading_namespace_setting_locked?(attribute, admin_group, include_self: true)
expect(admin_group.namespace_settings).to have_received(:delayed_project_removal_locked?).with(include_self: true)
expect(admin_group.namespace_settings).to have_received(:toggle_security_policy_custom_ci_locked?).with(include_self: true)
end
end
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe API::Helpers::IntegrationsHelpers, feature_category: :integrations do
let(:base_classes) { Integration::BASE_CLASSES.map(&:constantize) }
let(:development_classes) { [Integrations::MockCi, Integrations::MockMonitoring] }
let(:instance_level_classes) { [Integrations::BeyondIdentity] }
describe '.chat_notification_flags' do
it 'returns correct values' do
expect(described_class.chat_notification_flags).to match_array(
[
{
required: false,
name: :notify_only_broken_pipelines,
type: ::Grape::API::Boolean,
desc: 'Send notifications for broken pipelines'
}
]
)
end
end
describe '.integrations' do
it 'has correct integrations' do
expect(described_class.integrations.keys.map(&:underscore))
.to match_array(described_class.integration_classes.map(&:to_param))
end
end
describe '.integration_classes' do
it 'returns correct integrations' do
expect(described_class.integration_classes)
.to match_array(Integration.descendants.without(base_classes, development_classes, instance_level_classes))
end
end
describe '.development_integration_classes' do
it 'returns correct integrations' do
expect(described_class.development_integration_classes).to eq(development_classes)
end
end
end

View File

@ -46,31 +46,11 @@ RSpec.describe Gitlab::Ci::JwtV2, feature_category: :secrets_management do
expect(payload).not_to include(:user_identities)
end
context 'when oidc_issuer_url is disabled' do
before do
stub_feature_flags(oidc_issuer_url: false)
end
it 'has correct values for the standard JWT attributes' do
aggregate_failures do
expect(payload[:iss]).to eq(Settings.gitlab.base_url)
expect(payload[:aud]).to eq(Settings.gitlab.base_url)
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
end
end
end
context 'when oidc_issuer_url is enabled' do
before do
stub_feature_flags(oidc_issuer_url: true)
end
it 'has correct values for the standard JWT attributes' do
aggregate_failures do
expect(payload[:iss]).to eq(Gitlab.config.gitlab.url)
expect(payload[:aud]).to eq(Settings.gitlab.base_url)
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
end
it 'has correct values for the standard JWT attributes' do
aggregate_failures do
expect(payload[:iss]).to eq(Gitlab.config.gitlab.url)
expect(payload[:aud]).to eq(Settings.gitlab.base_url)
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
end
end

View File

@ -448,8 +448,12 @@ RSpec.describe NamespaceSetting, feature_category: :groups_and_projects, type: :
end
end
describe '#delayed_project_removal' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :delayed_project_removal
describe '#toggle_security_policy_custom_ci' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :toggle_security_policy_custom_ci
end
describe '#toggle_security_policies_policy_scope' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :toggle_security_policies_policy_scope
end
describe 'default_branch_protection_defaults' do