Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-06 15:16:24 +00:00
parent 4dd72e0b05
commit 9186354c5c
76 changed files with 672 additions and 444 deletions

View File

@ -37,10 +37,9 @@ See [the general developer security guidelines](https://gitlab.com/gitlab-org/re
/label ~security
[GitLab Security]: https://gitlab.com/gitlab-org/security/gitlab
[quick actions]: https://docs.gitlab.com/ee/user/project/quick_actions.html#quick-actions-for-issues-merge-requests-and-epics
[CHANGELOG entry]: https://docs.gitlab.com/ee/development/changelog.html#overview
[Code Review process]: https://docs.gitlab.com/ee/development/code_review.html
[Code reviews and Approvals]: (https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#code-reviews-and-approvals)
[Code reviews and Approvals]: (https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/engineer.md#code-reviews-and-approvals)
[Approval Guidelines]: https://docs.gitlab.com/ee/development/code_review.html#approval-guidelines
[Canonical repository]: https://gitlab.com/gitlab-org/gitlab
[`e2e:package-and-test` job]: https://docs.gitlab.com/ee/development/testing_guide/end_to_end/#using-the-package-and-test-job

View File

@ -85,22 +85,6 @@ Layout/ArgumentAlignment:
- 'app/graphql/types/board_type.rb'
- 'app/graphql/types/boards/board_issuable_input_base_type.rb'
- 'app/graphql/types/boards/board_issue_input_base_type.rb'
- 'app/graphql/types/commit_signature_interface.rb'
- 'app/graphql/types/commit_signatures/gpg_signature_type.rb'
- 'app/graphql/types/commit_signatures/ssh_signature_type.rb'
- 'app/graphql/types/commit_signatures/x509_signature_type.rb'
- 'app/graphql/types/commit_type.rb'
- 'app/graphql/types/container_repository_details_type.rb'
- 'app/graphql/types/countable_connection_type.rb'
- 'app/graphql/types/current_user_todos.rb'
- 'app/graphql/types/custom_emoji_type.rb'
- 'app/graphql/types/customer_relations/contact_state_counts_type.rb'
- 'app/graphql/types/customer_relations/contact_state_enum.rb'
- 'app/graphql/types/customer_relations/contact_type.rb'
- 'app/graphql/types/customer_relations/organization_state_counts_type.rb'
- 'app/graphql/types/customer_relations/organization_state_enum.rb'
- 'app/graphql/types/customer_relations/organization_type.rb'
- 'app/graphql/types/dependency_proxy/manifest_type.rb'
- 'app/graphql/types/error_tracking/sentry_detailed_error_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_collection_type.rb'
- 'app/graphql/types/error_tracking/sentry_error_stack_trace_context_type.rb'

View File

@ -141,7 +141,7 @@ export default {
class="gl-display-flex gl-align-items-center gl-justify-content-end gl-md-justify-content-start"
data-testid="terraform-states-table-name"
>
<p class="gl-font-weight-bold gl-m-0 gl-text-gray-900">
<p class="gl-m-0 gl-text-gray-900">
{{ item.name }}
</p>
@ -188,16 +188,15 @@ export default {
</template>
<template #cell(pipeline)="{ item }">
<div data-testid="terraform-states-table-pipeline" class="gl-min-h-7">
<div
data-testid="terraform-states-table-pipeline"
class="gl-md-display-flex gl-align-items-center gl-gap-3"
>
<gl-link v-if="pipelineID(item)" :href="pipelinePath(item)">
#{{ pipelineID(item) }}
</gl-link>
<div
v-if="pipelineDetailedStatus(item)"
:id="`terraformJobStatusContainer${item.name}`"
class="gl-my-2"
>
<div v-if="pipelineDetailedStatus(item)" :id="`terraformJobStatusContainer${item.name}`">
<ci-icon
:id="`terraformJobStatus${item.name}`"
:status="pipelineDetailedStatus(item)"

View File

@ -60,7 +60,7 @@ export default {
'Terraform|To remove the State file and its versions, type %{name} to confirm:',
),
modalRemove: s__('Terraform|Remove'),
remove: s__('Terraform|Remove state file and versions'),
remove: s__('Terraform|Remove'),
removeSuccessful: s__('Terraform|%{name} successfully removed'),
unlock: s__('Terraform|Unlock'),
copyCommand: s__('Terraform|Copy Terraform init command'),
@ -195,6 +195,7 @@ export default {
<div>
<gl-dropdown
icon="ellipsis_v"
category="tertiary"
right
:data-testid="`terraform-state-actions-${state.name}`"
:disabled="loading"
@ -232,7 +233,7 @@ export default {
<gl-dropdown-divider />
<gl-dropdown-item data-testid="terraform-state-remove" @click="showRemoveModal = true">
{{ $options.i18n.remove }}
<span class="gl-text-red-500">{{ $options.i18n.remove }}</span>
</gl-dropdown-item>
</gl-dropdown>

View File

@ -1,5 +1,5 @@
<script>
import { GlAlert, GlBadge, GlKeysetPagination, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { GlAlert, GlCard, GlIcon, GlKeysetPagination, GlLoadingIcon } from '@gitlab/ui';
import { MAX_LIST_COUNT } from '../constants';
import getStatesQuery from '../graphql/queries/get_states.query.graphql';
import EmptyState from './empty_state.vue';
@ -24,11 +24,10 @@ export default {
components: {
EmptyState,
GlAlert,
GlBadge,
GlCard,
GlIcon,
GlKeysetPagination,
GlLoadingIcon,
GlTab,
GlTabs,
StatesTable,
},
inject: ['projectPath'],
@ -93,33 +92,35 @@ export default {
<template>
<section>
<gl-tabs>
<gl-tab>
<template #title>
<p class="gl-m-0">
{{ s__('Terraform|Terraform states') }}
<gl-badge v-if="statesCount">{{ statesCount }}</gl-badge>
</p>
</template>
<gl-card
class="gl-new-card"
header-class="gl-new-card-header"
body-class="gl-new-card-body gl-px-0"
>
<template #header>
<div class="gl-new-card-title-wrapper">
<h5 class="gl-new-card-title">{{ s__('Terraform|Terraform states') }}</h5>
<span class="gl-new-card-count">
<gl-icon name="terraform" class="gl-mr-2" />
{{ statesCount }}
</span>
</div>
</template>
<gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-3" />
<div v-else-if="statesList">
<div v-if="statesCount">
<states-table :states="statesList" :terraform-admin="terraformAdmin" />
<gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-3" />
<div v-else-if="statesList">
<div v-if="statesCount">
<states-table :states="statesList" :terraform-admin="terraformAdmin" />
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
<gl-keyset-pagination v-bind="pageInfo" @prev="prevPage" @next="nextPage" />
</div>
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
<gl-keyset-pagination v-bind="pageInfo" @prev="prevPage" @next="nextPage" />
</div>
<empty-state v-else :image="emptyStateImage" />
</div>
<gl-alert v-else variant="danger" :dismissible="false">
{{ s__('Terraform|An error occurred while loading your Terraform States') }}
</gl-alert>
</gl-tab>
</gl-tabs>
<empty-state v-else :image="emptyStateImage" />
</div>
<gl-alert v-else variant="danger" :dismissible="false">
{{ s__('Terraform|An error occurred while loading your Terraform States') }}
</gl-alert>
</gl-card>
</section>
</template>

View File

@ -9,20 +9,20 @@ module Types
description 'Represents signing information for a commit'
field :verification_status, CommitSignatures::VerificationStatusEnum,
null: true,
description: 'Indicates verification status of the associated key or certificate.'
null: true,
description: 'Indicates verification status of the associated key or certificate.'
field :commit_sha, GraphQL::Types::String,
null: true,
description: 'SHA of the associated commit.'
null: true,
description: 'SHA of the associated commit.'
field :project, Types::ProjectType,
null: true,
description: 'Project of the associated commit.'
null: true,
description: 'Project of the associated commit.'
orphan_types Types::CommitSignatures::GpgSignatureType,
Types::CommitSignatures::X509SignatureType,
Types::CommitSignatures::SshSignatureType
Types::CommitSignatures::X509SignatureType,
Types::CommitSignatures::SshSignatureType
def self.resolve_type(object, context)
case object

View File

@ -11,20 +11,20 @@ module Types
authorize :download_code
field :user, Types::UserType, null: true,
method: :signed_by_user,
description: 'User associated with the key.'
method: :signed_by_user,
description: 'User associated with the key.'
field :gpg_key_user_name, GraphQL::Types::String,
null: true,
description: 'User name associated with the GPG key.'
null: true,
description: 'User name associated with the GPG key.'
field :gpg_key_user_email, GraphQL::Types::String,
null: true,
description: 'User email associated with the GPG key.'
null: true,
description: 'User email associated with the GPG key.'
field :gpg_key_primary_keyid, GraphQL::Types::String,
null: true,
description: 'ID of the GPG key.'
null: true,
description: 'ID of the GPG key.'
end
end
end

View File

@ -11,13 +11,13 @@ module Types
authorize :download_code
field :user, Types::UserType, null: true,
method: :signed_by_user,
calls_gitaly: true,
description: 'User associated with the key.'
method: :signed_by_user,
calls_gitaly: true,
description: 'User associated with the key.'
field :x509_certificate, Types::X509CertificateType,
null: true,
description: 'Certificate used for the signature.'
null: true,
description: 'Certificate used for the signature.'
end
end
end

View File

@ -11,75 +11,75 @@ module Types
implements Types::TodoableInterface
field :id, type: GraphQL::Types::ID, null: false,
description: 'ID (global ID) of the commit.'
description: 'ID (global ID) of the commit.'
field :sha, type: GraphQL::Types::String, null: false,
description: 'SHA1 ID of the commit.'
description: 'SHA1 ID of the commit.'
field :short_id, type: GraphQL::Types::String, null: false,
description: 'Short SHA1 ID of the commit.'
description: 'Short SHA1 ID of the commit.'
field :title, type: GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Title of the commit message.'
description: 'Title of the commit message.'
field :full_title, type: GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Full title of the commit message.'
description: 'Full title of the commit message.'
field :description, type: GraphQL::Types::String, null: true,
description: 'Description of the commit message.'
description: 'Description of the commit message.'
field :message, type: GraphQL::Types::String, null: true,
description: 'Raw commit message.'
description: 'Raw commit message.'
field :authored_date, type: Types::TimeType, null: true,
description: 'Timestamp of when the commit was authored.'
description: 'Timestamp of when the commit was authored.'
field :committed_date, type: Types::TimeType, null: true,
description: 'Timestamp of when the commit was committed.'
description: 'Timestamp of when the commit was committed.'
field :web_url, type: GraphQL::Types::String, null: false,
description: 'Web URL of the commit.'
description: 'Web URL of the commit.'
field :web_path, type: GraphQL::Types::String, null: false,
description: 'Web path of the commit.'
description: 'Web path of the commit.'
field :signature, type: Types::CommitSignatureInterface,
null: true,
calls_gitaly: true,
description: 'Signature of the commit.'
null: true,
calls_gitaly: true,
description: 'Signature of the commit.'
field :signature_html, type: GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Rendered HTML of the commit signature.'
description: 'Rendered HTML of the commit signature.'
field :author_email, type: GraphQL::Types::String, null: true,
description: "Commit author's email."
description: "Commit author's email."
field :author_gravatar, type: GraphQL::Types::String, null: true,
description: 'Commit authors gravatar.'
description: 'Commit authors gravatar.'
field :author_name, type: GraphQL::Types::String, null: true,
description: 'Commit authors name.'
description: 'Commit authors name.'
field :committer_email, type: GraphQL::Types::String, null: true,
description: "Email of the committer."
description: "Email of the committer."
field :committer_name, type: GraphQL::Types::String, null: true,
description: "Name of the committer."
description: "Name of the committer."
# models/commit lazy loads the author by email
field :author, type: Types::UserType, null: true,
description: 'Author of the commit.'
description: 'Author of the commit.'
field :diffs, [Types::DiffType], null: true, calls_gitaly: true,
description: 'Diffs contained within the commit. ' \
'This field can only be resolved for 10 diffs in any single request.' do
description: 'Diffs contained within the commit. ' \
'This field can only be resolved for 10 diffs in any single request.' do
# Limited to 10 calls per GraphQL request as calling `diffs` multiple times will
# lead to N+1 queries to Gitaly.
extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 10
end
field :pipelines,
null: true,
description: 'Pipelines of the commit ordered latest first.',
resolver: Resolvers::CommitPipelinesResolver
null: true,
description: 'Pipelines of the commit ordered latest first.',
resolver: Resolvers::CommitPipelinesResolver
markdown_field :title_html, null: true
markdown_field :full_title_html, null: true

View File

@ -9,25 +9,25 @@ module Types
authorize :read_container_image
field :tags,
Types::ContainerRepositoryTagType.connection_type,
null: true,
description: 'Tags of the container repository.',
max_page_size: 20,
resolver: Resolvers::ContainerRepositoryTagsResolver,
connection_extension: Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
Types::ContainerRepositoryTagType.connection_type,
null: true,
description: 'Tags of the container repository.',
max_page_size: 20,
resolver: Resolvers::ContainerRepositoryTagsResolver,
connection_extension: Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
field :manifest, GraphQL::Types::String,
null: true,
description: 'An image manifest from the container repository.' do
argument :reference, GraphQL::Types::String,
required: true,
description: 'Tag name or digest of the manifest.'
end
null: true,
description: 'An image manifest from the container repository.' do
argument :reference, GraphQL::Types::String,
required: true,
description: 'Tag name or digest of the manifest.'
end
field :size,
GraphQL::Types::Float,
null: true,
description: 'Deduplicated size of the image repository in bytes. This is only available on GitLab.com for repositories created after `2021-11-04`.'
GraphQL::Types::Float,
null: true,
description: 'Deduplicated size of the image repository in bytes. This is only available on GitLab.com for repositories created after `2021-11-04`.'
def can_delete
Ability.allowed?(current_user, :destroy_container_image, object)

View File

@ -4,7 +4,7 @@ module Types
# rubocop: disable Graphql/AuthorizeTypes
class CountableConnectionType < GraphQL::Types::Relay::BaseConnection
field :count, GraphQL::Types::Int, null: false,
description: 'Total count of collection.'
description: 'Total count of collection.'
def count
# rubocop: disable CodeReuse/ActiveRecord

View File

@ -8,12 +8,12 @@ module Types
field_class Types::BaseField
field :current_user_todos, Types::TodoType.connection_type,
description: 'To-do items for the current user.',
null: false do
argument :state, Types::TodoStateEnum,
description: 'State of the to-do items.',
required: false
end
description: 'To-do items for the current user.',
null: false do
argument :state, Types::TodoStateEnum,
description: 'State of the to-do items.',
required: false
end
def current_user_todos(state: nil)
state ||= %i[done pending] # TodosFinder treats a `nil` state param as `pending`

View File

@ -12,23 +12,23 @@ module Types
expose_permissions Types::PermissionTypes::CustomEmoji
field :id, ::Types::GlobalIDType[::CustomEmoji],
null: false,
description: 'ID of the emoji.'
null: false,
description: 'ID of the emoji.'
field :name, GraphQL::Types::String,
null: false,
description: 'Name of the emoji.'
null: false,
description: 'Name of the emoji.'
field :url, GraphQL::Types::String,
null: false,
description: 'Link to file of the emoji.'
null: false,
description: 'Link to file of the emoji.'
field :external, GraphQL::Types::Boolean,
null: false,
description: 'Whether the emoji is an external link.'
null: false,
description: 'Whether the emoji is an external link.'
field :created_at, Types::TimeType,
null: false,
description: 'Timestamp of when the custom emoji was created.'
null: false,
description: 'Timestamp of when the custom emoji was created.'
end
end

View File

@ -14,9 +14,9 @@ module Types
available_contact_states.each do |state|
field state,
GraphQL::Types::Int,
null: true,
description: "Number of contacts with state `#{state.upcase}`"
GraphQL::Types::Int,
null: true,
description: "Number of contacts with state `#{state.upcase}`"
end
end
end

View File

@ -6,16 +6,16 @@ module Types
graphql_name 'CustomerRelationsContactState'
value 'all',
description: "All available contacts.",
value: :all
description: "All available contacts.",
value: :all
value 'active',
description: "Active contacts.",
value: :active
description: "Active contacts.",
value: :active
value 'inactive',
description: "Inactive contacts.",
value: :inactive
description: "Inactive contacts.",
value: :inactive
end
end
end

View File

@ -8,53 +8,53 @@ module Types
authorize :read_crm_contact
field :id,
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the contact.'
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the contact.'
field :organization, Types::CustomerRelations::OrganizationType,
null: true,
description: "Organization of the contact."
null: true,
description: "Organization of the contact."
field :first_name,
GraphQL::Types::String,
null: false,
description: 'First name of the contact.'
GraphQL::Types::String,
null: false,
description: 'First name of the contact.'
field :last_name,
GraphQL::Types::String,
null: false,
description: 'Last name of the contact.'
GraphQL::Types::String,
null: false,
description: 'Last name of the contact.'
field :phone,
GraphQL::Types::String,
null: true,
description: 'Phone number of the contact.'
GraphQL::Types::String,
null: true,
description: 'Phone number of the contact.'
field :email,
GraphQL::Types::String,
null: true,
description: 'Email address of the contact.'
GraphQL::Types::String,
null: true,
description: 'Email address of the contact.'
field :description,
GraphQL::Types::String,
null: true,
description: 'Description of or notes for the contact.'
GraphQL::Types::String,
null: true,
description: 'Description of or notes for the contact.'
field :created_at,
Types::TimeType,
null: false,
description: 'Timestamp the contact was created.'
Types::TimeType,
null: false,
description: 'Timestamp the contact was created.'
field :updated_at,
Types::TimeType,
null: false,
description: 'Timestamp the contact was last updated.'
Types::TimeType,
null: false,
description: 'Timestamp the contact was last updated.'
field :active,
GraphQL::Types::Boolean,
null: false,
description: 'State of the contact.', method: :active?
GraphQL::Types::Boolean,
null: false,
description: 'State of the contact.', method: :active?
end
end
end

View File

@ -11,9 +11,9 @@ module Types
AVAILABLE_STATES.each do |state|
field state,
GraphQL::Types::Int,
null: true,
description: "Number of organizations with state `#{state.upcase}`"
GraphQL::Types::Int,
null: true,
description: "Number of organizations with state `#{state.upcase}`"
end
def all

View File

@ -6,16 +6,16 @@ module Types
graphql_name 'CustomerRelationsOrganizationState'
value 'all',
description: "All available organizations.",
value: :all
description: "All available organizations.",
value: :all
value 'active',
description: "Active organizations.",
value: :active
description: "Active organizations.",
value: :active
value 'inactive',
description: "Inactive organizations.",
value: :inactive
description: "Inactive organizations.",
value: :inactive
end
end
end

View File

@ -8,39 +8,39 @@ module Types
authorize :read_crm_organization
field :id,
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the organization.'
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the organization.'
field :name,
GraphQL::Types::String,
null: false,
description: 'Name of the organization.'
GraphQL::Types::String,
null: false,
description: 'Name of the organization.'
field :default_rate,
GraphQL::Types::Float,
null: true,
description: 'Standard billing rate for the organization.'
GraphQL::Types::Float,
null: true,
description: 'Standard billing rate for the organization.'
field :description,
GraphQL::Types::String,
null: true,
description: 'Description of or notes for the organization.'
GraphQL::Types::String,
null: true,
description: 'Description of or notes for the organization.'
field :created_at,
Types::TimeType,
null: false,
description: 'Timestamp the organization was created.'
Types::TimeType,
null: false,
description: 'Timestamp the organization was created.'
field :updated_at,
Types::TimeType,
null: false,
description: 'Timestamp the organization was last updated.'
Types::TimeType,
null: false,
description: 'Timestamp the organization was last updated.'
field :active,
GraphQL::Types::Boolean,
null: false,
description: 'State of the organization.', method: :active?
GraphQL::Types::Boolean,
null: false,
description: 'State of the organization.', method: :active?
end
end
end

View File

@ -15,9 +15,9 @@ module Types
field :image_name, GraphQL::Types::String, null: false, description: 'Name of the image.'
field :size, GraphQL::Types::String, null: false, description: 'Size of the manifest file.'
field :status,
Types::DependencyProxy::ManifestTypeEnum,
null: false,
description: "Status of the manifest (#{::DependencyProxy::Manifest.statuses.keys.join(', ')})"
Types::DependencyProxy::ManifestTypeEnum,
null: false,
description: "Status of the manifest (#{::DependencyProxy::Manifest.statuses.keys.join(', ')})"
field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
def image_name

View File

@ -499,6 +499,7 @@ module ApplicationSettingsHelper
:bulk_import_concurrent_pipeline_batch_limit,
:bulk_import_enabled,
:bulk_import_max_download_file_size,
:silent_admin_exports_enabled,
:allow_runner_registration_token,
:user_defaults_to_private_profile,
:deactivation_email_additional_text,
@ -516,17 +517,7 @@ module ApplicationSettingsHelper
:downstream_pipeline_trigger_limit_per_project_user_sha,
:asciidoc_max_includes
].tap do |settings|
if Gitlab.com?
settings << :container_registry_import_max_tags_count
settings << :container_registry_import_max_retries
settings << :container_registry_import_start_max_retries
settings << :container_registry_import_max_step_duration
settings << :container_registry_pre_import_tags_rate
settings << :container_registry_pre_import_timeout
settings << :container_registry_import_timeout
settings << :container_registry_import_target_plan
settings << :container_registry_import_created_before
else
unless Gitlab.com?
settings << :deactivate_dormant_users
settings << :deactivate_dormant_users_period
settings << :nuget_skip_metadata_url_validation

View File

@ -14,6 +14,17 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
ignore_columns %i[instance_administration_project_id instance_administrators_group_id], remove_with: '16.2', remove_after: '2023-06-22'
ignore_columns %i[repository_storages], remove_with: '16.8', remove_after: '2023-12-21'
ignore_column :required_instance_ci_template, remove_with: '17.1', remove_after: '2024-05-10'
ignore_columns %i[
container_registry_import_max_tags_count
container_registry_import_max_retries
container_registry_import_start_max_retries
container_registry_import_max_step_duration
container_registry_pre_import_tags_rate
container_registry_pre_import_timeout
container_registry_import_timeout
container_registry_import_target_plan
container_registry_import_created_before
], remove_with: '17.2', remove_after: '2024-06-24'
INSTANCE_REVIEW_MIN_USERS = 50
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
@ -369,12 +380,6 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
validates :container_registry_expiration_policies_caching,
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :container_registry_pre_import_tags_rate,
allow_nil: false,
numericality: { greater_than_or_equal_to: 0 }
validates :container_registry_import_target_plan, presence: true
validates :container_registry_import_created_before, presence: true
validates :invisible_captcha_enabled,
inclusion: { in: [true, false], message: N_('must be a boolean value') }
@ -571,12 +576,6 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
:container_registry_data_repair_detail_worker_max_concurrency,
:container_registry_delete_tags_service_timeout,
:container_registry_expiration_policies_worker_capacity,
:container_registry_import_max_retries,
:container_registry_import_max_step_duration,
:container_registry_import_max_tags_count,
:container_registry_import_start_max_retries,
:container_registry_import_timeout,
:container_registry_pre_import_timeout,
:decompress_archive_file_timeout,
:dependency_proxy_ttl_group_policy_worker_capacity,
:gitlab_shell_operation_limit,
@ -621,8 +620,13 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
throttle_unauthenticated_git_http_requests_per_period: [:integer, { default: 3600 }],
throttle_unauthenticated_git_http_period_in_seconds: [:integer, { default: 3600 }]
jsonb_accessor :importers,
silent_admin_exports_enabled: [:boolean, { default: false }]
validates :rate_limits, json_schema: { filename: "application_setting_rate_limits" }
validates :importers, json_schema: { filename: "application_setting_importers" }
jsonb_accessor :package_registry, nuget_skip_metadata_url_validation: [:boolean, { default: false }]
validates :package_registry, json_schema: { filename: 'application_setting_package_registry' }

View File

@ -245,15 +245,6 @@ module ApplicationSettingImplementation
container_registry_expiration_policies_worker_capacity: 4,
container_registry_cleanup_tags_service_max_list_size: 200,
container_registry_expiration_policies_caching: true,
container_registry_import_max_tags_count: 100,
container_registry_import_max_retries: 3,
container_registry_import_start_max_retries: 50,
container_registry_import_max_step_duration: 5.minutes,
container_registry_pre_import_tags_rate: 0.5,
container_registry_pre_import_timeout: 30.minutes,
container_registry_import_timeout: 10.minutes,
container_registry_import_target_plan: 'free',
container_registry_import_created_before: '2022-01-23 00:00:00',
kroki_enabled: false,
kroki_url: nil,
kroki_formats: { blockdiag: false, bpmn: false, excalidraw: false },
@ -269,6 +260,7 @@ module ApplicationSettingImplementation
can_create_organization: true,
bulk_import_enabled: false,
bulk_import_max_download_file_size: 5120,
silent_admin_exports_enabled: false,
allow_runner_registration_token: true,
user_defaults_to_private_profile: false,
projects_api_rate_limit_unauthenticated: 400,

View File

@ -16,6 +16,8 @@ module GpgKeys
private
def validate(key)
return false unless key.valid?
GpgKeys::ValidateIntegrationsService.new(key).execute
end

View File

@ -9,8 +9,6 @@ module GpgKeys
end
def execute
return false unless key.valid?
validate_beyond_identity!
key.errors.empty?
@ -26,9 +24,12 @@ module GpgKeys
return unless integration&.activated?
integration.execute({ key_id: key.primary_keyid, committer_email: key.user.email })
key.externally_verified = true
rescue ::Gitlab::BeyondIdentity::Client::Error => e
key.errors.add(:base, "BeyondIdentity: #{e.message}")
rescue ::Gitlab::BeyondIdentity::Client::ApiError => e
key.errors.add(:base, "BeyondIdentity: #{e.message}") unless e.acceptable_error?
key.externally_verified = false
end
end
end

View File

@ -13,6 +13,7 @@ module Issuable
end
def after_initialize; end
def before_create; end
def before_update; end
def after_update_commit; end
def after_save_commit; end

View File

@ -254,6 +254,7 @@ class IssuableBaseService < ::BaseContainerService
before_create(issuable)
issuable_saved = issuable.with_transaction_returning_status do
@callbacks.each(&:before_create)
transaction_create(issuable)
end

View File

@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Application settings for importers and exporters",
"type": "object",
"properties": {
"silent_admin_exports_enabled": {
"type": "boolean"
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,20 @@
---
description: Count usage of /add_email quickaction with a multiple arguments
internal_events: true
action: i_quickactions_add_email_multiple
identifiers:
- project
- user
- namespace
product_section: analytics
product_stage: monitor
product_group: respond
milestone: "17.0"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,20 @@
---
description: Count usage of /add_email quickaction with a single argument
internal_events: true
action: i_quickactions_add_email_single
identifiers:
- project
- user
- namespace
product_section: analytics
product_stage: monitor
product_group: respond
milestone: "17.0"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -557,7 +557,7 @@ Settings.cron_jobs['gitlab_service_ping_worker'] ||= {}
Settings.cron_jobs['gitlab_service_ping_worker']['cron'] ||= nil # This is dynamically loaded in the sidekiq initializer
Settings.cron_jobs['gitlab_service_ping_worker']['job_class'] = 'GitlabServicePingWorker'
Settings.cron_jobs['stuck_merge_jobs_worker'] ||= {}
Settings.cron_jobs['stuck_merge_jobs_worker']['cron'] ||= '0 */2 * * *'
Settings.cron_jobs['stuck_merge_jobs_worker']['cron'] ||= '*/15 * * * *'
Settings.cron_jobs['stuck_merge_jobs_worker']['job_class'] = 'StuckMergeJobsWorker'
Settings.cron_jobs['pages_domain_verification_cron_worker'] ||= {}
Settings.cron_jobs['pages_domain_verification_cron_worker']['cron'] ||= '*/15 * * * *'

View File

@ -0,0 +1,23 @@
---
key_path: counts.count_total_i_quickactions_add_email_multiple_monthly
description: Monthly count of external participants being added using the add_email quick action (multiple arguments)
product_section: analytics
product_stage: monitor
product_group: respond
performance_indicator_type: []
value_type: number
status: active
milestone: '17.0'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: i_quickactions_add_email_multiple

View File

@ -0,0 +1,23 @@
---
key_path: counts.count_total_i_quickactions_add_email_single_monthly
description: Monthly count of external participants being added using the add_email quick action (single argument)
product_section: analytics
product_stage: monitor
product_group: respond
performance_indicator_type: []
value_type: number
status: active
milestone: '17.0'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: i_quickactions_add_email_single

View File

@ -0,0 +1,23 @@
---
key_path: counts.count_total_i_quickactions_add_email_multiple_weekly
description: Weekly count of external participants being added using the add_email quick action (multiple arguments)
product_section: analytics
product_stage: monitor
product_group: respond
performance_indicator_type: []
value_type: number
status: active
milestone: '17.0'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: i_quickactions_add_email_multiple

View File

@ -0,0 +1,23 @@
---
key_path: counts.count_total_i_quickactions_add_email_single_weekly
description: Weekly count of external participants being added using the add_email quick action (single argument)
product_section: analytics
product_stage: monitor
product_group: respond
performance_indicator_type: []
value_type: number
status: active
milestone: '17.0'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151776
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: i_quickactions_add_email_single

View File

@ -8,3 +8,4 @@ description: Store build-related runner session. Data is removed after the respe
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6208
milestone: '11.1'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/459994

View File

@ -8,3 +8,4 @@ description: A set of cost factors per runner which are applied to ci job durati
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111977
milestone: '15.10'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460076

View File

@ -8,3 +8,4 @@ description: Information about runner managers associated to Ci::Runner models
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107801
milestone: '15.8'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460084

View File

@ -8,3 +8,4 @@ description: Relationships between runners and projects for project runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/459996

View File

@ -8,3 +8,4 @@ description: Information about used Ci::Runner versions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90982
milestone: '15.2'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460096

View File

@ -10,3 +10,4 @@ description: Registered CI runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f3131e72dcd2dbdacc5264d4aa62
milestone: '8.0'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442395

View File

@ -8,3 +8,4 @@ description: Holds references to finished CI builds ready to be synced to ClickH
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/
milestone: '16.5'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/459997

View File

@ -7,3 +7,4 @@ description: Relationships between builds and runner managers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111476
milestone: '15.9'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/459999

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddImportersToApplicationSettings < Gitlab::Database::Migration[2.2]
milestone '17.0'
enable_lock_retries!
def change
add_column :application_settings, :importers, :jsonb, default: {}, null: false
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddImportersHashConstraintToApplicationSettings < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '17.0'
CONSTRAINT_NAME = 'check_application_settings_importers_is_hash'
def up
add_check_constraint(
:application_settings,
"(jsonb_typeof(importers) = 'object')",
CONSTRAINT_NAME
)
end
def down
remove_check_constraint :application_settings, CONSTRAINT_NAME
end
end

View File

@ -0,0 +1 @@
5d583eb5c1f9312a488de07a4cdad58ec22e378830ac0731945344c471065bf6

View File

@ -0,0 +1 @@
58294e001551f2b64859b5e8f16fcbdebac32b2696479120240ae8979122c428

View File

@ -4449,6 +4449,7 @@ CREATE TABLE application_settings (
service_ping_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
package_registry jsonb DEFAULT '{}'::jsonb NOT NULL,
rate_limits_unauthenticated_git_http jsonb DEFAULT '{}'::jsonb NOT NULL,
importers jsonb DEFAULT '{}'::jsonb NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
@ -4500,6 +4501,7 @@ CREATE TABLE application_settings (
CONSTRAINT check_app_settings_namespace_storage_forks_cost_factor_range CHECK (((namespace_storage_forks_cost_factor >= (0)::double precision) AND (namespace_storage_forks_cost_factor <= (1)::double precision))),
CONSTRAINT check_app_settings_sentry_clientside_traces_sample_rate_range CHECK (((sentry_clientside_traces_sample_rate >= (0)::double precision) AND (sentry_clientside_traces_sample_rate <= (1)::double precision))),
CONSTRAINT check_application_settings_clickhouse_is_hash CHECK ((jsonb_typeof(clickhouse) = 'object'::text)),
CONSTRAINT check_application_settings_importers_is_hash CHECK ((jsonb_typeof(importers) = 'object'::text)),
CONSTRAINT check_application_settings_package_registry_is_hash CHECK ((jsonb_typeof(package_registry) = 'object'::text)),
CONSTRAINT check_application_settings_rate_limits_is_hash CHECK ((jsonb_typeof(rate_limits) = 'object'::text)),
CONSTRAINT check_application_settings_rate_limits_unauth_git_http_is_hash CHECK ((jsonb_typeof(rate_limits_unauthenticated_git_http) = 'object'::text)),

View File

@ -135,7 +135,8 @@ Example response:
"bulk_import_concurrent_pipeline_batch_limit": 25,
"concurrent_github_import_jobs_limit": 1000,
"concurrent_bitbucket_import_jobs_limit": 100,
"concurrent_bitbucket_server_import_jobs_limit": 100
"concurrent_bitbucket_server_import_jobs_limit": 100,
"silent_admin_exports_enabled": false
}
```
@ -294,7 +295,8 @@ Example response:
"downstream_pipeline_trigger_limit_per_project_user_sha": 0,
"concurrent_github_import_jobs_limit": 1000,
"concurrent_bitbucket_import_jobs_limit": 100,
"concurrent_bitbucket_server_import_jobs_limit": 100
"concurrent_bitbucket_server_import_jobs_limit": 100,
"silent_admin_exports_enabled": false
}
```
@ -329,6 +331,7 @@ Example responses:
> - Fields `housekeeping_full_repack_period`, `housekeeping_gc_period`, and `housekeeping_incremental_repack_period` [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106963) in GitLab 15.8. Use `housekeeping_optimize_repository_period` instead.
> - Parameter `allow_account_deletion` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412411) in GitLab 16.1.
> - Parameter `silent_admin_exports_enabled` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148918) in GitLab 17.0.
In general, all settings are optional. Certain settings though, if enabled,
require other settings to be set to function properly. These requirements are
@ -594,6 +597,7 @@ listed in the descriptions of the relevant settings.
| `sidekiq_job_limiter_limit_bytes` | integer | no | The threshold in bytes at which Sidekiq jobs are rejected. Default: 0 bytes (doesn't reject any job). |
| `signin_enabled` | string | no | (Deprecated: Use `password_authentication_enabled_for_web` instead) Flag indicating if password authentication is enabled for the web interface. |
| `signup_enabled` | boolean | no | Enable registration. Default is `true`. |
| `silent_admin_exports_enabled` | boolean | no | Enable silent exports for administrators. Default is `false`. |
| `silent_mode_enabled` | boolean | no | Enable [Silent mode](../administration/silent_mode/index.md). Default is `false`. |
| `slack_app_enabled` | boolean | no | (**If enabled, requires:** `slack_app_id`, `slack_app_secret`, `slack_app_signing_secret`, and `slack_app_verification_token`) Enable the GitLab for Slack app. |
| `slack_app_id` | string | required by: `slack_app_enabled` | The client ID of the GitLab for Slack app. |

View File

@ -52,4 +52,4 @@ curl "http://localhost:11434/api/chat" \
}'
```
It runs on the `11434` by default. If you are running into issues because this port is already in use by another application, you can follow [these instructions](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-configure-ollama-server).
It runs on the port `11434` by default. If you are running into issues because this port is already in use by another application, you can follow [these instructions](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-configure-ollama-server).

View File

@ -50,14 +50,14 @@ This page includes an exhaustive list of settings related to and maintained by t
| `container_registry_expiration_policies_worker_capacity` | `application_settings` | Number of concurrent container image cleanup policy workers allowed. |
| `container_registry_cleanup_tags_service_max_list_size` | `application_settings` | The maximum number of tags that can be deleted in a cleanup policy single execution. Additional tags must be deleted in another execution. |
| `container_registry_expiration_policies_caching` | `application_settings` | Enable or disable tag creation timestamp caching during execution of cleanup policies. |
| `container_registry_import_max_tags_count` | `application_settings` | Defines what is a the maximum amount of tags that we accept to migrate. |
| `container_registry_import_max_retries` | `application_settings` | The maximum amount of retries done on a migration that is aborted. |
| `container_registry_import_start_max_retries` | `application_settings` | The maximum amount of requests to start an import step that is sent to the container registry API. |
| `container_registry_import_max_step_duration` | `application_settings` | The maximum amount of seconds before an ongoing migration is considered as stale. |
| `container_registry_import_target_plan` | `application_settings` | The target subscription plan on which we're intend to pick container repositories. |
| `container_registry_import_created_before` | `application_settings` | Only image repositories created before this timestamp are eligible for the migration. |
| `container_registry_pre_import_timeout` | `application_settings` | The timeout for long running `pre_imports`. |
| `container_registry_import_timeout` | `application_settings` | The timeout for long running imports. |
| `container_registry_import_max_tags_count` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns 0 until it gets removed. |
| `container_registry_import_max_retries` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns 0 until it gets removed. |
| `container_registry_import_start_max_retries` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns 0 until it gets removed. |
| `container_registry_import_max_step_duration` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns 0 until it gets removed. |
| `container_registry_import_target_plan` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns an empty string ('') until it gets removed. |
| `container_registry_import_created_before` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns an empty string ('') until it gets removed. |
| `container_registry_pre_import_timeout` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns an empty string ('') until it gets removed. |
| `container_registry_import_timeout` | `application_settings` | **Deprecated** in 17.0. The migration for GitLab.com is now complete so we are starting to cleanup this field. This field returns an empty string ('') until it gets removed. |
| `dependency_proxy_ttl_group_policy_worker_capacity` | `application_settings` | Number of concurrent dependency proxy cleanup policy workers allowed. |
## Namespace/Group Settings

View File

@ -99,7 +99,7 @@ work-around was suggested in <https://gitlab.com/gitlab-org/omnibus-gitlab/-/iss
A feature proposal to segregate access control regarding running pipelines from ability to push/merge was also created at <https://gitlab.com/gitlab-org/gitlab/-/issues/24585>.
For more technical details on CI/CD setup and documentation on adding new test jobs to `e2e:package-and-test` pipeline, see
[`e2e:package_and_test` setup documentation](test_pipelines.md).
[`e2e:package-and-test` setup documentation](test_pipelines.md).
#### Using the `test-on-gdk` job

View File

@ -162,6 +162,8 @@ legacy issue, merge request, or epic updates.
- `after_initialize` is called after the work item is initialized by the `BuildService` and before
the work item is saved by the `CreateService` and `UpdateService`. This callback runs outside the
creation or update database transaction.
- `before_create` is called before the work item is saved by the `CreateService`. This callback runs
within the create database transaction.
- `before_update` is called before the work item is saved by the `UpdateService`. This callback runs
within the update database transaction.
- `after_update_commit` is called after the DB update transaction is committed by the `UpdateService`.

View File

@ -71,64 +71,53 @@ the components outlined above and the pre-loaded demo runbook.
```yaml
#-----------------------------------------------------------------------------
# The gitlab and ingress sections must be customized!
#-----------------------------------------------------------------------------
gitlab:
clientId: <Your OAuth Application ID>
clientSecret: <Your OAuth Application Secret>
callbackUrl: http://<Jupyter Hostname>/hub/oauth_callback,
# Limit access to members of specific projects or groups:
# allowedGitlabGroups: [ "my-group-1", "my-group-2" ]
# allowedProjectIds: [ 12345, 6789 ]
# ingress is required for OAuth to work
ingress:
enabled: true
host: <JupyterHostname>
# tls:
# - hosts:
# - <JupyterHostanme>
# secretName: jupyter-cert
# annotations:
# kubernetes.io/ingress.class: "nginx"
# kubernetes.io/tls-acme: "true"
#-----------------------------------------------------------------------------
# NO MODIFICATIONS REQUIRED BEYOND THIS POINT
# The hub.config.GitLabOAuthenticator section must be customized!
#-----------------------------------------------------------------------------
hub:
extraEnv:
JUPYTER_ENABLE_LAB: 1
extraConfig: |
c.KubeSpawner.cmd = ['jupyter-labhub']
c.GitLabOAuthenticator.scope = ['api read_repository write_repository']
config:
GitLabOAuthenticator:
# Limit access to members of specific projects or groups or to specific users:
# allowedGitlabGroups: [ "my-group-1", "my-group-2" ]
# allowedProjectIds: [ 12345, 6789 ]
# allowed_users: ["user-1", "user-2"]
client_id: <Your OAuth Application ID>
client_secret: <Your OAuth Application ID>
enable_auth_state: true
gitlab_url: https://gitlab.example.com
oauth_callback_url: http://<Jupyter Hostname>/hub/oauth_callback
scope:
- read_user
- read_api
- openid
- profile
- email
JupyterHub:
authenticator_class: gitlab
extraConfig:
gitlab-config: |
c.KubeSpawner.cmd = ['jupyter-labhub']
c.GitLabOAuthenticator.scope = ['api read_repository write_repository']
async def add_auth_env(spawner):
'''
We set user's id, login and access token on single user image to
enable repository integration for JupyterHub.
See: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47138#note_154294790
'''
auth_state = await spawner.user.get_auth_state()
async def add_auth_env(spawner):
'''
We set user's id, login and access token on single user image to
enable repository integration for JupyterHub.
See: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47138#note_154294790
'''
auth_state = await spawner.user.get_auth_state()
if not auth_state:
spawner.log.warning("No auth state for %s", spawner.user)
return
if not auth_state:
spawner.log.warning("No auth state for %s", spawner.user)
return
spawner.environment['GITLAB_ACCESS_TOKEN'] = auth_state['access_token']
spawner.environment['GITLAB_USER_LOGIN'] = auth_state['gitlab_user']['username']
spawner.environment['GITLAB_USER_ID'] = str(auth_state['gitlab_user']['id'])
spawner.environment['GITLAB_USER_EMAIL'] = auth_state['gitlab_user']['email']
spawner.environment['GITLAB_USER_NAME'] = auth_state['gitlab_user']['name']
spawner.environment['GITLAB_ACCESS_TOKEN'] = auth_state['access_token']
spawner.environment['GITLAB_USER_LOGIN'] = auth_state['gitlab_user']['username']
spawner.environment['GITLAB_USER_ID'] = str(auth_state['gitlab_user']['id'])
spawner.environment['GITLAB_USER_EMAIL'] = auth_state['gitlab_user']['email']
spawner.environment['GITLAB_USER_NAME'] = auth_state['gitlab_user']['name']
c.KubeSpawner.pre_spawn_hook = add_auth_env
auth:
type: gitlab
state:
enabled: true
c.KubeSpawner.pre_spawn_hook = add_auth_env
singleuser:
defaultUrl: "/lab"

View File

@ -53,6 +53,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| Command | Issue | Merge request | Epic | Action |
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
| `/add_contacts [contact:email1@example.com] [contact:email2@example.com]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more active [CRM contacts](../crm/index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413) in GitLab 14.6). |
| `/add_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
| `/approve` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Approve the merge request. |
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users. |
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself. |
@ -78,7 +79,6 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
| `/estimate <time>` or `/estimate_time <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. For more information, see [Time tracking](time_tracking.md). Alias `/estimate_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6. |
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7). |
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
| `/iteration *iteration:<iteration ID> or <iteration name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"`. |
| `/iteration [cadence:<iteration cadence ID> or <iteration cadence name>] <--current or --next>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration to the current or next upcoming iteration of the referenced iteration cadence. For example, `/iteration [cadence:"Team cadence"] --current` sets the iteration to the current iteration of the iteration cadence named "Team cadence". [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384885) in GitLab 16.9. |
| `/iteration <--current or --next>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration to the current or next upcoming iteration when a group has one iteration cadence. For example, `/iteration --current` sets the iteration to the current iteration of the iteration cadence. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384885) in GitLab 16.9. |

View File

@ -116,14 +116,14 @@ To see a list of all external participants:
### Add an external participant
Add an external participant using the `/invite_email` [quick action](../quick_actions.md) when you want
Add an external participant using the `/add_email` [quick action](../quick_actions.md) when you want
to include them in the conversation at any time.
When added, the external participant starts receiving notifications using Service Desk emails.
GitLab doesn't send a `thank_you` email for manually added external participants.
You should add external participants in a dedicated comment because they don't receive a notification
email for the comment that contains the `/invite_email` quick action.
email for the comment that contains the `/add_email` quick action.
Prerequisites:
@ -132,8 +132,8 @@ Prerequisites:
To add an external participant to an issue or ticket:
1. Go to the issue or ticket.
1. Add a comment that contains only the quick action `/invite_email user@example.com`.
You can chain up to 6 email addresses. For example `/invite_email user@example.com user2@example.com`
1. Add a comment that contains only the quick action `/add_email user@example.com`.
You can chain up to 6 email addresses. For example `/add_email user@example.com user2@example.com`
You should see a success message and a new system note with the email address.

View File

@ -51,6 +51,22 @@ module API
expose(:housekeeping_gc_period) { |settings, _options| settings.housekeeping_optimize_repository_period }
expose(:housekeeping_incremental_repack_period) { |settings, _options| settings.housekeeping_optimize_repository_period }
expose(:repository_storages_weighted) { |settings, _options| settings.repository_storages_with_default_weight }
# We are ignoring the container registry migration-related database columns.
# To be backwards compatible, we are keeping these fields in the API
# but we nullify them. We will eventually remove these as part of
# https://gitlab.com/gitlab-org/gitlab/-/issues/409873
{
container_registry_import_max_tags_count: 0,
container_registry_import_max_retries: 0,
container_registry_import_start_max_retries: 0,
container_registry_import_max_step_duration: 0,
container_registry_pre_import_tags_rate: 0,
container_registry_pre_import_timeout: 0,
container_registry_import_timeout: 0,
container_registry_import_target_plan: '',
container_registry_import_created_before: ''
}.each { |field, value| expose(field) { |_, _| value } }
end
end
end

View File

@ -7,6 +7,25 @@ module Gitlab
Error = Class.new(StandardError)
class ApiError < Class.new(StandardError)
ACCEPTABLE_ERROR_CODES = [404].freeze
attr_reader :code, :message
def initialize(message, code)
@code = code
@message = message
end
# In some cases, we treat error response as acceptable:
#
# A GPG key that wasn't issued by BeyondIdentity returns 404 status code
# but users should be able to add those GPG keys to their profile.
def acceptable_error?
ACCEPTABLE_ERROR_CODES.include?(code)
end
end
attr_reader :integration
def initialize(integration)
@ -20,12 +39,12 @@ module Gitlab
response = Gitlab::HTTP.get(API_URL, options)
body = Gitlab::Json.parse(response.body) || {}
raise Error, body.dig('error', 'message') unless response.success?
raise Error, "authorization denied: #{body['message']}" unless body['authorized']
raise ApiError.new(body.dig('error', 'message'), response.code) unless response.success?
raise ApiError.new(body['message'], response.code) unless body['authorized']
body
rescue JSON::ParserError
raise Error, 'invalid response format'
raise ApiError.new('invalid response format', response.code)
end
private

View File

@ -58,11 +58,19 @@ module Gitlab
break false unless key.externally_verified?
break true if gpg_key.updated_at > INTEGRATION_VERIFICATION_PERIOD.ago
GpgKeys::ValidateIntegrationsService.new(gpg_key.dup).execute.tap do |verified|
key.update(externally_verified: verified)
verified_externally?(gpg_key).tap do |verified_externally|
key.update!(externally_verified: verified_externally)
end
end
end
def verified_externally?(key)
integration.execute({ key_id: key.primary_keyid, committer_email: key.user.email })
true
rescue ::Gitlab::BeyondIdentity::Client::ApiError => _
false
end
end
end
end

View File

@ -44,30 +44,30 @@ module Gitlab
TRANSLATION_LEVELS = {
'bg' => 0,
'cs_CZ' => 0,
'da_DK' => 25,
'de' => 98,
'da_DK' => 24,
'de' => 99,
'en' => 100,
'eo' => 0,
'es' => 25,
'fil_PH' => 0,
'fr' => 98,
'fr' => 99,
'gl_ES' => 0,
'id_ID' => 0,
'it' => 1,
'ja' => 98,
'ko' => 21,
'ja' => 99,
'ko' => 20,
'nb_NO' => 18,
'nl_NL' => 0,
'pl_PL' => 2,
'pt_BR' => 54,
'ro_RO' => 63,
'ru' => 19,
'pt_BR' => 52,
'ro_RO' => 61,
'ru' => 18,
'si_LK' => 10,
'tr_TR' => 7,
'uk' => 47,
'zh_CN' => 93,
'uk' => 46,
'zh_CN' => 92,
'zh_HK' => 1,
'zh_TW' => 94
'zh_TW' => 98
}.freeze
private_constant :TRANSLATION_LEVELS

View File

@ -221,8 +221,8 @@ module Gitlab
@execution_message[:remove_zoom] = result.message
end
desc { _('Add email participant(s)') }
explanation { _('Adds email participant(s).') }
desc { _("Add email participant(s) that don't have a GitLab account.") }
explanation { _("Adds email participant(s) that don't have a GitLab account.") }
params 'email1@example.com email2@example.com (up to 6 emails)'
types Issue
condition do
@ -230,14 +230,14 @@ module Gitlab
Feature.enabled?(:issue_email_participants, parent) &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
end
command :invite_email do |emails = ""|
command :add_email do |emails = ""|
response = ::IssueEmailParticipants::CreateService.new(
target: quick_action_target,
current_user: current_user,
emails: emails.split(' ')
).execute
@execution_message[:invite_email] = response.message
@execution_message[:add_email] = response.message
end
desc { _('Remove email participant(s)') }

View File

@ -6,7 +6,13 @@ module Gitlab
class << self
# List of events that use the current internal events implementation.
# Only add internal events for new quick actions.
INTERNAL_EVENTS = %w[convert_to_ticket remove_email_single remove_email_multiple].freeze
INTERNAL_EVENTS = %w[
add_email_multiple
add_email_single
convert_to_ticket
remove_email_multiple
remove_email_single
].freeze
# Tracks the quick action with name `name`.
# `args` is expected to be a single string, will be split internally when necessary.
@ -46,10 +52,10 @@ module Gitlab
event_name_for_unassign(args)
when 'unlabel', 'remove_label'
event_name_for_unlabel(args)
when 'invite_email'
'invite_email' + event_name_quantifier(args.split)
when 'add_email'
"add_email#{event_name_quantifier(args.split)}"
when 'remove_email'
'remove_email' + event_name_quantifier(args.split)
"remove_email#{event_name_quantifier(args.split)}"
else
name
end
@ -61,7 +67,7 @@ module Gitlab
if args.count == 1 && args.first == 'me'
'assign_self'
else
'assign' + event_name_quantifier(args)
"assign#{event_name_quantifier(args)}"
end
end

View File

@ -3086,7 +3086,7 @@ msgstr ""
msgid "Add deploy keys to grant read/write access to this repository. %{link_start}What are deploy keys?%{link_end}"
msgstr ""
msgid "Add email participant(s)"
msgid "Add email participant(s) that don't have a GitLab account."
msgstr ""
msgid "Add environment"
@ -3341,7 +3341,7 @@ msgstr ""
msgid "Adds an issue to an epic."
msgstr ""
msgid "Adds email participant(s)."
msgid "Adds email participant(s) that don't have a GitLab account."
msgstr ""
msgid "Adds this %{issuable_type} as related to the %{issuable_type} it was created from"
@ -13195,6 +13195,9 @@ msgstr ""
msgid "ComplianceFrameworks|Name is required"
msgstr ""
msgid "ComplianceFrameworks|Name is required, and must be less than 255 characters"
msgstr ""
msgid "ComplianceFrameworks|Name, description"
msgstr ""
@ -51446,9 +51449,6 @@ msgstr ""
msgid "Terraform|Remove"
msgstr ""
msgid "Terraform|Remove state file and versions"
msgstr ""
msgid "Terraform|Removed"
msgstr ""

View File

@ -9,7 +9,7 @@ RSpec.describe 'User scrolls to deep-linked note', feature_category: :team_plann
let_it_be(:comments) { create_list(:note_on_issue, 20, noteable: issue, project: project, note: 'spacer note') }
context 'on issue page', :js do
it 'on comment' do
it 'on comment', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/446195' do
visit project_issue_path(project, issue, anchor: "note_#{comment_1.id}")
wait_for_requests

View File

@ -1,4 +1,4 @@
import { GlAlert, GlBadge, GlKeysetPagination, GlLoadingIcon, GlTab } from '@gitlab/ui';
import { GlAlert, GlCard, GlKeysetPagination, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@ -52,16 +52,16 @@ describe('TerraformList', () => {
propsData,
provide,
stubs: {
GlTab,
GlCard,
},
});
};
const findBadge = () => wrapper.findComponent(GlBadge);
const findEmptyState = () => wrapper.findComponent(EmptyState);
const findPaginationButtons = () => wrapper.findComponent(GlKeysetPagination);
const findStatesTable = () => wrapper.findComponent(StatesTable);
const findTab = () => wrapper.findComponent(GlTab);
const findCard = () => wrapper.findComponent(GlCard);
const findCardTitle = () => findCard().find('.gl-new-card-title-wrapper');
describe('when the terraform query has succeeded', () => {
describe('when there is a list of terraform states', () => {
@ -110,9 +110,9 @@ describe('TerraformList', () => {
return waitForPromises();
});
it('displays a terraform states tab and count', () => {
expect(findTab().text()).toContain('Terraform states');
expect(findBadge().text()).toBe('2');
it('displays a terraform states card and count', () => {
expect(findCardTitle().text()).toContain('Terraform states');
expect(findCardTitle().text()).toContain('2');
});
it('renders the states table and pagination buttons', () => {
@ -158,9 +158,9 @@ describe('TerraformList', () => {
return waitForPromises();
});
it('displays a terraform states tab with no count', () => {
expect(findTab().text()).toContain('Terraform states');
expect(findBadge().exists()).toBe(false);
it('displays a terraform states card with no count', () => {
expect(findCardTitle().text()).toContain('Terraform states');
expect(findCardTitle().text()).toContain('0');
});
it('renders the empty state', () => {

View File

@ -79,18 +79,6 @@ RSpec.describe ApplicationSettingsHelper do
expect(helper.visible_attributes).to include(:namespace_aggregation_schedule_lease_duration_in_seconds)
end
it 'does not contain :container_registry_import_* and :container_registry_pre_import_*', :aggregate_failures do
expect(helper.visible_attributes).not_to include(:container_registry_import_max_tags_count)
expect(helper.visible_attributes).not_to include(:container_registry_import_max_retries)
expect(helper.visible_attributes).not_to include(:container_registry_import_start_max_retries)
expect(helper.visible_attributes).not_to include(:container_registry_import_max_step_duration)
expect(helper.visible_attributes).not_to include(:container_registry_pre_import_tags_rate)
expect(helper.visible_attributes).not_to include(:container_registry_pre_import_timeout)
expect(helper.visible_attributes).not_to include(:container_registry_import_timeout)
expect(helper.visible_attributes).not_to include(:container_registry_import_target_plan)
expect(helper.visible_attributes).not_to include(:container_registry_import_created_before)
end
it 'contains service ping settings' do
expect(helper.visible_attributes).to include(
*%i[
@ -106,18 +94,6 @@ RSpec.describe ApplicationSettingsHelper do
it 'does not contain :deactivate_dormant_users_period' do
expect(helper.visible_attributes).not_to include(:deactivate_dormant_users_period)
end
it 'does contain :container_registry_import_* and :container_registry_pre_import_*', :aggregate_failures do
expect(helper.visible_attributes).to include(:container_registry_import_max_tags_count)
expect(helper.visible_attributes).to include(:container_registry_import_max_retries)
expect(helper.visible_attributes).to include(:container_registry_import_start_max_retries)
expect(helper.visible_attributes).to include(:container_registry_import_max_step_duration)
expect(helper.visible_attributes).to include(:container_registry_pre_import_tags_rate)
expect(helper.visible_attributes).to include(:container_registry_pre_import_timeout)
expect(helper.visible_attributes).to include(:container_registry_import_timeout)
expect(helper.visible_attributes).to include(:container_registry_import_target_plan)
expect(helper.visible_attributes).to include(:container_registry_import_created_before)
end
end
end

View File

@ -28,4 +28,22 @@ RSpec.describe API::Entities::ApplicationSetting do
end
end
end
context 'for container registry migration-related fields' do
it 'returns the static value assigned' do
{
container_registry_import_max_tags_count: 0,
container_registry_import_max_retries: 0,
container_registry_import_start_max_retries: 0,
container_registry_import_max_step_duration: 0,
container_registry_pre_import_tags_rate: 0,
container_registry_pre_import_timeout: 0,
container_registry_import_timeout: 0,
container_registry_import_target_plan: '',
container_registry_import_created_before: ''
}.each do |field, value|
expect(subject[field]).to eq(value)
end
end
end
end

View File

@ -48,7 +48,7 @@ RSpec.describe ::Gitlab::BeyondIdentity::Client, feature_category: :source_code_
it 'executes successfully' do
expect { client.execute(params) }.to raise_error(
::Gitlab::BeyondIdentity::Client::Error
::Gitlab::BeyondIdentity::Client::ApiError
).with_message('invalid response format')
end
end
@ -61,21 +61,40 @@ RSpec.describe ::Gitlab::BeyondIdentity::Client, feature_category: :source_code_
let(:status) { 400 }
it 'returns an error' do
expect { client.execute(params) }.to raise_error(
::Gitlab::BeyondIdentity::Client::Error
).with_message('gpg_key is invalid')
expect { client.execute(params) }.to raise_error do |error|
expect(error).to be_a(::Gitlab::BeyondIdentity::Client::ApiError)
expect(error.message).to eq('gpg_key is invalid')
expect(error.code).to eq(status)
expect(error).not_to be_acceptable_error
end
end
context 'when the error is acceptable' do
let(:status) { 404 }
it 'returns an error' do
expect { client.execute(params) }.to raise_error do |error|
expect(error).to be_a(::Gitlab::BeyondIdentity::Client::ApiError)
expect(error.message).to eq('gpg_key is invalid')
expect(error.code).to eq(status)
expect(error).to be_acceptable_error
end
end
end
end
context 'when key is unauthorized' do
let(:stubbed_response) do
{ 'unauthorized' => false, 'message' => 'key is unauthorized' }.to_json
{ 'authorized' => false, 'message' => 'key is unauthorized' }.to_json
end
it 'returns an error' do
expect { client.execute(params) }.to raise_error(
::Gitlab::BeyondIdentity::Client::Error
).with_message('authorization denied: key is unauthorized')
expect { client.execute(params) }.to raise_error do |error|
expect(error).to be_a(::Gitlab::BeyondIdentity::Client::ApiError)
expect(error.message).to eq('key is unauthorized')
expect(error.code).to eq(status)
expect(error).not_to be_acceptable_error
end
end
end
end

View File

@ -80,6 +80,10 @@ RSpec.describe Gitlab::Checks::Integrations::BeyondIdentityCheck, feature_catego
updated_at: (described_class::INTEGRATION_VERIFICATION_PERIOD + 1.day).ago
end
before do
allow(Integrations::BeyondIdentity).to receive(:for_instance).and_return([beyond_identity_integration])
end
context 'and the signature is verified' do
before do
allow_next_instances_of(CommitSignatures::GpgSignature, 3) do |signature|
@ -103,9 +107,9 @@ RSpec.describe Gitlab::Checks::Integrations::BeyondIdentityCheck, feature_catego
context 'when not verified by integrations' do
before do
allow_next_instance_of(GpgKeys::ValidateIntegrationsService) do |service|
allow(service).to receive(:execute).and_return(false)
end
allow(beyond_identity_integration).to receive(:execute).and_raise(
::Gitlab::BeyondIdentity::Client::ApiError.new('error', 403)
)
end
it 'raises an error' do
@ -118,9 +122,7 @@ RSpec.describe Gitlab::Checks::Integrations::BeyondIdentityCheck, feature_catego
context 'when verified by integrations' do
before do
allow_next_instance_of(GpgKeys::ValidateIntegrationsService) do |service|
allow(service).to receive(:execute).and_return(true)
end
allow(beyond_identity_integration).to receive(:execute)
end
it 'does not raise an error' do

View File

@ -200,22 +200,22 @@ RSpec.describe Gitlab::UsageDataCounters::QuickActionActivityUniqueCounter, :cle
end
end
context 'when tracking invite_email', feature_category: :service_desk do
let(:quickaction_name) { 'invite_email' }
context 'when tracking add_email', feature_category: :service_desk do
let(:quickaction_name) { 'add_email' }
context 'with single email' do
let(:args) { 'someone@gitlab.com' }
it_behaves_like 'a tracked quick action unique event' do
let(:action) { 'i_quickactions_invite_email_single' }
it_behaves_like 'a tracked quick action internal event' do
let(:action) { 'i_quickactions_add_email_single' }
end
end
context 'with multiple emails' do
let(:args) { 'someone@gitlab.com another@gitlab.com' }
it_behaves_like 'a tracked quick action unique event' do
let(:action) { 'i_quickactions_invite_email_multiple' }
it_behaves_like 'a tracked quick action internal event' do
let(:action) { 'i_quickactions_add_email_multiple' }
end
end
end

View File

@ -35,6 +35,7 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
it { expect(setting.concurrent_bitbucket_import_jobs_limit).to eq(100) }
it { expect(setting.concurrent_bitbucket_server_import_jobs_limit).to eq(100) }
it { expect(setting.nuget_skip_metadata_url_validation).to eq(false) }
it { expect(setting.silent_admin_exports_enabled).to eq(false) }
end
describe 'validations' do
@ -116,13 +117,6 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
it { is_expected.to validate_inclusion_of(:container_registry_expiration_policies_caching).in_array([true, false]) }
it { is_expected.to validate_numericality_of(:container_registry_pre_import_tags_rate).is_greater_than_or_equal_to(0) }
it { is_expected.not_to allow_value(nil).for(:container_registry_pre_import_tags_rate) }
it { is_expected.to allow_value(1.5).for(:container_registry_pre_import_tags_rate) }
it { is_expected.to validate_presence_of(:container_registry_import_target_plan) }
it { is_expected.to validate_presence_of(:container_registry_import_created_before) }
it { is_expected.to validate_numericality_of(:wiki_page_max_content_bytes).only_integer.is_greater_than_or_equal_to(1024) }
it { is_expected.to validate_inclusion_of(:wiki_asciidoc_allow_uri_includes).in_array([true, false]) }
it { is_expected.to validate_presence_of(:max_pages_size) }
@ -201,6 +195,8 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
it { is_expected.to validate_inclusion_of(:bulk_import_enabled).in_array([true, false]) }
it { is_expected.to validate_inclusion_of(:silent_admin_exports_enabled).in_array([true, false]) }
it { is_expected.to validate_inclusion_of(:allow_runner_registration_token).in_array([true, false]) }
it { is_expected.to validate_inclusion_of(:gitlab_dedicated_instance).in_array([true, false]) }
@ -221,12 +217,6 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
container_registry_data_repair_detail_worker_max_concurrency
container_registry_delete_tags_service_timeout
container_registry_expiration_policies_worker_capacity
container_registry_import_max_retries
container_registry_import_max_step_duration
container_registry_import_max_tags_count
container_registry_import_start_max_retries
container_registry_import_timeout
container_registry_pre_import_timeout
decompress_archive_file_timeout
dependency_proxy_ttl_group_policy_worker_capacity
gitlab_shell_operation_limit

View File

@ -35,9 +35,7 @@ RSpec.describe GpgKeys::CreateService, feature_category: :source_code_management
let(:params) { {} }
it 'returns an invalid key' do
expect_next_instance_of(GpgKeys::ValidateIntegrationsService) do |instance|
expect(instance).to receive(:execute)
end
expect(GpgKeys::ValidateIntegrationsService).not_to receive(:new)
gpg_key = subject.execute

View File

@ -9,16 +9,12 @@ RSpec.describe GpgKeys::ValidateIntegrationsService, feature_category: :source_c
subject(:service) { described_class.new(gpg_key) }
it 'returns true' do
expect(service.execute).to eq(true)
before do
gpg_key.valid?
end
context 'when key is invalid' do
it 'returns false' do
gpg_key.key = ''
expect(service.execute).to eq(false)
end
it 'returns true' do
expect(service.execute).to eq(true)
end
context 'when BeyondIdentity integration is not activated' do
@ -45,17 +41,36 @@ RSpec.describe GpgKeys::ValidateIntegrationsService, feature_category: :source_c
expect(gpg_key.externally_verified).to be_truthy
end
it 'returns false and sets an error on unsuccessful check' do
error = 'service error'
expect_next_instance_of(::Gitlab::BeyondIdentity::Client) do |instance|
expect(instance).to receive(:execute).with(
{ key_id: 'CCFBE19F00AC8B1D', committer_email: user.email }
).and_raise(::Gitlab::BeyondIdentity::Client::Error.new(error))
context 'when the check is unsuccessful' do
before do
allow_next_instance_of(::Gitlab::BeyondIdentity::Client) do |instance|
allow(instance).to receive(:execute).with(
{ key_id: 'CCFBE19F00AC8B1D', committer_email: user.email }
).and_raise(::Gitlab::BeyondIdentity::Client::ApiError.new(error_message, error_code))
end
end
expect(service.execute).to eq(false)
expect(gpg_key.errors.full_messages).to eq(['BeyondIdentity: service error'])
context 'when authorization fails' do
let(:error_message) { 'unauthorized: key is invalid' }
let(:error_code) { 403 }
it 'returns false and sets an error' do
expect(service.execute).to eq(false)
expect(gpg_key.errors.full_messages).to eq(["BeyondIdentity: #{error_message}"])
expect(gpg_key.externally_verified).to be_falsey
end
end
context 'when the key is not found' do
let(:error_message) { 'gpg key is not found' }
let(:error_code) { 404 }
it 'returns true and does not set an error' do
expect(service.execute).to eq(true)
expect(gpg_key.errors.full_messages).to eq([])
expect(gpg_key.externally_verified).to be_falsey
end
end
end
end
end

View File

@ -2375,15 +2375,15 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
end
describe 'invite_email command' do
describe 'add_email command' do
let_it_be(:issuable) { issue }
it_behaves_like 'failed command', "No email participants were added. Either none were provided, or they already exist." do
let(:content) { '/invite_email' }
let(:content) { '/add_email' }
end
context 'with existing email participant' do
let(:content) { '/invite_email a@gitlab.com' }
let(:content) { '/add_email a@gitlab.com' }
before do
issuable.issue_email_participants.create!(email: "a@gitlab.com")
@ -2393,7 +2393,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with new email participants' do
let(:content) { '/invite_email a@gitlab.com b@gitlab.com' }
let(:content) { '/add_email a@gitlab.com b@gitlab.com' }
subject(:add_emails) { service.execute(content, issuable) }
@ -2408,7 +2408,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with mixed case email' do
let(:content) { '/invite_email FirstLast@GitLab.com' }
let(:content) { '/add_email FirstLast@GitLab.com' }
it 'returns correctly cased message' do
_, _, message = add_emails
@ -2418,7 +2418,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with invalid email' do
let(:content) { '/invite_email a@gitlab.com bad_email' }
let(:content) { '/add_email a@gitlab.com bad_email' }
it 'only adds valid emails' do
expect { add_emails }.to change { issue.issue_email_participants.count }.by(1)
@ -2426,7 +2426,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with existing email' do
let(:content) { '/invite_email a@gitlab.com existing@gitlab.com' }
let(:content) { '/add_email a@gitlab.com existing@gitlab.com' }
it 'only adds new emails' do
issue.issue_email_participants.create!(email: 'existing@gitlab.com')
@ -2442,7 +2442,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with duplicate email' do
let(:content) { '/invite_email a@gitlab.com a@gitlab.com' }
let(:content) { '/add_email a@gitlab.com a@gitlab.com' }
it 'only adds unique new emails' do
expect { add_emails }.to change { issue.issue_email_participants.count }.by(1)
@ -2450,7 +2450,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
context 'with more than 6 emails' do
let(:content) { '/invite_email a@gitlab.com b@gitlab.com c@gitlab.com d@gitlab.com e@gitlab.com f@gitlab.com g@gitlab.com' }
let(:content) { '/add_email a@gitlab.com b@gitlab.com c@gitlab.com d@gitlab.com e@gitlab.com f@gitlab.com g@gitlab.com' }
it 'only adds 6 new emails' do
expect { add_emails }.to change { issue.issue_email_participants.count }.by(6)
@ -2463,7 +2463,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
stub_const("IssueEmailParticipants::CreateService::MAX_NUMBER_OF_RECORDS", 1)
end
let(:content) { '/invite_email a@gitlab.com' }
let(:content) { '/add_email a@gitlab.com' }
it_behaves_like 'failed command',
"No email participants were added. Either none were provided, or they already exist."
@ -2474,7 +2474,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
stub_const("IssueEmailParticipants::CreateService::MAX_NUMBER_OF_RECORDS", 1)
end
let(:content) { '/invite_email a@gitlab.com b@gitlab.com' }
let(:content) { '/add_email a@gitlab.com b@gitlab.com' }
it 'only adds one new email' do
expect { add_emails }.to change { issue.issue_email_participants.count }.by(1)
@ -2493,14 +2493,14 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
it 'is part of the available commands' do
expect(service.available_commands(issuable)).to include(a_hash_including(name: :invite_email))
expect(service.available_commands(issuable)).to include(a_hash_including(name: :add_email))
end
context 'with non-persisted issue' do
let(:issuable) { build(:issue) }
it 'is not part of the available commands' do
expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :invite_email))
expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :add_email))
end
end
end

View File

@ -14,9 +14,9 @@ import (
type metadata struct {
Modified int64 `json:"modified,omitempty"`
Mode string `json:"mode,omitempty"`
CRC uint32 `json:"crc,omitempty"`
Size uint64 `json:"size,omitempty"`
Zipped uint64 `json:"zipped,omitempty"`
CRC uint32 `json:"crc"`
Size uint64 `json:"size"`
Zipped uint64 `json:"zipped"`
Comment string `json:"comment,omitempty"`
}

View File

@ -50,6 +50,11 @@ func validateMetadata(r io.Reader) error {
}
}
emptyEntry := `{"crc":0,"size":0,"zipped":0}`
if !bytes.Contains(meta, []byte(emptyEntry)) {
return fmt.Errorf("zipartifacts: metadata for empty file not found")
}
return nil
}