Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bc69b91579
commit
2b9f6cfb3f
|
|
@ -1058,29 +1058,6 @@ Layout/ArgumentAlignment:
|
||||||
- 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
|
- 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
|
||||||
- 'ee/spec/features/projects/milestones/milestone_spec.rb'
|
- 'ee/spec/features/projects/milestones/milestone_spec.rb'
|
||||||
- 'ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
|
- 'ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
|
||||||
- 'ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/merge_requests/user_manages_approval_settings_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_pipelines_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_requests_template_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_trains_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/user_manages_approval_settings_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/settings/user_manages_merge_requests_template_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/email_confirmation_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/standard_flow_company_creating_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/standard_flow_company_joining_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/standard_flow_just_me_creating_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/standard_flow_just_me_importing_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/standard_flow_just_me_joining_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/subscription_flow_paid_plan_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/trial_flow_company_creating_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/trial_flow_company_importing_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/trial_flow_just_me_creating_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/saas/trial_flow_just_me_importing_project_spec.rb'
|
|
||||||
- 'ee/spec/features/registrations/sign_up_with_trial_from_external_site_without_confirmation_spec.rb'
|
|
||||||
- 'ee/spec/features/search/elastic/global_search_spec.rb'
|
|
||||||
- 'ee/spec/features/search/elastic/group_search_spec.rb'
|
|
||||||
- 'ee/spec/features/security/project/discover_spec.rb'
|
|
||||||
- 'ee/spec/features/users/identity_verification_spec.rb'
|
|
||||||
- 'ee/spec/frontend/fixtures/dora/metrics.rb'
|
- 'ee/spec/frontend/fixtures/dora/metrics.rb'
|
||||||
- 'ee/spec/frontend/fixtures/oncall_schedule.rb'
|
- 'ee/spec/frontend/fixtures/oncall_schedule.rb'
|
||||||
- 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
|
- 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
|
||||||
|
|
@ -1570,22 +1547,6 @@ Layout/ArgumentAlignment:
|
||||||
- 'spec/components/previews/pajamas/alert_component_preview.rb'
|
- 'spec/components/previews/pajamas/alert_component_preview.rb'
|
||||||
- 'spec/components/previews/pajamas/banner_component_preview.rb'
|
- 'spec/components/previews/pajamas/banner_component_preview.rb'
|
||||||
- 'spec/components/previews/pajamas/button_component_preview.rb'
|
- 'spec/components/previews/pajamas/button_component_preview.rb'
|
||||||
- 'spec/features/admin/admin_mode/login_spec.rb'
|
|
||||||
- 'spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb'
|
|
||||||
- 'spec/features/atom/issues_spec.rb'
|
|
||||||
- 'spec/features/atom/merge_requests_spec.rb'
|
|
||||||
- 'spec/features/atom/users_spec.rb'
|
|
||||||
- 'spec/features/boards/issue_ordering_spec.rb'
|
|
||||||
- 'spec/features/boards/multi_select_spec.rb'
|
|
||||||
- 'spec/features/boards/sidebar_assignee_spec.rb'
|
|
||||||
- 'spec/features/calendar_spec.rb'
|
|
||||||
- 'spec/features/clusters/cluster_health_dashboard_spec.rb'
|
|
||||||
- 'spec/features/commits_spec.rb'
|
|
||||||
- 'spec/features/dashboard/activity_spec.rb'
|
|
||||||
- 'spec/features/dashboard/datetime_on_tooltips_spec.rb'
|
|
||||||
- 'spec/features/dashboard/merge_requests_spec.rb'
|
|
||||||
- 'spec/features/dashboard/todos/todos_sorting_spec.rb'
|
|
||||||
- 'spec/features/dashboard/todos/todos_spec.rb'
|
|
||||||
- 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
|
- 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
|
||||||
- 'spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
|
- 'spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
|
||||||
- 'spec/features/error_tracking/user_sees_error_details_spec.rb'
|
- 'spec/features/error_tracking/user_sees_error_details_spec.rb'
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export default {
|
||||||
this.openModal();
|
this.openModal();
|
||||||
},
|
},
|
||||||
extraAttrs: {
|
extraAttrs: {
|
||||||
'data-qa-selector': 'delete_merged_branches_button',
|
'data-testid': 'delete-merged-branches-button',
|
||||||
class: 'gl-text-red-500!',
|
class: 'gl-text-red-500!',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -102,12 +102,11 @@ export default {
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
no-caret
|
no-caret
|
||||||
placement="right"
|
placement="right"
|
||||||
data-qa-selector="delete_merged_branches_dropdown_button"
|
|
||||||
class="gl-display-none gl-md-display-block!"
|
class="gl-display-none gl-md-display-block!"
|
||||||
:items="dropdownItems"
|
:items="dropdownItems"
|
||||||
/>
|
/>
|
||||||
<gl-button
|
<gl-button
|
||||||
data-qa-selector="delete_merged_branches_button"
|
data-testid="delete-merged-branches-button"
|
||||||
category="secondary"
|
category="secondary"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
class="gl-display-block gl-md-display-none!"
|
class="gl-display-block gl-md-display-none!"
|
||||||
|
|
@ -153,7 +152,6 @@ export default {
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
<gl-form-input
|
<gl-form-input
|
||||||
v-model="enteredText"
|
v-model="enteredText"
|
||||||
data-qa-selector="delete_merged_branches_input"
|
|
||||||
type="text"
|
type="text"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="gl-mt-2"
|
class="gl-mt-2"
|
||||||
|
|
@ -178,7 +176,6 @@ export default {
|
||||||
ref="deleteMergedBrancesButton"
|
ref="deleteMergedBrancesButton"
|
||||||
:disabled="isDeleteButtonDisabled"
|
:disabled="isDeleteButtonDisabled"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
data-qa-selector="delete_merged_branches_confirmation_button"
|
|
||||||
data-testid="delete-merged-branches-confirmation-button"
|
data-testid="delete-merged-branches-confirmation-button"
|
||||||
@click="submitForm"
|
@click="submitForm"
|
||||||
>{{ $options.i18n.deleteButtonText }}</gl-button
|
>{{ $options.i18n.deleteButtonText }}</gl-button
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
GlLink,
|
GlLink,
|
||||||
GlTable,
|
GlTable,
|
||||||
GlDropdownItem,
|
GlDisclosureDropdown,
|
||||||
GlDropdown,
|
GlDisclosureDropdownItem,
|
||||||
GlButton,
|
GlButton,
|
||||||
GlFormCheckbox,
|
GlFormCheckbox,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
|
|
@ -51,8 +51,8 @@ export default {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
GlLink,
|
GlLink,
|
||||||
GlTable,
|
GlTable,
|
||||||
GlDropdown,
|
GlDisclosureDropdown,
|
||||||
GlDropdownItem,
|
GlDisclosureDropdownItem,
|
||||||
GlFormCheckbox,
|
GlFormCheckbox,
|
||||||
GlButton,
|
GlButton,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
|
|
@ -426,6 +426,7 @@ export default {
|
||||||
v-if="hasDetails(item)"
|
v-if="hasDetails(item)"
|
||||||
:icon="detailsShowing ? 'chevron-up' : 'chevron-down'"
|
:icon="detailsShowing ? 'chevron-up' : 'chevron-down'"
|
||||||
:aria-label="detailsShowing ? __('Collapse') : __('Expand')"
|
:aria-label="detailsShowing ? __('Collapse') : __('Expand')"
|
||||||
|
data-testid="toggle-details-button"
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
size="small"
|
size="small"
|
||||||
@click="
|
@click="
|
||||||
|
|
@ -453,18 +454,23 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell(actions)="{ item }">
|
<template #cell(actions)="{ item }">
|
||||||
<gl-dropdown
|
<gl-disclosure-dropdown
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
icon="ellipsis_v"
|
icon="ellipsis_v"
|
||||||
:text-sr-only="true"
|
placement="right"
|
||||||
:text="$options.i18n.moreActionsText"
|
:toggle-text="$options.i18n.moreActionsText"
|
||||||
|
text-sr-only
|
||||||
no-caret
|
no-caret
|
||||||
right
|
|
||||||
>
|
>
|
||||||
<gl-dropdown-item data-testid="delete-file" @click="handleFileDelete([item])">
|
<gl-disclosure-dropdown-item
|
||||||
{{ $options.i18n.deleteFile }}
|
data-testid="delete-file"
|
||||||
</gl-dropdown-item>
|
@action="handleFileDelete([item])"
|
||||||
</gl-dropdown>
|
>
|
||||||
|
<template #list-item>
|
||||||
|
{{ $options.i18n.deleteFile }}
|
||||||
|
</template>
|
||||||
|
</gl-disclosure-dropdown-item>
|
||||||
|
</gl-disclosure-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #row-details="{ item }">
|
<template #row-details="{ item }">
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
= branch.name
|
= branch.name
|
||||||
= clipboard_button(text: branch.name, title: _("Copy branch name"))
|
= clipboard_button(text: branch.name, title: _("Copy branch name"))
|
||||||
- if is_default_branch
|
- if is_default_branch
|
||||||
= gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
|
= gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2' }
|
||||||
- if protected_branch?(@project, branch)
|
- if protected_branch?(@project, branch)
|
||||||
= gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
|
= gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2' }
|
||||||
|
|
||||||
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
|
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
.issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
|
.issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
|
||||||
= gl_badge_tag issuable_reference(related_merge_request),
|
= gl_badge_tag issuable_reference(related_merge_request),
|
||||||
{ icon: mr_status[:icon], variant: mr_status[:variant], size: :md, href: merge_request_path(related_merge_request) },
|
{ icon: mr_status[:icon], variant: mr_status[:variant], size: :md, href: merge_request_path(related_merge_request) },
|
||||||
{ class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body', qa_selector: 'badge_content' } }
|
{ class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body' } }
|
||||||
|
|
||||||
- elsif mr_status.nil? && create_mr_button?(from: branch.name, source_project: @project)
|
- elsif mr_status.nil? && create_mr_button?(from: branch.name, source_project: @project)
|
||||||
= render Pajamas::ButtonComponent.new(icon: 'merge-request', href: create_mr_path(from: branch.name, source_project: @project), button_options: { class: 'has-tooltip', title: _('New merge request') }) do
|
= render Pajamas::ButtonComponent.new(icon: 'merge-request', href: create_mr_path(from: branch.name, source_project: @project), button_options: { class: 'has-tooltip', title: _('New merge request') }) do
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ScheduleIndexEventsOnProjectIdAndIdDescOnMergedActionForRemoval < Gitlab::Database::Migration[2.1]
|
||||||
|
INDEX_NAME = 'index_events_on_project_id_and_id_desc_on_merged_action'
|
||||||
|
|
||||||
|
# TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/415091
|
||||||
|
|
||||||
|
def up
|
||||||
|
prepare_async_index_removal :events, [:project_id, :id], order: { id: :desc },
|
||||||
|
where: "action = 7", name: INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
unprepare_async_index :events, [:project_id, :id], order: { id: :desc },
|
||||||
|
where: "action = 7", name: INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PrepareIndexForVulnerabilityReadsOnCommonProjectFilters < Gitlab::Database::Migration[2.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_project_vulnerability_reads_common_finder_query_desc'
|
||||||
|
|
||||||
|
def up
|
||||||
|
prepare_async_index :vulnerability_reads,
|
||||||
|
[:project_id, :state, :report_type, :severity, :vulnerability_id],
|
||||||
|
order: { severity: :desc, vulnerability_id: :desc },
|
||||||
|
name: INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
unprepare_async_index_by_name :vulnerability_reads, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
6bf4fa6d2e43f1b589204f3b58323f32d9db2344882507e14bc487913cbe6f8e
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
248e7dabf83e225c5f5ee0de87e0842e8c3ec13f6098720830ce3b817a4d36a8
|
||||||
|
|
@ -33596,7 +33596,7 @@ CREATE UNIQUE INDEX unique_index_for_project_pages_unique_domain ON project_sett
|
||||||
|
|
||||||
CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
|
CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX unique_instance_audit_event_destination_namespace_id_and_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
|
CREATE UNIQUE INDEX unique_instance_audit_event_destination_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
|
CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
|
||||||
1. Install and configure Tomcat 10:
|
1. Install and configure Tomcat 10:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd /tmp & wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz
|
wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz -P /tmp
|
||||||
sudo tar xzvf apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
|
sudo tar xzvf /tmp/apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
|
||||||
sudo chown -R tomcat:tomcat /opt/tomcat/
|
sudo chown -R tomcat:tomcat /opt/tomcat/
|
||||||
sudo chmod -R u+x /opt/tomcat/bin
|
sudo chmod -R u+x /opt/tomcat/bin
|
||||||
```
|
```
|
||||||
|
|
@ -270,7 +270,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
|
||||||
1. Install PlantUML and copy the `.war` file:
|
1. Install PlantUML and copy the `.war` file:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd / & git clone https://github.com/plantuml/plantuml-server.git
|
cd /
|
||||||
|
git clone https://github.com/plantuml/plantuml-server.git
|
||||||
cd plantuml-server
|
cd plantuml-server
|
||||||
mvn package
|
mvn package
|
||||||
cp /plantuml-server/target/plantuml.war /opt/tomcat/webapps/plantuml.war
|
cp /plantuml-server/target/plantuml.war /opt/tomcat/webapps/plantuml.war
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,20 @@ Example response:
|
||||||
```
|
```
|
||||||
|
|
||||||
Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
|
Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
|
||||||
the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`,
|
these parameters:
|
||||||
`delayed_group_deletion`, `default_project_deletion_protection`, `deletion_adjourned_period`, `disable_personal_access_tokens`, `geo_node_allowed_ips`,
|
|
||||||
or the `security_policy_global_group_approvers_enabled` parameters.
|
- `group_owners_can_manage_default_branch_protection`
|
||||||
|
- `file_template_project_id`
|
||||||
|
- `geo_node_allowed_ips`
|
||||||
|
- `geo_status_timeout`
|
||||||
|
- `delayed_project_deletion`
|
||||||
|
- `delayed_group_deletion`
|
||||||
|
- `default_project_deletion_protection`
|
||||||
|
- `deletion_adjourned_period`
|
||||||
|
- `disable_personal_access_tokens`
|
||||||
|
- `security_policy_global_group_approvers_enabled`
|
||||||
|
- `delete_unconfirmed_users`
|
||||||
|
- `unconfirmed_users_delete_after_days`
|
||||||
|
|
||||||
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
|
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
|
||||||
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
|
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
|
||||||
|
|
@ -257,6 +268,8 @@ these parameters:
|
||||||
- `deletion_adjourned_period`
|
- `deletion_adjourned_period`
|
||||||
- `disable_personal_access_tokens`
|
- `disable_personal_access_tokens`
|
||||||
- `security_policy_global_group_approvers_enabled`
|
- `security_policy_global_group_approvers_enabled`
|
||||||
|
- `delete_unconfirmed_users`
|
||||||
|
- `unconfirmed_users_delete_after_days`
|
||||||
|
|
||||||
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
|
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
|
||||||
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
|
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
|
||||||
|
|
@ -328,6 +341,7 @@ listed in the descriptions of the relevant settings.
|
||||||
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
|
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
|
||||||
| `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
|
| `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
|
||||||
| `default_project_deletion_protection` **(PREMIUM SELF)** | boolean | no | Enable default project deletion protection so only administrators can delete projects. Default is `false`. |
|
| `default_project_deletion_protection` **(PREMIUM SELF)** | boolean | no | Enable default project deletion protection so only administrators can delete projects. Default is `false`. |
|
||||||
|
| `delete_unconfirmed_users` **(PREMIUM SELF)** | boolean | no | Specifies whether users who have not confirmed their email should be deleted. Default is `false`. When set to `true`, unconfirmed users are deleted after `unconfirmed_users_delete_after_days` days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
|
||||||
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
|
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
|
||||||
| `diagramsnet_enabled` | boolean | no | (If enabled, requires `diagramsnet_url`) Enable [Diagrams.net integration](../administration/integration/diagrams_net.md). Default is `true`. |
|
| `diagramsnet_enabled` | boolean | no | (If enabled, requires `diagramsnet_url`) Enable [Diagrams.net integration](../administration/integration/diagrams_net.md). Default is `true`. |
|
||||||
| `diagramsnet_url` | string | required by: `diagramsnet_enabled` | The Diagrams.net instance URL for integration. |
|
| `diagramsnet_url` | string | required by: `diagramsnet_enabled` | The Diagrams.net instance URL for integration. |
|
||||||
|
|
@ -534,6 +548,7 @@ listed in the descriptions of the relevant settings.
|
||||||
| `throttle_unauthenticated_web_requests_per_period` | integer | required by:<br>`throttle_unauthenticated_web_enabled` | Max requests per period per IP. |
|
| `throttle_unauthenticated_web_requests_per_period` | integer | required by:<br>`throttle_unauthenticated_web_enabled` | Max requests per period per IP. |
|
||||||
| `time_tracking_limit_to_hours` | boolean | no | Limit display of time tracking units to hours. Default is `false`. |
|
| `time_tracking_limit_to_hours` | boolean | no | Limit display of time tracking units to hours. Default is `false`. |
|
||||||
| `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
|
| `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
|
||||||
|
| `unconfirmed_users_delete_after_days` **(PREMIUM SELF)** | integer | no | Specifies how many days after sign-up to delete users who have not confirmed their email. Only applicable if `delete_unconfirmed_users` is set to `true`. Must be `1` or greater. Default is `7`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
|
||||||
| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
|
| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
|
||||||
| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
|
| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
|
||||||
| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
|
| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
|
||||||
|
|
|
||||||
|
|
@ -872,20 +872,21 @@ If you want to reduce risk slightly, consider putting the migrations into a
|
||||||
second merge request after the application changes are merged. This approach
|
second merge request after the application changes are merged. This approach
|
||||||
provides an opportunity to roll back.
|
provides an opportunity to roll back.
|
||||||
|
|
||||||
Removing the foreign key on the `projects` table:
|
Removing the foreign key on the `projects` table using a non-transactional migration:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# first migration file
|
# first migration file
|
||||||
|
class RemovingForeignKeyMigrationClass < Gitlab::Database::Migration[2.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
def up
|
def up
|
||||||
with_lock_retries do
|
with_lock_retries do
|
||||||
remove_foreign_key :my_table, :projects
|
remove_foreign_key :my_table, :projects
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def down
|
def down
|
||||||
with_lock_retries do
|
add_concurrent_foreign_key :my_table, :projects, column: COLUMN_NAME
|
||||||
add_foreign_key :my_table, :projects
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
@ -894,17 +895,19 @@ Dropping the table:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# second migration file
|
# second migration file
|
||||||
|
class DroppingTableMigrationClass < Gitlab::Database::Migration[2.1]
|
||||||
|
def up
|
||||||
|
drop_table :my_table
|
||||||
|
end
|
||||||
|
|
||||||
def up
|
def down
|
||||||
drop_table :my_table
|
# create_table with the same schema but without the removed foreign key ...
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
|
||||||
# create_table ...
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
After a table has been dropped, it should be added to the database dictionary, following the steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
|
After a table has been dropped, it should be added to the database dictionary, following the
|
||||||
|
steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
|
||||||
|
|
||||||
## Dropping a sequence
|
## Dropping a sequence
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,3 +139,165 @@ end
|
||||||
|
|
||||||
If classes that are defined into a namespace have a lot in common with classes in other namespaces,
|
If classes that are defined into a namespace have a lot in common with classes in other namespaces,
|
||||||
chances are that these two namespaces are part of the same bounded context.
|
chances are that these two namespaces are part of the same bounded context.
|
||||||
|
|
||||||
|
## Taming Omniscient classes
|
||||||
|
|
||||||
|
We must consider not adding new data and behavior to [omniscient classes](https://en.wikipedia.org/wiki/God_object) (also known as god objects).
|
||||||
|
We consider `Project`, `User`, `MergeRequest`, `Ci::Pipeline` and any classes above 1000 LOC to be omniscient.
|
||||||
|
|
||||||
|
Such classes are overloaded with responsibilities. New data and behavior can most of the time be added
|
||||||
|
as a separate and dedicated class.
|
||||||
|
|
||||||
|
Guidelines:
|
||||||
|
|
||||||
|
- If you mostly need a reference to the object ID (for example `Project#id`) you could add a new model
|
||||||
|
that uses the foreign key or a thin wrapper around the object to add special behavior.
|
||||||
|
- If you find out that by adding a method to the omniscient class you also end up adding a couple of other methods
|
||||||
|
(private or public) it's a sign that these methods should be encapsulated in a dedicated class.
|
||||||
|
- It's temping to add a method to `Project` because that's the starting point of data and associations.
|
||||||
|
Try to define behavior in the bounded context where it belongs, not where the data (or some of it) is.
|
||||||
|
This helps creating facets of the omniscient object that are much more relevant in the bounded context than
|
||||||
|
having generic and overloaded objects which bring more coupling and complexity.
|
||||||
|
|
||||||
|
### Example: Define a thin domain object around a generic model
|
||||||
|
|
||||||
|
Instead of adding multiple methods to `User` because it has an association to `abuse_trust_scores`,
|
||||||
|
try inverting the dependency.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
##
|
||||||
|
# BAD: Behavior added to User object.
|
||||||
|
class User
|
||||||
|
def spam_score
|
||||||
|
abuse_trust_scores.spamcheck.average(:score) || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def spammer?
|
||||||
|
# Warning sign: we use a constant that belongs to a specific bounded context!
|
||||||
|
spam_score > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
|
||||||
|
end
|
||||||
|
|
||||||
|
def telesign_score
|
||||||
|
abuse_trust_scores.telesign.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def arkose_global_score
|
||||||
|
abuse_trust_scores.arkose_global_score.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def arkose_custom_score
|
||||||
|
abuse_trust_scores.arkose_custom_score.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
user = User.find(1)
|
||||||
|
user.spam_score
|
||||||
|
user.telesign_score
|
||||||
|
user.arkose_global_score
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
##
|
||||||
|
# GOOD: Define a thin class that represents a user trust score
|
||||||
|
class Abuse::UserTrustScore
|
||||||
|
def initialize(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def spam
|
||||||
|
scores.spamcheck.average(:score) || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def spammer?
|
||||||
|
spam > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
|
||||||
|
end
|
||||||
|
|
||||||
|
def telesign
|
||||||
|
scores.telesign.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def arkose_global
|
||||||
|
scores.arkose_global_score.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def arkose_custom
|
||||||
|
scores.arkose_custom_score.recent_first.first&.score || 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def scores
|
||||||
|
Abuse::TrustScore.for_user(@user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
user = User.find(1)
|
||||||
|
user_score = Abuse::UserTrustScore.new(user)
|
||||||
|
user_score.spam
|
||||||
|
user_score.spammer?
|
||||||
|
user_score.telesign
|
||||||
|
user_score.arkose_global
|
||||||
|
```
|
||||||
|
|
||||||
|
See a real example [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117853#note_1423070054).
|
||||||
|
|
||||||
|
### Example: Use Dependency Inversion to extract a domain concept
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
##
|
||||||
|
# BAD: methods related to integrations defined in Project.
|
||||||
|
class Project
|
||||||
|
has_many :integrations
|
||||||
|
|
||||||
|
def find_or_initialize_integrations
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_initialize_integration(name)
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
def disabled_integrations
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
def ci_integrations
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
# many more methods...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
##
|
||||||
|
# GOOD: All logic related to Integrations is enclosed inside the `Integrations::`
|
||||||
|
# bounded context.
|
||||||
|
module Integrations
|
||||||
|
class ProjectIntegrations
|
||||||
|
def initialize(project)
|
||||||
|
@project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_integrations
|
||||||
|
@project.integrations # can still leverage caching of AR associations
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_initialize(name)
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_disabled
|
||||||
|
all_integrations.disabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_ci
|
||||||
|
all_integrations.ci_integration
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Real example of [similar refactoring](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92985).
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ PKG_METADATA_MANIFEST_OUTPUT_FILE="/tmp/license_db_export_manifest.json"
|
||||||
PKG_METADATA_DOWNLOADS_OUTPUT_FILE="/tmp/license_db_object_links.tsv"
|
PKG_METADATA_DOWNLOADS_OUTPUT_FILE="/tmp/license_db_object_links.tsv"
|
||||||
|
|
||||||
# Download the contents of the bucket
|
# Download the contents of the bucket
|
||||||
curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
|
curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o?maxResults=7500" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
|
||||||
|
|
||||||
# Parse the links and names for the bucket objects and output them into a tsv file
|
# Parse the links and names for the bucket objects and output them into a tsv file
|
||||||
jq -r '.items[] | [.name, .mediaLink] | @tsv' "$PKG_METADATA_MANIFEST_OUTPUT_FILE" > "$PKG_METADATA_DOWNLOADS_OUTPUT_FILE"
|
jq -r '.items[] | [.name, .mediaLink] | @tsv' "$PKG_METADATA_MANIFEST_OUTPUT_FILE" > "$PKG_METADATA_DOWNLOADS_OUTPUT_FILE"
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,15 @@ If you don't want any downtime, read how to [upgrade with zero downtime](zero_do
|
||||||
|
|
||||||
For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path).
|
For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path).
|
||||||
|
|
||||||
|
Required upgrade stops are versions of GitLab that you must upgrade to before upgrading to later versions. Required upgrade stops allow required background
|
||||||
|
migrations to finish.
|
||||||
|
|
||||||
|
During GitLab 16.x, we are scheduling two or three required upgrade stops. We will give at least two milestones of notice when we
|
||||||
|
schedule a required upgrade stop.
|
||||||
|
|
||||||
|
The first planned required upgrade stop is scheduled for GitLab 16.3. If nothing is introduced requiring an upgrade stop, GitLab 16.3 will be treated as a
|
||||||
|
regular upgrade.
|
||||||
|
|
||||||
Find where your version sits in the upgrade path below, and upgrade GitLab
|
Find where your version sits in the upgrade path below, and upgrade GitLab
|
||||||
accordingly, while also consulting the
|
accordingly, while also consulting the
|
||||||
[version-specific upgrade instructions](#version-specific-upgrading-instructions):
|
[version-specific upgrade instructions](#version-specific-upgrading-instructions):
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,33 @@ When this feature is enabled, GitLab runs a job once a day to deactivate the dor
|
||||||
|
|
||||||
A maximum of 100,000 users can be deactivated per day.
|
A maximum of 100,000 users can be deactivated per day.
|
||||||
|
|
||||||
|
### Automatically delete unconfirmed users **(PREMIUM SELF)**
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `delete_unconfirmed_users_setting`. Disabled by default.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
|
||||||
|
- You must be an administrator.
|
||||||
|
|
||||||
|
You can enable automatic deletion of users who both:
|
||||||
|
|
||||||
|
- Never confirmed their email address.
|
||||||
|
- Signed up for GitLab more than a specified number of days in the past.
|
||||||
|
|
||||||
|
You can configure these settings using either the [Settings API](../../api/settings.md) or in a Rails console:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Gitlab::CurrentSettings.update(delete_unconfirmed_users: true)
|
||||||
|
Gitlab::CurrentSettings.update(unconfirmed_users_delete_after_days: 365)
|
||||||
|
```
|
||||||
|
|
||||||
|
When the `delete_unconfirmed_users` setting is enabled, GitLab runs a job once an hour to delete the unconfirmed users.
|
||||||
|
The job only deletes users who signed up more than `unconfirmed_users_delete_after_days` days in the past.
|
||||||
|
|
||||||
|
This job only runs when the `email_confirmation_setting` is set to `soft` or `hard`.
|
||||||
|
|
||||||
|
A maximum of 240,000 users can be deleted per day.
|
||||||
|
|
||||||
### Activate a user
|
### Activate a user
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
|
|
||||||
You can create a compliance framework that is a label to identify that your project has certain compliance
|
You can create a compliance framework that is a label to identify that your project has certain compliance
|
||||||
requirements or needs additional oversight. The label can optionally enforce
|
requirements or needs additional oversight. The label can optionally enforce
|
||||||
[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is
|
[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is applied.
|
||||||
applied. Refer to our
|
|
||||||
applied. For more information, see [Add a compliance framework to a project](../project/settings/index.md#add-a-compliance-framework-to-a-project).
|
|
||||||
|
|
||||||
Compliance frameworks are created on top-level groups. Group owners can create, edit, and delete compliance frameworks:
|
Compliance frameworks are created on top-level groups. Group owners can create, edit, and delete compliance frameworks:
|
||||||
|
|
||||||
|
|
@ -25,6 +23,33 @@ Compliance frameworks are created on top-level groups. Group owners can create,
|
||||||
Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
|
Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
|
||||||
or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
|
or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
|
||||||
|
|
||||||
|
## Add a compliance framework to a project
|
||||||
|
|
||||||
|
Prerequisite:
|
||||||
|
|
||||||
|
- The group to which the project belongs must have a compliance framework.
|
||||||
|
|
||||||
|
To assign a compliance framework to a project:
|
||||||
|
|
||||||
|
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
|
||||||
|
1. Select **Settings** > **General**.
|
||||||
|
1. Expand **Compliance frameworks**.
|
||||||
|
1. Select a compliance framework.
|
||||||
|
1. Select **Save changes**.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
Frameworks cannot be added to projects in personal namespaces.
|
||||||
|
|
||||||
|
### GraphQL API
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) in GitLab 14.2.
|
||||||
|
|
||||||
|
You can use the [GraphQL API](../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework) to add a
|
||||||
|
compliance framework to a project.
|
||||||
|
|
||||||
|
If you create compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user
|
||||||
|
has the correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
|
||||||
|
|
||||||
## Default compliance frameworks
|
## Default compliance frameworks
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
|
||||||
|
|
|
||||||
|
|
@ -45,22 +45,9 @@ If you're an instance administrator, you can administer all project topics from
|
||||||
|
|
||||||
## Add a compliance framework to a project **(PREMIUM)**
|
## Add a compliance framework to a project **(PREMIUM)**
|
||||||
|
|
||||||
[Compliance frameworks](../../group/compliance_frameworks.md) can be assigned to projects within group that has a
|
You can
|
||||||
compliance framework using either:
|
[add compliance frameworks to projects](../../group/compliance_frameworks.md#add-a-compliance-framework-to-a-project)
|
||||||
|
in a group that has a compliance framework.
|
||||||
- The GitLab UI:
|
|
||||||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
|
|
||||||
1. Select **Settings** > **General**.
|
|
||||||
1. Expand **Compliance frameworks**.
|
|
||||||
1. Select a compliance framework.
|
|
||||||
1. Select **Save changes**.
|
|
||||||
- In [GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) and later, using the
|
|
||||||
[GraphQL API](../../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework). If you create
|
|
||||||
compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user has the
|
|
||||||
correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
|
|
||||||
|
|
||||||
NOTE:
|
|
||||||
Frameworks can not be added to projects in personal namespaces.
|
|
||||||
|
|
||||||
## Configure project visibility, features, and permissions
|
## Configure project visibility, features, and permissions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6540,6 +6540,9 @@ msgstr ""
|
||||||
msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
|
msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AuditStreams|Are you sure about deleting this destination?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AuditStreams|Cancel editing"
|
msgid "AuditStreams|Cancel editing"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -6549,6 +6552,12 @@ msgstr ""
|
||||||
msgid "AuditStreams|Delete %{link}"
|
msgid "AuditStreams|Delete %{link}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AuditStreams|Delete destination"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AuditStreams|Deleting the streaming destination %{destination} will stop audit events being streamed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AuditStreams|Destination URL"
|
msgid "AuditStreams|Destination URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_repository
|
def init_repository
|
||||||
run_git("git init")
|
run_git("git init --initial-branch=#{default_branch}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def pull(repository = nil, branch = nil)
|
def pull(repository = nil, branch = nil)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
view 'app/views/projects/branches/_branch.html.haml' do
|
view 'app/views/projects/branches/_branch.html.haml' do
|
||||||
element :badge_content
|
|
||||||
element :branch_container
|
element :branch_container
|
||||||
element :branch_link
|
element :branch_link
|
||||||
end
|
end
|
||||||
|
|
@ -23,13 +22,6 @@ module QA
|
||||||
element :all_branches_container
|
element :all_branches_container
|
||||||
end
|
end
|
||||||
|
|
||||||
view 'app/assets/javascripts/branches/components/delete_merged_branches.vue' do
|
|
||||||
element :delete_merged_branches_dropdown_button
|
|
||||||
element :delete_merged_branches_button
|
|
||||||
element :delete_merged_branches_input
|
|
||||||
element :delete_merged_branches_confirmation_button
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_branch(branch_name)
|
def delete_branch(branch_name)
|
||||||
within_element(:branch_container, name: branch_name) do
|
within_element(:branch_container, name: branch_name) do
|
||||||
click_element(:delete_branch_button)
|
click_element(:delete_branch_button)
|
||||||
|
|
@ -47,20 +39,6 @@ module QA
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_branch_with_badge?(branch_name, badge)
|
|
||||||
within_element(:branch_container, name: branch_name) do
|
|
||||||
has_element?(:badge_content, text: badge)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_merged_branches(branches_length)
|
|
||||||
click_element(:delete_merged_branches_dropdown_button)
|
|
||||||
click_element(:delete_merged_branches_button)
|
|
||||||
fill_element(:delete_merged_branches_input, branches_length)
|
|
||||||
click_element(:delete_merged_branches_confirmation_button)
|
|
||||||
finished_loading?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Resource
|
||||||
|
module Repository
|
||||||
|
class Branch < Base
|
||||||
|
attr_accessor :name, :ref
|
||||||
|
|
||||||
|
attribute :project do
|
||||||
|
Project.fabricate_via_api! do |resource|
|
||||||
|
resource.name = 'branch-project'
|
||||||
|
resource.initialize_with_readme = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@name = 'test'
|
||||||
|
@ref = Runtime::Env.default_branch
|
||||||
|
end
|
||||||
|
|
||||||
|
def fabricate!
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def fabricate_via_api!
|
||||||
|
resource_web_url(api_get)
|
||||||
|
rescue ResourceNotFoundError
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_get_path
|
||||||
|
"/projects/#{project.id}/repository/branches/#{name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_delete_path
|
||||||
|
api_get_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_post_path
|
||||||
|
"/projects/#{project.id}/repository/branches"
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_post_body
|
||||||
|
{
|
||||||
|
branch: name,
|
||||||
|
ref: ref
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
RSpec.describe 'Create' do
|
||||||
|
let(:project) do
|
||||||
|
Resource::Project.fabricate_via_api! do |project|
|
||||||
|
project.name = 'project-qa-test'
|
||||||
|
project.description = 'project for qa test'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Create, Retrieve and Delete branches via API', :requires_admin, product_group: :source_code do
|
||||||
|
created_branch = 'create-branch'
|
||||||
|
deleted_branch = 'delete-branch'
|
||||||
|
filename = 'file.txt'
|
||||||
|
default_branch_commit_message = "Add #{filename}"
|
||||||
|
|
||||||
|
before do
|
||||||
|
Git::Repository.perform do |repository|
|
||||||
|
repository.uri = project.repository_http_location.uri
|
||||||
|
repository.use_default_credentials
|
||||||
|
repository.try_add_credentials_to_netrc
|
||||||
|
|
||||||
|
repository.act do
|
||||||
|
init_repository
|
||||||
|
configure_identity('GitLab QA', 'root@gitlab.com')
|
||||||
|
|
||||||
|
commit_file(filename, 'Test file content', default_branch_commit_message)
|
||||||
|
push_changes
|
||||||
|
|
||||||
|
checkout(deleted_branch, new_branch: true)
|
||||||
|
push_changes(deleted_branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
project.wait_for_push default_branch_commit_message
|
||||||
|
end
|
||||||
|
|
||||||
|
it(
|
||||||
|
'creates, retrieves and deletes branches',
|
||||||
|
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740'
|
||||||
|
) do
|
||||||
|
# Create branch
|
||||||
|
Resource::Repository::Branch.fabricate_via_api! do |branch|
|
||||||
|
branch.name = created_branch
|
||||||
|
branch.project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve branch
|
||||||
|
delete_branch = Resource::Repository::Branch.fabricate_via_api! do |branch|
|
||||||
|
branch.name = deleted_branch
|
||||||
|
branch.project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete branch
|
||||||
|
delete_branch.remove_via_api!
|
||||||
|
|
||||||
|
# Clone repository, verify branches and commits
|
||||||
|
Git::Repository.perform do |repository|
|
||||||
|
repository.uri = project.repository_http_location.uri
|
||||||
|
repository.use_default_credentials
|
||||||
|
repository.try_add_credentials_to_netrc
|
||||||
|
|
||||||
|
repository.clone
|
||||||
|
|
||||||
|
branches = repository.remote_branches
|
||||||
|
expect(branches).to include(created_branch)
|
||||||
|
expect(branches).not_to include(deleted_branch)
|
||||||
|
|
||||||
|
expect(repository.commits.first).to include(default_branch_commit_message)
|
||||||
|
|
||||||
|
repository.checkout(created_branch)
|
||||||
|
expect(repository.commits.first).to include(default_branch_commit_message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module QA
|
|
||||||
RSpec.describe 'Create' do
|
|
||||||
describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
|
|
||||||
master_branch = nil
|
|
||||||
second_branch = 'second-branch'
|
|
||||||
third_branch = 'third-branch'
|
|
||||||
file_1_master = 'file.txt'
|
|
||||||
file_2_master = 'other-file.txt'
|
|
||||||
file_second_branch = 'file-2.txt'
|
|
||||||
file_third_branch = 'file-3.txt'
|
|
||||||
first_commit_message_of_master_branch = "Add #{file_1_master}"
|
|
||||||
second_commit_message_of_master_branch = "Add #{file_2_master}"
|
|
||||||
commit_message_of_second_branch = "Add #{file_second_branch}"
|
|
||||||
commit_message_of_third_branch = "Add #{file_third_branch}"
|
|
||||||
|
|
||||||
before do
|
|
||||||
Flow::Login.sign_in
|
|
||||||
|
|
||||||
project = Resource::Project.fabricate_via_api! do |proj|
|
|
||||||
proj.name = 'project-qa-test'
|
|
||||||
proj.description = 'project for qa test'
|
|
||||||
proj.initialize_with_readme = true
|
|
||||||
end
|
|
||||||
|
|
||||||
master_branch = project.default_branch
|
|
||||||
|
|
||||||
Git::Repository.perform do |repository|
|
|
||||||
repository.uri = project.repository_http_location.uri
|
|
||||||
repository.use_default_credentials
|
|
||||||
repository.try_add_credentials_to_netrc
|
|
||||||
repository.default_branch = master_branch
|
|
||||||
|
|
||||||
repository.act do
|
|
||||||
clone
|
|
||||||
configure_identity('GitLab QA', 'root@gitlab.com')
|
|
||||||
commit_file(file_1_master, 'Test file content', first_commit_message_of_master_branch)
|
|
||||||
push_changes
|
|
||||||
checkout(second_branch, new_branch: true)
|
|
||||||
commit_file(file_second_branch, 'File 2 content', commit_message_of_second_branch)
|
|
||||||
push_changes(second_branch)
|
|
||||||
checkout(master_branch)
|
|
||||||
# This second commit on master is needed for the master branch to be ahead
|
|
||||||
# of the second branch, and when the second branch is merged to master it will
|
|
||||||
# show the 'merged' badge on it.
|
|
||||||
# Refer to the below issue note:
|
|
||||||
# https://gitlab.com/gitlab-org/gitlab-foss/issues/55524#note_126100848
|
|
||||||
commit_file(file_2_master, 'Other test file content', second_commit_message_of_master_branch)
|
|
||||||
push_changes
|
|
||||||
merge(second_branch)
|
|
||||||
push_changes
|
|
||||||
checkout(third_branch, new_branch: true)
|
|
||||||
commit_file(file_third_branch, 'File 3 content', commit_message_of_third_branch)
|
|
||||||
push_changes(third_branch)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
project.wait_for_push commit_message_of_third_branch
|
|
||||||
project.visit!
|
|
||||||
end
|
|
||||||
|
|
||||||
it(
|
|
||||||
'lists branches correctly after CRUD operations',
|
|
||||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740',
|
|
||||||
quarantine: {
|
|
||||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/414026',
|
|
||||||
type: :stale
|
|
||||||
}
|
|
||||||
) do
|
|
||||||
Page::Project::Menu.perform(&:go_to_repository_branches)
|
|
||||||
|
|
||||||
expect(page).to have_content(master_branch)
|
|
||||||
expect(page).to have_content(second_branch)
|
|
||||||
expect(page).to have_content(third_branch)
|
|
||||||
expect(page).to have_content("Merge branch 'second-branch'")
|
|
||||||
expect(page).to have_content(commit_message_of_second_branch)
|
|
||||||
expect(page).to have_content(commit_message_of_third_branch)
|
|
||||||
|
|
||||||
Page::Project::Branches::Show.perform do |branches_page|
|
|
||||||
expect(branches_page).to have_branch_with_badge(second_branch, 'merged')
|
|
||||||
|
|
||||||
branches_page.delete_branch(third_branch)
|
|
||||||
|
|
||||||
expect(branches_page).to have_no_branch(third_branch)
|
|
||||||
|
|
||||||
branches_page.delete_merged_branches('delete')
|
|
||||||
|
|
||||||
expect(branches_page).to have_content(
|
|
||||||
'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
|
|
||||||
)
|
|
||||||
|
|
||||||
branches_page.refresh
|
|
||||||
|
|
||||||
expect(branches_page).to have_no_branch(second_branch, reload: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -139,8 +139,10 @@ RSpec.describe 'Admin Mode Login', feature_category: :system_access do
|
||||||
context 'when authn_context is worth two factors' do
|
context 'when authn_context is worth two factors' do
|
||||||
let(:mock_saml_response) do
|
let(:mock_saml_response) do
|
||||||
File.read('spec/fixtures/authentication/saml_response.xml')
|
File.read('spec/fixtures/authentication/saml_response.xml')
|
||||||
.gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
|
.gsub(
|
||||||
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
|
'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
|
||||||
|
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'signs user in without prompting for second factor' do
|
it 'signs user in without prompting for second factor' do
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js,
|
RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js,
|
||||||
feature_category: :integrations do
|
feature_category: :integrations do
|
||||||
include_context 'instance integration activation'
|
include_context 'instance integration activation'
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
|
||||||
before do
|
before do
|
||||||
personal_access_token = create(:personal_access_token, user: user)
|
personal_access_token = create(:personal_access_token, user: user)
|
||||||
|
|
||||||
visit project_issues_path(project, :atom,
|
visit project_issues_path(
|
||||||
private_token: personal_access_token.token)
|
project,
|
||||||
|
:atom,
|
||||||
|
private_token: personal_access_token.token
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'an authenticated issuable atom feed'
|
it_behaves_like 'an authenticated issuable atom feed'
|
||||||
|
|
@ -59,8 +62,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
|
||||||
|
|
||||||
context 'when authenticated via feed token' do
|
context 'when authenticated via feed token' do
|
||||||
before do
|
before do
|
||||||
visit project_issues_path(project, :atom,
|
visit project_issues_path(
|
||||||
feed_token: user.feed_token)
|
project,
|
||||||
|
:atom,
|
||||||
|
feed_token: user.feed_token
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'an authenticated issuable atom feed'
|
it_behaves_like 'an authenticated issuable atom feed'
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
|
||||||
before do
|
before do
|
||||||
personal_access_token = create(:personal_access_token, user: user)
|
personal_access_token = create(:personal_access_token, user: user)
|
||||||
|
|
||||||
visit project_merge_requests_path(project, :atom,
|
visit project_merge_requests_path(
|
||||||
private_token: personal_access_token.token)
|
project,
|
||||||
|
:atom,
|
||||||
|
private_token: personal_access_token.token
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'an authenticated issuable atom feed'
|
it_behaves_like 'an authenticated issuable atom feed'
|
||||||
|
|
@ -56,8 +59,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
|
||||||
|
|
||||||
context 'when authenticated via feed token' do
|
context 'when authenticated via feed token' do
|
||||||
before do
|
before do
|
||||||
visit project_merge_requests_path(project, :atom,
|
visit project_merge_requests_path(
|
||||||
feed_token: user.feed_token)
|
project,
|
||||||
|
:atom,
|
||||||
|
feed_token: user.feed_token
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'an authenticated issuable atom feed'
|
it_behaves_like 'an authenticated issuable atom feed'
|
||||||
|
|
|
||||||
|
|
@ -25,27 +25,33 @@ RSpec.describe "User Feed", feature_category: :devops_reports do
|
||||||
context 'feed content' do
|
context 'feed content' do
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository) }
|
||||||
let(:issue) do
|
let(:issue) do
|
||||||
create(:issue,
|
create(
|
||||||
project: project,
|
:issue,
|
||||||
author: user,
|
project: project,
|
||||||
description: "Houston, we have a bug!\n\n***\n\nI guess.")
|
author: user,
|
||||||
|
description: "Houston, we have a bug!\n\n***\n\nI guess."
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:note) do
|
let(:note) do
|
||||||
create(:note,
|
create(
|
||||||
noteable: issue,
|
:note,
|
||||||
author: user,
|
noteable: issue,
|
||||||
note: 'Bug confirmed :+1:',
|
author: user,
|
||||||
project: project)
|
note: 'Bug confirmed :+1:',
|
||||||
|
project: project
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:merge_request) do
|
let(:merge_request) do
|
||||||
create(:merge_request,
|
create(
|
||||||
title: 'Fix bug',
|
:merge_request,
|
||||||
author: user,
|
title: 'Fix bug',
|
||||||
source_project: project,
|
author: user,
|
||||||
target_project: project,
|
source_project: project,
|
||||||
description: "Here is the fix: ")
|
target_project: project,
|
||||||
|
description: "Here is the fix: "
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:push_event) { create(:push_event, project: project, author: user) }
|
let(:push_event) { create(:push_event, project: project, author: user) }
|
||||||
|
|
|
||||||
|
|
@ -220,12 +220,14 @@ RSpec.describe 'Issue Boards', :js, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
|
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
|
||||||
drag_to(selector: selector,
|
drag_to(
|
||||||
scrollable: '#board-app',
|
selector: selector,
|
||||||
list_from_index: list_from_index,
|
scrollable: '#board-app',
|
||||||
from_index: from_index,
|
list_from_index: list_from_index,
|
||||||
to_index: to_index,
|
from_index: from_index,
|
||||||
list_to_index: list_to_index,
|
to_index: to_index,
|
||||||
duration: duration)
|
list_to_index: list_to_index,
|
||||||
|
duration: duration
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,15 @@ RSpec.describe 'Multi Select Issue', :js, feature_category: :team_planning do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
|
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
|
||||||
drag_to(selector: selector,
|
drag_to(
|
||||||
scrollable: '#board-app',
|
selector: selector,
|
||||||
list_from_index: list_from_index,
|
scrollable: '#board-app',
|
||||||
from_index: from_index,
|
list_from_index: list_from_index,
|
||||||
to_index: to_index,
|
from_index: from_index,
|
||||||
list_to_index: list_to_index,
|
to_index: to_index,
|
||||||
duration: duration)
|
list_to_index: list_to_index,
|
||||||
|
duration: duration
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_for_board_cards(board_number, expected_cards)
|
def wait_for_board_cards(board_number, expected_cards)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
|
RSpec.describe 'Project issue boards sidebar assignee', :js,
|
||||||
feature_category: :team_planning do
|
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
|
||||||
|
feature_category: :team_planning do
|
||||||
include BoardHelpers
|
include BoardHelpers
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,15 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
|
||||||
|
|
||||||
let(:creator) { create(:user, developer_projects: [project]) }
|
let(:creator) { create(:user, developer_projects: [project]) }
|
||||||
let!(:pipeline) do
|
let!(:pipeline) do
|
||||||
create(:ci_pipeline,
|
create(
|
||||||
project: project,
|
:ci_pipeline,
|
||||||
user: creator,
|
project: project,
|
||||||
ref: project.default_branch,
|
user: creator,
|
||||||
sha: project.commit.sha,
|
ref: project.default_branch,
|
||||||
status: :success,
|
sha: project.commit.sha,
|
||||||
created_at: 5.months.ago)
|
status: :success,
|
||||||
|
created_at: 5.months.ago
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'commit status is Generic Commit Status' do
|
context 'commit status is Generic Commit Status' do
|
||||||
|
|
@ -61,11 +63,13 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
|
||||||
|
|
||||||
describe 'Project commits' do
|
describe 'Project commits' do
|
||||||
let!(:pipeline_from_other_branch) do
|
let!(:pipeline_from_other_branch) do
|
||||||
create(:ci_pipeline,
|
create(
|
||||||
project: project,
|
:ci_pipeline,
|
||||||
ref: 'fix',
|
project: project,
|
||||||
sha: project.commit.sha,
|
ref: 'fix',
|
||||||
status: :failed)
|
sha: project.commit.sha,
|
||||||
|
status: :failed
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,14 @@ RSpec.describe 'Dashboard > Activity', feature_category: :user_profile do
|
||||||
let!(:push_event) do
|
let!(:push_event) do
|
||||||
event = create(:push_event, project: project, author: user)
|
event = create(:push_event, project: project, author: user)
|
||||||
|
|
||||||
create(:push_event_payload,
|
create(
|
||||||
event: event,
|
:push_event_payload,
|
||||||
action: :created,
|
event: event,
|
||||||
commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
|
action: :created,
|
||||||
ref: 'new_design',
|
commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
|
||||||
commit_count: 1)
|
ref: 'new_design',
|
||||||
|
commit_count: 1
|
||||||
|
)
|
||||||
|
|
||||||
event
|
event
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,13 @@ RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :user_profil
|
||||||
|
|
||||||
context 'on the activity tab' do
|
context 'on the activity tab' do
|
||||||
before do
|
before do
|
||||||
Event.create!(project: project, author_id: user.id, action: :joined,
|
Event.create!(
|
||||||
updated_at: created_date, created_at: created_date)
|
project: project,
|
||||||
|
author_id: user.id,
|
||||||
|
action: :joined,
|
||||||
|
updated_at: created_date,
|
||||||
|
created_at: created_date
|
||||||
|
)
|
||||||
|
|
||||||
sign_in user
|
sign_in user
|
||||||
visit user_activity_path(user)
|
visit user_activity_path(user)
|
||||||
|
|
|
||||||
|
|
@ -79,39 +79,52 @@ RSpec.describe 'Dashboard Merge Requests', feature_category: :code_review_workfl
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:assigned_merge_request_from_fork) do
|
let!(:assigned_merge_request_from_fork) do
|
||||||
create(:merge_request,
|
create(
|
||||||
source_branch: 'markdown', assignees: [current_user],
|
:merge_request,
|
||||||
target_project: public_project, source_project: forked_project,
|
source_branch: 'markdown',
|
||||||
author: author_user)
|
assignees: [current_user],
|
||||||
|
target_project: public_project,
|
||||||
|
source_project: forked_project,
|
||||||
|
author: author_user
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:authored_merge_request) do
|
let!(:authored_merge_request) do
|
||||||
create(:merge_request,
|
create(
|
||||||
source_branch: 'markdown',
|
:merge_request,
|
||||||
source_project: project,
|
source_branch: 'markdown',
|
||||||
author: current_user)
|
source_project: project,
|
||||||
|
author: current_user
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:authored_merge_request_from_fork) do
|
let!(:authored_merge_request_from_fork) do
|
||||||
create(:merge_request,
|
create(
|
||||||
source_branch: 'feature_conflict',
|
:merge_request,
|
||||||
author: current_user,
|
source_branch: 'feature_conflict',
|
||||||
target_project: public_project, source_project: forked_project)
|
author: current_user,
|
||||||
|
target_project: public_project,
|
||||||
|
source_project: forked_project
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:labeled_merge_request) do
|
let!(:labeled_merge_request) do
|
||||||
create(:labeled_merge_request,
|
create(
|
||||||
source_branch: 'labeled',
|
:labeled_merge_request,
|
||||||
labels: [label],
|
source_branch: 'labeled',
|
||||||
author: current_user,
|
labels: [label],
|
||||||
source_project: project)
|
author: current_user,
|
||||||
|
source_project: project
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:other_merge_request) do
|
let!(:other_merge_request) do
|
||||||
create(:merge_request,
|
create(
|
||||||
source_branch: 'fix',
|
:merge_request,
|
||||||
source_project: project,
|
source_branch: 'fix',
|
||||||
author: author_user)
|
source_project: project,
|
||||||
|
author: author_user
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,9 @@ RSpec.describe 'Dashboard > User sorts todos', feature_category: :team_planning
|
||||||
create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
|
create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
|
||||||
create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
|
create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
|
||||||
create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
|
create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
|
||||||
create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago,
|
create(
|
||||||
updated_at: 1.hour.ago)
|
:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago, updated_at: 1.hour.ago
|
||||||
|
)
|
||||||
|
|
||||||
merge_request_1.labels << label_1
|
merge_request_1.labels << label_1
|
||||||
issue_3.labels << label_1
|
issue_3.labels << label_1
|
||||||
|
|
|
||||||
|
|
@ -443,12 +443,15 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
||||||
let_it_be(:target) { create(:design, issue: issue, project: project) }
|
let_it_be(:target) { create(:design, issue: issue, project: project) }
|
||||||
let_it_be(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
|
let_it_be(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
|
||||||
let_it_be(:todo) do
|
let_it_be(:todo) do
|
||||||
create(:todo, :mentioned,
|
create(
|
||||||
user: user,
|
:todo,
|
||||||
project: project,
|
:mentioned,
|
||||||
target: target,
|
user: user,
|
||||||
author: author,
|
project: project,
|
||||||
note: note)
|
target: target,
|
||||||
|
author: author,
|
||||||
|
note: note
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
@ -467,10 +470,12 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
||||||
context 'User requested access' do
|
context 'User requested access' do
|
||||||
shared_examples 'has todo present with access request content' do
|
shared_examples 'has todo present with access request content' do
|
||||||
specify do
|
specify do
|
||||||
create(:todo, :member_access_requested,
|
create(
|
||||||
user: user,
|
:todo,
|
||||||
target: target,
|
:member_access_requested,
|
||||||
author: author
|
user: user,
|
||||||
|
target: target,
|
||||||
|
author: author
|
||||||
)
|
)
|
||||||
target.add_owner(user)
|
target.add_owner(user)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'File blame', :js, feature_category: :groups_and_projects do
|
RSpec.describe 'File blame', :js, feature_category: :source_code_management do
|
||||||
include TreeHelper
|
include TreeHelper
|
||||||
|
|
||||||
let_it_be(:project) { create(:project, :public, :repository) }
|
let_it_be(:project) { create(:project, :public, :repository) }
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'Environments page', :js, feature_category: :groups_and_projects do
|
RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery do
|
||||||
include Spec::Support::Helpers::ModalHelpers
|
include Spec::Support::Helpers::ModalHelpers
|
||||||
|
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'Pipelines', :js, feature_category: :groups_and_projects do
|
RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
|
||||||
include ListboxHelpers
|
include ListboxHelpers
|
||||||
include ProjectForksHelper
|
include ProjectForksHelper
|
||||||
include Spec::Support::Helpers::ModalHelpers
|
include Spec::Support::Helpers::ModalHelpers
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
||||||
<gl-base-dropdown-stub
|
<gl-base-dropdown-stub
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
class="gl-disclosure-dropdown gl-display-none gl-md-display-block!"
|
class="gl-disclosure-dropdown gl-display-none gl-md-display-block!"
|
||||||
data-qa-selector="delete_merged_branches_dropdown_button"
|
|
||||||
icon="ellipsis_v"
|
icon="ellipsis_v"
|
||||||
nocaret="true"
|
nocaret="true"
|
||||||
offset="[object Object]"
|
offset="[object Object]"
|
||||||
|
|
@ -34,7 +33,7 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
||||||
|
|
||||||
<b-button-stub
|
<b-button-stub
|
||||||
class="gl-display-block gl-md-display-none! gl-button btn-danger-secondary"
|
class="gl-display-block gl-md-display-none! gl-button btn-danger-secondary"
|
||||||
data-qa-selector="delete_merged_branches_button"
|
data-testid="delete-merged-branches-button"
|
||||||
size="md"
|
size="md"
|
||||||
tag="button"
|
tag="button"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -100,7 +99,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
||||||
aria-labelledby="input-label"
|
aria-labelledby="input-label"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="gl-form-input gl-mt-2 gl-form-input-sm"
|
class="gl-form-input gl-mt-2 gl-form-input-sm"
|
||||||
data-qa-selector="delete_merged_branches_input"
|
|
||||||
debounce="0"
|
debounce="0"
|
||||||
formatter="[Function]"
|
formatter="[Function]"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -146,7 +144,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
||||||
|
|
||||||
<b-button-stub
|
<b-button-stub
|
||||||
class="gl-button"
|
class="gl-button"
|
||||||
data-qa-selector="delete_merged_branches_confirmation_button"
|
|
||||||
data-testid="delete-merged-branches-confirmation-button"
|
data-testid="delete-merged-branches-confirmation-button"
|
||||||
disabled="true"
|
disabled="true"
|
||||||
size="md"
|
size="md"
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const createComponent = (mountFn = shallowMountExtended, stubs = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const findDeleteButton = () =>
|
const findDeleteButton = () =>
|
||||||
wrapper.findComponent('[data-qa-selector="delete_merged_branches_button"]');
|
wrapper.findComponent('[data-testid="delete-merged-branches-button"]');
|
||||||
const findModal = () => wrapper.findComponent(GlModal);
|
const findModal = () => wrapper.findComponent(GlModal);
|
||||||
const findConfirmationButton = () =>
|
const findConfirmationButton = () =>
|
||||||
wrapper.findByTestId('delete-merged-branches-confirmation-button');
|
wrapper.findByTestId('delete-merged-branches-confirmation-button');
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,14 @@ describe('DeleteIssueModal component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when "primary" event is emitted', () => {
|
describe('when "primary" event is emitted', () => {
|
||||||
let formSubmitSpy;
|
const submitMock = jest.fn();
|
||||||
|
// Mock the form submit method
|
||||||
|
Object.defineProperty(HTMLFormElement.prototype, 'submit', {
|
||||||
|
value: submitMock,
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = mountComponent();
|
wrapper = mountComponent();
|
||||||
formSubmitSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
|
|
||||||
findModal().vm.$emit('primary');
|
findModal().vm.$emit('primary');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -50,7 +53,7 @@ describe('DeleteIssueModal component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('submits the form', () => {
|
it('submits the form', () => {
|
||||||
expect(formSubmitSpy).toHaveBeenCalled();
|
expect(submitMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
GlDropdown,
|
GlDisclosureDropdown,
|
||||||
GlButton,
|
|
||||||
GlFormCheckbox,
|
GlFormCheckbox,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
GlModal,
|
GlModal,
|
||||||
|
|
@ -64,9 +63,10 @@ describe('Package Files', () => {
|
||||||
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
|
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
|
||||||
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
|
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
|
||||||
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
|
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
|
||||||
const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
|
const findFirstActionMenu = () =>
|
||||||
|
extendedWrapper(findFirstRow().findComponent(GlDisclosureDropdown));
|
||||||
const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
|
const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
|
||||||
const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
|
const findFirstToggleDetailsButton = () => findFirstRow().findByTestId('toggle-details-button');
|
||||||
const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
|
const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
|
||||||
const findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
|
const findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
|
||||||
const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
|
const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
|
||||||
|
|
@ -262,7 +262,7 @@ describe('Package Files', () => {
|
||||||
expect(findFirstActionMenu().exists()).toBe(true);
|
expect(findFirstActionMenu().exists()).toBe(true);
|
||||||
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
|
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
|
||||||
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
|
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
|
||||||
expect(findFirstActionMenu().props('text')).toMatchInterpolatedText('More actions');
|
expect(findFirstActionMenu().props('toggleText')).toMatchInterpolatedText('More actions');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('menu items', () => {
|
describe('menu items', () => {
|
||||||
|
|
@ -272,7 +272,7 @@ describe('Package Files', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows delete file confirmation modal', async () => {
|
it('shows delete file confirmation modal', async () => {
|
||||||
await findActionMenuDelete().trigger('click');
|
await findActionMenuDelete().vm.$emit('action');
|
||||||
|
|
||||||
expect(showMock).toHaveBeenCalledTimes(1);
|
expect(showMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue