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/milestones/milestone_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/oncall_schedule.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/banner_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_searches_sentry_errors_spec.rb'
|
||||
- 'spec/features/error_tracking/user_sees_error_details_spec.rb'
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export default {
|
|||
this.openModal();
|
||||
},
|
||||
extraAttrs: {
|
||||
'data-qa-selector': 'delete_merged_branches_button',
|
||||
'data-testid': 'delete-merged-branches-button',
|
||||
class: 'gl-text-red-500!',
|
||||
},
|
||||
},
|
||||
|
|
@ -102,12 +102,11 @@ export default {
|
|||
category="tertiary"
|
||||
no-caret
|
||||
placement="right"
|
||||
data-qa-selector="delete_merged_branches_dropdown_button"
|
||||
class="gl-display-none gl-md-display-block!"
|
||||
:items="dropdownItems"
|
||||
/>
|
||||
<gl-button
|
||||
data-qa-selector="delete_merged_branches_button"
|
||||
data-testid="delete-merged-branches-button"
|
||||
category="secondary"
|
||||
variant="danger"
|
||||
class="gl-display-block gl-md-display-none!"
|
||||
|
|
@ -153,7 +152,6 @@ export default {
|
|||
</gl-sprintf>
|
||||
<gl-form-input
|
||||
v-model="enteredText"
|
||||
data-qa-selector="delete_merged_branches_input"
|
||||
type="text"
|
||||
size="sm"
|
||||
class="gl-mt-2"
|
||||
|
|
@ -178,7 +176,6 @@ export default {
|
|||
ref="deleteMergedBrancesButton"
|
||||
:disabled="isDeleteButtonDisabled"
|
||||
variant="danger"
|
||||
data-qa-selector="delete_merged_branches_confirmation_button"
|
||||
data-testid="delete-merged-branches-confirmation-button"
|
||||
@click="submitForm"
|
||||
>{{ $options.i18n.deleteButtonText }}</gl-button
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import {
|
|||
GlAlert,
|
||||
GlLink,
|
||||
GlTable,
|
||||
GlDropdownItem,
|
||||
GlDropdown,
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownItem,
|
||||
GlButton,
|
||||
GlFormCheckbox,
|
||||
GlLoadingIcon,
|
||||
|
|
@ -51,8 +51,8 @@ export default {
|
|||
GlAlert,
|
||||
GlLink,
|
||||
GlTable,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownItem,
|
||||
GlFormCheckbox,
|
||||
GlButton,
|
||||
GlLoadingIcon,
|
||||
|
|
@ -426,6 +426,7 @@ export default {
|
|||
v-if="hasDetails(item)"
|
||||
:icon="detailsShowing ? 'chevron-up' : 'chevron-down'"
|
||||
:aria-label="detailsShowing ? __('Collapse') : __('Expand')"
|
||||
data-testid="toggle-details-button"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
@click="
|
||||
|
|
@ -453,18 +454,23 @@ export default {
|
|||
</template>
|
||||
|
||||
<template #cell(actions)="{ item }">
|
||||
<gl-dropdown
|
||||
<gl-disclosure-dropdown
|
||||
category="tertiary"
|
||||
icon="ellipsis_v"
|
||||
:text-sr-only="true"
|
||||
:text="$options.i18n.moreActionsText"
|
||||
placement="right"
|
||||
:toggle-text="$options.i18n.moreActionsText"
|
||||
text-sr-only
|
||||
no-caret
|
||||
right
|
||||
>
|
||||
<gl-dropdown-item data-testid="delete-file" @click="handleFileDelete([item])">
|
||||
{{ $options.i18n.deleteFile }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<gl-disclosure-dropdown-item
|
||||
data-testid="delete-file"
|
||||
@action="handleFileDelete([item])"
|
||||
>
|
||||
<template #list-item>
|
||||
{{ $options.i18n.deleteFile }}
|
||||
</template>
|
||||
</gl-disclosure-dropdown-item>
|
||||
</gl-disclosure-dropdown>
|
||||
</template>
|
||||
|
||||
<template #row-details="{ item }">
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
= branch.name
|
||||
= clipboard_button(text: branch.name, title: _("Copy branch name"))
|
||||
- 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)
|
||||
= 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
|
||||
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
.issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
|
||||
= 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) },
|
||||
{ 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)
|
||||
= 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_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);
|
||||
|
||||
|
|
|
|||
|
|
@ -183,8 +183,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
|
|||
1. Install and configure Tomcat 10:
|
||||
|
||||
```shell
|
||||
cd /tmp & wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz
|
||||
sudo tar xzvf apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
|
||||
wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz -P /tmp
|
||||
sudo tar xzvf /tmp/apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
|
||||
sudo chown -R tomcat:tomcat /opt/tomcat/
|
||||
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:
|
||||
|
||||
```shell
|
||||
cd / & git clone https://github.com/plantuml/plantuml-server.git
|
||||
cd /
|
||||
git clone https://github.com/plantuml/plantuml-server.git
|
||||
cd plantuml-server
|
||||
mvn package
|
||||
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
|
||||
the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`,
|
||||
`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.
|
||||
these 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,
|
||||
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`
|
||||
- `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,
|
||||
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_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`. |
|
||||
| `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`. |
|
||||
| `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. |
|
||||
|
|
@ -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. |
|
||||
| `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. |
|
||||
| `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_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. |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
# first migration file
|
||||
class RemovingForeignKeyMigrationClass < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_foreign_key :my_table, :projects
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_foreign_key :my_table, :projects
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
add_foreign_key :my_table, :projects
|
||||
def down
|
||||
add_concurrent_foreign_key :my_table, :projects, column: COLUMN_NAME
|
||||
end
|
||||
end
|
||||
```
|
||||
|
|
@ -894,17 +895,19 @@ Dropping the table:
|
|||
|
||||
```ruby
|
||||
# second migration file
|
||||
class DroppingTableMigrationClass < Gitlab::Database::Migration[2.1]
|
||||
def up
|
||||
drop_table :my_table
|
||||
end
|
||||
|
||||
def up
|
||||
drop_table :my_table
|
||||
end
|
||||
|
||||
def down
|
||||
# create_table ...
|
||||
def down
|
||||
# create_table with the same schema but without the removed foreign key ...
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -139,3 +139,165 @@ end
|
|||
|
||||
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.
|
||||
|
||||
## 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"
|
||||
|
||||
# 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
|
||||
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).
|
||||
|
||||
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
|
||||
accordingly, while also consulting the
|
||||
[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.
|
||||
|
||||
### 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
|
||||
|
||||
> [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
|
||||
requirements or needs additional oversight. The label can optionally enforce
|
||||
[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is
|
||||
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 pipeline configuration](#compliance-pipelines) to the projects on which it is applied.
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
> [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)**
|
||||
|
||||
[Compliance frameworks](../../group/compliance_frameworks.md) can be assigned to projects within group that has a
|
||||
compliance framework using either:
|
||||
|
||||
- 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.
|
||||
You can
|
||||
[add compliance frameworks to projects](../../group/compliance_frameworks.md#add-a-compliance-framework-to-a-project)
|
||||
in a group that has a compliance framework.
|
||||
|
||||
## 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."
|
||||
msgstr ""
|
||||
|
||||
msgid "AuditStreams|Are you sure about deleting this destination?"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuditStreams|Cancel editing"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6549,6 +6552,12 @@ msgstr ""
|
|||
msgid "AuditStreams|Delete %{link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuditStreams|Delete destination"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuditStreams|Deleting the streaming destination %{destination} will stop audit events being streamed"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuditStreams|Destination URL"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ module QA
|
|||
end
|
||||
|
||||
def init_repository
|
||||
run_git("git init")
|
||||
run_git("git init --initial-branch=#{default_branch}")
|
||||
end
|
||||
|
||||
def pull(repository = nil, branch = nil)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ module QA
|
|||
end
|
||||
|
||||
view 'app/views/projects/branches/_branch.html.haml' do
|
||||
element :badge_content
|
||||
element :branch_container
|
||||
element :branch_link
|
||||
end
|
||||
|
|
@ -23,13 +22,6 @@ module QA
|
|||
element :all_branches_container
|
||||
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)
|
||||
within_element(:branch_container, name: branch_name) do
|
||||
click_element(:delete_branch_button)
|
||||
|
|
@ -47,20 +39,6 @@ module QA
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
let(:mock_saml_response) do
|
||||
File.read('spec/fixtures/authentication/saml_response.xml')
|
||||
.gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
|
||||
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
|
||||
.gsub(
|
||||
'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
|
||||
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS'
|
||||
)
|
||||
end
|
||||
|
||||
it 'signs user in without prompting for second factor' do
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
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'
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -49,8 +49,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
|
|||
before do
|
||||
personal_access_token = create(:personal_access_token, user: user)
|
||||
|
||||
visit project_issues_path(project, :atom,
|
||||
private_token: personal_access_token.token)
|
||||
visit project_issues_path(
|
||||
project,
|
||||
:atom,
|
||||
private_token: personal_access_token.token
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
before do
|
||||
visit project_issues_path(project, :atom,
|
||||
feed_token: user.feed_token)
|
||||
visit project_issues_path(
|
||||
project,
|
||||
:atom,
|
||||
feed_token: user.feed_token
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'an authenticated issuable atom feed'
|
||||
|
|
|
|||
|
|
@ -46,8 +46,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
|
|||
before do
|
||||
personal_access_token = create(:personal_access_token, user: user)
|
||||
|
||||
visit project_merge_requests_path(project, :atom,
|
||||
private_token: personal_access_token.token)
|
||||
visit project_merge_requests_path(
|
||||
project,
|
||||
:atom,
|
||||
private_token: personal_access_token.token
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
before do
|
||||
visit project_merge_requests_path(project, :atom,
|
||||
feed_token: user.feed_token)
|
||||
visit project_merge_requests_path(
|
||||
project,
|
||||
:atom,
|
||||
feed_token: user.feed_token
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:issue) do
|
||||
create(:issue,
|
||||
project: project,
|
||||
author: user,
|
||||
description: "Houston, we have a bug!\n\n***\n\nI guess.")
|
||||
create(
|
||||
:issue,
|
||||
project: project,
|
||||
author: user,
|
||||
description: "Houston, we have a bug!\n\n***\n\nI guess."
|
||||
)
|
||||
end
|
||||
|
||||
let(:note) do
|
||||
create(:note,
|
||||
noteable: issue,
|
||||
author: user,
|
||||
note: 'Bug confirmed :+1:',
|
||||
project: project)
|
||||
create(
|
||||
:note,
|
||||
noteable: issue,
|
||||
author: user,
|
||||
note: 'Bug confirmed :+1:',
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
let(:merge_request) do
|
||||
create(:merge_request,
|
||||
title: 'Fix bug',
|
||||
author: user,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
description: "Here is the fix: ")
|
||||
create(
|
||||
:merge_request,
|
||||
title: 'Fix bug',
|
||||
author: user,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
description: "Here is the fix: "
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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,
|
||||
scrollable: '#board-app',
|
||||
list_from_index: list_from_index,
|
||||
from_index: from_index,
|
||||
to_index: to_index,
|
||||
list_to_index: list_to_index,
|
||||
duration: duration)
|
||||
drag_to(
|
||||
selector: selector,
|
||||
scrollable: '#board-app',
|
||||
list_from_index: list_from_index,
|
||||
from_index: from_index,
|
||||
to_index: to_index,
|
||||
list_to_index: list_to_index,
|
||||
duration: duration
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@ RSpec.describe 'Multi Select Issue', :js, feature_category: :team_planning do
|
|||
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)
|
||||
drag_to(selector: selector,
|
||||
scrollable: '#board-app',
|
||||
list_from_index: list_from_index,
|
||||
from_index: from_index,
|
||||
to_index: to_index,
|
||||
list_to_index: list_to_index,
|
||||
duration: duration)
|
||||
drag_to(
|
||||
selector: selector,
|
||||
scrollable: '#board-app',
|
||||
list_from_index: list_from_index,
|
||||
from_index: from_index,
|
||||
to_index: to_index,
|
||||
list_to_index: list_to_index,
|
||||
duration: duration
|
||||
)
|
||||
end
|
||||
|
||||
def wait_for_board_cards(board_number, expected_cards)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
|
||||
feature_category: :team_planning do
|
||||
RSpec.describe 'Project issue boards sidebar assignee', :js,
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
|
||||
feature_category: :team_planning do
|
||||
include BoardHelpers
|
||||
|
||||
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!(:pipeline) do
|
||||
create(:ci_pipeline,
|
||||
project: project,
|
||||
user: creator,
|
||||
ref: project.default_branch,
|
||||
sha: project.commit.sha,
|
||||
status: :success,
|
||||
created_at: 5.months.ago)
|
||||
create(
|
||||
:ci_pipeline,
|
||||
project: project,
|
||||
user: creator,
|
||||
ref: project.default_branch,
|
||||
sha: project.commit.sha,
|
||||
status: :success,
|
||||
created_at: 5.months.ago
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
let!(:pipeline_from_other_branch) do
|
||||
create(:ci_pipeline,
|
||||
project: project,
|
||||
ref: 'fix',
|
||||
sha: project.commit.sha,
|
||||
status: :failed)
|
||||
create(
|
||||
:ci_pipeline,
|
||||
project: project,
|
||||
ref: 'fix',
|
||||
sha: project.commit.sha,
|
||||
status: :failed
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -59,12 +59,14 @@ RSpec.describe 'Dashboard > Activity', feature_category: :user_profile do
|
|||
let!(:push_event) do
|
||||
event = create(:push_event, project: project, author: user)
|
||||
|
||||
create(:push_event_payload,
|
||||
event: event,
|
||||
action: :created,
|
||||
commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
|
||||
ref: 'new_design',
|
||||
commit_count: 1)
|
||||
create(
|
||||
:push_event_payload,
|
||||
event: event,
|
||||
action: :created,
|
||||
commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
|
||||
ref: 'new_design',
|
||||
commit_count: 1
|
||||
)
|
||||
|
||||
event
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,8 +18,13 @@ RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :user_profil
|
|||
|
||||
context 'on the activity tab' do
|
||||
before do
|
||||
Event.create!(project: project, author_id: user.id, action: :joined,
|
||||
updated_at: created_date, created_at: created_date)
|
||||
Event.create!(
|
||||
project: project,
|
||||
author_id: user.id,
|
||||
action: :joined,
|
||||
updated_at: created_date,
|
||||
created_at: created_date
|
||||
)
|
||||
|
||||
sign_in user
|
||||
visit user_activity_path(user)
|
||||
|
|
|
|||
|
|
@ -79,39 +79,52 @@ RSpec.describe 'Dashboard Merge Requests', feature_category: :code_review_workfl
|
|||
end
|
||||
|
||||
let!(:assigned_merge_request_from_fork) do
|
||||
create(:merge_request,
|
||||
source_branch: 'markdown', assignees: [current_user],
|
||||
target_project: public_project, source_project: forked_project,
|
||||
author: author_user)
|
||||
create(
|
||||
:merge_request,
|
||||
source_branch: 'markdown',
|
||||
assignees: [current_user],
|
||||
target_project: public_project,
|
||||
source_project: forked_project,
|
||||
author: author_user
|
||||
)
|
||||
end
|
||||
|
||||
let!(:authored_merge_request) do
|
||||
create(:merge_request,
|
||||
source_branch: 'markdown',
|
||||
source_project: project,
|
||||
author: current_user)
|
||||
create(
|
||||
:merge_request,
|
||||
source_branch: 'markdown',
|
||||
source_project: project,
|
||||
author: current_user
|
||||
)
|
||||
end
|
||||
|
||||
let!(:authored_merge_request_from_fork) do
|
||||
create(:merge_request,
|
||||
source_branch: 'feature_conflict',
|
||||
author: current_user,
|
||||
target_project: public_project, source_project: forked_project)
|
||||
create(
|
||||
:merge_request,
|
||||
source_branch: 'feature_conflict',
|
||||
author: current_user,
|
||||
target_project: public_project,
|
||||
source_project: forked_project
|
||||
)
|
||||
end
|
||||
|
||||
let!(:labeled_merge_request) do
|
||||
create(:labeled_merge_request,
|
||||
source_branch: 'labeled',
|
||||
labels: [label],
|
||||
author: current_user,
|
||||
source_project: project)
|
||||
create(
|
||||
:labeled_merge_request,
|
||||
source_branch: 'labeled',
|
||||
labels: [label],
|
||||
author: current_user,
|
||||
source_project: project
|
||||
)
|
||||
end
|
||||
|
||||
let!(:other_merge_request) do
|
||||
create(:merge_request,
|
||||
source_branch: 'fix',
|
||||
source_project: project,
|
||||
author: author_user)
|
||||
create(
|
||||
:merge_request,
|
||||
source_branch: 'fix',
|
||||
source_project: project,
|
||||
author: author_user
|
||||
)
|
||||
end
|
||||
|
||||
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_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: merge_request_1, created_at: 1.hour.ago,
|
||||
updated_at: 1.hour.ago)
|
||||
create(
|
||||
: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
|
||||
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(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
|
||||
let_it_be(:todo) do
|
||||
create(:todo, :mentioned,
|
||||
user: user,
|
||||
project: project,
|
||||
target: target,
|
||||
author: author,
|
||||
note: note)
|
||||
create(
|
||||
:todo,
|
||||
:mentioned,
|
||||
user: user,
|
||||
project: project,
|
||||
target: target,
|
||||
author: author,
|
||||
note: note
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -467,10 +470,12 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
|||
context 'User requested access' do
|
||||
shared_examples 'has todo present with access request content' do
|
||||
specify do
|
||||
create(:todo, :member_access_requested,
|
||||
user: user,
|
||||
target: target,
|
||||
author: author
|
||||
create(
|
||||
:todo,
|
||||
:member_access_requested,
|
||||
user: user,
|
||||
target: target,
|
||||
author: author
|
||||
)
|
||||
target.add_owner(user)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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
|
||||
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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
|
||||
|
||||
let(:project) { create(:project) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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 ProjectForksHelper
|
||||
include Spec::Support::Helpers::ModalHelpers
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
|||
<gl-base-dropdown-stub
|
||||
category="tertiary"
|
||||
class="gl-disclosure-dropdown gl-display-none gl-md-display-block!"
|
||||
data-qa-selector="delete_merged_branches_dropdown_button"
|
||||
icon="ellipsis_v"
|
||||
nocaret="true"
|
||||
offset="[object Object]"
|
||||
|
|
@ -34,7 +33,7 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
|||
|
||||
<b-button-stub
|
||||
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"
|
||||
tag="button"
|
||||
type="button"
|
||||
|
|
@ -100,7 +99,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
|||
aria-labelledby="input-label"
|
||||
autocomplete="off"
|
||||
class="gl-form-input gl-mt-2 gl-form-input-sm"
|
||||
data-qa-selector="delete_merged_branches_input"
|
||||
debounce="0"
|
||||
formatter="[Function]"
|
||||
type="text"
|
||||
|
|
@ -146,7 +144,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
|
|||
|
||||
<b-button-stub
|
||||
class="gl-button"
|
||||
data-qa-selector="delete_merged_branches_confirmation_button"
|
||||
data-testid="delete-merged-branches-confirmation-button"
|
||||
disabled="true"
|
||||
size="md"
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const createComponent = (mountFn = shallowMountExtended, stubs = {}) => {
|
|||
};
|
||||
|
||||
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 findConfirmationButton = () =>
|
||||
wrapper.findByTestId('delete-merged-branches-confirmation-button');
|
||||
|
|
|
|||
|
|
@ -37,11 +37,14 @@ describe('DeleteIssueModal component', () => {
|
|||
});
|
||||
|
||||
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(() => {
|
||||
wrapper = mountComponent();
|
||||
formSubmitSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
|
||||
findModal().vm.$emit('primary');
|
||||
});
|
||||
|
||||
|
|
@ -50,7 +53,7 @@ describe('DeleteIssueModal component', () => {
|
|||
});
|
||||
|
||||
it('submits the form', () => {
|
||||
expect(formSubmitSpy).toHaveBeenCalled();
|
||||
expect(submitMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
GlAlert,
|
||||
GlDropdown,
|
||||
GlButton,
|
||||
GlDisclosureDropdown,
|
||||
GlFormCheckbox,
|
||||
GlLoadingIcon,
|
||||
GlModal,
|
||||
|
|
@ -64,9 +63,10 @@ describe('Package Files', () => {
|
|||
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
|
||||
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
|
||||
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
|
||||
const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
|
||||
const findFirstActionMenu = () =>
|
||||
extendedWrapper(findFirstRow().findComponent(GlDisclosureDropdown));
|
||||
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 findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
|
||||
const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
|
||||
|
|
@ -262,7 +262,7 @@ describe('Package Files', () => {
|
|||
expect(findFirstActionMenu().exists()).toBe(true);
|
||||
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
|
||||
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
|
||||
expect(findFirstActionMenu().props('text')).toMatchInterpolatedText('More actions');
|
||||
expect(findFirstActionMenu().props('toggleText')).toMatchInterpolatedText('More actions');
|
||||
});
|
||||
|
||||
describe('menu items', () => {
|
||||
|
|
@ -272,7 +272,7 @@ describe('Package Files', () => {
|
|||
});
|
||||
|
||||
it('shows delete file confirmation modal', async () => {
|
||||
await findActionMenuDelete().trigger('click');
|
||||
await findActionMenuDelete().vm.$emit('action');
|
||||
|
||||
expect(showMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue