Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c13c8ff01f
commit
aa720e5357
15
.rubocop.yml
15
.rubocop.yml
|
|
@ -595,6 +595,21 @@ Gitlab/BoundedContexts:
|
|||
- 'lib/**/*'
|
||||
- 'ee/lib/**/*'
|
||||
|
||||
Gitlab/HardDeleteCalls:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'ee/spec/**/*'
|
||||
- 'ee/db/fixtures/**/*'
|
||||
- 'lib/tasks/**/*.rake'
|
||||
- 'ee/lib/tasks/**/*.rake'
|
||||
- 'app/services/groups/destroy_service.rb'
|
||||
- 'app/services/projects/destroy_service.rb'
|
||||
- 'app/workers/group_destroy_worker.rb'
|
||||
- 'app/workers/project_destroy_worker.rb'
|
||||
- 'ee/app/services/namespaces/groups/adjourned_deletion_service.rb'
|
||||
- 'ee/app/services/projects/adjourned_deletion_service.rb'
|
||||
|
||||
Gitlab/PolicyRuleBoolean:
|
||||
Enabled: true
|
||||
Include:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
Gitlab/HardDeleteCalls:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/controllers/admin/groups_controller.rb'
|
||||
- 'app/controllers/admin/projects_controller.rb'
|
||||
- 'app/controllers/groups_controller.rb'
|
||||
- 'app/controllers/organizations/groups_controller.rb'
|
||||
- 'app/controllers/projects_controller.rb'
|
||||
- 'app/services/groups/mark_for_deletion_service.rb'
|
||||
- 'app/services/projects/mark_for_deletion_service.rb'
|
||||
- 'app/services/projects/overwrite_project_service.rb'
|
||||
- 'app/services/users/destroy_service.rb'
|
||||
- 'app/workers/anti_abuse/banned_user_project_deletion_worker.rb'
|
||||
- 'app/workers/projects/inactive_projects_deletion_cron_worker.rb'
|
||||
- 'lib/api/groups.rb'
|
||||
- 'lib/api/projects.rb'
|
||||
- 'lib/gitlab/background_migration/delete_orphaned_groups.rb'
|
||||
|
|
@ -53,7 +53,6 @@ Gitlab/Rails/AttrEncrypted:
|
|||
- 'ee/app/models/dependency_proxy/packages/setting.rb'
|
||||
- 'ee/app/models/geo_node.rb'
|
||||
- 'ee/app/models/merge_requests/external_status_check.rb'
|
||||
- 'ee/app/models/remote_development/workspace_variable.rb'
|
||||
- 'ee/app/models/status_page/project_setting.rb'
|
||||
- 'ee/app/models/system_access/group_microsoft_application.rb'
|
||||
- 'ee/app/models/system_access/group_microsoft_graph_access_token.rb'
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ Gitlab/ServiceResponse:
|
|||
- 'app/services/packages/debian/create_distribution_service.rb'
|
||||
- 'app/services/packages/mark_package_for_destruction_service.rb'
|
||||
- 'app/services/packages/rubygems/dependency_resolver_service.rb'
|
||||
- 'app/services/snippets/base_service.rb'
|
||||
- 'app/services/timelogs/base_service.rb'
|
||||
- 'app/services/work_items/create_and_link_service.rb'
|
||||
- 'app/services/work_items/create_from_task_service.rb'
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -522,7 +522,7 @@ group :development, :test do
|
|||
gem 'spring-commands-rspec', '~> 1.0.4', feature_category: :shared
|
||||
|
||||
gem 'gitlab-styles', '~> 13.1.0', feature_category: :tooling, require: false
|
||||
gem 'haml_lint', '~> 0.58', feature_category: :tooling
|
||||
gem 'haml_lint', '~> 0.58', feature_category: :tooling, require: false
|
||||
|
||||
# Benchmarking & profiling
|
||||
gem 'benchmark-ips', '~> 2.14.0', require: false, feature_category: :shared
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlSkeletonLoader } from '@gitlab/ui';
|
||||
import { getSkeletonRectProps } from './utils';
|
||||
|
||||
export default {
|
||||
name: 'InputsTableSkeletonLoader',
|
||||
|
|
@ -7,16 +8,7 @@ export default {
|
|||
GlSkeletonLoader,
|
||||
},
|
||||
methods: {
|
||||
getSkeletonRectProps(columnIndex, rowIndex) {
|
||||
return {
|
||||
x: `${columnIndex * 25.5}%`,
|
||||
y: rowIndex * 10,
|
||||
width: '23%',
|
||||
height: 6,
|
||||
rx: 2,
|
||||
ry: 2,
|
||||
};
|
||||
},
|
||||
getSkeletonRectProps,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
savedInputs: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ['update-inputs'],
|
||||
data() {
|
||||
|
|
@ -36,8 +41,20 @@ export default {
|
|||
ref: this.queryRef,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.projectPath;
|
||||
},
|
||||
update({ project }) {
|
||||
return project?.ciPipelineCreationInputs || [];
|
||||
const queryInputs = project?.ciPipelineCreationInputs || [];
|
||||
const savedInputsMap = Object.fromEntries(
|
||||
this.savedInputs.map(({ name, value }) => [name, value]),
|
||||
);
|
||||
|
||||
// if there are any saved inputs, overwrite the values
|
||||
return queryInputs.map((input) => ({
|
||||
...input,
|
||||
default: savedInputsMap[input.name] ?? input.default,
|
||||
}));
|
||||
},
|
||||
error(error) {
|
||||
createAlert({
|
||||
|
|
@ -83,7 +100,7 @@ export default {
|
|||
<template v-else>
|
||||
<pipeline-inputs-table v-if="hasInputs" :inputs="inputs" @update="handleInputsUpdated" />
|
||||
<div v-else class="gl-flex gl-justify-center gl-text-subtle">
|
||||
{{ __('There are no inputs for this configuration.') }}
|
||||
{{ s__('Pipelines|There are no inputs for this configuration.') }}
|
||||
</div>
|
||||
</template>
|
||||
</crud-component>
|
||||
|
|
|
|||
|
|
@ -52,9 +52,8 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Using inline style for max-height as gl-max-h-* utilities are insufficient for our needs.
|
||||
Will replace with pagination or better utilities in the future. -->
|
||||
<div class="gl-overflow-y-auto" style="max-height: 50rem">
|
||||
<!-- Will replace with pagination in the future. -->
|
||||
<div class="gl-overflow-y-auto md:gl-max-h-[50rem]">
|
||||
<gl-table-lite
|
||||
class="gl-mb-0"
|
||||
:items="inputs"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Generates skeleton rect props for the skeleton loader based on column and row indices
|
||||
*
|
||||
* @param {Number} columnIndex - The column index (0-based)
|
||||
* @param {Number} rowIndex - The row index (0-based)
|
||||
* @returns {Object} - The props for the skeleton rect
|
||||
*/
|
||||
export const getSkeletonRectProps = (columnIndex, rowIndex) => {
|
||||
return {
|
||||
x: `${columnIndex * 25.5}%`,
|
||||
y: rowIndex * 10,
|
||||
width: '23%',
|
||||
height: 6,
|
||||
rx: 2,
|
||||
ry: 2,
|
||||
};
|
||||
};
|
||||
|
|
@ -12,8 +12,10 @@ import { createAlert } from '~/alert';
|
|||
import { visitUrl, queryToObject } from '~/lib/utils/url_utility';
|
||||
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
|
||||
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
|
||||
import PipelineInputsForm from '~/ci/common/pipeline_inputs/pipeline_inputs_form.vue';
|
||||
import PipelineVariablesPermissionsMixin from '~/ci/mixins/pipeline_variables_permissions_mixin';
|
||||
import createPipelineScheduleMutation from '../graphql/mutations/create_pipeline_schedule.mutation.graphql';
|
||||
import updatePipelineScheduleMutation from '../graphql/mutations/update_pipeline_schedule.mutation.graphql';
|
||||
|
|
@ -30,12 +32,13 @@ export default {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlLoadingIcon,
|
||||
IntervalPatternInput,
|
||||
PipelineInputsForm,
|
||||
PipelineVariablesFormGroup,
|
||||
RefSelector,
|
||||
TimezoneDropdown,
|
||||
IntervalPatternInput,
|
||||
PipelineVariablesFormGroup,
|
||||
},
|
||||
mixins: [PipelineVariablesPermissionsMixin],
|
||||
mixins: [glFeatureFlagsMixin(), PipelineVariablesPermissionsMixin],
|
||||
inject: [
|
||||
'projectPath',
|
||||
'projectId',
|
||||
|
|
@ -86,6 +89,7 @@ export default {
|
|||
this.description = schedule.description;
|
||||
this.cron = schedule.cron;
|
||||
this.cronTimezone = schedule.cronTimezone;
|
||||
this.savedInputs = schedule.inputs?.nodes || [];
|
||||
this.scheduleRef = schedule.ref || this.defaultBranch;
|
||||
this.variables = variables.map((variable) => {
|
||||
return {
|
||||
|
|
@ -109,14 +113,16 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
cron: '',
|
||||
description: '',
|
||||
scheduleRef: this.defaultBranch,
|
||||
activated: true,
|
||||
cron: '',
|
||||
cronTimezone: '',
|
||||
variables: [],
|
||||
description: '',
|
||||
pipelineInputs: [],
|
||||
savedInputs: [],
|
||||
schedule: {},
|
||||
scheduleRef: this.defaultBranch,
|
||||
updatedVariables: [],
|
||||
variables: [],
|
||||
};
|
||||
},
|
||||
i18n: {
|
||||
|
|
@ -154,6 +160,9 @@ export default {
|
|||
filledVariables() {
|
||||
return this.updatedVariables.filter((variable) => variable.key !== '' && !variable.empty);
|
||||
},
|
||||
isPipelineInputsFeatureAvailable() {
|
||||
return this.glFeatures.ciInputsForPipelines;
|
||||
},
|
||||
preparedVariablesUpdate() {
|
||||
return this.filledVariables.map((variable) => {
|
||||
return {
|
||||
|
|
@ -203,6 +212,7 @@ export default {
|
|||
variables: this.preparedVariablesCreate,
|
||||
active: this.activated,
|
||||
projectPath: this.projectPath,
|
||||
...(this.isPipelineInputsFeatureAvailable && { inputs: this.pipelineInputs }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -233,6 +243,7 @@ export default {
|
|||
ref: this.scheduleRef,
|
||||
variables: this.preparedVariablesUpdate,
|
||||
active: this.activated,
|
||||
...(this.isPipelineInputsFeatureAvailable && { inputs: this.pipelineInputs }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -311,6 +322,14 @@ export default {
|
|||
class="gl-w-full"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<!--Pipeline inputs-->
|
||||
<pipeline-inputs-form
|
||||
v-if="isPipelineInputsFeatureAvailable"
|
||||
:saved-inputs="savedInputs"
|
||||
:query-ref="scheduleRef"
|
||||
class="gl-mb-6"
|
||||
@update-inputs="pipelineInputs = $event"
|
||||
/>
|
||||
<!--Variable List-->
|
||||
<pipeline-variables-form-group
|
||||
v-if="canViewPipelineVariables"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@ query getPipelineSchedulesQuery(
|
|||
name
|
||||
webPath
|
||||
}
|
||||
inputs {
|
||||
nodes {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
variables {
|
||||
nodes {
|
||||
id
|
||||
|
|
|
|||
|
|
@ -309,6 +309,23 @@ class ContainerRepository < ApplicationRecord
|
|||
self.find_by(project: path.repository_project, name: path.repository_name)
|
||||
end
|
||||
|
||||
def has_protected_tag_rules_for_delete?(user)
|
||||
return false if Feature.disabled?(:container_registry_protected_tags, project)
|
||||
|
||||
return true if user.nil?
|
||||
return false if user.can_admin_all_resources?
|
||||
|
||||
return false unless project.has_container_registry_protected_tag_rules?(
|
||||
action: 'delete',
|
||||
access_level: user.max_member_access_for_project(project.id)
|
||||
)
|
||||
|
||||
# This is an API call so we put it last
|
||||
return false unless has_tags?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def transform_tags_page(tags_response_body)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
class ForkNetwork < ApplicationRecord
|
||||
belongs_to :root_project, class_name: 'Project'
|
||||
belongs_to :organization, class_name: 'Organizations::Organization', optional: true
|
||||
|
||||
has_many :fork_network_members
|
||||
has_many :projects, through: :fork_network_members
|
||||
|
||||
|
|
|
|||
|
|
@ -427,6 +427,7 @@ class Project < ApplicationRecord
|
|||
|
||||
has_many :container_registry_protection_rules, class_name: 'ContainerRegistry::Protection::Rule', inverse_of: :project
|
||||
has_many :container_registry_protection_tag_rules, class_name: 'ContainerRegistry::Protection::TagRule', inverse_of: :project
|
||||
|
||||
# Container repositories need to remove data from the container registry,
|
||||
# which is not managed by the DB. Hence we're still using dependent: :destroy
|
||||
# here.
|
||||
|
|
@ -3478,6 +3479,12 @@ class Project < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def has_container_registry_protected_tag_rules?(action:, access_level:)
|
||||
strong_memoize_with(:has_container_registry_protected_tag_rules, action, access_level) do
|
||||
container_registry_protection_tag_rules.for_actions_and_access([action], access_level).exists?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_redis(&block)
|
||||
|
|
|
|||
|
|
@ -2,4 +2,10 @@
|
|||
|
||||
class ContainerRepositoryPolicy < BasePolicy
|
||||
delegate { @subject.project }
|
||||
|
||||
condition(:protected_for_delete) { @subject.has_protected_tag_rules_for_delete?(@user) }
|
||||
|
||||
rule { protected_for_delete }.policy do
|
||||
prevent :destroy_container_image
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -109,7 +109,13 @@ module Projects
|
|||
end
|
||||
|
||||
def fork_network
|
||||
@fork_network ||= @project.fork_network || @project.build_root_of_fork_network
|
||||
@fork_network ||= @project.fork_network || build_fork_network
|
||||
end
|
||||
|
||||
def build_fork_network
|
||||
@project.build_root_of_fork_network.tap do |fork_network|
|
||||
fork_network.organization = @project.organization
|
||||
end
|
||||
end
|
||||
|
||||
def build_fork_network_member(fork_to_project)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ module Snippets
|
|||
class BaseService < ::BaseProjectService
|
||||
UPDATE_COMMIT_MSG = 'Update snippet'
|
||||
INITIAL_COMMIT_MSG = 'Initial commit'
|
||||
INVALID_PARAMS_ERROR = :invalid_params_error
|
||||
INVALID_PARAMS_MESSAGES = {
|
||||
cannot_be_used_together: 'and snippet files cannot be used together',
|
||||
invalid_data: 'have invalid data'
|
||||
}.freeze
|
||||
|
||||
CreateRepositoryError = Class.new(StandardError)
|
||||
|
||||
|
|
@ -39,19 +44,20 @@ module Snippets
|
|||
def invalid_params_error(snippet)
|
||||
if snippet_actions.valid?
|
||||
[:content, :file_name].each do |key|
|
||||
snippet.errors.add(key, 'and snippet files cannot be used together') if params.key?(key)
|
||||
snippet.errors.add(key, INVALID_PARAMS_MESSAGES[:cannot_be_used_together]) if params.key?(key)
|
||||
end
|
||||
else
|
||||
snippet.errors.add(:snippet_actions, 'have invalid data')
|
||||
snippet.errors.add(:snippet_actions, INVALID_PARAMS_MESSAGES[:invalid_data])
|
||||
end
|
||||
|
||||
snippet_error_response(snippet, 422)
|
||||
# Callers need to interpret into 422
|
||||
snippet_error_response(snippet, INVALID_PARAMS_ERROR)
|
||||
end
|
||||
|
||||
def snippet_error_response(snippet, http_status)
|
||||
def snippet_error_response(snippet, reason)
|
||||
ServiceResponse.error(
|
||||
message: snippet.errors.full_messages.to_sentence,
|
||||
http_status: http_status,
|
||||
reason: reason,
|
||||
payload: { snippet: snippet }
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ module Snippets
|
|||
class CreateService < Snippets::BaseService
|
||||
include Gitlab::InternalEventsTracking
|
||||
|
||||
FAILED_TO_CREATE_ERROR = :failed_to_create_error
|
||||
|
||||
def initialize(project:, current_user: nil, params: {}, perform_spam_check: true)
|
||||
super(project: project, current_user: current_user, params: params)
|
||||
@perform_spam_check = perform_spam_check
|
||||
|
|
@ -32,7 +34,7 @@ module Snippets
|
|||
|
||||
ServiceResponse.success(payload: { snippet: @snippet })
|
||||
else
|
||||
snippet_error_response(@snippet, 400)
|
||||
snippet_error_response(@snippet, FAILED_TO_CREATE_ERROR)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ module Snippets
|
|||
include Gitlab::InternalEventsTracking
|
||||
|
||||
COMMITTABLE_ATTRIBUTES = %w[file_name content].freeze
|
||||
FAILED_TO_UPDATE_ERROR = :failed_to_update_error
|
||||
|
||||
UpdateError = Class.new(StandardError)
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ module Snippets
|
|||
|
||||
ServiceResponse.success(payload: { snippet: snippet })
|
||||
else
|
||||
snippet_error_response(snippet, 400)
|
||||
snippet_error_response(snippet, FAILED_TO_UPDATE_ERROR)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ and isn't the most recent one).
|
|||
MSG
|
||||
|
||||
DB_MESSAGE = <<~MSG
|
||||
## Database review
|
||||
|
||||
This merge request requires a database review. To make sure these
|
||||
changes are reviewed, take the following steps:
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,6 @@ description: The relationships between Epics and Issues
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3302
|
||||
milestone: '10.2'
|
||||
gitlab_schema: gitlab_main_cell
|
||||
desired_sharding_key:
|
||||
namespace_id:
|
||||
references: namespaces
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: issue_id
|
||||
table: issues
|
||||
sharding_key: namespace_id
|
||||
belongs_to: issue
|
||||
desired_sharding_key_migration_job_name: BackfillEpicIssuesNamespaceId
|
||||
table_size: small
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
table_name: secret_detection_token_statuses
|
||||
classes:
|
||||
- Vulnerabilities::FindingTokenStatus
|
||||
feature_categories:
|
||||
- secret_detection
|
||||
description: Stores the status of token detected by a secret detection scan
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182489
|
||||
milestone: '17.11'
|
||||
gitlab_schema: gitlab_sec
|
||||
table_size: small
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateSecretDetectionTokenStatus < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
|
||||
def change
|
||||
create_table :secret_detection_token_statuses, id: false do |t|
|
||||
t.bigint :vulnerability_occurrence_id, primary_key: true, default: nil
|
||||
t.bigint :project_id, null: false
|
||||
t.timestamps_with_timezone null: false
|
||||
t.integer :status, limit: 2, null: false, default: 0
|
||||
|
||||
t.index :project_id, name: 'idx_secret_detect_token_on_project_id'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddForeignKeyToVulnerabilityOccurrencesOnSecretDetectionTokenStatus < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :secret_detection_token_statuses, :vulnerability_occurrences,
|
||||
column: :vulnerability_occurrence_id, on_delete: :cascade, reverse_lock_order: true
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key_if_exists :secret_detection_token_statuses, column: :vulnerability_occurrence_id,
|
||||
reverse_lock_order: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEpicIssuesNamespaceIdNotNull < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_not_null_constraint :epic_issues, :namespace_id
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint :epic_issues, :namespace_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
774b323376d9227eb65ec49bfb7b55d418c86fd73d01a023794e93ce1a911492
|
||||
|
|
@ -0,0 +1 @@
|
|||
57df94cf5617553e2bba34da01fd617030fa638fbe8970e29a82e9698b9207b3
|
||||
|
|
@ -0,0 +1 @@
|
|||
cd19ac58831db5ae7fc67a6b5d8b5e832da4153a11aeb24fca30a0bc27cfee57
|
||||
|
|
@ -13827,7 +13827,8 @@ CREATE TABLE epic_issues (
|
|||
issue_id bigint NOT NULL,
|
||||
relative_position integer,
|
||||
namespace_id bigint,
|
||||
work_item_parent_link_id bigint
|
||||
work_item_parent_link_id bigint,
|
||||
CONSTRAINT check_885d672eec CHECK ((namespace_id IS NOT NULL))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE epic_issues_id_seq
|
||||
|
|
@ -21764,6 +21765,14 @@ CREATE SEQUENCE scim_oauth_access_tokens_id_seq
|
|||
|
||||
ALTER SEQUENCE scim_oauth_access_tokens_id_seq OWNED BY scim_oauth_access_tokens.id;
|
||||
|
||||
CREATE TABLE secret_detection_token_statuses (
|
||||
vulnerability_occurrence_id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
status smallint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE security_findings_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
|
|
@ -29620,6 +29629,9 @@ ALTER TABLE ONLY scim_identities
|
|||
ALTER TABLE ONLY scim_oauth_access_tokens
|
||||
ADD CONSTRAINT scim_oauth_access_tokens_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY secret_detection_token_statuses
|
||||
ADD CONSTRAINT secret_detection_token_statuses_pkey PRIMARY KEY (vulnerability_occurrence_id);
|
||||
|
||||
ALTER TABLE ONLY security_findings
|
||||
ADD CONSTRAINT security_findings_pkey PRIMARY KEY (id, partition_number);
|
||||
|
||||
|
|
@ -31995,6 +32007,8 @@ CREATE INDEX idx_scan_result_policies_on_configuration_id_id_updated_at ON scan_
|
|||
|
||||
CREATE INDEX idx_scan_result_policy_violations_on_policy_id_and_id ON scan_result_policy_violations USING btree (scan_result_policy_id, id);
|
||||
|
||||
CREATE INDEX idx_secret_detect_token_on_project_id ON secret_detection_token_statuses USING btree (project_id);
|
||||
|
||||
CREATE INDEX idx_security_pipeline_execution_project_schedules_next_run_at ON security_pipeline_execution_project_schedules USING btree (next_run_at, id);
|
||||
|
||||
CREATE INDEX idx_security_policies_config_id_policy_index ON security_policies USING btree (security_orchestration_policy_configuration_id, policy_index);
|
||||
|
|
@ -40753,6 +40767,9 @@ ALTER TABLE ONLY todos
|
|||
ALTER TABLE ONLY packages_debian_group_architectures
|
||||
ADD CONSTRAINT fk_92714bcab1 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY secret_detection_token_statuses
|
||||
ADD CONSTRAINT fk_928017ddbc FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspaces_agent_configs
|
||||
ADD CONSTRAINT fk_94660551c8 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
---
|
||||
stage: AI-powered
|
||||
group: AI Framework
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
title: Set up Duo on your GitLab.com staging account
|
||||
redirect_to: '../ai_features/ai_development_license.md#setting-up-duo-on-your-gitlabcom-staging-account'
|
||||
redirect_to: 'ai_development_license.md'
|
||||
remove_date: '2025-04-18'
|
||||
---
|
||||
|
||||
This document has been consolidated into [GitLab Duo licensing for local development](ai_development_license.md#setting-up-duo-on-your-gitlabcom-staging-account).
|
||||
<!-- markdownlint-disable -->
|
||||
|
||||
This document was moved to [another location](ai_development_license.md).
|
||||
|
||||
<!-- This redirect file can be deleted after <2025-04-18>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/development/documentation/redirects -->
|
||||
|
|
|
|||
|
|
@ -474,7 +474,6 @@ Merge requests enforce these maximums:
|
|||
|
||||
The availability of this feature is controlled by a feature flag.
|
||||
For more information, see the history.
|
||||
This feature is available for testing, but not ready for production use.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,22 +118,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, Self-managed, GitLab Dedicated | General availability |
|
||||
| [GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md) | Ultimate | GitLab Duo Enterprise | Self-managed | General availability |
|
||||
| [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 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 | Experiment |
|
||||
| [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, Self-managed, GitLab Dedicated | General availability |
|
||||
| [Code Suggestions](../project/repository/code_suggestions/_index.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, Self-managed, GitLab Dedicated | General availability |
|
||||
| [Code Explanation](../project/repository/code_explain.md) | Premium, Ultimate | GitLab Duo Pro or Enterprise | GitLab.com, 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, 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, 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, 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, Self-managed, GitLab Dedicated | General availability |
|
||||
| [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 |
|
||||
| [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 | Beta |
|
||||
| [Code Review](../project/merge_requests/duo_in_merge_requests.md#have-gitlab-duo-review-your-code) | Ultimate | GitLab Duo Enterprise | GitLab.com | Beta |
|
||||
| [Code Review Summary](../project/merge_requests/duo_in_merge_requests.md#summarize-a-code-review) | Ultimate | GitLab Duo Enterprise | GitLab.com, 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, 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, Self-managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Explanation](../application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | Ultimate | GitLab Duo Enterprise | GitLab.com, Self-managed, GitLab Dedicated | General availability |
|
||||
| [Vulnerability Resolution](../application_security/vulnerabilities/_index.md#vulnerability-resolution) | Ultimate | GitLab Duo Enterprise | GitLab.com, Self-managed, GitLab Dedicated | General availability |
|
||||
| [AI Impact Dashboard](../analytics/ai_impact_analytics.md) | Ultimate | GitLab Duo Enterprise | GitLab.com, Self-managed | General availability |
|
||||
| [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 |
|
||||
| [AI Impact Dashboard](../analytics/ai_impact_analytics.md) | Ultimate | GitLab Duo Enterprise | GitLab.com, GitLab Self-Managed | General availability |
|
||||
|
|
|
|||
|
|
@ -68,43 +68,58 @@ To create a GitLab agent for Kubernetes:
|
|||
Set up your AWS credentials when you want to authenticate AWS with GitLab.
|
||||
|
||||
1. Create an [IAM User](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) or [IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html).
|
||||
1. Make sure that your IAM user or role has the appropriate permissions for your project. For this example project, you must have the permissions shown below. You can expand this when you set up your own project.
|
||||
1. Make sure that your IAM user or role has the appropriate permissions for your project. For this example project, you must have the permissions listed in the following JSON block. You can expand these permissions when you set up your own project.
|
||||
|
||||
```json
|
||||
// IAM custom Policy definition
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "VisualEditor0",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"eks:*",
|
||||
"elasticloadbalancing:*",
|
||||
"autoscaling:*",
|
||||
"cloudwatch:*",
|
||||
"logs:*",
|
||||
"kms:DescribeKey",
|
||||
"iam:AddRoleToInstanceProfile",
|
||||
"iam:AttachRolePolicy",
|
||||
"iam:CreateInstanceProfile",
|
||||
"iam:CreateRole",
|
||||
"iam:CreateServiceLinkedRole",
|
||||
"iam:GetRole",
|
||||
"iam:ListAttachedRolePolicies",
|
||||
"iam:ListRolePolicies",
|
||||
"iam:ListRoles",
|
||||
"iam:PassRole",
|
||||
// required for destroy step
|
||||
"iam:DetachRolePolicy",
|
||||
"iam:ListInstanceProfilesForRole",
|
||||
"iam:DeleteRole"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
{
|
||||
"Sid": "VisualEditor0",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"eks:*",
|
||||
"elasticloadbalancing:*",
|
||||
"autoscaling:*",
|
||||
"cloudwatch:*",
|
||||
"logs:*",
|
||||
"kms:DescribeKey",
|
||||
"kms:TagResource",
|
||||
"kms:UntagResource",
|
||||
"kms:ListResourceTags",
|
||||
"kms:CreateKey",
|
||||
"kms:CreateAlias",
|
||||
"kms:ListAliases",
|
||||
"kms:DeleteAlias",
|
||||
"iam:AddRoleToInstanceProfile",
|
||||
"iam:AttachRolePolicy",
|
||||
"iam:CreateInstanceProfile",
|
||||
"iam:CreateRole",
|
||||
"iam:CreateServiceLinkedRole",
|
||||
"iam:GetRole",
|
||||
"iam:ListAttachedRolePolicies",
|
||||
"iam:ListRolePolicies",
|
||||
"iam:ListRoles",
|
||||
"iam:PassRole",
|
||||
"iam:DetachRolePolicy",
|
||||
"iam:ListInstanceProfilesForRole",
|
||||
"iam:DeleteRole",
|
||||
"iam:CreateOpenIDConnectProvider",
|
||||
"iam:CreatePolicy",
|
||||
"iam:TagOpenIDConnectProvider",
|
||||
"iam:GetPolicy",
|
||||
"iam:GetPolicyVersion",
|
||||
"iam:GetOpenIDConnectProvider",
|
||||
"iam:DeleteOpenIDConnectProvider",
|
||||
"iam:ListPolicyVersions",
|
||||
"iam:DeletePolicy"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. [Create an access key for the user or role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
|
||||
|
|
|
|||
|
|
@ -115,3 +115,13 @@ To ensure tag protection, direct manifest deletion requests are only allowed whe
|
|||
|
||||
- Tag protection is disabled
|
||||
- The user has permission to delete any protected tags
|
||||
|
||||
## Deleting container images
|
||||
|
||||
You cannot [delete container images](../../packages/container_registry/delete_container_registry_images.md) if all the following conditions are true:
|
||||
|
||||
- The container image has tags.
|
||||
- The project has container registry tag protection rules.
|
||||
- Your access level is lower than the `minimum_access_delete_level` defined in any of the rules.
|
||||
|
||||
This restriction applies regardless of whether the rule patterns match the container image tags.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ Provide feedback on this feature in [issue 443236](https://gitlab.com/gitlab-org
|
|||
|
||||
- Status: Beta
|
||||
- LLM: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
|
||||
{{< /details >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Helpers
|
||||
module Snippets
|
||||
# Maps service response reasons to HTTP status codes.
|
||||
# See design discussion: https://gitlab.com/gitlab-org/gitlab/-/issues/356036
|
||||
class HttpResponseMap
|
||||
REASON_TO_HTTP_STATUS = {
|
||||
success: 200,
|
||||
error: 400,
|
||||
invalid_params_error: 422,
|
||||
failed_to_create_error: 400,
|
||||
failed_to_update_error: 400
|
||||
}.freeze
|
||||
|
||||
UNHANDLED = 'Unhandled service reason'
|
||||
|
||||
def self.status_for(reason)
|
||||
REASON_TO_HTTP_STATUS[reason] || unhandled_reason_error(reason)
|
||||
end
|
||||
|
||||
def self.unhandled_reason_error(reason)
|
||||
Gitlab::AppLogger.warn(message: UNHANDLED, reason: reason.inspect)
|
||||
|
||||
500
|
||||
end
|
||||
private_class_method :unhandled_reason_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -97,7 +97,8 @@ module API
|
|||
present snippet, with: Entities::ProjectSnippet, current_user: current_user
|
||||
else
|
||||
with_captcha_check_rest_api(spammable: snippet) do
|
||||
render_api_error!({ error: service_response.message }, service_response.http_status)
|
||||
http_status = Helpers::Snippets::HttpResponseMap.status_for(service_response.reason)
|
||||
render_api_error!({ error: service_response.message }, http_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -143,7 +144,8 @@ module API
|
|||
present snippet, with: Entities::ProjectSnippet, current_user: current_user
|
||||
else
|
||||
with_captcha_check_rest_api(spammable: snippet) do
|
||||
render_api_error!({ error: service_response.message }, service_response.http_status)
|
||||
http_status = Helpers::Snippets::HttpResponseMap.status_for(service_response.reason)
|
||||
render_api_error!({ error: service_response.message }, http_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -148,7 +148,8 @@ module API
|
|||
present snippet, with: Entities::PersonalSnippet, current_user: current_user
|
||||
else
|
||||
with_captcha_check_rest_api(spammable: snippet) do
|
||||
render_api_error!({ error: service_response.message }, service_response.http_status)
|
||||
http_status = Helpers::Snippets::HttpResponseMap.status_for(service_response.reason)
|
||||
render_api_error!({ error: service_response.message }, http_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -195,7 +196,8 @@ module API
|
|||
present snippet, with: Entities::PersonalSnippet, current_user: current_user
|
||||
else
|
||||
with_captcha_check_rest_api(spammable: snippet) do
|
||||
render_api_error!({ error: service_response.message }, service_response.http_status)
|
||||
http_status = Helpers::Snippets::HttpResponseMap.status_for(service_response.reason)
|
||||
render_api_error!({ error: service_response.message }, http_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module Gitlab
|
|||
].freeze
|
||||
|
||||
LONG_RUNNING_TASKS = [
|
||||
Sos::PgStatStatements,
|
||||
Sos::DbLoopStatsActivity
|
||||
].freeze
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
module Sos
|
||||
class PgStatStatements < BaseDbStatsHandler
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
QUERY = <<~SQL
|
||||
SELECT now() AS timestamp, *
|
||||
FROM pg_stat_statements;
|
||||
SQL
|
||||
|
||||
def run
|
||||
return unless pg_stat_statements_installed?
|
||||
|
||||
result = execute_query(QUERY)
|
||||
write_to_csv("pg_stat_statements", result, include_timestamp: true)
|
||||
end
|
||||
|
||||
def pg_stat_statements_installed?
|
||||
query = "select exists(select 1 from pg_extension where extname = 'pg_stat_statements');"
|
||||
result = execute_query(query)
|
||||
result.first['exists']
|
||||
end
|
||||
strong_memoize_attr :pg_stat_statements_installed?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2916,6 +2916,12 @@ msgstr ""
|
|||
msgid "AccessTokens|Add a %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Add new token"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|An error occurred while creating the token."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|An error occurred while fetching the tokens."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2946,6 +2952,9 @@ msgstr ""
|
|||
msgid "AccessTokens|Are you sure? Any issue email addresses currently in use will stop working."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|At least one scope is required."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Copy feed token"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2961,18 +2970,27 @@ msgstr ""
|
|||
msgid "AccessTokens|Create %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Create token"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Created"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Created date"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Description"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Enable DPoP"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Expiration date"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Expiration date is required."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Expired"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2994,6 +3012,45 @@ msgstr ""
|
|||
msgid "AccessTokens|For example, the application using the token or the purpose of the token. Do not give sensitive information for the name of the token, as it will be visible to all %{resource_type} members."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grant access to download Service Ping payload via API when authenticated as an admin user."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants access to GitLab Duo related API endpoints."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants access to manage the runners."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants complete read/write access to the API, including all groups and projects, the container registry, the dependency proxy, and the package registry."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants create access to the runners."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission for token to rotate itself."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform API actions as an administrator, when Admin Mode is enabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform API actions as any user in the system, when authenticated as an admin user."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform Kubernetes API calls using the agent for Kubernetes."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read access to the API, including all groups and projects, the container registry, and the package registry."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-only access to repositories on private projects using Git-over-HTTP or the Repository Files API."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-only access to your profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API)."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|How do I use DPoP headers?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -3080,6 +3137,9 @@ msgstr ""
|
|||
msgid "AccessTokens|Scopes set the permission levels granted to the token."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Scopes set the permission levels granted to the token. %{linkStart}Learn more%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Search or filter access tokens..."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -3122,6 +3182,9 @@ msgstr ""
|
|||
msgid "AccessTokens|Token name"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Token name is required."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Usage"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -3137,6 +3200,9 @@ msgstr ""
|
|||
msgid "AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API. You can also use personal access tokens to authenticate against Git over HTTP. They are the only accepted password when you have Two-Factor Authentication (2FA) enabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|You can only have one active project access token with a trial license. You cannot generate a new token until the existing token is deleted, or you upgrade your subscription."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -43395,6 +43461,9 @@ msgstr ""
|
|||
msgid "Pipelines|There are currently no pipelines."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|There are no inputs for this configuration."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|There are no merge trains for the %{branch} target branch in %{projectName}. Merge requests added to a merge train are displayed on this page. Go to a merge request to %{linkStart}start a merge train.%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -59316,9 +59385,6 @@ msgstr ""
|
|||
msgid "There are no custom project templates set up for this GitLab instance. They are enabled from GitLab's Admin area. Contact your GitLab instance administrator to setup custom project templates."
|
||||
msgstr ""
|
||||
|
||||
msgid "There are no inputs for this configuration."
|
||||
msgstr ""
|
||||
|
||||
msgid "There are no issues to show"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,297 +1,294 @@
|
|||
{
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 31.312703777000003,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 85.723811025,
|
||||
"qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb": 108.48175637,
|
||||
"qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb": 111.364926809,
|
||||
"qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb": 117.373596391,
|
||||
"qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb": 20.240661693,
|
||||
"qa/specs/features/api/1_manage/import/import_github_repo_spec.rb": 100.604753928,
|
||||
"qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb": 51.773151303000006,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb": 57.164676432,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb": 209.66836027,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb": 95.145731268,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb": 96.669735449,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 22.777700688,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 12.804441871,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 43.539234226000005,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 14.737558998,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 36.495285002,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 53.455758953,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 32.191657311,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 3.163201342,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 24.429672997,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 24.674972863,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 17.603209331,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 6.074523577,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 7.039305525,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 18.893561881,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 18.331837077,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 7.25170929,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 138.732236624,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 14.713357095,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 106.199417264,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 33.370348565,
|
||||
"qa/specs/features/api/8_monitor/metrics_spec.rb": 4.672106511,
|
||||
"qa/specs/features/api/9_data_stores/user_inherited_access_spec.rb": 124.106589226,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 7.531413161,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 20.421148217,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 47.29602662,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 57.590253872,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 12.904743836,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 103.6354147,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_gitlab_via_ldap_spec.rb": 4.187795071,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_mattermost_via_gitlab_spec.rb": 32.433976297,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_instance_wide_saml_sso_spec.rb": 18.72828949,
|
||||
"qa/specs/features/browser_ui/10_govern/login/oauth_login_with_github_spec.rb": 41.157460572,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 211.56071979799998,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 24.15947311,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 34.264426186,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 40.190177983,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 30.26643493,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 9.481520661,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_disabled_spec.rb": 12.117321519,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 101.140834264,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb": 49.602512911999995,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb": 36.148346744,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb": 66.365016218,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb": 72.170178938,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_user_contribution_reassignment_spec.rb": 170.633368898,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 27.361170958,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 32.810219511,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 22.414812761,
|
||||
"qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb": 21.731724809,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 26.39903421,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 28.530292849,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 23.803294166,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 68.543888982,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 24.533760912,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 20.111584639,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 29.837648576,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 15.112964234,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 27.0954566,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 23.190903783,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 16.2804108,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 94.742394875,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 12.337184544,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 25.72992505,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 72.432054287,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 44.995716559,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 20.641570945,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 36.423745517,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 58.292547928,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 44.441698066,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 24.15771234,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 59.042601764,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 35.607056678,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 89.090076795,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb": 36.630282281,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 81.166348019,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_request_set_to_auto_merge_spec.rb": 64.703022855,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 79.737368209,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 39.219961205,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 102.978471064,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 92.169994349,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 61.334739567,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 52.573579617,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 57.251906456,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 67.625249287,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 20.018602586,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 20.632802902,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 53.986348589,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 19.492480119,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 11.101996261,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 17.542070237,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 29.047117234,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 53.045863252000004,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 85.73131555399999,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 33.625171262,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 18.588071995,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 30.672816907,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 56.033679471,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 53.30382817,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 47.768821505,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 31.065604238,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 53.456585986,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 9.483635175,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb": 23.004905801,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 24.234086567,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 21.222221601,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 35.108021069,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 32.508396036,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 36.330483318,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 42.982747187,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 66.373316612,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 27.828975043,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 14.292804146,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 8.376301189,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 19.310346212,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 18.103331834,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 27.902920605,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 18.601709575,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 58.335178918,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 20.684676419,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 46.25020741,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 68.623372252,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 15.533937005,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 64.056241196,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 75.73989959400001,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_glab_spec.rb": 154.711315543,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_release_cli_spec.rb": 132.788845052,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 53.999267897,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 64.325558272,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 342.39437633,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 248.005338145,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 83.139581136,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb": 40.203053418,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 112.091917162,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb": 51.780327903,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 59.266756507,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 126.555352978,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 26.146649165,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 88.597246375,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 61.090969346,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 96.955356456,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 83.837600245,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 107.868942601,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 93.38860749,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 65.814947628,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 24.370662572,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_registration_token_spec.rb": 19.696668852,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_unregister_runner_spec.rb": 32.783702631,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_counts_spec.rb": 23.285444497,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_status_counts_spec.rb": 16.425156745,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 12.010111147,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_project_runner_spec.rb": 56.395677587,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 56.948236212,
|
||||
"qa/specs/features/browser_ui/5_package/container_registry/self_managed/container_registry_spec.rb": 336.46897839,
|
||||
"qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb": 170.558130572,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 44.871103009,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 99.561866722,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 71.396030986,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 272.46680776,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 513.4305678439999,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 324.59947701400006,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 290.930515917,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 348.315553671,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 363.461743166,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 99.501268752,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 25.534309915,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 171.074830804,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 11.747934645,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 43.58996528,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 57.292421387000005,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 46.445470523,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb": 61.596451156,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 26.865431941,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/create_group_with_mattermost_team_spec.rb": 7.62795593,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/group_member_access_request_spec.rb": 65.166631909,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/transfer_project_spec.rb": 25.912590716,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 30.677051466,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_badge_spec.rb": 23.754947808,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_spec.rb": 57.371518925000004,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/dashboard_images_spec.rb": 17.48816339,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/invite_group_to_project_spec.rb": 72.565639924,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/project_owner_permissions_spec.rb": 105.512590394,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/view_project_activity_spec.rb": 26.944971584,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/follow_user_activity_spec.rb": 36.464417609,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/parent_group_access_termination_spec.rb": 27.87712138,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/user_inherited_access_spec.rb": 35.758359558,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 55.897473623,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 25.263763109000003,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 65.364787871,
|
||||
"qa/specs/features/ee/api/1_manage/import/import_github_repo_spec.rb": 31.153499632,
|
||||
"qa/specs/features/ee/api/1_manage/integrations/group_webhook_events_spec.rb": 12.094830499,
|
||||
"qa/specs/features/ee/api/1_manage/migration/gitlab_migration_group_spec.rb": 78.648789815,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 60.794241526,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 45.19848707500001,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/advanced_global_advanced_syntax_search_spec.rb": 117.250437922,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/elasticsearch_api_spec.rb": 55.743935538,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/commit_index/commit_index_spec.rb": 115.61303047,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/issues_index/issue_index_spec.rb": 56.709287471,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/main_index/blob_index_spec.rb": 21.207008245,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/merge_request_index/merge_request_index_spec.rb": 70.431202498,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/notes_index/note_index_spec.rb": 37.811999641,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/user_index/user_index_spec.rb": 132.069291484,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/nightly_elasticsearch_test_spec.rb": 19.928258532,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 122.81131311300001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 98.919022938,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb": 86.80656613,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 28.758285522,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 153.487471293,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 30.506825360000004,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 102.202442549,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_ldap_sync_spec.rb": 126.297446367,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 114.62520591999998,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group_pipeline_execution_policy_spec.rb": 322.994291718,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 100.8588692,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 164.72135849900002,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 54.989027421,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 200.66570050400003,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 166.07628630800002,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_policies_spec.rb": 80.963582403,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 304.313640466,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 21.265823402,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb": 33.402676897,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 345.326474276,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb": 134.357049619,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb": 20.569162075999998,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 4.843355821,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 25.728816186,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/cvs_dependency_scanning_spec.rb": 52.345787099,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_advanced_sast_spec.rb": 137.126234557,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb": 82.742356453,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/secret_push_protection_spec.rb": 105.390265021,
|
||||
"qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb": 15.931974517,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/integrations/jira_issues_list_spec.rb": 57.699235578,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 73.229403091,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 105.594426127,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 38.868889772,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 11.316103986,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 10.099606894,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 126.592772512,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 20.105267349,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 7.653830696,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 20.633041741,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 10.264453209,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 26.240314396,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 28.107581157,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 29.075443314,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 10.271708079,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 26.342305449,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 16.226553964,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 21.541011214,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 49.58064934400001,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 31.174940456,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 24.526747347,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 19.823153894,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 22.978240583,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 20.568377114,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 56.270250495999996,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 25.845107059,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 39.395453035,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 20.343913591,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 103.80653801,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 78.648992686,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 27.28980532,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 44.96693451,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 25.658054484,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 28.023039462,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 219.367284116,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 118.93965357600001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 134.931337431,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 253.95235804499998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 146.06331843499999,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 29.286008566,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 47.92659186,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 320.59896564599995,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 198.83719065100001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 171.82875241199997,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 107.684678228,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 171.792003449,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_for_merged_result_spec.rb": 93.729740876,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 51.093958903,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 23.608011262,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/elasticsearch/elasticsearch_reindexing_spec.rb": 138.36814982599998,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/prevent_forking_outside_group_spec.rb": 43.633666222,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/share_group_with_group_spec.rb": 45.645946482
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 39.015416951000006,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 106.368852176,
|
||||
"qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb": 105.049180592,
|
||||
"qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb": 114.117016781,
|
||||
"qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb": 112.215821194,
|
||||
"qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb": 20.070271725,
|
||||
"qa/specs/features/api/1_manage/import/import_github_repo_spec.rb": 62.003969309,
|
||||
"qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb": 55.825349622,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb": 62.935936429,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb": 202.493898823,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb": 87.499751174,
|
||||
"qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb": 88.508076714,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 13.277984319,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 24.708294559,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 19.257215062,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 23.549206831,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 17.103644498,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_spec.rb": 48.787745737,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 14.30835218,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 13.037028017,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 3.362543138,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 22.521983931,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 16.986733483,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 10.438538951,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 8.292723854,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 14.309379313,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 46.82348699,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 18.842996995,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 4.836340896,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 130.826120863,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 17.87065088,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 59.64439644,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 32.128043063,
|
||||
"qa/specs/features/api/8_monitor/metrics_spec.rb": 4.933892169,
|
||||
"qa/specs/features/api/9_data_stores/user_inherited_access_spec.rb": 101.195678975,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 7.995516989,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 22.527453243,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 53.999705944,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 53.908889334,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 13.911981617,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 106.007041931,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_gitlab_via_ldap_spec.rb": 3.283060791,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_into_mattermost_via_gitlab_spec.rb": 29.144384337,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_instance_wide_saml_sso_spec.rb": 15.951114865,
|
||||
"qa/specs/features/browser_ui/10_govern/login/oauth_login_with_github_spec.rb": 47.003167955,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 208.003737035,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 25.351553058,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 33.319005124,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 40.696586075,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 30.745564205,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 11.25534958,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_disabled_spec.rb": 12.456176334,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 78.935532839,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb": 53.867358392,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb": 44.402761204,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb": 74.402252836,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb": 61.509075739,
|
||||
"qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_user_contribution_reassignment_spec.rb": 130.406320304,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 22.456073862,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 30.704792421,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 23.416268612,
|
||||
"qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb": 19.650408505,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 39.904237402,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 23.676053478,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 28.733551729,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 160.463164561,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 19.1935964,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 42.444434445,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 38.610917306,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 22.115639337,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 30.853094396,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 24.081250961,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 15.645366466,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 135.79523272199998,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 12.480264716,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 34.270604428,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 77.81674518,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 48.630988975,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 18.598626078,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 37.093153591,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 37.76284062,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 48.864911859,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 25.087867893,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 79.635242401,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 45.435782761,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 31.730203301,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 82.792721693,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 79.294377152,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_request_set_to_auto_merge_spec.rb": 75.962346477,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 78.547733484,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 30.715780741,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 72.845907065,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 55.065707066,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 56.127548556,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 48.691703135,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 72.095818685,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 24.217319974,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 22.145803562,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 41.870368975000005,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 15.722445173,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 20.471180894,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 20.460344621,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 23.14264736,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 39.458224772,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 112.11682745499999,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 11.899885153,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 27.750587168,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 23.152083494,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 58.475345381,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 33.741464532,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 47.433937904000004,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 35.559179209999996,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_file_size_spec.rb": 52.099899994,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 49.290506297,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 13.125618596,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb": 27.779947735,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 29.195662745,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 25.826480199,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 42.173013838,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 37.395484355,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 39.528317903,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 59.304316883,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 53.150999789,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 30.702030026,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 14.854521748,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 10.754807591,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 17.408281231,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 20.837560765,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 37.344303433,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 17.954152331000003,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 75.552891701,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 20.457936094,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 47.844030061,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 59.437038702,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 17.528917038,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 65.344498643,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 82.170487484,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_glab_spec.rb": 128.799186741,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/release_with_release_cli_spec.rb": 132.329964178,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 47.076370568,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 83.300867242,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 295.80982683700006,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 330.727897538,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 71.050121225,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 160.175345309,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 38.954995341,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 118.092139808,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 22.696241489,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 54.407585417,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 68.481154055,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 117.82635571899999,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 50.944508088,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 85.520066019,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 38.289494734,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 76.068754019,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 27.411302724,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_registration_token_spec.rb": 17.972469624,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/deprecated_unregister_runner_spec.rb": 27.839527679,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_counts_spec.rb": 26.191812661,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_visibility/group_runner_status_counts_spec.rb": 20.972690884,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 16.967509613,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_project_runner_spec.rb": 44.441871718,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 52.074608676,
|
||||
"qa/specs/features/browser_ui/5_package/container_registry/self_managed/container_registry_spec.rb": 339.815146561,
|
||||
"qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb": 158.892163116,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 67.898079823,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 79.948760081,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 76.56463333,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 261.917341999,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 537.798583543,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 312.88760401900004,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 270.221598764,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 301.388367709,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 275.612222368,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 105.725227548,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 27.660887847,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 139.250853836,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 6.653796947,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 61.254122884,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 63.371906673,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 60.674454635000004,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb": 76.543664971,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 18.743305802,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/create_group_with_mattermost_team_spec.rb": 8.860879939,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/group_member_access_request_spec.rb": 56.79010400199999,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/transfer_project_spec.rb": 27.603434121,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 37.518034725,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_badge_spec.rb": 20.474208584,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_spec.rb": 57.698194097,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/dashboard_images_spec.rb": 14.717029229,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/invite_group_to_project_spec.rb": 52.531947523,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/project_owner_permissions_spec.rb": 101.944178823,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/view_project_activity_spec.rb": 31.945936,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/follow_user_activity_spec.rb": 40.756750728,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/parent_group_access_termination_spec.rb": 31.607358063,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/user_inherited_access_spec.rb": 34.836026899000004,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 39.535526732,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 35.68952704,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 60.760585273000004,
|
||||
"qa/specs/features/ee/api/1_manage/import/import_github_repo_spec.rb": 63.724848364,
|
||||
"qa/specs/features/ee/api/1_manage/integrations/group_webhook_events_spec.rb": 5.841713835,
|
||||
"qa/specs/features/ee/api/1_manage/migration/gitlab_migration_group_spec.rb": 84.330011071,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 67.734790242,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 40.88584918,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/advanced_global_advanced_syntax_search_spec.rb": 107.88748357200001,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/elasticsearch_api_spec.rb": 30.462039465999997,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/commit_index/commit_index_spec.rb": 21.92566804,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/issues_index/issue_index_spec.rb": 21.179437801,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/main_index/blob_index_spec.rb": 113.333326954,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/merge_request_index/merge_request_index_spec.rb": 72.845728048,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/notes_index/note_index_spec.rb": 48.397926046,
|
||||
"qa/specs/features/ee/api/9_data_stores/elasticsearch/index_tests/user_index/user_index_spec.rb": 66.84477839,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 110.74005522899999,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 74.717975852,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb": 111.91902516,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 31.138067809,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 131.344681354,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 59.072728833999996,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 118.707501791,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_ldap_sync_spec.rb": 115.93010979799999,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 118.218510615,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group_pipeline_execution_policy_spec.rb": 224.736325594,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 123.880161656,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 154.513786391,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 73.222131825,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 196.305962014,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 145.00942302,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_policies_spec.rb": 90.637912799,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 382.386519925,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 11.842035565,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb": 23.163571571,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 377.698799393,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb": 21.309370363,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 6.025411135,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 22.143682348,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/cvs_dependency_scanning_spec.rb": 43.336889577,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_advanced_sast_spec.rb": 131.519278129,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/on_demand_dast_spec.rb": 129.325574593,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/secret_push_protection_spec.rb": 98.04171613899999,
|
||||
"qa/specs/features/ee/browser_ui/16_ai_powered/duo_chat/duo_chat_spec.rb": 11.579960114,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/integrations/jira_issues_list_spec.rb": 58.073892832000006,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 54.644641075,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 46.295190376,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 39.414439645,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 19.304536865,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 10.286369479,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 197.91125446900003,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 42.211732379,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 13.477497003,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 19.156923591,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 11.53903459,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 33.608027282,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 22.345140744,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 40.64401603,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 16.258654052,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 30.093744852,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 20.979680426,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 20.356119157,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 46.448732539999995,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 23.489636985,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 14.499187043,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 21.178604938,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 28.992488871,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 21.227470595,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 35.585879116,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 47.009384816,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 46.469674276,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 18.064138468,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 57.508876344,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 88.699750432,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 36.469501829,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 40.069966681,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 28.517757514,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 24.368511089,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 181.395316981,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 105.0553455,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 104.534100176,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 170.577859332,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/prevent_forking_outside_group_spec.rb": 49.567739792,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 74.894425885,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 53.353582035,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 43.11710376,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 343.07040934199995,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 209.229670413,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 177.14888967,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 38.15616983,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 109.025755969,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_for_merged_result_spec.rb": 35.021199942,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 48.187999655,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 21.398259859,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/elasticsearch/elasticsearch_reindexing_spec.rb": 139.892187759,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/share_group_with_group_spec.rb": 30.381249007
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Gitlab
|
||||
# This cop identifies direct calls to hard delete classes that could lead to data loss.
|
||||
class HardDeleteCalls < RuboCop::Cop::Base
|
||||
MSG = 'Avoid the use of `%{observed_class}`. Use `%{preferred_class}` instead. ' \
|
||||
'See https://docs.gitlab.com/development/deleting_data/ '
|
||||
|
||||
HARD_DELETE_CLASSES = {
|
||||
'Projects::DestroyService' => 'Projects::MarkForDeletionService',
|
||||
'ProjectDestroyWorker' => 'Projects::MarkForDeletionService',
|
||||
'Groups::DestroyService' => 'Groups::MarkForDeletionService',
|
||||
'GroupDestroyWorker' => 'Groups::MarkForDeletionService'
|
||||
}.freeze
|
||||
|
||||
def on_send(node)
|
||||
check_node(node)
|
||||
end
|
||||
|
||||
def on_csend(node)
|
||||
check_node(node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_node(node)
|
||||
receiver = node.receiver
|
||||
|
||||
return unless receiver && receiver.const_type?
|
||||
|
||||
preferred_class = HARD_DELETE_CLASSES[receiver.const_name]
|
||||
|
||||
return unless preferred_class
|
||||
|
||||
add_offense(node, message: message(receiver.const_name, preferred_class))
|
||||
end
|
||||
|
||||
def message(observed_class, preferred_class)
|
||||
format(MSG, observed_class: observed_class, preferred_class: preferred_class)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -225,6 +225,7 @@ RSpec.describe 'Database schema',
|
|||
instance_integrations: %w[project_id group_id inherit_from_id], # these columns are not used in instance integrations
|
||||
group_scim_identities: %w[temp_source_id], # temporary column that is not a foreign key
|
||||
group_scim_auth_access_tokens: %w[temp_source_id], # temporary column that is not a foreign key
|
||||
secret_detection_token_statuses: %w[project_id],
|
||||
system_access_group_microsoft_graph_access_tokens: %w[temp_source_id], # temporary column that is not a foreign key
|
||||
system_access_group_microsoft_applications: %w[temp_source_id], # temporary column that is not a foreign key
|
||||
subscription_user_add_on_assignment_versions: %w[item_id user_id purchase_id], # Managed by paper_trail gem, no need for FK on the historical data
|
||||
|
|
|
|||
|
|
@ -3,5 +3,9 @@
|
|||
FactoryBot.define do
|
||||
factory :fork_network do
|
||||
association :root_project, factory: :project
|
||||
|
||||
before(:create) do |network|
|
||||
network.organization_id = network.root_project.organization_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RSpec.describe 'Pipeline Schedules', :js, feature_category: :continuous_integrat
|
|||
|
||||
before do
|
||||
project.update!(ci_pipeline_variables_minimum_override_role: :developer)
|
||||
stub_feature_flags(ci_inputs_for_pipelines: false)
|
||||
end
|
||||
|
||||
context 'logged in as the pipeline schedule owner' do
|
||||
|
|
|
|||
|
|
@ -181,8 +181,9 @@ describe('DynamicValueRenderer', () => {
|
|||
${'NUMBER'} | ${'42'} | ${42} | ${false}
|
||||
${'BOOLEAN'} | ${'true'} | ${true} | ${true}
|
||||
${'BOOLEAN'} | ${'false'} | ${false} | ${true}
|
||||
${'ARRAY'} | ${'a,b,c'} | ${'a,b,c'} | ${false}
|
||||
`(
|
||||
'converts input value "$inputValue" to $type typed value',
|
||||
'handles input value "$inputValue" for $type type appropriately',
|
||||
async ({ type, inputValue, expectedTypedValue, usesDropdown }) => {
|
||||
createComponent({
|
||||
props: { item: { ...defaultProps.item, type } },
|
||||
|
|
|
|||
|
|
@ -31,16 +31,17 @@ describe('PipelineInputsForm', () => {
|
|||
let wrapper;
|
||||
let pipelineInputsHandler;
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
const createComponent = ({ props = {}, provide = {} } = {}) => {
|
||||
const handlers = [[getPipelineInputsQuery, pipelineInputsHandler]];
|
||||
const mockApollo = createMockApollo(handlers);
|
||||
wrapper = shallowMountExtended(PipelineInputsForm, {
|
||||
propsData: {
|
||||
...props,
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
...defaultProvide,
|
||||
...provide,
|
||||
},
|
||||
apolloProvider: mockApollo,
|
||||
});
|
||||
|
|
@ -49,6 +50,7 @@ describe('PipelineInputsForm', () => {
|
|||
const findSkeletonLoader = () => wrapper.findComponent(InputsTableSkeletonLoader);
|
||||
const findInputsTable = () => wrapper.findComponent(PipelineInputsTable);
|
||||
const findCrudComponent = () => wrapper.findComponent(CrudComponent);
|
||||
const findEmptyState = () => wrapper.findByText('There are no inputs for this configuration.');
|
||||
|
||||
describe('mounted', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -83,13 +85,31 @@ describe('PipelineInputsForm', () => {
|
|||
});
|
||||
|
||||
it('sends the correct props to the table', () => {
|
||||
const expectedInputs = mockPipelineInputsResponse.data.project.ciPipelineCreationInputs;
|
||||
const expectedInputs = [
|
||||
{
|
||||
name: 'deploy_environment',
|
||||
description: 'Specify deployment environment',
|
||||
default: 'staging',
|
||||
type: 'text',
|
||||
required: false,
|
||||
options: ['staging', 'production'],
|
||||
regex: '^(staging|production)$',
|
||||
},
|
||||
{
|
||||
name: 'api_token',
|
||||
description: 'API token for deployment',
|
||||
default: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: [],
|
||||
regex: null,
|
||||
},
|
||||
];
|
||||
expect(findInputsTable().props('inputs')).toEqual(expectedInputs);
|
||||
});
|
||||
|
||||
it('updates the count in the crud component', () => {
|
||||
const count = mockPipelineInputsResponse.data.project.ciPipelineCreationInputs.length;
|
||||
expect(findCrudComponent().props('count')).toBe(count);
|
||||
expect(findCrudComponent().props('count')).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -104,9 +124,7 @@ describe('PipelineInputsForm', () => {
|
|||
});
|
||||
|
||||
it('displays the empty state message when there are no inputs', () => {
|
||||
expect(wrapper.findByText('There are no inputs for this configuration.').exists()).toBe(
|
||||
true,
|
||||
);
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -124,6 +142,32 @@ describe('PipelineInputsForm', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when projectPath is not provided', () => {
|
||||
beforeEach(async () => {
|
||||
pipelineInputsHandler = jest.fn();
|
||||
await createComponent({ provide: { projectPath: '' } });
|
||||
});
|
||||
|
||||
it('does not execute the query', () => {
|
||||
expect(pipelineInputsHandler).not.toHaveBeenCalled();
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('savedInputs prop', () => {
|
||||
it('overwrites default values if saved input values are provided', async () => {
|
||||
pipelineInputsHandler = jest.fn().mockResolvedValue(mockPipelineInputsResponse);
|
||||
const savedInputs = [{ name: 'deploy_environment', value: 'saved-value' }];
|
||||
await createComponent({ props: { savedInputs } });
|
||||
await waitForPromises();
|
||||
|
||||
const updatedInput = findInputsTable()
|
||||
.props('inputs')
|
||||
.find((i) => i.name === 'deploy_environment');
|
||||
expect(updatedInput.default).toBe('saved-value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('event handling', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { getSkeletonRectProps } from '~/ci/common/pipeline_inputs/utils';
|
||||
|
||||
describe('Skeleton utils', () => {
|
||||
describe('getSkeletonRectProps', () => {
|
||||
it.each`
|
||||
columnIndex | rowIndex | expectedX | expectedY
|
||||
${0} | ${0} | ${'0%'} | ${0}
|
||||
${1} | ${0} | ${'25.5%'} | ${0}
|
||||
${2} | ${0} | ${'51%'} | ${0}
|
||||
${3} | ${0} | ${'76.5%'} | ${0}
|
||||
${0} | ${1} | ${'0%'} | ${10}
|
||||
${2} | ${3} | ${'51%'} | ${30}
|
||||
`(
|
||||
'calculates correct position for col $columnIndex, row $rowIndex',
|
||||
({ columnIndex, rowIndex, expectedX, expectedY }) => {
|
||||
const result = getSkeletonRectProps(columnIndex, rowIndex);
|
||||
|
||||
expect(result.x).toBe(expectedX);
|
||||
expect(result.y).toBe(expectedY);
|
||||
expect(result.width).toBe('23%');
|
||||
expect(result.height).toBe(6);
|
||||
expect(result.rx).toBe(2);
|
||||
expect(result.ry).toBe(2);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -6,10 +6,12 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { createAlert } from '~/alert';
|
||||
import PipelineInputsForm from '~/ci/common/pipeline_inputs/pipeline_inputs_form.vue';
|
||||
import PipelineSchedulesForm from '~/ci/pipeline_schedules/components/pipeline_schedules_form.vue';
|
||||
import PipelineVariablesFormGroup from '~/ci/pipeline_schedules/components/pipeline_variables_form_group.vue';
|
||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
|
||||
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
|
||||
import createPipelineScheduleMutation from '~/ci/pipeline_schedules/graphql/mutations/create_pipeline_schedule.mutation.graphql';
|
||||
|
|
@ -87,6 +89,7 @@ describe('Pipeline schedules form', () => {
|
|||
editing = false,
|
||||
pipelineVariablesPermissionsMixin = mockPipelineVariablesPermissions(true),
|
||||
requestHandlers,
|
||||
ciInputsForPipelines = false,
|
||||
} = {}) => {
|
||||
wrapper = shallowMountExtended(PipelineSchedulesForm, {
|
||||
propsData: {
|
||||
|
|
@ -96,8 +99,11 @@ describe('Pipeline schedules form', () => {
|
|||
},
|
||||
provide: {
|
||||
...defaultProvide,
|
||||
glFeatures: {
|
||||
ciInputsForPipelines,
|
||||
},
|
||||
},
|
||||
mixins: [pipelineVariablesPermissionsMixin],
|
||||
mixins: [glFeatureFlagMixin(), pipelineVariablesPermissionsMixin],
|
||||
apolloProvider: createMockApolloProvider(requestHandlers),
|
||||
});
|
||||
};
|
||||
|
|
@ -110,6 +116,7 @@ describe('Pipeline schedules form', () => {
|
|||
const findSubmitButton = () => wrapper.findByTestId('schedule-submit-button');
|
||||
const findCancelButton = () => wrapper.findByTestId('schedule-cancel-button');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findPipelineInputsForm = () => wrapper.findComponent(PipelineInputsForm);
|
||||
const findPipelineVariables = () => wrapper.findComponent(PipelineVariablesFormGroup);
|
||||
|
||||
describe('Form elements', () => {
|
||||
|
|
@ -168,6 +175,22 @@ describe('Pipeline schedules form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not display inputs form when feature flag is disabled', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findPipelineInputsForm().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('displays inputs form when feature flag is enabled', () => {
|
||||
createComponent({ ciInputsForPipelines: true });
|
||||
|
||||
expect(findPipelineInputsForm().exists()).toBe(true);
|
||||
expect(findPipelineInputsForm().props()).toMatchObject({
|
||||
queryRef: 'main',
|
||||
savedInputs: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('displays variable list when the user has permissions', () => {
|
||||
createComponent();
|
||||
|
||||
|
|
@ -237,71 +260,82 @@ describe('Pipeline schedules form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('schedule creation success', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
it('creates pipeline schedule successfully', async () => {
|
||||
createComponent({ ciInputsForPipelines: true });
|
||||
|
||||
const updatedInputs = [
|
||||
{ name: 'input1', value: 'value1' },
|
||||
{ name: 'input2', value: 'value2' },
|
||||
];
|
||||
const updatedVariables = [
|
||||
{
|
||||
key: 'test_var_2',
|
||||
value: 'value_2',
|
||||
variableType: 'ENV_VAR',
|
||||
},
|
||||
];
|
||||
|
||||
findDescription().vm.$emit('input', 'My schedule');
|
||||
|
||||
findTimezoneDropdown().vm.$emit('input', {
|
||||
formattedTimezone: '[UTC-4] Eastern Time (US & Canada)',
|
||||
identifier: 'America/New_York',
|
||||
});
|
||||
|
||||
it('creates pipeline schedule', async () => {
|
||||
findDescription().vm.$emit('input', 'My schedule');
|
||||
findIntervalComponent().vm.$emit('cronValue', '0 16 * * *');
|
||||
|
||||
findTimezoneDropdown().vm.$emit('input', {
|
||||
formattedTimezone: '[UTC-4] Eastern Time (US & Canada)',
|
||||
identifier: 'America/New_York',
|
||||
});
|
||||
findPipelineVariables().vm.$emit('update-variables', updatedVariables);
|
||||
findPipelineInputsForm().vm.$emit('update-inputs', updatedInputs);
|
||||
|
||||
findIntervalComponent().vm.$emit('cronValue', '0 16 * * *');
|
||||
findSubmitButton().vm.$emit('click');
|
||||
|
||||
findPipelineVariables().vm.$emit('update-variables', [
|
||||
{
|
||||
key: 'test_var_2',
|
||||
value: 'value_2',
|
||||
variableType: 'ENV_VAR',
|
||||
},
|
||||
]);
|
||||
await waitForPromises();
|
||||
|
||||
findSubmitButton().vm.$emit('click');
|
||||
expect(createMutationHandlerSuccess).toHaveBeenCalledWith({
|
||||
input: {
|
||||
active: true,
|
||||
cron: '0 16 * * *',
|
||||
cronTimezone: 'America/New_York',
|
||||
description: 'My schedule',
|
||||
projectPath: 'gitlab-org/gitlab',
|
||||
ref: 'main',
|
||||
variables: updatedVariables,
|
||||
inputs: updatedInputs,
|
||||
},
|
||||
});
|
||||
expect(visitUrl).toHaveBeenCalledWith('/root/ci-project/-/pipeline_schedules');
|
||||
expect(createAlert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
it('shows error for failed pipeline schedule creation', async () => {
|
||||
createComponent({
|
||||
requestHandlers: [[createPipelineScheduleMutation, createMutationHandlerFailed]],
|
||||
});
|
||||
findSubmitButton().vm.$emit('click');
|
||||
|
||||
expect(createMutationHandlerSuccess).toHaveBeenCalledWith({
|
||||
input: {
|
||||
active: true,
|
||||
cron: '0 16 * * *',
|
||||
cronTimezone: 'America/New_York',
|
||||
description: 'My schedule',
|
||||
projectPath: 'gitlab-org/gitlab',
|
||||
ref: 'main',
|
||||
variables: [
|
||||
{
|
||||
key: 'test_var_2',
|
||||
value: 'value_2',
|
||||
variableType: 'ENV_VAR',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(visitUrl).toHaveBeenCalledWith('/root/ci-project/-/pipeline_schedules');
|
||||
expect(createAlert).not.toHaveBeenCalled();
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred while creating the pipeline schedule.',
|
||||
});
|
||||
});
|
||||
|
||||
describe('schedule creation failure', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
requestHandlers: [[createPipelineScheduleMutation, createMutationHandlerFailed]],
|
||||
});
|
||||
it('does not include inputs in mutation if feature flag is disabled', async () => {
|
||||
createComponent({
|
||||
requestHandlers: [[createPipelineScheduleMutation, createMutationHandlerSuccess]],
|
||||
});
|
||||
|
||||
it('shows error for failed pipeline schedule creation', async () => {
|
||||
findSubmitButton().vm.$emit('click');
|
||||
await waitForPromises();
|
||||
|
||||
await waitForPromises();
|
||||
await findSubmitButton().vm.$emit('click');
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred while creating the pipeline schedule.',
|
||||
});
|
||||
});
|
||||
expect(createMutationHandlerSuccess).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
input: expect.not.objectContaining({
|
||||
inputs: expect.anything(),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -330,25 +364,23 @@ describe('Pipeline schedules form', () => {
|
|||
expect(findPipelineVariables().props('initialVariables')).toHaveLength(variables.length);
|
||||
});
|
||||
|
||||
describe('schedule fetch success', () => {
|
||||
it('fetches schedule and sets form data correctly', async () => {
|
||||
createComponent({
|
||||
editing: true,
|
||||
requestHandlers: [[getPipelineSchedulesQuery, querySuccessHandler]],
|
||||
});
|
||||
|
||||
expect(querySuccessHandler).toHaveBeenCalled();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDescription().props('value')).toBe(schedule.description);
|
||||
expect(findIntervalComponent().props('initialCronInterval')).toBe(schedule.cron);
|
||||
expect(findTimezoneDropdown().props('value')).toBe(schedule.cronTimezone);
|
||||
expect(findRefSelector().props('value')).toBe(schedule.ref);
|
||||
expect(findPipelineVariables().props('initialVariables')).toHaveLength(2);
|
||||
expect(findPipelineVariables().props('initialVariables')[0].key).toBe(variables[0].key);
|
||||
expect(findPipelineVariables().props('initialVariables')[1].key).toBe(variables[1].key);
|
||||
it('fetches schedule and sets form data correctly', async () => {
|
||||
createComponent({
|
||||
editing: true,
|
||||
requestHandlers: [[getPipelineSchedulesQuery, querySuccessHandler]],
|
||||
});
|
||||
|
||||
expect(querySuccessHandler).toHaveBeenCalled();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDescription().props('value')).toBe(schedule.description);
|
||||
expect(findIntervalComponent().props('initialCronInterval')).toBe(schedule.cron);
|
||||
expect(findTimezoneDropdown().props('value')).toBe(schedule.cronTimezone);
|
||||
expect(findRefSelector().props('value')).toBe(schedule.ref);
|
||||
expect(findPipelineVariables().props('initialVariables')).toHaveLength(2);
|
||||
expect(findPipelineVariables().props('initialVariables')[0].key).toBe(variables[0].key);
|
||||
expect(findPipelineVariables().props('initialVariables')[1].key).toBe(variables[1].key);
|
||||
});
|
||||
|
||||
it('schedule fetch failure', async () => {
|
||||
|
|
@ -365,22 +397,11 @@ describe('Pipeline schedules form', () => {
|
|||
});
|
||||
|
||||
it('edit schedule success', async () => {
|
||||
createComponent({
|
||||
editing: true,
|
||||
requestHandlers: [
|
||||
[getPipelineSchedulesQuery, querySuccessHandler],
|
||||
[updatePipelineScheduleMutation, updateMutationHandlerSuccess],
|
||||
],
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
findDescription().vm.$emit('input', 'Updated schedule');
|
||||
|
||||
findIntervalComponent().vm.$emit('cronValue', '0 22 16 * *');
|
||||
|
||||
// Ensures variable is sent with destroy property set true
|
||||
findPipelineVariables().vm.$emit('update-variables', [
|
||||
const updatedInputs = [
|
||||
{ name: 'input1', value: 'value1' },
|
||||
{ name: 'input2', value: 'value2' },
|
||||
];
|
||||
const updatedVariables = [
|
||||
{
|
||||
id: variables[0].id,
|
||||
key: variables[0].key,
|
||||
|
|
@ -395,7 +416,26 @@ describe('Pipeline schedules form', () => {
|
|||
variableType: variables[1].variableType,
|
||||
destroy: false,
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
createComponent({
|
||||
ciInputsForPipelines: true,
|
||||
editing: true,
|
||||
requestHandlers: [
|
||||
[getPipelineSchedulesQuery, querySuccessHandler],
|
||||
[updatePipelineScheduleMutation, updateMutationHandlerSuccess],
|
||||
],
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
findDescription().vm.$emit('input', 'Updated schedule');
|
||||
|
||||
findIntervalComponent().vm.$emit('cronValue', '0 22 16 * *');
|
||||
|
||||
// Ensures variable is sent with destroy property set true
|
||||
findPipelineVariables().vm.$emit('update-variables', updatedVariables);
|
||||
findPipelineInputsForm().vm.$emit('update-inputs', updatedInputs);
|
||||
|
||||
findSubmitButton().vm.$emit('click');
|
||||
|
||||
|
|
@ -409,22 +449,8 @@ describe('Pipeline schedules form', () => {
|
|||
id: schedule.id,
|
||||
ref: schedule.ref,
|
||||
description: 'Updated schedule',
|
||||
variables: [
|
||||
{
|
||||
destroy: true,
|
||||
id: variables[0].id,
|
||||
key: variables[0].key,
|
||||
value: variables[0].value,
|
||||
variableType: variables[0].variableType,
|
||||
},
|
||||
{
|
||||
destroy: false,
|
||||
id: variables[1].id,
|
||||
key: variables[1].key,
|
||||
value: variables[1].value,
|
||||
variableType: variables[1].variableType,
|
||||
},
|
||||
],
|
||||
variables: updatedVariables,
|
||||
inputs: updatedInputs,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
@ -448,5 +474,24 @@ describe('Pipeline schedules form', () => {
|
|||
message: 'An error occurred while updating the pipeline schedule.',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not include inputs in mutation if feature flag is disabled', async () => {
|
||||
createComponent({
|
||||
editing: true,
|
||||
requestHandlers: [[updatePipelineScheduleMutation, updateMutationHandlerSuccess]],
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
await findSubmitButton().vm.$emit('click');
|
||||
|
||||
expect(updateMutationHandlerSuccess).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
input: expect.not.objectContaining({
|
||||
inputs: expect.anything(),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,12 +9,19 @@ RSpec.describe Mutations::ContainerRepositories::Destroy, feature_category: :con
|
|||
let_it_be_with_reload(:container_repository) { create(:container_repository) }
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
let(:project) { container_repository.project }
|
||||
let_it_be(:project) { container_repository.project }
|
||||
let(:id) { container_repository.to_global_id }
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:destroy_container_image) }
|
||||
|
||||
describe '#resolve' do
|
||||
let(:tags) { %w[a b c] }
|
||||
|
||||
before do
|
||||
stub_container_registry_config(enabled: true)
|
||||
stub_container_registry_tags(tags: tags)
|
||||
end
|
||||
|
||||
subject do
|
||||
described_class.new(object: nil, context: query_context, field: nil)
|
||||
.resolve(id: id)
|
||||
|
|
@ -30,7 +37,7 @@ RSpec.describe Mutations::ContainerRepositories::Destroy, feature_category: :con
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'denying access to container respository' do
|
||||
shared_examples 'denying access to container repository' do
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
|
|
@ -40,9 +47,9 @@ RSpec.describe Mutations::ContainerRepositories::Destroy, feature_category: :con
|
|||
where(:user_role, :shared_examples_name) do
|
||||
:maintainer | 'destroying the container repository'
|
||||
:developer | 'destroying the container repository'
|
||||
:reporter | 'denying access to container respository'
|
||||
:guest | 'denying access to container respository'
|
||||
:anonymous | 'denying access to container respository'
|
||||
:reporter | 'denying access to container repository'
|
||||
:guest | 'denying access to container repository'
|
||||
:anonymous | 'denying access to container repository'
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
@ -53,5 +60,64 @@ RSpec.describe Mutations::ContainerRepositories::Destroy, feature_category: :con
|
|||
it_behaves_like params[:shared_examples_name]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project has tag protection rules' do
|
||||
before_all do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_delete: :owner
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the container repository has tags' do
|
||||
where(:user_role, :shared_examples_name) do
|
||||
:owner | 'destroying the container repository'
|
||||
:maintainer | 'denying access to container repository'
|
||||
:developer | 'denying access to container repository'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name]
|
||||
end
|
||||
|
||||
context 'when the current user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { build_stubbed(:admin) }
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
|
||||
context 'when the feature container_registry_protected_tags is disabled' do
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
stub_feature_flags(container_registry_protected_tags: false)
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the container repository does not have tags' do
|
||||
let(:tags) { [] }
|
||||
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::Helpers::Snippets::HttpResponseMap, feature_category: :source_code_management do
|
||||
describe '.status_for' do
|
||||
context 'when reason is in the map' do
|
||||
it 'returns the corresponding HTTP status', :aggregate_failures do
|
||||
expect(described_class.status_for(:success)).to eq(200)
|
||||
expect(described_class.status_for(:error)).to eq(400)
|
||||
expect(described_class.status_for(:invalid_params_error)).to eq(422)
|
||||
expect(described_class.status_for(:failed_to_create_error)).to eq(400)
|
||||
expect(described_class.status_for(:failed_to_update_error)).to eq(400)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when reason is not in the map' do
|
||||
it 'returns 500 and logs a structured warning' do
|
||||
some_unknown_reason = :some_unknown_reason
|
||||
|
||||
expect(Gitlab::AppLogger).to receive(:warn).with(
|
||||
message: described_class::UNHANDLED,
|
||||
reason: some_unknown_reason.inspect
|
||||
)
|
||||
|
||||
expect(described_class.status_for(some_unknown_reason)).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -70,6 +70,8 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :cell do
|
|||
'dast_profiles_pipelines.project_id', # LFK already present on dast_profiles and will cascade delete
|
||||
'dast_scanner_profiles_builds.project_id', # LFK already present on dast_scanner_profiles and will cascade delete
|
||||
'vulnerability_finding_links.project_id', # LFK already present on vulnerability_occurrence with cascade delete
|
||||
'secret_detection_token_statuses.project_id',
|
||||
# LFK already present on vulnerability_occurrence with cascade delete.
|
||||
'ldap_group_links.group_id',
|
||||
'namespace_descendants.namespace_id',
|
||||
'p_batched_git_ref_updates_deletions.project_id',
|
||||
|
|
|
|||
|
|
@ -4,14 +4,10 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::Database::Sos::DbLoopStatsActivity, feature_category: :database do
|
||||
let(:temp_directory) { Dir.mktmpdir }
|
||||
let(:output_file_path) { temp_directory }
|
||||
let(:output) { Gitlab::Database::Sos::Output.new(temp_directory, mode: :directory) }
|
||||
let(:db_name) { 'test_db' }
|
||||
let(:connection) { ApplicationRecord.connection }
|
||||
let(:handler) { described_class.new(connection, db_name, output) }
|
||||
let(:query) { { pg_stat_user_indexes: "SELECT * FROM pg_stat_user_indexes;" } }
|
||||
let(:result) { ApplicationRecord.connection.execute(query[:pg_stat_user_indexes]) }
|
||||
let(:timestamp) { Time.zone.now.strftime("%Y%m%d_%H%M%S") }
|
||||
|
||||
after do
|
||||
FileUtils.remove_entry(temp_directory)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ RSpec.describe Gitlab::Database::Sos::DbStatsActivity, feature_category: :databa
|
|||
end
|
||||
|
||||
describe '#run' do
|
||||
it 'executes each query successfully and writes results to CSV' do
|
||||
it 'successfully writes each query result to csv' do
|
||||
handler.run
|
||||
|
||||
described_class::QUERIES.each_key do |name|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::Sos::PgStatStatements, feature_category: :database do
|
||||
include Database::DatabaseHelpers
|
||||
|
||||
let(:temp_directory) { Dir.mktmpdir }
|
||||
let(:output) { Gitlab::Database::Sos::Output.new(temp_directory, mode: :directory) }
|
||||
let(:db_name) { 'test_db' }
|
||||
let(:connection) { ApplicationRecord.connection }
|
||||
let(:handler) { described_class.new(connection, db_name, output) }
|
||||
|
||||
after do
|
||||
FileUtils.remove_entry(temp_directory)
|
||||
end
|
||||
|
||||
describe '#run' do
|
||||
context 'when pg_stat_statements is installed' do
|
||||
before do
|
||||
connection.execute(<<~SQL)
|
||||
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
||||
CREATE TABLE _test_pg_stat_statements_copy (LIKE pg_stat_statements);
|
||||
CREATE OR REPLACE VIEW pg_stat_statements AS ( SELECT * FROM _test_pg_stat_statements_copy );
|
||||
SQL
|
||||
|
||||
connection.execute(<<~SQL.squish)
|
||||
INSERT INTO pg_stat_statements (
|
||||
userid, dbid, queryid, query,
|
||||
plans, total_plan_time, min_plan_time, max_plan_time, mean_plan_time)
|
||||
VALUES (
|
||||
1, 2727493, 23234938938, 'ALTER TABLE "table" DISABLE TRIGGER ALL', 0,
|
||||
0.0, 0.0, 0.0, 0.0)
|
||||
SQL
|
||||
end
|
||||
|
||||
it "successfully writes the executed query results to CSV" do
|
||||
allow(handler).to receive(:pg_stat_statements_installed?).and_return(true)
|
||||
handler.run
|
||||
file_path = File.join(temp_directory, db_name, "pg_stat_statements", "*.csv")
|
||||
expect(Dir.glob(file_path).any?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pg_stat_statements is not installed' do
|
||||
it "skips executing and writing to csv" do
|
||||
allow(handler).to receive(:pg_stat_statements_installed?).and_return(false)
|
||||
|
||||
handler.run
|
||||
|
||||
file_path = File.join(temp_directory, db_name, "pg_stat_statements", "*.csv")
|
||||
expect(Dir.glob(file_path).any?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pg_stats_statements_installed?' do
|
||||
context 'when the pg_stat_statements is installed' do
|
||||
before do
|
||||
connection.execute('CREATE EXTENSION IF NOT EXISTS pg_stat_statements;')
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(handler.pg_stat_statements_installed?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pg_stat_statments is not installed' do
|
||||
before do
|
||||
connection.execute('DROP EXTENSION IF EXISTS pg_stat_statements;')
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(handler.pg_stat_statements_installed?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1238,6 +1238,7 @@ vulnerability_finding:
|
|||
- feedbacks
|
||||
- finding_evidence
|
||||
- security_findings
|
||||
- finding_token_status
|
||||
scanner:
|
||||
- findings
|
||||
- security_findings
|
||||
|
|
|
|||
|
|
@ -1256,4 +1256,75 @@ RSpec.describe ContainerRepository, :aggregate_failures, feature_category: :cont
|
|||
expect(registry2.object_id).not_to be(registry.object_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_protected_tag_rules_for_delete?' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:has_tags) { true }
|
||||
|
||||
subject { repository.has_protected_tag_rules_for_delete?(user) }
|
||||
|
||||
before do
|
||||
allow(repository).to receive(:has_tags?).and_return(has_tags)
|
||||
end
|
||||
|
||||
context 'when the feature container_registry_protected_tags is disabled' do
|
||||
before do
|
||||
stub_feature_flags(container_registry_protected_tags: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when the project does not have tag protection rules' do
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when the user is nil' do
|
||||
let(:user) { nil }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when the project has tag protection rules' do
|
||||
let_it_be(:project) { create(:project, path: 'test') }
|
||||
|
||||
before_all do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_delete: Gitlab::Access::OWNER
|
||||
)
|
||||
end
|
||||
|
||||
context 'for admin' do
|
||||
before do
|
||||
allow(user).to receive(:can_admin_all_resources?).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when user has lower access level' do
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'when the container repository does not have tags' do
|
||||
let(:has_tags) { false }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has the same or higher access level' do
|
||||
before_all do
|
||||
project.add_owner(user)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9956,4 +9956,41 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_container_registry_protected_tag_rules?' do
|
||||
let_it_be_with_refind(:project) { create(:project) }
|
||||
|
||||
subject { project.has_container_registry_protected_tag_rules?(action: 'delete', access_level: Gitlab::Access::OWNER) }
|
||||
|
||||
it 'returns false when there is no matching tag protection rule' do
|
||||
create(:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_push: :admin,
|
||||
minimum_access_level_for_delete: :maintainer
|
||||
)
|
||||
|
||||
expect(subject).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns true when there exists a matching tag protection rule' do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_push: :maintainer,
|
||||
minimum_access_level_for_delete: :admin
|
||||
)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
|
||||
it 'memoizes the call' do
|
||||
allow(project.container_registry_protection_tag_rules).to receive(:for_actions_and_access).and_call_original
|
||||
|
||||
2.times do
|
||||
project.has_container_registry_protected_tag_rules?(action: 'push', access_level: :maintainer)
|
||||
end
|
||||
|
||||
expect(project.container_registry_protection_tag_rules).to have_received(:for_actions_and_access).with(%w[push], :maintainer).once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ContainerRepositoryPolicy, feature_category: :container_registry do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be_with_reload(:project) { create(:project, creator: user) }
|
||||
let_it_be_with_reload(:container_repository) { create(:container_repository, project: project) }
|
||||
|
||||
subject { described_class.new(user, container_repository) }
|
||||
|
||||
shared_examples 'not allowing anonymous user' do
|
||||
context 'when the current user is anonymous' do
|
||||
let(:user) { nil }
|
||||
|
||||
it { is_expected.to be_disallowed(:destroy_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'destroy_container_image' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
before do
|
||||
allow(container_repository).to receive(:has_tags?).and_return(has_tags)
|
||||
end
|
||||
|
||||
context 'when the project has tag protection rules' do
|
||||
before_all do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_delete: :owner
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the container repository has tags' do
|
||||
let(:has_tags) { true }
|
||||
|
||||
where(:user_role, :expected_result) do
|
||||
:owner | :be_allowed
|
||||
:maintainer | :be_disallowed
|
||||
:developer | :be_disallowed
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send(:"add_#{user_role}", user)
|
||||
end
|
||||
|
||||
it { is_expected.to send(expected_result, :destroy_container_image) }
|
||||
end
|
||||
|
||||
context 'when the current user is an admin', :enable_admin_mode do
|
||||
let(:user) { build_stubbed(:admin) }
|
||||
|
||||
it { expect_allowed(:destroy_container_image) }
|
||||
end
|
||||
|
||||
it_behaves_like 'not allowing anonymous user'
|
||||
|
||||
context 'when the feature container_registry_protected_tags is disabled' do
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
stub_feature_flags(container_registry_protected_tags: false)
|
||||
project.send(:"add_#{user_role}", user)
|
||||
end
|
||||
|
||||
it { expect_allowed(:destroy_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'not allowing anonymous user'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the container repository does not have tags' do
|
||||
let(:has_tags) { false }
|
||||
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
project.send(:"add_#{user_role}", user)
|
||||
end
|
||||
|
||||
it { expect_allowed(:destroy_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'not allowing anonymous user'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project does not have tag protection rules' do
|
||||
let(:has_tags) { true }
|
||||
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
project.send(:"add_#{user_role}", user)
|
||||
end
|
||||
|
||||
it { expect_allowed(:destroy_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'not allowing anonymous user'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -8,9 +8,9 @@ RSpec.describe 'Destroying a container repository', feature_category: :container
|
|||
include GraphqlHelpers
|
||||
|
||||
let_it_be_with_reload(:container_repository) { create(:container_repository) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
let(:project) { container_repository.project }
|
||||
let_it_be(:project) { container_repository.project }
|
||||
let(:id) { container_repository.to_global_id.to_s }
|
||||
|
||||
let(:query) do
|
||||
|
|
@ -27,15 +27,17 @@ RSpec.describe 'Destroying a container repository', feature_category: :container
|
|||
let(:mutation_response) { graphql_mutation_response(:destroyContainerRepository) }
|
||||
let(:container_repository_mutation_response) { mutation_response['containerRepository'] }
|
||||
|
||||
let(:tags) { %w[a b c] }
|
||||
|
||||
before do
|
||||
stub_container_registry_config(enabled: true)
|
||||
stub_container_registry_tags(tags: %w[a b c])
|
||||
stub_container_registry_tags(tags: tags)
|
||||
end
|
||||
|
||||
shared_examples 'destroying the container repository' do
|
||||
it 'marks the container repository as delete_scheduled' do
|
||||
expect(::Packages::CreateEventService)
|
||||
.to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original
|
||||
.to receive(:new).with(nil, current_user, event_name: :delete_repository, scope: :container).and_call_original
|
||||
|
||||
subject
|
||||
|
||||
|
|
@ -56,8 +58,19 @@ RSpec.describe 'Destroying a container repository', feature_category: :container
|
|||
it_behaves_like 'returning response status', :success
|
||||
end
|
||||
|
||||
shared_examples 'returning an error' do
|
||||
it 'returns an error' do
|
||||
subject
|
||||
|
||||
expect_graphql_errors_to_include(
|
||||
'The resource that you are attempting to access does not exist ' \
|
||||
'or you don\'t have permission to perform this action'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'post graphql mutation' do
|
||||
subject { post_graphql_mutation(mutation, current_user: user) }
|
||||
subject { post_graphql_mutation(mutation, current_user:) }
|
||||
|
||||
context 'with valid id' do
|
||||
where(:user_role, :shared_examples_name) do
|
||||
|
|
@ -70,7 +83,7 @@ RSpec.describe 'Destroying a container repository', feature_category: :container
|
|||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", user) unless user_role == :anonymous
|
||||
project.send("add_#{user_role}", current_user) unless user_role == :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name]
|
||||
|
|
@ -82,5 +95,62 @@ RSpec.describe 'Destroying a container repository', feature_category: :container
|
|||
|
||||
it_behaves_like 'denying the mutation request'
|
||||
end
|
||||
|
||||
context 'when the project has tag protection rules' do
|
||||
before_all do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_delete: :owner
|
||||
)
|
||||
end
|
||||
|
||||
where(:user_role, :shared_examples_name) do
|
||||
:owner | 'destroying the container repository'
|
||||
:maintainer | 'returning an error'
|
||||
:developer | 'returning an error'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name]
|
||||
end
|
||||
|
||||
context 'when the container repository does not have tags' do
|
||||
let(:tags) { [] }
|
||||
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the current user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { create(:admin) }
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
|
||||
context 'when the feature container_registry_protected_tags is disabled' do
|
||||
%i[owner maintainer developer].each do |user_role|
|
||||
context "with the role of #{user_role}" do
|
||||
before do
|
||||
stub_feature_flags(container_registry_protected_tags: false)
|
||||
project.send("add_#{user_role}", current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'destroying the container repository'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -285,4 +285,55 @@ RSpec.describe 'getting container repositories in a project', feature_category:
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'destroyContainerRepository' do
|
||||
describe 'efficient database queries' do
|
||||
let_it_be(:project) { create(:project, :private) }
|
||||
let_it_be(:project_container_repositories) { create_list(:container_repository, 2, project: project) }
|
||||
|
||||
let(:fields) do
|
||||
<<~GQL
|
||||
containerRepositories {
|
||||
nodes {
|
||||
userPermissions {
|
||||
destroyContainerRepository
|
||||
}
|
||||
}
|
||||
}
|
||||
GQL
|
||||
end
|
||||
|
||||
before_all do
|
||||
create(:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
tag_name_pattern: 'x'
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
project_container_repositories.each do |repository|
|
||||
stub_container_registry_tags(repository: repository.path, tags: %w[tag1 tag2 tag3], with_manifest: false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'avoids N+1 database queries', :use_sql_query_cache do
|
||||
query = graphql_query_for('project', { 'fullPath' => project.full_path }, fields)
|
||||
|
||||
first_user = create(:user, developer_of: project)
|
||||
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
|
||||
post_graphql(query, current_user: first_user)
|
||||
end
|
||||
|
||||
second_user = create(:user, developer_of: project)
|
||||
new_repositories = create_list(:container_repository, 2, project: project)
|
||||
new_repositories.each do |repository|
|
||||
stub_container_registry_tags(repository: repository.path, tags: %w[tag1 tag2 tag3], with_manifest: false)
|
||||
end
|
||||
|
||||
expect do
|
||||
post_graphql(query, current_user: second_user)
|
||||
end.to issue_same_number_of_queries_as(control)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gitlab/hard_delete_calls'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gitlab::HardDeleteCalls, feature_category: :incident_management do
|
||||
it 'registers an offense when using Projects::DestroyService' do
|
||||
expect_offense(<<~RUBY)
|
||||
Projects::DestroyService.new(project, user).execute
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid the use of `Projects::DestroyService`. Use `Projects::MarkForDeletionService` instead. [...]
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using Projects::DestroyWorker' do
|
||||
expect_offense(<<~RUBY)
|
||||
ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid the use of `ProjectDestroyWorker`. Use `Projects::MarkForDeletionService` instead. [...]
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using Groups::DestroyService' do
|
||||
expect_offense(<<~RUBY)
|
||||
Groups::DestroyService.new(group, user).execute
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid the use of `Groups::DestroyService`. Use `Groups::MarkForDeletionService` instead. [...]
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using GroupDestroyWorker' do
|
||||
expect_offense(<<~RUBY)
|
||||
GroupDestroyWorker.perform_async(group.id, user.id)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid the use of `GroupDestroyWorker`. Use `Groups::MarkForDeletionService` instead. [...]
|
||||
RUBY
|
||||
end
|
||||
|
||||
context 'when hard delete classes are called with safe navigation' do
|
||||
it 'registers an offense for Projects::DestroyService with safe navigation' do
|
||||
expect_offense(<<~RUBY)
|
||||
def delete_project(project)
|
||||
Projects::DestroyService&.new(project, user)&.execute
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid the use of `Projects::DestroyService`. Use `Projects::MarkForDeletionService` instead. [...]
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -91,6 +91,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
expect(fork_network).not_to be_nil
|
||||
expect(fork_network.root_project).to eq(project)
|
||||
expect(fork_network.projects).to contain_exactly(project, fork_of_project)
|
||||
expect(fork_network.organization).to eq(project.organization)
|
||||
end
|
||||
|
||||
it 'imports the repository of the forked project', :sidekiq_might_not_need_inline do
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ RSpec.shared_examples 'invalid params error response' do
|
|||
|
||||
aggregate_failures do
|
||||
expect(response).to be_error
|
||||
expect(response.http_status).to eq 422
|
||||
expect(response.reason).to eq(described_class::INVALID_PARAMS_ERROR)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue