Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b57bbf5a40
commit
dce429bdac
|
|
@ -461,6 +461,12 @@ Gitlab/EventStoreSubscriber:
|
|||
- 'spec/**/*'
|
||||
- 'ee/spec/**/*'
|
||||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/496562
|
||||
Gitlab/Ai/OrderConstants:
|
||||
Enabled: true
|
||||
Include:
|
||||
- 'ee/lib/ai/context/dependencies/config_files/constants.rb'
|
||||
|
||||
Gitlab/DocumentationLinks/HardcodedUrl:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
|
|
|
|||
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -2,6 +2,17 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 17.10.2 (2025-04-02)
|
||||
|
||||
### Fixed (2 changes)
|
||||
|
||||
- [Fix free push limit on non-saas](https://gitlab.com/gitlab-org/gitlab/-/commit/41d60e463f147b2cc76889e0f97a3192a9654ec9) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186166))
|
||||
- [Ensure runner taggings are copied from taggings](https://gitlab.com/gitlab-org/gitlab/-/commit/225b22847600e53bcf83d26b85e0a5e80b38c470) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186279))
|
||||
|
||||
### Other (1 change)
|
||||
|
||||
- [No-op ci_runner_machines_687967fa8a table backfill migration](https://gitlab.com/gitlab-org/gitlab/-/commit/5e9c7c787a1fb500707fbbceea2dccd1fd86ab92) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/185304))
|
||||
|
||||
## 17.10.1 (2025-03-26)
|
||||
|
||||
### Security (7 changes)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.0.29
|
||||
0.0.30
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
f204b12d2babba4d5d4bbe63fc5d1a2cf1e4c276
|
||||
6a91a094bd42c5877c1b9cf782ce49d55186ad0d
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
4c89bc3a1957b7f6766a5dab1446c45721d96753
|
||||
71a2331de156ab113749abc7cb82f6439b0436f9
|
||||
|
|
|
|||
|
|
@ -117,16 +117,13 @@ export default {
|
|||
const packageTypeOptions = [
|
||||
{ value: 'NPM', text: s__('PackageRegistry|Npm') },
|
||||
{ value: 'PYPI', text: s__('PackageRegistry|PyPI') },
|
||||
{ value: 'MAVEN', text: s__('PackageRegistry|Maven') },
|
||||
];
|
||||
|
||||
if (this.glFeatures.packagesProtectedPackagesConan) {
|
||||
packageTypeOptions.push({ value: 'CONAN', text: s__('PackageRegistry|Conan') });
|
||||
}
|
||||
|
||||
if (this.glFeatures.packagesProtectedPackagesMaven) {
|
||||
packageTypeOptions.push({ value: 'MAVEN', text: s__('PackageRegistry|Maven') });
|
||||
}
|
||||
|
||||
return packageTypeOptions.sort((a, b) => a.text.localeCompare(b.text));
|
||||
},
|
||||
minimumAccessLevelForPushOptions() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
GlSprintf,
|
||||
GlIcon,
|
||||
} from '@gitlab/ui';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { createAlert } from '~/alert';
|
||||
import { clearDraft } from '~/lib/utils/autosave';
|
||||
import { isMetaEnterKeyPair } from '~/lib/utils/common_utils';
|
||||
|
|
@ -56,6 +57,7 @@ import {
|
|||
WORK_ITEM_TYPE_VALUE_MAP,
|
||||
WORK_ITEM_TYPE_NAME_INCIDENT,
|
||||
WORK_ITEM_TYPE_NAME_EPIC,
|
||||
WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
} from '../constants';
|
||||
import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql';
|
||||
import namespaceWorkItemTypesQuery from '../graphql/namespace_work_item_types.query.graphql';
|
||||
|
|
@ -98,7 +100,10 @@ export default {
|
|||
import('ee_component/work_items/components/work_item_health_status.vue'),
|
||||
WorkItemColor: () => import('ee_component/work_items/components/work_item_color.vue'),
|
||||
WorkItemIteration: () => import('ee_component/work_items/components/work_item_iteration.vue'),
|
||||
WorkItemCustomFields: () =>
|
||||
import('ee_component/work_items/components/work_item_custom_fields.vue'),
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
inject: ['fullPath', 'groupPath'],
|
||||
i18n: {
|
||||
suggestionTitle: s__('WorkItem|Similar items'),
|
||||
|
|
@ -506,6 +511,12 @@ export default {
|
|||
shouldDatesRollup() {
|
||||
return this.selectedWorkItemTypeName === WORK_ITEM_TYPE_NAME_EPIC;
|
||||
},
|
||||
workItemCustomFields() {
|
||||
return findWidget(WIDGET_TYPE_CUSTOM_FIELDS, this.workItem)?.customFieldValues ?? null;
|
||||
},
|
||||
showWorkItemCustomFields() {
|
||||
return this.glFeatures.customFieldsFeature && this.workItemCustomFields;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
shouldDiscardDraft: {
|
||||
|
|
@ -984,6 +995,16 @@ export default {
|
|||
:can-update="canUpdate"
|
||||
@error="$emit('error', $event)"
|
||||
/>
|
||||
<work-item-custom-fields
|
||||
v-if="showWorkItemCustomFields"
|
||||
:work-item-id="workItemId"
|
||||
:work-item-type="selectedWorkItemTypeName"
|
||||
:custom-fields="workItemCustomFields"
|
||||
:full-path="fullPath"
|
||||
:is-group="isGroup"
|
||||
:can-update="canUpdate"
|
||||
@error="$emit('error', $event)"
|
||||
/>
|
||||
<work-item-parent
|
||||
v-if="showParentAttribute"
|
||||
class="work-item-attributes-item"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
NEW_WORK_ITEM_IID,
|
||||
WIDGET_TYPE_LINKED_ITEMS,
|
||||
STATE_CLOSED,
|
||||
WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
} from '../constants';
|
||||
import workItemByIidQuery from './work_item_by_iid.query.graphql';
|
||||
import workItemByIdQuery from './work_item_by_id.query.graphql';
|
||||
|
|
@ -323,6 +324,7 @@ export const setNewWorkItemCache = async (
|
|||
WIDGET_TYPE_HEALTH_STATUS,
|
||||
WIDGET_TYPE_LINKED_ITEMS,
|
||||
WIDGET_TYPE_COLOR,
|
||||
WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
WIDGET_TYPE_HIERARCHY,
|
||||
WIDGET_TYPE_TIME_TRACKING,
|
||||
WIDGET_TYPE_PARTICIPANTS,
|
||||
|
|
@ -516,6 +518,18 @@ export const setNewWorkItemCache = async (
|
|||
__typename: 'WorkItemWidgetTimeTracking',
|
||||
});
|
||||
}
|
||||
|
||||
if (widgetName === WIDGET_TYPE_CUSTOM_FIELDS) {
|
||||
const customFieldsWidgetData = widgetDefinitions.find(
|
||||
(definition) => definition.type === WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
);
|
||||
|
||||
widgets.push({
|
||||
type: WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
customFieldValues: customFieldsWidgetData?.customFieldValues ?? [],
|
||||
__typename: 'WorkItemWidgetCustomFields',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ import {
|
|||
NEW_WORK_ITEM_IID,
|
||||
WIDGET_TYPE_MILESTONE,
|
||||
WIDGET_TYPE_HIERARCHY,
|
||||
WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
CUSTOM_FIELDS_TYPE_SINGLE_SELECT,
|
||||
CUSTOM_FIELDS_TYPE_MULTI_SELECT,
|
||||
CUSTOM_FIELDS_TYPE_TEXT,
|
||||
} from '../constants';
|
||||
import workItemByIidQuery from './work_item_by_iid.query.graphql';
|
||||
|
||||
|
|
@ -50,6 +54,39 @@ const updateDatesWidget = (draftData, dates) => {
|
|||
});
|
||||
};
|
||||
|
||||
const updateCustomFieldsWidget = (sourceData, draftData, customField) => {
|
||||
if (!customField) return;
|
||||
|
||||
const widget = findWidget(WIDGET_TYPE_CUSTOM_FIELDS, draftData.workspace.workItem);
|
||||
|
||||
if (!widget) return;
|
||||
|
||||
const currentValues =
|
||||
sourceData?.workspace?.workItem?.widgets?.find((w) => w.type === WIDGET_TYPE_CUSTOM_FIELDS)
|
||||
?.customFieldValues ?? [];
|
||||
|
||||
const updatedCustomFieldValues = currentValues.map((field) => {
|
||||
if (field?.customField?.id === customField.id) {
|
||||
if (
|
||||
field.customField.fieldType === CUSTOM_FIELDS_TYPE_SINGLE_SELECT ||
|
||||
field.customField.fieldType === CUSTOM_FIELDS_TYPE_MULTI_SELECT
|
||||
) {
|
||||
return { ...field, selectedOptions: customField.selectedOptions };
|
||||
}
|
||||
if (field.customField.fieldType === CUSTOM_FIELDS_TYPE_TEXT) {
|
||||
return { ...field, value: customField.textValue };
|
||||
}
|
||||
return { ...field, value: customField.numberValue };
|
||||
}
|
||||
return field;
|
||||
});
|
||||
|
||||
Object.assign(widget, {
|
||||
customFieldValues: updatedCustomFieldValues,
|
||||
__typename: 'WorkItemWidgetCustomFields',
|
||||
});
|
||||
};
|
||||
|
||||
export const updateNewWorkItemCache = (input, cache) => {
|
||||
const {
|
||||
healthStatus,
|
||||
|
|
@ -67,6 +104,7 @@ export const updateNewWorkItemCache = (input, cache) => {
|
|||
weight,
|
||||
milestone,
|
||||
parent,
|
||||
customField,
|
||||
} = input;
|
||||
|
||||
try {
|
||||
|
|
@ -136,6 +174,7 @@ export const updateNewWorkItemCache = (input, cache) => {
|
|||
});
|
||||
|
||||
updateDatesWidget(draftData, rolledUpDates);
|
||||
updateCustomFieldsWidget(sourceData, draftData, customField);
|
||||
|
||||
// We want to allow users to delete a title for an in-progress work item draft
|
||||
// as we check for the title being valid when submitting the form
|
||||
|
|
|
|||
|
|
@ -82,6 +82,19 @@ type LocalWorkItemPayload {
|
|||
errors: [String!]
|
||||
}
|
||||
|
||||
input LocalCustomFieldSelectOptionInput {
|
||||
id: ID!
|
||||
value: String
|
||||
}
|
||||
|
||||
input LocalCustomFieldInput {
|
||||
id: ID!
|
||||
type: String!
|
||||
numberValue: Float
|
||||
textValue: String
|
||||
selectedOptions: [LocalCustomFieldSelectOptionInput]
|
||||
}
|
||||
|
||||
input LocalUpdateNewWorkItemInput {
|
||||
fullPath: String!
|
||||
workItemType: String!
|
||||
|
|
@ -97,6 +110,7 @@ input LocalUpdateNewWorkItemInput {
|
|||
rolledUpDates: [LocalRolledUpDatesInput]
|
||||
crmContacts: [LocalCrmContactsInput]
|
||||
weight: Int
|
||||
customField: LocalCustomFieldInput
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ module Projects
|
|||
|
||||
def set_feature_flag_packages_protected_packages
|
||||
push_frontend_feature_flag(:packages_protected_packages_conan, project)
|
||||
push_frontend_feature_flag(:packages_protected_packages_maven, project)
|
||||
push_frontend_feature_flag(:packages_protected_packages_delete, project)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ module Types
|
|||
|
||||
value 'MAVEN',
|
||||
value: 'maven',
|
||||
experiment: { milestone: '17.9' },
|
||||
description: 'Packages of the Maven format. ' \
|
||||
'Available only when feature flag `packages_protected_packages_maven` is enabled.'
|
||||
description: 'Packages of the Maven format.'
|
||||
|
||||
value 'NPM',
|
||||
value: 'npm',
|
||||
|
|
|
|||
|
|
@ -1637,7 +1637,7 @@ class MergeRequest < ApplicationRecord
|
|||
visible_notes = user.can?(:read_internal_note, project) ? notes : notes.not_internal
|
||||
|
||||
messages = [title, description, *visible_notes.pluck(:note)]
|
||||
messages += commits.map(&:safe_message) if merge_request_diff.persisted?
|
||||
messages += commits(load_from_gitaly: Feature.enabled?(:more_commits_from_gitaly, target_project)).map(&:safe_message) if merge_request_diff.persisted?
|
||||
|
||||
ext = Gitlab::ReferenceExtractor.new(project, user)
|
||||
ext.analyze(messages.join("\n"))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Packages
|
|||
module Maven
|
||||
class CreatePackageService < ::Packages::CreatePackageService
|
||||
def execute
|
||||
return ERROR_RESPONSE_PACKAGE_PROTECTED if package_protected?
|
||||
return ERROR_RESPONSE_PACKAGE_PROTECTED if package_protected?(package_name: params[:name], package_type: :maven)
|
||||
|
||||
app_group, _, app_name = params[:name].rpartition('/')
|
||||
app_group.tr!('/', '.')
|
||||
|
|
@ -24,14 +24,6 @@ module Packages
|
|||
|
||||
ServiceResponse.error(message: e.message, reason: reason)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def package_protected?
|
||||
return false if Feature.disabled?(:packages_protected_packages_maven, project)
|
||||
|
||||
super(package_name: params[:name], package_type: :maven)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
class FlushCounterIncrementsWorker
|
||||
include ApplicationWorker
|
||||
|
||||
data_consistency :always
|
||||
data_consistency :delayed, feature_flag: :load_balancing_for_flush_counter_increments_worker
|
||||
|
||||
sidekiq_options retry: 3
|
||||
loggable_arguments 0, 2
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module WebHooks
|
|||
class LogExecutionWorker
|
||||
include ApplicationWorker
|
||||
|
||||
data_consistency :always
|
||||
data_consistency :delayed, feature_flag: :load_balancing_for_web_hooks_log_execution_worker
|
||||
feature_category :webhooks
|
||||
urgency :low
|
||||
sidekiq_options retry: 3
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: packages_protected_packages_maven
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323969
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147055
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/497082
|
||||
milestone: '17.9'
|
||||
group: group::package registry
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: load_balancing_for_flush_counter_increments_worker
|
||||
feature_issue_url: https://gitlab.com/gitlab-com/gl-infra/data-access/durability/team/-/issues/121
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186079
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/527277
|
||||
milestone: '17.11'
|
||||
group: group::durability
|
||||
type: worker
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: load_balancing_for_web_hooks_log_execution_worker
|
||||
feature_issue_url: https://gitlab.com/gitlab-com/gl-infra/data-access/durability/team/-/issues/121
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186079
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/527276
|
||||
milestone: '17.11'
|
||||
group: group::durability
|
||||
type: worker
|
||||
default_enabled: false
|
||||
|
|
@ -64,9 +64,9 @@ The following table lists the GitLab Duo features, and whether they are availabl
|
|||
| [Merge Commit Message Generation](../../user/project/merge_requests/duo_in_merge_requests.md#generate-a-merge-commit-message) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.11 and later |
|
||||
| [Summarize New Merge Request](../../user/project/merge_requests/duo_in_merge_requests.md#generate-a-description-by-summarizing-code-changes) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.11 and later |
|
||||
| [Vulnerability Explanation](../../user/application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.11 and later |
|
||||
| [Vulnerability Resolution](../../user/application_security/vulnerabilities/_index.md#vulnerability-resolution) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.11 and later |
|
||||
| [Discussion Summary](../../user/discussions/_index.md#summarize-issue-discussions-with-duo-chat) | {{< icon name="dash-circle" >}} No | Not applicable |
|
||||
| [GitLab Duo for the CLI](../../editor_extensions/gitlab_cli/_index.md#gitlab-duo-for-the-cli) | {{< icon name="dash-circle" >}} No | Not applicable |
|
||||
| [Vulnerability Resolution](../../user/application_security/vulnerabilities/_index.md#vulnerability-resolution) | {{< icon name="dash-circle" >}} No | Not applicable |
|
||||
|
||||
#### Supported Duo Chat features
|
||||
|
||||
|
|
|
|||
|
|
@ -41446,6 +41446,7 @@ AI features that can be configured through the Duo self-hosted feature settings.
|
|||
| <a id="aifeaturesduo_chat_troubleshoot_job"></a>`DUO_CHAT_TROUBLESHOOT_JOB` | Duo chat troubleshoot job feature setting. |
|
||||
| <a id="aifeaturesduo_chat_write_tests"></a>`DUO_CHAT_WRITE_TESTS` | Duo chat write test feature setting. |
|
||||
| <a id="aifeaturesgenerate_commit_message"></a>`GENERATE_COMMIT_MESSAGE` | Generate commit message feature setting. |
|
||||
| <a id="aifeaturesresolve_vulnerability"></a>`RESOLVE_VULNERABILITY` | Resolve vulnerability feature setting. |
|
||||
| <a id="aifeaturessummarize_new_merge_request"></a>`SUMMARIZE_NEW_MERGE_REQUEST` | Summarize new merge request feature setting. |
|
||||
|
||||
### `AiMessageRole`
|
||||
|
|
@ -43938,7 +43939,7 @@ Package type of a package protection rule resource.
|
|||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="packagesprotectionrulepackagetypeconan"></a>`CONAN` {{< icon name="warning-solid" >}} | **Introduced** in GitLab 17.6. **Status**: Experiment. Packages of the Conan format. Available only when feature flag `packages_protected_packages_conan` is enabled. |
|
||||
| <a id="packagesprotectionrulepackagetypemaven"></a>`MAVEN` {{< icon name="warning-solid" >}} | **Introduced** in GitLab 17.9. **Status**: Experiment. Packages of the Maven format. Available only when feature flag `packages_protected_packages_maven` is enabled. |
|
||||
| <a id="packagesprotectionrulepackagetypemaven"></a>`MAVEN` | Packages of the Maven format. |
|
||||
| <a id="packagesprotectionrulepackagetypenpm"></a>`NPM` | Packages of the npm format. |
|
||||
| <a id="packagesprotectionrulepackagetypepypi"></a>`PYPI` | Packages of the PyPI format. |
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ Use this guide to understand how different kinds of secrets are stored and manag
|
|||
|
||||
Broadly speaking, there are two classes of secrets:
|
||||
|
||||
<!-- vale gitlab_base.SubstitutionWarning = NO -->
|
||||
|
||||
1. **Application secrets.** The GitLab application uses these to implement a particular feature or function.
|
||||
An example would be access tokens or private keys to create cryptographic signatures. We store
|
||||
these secrets in the database in encrypted columns.
|
||||
|
|
@ -20,13 +22,17 @@ Broadly speaking, there are two classes of secrets:
|
|||
1. **Operational secrets.** Used to read and store other secrets or bootstrap the application. For this reason,
|
||||
they cannot be stored in the database.
|
||||
These secrets are stored as [Rails credentials](https://guides.rubyonrails.org/security.html#environmental-security)
|
||||
in the `config/secrets.yml` file, directly for source installation, or through an installer like Omnibus or Helm (where
|
||||
actual secrets can be stored in an external secrets container like
|
||||
[Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) or [Vault](https://www.vaultproject.io/)).
|
||||
in the `config/secrets.yml` file:
|
||||
|
||||
- Directly for self-compiled installations.
|
||||
- Through an installer like Omnibus or Helm (where actual secrets can be stored in an external secrets container like
|
||||
[Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) or [Vault](https://www.vaultproject.io/)).
|
||||
|
||||
<!-- vale gitlab_base.SubstitutionWarning = YES -->
|
||||
|
||||
## Application secrets
|
||||
|
||||
Application secrets should be stored in postgres using `ActiveRecord::Encryption`:
|
||||
Application secrets should be stored in PostgreSQL using `ActiveRecord::Encryption`:
|
||||
|
||||
```ruby
|
||||
class MyModel < ApplicationRecord
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ To address the above two scenarios, it is advised to do the following prior to u
|
|||
1. Pause your runners, or block new jobs from starting by adding the following to your `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
nginx['custom_gitlab_server_config'] = "location ^~ /api/v4/jobs/request {\n deny all;\n return 503;\n}\n"
|
||||
nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n deny all;\n return 503;\n}\n"
|
||||
```
|
||||
|
||||
And reconfigure GitLab with:
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ ensure that your proxy server does not alter or remove signed HTTP headers.
|
|||
|
||||
## 17.7.0
|
||||
|
||||
- Git 2.47.0 and later is required by Gitaly. For installations from source, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- Git 2.47.0 and later is required by Gitaly. For self-compiled installations, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- FIPS Linux packages now use the system Libgcrypt, except FIPS Linux packages for AmazonLinux 2. Previous versions of the FIPS Linux packages used the
|
||||
same Libgcrypt used by the regular Linux packages, which was a bug. For more information, see
|
||||
[the FIPS documentation](../../development/fips_gitlab.md#system-libgcrypt).
|
||||
|
|
@ -360,7 +360,7 @@ The OpenSSL 3 upgrade has been postponed to GitLab 17.7.0.
|
|||
database migrations as your existing environments. This isn't necessary if you're restoring from backup into the
|
||||
new environment as the database restore removes the existing database schema definition and uses the definition
|
||||
that's stored as part of the backup.
|
||||
- Git 2.46.0 and later is required by Gitaly. For installations from source, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- Git 2.46.0 and later is required by Gitaly. For self-compiled installations, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- S3 object storage uploads in Workhorse are now handled by default using the [AWS SDK v2 for Go](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/164597). If you experience issues
|
||||
with S3 object storage uploads, you can downgrade to v1 of by disabling the `workhorse_use_aws_sdk_v2` [feature flag](../../administration/feature_flags.md#enable-or-disable-the-feature).
|
||||
- When you upgrade to GitLab 17.4, an OAuth application is generated for the Web IDE.
|
||||
|
|
@ -384,7 +384,7 @@ The OpenSSL 3 upgrade has been postponed to GitLab 17.7.0.
|
|||
|
||||
## 17.3.0
|
||||
|
||||
- Git 2.45.0 and later is required by Gitaly. For installations from source, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- Git 2.45.0 and later is required by Gitaly. For self-compiled installations, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
|
||||
### Geo installations 17.3.0
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ the top of the vulnerability's page.
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM: Anthropic [Claude 3 Haiku](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai)
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ The following data is shared with third-party AI APIs:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
- LLM for GitLab.com: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ such as:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
- LLM for GitLab.com: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ title: GitLab Duo with Amazon Q
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo with Amazon Q
|
||||
- Offering: GitLab Self-Managed
|
||||
- Status: Preview/Beta
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ title: Set up GitLab Duo with Amazon Q
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo with Amazon Q
|
||||
- Offering: GitLab Self-Managed
|
||||
- Status: Preview/Beta
|
||||
|
||||
|
|
|
|||
|
|
@ -116,22 +116,22 @@ To improve your security, try these features:
|
|||
|
||||
| Feature | Tier | Add-on | Offering | Status |
|
||||
| ------- | ---- | ------ | -------- | ------ |
|
||||
| [GitLab Duo Chat](../gitlab_duo_chat/_index.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [GitLab Duo Chat](../gitlab_duo_chat/_index.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md) | Ultimate | GitLab Duo Enterprise | GitLab Self-Managed | General availability |
|
||||
| [GitLab Duo Workflow](../duo_workflow/_index.md) | Ultimate | - | GitLab.com | Private beta |
|
||||
| [Issue Description Generation](../project/issues/managing_issues.md#populate-an-issue-with-issue-description-generation) | Ultimate | GitLab Duo Enterprise | GitLab.com | Experiment |
|
||||
| [Discussion Summary](../discussions/_index.md#summarize-issue-discussions-with-duo-chat) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Code Suggestions](../project/repository/code_suggestions/_index.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Code Explanation](../project/repository/code_explain.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Test Generation](../gitlab_duo_chat/examples.md#write-tests-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Refactor Code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Fix Code](../gitlab_duo_chat/examples.md#fix-code-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Discussion Summary](../discussions/_index.md#summarize-issue-discussions-with-duo-chat) | Ultimate | GitLab Duo Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Code Suggestions](../project/repository/code_suggestions/_index.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Code Explanation](../project/repository/code_explain.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Test Generation](../gitlab_duo_chat/examples.md#write-tests-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Refactor Code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Fix Code](../gitlab_duo_chat/examples.md#fix-code-in-the-ide) | Premium, Ultimate | GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [GitLab Duo for the CLI](../../editor_extensions/gitlab_cli/_index.md#gitlab-duo-for-the-cli) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Merge Request Summary](../project/merge_requests/duo_in_merge_requests.md#generate-a-description-by-summarizing-code-changes) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed | Beta |
|
||||
| [Code Review](../project/merge_requests/duo_in_merge_requests.md#have-gitlab-duo-review-your-code) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | Beta |
|
||||
| [Code Review Summary](../project/merge_requests/duo_in_merge_requests.md#summarize-a-code-review) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed | Experiment |
|
||||
| [Merge Commit Message Generation](../project/merge_requests/duo_in_merge_requests.md#generate-a-merge-commit-message) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Root Cause Analysis](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Explanation](../application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Resolution](../application_security/vulnerabilities/_index.md#vulnerability-resolution) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Root Cause Analysis](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | Ultimate | GitLab Duo Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Explanation](../application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | Ultimate | GitLab Duo Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Resolution](../application_security/vulnerabilities/_index.md#vulnerability-resolution) | Ultimate | GitLab Duo Enterprise, GitLab Duo with Amazon Q | GitLab.com, GitLab Self-Managed, GitLab Dedicated | General availability |
|
||||
| [AI Impact Dashboard](../analytics/ai_impact_analytics.md) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed | General availability |
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ title: GitLab Duo Chat
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLMs: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet), Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2), Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet), Anthropic [Claude 3.5 Haiku](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-haiku), and [Vertex AI Search](https://cloud.google.com/enterprise-search). The LLM depends on the question asked.
|
||||
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ You can ask about a specific GitLab pipeline job. For example:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: GitLab UI, Web IDE, VS Code, JetBrains IDEs
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2)
|
||||
|
|
@ -396,7 +396,7 @@ You cannot use [Quick Chat](_index.md#in-gitlab-duo-quick-chat-in-the-editor-vie
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: Web IDE, VS Code, JetBrains IDEs
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2)
|
||||
|
|
@ -432,7 +432,7 @@ You can include additional instructions to be considered. For example:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: Web IDE, VS Code, JetBrains IDEs
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2)
|
||||
|
|
@ -466,7 +466,7 @@ You can include additional instructions to be considered. For example:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: Web IDE, VS Code, JetBrains IDEs
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2)
|
||||
|
|
@ -542,7 +542,7 @@ Alternatively, you can use GitLab Duo Root Cause Analysis to [troubleshoot faile
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: GitLab UI
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet V2](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet-v2)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ title: Protected packages
|
|||
- The protection rule setting **Push protected up to access level** [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/416382) to **Minimum access level for push** in GitLab 17.1
|
||||
- [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/472655) in GitLab 17.5.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/472655) in GitLab 17.6. Feature flag `packages_protected_packages` removed.
|
||||
- Maven protected packages [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323969) in GitLab 17.9 [with a flag](../../../administration/feature_flags.md) named `packages_protected_packages_maven`. Disabled by default. This feature is an [experiment](../../../policy/development_stages_support.md).
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/497082) in GitLab 17.11. Feature flag `packages_protected_packages_maven` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ By default, any user with at least the Developer role can create,
|
|||
edit, and delete packages. Add a package protection rule to restrict
|
||||
which users can make changes to your packages.
|
||||
|
||||
GitLab supports only push protection for npm packages, but [epic 5574](https://gitlab.com/groups/gitlab-org/-/epics/5574) proposes to add additional features and package formats.
|
||||
GitLab supports only push protection for npm, pypi and maven packages, but [epic 5574](https://gitlab.com/groups/gitlab-org/-/epics/5574) proposes to add additional features and package formats.
|
||||
|
||||
When a package is protected, the default behavior enforces these restrictions on the package:
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ To change how a merge request shows changed lines:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
- LLM for GitLab.com: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ title: Explain code in a file
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise. GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM for GitLab Self-Managed, GitLab Dedicated: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
- LLM for GitLab.com: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ title: Code Suggestions
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLMs: For code completion, Fireworks AI-hosted [`Qwen2.5 7B`](https://fireworks.ai/models/fireworks/qwen2p5-coder-7b) and Vertex AI Codey [`code-gecko`](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/code-gecko). For code generation, Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet).
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ title: Supported extensions and languages
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
|
||||
{{< /details >}}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ title: Troubleshooting Code Suggestions
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Premium, Ultimate
|
||||
- Add-on: GitLab Duo Pro or Enterprise
|
||||
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
|
||||
{{< /details >}}
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ Class methods required:
|
|||
Instance methods required:
|
||||
|
||||
- `init`: reads from `serialized_args`
|
||||
- `as_indexed_json`: a hash containing the data representation of the object
|
||||
- `operation`: determines the operation which can be one of `index`, `upsert` or `delete`
|
||||
- `as_indexed_json` or `as_indexed_jsons`: a hash or array of hashes containing the data representation of the object
|
||||
- `operation`: determines the operation which can be one of `upsert` or `delete`
|
||||
- `identifier`: unique identifier
|
||||
|
||||
Example for a reference reading from a database relation, with preloading and bulk embedding generation:
|
||||
|
|
@ -194,7 +194,7 @@ module Ai
|
|||
blob.data ? :upsert : :delete
|
||||
end
|
||||
|
||||
def as_indexed_json
|
||||
def as_indexed_jsons
|
||||
{
|
||||
project_id: project_id,
|
||||
embeddings: embedding
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module ActiveContext
|
|||
'queue' => queue,
|
||||
'message' => 'bulk_indexing_start',
|
||||
'meta.indexing.redis_set' => set_key,
|
||||
'meta.indexing.records_count' => specs.count,
|
||||
'meta.indexing.refs_count' => specs.count,
|
||||
'meta.indexing.first_score' => first_score,
|
||||
'meta.indexing.last_score' => last_score
|
||||
)
|
||||
|
|
@ -73,7 +73,7 @@ module ActiveContext
|
|||
'class' => self.class.name,
|
||||
'message' => 'bulk_indexing_end',
|
||||
'meta.indexing.redis_set' => set_key,
|
||||
'meta.indexing.records_count' => count,
|
||||
'meta.indexing.refs_count' => count,
|
||||
'meta.indexing.first_score' => first_score,
|
||||
'meta.indexing.last_score' => last_score,
|
||||
'meta.indexing.failures_count' => @failures.count,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ module ActiveContext
|
|||
def create_partition(name, fields)
|
||||
body = {
|
||||
mappings: {
|
||||
properties: build_field_mappings(fields)
|
||||
dynamic: 'strict',
|
||||
properties: mappings(fields)
|
||||
},
|
||||
settings: settings(fields)
|
||||
}
|
||||
|
|
@ -51,6 +52,13 @@ module ActiveContext
|
|||
raw_client.indices.update_aliases(body: { actions: actions })
|
||||
end
|
||||
|
||||
def mappings(fields)
|
||||
build_field_mappings(fields).merge(
|
||||
ref_id: { type: 'keyword' },
|
||||
ref_version: { type: 'long' }
|
||||
)
|
||||
end
|
||||
|
||||
def build_field_mappings(fields)
|
||||
fields.each_with_object({}) do |field, mappings|
|
||||
mappings[field.name] = case field
|
||||
|
|
|
|||
|
|
@ -10,80 +10,120 @@ module ActiveContext
|
|||
|
||||
DEFAULT_MAX_BULK_SIZE = 10.megabytes
|
||||
|
||||
attr_reader :operations, :bulk_size
|
||||
attr_reader :index_operations, :bulk_size
|
||||
|
||||
def initialize(...)
|
||||
super
|
||||
@operations = []
|
||||
@index_operations = []
|
||||
@bulk_size = 0
|
||||
end
|
||||
|
||||
def add_ref(ref)
|
||||
operation = build_operation(ref)
|
||||
@refs << ref
|
||||
@operations << operation
|
||||
@bulk_size += calculate_operation_size(operation)
|
||||
build_index_operations(ref)
|
||||
|
||||
bulk_size >= bulk_threshold
|
||||
end
|
||||
|
||||
def empty?
|
||||
operations.empty?
|
||||
refs.empty?
|
||||
end
|
||||
|
||||
# Executes upsert operations in one bulk request
|
||||
# Create a single delete_by_query request for processing deletes
|
||||
def bulk
|
||||
client.bulk(body: operations.flatten)
|
||||
end
|
||||
results = []
|
||||
|
||||
def process_bulk_errors(result)
|
||||
return [] unless result['errors']
|
||||
results << client.bulk(body: index_operations.flatten, refresh: true) unless index_operations.empty?
|
||||
|
||||
failed_refs = []
|
||||
|
||||
result['items'].each_with_index do |item, index|
|
||||
op = item['index'] || item['update'] || item['delete']
|
||||
|
||||
next unless op.nil? || op['error']
|
||||
|
||||
ref = refs[index]
|
||||
|
||||
logger.warn(
|
||||
'message' => 'indexing_failed',
|
||||
'meta.indexing.error' => op&.dig('error') || 'Operation was nil',
|
||||
'meta.indexing.status' => op&.dig('status'),
|
||||
'meta.indexing.operation_type' => item.each_key.first,
|
||||
'meta.indexing.ref' => ref.serialize,
|
||||
'meta.indexing.identifier' => ref.identifier
|
||||
build_delete_operations.each do |op|
|
||||
results << client.delete_by_query(
|
||||
index: op[:index],
|
||||
body: op[:body]
|
||||
)
|
||||
|
||||
failed_refs << ref
|
||||
end
|
||||
|
||||
failed_refs
|
||||
results
|
||||
end
|
||||
|
||||
def process_bulk_errors(results)
|
||||
failed_refs_set = Set.new
|
||||
|
||||
results.each do |result|
|
||||
next unless result['errors']
|
||||
|
||||
result['items'].each do |item|
|
||||
op = item['index'] || item['update'] || item['delete']
|
||||
|
||||
next unless op.nil? || op['error']
|
||||
|
||||
ref = refs.find { |ref| ref.identifier == extract_identifier(op['_id']) }
|
||||
|
||||
logger.warn(
|
||||
'message' => 'indexing_failed',
|
||||
'meta.indexing.error' => op&.dig('error') || 'Operation was nil',
|
||||
'meta.indexing.status' => op&.dig('status'),
|
||||
'meta.indexing.operation_type' => item.each_key.first,
|
||||
'meta.indexing.ref' => ref&.serialize,
|
||||
'meta.indexing.identifier' => ref&.identifier
|
||||
)
|
||||
|
||||
failed_refs_set.add(ref) if ref
|
||||
end
|
||||
end
|
||||
|
||||
failed_refs_set.to_a
|
||||
end
|
||||
|
||||
def reset
|
||||
super
|
||||
@operations = []
|
||||
@index_operations = []
|
||||
@bulk_size = 0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_operation(ref)
|
||||
case ref.operation.to_sym
|
||||
when :index, :upsert
|
||||
[
|
||||
{ update: { _index: ref.partition, _id: ref.identifier, routing: ref.routing }.compact },
|
||||
{ doc: ref.as_indexed_json, doc_as_upsert: true }
|
||||
]
|
||||
when :delete
|
||||
[{ delete: { _index: ref.partition, _id: ref.identifier, routing: ref.routing }.compact }]
|
||||
else
|
||||
raise StandardError, "Operation #{ref.operation} is not supported"
|
||||
# Builds an upsert operation for every ref where operation is :upsert
|
||||
# These operations will be processed in bulk
|
||||
def build_index_operations(ref)
|
||||
return unless ref.operation.to_sym == :upsert
|
||||
|
||||
ref.jsons.map.with_index do |hash, index|
|
||||
add_index_operation([
|
||||
{ update: { _index: ref.partition, _id: unique_identifier(ref, index), routing: ref.routing }.compact },
|
||||
{ doc: hash, doc_as_upsert: true }
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
# Builds up a bool query containing multiple shoulds:
|
||||
# A single terms query containing ids of refs where operation is :delete
|
||||
# A bool query with a `filter` for the `ref_id` and `must_not` for the `ref_version` for :upsert refs
|
||||
# This ensures we only delete old versions of the document
|
||||
def build_delete_operations
|
||||
delete_operations = []
|
||||
|
||||
refs.group_by(&:partition).each do |partition, partition_refs|
|
||||
shoulds = []
|
||||
ref_ids_to_delete = []
|
||||
|
||||
partition_refs.each do |ref|
|
||||
case ref.operation.to_sym
|
||||
when :upsert
|
||||
shoulds << delete_with_version_query(ref)
|
||||
when :delete
|
||||
ref_ids_to_delete << ref.identifier
|
||||
else
|
||||
raise StandardError, "Operation #{ref.operation} is not supported"
|
||||
end
|
||||
end
|
||||
|
||||
delete_operations << { index: partition, body: build_delete_query(shoulds, ref_ids_to_delete) }
|
||||
end
|
||||
|
||||
delete_operations
|
||||
end
|
||||
|
||||
def calculate_operation_size(operation)
|
||||
operation.to_json.bytesize + 2 # Account for newlines
|
||||
end
|
||||
|
|
@ -95,6 +135,37 @@ module ActiveContext
|
|||
def logger
|
||||
@logger ||= ActiveContext::Config.logger
|
||||
end
|
||||
|
||||
def add_index_operation(op)
|
||||
@index_operations << op
|
||||
@bulk_size += calculate_operation_size(op)
|
||||
end
|
||||
|
||||
def delete_without_version_query(ref_ids)
|
||||
{ terms: { ref_id: ref_ids } }
|
||||
end
|
||||
|
||||
def delete_with_version_query(ref)
|
||||
{
|
||||
bool: {
|
||||
filter: { term: { ref_id: ref.identifier } },
|
||||
must_not: { term: { ref_version: ref.ref_version } }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def build_delete_query(shoulds, ref_ids_to_delete)
|
||||
shoulds << delete_without_version_query(ref_ids_to_delete) if ref_ids_to_delete.any?
|
||||
|
||||
{
|
||||
query: {
|
||||
bool: {
|
||||
should: shoulds,
|
||||
minimum_should_match: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,6 +53,14 @@ module ActiveContext
|
|||
@refs = []
|
||||
# also reset anything that builds up from the refs array
|
||||
end
|
||||
|
||||
def unique_identifier(ref, index)
|
||||
"#{ref.identifier}:#{index}"
|
||||
end
|
||||
|
||||
def extract_identifier(string)
|
||||
string.split(':').first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module ActiveContext
|
|||
class Client
|
||||
include ActiveContext::Databases::Concerns::Client
|
||||
|
||||
delegate :bulk, to: :client
|
||||
delegate :bulk, :delete_by_query, to: :client
|
||||
|
||||
OPEN_TIMEOUT = 5
|
||||
NO_RETRY = 0
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module ActiveContext
|
|||
class Client
|
||||
include ActiveContext::Databases::Concerns::Client
|
||||
|
||||
delegate :bulk, to: :client
|
||||
delegate :bulk, :delete_by_query, to: :client
|
||||
|
||||
OPEN_TIMEOUT = 5
|
||||
NO_RETRY = 0
|
||||
|
|
|
|||
|
|
@ -166,7 +166,17 @@ module ActiveContext
|
|||
end
|
||||
end
|
||||
when :delete
|
||||
model.where(id: data).delete_all
|
||||
prepare_delete_data(data).each do |group|
|
||||
ref_ids = group[:ref_ids]
|
||||
ref_version = group[:ref_version]
|
||||
|
||||
next if ref_ids.empty?
|
||||
|
||||
query = model.where(ref_id: ref_ids)
|
||||
query = query.where.not(ref_version: ref_version) if ref_version.present?
|
||||
|
||||
query.delete_all
|
||||
end
|
||||
end
|
||||
|
||||
[]
|
||||
|
|
@ -185,6 +195,16 @@ module ActiveContext
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_delete_data(data)
|
||||
data_by_version = data.group_by { |record| record[:ref_version] }
|
||||
data_by_version.map do |ref_version, records|
|
||||
{
|
||||
ref_version: ref_version,
|
||||
ref_ids: records.pluck(:ref_id)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,8 +48,12 @@ module ActiveContext
|
|||
add_column_from_field(table, field)
|
||||
end
|
||||
|
||||
# Add id column
|
||||
# Add id columns
|
||||
table.string :id, null: false
|
||||
table.string :ref_id, null: false
|
||||
|
||||
# Add ref_version column
|
||||
table.bigint :ref_version, null: false
|
||||
|
||||
# Add variable width columns last
|
||||
variable_columns.each do |field|
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ module ActiveContext
|
|||
|
||||
def add_ref(ref)
|
||||
@refs << ref
|
||||
@operations << build_operation(ref)
|
||||
build_operation(ref)
|
||||
|
||||
refs.size >= BATCH_SIZE
|
||||
end
|
||||
|
|
@ -43,20 +43,33 @@ module ActiveContext
|
|||
def build_operation(ref)
|
||||
case ref.operation.to_sym
|
||||
when :upsert
|
||||
{ "#{ref.partition_name}": { upsert: build_indexed_json(ref) }, ref: ref }
|
||||
ref.jsons.each.with_index do |hash, index|
|
||||
@operations << { "#{ref.partition_name}": { upsert: build_indexed_json(hash, ref, index) }, ref: ref }
|
||||
end
|
||||
@operations << build_delete_operation(ref: ref, include_ref_version: true)
|
||||
when :delete
|
||||
{ "#{ref.partition_name}": { delete: ref.identifier }, ref: ref }
|
||||
@operations << build_delete_operation(ref: ref)
|
||||
else
|
||||
raise StandardError, "Operation #{ref.operation} is not supported"
|
||||
end
|
||||
end
|
||||
|
||||
def build_indexed_json(ref)
|
||||
ref.as_indexed_json
|
||||
.merge(partition_id: ref.partition_number)
|
||||
def build_indexed_json(hash, ref, index)
|
||||
hash
|
||||
.merge(
|
||||
partition_id: ref.partition_number,
|
||||
id: unique_identifier(ref, index)
|
||||
)
|
||||
.transform_values { |value| convert_pg_array(value) }
|
||||
end
|
||||
|
||||
def build_delete_operation(ref:, include_ref_version: false)
|
||||
hash = { ref_id: ref.identifier }
|
||||
hash[:ref_version] = ref.ref_version if include_ref_version
|
||||
|
||||
{ "#{ref.partition_name}": { delete: hash }, ref: ref }
|
||||
end
|
||||
|
||||
def convert_pg_array(value)
|
||||
value.is_a?(Array) ? "[#{value.join(',')}]" : value
|
||||
end
|
||||
|
|
|
|||
|
|
@ -35,13 +35,14 @@ module ActiveContext
|
|||
end
|
||||
end
|
||||
|
||||
attr_reader :collection_id, :collection, :routing, :serialized_args
|
||||
attr_reader :collection_id, :collection, :routing, :serialized_args, :ref_version
|
||||
|
||||
def initialize(collection_id:, routing:, args: [])
|
||||
@collection_id = collection_id.to_i
|
||||
@collection = ActiveContext::CollectionCache.fetch(@collection_id)
|
||||
@routing = routing
|
||||
@serialized_args = Array(args)
|
||||
@ref_version = Time.now.to_i
|
||||
init
|
||||
end
|
||||
|
||||
|
|
@ -61,8 +62,19 @@ module ActiveContext
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def as_indexed_json
|
||||
raise NotImplementedError
|
||||
def jsons
|
||||
as_indexed_jsons.map do |json|
|
||||
json.merge(
|
||||
ref_id: identifier,
|
||||
ref_version: ref_version
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def as_indexed_jsons
|
||||
return Array.wrap(as_indexed_json) if respond_to?(:as_indexed_json)
|
||||
|
||||
raise NotImplementedError, "#{self.class} must implement either :as_indexed_json or :as_indexed_jsons"
|
||||
end
|
||||
|
||||
def operation
|
||||
|
|
|
|||
|
|
@ -4,22 +4,28 @@ RSpec.describe ActiveContext::BulkProcessor do
|
|||
let(:connection) { double('Connection') }
|
||||
let(:adapter) { ActiveContext::Databases::Elasticsearch::Adapter.new(connection, options: { url: 'http://localhost:9200' }) }
|
||||
let(:logger) { instance_double(Logger) }
|
||||
let(:ref) { double }
|
||||
let(:reference_class) { Test::References::MockWithDatabaseRecord }
|
||||
|
||||
let(:ref) { reference_class.new(collection_id: collection_id, routing: partition, args: 1) }
|
||||
let(:mock_collection) { double(name: collection_name, partition_for: partition) }
|
||||
let(:mock_object) { double(id: object_id) }
|
||||
let(:mock_relation) { double(find_by: mock_object) }
|
||||
let(:mock_connection) { double(id: connection_id) }
|
||||
|
||||
let(:connection_id) { 3 }
|
||||
let(:partition) { 2 }
|
||||
let(:collection_id) { 1 }
|
||||
let(:object_id) { 5 }
|
||||
let(:collection_name) { 'mock_collection' }
|
||||
|
||||
before do
|
||||
allow(ActiveContext).to receive(:adapter).and_return(adapter)
|
||||
allow(ActiveContext::Config).to receive(:logger).and_return(logger)
|
||||
allow(ActiveContext::CollectionCache).to receive(:fetch).and_return(mock_collection)
|
||||
allow(ActiveContext::Logger).to receive(:exception).and_return(nil)
|
||||
allow(reference_class).to receive(:model_klass).and_return(mock_relation)
|
||||
allow(logger).to receive(:info)
|
||||
allow(logger).to receive(:error)
|
||||
allow(ref).to receive_messages(
|
||||
operation: :index,
|
||||
id: 1,
|
||||
as_indexed_json: { title: 'Test Issue' },
|
||||
partition_name: 'issues',
|
||||
partition: 'issues_0',
|
||||
identifier: '1',
|
||||
routing: 'group_1'
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
|
|
@ -66,7 +72,7 @@ RSpec.describe ActiveContext::BulkProcessor do
|
|||
end
|
||||
|
||||
it 'processes bulk and logs info' do
|
||||
allow(adapter).to receive(:bulk).and_return({ 'items' => [] })
|
||||
allow(adapter).to receive(:bulk).and_return([{ 'items' => [] }])
|
||||
|
||||
expect(logger).to receive(:info).with(
|
||||
'message' => 'bulk_submitted',
|
||||
|
|
@ -78,7 +84,7 @@ RSpec.describe ActiveContext::BulkProcessor do
|
|||
end
|
||||
|
||||
it 'resets the adapter after processing' do
|
||||
allow(adapter).to receive(:bulk).and_return({ 'items' => [] })
|
||||
allow(adapter).to receive(:bulk).and_return([{ 'items' => [] }])
|
||||
expect(adapter).to receive(:reset)
|
||||
|
||||
processor.send(:send_bulk)
|
||||
|
|
@ -94,7 +100,7 @@ RSpec.describe ActiveContext::BulkProcessor do
|
|||
|
||||
context 'when bulk processing succeeds' do
|
||||
it 'returns empty array' do
|
||||
allow(adapter).to receive(:bulk).and_return({ 'items' => [] })
|
||||
allow(adapter).to receive(:bulk).and_return([{ 'items' => [] }])
|
||||
expect(processor.send(:try_send_bulk)).to eq([])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,122 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ActiveContext::Databases::Elasticsearch::Indexer do
|
||||
let(:es_client) { instance_double(Elasticsearch::Client) }
|
||||
let(:logger) { instance_double(Logger, warn: nil) }
|
||||
let(:options) { {} }
|
||||
let(:indexer) { described_class.new(options, es_client) }
|
||||
let(:ref) { double }
|
||||
let(:client) { instance_double(Elasticsearch::Client) }
|
||||
let(:indexer) { described_class.new(options, client) }
|
||||
|
||||
before do
|
||||
allow(ActiveContext::Config).to receive(:logger).and_return(logger)
|
||||
allow(ref).to receive_messages(
|
||||
operation: :index,
|
||||
id: 1,
|
||||
as_indexed_json: { title: 'Test Issue' },
|
||||
partition_name: 'issues',
|
||||
identifier: '1',
|
||||
partition: 'issues_0',
|
||||
routing: 'group_1',
|
||||
serialize: 'issue 1 group_1'
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'initializes with empty operations and zero bulk size' do
|
||||
expect(indexer.operations).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add_ref' do
|
||||
it 'adds the ref and returns true when bulk threshold is reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1)
|
||||
expect(indexer.add_ref(ref)).to be true
|
||||
expect(indexer.operations).not_to be_empty
|
||||
end
|
||||
|
||||
it 'adds the ref and returns false when bulk threshold is not reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1000000)
|
||||
expect(indexer.add_ref(ref)).to be false
|
||||
expect(indexer.operations).not_to be_empty
|
||||
end
|
||||
|
||||
it 'raises an error for unsupported operations' do
|
||||
allow(ref).to receive(:operation).and_return(:unsupported)
|
||||
expect { indexer.add_ref(ref) }.to raise_error(StandardError, /Operation unsupported is not supported/)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty?' do
|
||||
it 'returns true when there are no operations' do
|
||||
expect(indexer).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false when there are operations' do
|
||||
indexer.instance_variable_set(:@operations, [{}])
|
||||
expect(indexer).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bulk' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@operations, [{ index: {} }])
|
||||
end
|
||||
|
||||
it 'calls bulk on the client with flattened operations' do
|
||||
expect(es_client).to receive(:bulk).with(body: [{ index: {} }])
|
||||
indexer.bulk
|
||||
end
|
||||
end
|
||||
|
||||
describe '#process_bulk_errors' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
end
|
||||
|
||||
context 'when there are no errors' do
|
||||
it 'returns an empty array' do
|
||||
result = { 'errors' => false }
|
||||
expect(indexer.process_bulk_errors(result)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are errors' do
|
||||
let(:result) do
|
||||
{
|
||||
'errors' => true,
|
||||
'items' => [
|
||||
{ 'index' => { 'error' => 'Error message', 'status' => 400 } }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
it 'logs warnings and returns failed refs' do
|
||||
expect(logger).to receive(:warn).with(
|
||||
'message' => 'indexing_failed',
|
||||
'meta.indexing.error' => 'Error message',
|
||||
'meta.indexing.status' => 400,
|
||||
'meta.indexing.operation_type' => 'index',
|
||||
'meta.indexing.ref' => 'issue 1 group_1',
|
||||
'meta.indexing.identifier' => '1'
|
||||
)
|
||||
|
||||
failed_refs = indexer.process_bulk_errors(result)
|
||||
expect(failed_refs).to eq([ref])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@operations, [{}])
|
||||
indexer.instance_variable_set(:@bulk_size, 100)
|
||||
end
|
||||
|
||||
it 'resets operations and bulk size' do
|
||||
indexer.reset
|
||||
expect(indexer.operations).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
it_behaves_like 'an elastic indexer'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,122 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ActiveContext::Databases::Opensearch::Indexer do
|
||||
let(:opensearch_client) { instance_double(OpenSearch::Client) }
|
||||
let(:logger) { instance_double(Logger, warn: nil) }
|
||||
let(:options) { {} }
|
||||
let(:indexer) { described_class.new(options, opensearch_client) }
|
||||
let(:ref) { double }
|
||||
let(:client) { instance_double(OpenSearch::Client) }
|
||||
let(:indexer) { described_class.new(options, client) }
|
||||
|
||||
before do
|
||||
allow(ActiveContext::Config).to receive(:logger).and_return(logger)
|
||||
allow(ref).to receive_messages(
|
||||
operation: :index,
|
||||
id: 1,
|
||||
as_indexed_json: { title: 'Test Issue' },
|
||||
partition_name: 'issues',
|
||||
identifier: '1',
|
||||
partition: 'issues_0',
|
||||
routing: 'group_1',
|
||||
serialize: 'issue 1 group_1'
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'initializes with empty operations and zero bulk size' do
|
||||
expect(indexer.operations).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add_ref' do
|
||||
it 'adds the ref and returns true when bulk threshold is reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1)
|
||||
expect(indexer.add_ref(ref)).to be true
|
||||
expect(indexer.operations).not_to be_empty
|
||||
end
|
||||
|
||||
it 'adds the ref and returns false when bulk threshold is not reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1000000)
|
||||
expect(indexer.add_ref(ref)).to be false
|
||||
expect(indexer.operations).not_to be_empty
|
||||
end
|
||||
|
||||
it 'raises an error for unsupported operations' do
|
||||
allow(ref).to receive(:operation).and_return(:unsupported)
|
||||
expect { indexer.add_ref(ref) }.to raise_error(StandardError, /Operation unsupported is not supported/)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty?' do
|
||||
it 'returns true when there are no operations' do
|
||||
expect(indexer).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false when there are operations' do
|
||||
indexer.instance_variable_set(:@operations, [{}])
|
||||
expect(indexer).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bulk' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@operations, [{ index: {} }])
|
||||
end
|
||||
|
||||
it 'calls bulk on the client with flattened operations' do
|
||||
expect(opensearch_client).to receive(:bulk).with(body: [{ index: {} }])
|
||||
indexer.bulk
|
||||
end
|
||||
end
|
||||
|
||||
describe '#process_bulk_errors' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
end
|
||||
|
||||
context 'when there are no errors' do
|
||||
it 'returns an empty array' do
|
||||
result = { 'errors' => false }
|
||||
expect(indexer.process_bulk_errors(result)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are errors' do
|
||||
let(:result) do
|
||||
{
|
||||
'errors' => true,
|
||||
'items' => [
|
||||
{ 'index' => { 'error' => 'Error message', 'status' => 400 } }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
it 'logs warnings and returns failed refs' do
|
||||
expect(logger).to receive(:warn).with(
|
||||
'message' => 'indexing_failed',
|
||||
'meta.indexing.error' => 'Error message',
|
||||
'meta.indexing.status' => 400,
|
||||
'meta.indexing.operation_type' => 'index',
|
||||
'meta.indexing.ref' => 'issue 1 group_1',
|
||||
'meta.indexing.identifier' => '1'
|
||||
)
|
||||
|
||||
failed_refs = indexer.process_bulk_errors(result)
|
||||
expect(failed_refs).to eq([ref])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@operations, [{}])
|
||||
indexer.instance_variable_set(:@bulk_size, 100)
|
||||
end
|
||||
|
||||
it 'resets operations and bulk size' do
|
||||
indexer.reset
|
||||
expect(indexer.operations).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
it_behaves_like 'an elastic indexer'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -323,17 +323,17 @@ RSpec.describe ActiveContext::Databases::Postgresql::Client do
|
|||
let(:collection_name) { 'test_collection' }
|
||||
let(:operations) do
|
||||
[
|
||||
{ collection_name => { delete: 1 } }
|
||||
{ collection_name => { delete: { ref_id: 1 } } }
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
allow(model_class).to receive(:where).with(id: [1]).and_return(model_class)
|
||||
allow(model_class).to receive(:where).with(ref_id: [1]).and_return(model_class)
|
||||
allow(model_class).to receive(:delete_all).and_return(1)
|
||||
end
|
||||
|
||||
it 'processes delete operations with the model' do
|
||||
expect(model_class).to receive(:where).with(id: [1])
|
||||
expect(model_class).to receive(:where).with(ref_id: [1])
|
||||
expect(model_class).to receive(:delete_all)
|
||||
|
||||
result = client.bulk_process(operations)
|
||||
|
|
@ -654,15 +654,15 @@ RSpec.describe ActiveContext::Databases::Postgresql::Client do
|
|||
|
||||
context 'with delete operation' do
|
||||
let(:operation_type) { :delete }
|
||||
let(:operation_data) { 1 }
|
||||
let(:operation_data) { { ref_id: 1 } }
|
||||
|
||||
before do
|
||||
allow(model).to receive(:where).with(id: [operation_data]).and_return(model)
|
||||
allow(model).to receive(:where).with(ref_id: [1]).and_return(model)
|
||||
allow(model).to receive(:delete_all).and_return(1)
|
||||
end
|
||||
|
||||
it 'processes delete operations successfully' do
|
||||
expect(model).to receive(:where).with(id: [operation_data])
|
||||
expect(model).to receive(:where).with(ref_id: [1])
|
||||
expect(model).to receive(:delete_all)
|
||||
|
||||
result = client.send(:perform_bulk_operation, operation_type, model, collection_name, operations)
|
||||
|
|
|
|||
|
|
@ -21,10 +21,16 @@ module Test
|
|||
:upsert
|
||||
end
|
||||
|
||||
def as_indexed_json
|
||||
{
|
||||
id: identifier
|
||||
}
|
||||
def as_indexed_jsons
|
||||
[{ id: identifier }]
|
||||
end
|
||||
|
||||
def partition_name
|
||||
'test'
|
||||
end
|
||||
|
||||
def partition
|
||||
"#{partition_name}_0"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@
|
|||
module Test
|
||||
module References
|
||||
class MockWithDatabaseRecord < Mock
|
||||
def self.model_klass
|
||||
Class.new do
|
||||
def self.find_by(id:)
|
||||
{ id: id }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def model_klass
|
||||
self.class.model_klass
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'an elastic indexer' do
|
||||
let(:logger) { instance_double(Logger, warn: nil) }
|
||||
let(:options) { {} }
|
||||
let(:ref) { double }
|
||||
|
||||
before do
|
||||
allow(ActiveContext::Config).to receive(:logger).and_return(logger)
|
||||
allow(ref).to receive_messages(
|
||||
operation: :upsert,
|
||||
id: 1,
|
||||
partition_name: 'issues',
|
||||
identifier: '1',
|
||||
partition: 'issues_0',
|
||||
routing: 'group_1',
|
||||
serialize: 'issue 1 group_1',
|
||||
jsons: [{ title: 'Test Issue' }],
|
||||
ref_version: 123456
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'initializes with empty operations and zero bulk size' do
|
||||
expect(indexer.index_operations).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add_ref' do
|
||||
it 'adds the ref and returns true when bulk threshold is reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1)
|
||||
expect(indexer.add_ref(ref)).to be true
|
||||
expect(indexer.index_operations).not_to be_empty
|
||||
end
|
||||
|
||||
it 'adds the ref and returns false when bulk threshold is not reached' do
|
||||
allow(indexer).to receive(:bulk_threshold).and_return(1000000)
|
||||
expect(indexer.add_ref(ref)).to be false
|
||||
expect(indexer.instance_variable_get(:@refs)).to include(ref)
|
||||
end
|
||||
|
||||
it 'raises an error for unsupported operations' do
|
||||
allow(ref).to receive(:operation).and_return(:unsupported)
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
expect { indexer.send(:build_delete_operations) }
|
||||
.to raise_error(StandardError, /Operation unsupported is not supported/)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty?' do
|
||||
it 'returns true when there are no operations' do
|
||||
expect(indexer).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false when there are operations' do
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
expect(indexer).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bulk' do
|
||||
context 'when only index operations are present' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@index_operations, [{ index: {} }])
|
||||
indexer.instance_variable_set(:@refs, [])
|
||||
end
|
||||
|
||||
it 'calls bulk on the client with flattened operations' do
|
||||
expect(client).to receive(:bulk).with(body: [{ index: {} }], refresh: true)
|
||||
indexer.bulk
|
||||
end
|
||||
end
|
||||
|
||||
context 'when delete operations are present' do
|
||||
let(:delete_ref) { double }
|
||||
|
||||
before do
|
||||
indexer.instance_variable_set(:@index_operations, [])
|
||||
|
||||
allow(delete_ref).to receive_messages(
|
||||
operation: :delete,
|
||||
identifier: '1',
|
||||
partition: 'issues_0'
|
||||
)
|
||||
|
||||
indexer.instance_variable_set(:@refs, [delete_ref])
|
||||
end
|
||||
|
||||
it 'calls delete_by_query on the client with the correct parameters' do
|
||||
expect(client).to receive(:delete_by_query).with(
|
||||
hash_including(
|
||||
index: 'issues_0',
|
||||
body: hash_including(
|
||||
query: hash_including(
|
||||
bool: hash_including(
|
||||
should: array_including(
|
||||
hash_including(terms: hash_including(ref_id: ['1']))
|
||||
),
|
||||
minimum_should_match: 1
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
indexer.bulk
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both index and delete operations are present' do
|
||||
let(:delete_ref) { double }
|
||||
|
||||
before do
|
||||
indexer.instance_variable_set(:@index_operations, [{ index: {} }])
|
||||
|
||||
allow(delete_ref).to receive_messages(
|
||||
operation: :delete,
|
||||
identifier: '1',
|
||||
partition: 'issues_0'
|
||||
)
|
||||
|
||||
indexer.instance_variable_set(:@refs, [delete_ref])
|
||||
end
|
||||
|
||||
it 'calls both bulk and delete_by_query on the client' do
|
||||
expect(client).to receive(:bulk).with(body: [{ index: {} }], refresh: true)
|
||||
|
||||
expect(client).to receive(:delete_by_query).with(
|
||||
hash_including(
|
||||
index: 'issues_0',
|
||||
body: hash_including(
|
||||
query: hash_including(
|
||||
bool: hash_including(
|
||||
should: array_including(
|
||||
hash_including(terms: hash_including(ref_id: ['1']))
|
||||
),
|
||||
minimum_should_match: 1
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
indexer.bulk
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#process_bulk_errors' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
end
|
||||
|
||||
context 'when there are no errors' do
|
||||
it 'returns an empty array' do
|
||||
result = [{ 'errors' => false }]
|
||||
expect(indexer.process_bulk_errors(result)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are errors' do
|
||||
let(:result) do
|
||||
[{
|
||||
'errors' => true,
|
||||
'items' => [
|
||||
{ 'index' => { '_id' => '1:0', 'error' => 'Error message', 'status' => 400 } }
|
||||
]
|
||||
}]
|
||||
end
|
||||
|
||||
it 'logs warnings and returns failed refs' do
|
||||
allow(indexer).to receive(:extract_identifier).with(nil).and_return(nil)
|
||||
allow(indexer).to receive(:extract_identifier).with('1:0').and_return('1')
|
||||
|
||||
expect(logger).to receive(:warn).with(
|
||||
'message' => 'indexing_failed',
|
||||
'meta.indexing.error' => 'Error message',
|
||||
'meta.indexing.status' => 400,
|
||||
'meta.indexing.operation_type' => 'index',
|
||||
'meta.indexing.ref' => 'issue 1 group_1',
|
||||
'meta.indexing.identifier' => '1'
|
||||
)
|
||||
|
||||
failed_refs = indexer.process_bulk_errors(result)
|
||||
expect(failed_refs).to eq([ref])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset' do
|
||||
before do
|
||||
indexer.instance_variable_set(:@index_operations, [{}])
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
indexer.instance_variable_set(:@bulk_size, 100)
|
||||
end
|
||||
|
||||
it 'resets operations and bulk size' do
|
||||
indexer.reset
|
||||
expect(indexer.index_operations).to be_empty
|
||||
expect(indexer.instance_variable_get(:@refs)).to be_empty
|
||||
expect(indexer.bulk_size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build_delete_operations' do
|
||||
context 'when operation is :upsert' do
|
||||
before do
|
||||
allow(ref).to receive(:operation).and_return(:upsert)
|
||||
indexer.instance_variable_set(:@refs, [ref])
|
||||
end
|
||||
|
||||
it 'creates delete operations with version query' do
|
||||
delete_ops = indexer.send(:build_delete_operations)
|
||||
|
||||
expect(delete_ops.first[:index]).to eq('issues_0')
|
||||
expect(delete_ops.first[:body][:query][:bool][:should].first[:bool]).to be_present
|
||||
expect(delete_ops.first[:body][:query][:bool][:minimum_should_match]).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when operation is :delete' do
|
||||
let(:delete_ref) { double }
|
||||
|
||||
before do
|
||||
allow(delete_ref).to receive_messages(
|
||||
operation: :delete,
|
||||
identifier: '1',
|
||||
partition: 'issues_0'
|
||||
)
|
||||
|
||||
indexer.instance_variable_set(:@refs, [delete_ref])
|
||||
end
|
||||
|
||||
it 'creates delete operations with terms query' do
|
||||
delete_ops = indexer.send(:build_delete_operations)
|
||||
|
||||
expect(delete_ops.first[:index]).to eq('issues_0')
|
||||
expect(delete_ops.first[:body][:query][:bool][:should].first[:terms]).to be_present
|
||||
expect(delete_ops.first[:body][:query][:bool][:minimum_should_match]).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build_index_operations' do
|
||||
it 'adds index operations for upsert' do
|
||||
indexer.instance_variable_set(:@index_operations, [])
|
||||
indexer.instance_variable_set(:@bulk_size, 0)
|
||||
|
||||
allow(ref).to receive(:operation).and_return(:upsert)
|
||||
indexer.send(:build_index_operations, ref)
|
||||
|
||||
expect(indexer.index_operations).not_to be_empty
|
||||
expect(indexer.bulk_size).to be > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -242,10 +242,8 @@ module API
|
|||
put ':id/packages/maven/*path/:file_name/authorize', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
|
||||
authorize_upload!
|
||||
|
||||
if Feature.enabled?(:packages_protected_packages_maven, user_project)
|
||||
package_name = params[:path].rpartition('/').first
|
||||
protect_package!(package_name, :maven)
|
||||
end
|
||||
package_name = params[:path].rpartition('/').first
|
||||
protect_package!(package_name, :maven)
|
||||
|
||||
status 200
|
||||
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# This template is for testing cutting-edge features.
|
||||
# It is not considered stable and might include breaking changes that are planned for the next major release.
|
||||
# For more information: https://docs.gitlab.com/user/application_security/detect/roll_out_security_scanning/#template-editions
|
||||
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/development/cicd/templates/
|
||||
# This specific template is located at:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# This template is for testing cutting-edge features.
|
||||
# It is not considered stable and might include breaking changes that are planned for the next major release.
|
||||
# For more information: https://docs.gitlab.com/user/application_security/detect/roll_out_security_scanning/#template-editions
|
||||
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/development/cicd/templates/
|
||||
# This specific template is located at:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# This template is for testing cutting-edge features.
|
||||
# It is not considered stable and might include breaking changes that are planned for the next major release.
|
||||
# For more information: https://docs.gitlab.com/user/application_security/detect/roll_out_security_scanning/#template-editions
|
||||
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/iac_scanning/
|
||||
#
|
||||
# Configure SAST with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/).
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# This template is for testing cutting-edge features.
|
||||
# It is not considered stable and might include breaking changes that are planned for the next major release.
|
||||
# For more information: https://docs.gitlab.com/user/application_security/sast/#stable-vs-latest-sast-templates
|
||||
#
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/sast/
|
||||
#
|
||||
# Configure SAST with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/).
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# This template is for testing cutting-edge features.
|
||||
# It is not considered stable and might include breaking changes that are planned for the next major release.
|
||||
# For more information: https://docs.gitlab.com/user/application_security/detect/roll_out_security_scanning/#template-editions
|
||||
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/secret_detection
|
||||
#
|
||||
# Configure the scanning tool through the environment variables.
|
||||
|
|
|
|||
|
|
@ -66955,6 +66955,9 @@ msgstr ""
|
|||
msgid "WorkItemCustomFields|Failed to load custom fields."
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItemCustomFields|Options could not be loaded for field: %{customFieldName}. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItemCustomFields|Options could not be loaded for field: %{dropdownLabel}. Please try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,288 +1,286 @@
|
|||
{
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 37.207076614,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 87.91302946399999,
|
||||
"qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb": 106.74897681499999,
|
||||
"qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb": 106.524588536,
|
||||
"qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb": 99.92565242,
|
||||
"qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb": 16.319589317,
|
||||
"qa/specs/features/api/1_manage/import/import_github_repo_spec.rb": 99.668106968,
|
||||
"qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb": 58.647108560999996,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb": 62.553768578,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb": 200.961366816,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb": 93.299031782,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb": 90.30839749,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 12.777861528,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 19.691846473,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 15.367861742,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 22.694099294,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_spec.rb": 42.754932313,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 0.76019081,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 28.824045455,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 10.793957657,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 14.329634175,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 7.03710898,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 10.503566799,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 15.217855771,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 18.914663631,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 10.706025635,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 96.289392366,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 15.150942196,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 53.61972165,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 76.561662825,
|
||||
"qa/specs/features/api/8_monitor/metrics_spec.rb": 6.288770821,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 0.8853165,
|
||||
"qa/specs/features/api/9_tenant_scale/user_inherited_access_spec.rb": 83.69603124600002,
|
||||
"qa/specs/features/api/9_tenant_scale/users_spec.rb": 5.886840898,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 20.867824391,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 56.876938819,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 38.964384716,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 12.901393894,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 90.797501204,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_gitlab_via_ldap_spec.rb": 3.564938402,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_mattermost_via_gitlab_spec.rb": 27.071739976,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_instance_wide_saml_sso_spec.rb": 15.627328393,
|
||||
"qa/specs/features/browser_ui/10_govern/login/oauth_login_with_github_spec.rb": 40.165915059,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 110.890801518,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 26.258944727,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 35.207811022,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 36.893613149000004,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 30.30578855,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 11.848021995,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_disabled_spec.rb": 13.381185554,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 71.673357617,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb": 52.89126454,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb": 47.05295486,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb": 69.335723063,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb": 55.699516126,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_user_contribution_reassignment_spec.rb": 129.866549506,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 22.657133123,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 19.437812084,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 23.46002424,
|
||||
"qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb": 21.213680251,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 47.462248957,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 23.190359377,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 25.35169902,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 96.045413587,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 26.053069959,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 44.520782235,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 30.736119828,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 26.933119197,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 39.820321974,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 21.03936907,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 14.710257794,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 104.76434839000001,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 16.274443569,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 30.612303234,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 76.268792724,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 43.012629031,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 14.512877393,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 38.402729613,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 56.591316352999996,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 48.519975719,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 26.487291187,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 127.484864026,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 46.270185617,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 32.477435558,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 104.69259979899999,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 51.659230202,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_request_set_to_auto_merge_spec.rb": 76.296143477,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 35.632093892,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 140.941483608,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 68.137962655,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 61.142612451,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 65.668166985,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 20.597550643,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 20.29884586,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 17.88420823,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 49.311672463,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 40.381127155,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 87.010305845,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 25.380460108,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 16.111231942,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 23.780837922,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 67.684553122,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 63.620997057,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 53.92652669,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 37.048772877,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_file_size_spec.rb": 54.173852355,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 51.322948611,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 24.681057654,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb": 24.52283251,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 28.413507409,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 34.291079461,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 39.663983822000006,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 30.101478119,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 38.96667861,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 52.105896124,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 52.745480488,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 45.73494453,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 17.936878554,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 13.901061532,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 17.87558427,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 27.653818959,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 44.093374056,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 30.206094125,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 71.85765468700001,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 18.87199177,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 53.059544865,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 71.735155713,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 14.820729826,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/settings_sync_web_ide_spec.rb": 173.975187678,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 93.318060577,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_glab_spec.rb": 178.270933275,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_release_cli_spec.rb": 106.014626072,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 53.140124308,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 80.708315384,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 252.641044663,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 322.869775152,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 81.585566202,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 189.002493545,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 33.862400457,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 103.601551263,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 27.491900575,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 61.261400953,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 55.167737803,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 153.736479467,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 86.508999336,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 123.151451935,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 84.057822193,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 45.256516682,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 29.090123232,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_registration_token_spec.rb": 17.730218708,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_unregister_runner_spec.rb": 31.621485767,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_counts_spec.rb": 26.059744009,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_status_counts_spec.rb": 15.953068157,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 21.118085708,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_project_runner_spec.rb": 46.694633526,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 70.088950274,
|
||||
"qa/specs/features/browser_ui/5_package/container_registry/self_managed/container_registry_spec.rb": 338.923458313,
|
||||
"qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb": 161.297850669,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 57.009161977,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 86.950567213,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 57.258997403,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 280.273624913,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 488.86347062000004,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 333.228560615,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 265.746578356,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 289.779857245,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 294.921764442,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 86.880618931,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 30.683660323,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 151.384157101,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 7.431704801,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 51.293519028999995,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 74.735893116,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 53.006056884,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb": 59.754604756000006,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 17.146788374,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 21.699033327,
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 40.691143808,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 87.194960269,
|
||||
"qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb": 124.591377774,
|
||||
"qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb": 133.341350153,
|
||||
"qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb": 135.629715876,
|
||||
"qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb": 8.710134297,
|
||||
"qa/specs/features/api/1_manage/import/import_github_repo_spec.rb": 94.836860872,
|
||||
"qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb": 55.202470212,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb": 71.068568299,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb": 191.40671086700002,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb": 101.418912323,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb": 86.199285414,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 13.681232201,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 16.628659891,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 18.632759778,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 40.50874593,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_spec.rb": 40.941381925,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 1.984831665,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 19.166037203,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 10.386859875,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 14.950358283,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 5.399797597,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 13.926035241,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 20.095130139,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 17.40260055,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 4.557645028,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 102.59533486,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 17.921089144,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 108.77503098,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 60.320143223,
|
||||
"qa/specs/features/api/8_monitor/metrics_spec.rb": 4.849474337,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 0.892267347,
|
||||
"qa/specs/features/api/9_tenant_scale/user_inherited_access_spec.rb": 131.43909578199998,
|
||||
"qa/specs/features/api/9_tenant_scale/users_spec.rb": 5.447678849,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 21.318932887,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 47.737133234,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 48.143979841,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 14.860763952,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 103.087909674,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_gitlab_via_ldap_spec.rb": 3.496607815,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_mattermost_via_gitlab_spec.rb": 29.053194068,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_instance_wide_saml_sso_spec.rb": 15.806100889,
|
||||
"qa/specs/features/browser_ui/10_govern/login/oauth_login_with_github_spec.rb": 40.665469502,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 96.334226178,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 24.673522444,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 32.700644966,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 34.641708789,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 34.703431313,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 11.914625589,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_disabled_spec.rb": 12.014757008,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 72.586710287,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb": 63.084658112,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb": 73.852005486,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb": 53.950460633,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 21.000879009,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 28.953349875,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 26.645472257,
|
||||
"qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb": 13.756195215,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 29.575440439,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 24.926239844,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 23.023338104,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 151.174229924,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 30.057413526,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 38.970955318,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 29.299008757,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 23.720133674,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 36.179742962,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 26.324370987,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 18.079963204,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 105.340186238,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 21.671163394,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 25.749516379,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 71.814413183,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 52.613164620999996,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 20.068066802,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 34.591485112,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 58.819139284,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 47.277272998,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 23.255745385,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 127.168158595,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 67.252757943,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 32.223963051,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 87.827782154,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 53.056355204,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_request_set_to_auto_merge_spec.rb": 87.360235814,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 31.587529235,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 52.606350146,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 63.193462575,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 66.920456466,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 29.99328968,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 18.334742894,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 20.731932415,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 21.381189561,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 29.900971524,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 39.629157223,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 93.708429757,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 18.17010505,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 21.111061798,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 22.452186483,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 63.882396272,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 62.198666296,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 47.464493569,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 33.633618531,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_file_size_spec.rb": 61.309425759999996,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 45.961936588,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 10.71170848,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb": 23.203424045,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 25.000182089,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 27.827771667,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 44.768375755,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 40.496648039,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 25.161237139,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 45.836259861,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 41.939930087,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 25.903048665,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 13.900035191,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 11.621989822,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 27.377261185,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 17.18307556,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 34.146785136,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 23.542113286000003,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 65.335500732,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 20.716304489,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 45.115673919,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 65.61314646,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 18.933166402,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/settings_sync_web_ide_spec.rb": 170.151622798,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 131.685566209,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 85.14900900399999,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_glab_spec.rb": 165.436427342,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_release_cli_spec.rb": 113.980604624,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 46.65707505,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 43.341571268,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 250.111015097,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 289.840771931,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 71.028508365,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 144.433797235,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 54.491041314,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 94.980152438,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 29.989966111,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 97.780808817,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 59.438863121,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 99.436951084,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 83.332001529,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 99.977753257,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 84.607064656,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 76.262548031,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 24.490188353,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_registration_token_spec.rb": 17.938743554,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_unregister_runner_spec.rb": 29.797505381,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_counts_spec.rb": 26.805126272,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_status_counts_spec.rb": 17.624983578,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 20.85656348,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_project_runner_spec.rb": 68.961966963,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 58.42357976,
|
||||
"qa/specs/features/browser_ui/5_package/container_registry/self_managed/container_registry_spec.rb": 326.001061823,
|
||||
"qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb": 166.521857226,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 53.397001906,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 71.812714409,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 49.3065106,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 275.762812457,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 563.7325411840001,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 338.291903648,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 308.187804595,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 270.596665232,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 259.061583186,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 78.865083326,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 33.046605622,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 165.311982478,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 10.313825005,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 55.240105387,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 63.256907842000004,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 50.802184255,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb": 62.301000474,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 26.296569993,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 22.594858998,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_spec.rb": 24.599204923000002,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/view_project_activity_spec.rb": 23.850562164,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/create_group_with_mattermost_team_spec.rb": 6.240378808,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/group_member_access_request_spec.rb": 60.852950331,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/transfer_project_spec.rb": 34.070024556,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/add_project_member_spec.rb": 32.422878768,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/create_project_badge_spec.rb": 18.46602993,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/create_project_spec.rb": 65.529161432,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/dashboard_images_spec.rb": 14.00007171,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/invite_group_to_project_spec.rb": 54.089279590000004,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/project_owner_permissions_spec.rb": 88.32154976999999,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/view_project_activity_spec.rb": 31.978578499,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/follow_user_activity_spec.rb": 18.7066108,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/parent_group_access_termination_spec.rb": 23.543098987,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/user_inherited_access_spec.rb": 30.211737995,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 60.564013815,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 31.675662473,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 60.127438927,
|
||||
"qa/specs/features/ee/api/1_manage/import/import_github_repo_spec.rb": 155.987421953,
|
||||
"qa/specs/features/ee/api/1_manage/integrations/group_webhook_events_spec.rb": 14.192521699,
|
||||
"qa/specs/features/ee/api/1_manage/migration/gitlab_migration_group_spec.rb": 69.474014124,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 63.176276094,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 43.533472471,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/advanced_global_advanced_syntax_search_spec.rb": 112.02314393200001,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/elasticsearch_api_spec.rb": 35.638105102,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/commit_index/commit_index_spec.rb": 22.440969574,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/issues_index/issue_index_spec.rb": 150.585063896,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/main_index/blob_index_spec.rb": 19.133683791,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/merge_request_index/merge_request_index_spec.rb": 49.420091828,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/notes_index/note_index_spec.rb": 51.884753885,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/user_index/user_index_spec.rb": 53.562935587,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 108.20761873200001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 68.107695752,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb": 89.465203516,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 31.659377304,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 156.79193972,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 66.973654801,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 136.07338086,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_ldap_sync_spec.rb": 113.822288455,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 110.51489972799999,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group_pipeline_execution_policy_spec.rb": 281.034084289,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 99.384373985,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 146.648856414,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 63.87743802999999,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 157.077619561,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 116.88328403300001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_policies_spec.rb": 85.77617316300001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 344.98750820000004,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 19.211998915,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb": 31.769016921,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 259.54058256400003,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb": 10.309449618,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 4.82132239,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 14.228443253,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/cvs_dependency_scanning_spec.rb": 41.162109412,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_advanced_sast_spec.rb": 125.18084846,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/on_demand_dast_spec.rb": 102.143966936,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/secret_push_protection_spec.rb": 120.70963605899999,
|
||||
"qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb": 12.988818241,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/integrations/jira_issues_list_spec.rb": 58.641500210000004,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 38.7953483,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 58.647171594,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 38.314743697,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 15.599344136,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 15.578684487,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 208.45408638,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 41.716152389,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 7.349980713,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 29.777405177,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 15.870823101,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 42.317240466,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 17.819557861,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 31.566727651,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 19.621939433,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 27.649151253,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 20.289821851,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 22.098904729,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 45.135074855999996,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 26.535937236,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 12.082668441,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 23.925967119,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 44.864304038,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 19.451816733,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 40.885643196,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 43.112278353,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 61.966444696,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 22.993249051,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 107.20613004,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 42.0947788,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 79.86792701,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 28.892405643,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 152.138745253,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 23.68209186,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 169.404830288,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 133.96777923,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/prevent_forking_outside_group_spec.rb": 41.2943997,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 85.309391562,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 44.83058516,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 63.87730038,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 321.3376084590001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 174.94060700300003,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 157.489280599,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 126.869427415,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 126.336214883,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_for_merged_result_spec.rb": 45.157701197,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 65.373292235,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 17.547394519,
|
||||
"qa/specs/features/ee/browser_ui/9_tenant_scale/elasticsearch/elasticsearch_reindexing_spec.rb": 110.16069590800001,
|
||||
"qa/specs/features/ee/browser_ui/9_tenant_scale/group/share_group_with_group_spec.rb": 21.356710449
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/create_group_with_mattermost_team_spec.rb": 5.987978583,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/group_member_access_request_spec.rb": 57.246668183000004,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/group/transfer_project_spec.rb": 28.280268834,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/add_project_member_spec.rb": 31.780745051,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/create_project_badge_spec.rb": 26.777403345,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/create_project_spec.rb": 56.256326088,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/dashboard_images_spec.rb": 11.742786971000001,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/invite_group_to_project_spec.rb": 48.748845588,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/project_owner_permissions_spec.rb": 149.051296687,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/project/view_project_activity_spec.rb": 28.05712424,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/follow_user_activity_spec.rb": 31.198744317,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/parent_group_access_termination_spec.rb": 22.908094528,
|
||||
"qa/specs/features/browser_ui/9_tenant_scale/user/user_inherited_access_spec.rb": 23.664832740999998,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 45.694278522,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 25.447950898000002,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 73.251858542,
|
||||
"qa/specs/features/ee/api/1_manage/import/import_github_repo_spec.rb": 57.19293936,
|
||||
"qa/specs/features/ee/api/1_manage/integrations/group_webhook_events_spec.rb": 6.68710722,
|
||||
"qa/specs/features/ee/api/1_manage/migration/gitlab_migration_group_spec.rb": 72.219462862,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 59.702647653999996,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 42.72711256800001,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/advanced_global_advanced_syntax_search_spec.rb": 87.817043927,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/elasticsearch_api_spec.rb": 36.310745342000004,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/commit_index/commit_index_spec.rb": 100.588870488,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/issues_index/issue_index_spec.rb": 19.536915797,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/main_index/blob_index_spec.rb": 20.439565261,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/merge_request_index/merge_request_index_spec.rb": 59.197920659,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/notes_index/note_index_spec.rb": 73.771512509,
|
||||
"qa/specs/features/ee/api/9_tenant_scale/elasticsearch/index_tests/user_index/user_index_spec.rb": 40.680925353,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 101.22409271500001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 79.662903667,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb": 113.813310238,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 18.775476413,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 174.872336133,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 45.216024108,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 108.507464134,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_ldap_sync_spec.rb": 112.39822228399998,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 109.94306708199998,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group_pipeline_execution_policy_spec.rb": 234.487165319,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 130.770201629,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 157.561304826,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 59.975743636999994,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 202.804135304,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 141.810384486,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_policies_spec.rb": 89.99680464100001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 385.28500405799997,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 20.459961271,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb": 28.492389793,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 402.2089694900001,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb": 20.540360151,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 6.081126495,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 17.358051514,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/cvs_dependency_scanning_spec.rb": 72.98500408,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/on_demand_dast_spec.rb": 143.345512721,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/secret_push_protection_spec.rb": 91.26585501,
|
||||
"qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb": 10.269040891,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/integrations/jira_issues_list_spec.rb": 61.9165773,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 80.390964641,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 65.521365178,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 42.192420010999996,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 22.278444452,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 17.130871428,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 206.29039720699998,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 44.791174013,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 10.236663233,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 34.580014542,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 9.37836252,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 26.474749558,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 27.173981141,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 33.944733478,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 15.361843155,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 25.387587103,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 17.323462934,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 27.294679076,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 41.109234448,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 26.964102879,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 19.494462026,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 13.801373899,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 25.73753042,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 13.644869101,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 44.496911121000004,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 39.641629496,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 44.079304436,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 25.876180949,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 94.024174037,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 31.8476772,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 47.59685267,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 27.825299577,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 137.34111622799998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 29.259726312,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 94.80818762300001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 180.399954169,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/prevent_forking_outside_group_spec.rb": 42.672418057,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 80.48694758100001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 37.078803079,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 57.964661088,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 303.83190287,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 253.695409001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 170.830466145,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 112.459076308,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 180.83007212,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_for_merged_result_spec.rb": 29.78252656,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 56.542371793,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 21.442962804,
|
||||
"qa/specs/features/ee/browser_ui/9_tenant_scale/elasticsearch/elasticsearch_reindexing_spec.rb": 151.28373901700002,
|
||||
"qa/specs/features/ee/browser_ui/9_tenant_scale/group/share_group_with_group_spec.rb": 36.903485776
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,29 @@
|
|||
module RuboCop
|
||||
module Cop
|
||||
module API
|
||||
# This cop checks that APIs subclass API::Base.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# module API
|
||||
# class Projects < Grape::API
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# module API
|
||||
# class Projects < Grape::API::Instance
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# module API
|
||||
# class Projects < ::API::Base
|
||||
# end
|
||||
# end
|
||||
class Base < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
||||
# This cop checks that APIs subclass API::Base.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# module API
|
||||
# class Projects < Grape::API
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# module API
|
||||
# class Projects < Grape::API::Instance
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# module API
|
||||
# class Projects < ::API::Base
|
||||
# end
|
||||
# end
|
||||
MSG = 'Inherit from ::API::Base instead of Grape::API::Instance or Grape::API. ' \
|
||||
'For more details check https://gitlab.com/gitlab-org/gitlab/-/issues/215230.'
|
||||
|
||||
|
|
|
|||
|
|
@ -5,29 +5,28 @@ require_relative '../../code_reuse_helpers'
|
|||
module RuboCop
|
||||
module Cop
|
||||
module API
|
||||
# This cop checks that `allow_access_with_scope` is called only at the class level.
|
||||
# This is because `allow_access_with_scope` aggregates scopes for each call in a class.
|
||||
# Calling `allow_access_with_scope` within a `namespace` or an alias method such as
|
||||
# `resource`, `resources`, `segment` or `group` may mislead developers to think the scope
|
||||
# would be only allowed within given namespace which is not the case.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class MyClass < ::API::Base
|
||||
# include APIGuard
|
||||
# namespace 'my_namespace' do
|
||||
# resource :my_resource do
|
||||
# allow_access_with_scope :ai_workflows
|
||||
#
|
||||
# # good
|
||||
# class MyClass < ::API::Base
|
||||
# include APIGuard
|
||||
# allow_access_with_scope :ai_workflows
|
||||
class ClassLevelAllowAccessWithScope < RuboCop::Cop::Base
|
||||
include CodeReuseHelpers
|
||||
|
||||
# This cop checks that `allow_access_with_scope` is called only at the class level.
|
||||
# This is because `allow_access_with_scope` aggregates scopes for each call in a class.
|
||||
# Calling `allow_access_with_scope` within a `namespace` or an alias method such as
|
||||
# `resource`, `resources`, `segment` or `group` may mislead developers to think the scope
|
||||
# would be only allowed within given namespace which is not the case.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class MyClass < ::API::Base
|
||||
# include APIGuard
|
||||
# namespace 'my_namespace' do
|
||||
# resource :my_resource do
|
||||
# allow_access_with_scope :ai_workflows
|
||||
#
|
||||
# # good
|
||||
# class MyClass < ::API::Base
|
||||
# include APIGuard
|
||||
# allow_access_with_scope :ai_workflows
|
||||
#
|
||||
MSG = '`allow_access_with_scope` should only be called on class-level and not within a namespace.'
|
||||
|
||||
# In Grape::DSL::Routing::ClassMethods
|
||||
|
|
|
|||
|
|
@ -5,23 +5,22 @@ require_relative '../../code_reuse_helpers'
|
|||
module RuboCop
|
||||
module Cop
|
||||
module API
|
||||
# This cop checks that API detail entries use Strings
|
||||
#
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/379037
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# detail ['Foo bar baz bat', 'http://example.com']
|
||||
#
|
||||
# # good
|
||||
# detail 'Foo bar baz bat. http://example.com'
|
||||
#
|
||||
# end
|
||||
class EnsureStringDetail < RuboCop::Cop::Base
|
||||
include CodeReuseHelpers
|
||||
|
||||
# This cop checks that API detail entries use Strings
|
||||
#
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/379037
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# detail ['Foo bar baz bat', 'http://example.com']
|
||||
#
|
||||
# # good
|
||||
# detail 'Foo bar baz bat. http://example.com'
|
||||
#
|
||||
# end
|
||||
#
|
||||
MSG = 'Only String objects are permitted in API detail field.'
|
||||
|
||||
def_node_matcher :detail_in_desc, <<~PATTERN
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@
|
|||
module RuboCop
|
||||
module Cop
|
||||
module API
|
||||
# This cop checks that Grape API parameters using an Array type
|
||||
# implement a coerce_with method:
|
||||
#
|
||||
# https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# requires :values, type: Array[String]
|
||||
#
|
||||
# # good
|
||||
# requires :values, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce
|
||||
#
|
||||
# end
|
||||
class GrapeArrayMissingCoerce < RuboCop::Cop::Base
|
||||
# This cop checks that Grape API parameters using an Array type
|
||||
# implement a coerce_with method:
|
||||
#
|
||||
# https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# requires :values, type: Array[String]
|
||||
#
|
||||
# # good
|
||||
# requires :values, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce
|
||||
#
|
||||
# end
|
||||
MSG = 'This Grape parameter defines an Array but is missing a coerce_with definition. ' \
|
||||
'For more details, see https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop-rspec'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Gitlab
|
||||
module Ai
|
||||
# This cop enforces the order of the ConfigFiles::Constants.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# [ConfigFiles::PythonPoetry, ConfigFiles::CConanPy, ConfigFiles::CConanTxt]
|
||||
# [ConfigFiles::PythonPoetry, ConfigFiles::PythonPoetryLock, ConfigFiles::RubyGemsLock]
|
||||
#
|
||||
# # good
|
||||
# [ConfigFiles::CConanPy, ConfigFiles::CConanTxt, ConfigFiles::PythonPoetry]
|
||||
# [ConfigFiles::PythonPoetryLock, ConfigFiles::PythonPoetry, ConfigFiles::RubyGemsLock]
|
||||
#
|
||||
class OrderConstants < RuboCop::Cop::Base
|
||||
MSG = 'Order lock files by language (alphabetically), then by precedence. ' \
|
||||
'Lock files should appear first before their non-lock file counterparts.'
|
||||
|
||||
# @!method config_file_classes(node)
|
||||
def_node_matcher :config_file_classes, <<~PATTERN
|
||||
$(
|
||||
casgn nil? :CONFIG_FILE_CLASSES (send $array ...)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
# @!method config_files_constants?(node)
|
||||
def_node_matcher :config_files_constants?, <<~PATTERN
|
||||
$(module
|
||||
(const nil? :ConfigFiles)
|
||||
(module
|
||||
(const nil? :Constants)
|
||||
...
|
||||
)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_casgn(node)
|
||||
# we want to make sure that we are running the cop only on
|
||||
# ConfigFiles::Constants::CONFIG_FILES_CONSTANTS
|
||||
return unless config_files_constants?(node.parent.parent)
|
||||
|
||||
_matcher, constants_array = config_file_classes(node)
|
||||
|
||||
constants_names = constants_array.child_nodes.map(&:source)
|
||||
|
||||
return if constants_names == sort_with_lock_priority(constants_names)
|
||||
|
||||
add_offense(node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sort_with_lock_priority(config_file_classes)
|
||||
base_classes = config_file_classes.group_by { |class_name| class_name.gsub("Lock", "") }
|
||||
|
||||
base_classes.each_value do |classes|
|
||||
classes.sort! do |a, b|
|
||||
if b.include?("Lock")
|
||||
1
|
||||
else
|
||||
(a.include?("Lock") ? -1 : (a <=> b))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sort the base names alphabetically and flatten the result
|
||||
base_classes.keys.sort.flat_map { |base_name| base_classes[base_name] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,18 +7,18 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# # bad
|
||||
#
|
||||
# Feature.get(:x).enable
|
||||
# Feature.get(:x).enable_percentage_of_time(100)
|
||||
# Feature.get(:x).remove
|
||||
# Feature.get(:x).enable
|
||||
# Feature.get(:x).enable_percentage_of_time(100)
|
||||
# Feature.get(:x).remove
|
||||
#
|
||||
# # good
|
||||
# # good
|
||||
#
|
||||
# stub_feature_flags(x: true)
|
||||
# Feature.enable(:x)
|
||||
# Feature.enable_percentage_of_time(:x, 100)
|
||||
# Feature.remove(:x)
|
||||
# stub_feature_flags(x: true)
|
||||
# Feature.enable(:x)
|
||||
# Feature.enable_percentage_of_time(:x, 100)
|
||||
# Feature.remove(:x)
|
||||
#
|
||||
class AvoidFeatureGet < RuboCop::Cop::Base
|
||||
MSG = 'Use `stub_feature_flags` method instead of `Feature.get`. ' \
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# # bad
|
||||
#
|
||||
# hash.keys.first
|
||||
# hash.values.first
|
||||
# hash.keys.first
|
||||
# hash.values.first
|
||||
#
|
||||
# # good
|
||||
# # good
|
||||
#
|
||||
# hash.each_key.first
|
||||
# hash.each_value.first
|
||||
# hash.each_key.first
|
||||
# hash.each_value.first
|
||||
#
|
||||
class KeysFirstAndValuesFirst < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
|
|
|||
|
|
@ -8,20 +8,20 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad, `conducts_electricity` returns a Rule object, not a boolean!
|
||||
# rule { conducts_electricity && batteries }.enable :light_bulb
|
||||
# # bad, `conducts_electricity` returns a Rule object, not a boolean!
|
||||
# rule { conducts_electricity && batteries }.enable :light_bulb
|
||||
#
|
||||
# # good
|
||||
# rule { conducts_electricity & batteries }.enable :light_bulb
|
||||
# # good
|
||||
# rule { conducts_electricity & batteries }.enable :light_bulb
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad, `conducts_electricity` returns a Rule object, so the ternary is always going to be true
|
||||
# rule { conducts_electricity ? can?(:magnetize) : batteries }.enable :motor
|
||||
# # bad, `conducts_electricity` returns a Rule object, so the ternary is always going to be true
|
||||
# rule { conducts_electricity ? can?(:magnetize) : batteries }.enable :motor
|
||||
#
|
||||
# # good
|
||||
# rule { conducts_electricity & can?(:magnetize) }.enable :motor
|
||||
# rule { ~conducts_electricity & batteries }.enable :motor
|
||||
# # good
|
||||
# rule { conducts_electricity & can?(:magnetize) }.enable :motor
|
||||
# rule { ~conducts_electricity & batteries }.enable :motor
|
||||
class PolicyRuleBoolean < RuboCop::Cop::Base
|
||||
def_node_search :has_and_operator?, <<~PATTERN
|
||||
(and ...)
|
||||
|
|
|
|||
|
|
@ -1,52 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop checks for missing GraphQL descriptions and enforces the description style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe because not all cases of "this" can be substituted with
|
||||
# "the". This will require a technical writer to assist with the alternative,
|
||||
# proper grammar that can be used for that particular GraphQL descriptions.
|
||||
#
|
||||
# @examples
|
||||
#
|
||||
# # bad
|
||||
# class AwfulType
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
#
|
||||
# class TerribleType
|
||||
# argument :some_argument, GraphQL::Types::String
|
||||
# end
|
||||
#
|
||||
# class UngoodType
|
||||
# field :some_argument,
|
||||
# GraphQL::Types::String,
|
||||
# description: "A description that does not end in a period"
|
||||
# end
|
||||
#
|
||||
# class BadEnum
|
||||
# value "some_value"
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatType
|
||||
# argument :some_field,
|
||||
# GraphQL::Types::String,
|
||||
# description: "Well described - a superb description."
|
||||
#
|
||||
# field :some_field,
|
||||
# GraphQL::Types::String,
|
||||
# description: "Thorough and compelling description."
|
||||
# end
|
||||
#
|
||||
# class GoodEnum
|
||||
# value "some_value", "Good description."
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop checks for missing GraphQL descriptions and enforces the description style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide
|
||||
#
|
||||
# @note
|
||||
# This cop is unsafe because not all cases of "this" can be substituted with
|
||||
# "the". This will require a technical writer to assist with the alternative,
|
||||
# proper grammar that can be used for that particular GraphQL descriptions.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulType
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
#
|
||||
# class TerribleType
|
||||
# argument :some_argument, GraphQL::Types::String
|
||||
# end
|
||||
#
|
||||
# class UngoodType
|
||||
# field :some_argument,
|
||||
# GraphQL::Types::String,
|
||||
# description: "A description that does not end in a period"
|
||||
# end
|
||||
#
|
||||
# class BadEnum
|
||||
# value "some_value"
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatType
|
||||
# argument :some_field,
|
||||
# GraphQL::Types::String,
|
||||
# description: "Well described - a superb description."
|
||||
#
|
||||
# field :some_field,
|
||||
# GraphQL::Types::String,
|
||||
# description: "Thorough and compelling description."
|
||||
# end
|
||||
#
|
||||
# class GoodEnum
|
||||
# value "some_value", "Good description."
|
||||
# end
|
||||
class Descriptions < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop enforces the enum naming conventions from the enum style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class FooBar < BaseEnum
|
||||
# value 'FOO'
|
||||
# end
|
||||
#
|
||||
# class SubparEnum < BaseEnum
|
||||
# end
|
||||
#
|
||||
# class UngoodEnum < BaseEnum
|
||||
# graphql_name 'UngoodEnum'
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
#
|
||||
# class GreatEnum < BaseEnum
|
||||
# graphql_name 'Great'
|
||||
#
|
||||
# value 'BAR'
|
||||
# end
|
||||
#
|
||||
# class NiceEnum < BaseEnum
|
||||
# declarative_enum NiceDeclarativeEnum
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop enforces the enum naming conventions from the enum style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class FooBar < BaseEnum
|
||||
# value 'FOO'
|
||||
# end
|
||||
#
|
||||
# class SubparEnum < BaseEnum
|
||||
# end
|
||||
#
|
||||
# class UngoodEnum < BaseEnum
|
||||
# graphql_name 'UngoodEnum'
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
#
|
||||
# class GreatEnum < BaseEnum
|
||||
# graphql_name 'Great'
|
||||
#
|
||||
# value 'BAR'
|
||||
# end
|
||||
#
|
||||
# class NiceEnum < BaseEnum
|
||||
# declarative_enum NiceDeclarativeEnum
|
||||
# end
|
||||
class EnumNames < RuboCop::Cop::Base
|
||||
SEE_SG_MSG = "See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums"
|
||||
CLASS_NAME_SUFFIX_MSG = "Enum class names must end with `Enum`. #{SEE_SG_MSG}".freeze
|
||||
|
|
|
|||
|
|
@ -1,43 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop enforces the enum value conventions from the enum style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class BadEnum < BaseEnum
|
||||
# graphql_name 'Bad'
|
||||
#
|
||||
# value 'foo'
|
||||
# end
|
||||
#
|
||||
# class UngoodEnum < BaseEnum
|
||||
# graphql_name 'Ungood'
|
||||
#
|
||||
# ['bar'].each do |val|
|
||||
# value val
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GoodEnum < BaseEnum
|
||||
# graphql_name 'Good'
|
||||
#
|
||||
# value 'FOO'
|
||||
# end
|
||||
#
|
||||
# class GreatEnum < BaseEnum
|
||||
# graphql_name 'Great'
|
||||
#
|
||||
# ['bar'].each do |val|
|
||||
# value val.upcase
|
||||
# end
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop enforces the enum value conventions from the enum style guide:
|
||||
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class BadEnum < BaseEnum
|
||||
# graphql_name 'Bad'
|
||||
#
|
||||
# value 'foo'
|
||||
# end
|
||||
#
|
||||
# class UngoodEnum < BaseEnum
|
||||
# graphql_name 'Ungood'
|
||||
#
|
||||
# ['bar'].each do |val|
|
||||
# value val
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GoodEnum < BaseEnum
|
||||
# graphql_name 'Good'
|
||||
#
|
||||
# value 'FOO'
|
||||
# end
|
||||
#
|
||||
# class GreatEnum < BaseEnum
|
||||
# graphql_name 'Great'
|
||||
#
|
||||
# ['bar'].each do |val|
|
||||
# value val.upcase
|
||||
# end
|
||||
# end
|
||||
class EnumValues < RuboCop::Cop::Base
|
||||
MSG = "Enum values must either be an uppercase string literal or uppercased with the `upcase` method. " \
|
||||
"See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums"
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop ensures that if a class uses `graphql_name`, then
|
||||
# it's the first line of the class
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::Types::JSON
|
||||
# graphql_name 'AwfulClass'
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# graphql_name 'AwfulClass'
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop ensures that if a class uses `graphql_name`, then
|
||||
# it's the first line of the class
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::Types::JSON
|
||||
# graphql_name 'AwfulClass'
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# graphql_name 'AwfulClass'
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
class GraphqlNamePosition < RuboCop::Cop::Base
|
||||
MSG = '`graphql_name` should be the first line of the class: '\
|
||||
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#naming-conventions'
|
||||
|
|
|
|||
|
|
@ -1,23 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop checks for use of GraphQL::Types::JSON types in GraphQL fields
|
||||
# and arguments.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::Types::JSON
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop checks for use of GraphQL::Types::JSON types in GraphQL fields
|
||||
# and arguments.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::Types::JSON
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
class JSONType < RuboCop::Cop::Base
|
||||
MSG = 'Avoid using GraphQL::Types::JSON. See: ' \
|
||||
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop checks for use of older GraphQL types in GraphQL fields
|
||||
# and arguments.
|
||||
# GraphQL::ID_TYPE, GraphQL::INT_TYPE, GraphQL::STRING_TYPE, GraphQL::BOOLEAN_TYPE
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::STRING_TYPE
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop checks for use of older GraphQL types in GraphQL fields
|
||||
# and arguments.
|
||||
# GraphQL::ID_TYPE, GraphQL::INT_TYPE, GraphQL::STRING_TYPE, GraphQL::BOOLEAN_TYPE
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class AwfulClass
|
||||
# field :some_field, GraphQL::STRING_TYPE
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# class GreatClass
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
class OldTypes < RuboCop::Cop::Base
|
||||
MSG_ID = 'Avoid using GraphQL::ID_TYPE. Use GraphQL::Types::ID instead'
|
||||
MSG_INT = 'Avoid using GraphQL::INT_TYPE. Use GraphQL::Types::Int instead'
|
||||
|
|
|
|||
|
|
@ -1,28 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This cop checks for missing GraphQL type annotations on resolvers
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# module Resolvers
|
||||
# class NoTypeResolver < BaseResolver
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# module Resolvers
|
||||
# class WithTypeResolver < BaseResolver
|
||||
# type MyType, null: true
|
||||
#
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
# end
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Graphql
|
||||
# This cop checks for missing GraphQL type annotations on resolvers
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# module Resolvers
|
||||
# class NoTypeResolver < BaseResolver
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# module Resolvers
|
||||
# class WithTypeResolver < BaseResolver
|
||||
# type MyType, null: true
|
||||
#
|
||||
# field :some_field, GraphQL::Types::String
|
||||
# end
|
||||
# end
|
||||
class ResolverType < RuboCop::Cop::Base
|
||||
MSG = 'Missing type annotation: Please add `type` DSL method call. ' \
|
||||
'e.g: type UserType.connection_type, null: true'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module RuboCop
|
|||
module Migration
|
||||
# Cop that prevents introducing `encrypted_*` columns (used by the `attr_encrypted` gem).
|
||||
#
|
||||
# @examples
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class CreateAuditEventsInstanceAmazonS3Configurations < ActiveRecord::Migration[6.0]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module RuboCop
|
|||
# This cop was introduced to clarify the need for disable_ddl_transaction!
|
||||
# and to avoid bike-shedding and review back-and-forth.
|
||||
#
|
||||
# @examples
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# class SomeMigration < Gitlab::Database::Migration[2.1]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# # bad
|
||||
#
|
||||
# page.has_css?('[data-testid="begin-commit-button"]') ? find('[data-testid="begin-commit-button"]').click : nil
|
||||
#
|
||||
|
|
|
|||
|
|
@ -10,25 +10,25 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# it 'expects a snowplow event' do
|
||||
# expect(Gitlab::Tracking).to receive(:event).with("Category", "action", ...)
|
||||
# end
|
||||
# # bad
|
||||
# it 'expects a snowplow event' do
|
||||
# expect(Gitlab::Tracking).to receive(:event).with("Category", "action", ...)
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# it 'expects a snowplow event', :snowplow do
|
||||
# expect_snowplow_event(category: "Category", action: "action", ...)
|
||||
# end
|
||||
# # good
|
||||
# it 'expects a snowplow event', :snowplow do
|
||||
# expect_snowplow_event(category: "Category", action: "action", ...)
|
||||
# end
|
||||
#
|
||||
# # bad
|
||||
# it 'does not expect a snowplow event' do
|
||||
# expect(Gitlab::Tracking).not_to receive(:event)
|
||||
# end
|
||||
# # bad
|
||||
# it 'does not expect a snowplow event' do
|
||||
# expect(Gitlab::Tracking).not_to receive(:event)
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# it 'does not expect a snowplow event', :snowplow do
|
||||
# expect_no_snowplow_event
|
||||
# end
|
||||
# # good
|
||||
# it 'does not expect a snowplow event', :snowplow do
|
||||
# expect_no_snowplow_event
|
||||
# end
|
||||
class ExpectGitlabTracking < RuboCop::Cop::Base
|
||||
MSG = 'Do not expect directly on `Gitlab::Tracking#event`, add the `snowplow` annotation and use ' \
|
||||
'`expect_snowplow_event` instead. ' \
|
||||
|
|
|
|||
|
|
@ -13,37 +13,37 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# Context:
|
||||
# Context:
|
||||
#
|
||||
# Factory.define do
|
||||
# factory :project, class: 'Project'
|
||||
# # EXAMPLE below
|
||||
# Factory.define do
|
||||
# factory :project, class: 'Project'
|
||||
# # EXAMPLE below
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # bad
|
||||
# creator { create(:user) }
|
||||
# creator { create(:user, :admin) }
|
||||
# creator { build(:user) }
|
||||
# creator { FactoryBot.build(:user) }
|
||||
# creator { ::FactoryBot.build(:user) }
|
||||
# add_attribute(:creator) { build(:user) }
|
||||
#
|
||||
# # good
|
||||
# creator { association(:user) }
|
||||
# creator { association(:user, :admin) }
|
||||
# add_attribute(:creator) { association(:user) }
|
||||
#
|
||||
# # Accepted
|
||||
# after(:build) do |instance|
|
||||
# instance.creator = create(:user)
|
||||
# end
|
||||
#
|
||||
# # bad
|
||||
# creator { create(:user) }
|
||||
# creator { create(:user, :admin) }
|
||||
# creator { build(:user) }
|
||||
# creator { FactoryBot.build(:user) }
|
||||
# creator { ::FactoryBot.build(:user) }
|
||||
# add_attribute(:creator) { build(:user) }
|
||||
# initialize_with do
|
||||
# create(:project)
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# creator { association(:user) }
|
||||
# creator { association(:user, :admin) }
|
||||
# add_attribute(:creator) { association(:user) }
|
||||
#
|
||||
# # Accepted
|
||||
# after(:build) do |instance|
|
||||
# instance.creator = create(:user)
|
||||
# end
|
||||
#
|
||||
# initialize_with do
|
||||
# create(:project)
|
||||
# end
|
||||
#
|
||||
# creator_id { create(:user).id }
|
||||
# creator_id { create(:user).id }
|
||||
#
|
||||
class InlineAssociation < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# expect(response).to have_http_status(200)
|
||||
# expect(response).to have_http_status(:ok)
|
||||
# expect(response).to have_gitlab_http_status(200)
|
||||
# expect(response.status).to eq(200)
|
||||
# expect(response.status).not_to eq(200)
|
||||
# # bad
|
||||
# expect(response).to have_http_status(200)
|
||||
# expect(response).to have_http_status(:ok)
|
||||
# expect(response).to have_gitlab_http_status(200)
|
||||
# expect(response.status).to eq(200)
|
||||
# expect(response.status).not_to eq(200)
|
||||
#
|
||||
# # good
|
||||
# expect(response).to have_gitlab_http_status(:ok)
|
||||
# expect(response).not_to have_gitlab_http_status(:ok)
|
||||
# # good
|
||||
# expect(response).to have_gitlab_http_status(:ok)
|
||||
# expect(response).not_to have_gitlab_http_status(:ok)
|
||||
#
|
||||
class HaveGitlabHttpStatus < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# histogram(Issue, buckets: 1..100)
|
||||
# histogram(User.active, buckets: 1..100)
|
||||
# # bad
|
||||
# histogram(Issue, buckets: 1..100)
|
||||
# histogram(User.active, buckets: 1..100)
|
||||
class HistogramWithLargeTable < RuboCop::Cop::Base
|
||||
include UsageDataHelpers
|
||||
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@ require_relative '../../usage_data_helpers'
|
|||
module RuboCop
|
||||
module Cop
|
||||
module UsageData
|
||||
# This cop checks that batch count and distinct_count are used in usage_data.rb files in metrics based on ActiveRecord models.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# Issue.count
|
||||
# List.assignee.count
|
||||
# ::Ci::Pipeline.auto_devops_source.count
|
||||
# ZoomMeeting.distinct.count(:issue_id)
|
||||
#
|
||||
# # Good
|
||||
# count(Issue)
|
||||
# count(List.assignee)
|
||||
# count(::Ci::Pipeline.auto_devops_source)
|
||||
# distinct_count(ZoomMeeting, :issue_id)
|
||||
class LargeTable < RuboCop::Cop::Base
|
||||
include UsageDataHelpers
|
||||
|
||||
# This cop checks that batch count and distinct_count are used in usage_data.rb files in metrics based on ActiveRecord models.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# Issue.count
|
||||
# List.assignee.count
|
||||
# ::Ci::Pipeline.auto_devops_source.count
|
||||
# ZoomMeeting.distinct.count(:issue_id)
|
||||
#
|
||||
# # Good
|
||||
# count(Issue)
|
||||
# count(List.assignee)
|
||||
# count(::Ci::Pipeline.auto_devops_source)
|
||||
# distinct_count(ZoomMeeting, :issue_id)
|
||||
MSG = 'Use one of the %{count_methods} methods for counting on %{class_name}'
|
||||
|
||||
# Match one level const as Issue, Gitlab
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ describe('Packages Protection Rule Form', () => {
|
|||
projectPath: 'path',
|
||||
glFeatures: {
|
||||
packagesProtectedPackagesConan: true,
|
||||
packagesProtectedPackagesMaven: true,
|
||||
packagesProtectedPackagesDelete: true,
|
||||
},
|
||||
};
|
||||
|
|
@ -114,23 +113,6 @@ describe('Packages Protection Rule Form', () => {
|
|||
expect(packageTypeSelectOptions()).toEqual(['MAVEN', 'NPM', 'PYPI']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature flag packagesProtectedPackagesMaven is disabled', () => {
|
||||
it('contains available options without option "MAVEN"', () => {
|
||||
mountComponent({
|
||||
provide: {
|
||||
...defaultProvidedValues,
|
||||
glFeatures: {
|
||||
...defaultProvidedValues.glFeatures,
|
||||
packagesProtectedPackagesMaven: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(findPackageTypeSelect().exists()).toBe(true);
|
||||
expect(packageTypeSelectOptions()).toEqual(['CONAN', 'NPM', 'PYPI']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form field "minimumAccessLevelForPushSelect"', () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { WIDGET_TYPE_HIERARCHY } from '~/work_items/constants';
|
||||
import { WIDGET_TYPE_HIERARCHY, WIDGET_TYPE_CUSTOM_FIELDS } from '~/work_items/constants';
|
||||
import {
|
||||
addHierarchyChild,
|
||||
removeHierarchyChild,
|
||||
|
|
@ -359,6 +359,11 @@ describe('work items graphql cache utils', () => {
|
|||
timelogs: { __typename: 'WorkItemTimelogConnection', nodes: [] },
|
||||
totalTimeSpent: 0,
|
||||
},
|
||||
{
|
||||
__typename: 'WorkItemWidgetCustomFields',
|
||||
type: WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
customFieldValues: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -447,6 +452,10 @@ describe('work items graphql cache utils', () => {
|
|||
editable: false,
|
||||
rollUp: true,
|
||||
},
|
||||
{
|
||||
__typename: 'WorkItemWidgetDefinitionCustomFields',
|
||||
type: WIDGET_TYPE_CUSTOM_FIELDS,
|
||||
},
|
||||
],
|
||||
'EPIC',
|
||||
'gid://gitlab/WorkItems::Type/8 ',
|
||||
|
|
|
|||
|
|
@ -5885,6 +5885,7 @@ export const createWorkItemQueryResponse = {
|
|||
},
|
||||
__typename: 'WorkItemWidgetWeight',
|
||||
},
|
||||
customFieldsWidgetResponseFactory(),
|
||||
],
|
||||
__typename: 'WorkItem',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1329,6 +1329,28 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
|
|||
create(:note, :internal, noteable: merge_request, note: issue_referenced_in_internal_mr_note.to_reference)
|
||||
end
|
||||
|
||||
context 'feature flag: more_commits_from_gitaly' do
|
||||
let_it_be(:user) { create(:user, guest_of: project) }
|
||||
|
||||
it 'loads commits from Gitaly' do
|
||||
expect(merge_request).to receive(:commits).with(load_from_gitaly: true).and_call_original
|
||||
|
||||
related_issues
|
||||
end
|
||||
|
||||
context 'when "more_commits_from_gitaly" is disabled' do
|
||||
before do
|
||||
stub_feature_flags(more_commits_from_gitaly: false)
|
||||
end
|
||||
|
||||
it 'loads commits from DB' do
|
||||
expect(merge_request).to receive(:commits).with(load_from_gitaly: false).and_call_original
|
||||
|
||||
related_issues
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for guest' do
|
||||
let_it_be(:user) { create(:user, guest_of: project) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1026,14 +1026,6 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
|
|||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(json_response['message']).to eq '403 Forbidden - Package protected.'
|
||||
end
|
||||
|
||||
context 'when feature flag :packages_protected_packages_maven is disabled' do
|
||||
before do
|
||||
stub_feature_flags(packages_protected_packages_maven: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'authorized package'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for personal access token' do
|
||||
|
|
@ -1415,14 +1407,6 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
|
|||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(json_response['message']).to eq '403 Forbidden - Package protected.'
|
||||
end
|
||||
|
||||
context 'when feature flag :packages_protected_packages_maven is disabled' do
|
||||
before do
|
||||
stub_feature_flags(packages_protected_packages_maven: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'package workhorse uploads'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for personal access token' do
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ RSpec.describe Projects::Settings::PackagesAndRegistriesController, feature_cate
|
|||
end
|
||||
|
||||
it_behaves_like 'pushed feature flag', :packages_protected_packages_conan
|
||||
it_behaves_like 'pushed feature flag', :packages_protected_packages_maven
|
||||
it_behaves_like 'pushed feature flag', :packages_protected_packages_delete
|
||||
it_behaves_like 'pushed feature flag', :container_registry_protected_tags
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require_relative '../../../../../rubocop/cop/gitlab/ai/order_constants'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gitlab::Ai::OrderConstants, feature_category: :dependency_management do
|
||||
context 'when not in the expected order' do
|
||||
it 'registers an offense when not in alphabetical order' do
|
||||
expect_offense(<<~RUBY)
|
||||
module Ai
|
||||
module Context
|
||||
module Dependencies
|
||||
module ConfigFiles
|
||||
module Constants
|
||||
CONFIG_FILE_CLASSES = [
|
||||
^^^^^^^^^^^^^^^^^^^^^^^ Order lock files by language (alphabetically), then by precedence. Lock files should appear first before their non-lock file counterparts.
|
||||
ConfigFiles::PythonPoetry,
|
||||
ConfigFiles::CConanPy,
|
||||
ConfigFiles::CConanTxt
|
||||
].freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when the lock file is before the non-lock file' do
|
||||
expect_offense(<<~RUBY)
|
||||
module Ai
|
||||
module Context
|
||||
module Dependencies
|
||||
module ConfigFiles
|
||||
module Constants
|
||||
CONFIG_FILE_CLASSES = [
|
||||
^^^^^^^^^^^^^^^^^^^^^^^ Order lock files by language (alphabetically), then by precedence. Lock files should appear first before their non-lock file counterparts.
|
||||
ConfigFiles::JavaMaven,
|
||||
ConfigFiles::JavascriptNpm,
|
||||
ConfigFiles::JavascriptNpmLock,
|
||||
].freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when the lock file is after the non-lock file and not in alphabetical order' do
|
||||
expect_offense(<<~RUBY)
|
||||
module Ai
|
||||
module Context
|
||||
module Dependencies
|
||||
module ConfigFiles
|
||||
module Constants
|
||||
CONFIG_FILE_CLASSES = [
|
||||
^^^^^^^^^^^^^^^^^^^^^^^ Order lock files by language (alphabetically), then by precedence. Lock files should appear first before their non-lock file counterparts.
|
||||
ConfigFiles::JavaMaven,
|
||||
ConfigFiles::KotlinGradle,
|
||||
ConfigFiles::JavascriptNpm,
|
||||
ConfigFiles::JavascriptNpmLock,
|
||||
ConfigFiles::PhpComposerLock,
|
||||
ConfigFiles::PhpComposer,
|
||||
].freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -131,14 +131,6 @@ RSpec.describe Packages::Maven::CreatePackageService, feature_category: :package
|
|||
.and not_change { Packages::Package.maven.count }
|
||||
.and not_change { Packages::PackageFile.count }
|
||||
end
|
||||
|
||||
context 'when feature flag :packages_protected_packages_maven is disabled' do
|
||||
before do
|
||||
stub_feature_flags(packages_protected_packages_maven: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'valid package'
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'an error service response for unauthorized' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue