Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1c3cf2e3cd
commit
a73ecda21e
|
|
@ -53,6 +53,18 @@ rails-production-server-boot-puma-cng:
|
|||
- retry_times_sleep 10 5 "curl http://127.0.0.1:8080"
|
||||
- kill $(jobs -p)
|
||||
|
||||
ruby_syntax:
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
before_script:
|
||||
- source scripts/utils.sh
|
||||
image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}
|
||||
parallel:
|
||||
matrix:
|
||||
- RUBY_VERSION: ["3.0", "3.1", "3.2"]
|
||||
script:
|
||||
- run_timed_command "fail_on_warnings scripts/lint/check_ruby_syntax.rb"
|
||||
|
||||
no-ee-check:
|
||||
extends:
|
||||
- .preflight-job-base
|
||||
|
|
|
|||
|
|
@ -2939,6 +2939,11 @@
|
|||
- <<: *if-default-refs
|
||||
changes: *code-patterns
|
||||
|
||||
.preflight:rules:ruby_syntax:
|
||||
rules:
|
||||
- <<: *if-default-refs
|
||||
changes: *backend-patterns
|
||||
|
||||
.preflight:rules:no-ee-check:
|
||||
rules:
|
||||
- <<: *if-not-foss
|
||||
|
|
|
|||
|
|
@ -4370,7 +4370,6 @@ RSpec/FeatureCategory:
|
|||
- 'spec/models/concerns/signature_type_spec.rb'
|
||||
- 'spec/models/concerns/sortable_spec.rb'
|
||||
- 'spec/models/concerns/spammable_spec.rb'
|
||||
- 'spec/models/concerns/stepable_spec.rb'
|
||||
- 'spec/models/concerns/strip_attribute_spec.rb'
|
||||
- 'spec/models/concerns/subquery_spec.rb'
|
||||
- 'spec/models/concerns/subscribable_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2654,7 +2654,6 @@ RSpec/NamedSubject:
|
|||
- 'spec/models/concerns/resolvable_note_spec.rb'
|
||||
- 'spec/models/concerns/runners_token_prefixable_spec.rb'
|
||||
- 'spec/models/concerns/spammable_spec.rb'
|
||||
- 'spec/models/concerns/stepable_spec.rb'
|
||||
- 'spec/models/concerns/subquery_spec.rb'
|
||||
- 'spec/models/concerns/token_authenticatable_spec.rb'
|
||||
- 'spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2,8 +2,16 @@
|
|||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import { makeLoadVersionsErrorMessage } from '~/ml/model_registry/translations';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { s__ } from '~/locale';
|
||||
import getModelVersionsQuery from '../graphql/queries/get_model_versions.query.graphql';
|
||||
import { GRAPHQL_PAGE_SIZE, MODEL_ENTITIES } from '../constants';
|
||||
import {
|
||||
GRAPHQL_PAGE_SIZE,
|
||||
LIST_KEY_CREATED_AT,
|
||||
LIST_KEY_VERSION,
|
||||
MODEL_ENTITIES,
|
||||
SORT_KEY_CREATED_AT,
|
||||
SORT_KEY_ORDER,
|
||||
} from '../constants';
|
||||
import SearchableList from './searchable_list.vue';
|
||||
import EmptyState from './empty_state.vue';
|
||||
import ModelVersionRow from './model_version_row.vue';
|
||||
|
|
@ -24,6 +32,8 @@ export default {
|
|||
return {
|
||||
modelVersions: {},
|
||||
errorMessage: undefined,
|
||||
skipQueries: true,
|
||||
queryVariables: {},
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
|
|
@ -38,6 +48,9 @@ export default {
|
|||
error(error) {
|
||||
this.handleError(error);
|
||||
},
|
||||
skip() {
|
||||
return this.skipQueries;
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -50,31 +63,25 @@ export default {
|
|||
pageInfo() {
|
||||
return this.modelVersions?.pageInfo ?? {};
|
||||
},
|
||||
queryVariables() {
|
||||
return {
|
||||
id: this.gid,
|
||||
first: GRAPHQL_PAGE_SIZE,
|
||||
};
|
||||
},
|
||||
versions() {
|
||||
return this.modelVersions?.nodes ?? [];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchPage(pageInfo) {
|
||||
const variables = {
|
||||
...this.queryVariables,
|
||||
...pageInfo,
|
||||
fetchPage(variables) {
|
||||
this.queryVariables = {
|
||||
id: this.gid,
|
||||
first: GRAPHQL_PAGE_SIZE,
|
||||
...variables,
|
||||
version: variables.name,
|
||||
orderBy: variables.orderBy?.toUpperCase() || SORT_KEY_CREATED_AT,
|
||||
sort: variables.sort?.toUpperCase() || SORT_KEY_ORDER,
|
||||
};
|
||||
|
||||
this.$apollo.queries.modelVersions
|
||||
.fetchMore({
|
||||
variables,
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
return fetchMoreResult;
|
||||
},
|
||||
})
|
||||
.catch(this.handleError);
|
||||
this.errorMessage = null;
|
||||
this.skipQueries = false;
|
||||
|
||||
this.$apollo.queries.modelVersions.fetchMore({});
|
||||
},
|
||||
handleError(error) {
|
||||
this.errorMessage = makeLoadVersionsErrorMessage(error.message);
|
||||
|
|
@ -82,13 +89,26 @@ export default {
|
|||
},
|
||||
},
|
||||
modelVersionEntity: MODEL_ENTITIES.modelVersion,
|
||||
sortableFields: [
|
||||
{
|
||||
orderBy: LIST_KEY_VERSION,
|
||||
label: s__('MlExperimentTracking|Version'),
|
||||
},
|
||||
{
|
||||
orderBy: LIST_KEY_CREATED_AT,
|
||||
label: s__('MlExperimentTracking|Created at'),
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<searchable-list
|
||||
show-search
|
||||
:page-info="pageInfo"
|
||||
:items="versions"
|
||||
:error-message="errorMessage"
|
||||
:is-loading="isLoading"
|
||||
:sortable-fields="$options.sortableFields"
|
||||
@fetch-page="fetchPage"
|
||||
>
|
||||
<template #empty-state>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const LIST_KEY_CREATED_AT = 'created_at';
|
||||
export const LIST_KEY_VERSION = 'version';
|
||||
export const SORT_KEY_CREATED_AT = 'CREATED_AT';
|
||||
export const SORT_KEY_ORDER = 'DESC';
|
||||
|
||||
export const BASE_SORT_FIELDS = Object.freeze([
|
||||
{
|
||||
orderBy: 'name',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,24 @@
|
|||
query getModelVersions($id: MlModelID!, $first: Int, $last: Int, $after: String, $before: String) {
|
||||
query getModelVersions(
|
||||
$id: MlModelID!
|
||||
$version: String
|
||||
$orderBy: MlModelVersionsOrderBy
|
||||
$sort: SortDirectionEnum
|
||||
$first: Int
|
||||
$last: Int
|
||||
$after: String
|
||||
$before: String
|
||||
) {
|
||||
mlModel(id: $id) {
|
||||
id
|
||||
versions(after: $after, before: $before, first: $first, last: $last) {
|
||||
versions(
|
||||
version: $version
|
||||
orderBy: $orderBy
|
||||
sort: $sort
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
) {
|
||||
count
|
||||
nodes {
|
||||
id
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Stepable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def steps
|
||||
self.class._all_steps
|
||||
end
|
||||
|
||||
def execute_steps
|
||||
initial_result = {}
|
||||
|
||||
steps.inject(initial_result) do |previous_result, callback|
|
||||
result = method(callback).call(previous_result)
|
||||
|
||||
if result[:status] != :success
|
||||
result[:last_step] = callback
|
||||
|
||||
break result
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def _all_steps
|
||||
@_all_steps ||= []
|
||||
end
|
||||
|
||||
def steps(*methods)
|
||||
_all_steps.concat methods
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -33,6 +33,7 @@ module Organizations
|
|||
delegate :description, :description_html, :avatar, :avatar_url, :remove_avatar!, to: :organization_detail
|
||||
|
||||
accepts_nested_attributes_for :organization_detail
|
||||
accepts_nested_attributes_for :organization_users
|
||||
|
||||
def self.default_organization
|
||||
find_by(id: DEFAULT_ORGANIZATION_ID)
|
||||
|
|
|
|||
|
|
@ -26,10 +26,17 @@ module Packages
|
|||
|
||||
scope :stale, -> { where(package_id: nil) }
|
||||
scope :pending_destruction, -> { stale.default }
|
||||
scope :with_file_name, ->(file_name) { where(file: file_name) }
|
||||
scope :with_signature, ->(signature) { where(signature: signature) }
|
||||
scope :with_file_name, ->(file_name) { where(arel_table[:file].lower.eq(file_name.downcase)) }
|
||||
scope :with_signature, ->(signature) { where(arel_table[:signature].lower.eq(signature.downcase)) }
|
||||
scope :with_file_sha256, ->(checksums) { where(file_sha256: checksums) }
|
||||
|
||||
def self.find_by_signature_and_file_and_checksum(signature, file_name, checksums)
|
||||
with_signature(signature)
|
||||
.with_file_name(file_name)
|
||||
.with_file_sha256(checksums)
|
||||
.take
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_object_storage_key
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ module WorkItems
|
|||
end
|
||||
|
||||
def self.quick_action_commands
|
||||
[:set_parent, :add_child]
|
||||
[:set_parent, :add_child, :remove_parent]
|
||||
end
|
||||
|
||||
def self.quick_action_params
|
||||
[:set_parent, :add_child]
|
||||
[:set_parent, :add_child, :remove_parent]
|
||||
end
|
||||
|
||||
def self.sync_params
|
||||
|
|
@ -30,9 +30,11 @@ module WorkItems
|
|||
def self.process_quick_action_param(param_name, value)
|
||||
return super unless param_name.in?(quick_action_params) && value.present?
|
||||
|
||||
return { parent: value } if param_name == :set_parent
|
||||
|
||||
return { children: value } if param_name == :add_child
|
||||
if [:set_parent, :remove_parent].include?(param_name)
|
||||
{ parent: value.is_a?(WorkItem) ? value : nil }
|
||||
else
|
||||
{ children: value }
|
||||
end
|
||||
end
|
||||
|
||||
def self.process_sync_params(params)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@
|
|||
module Organizations
|
||||
class CreateService < ::Organizations::BaseService
|
||||
def execute
|
||||
return error_no_permissions unless current_user&.can?(:create_organization)
|
||||
return error_no_permissions unless can?(current_user, :create_organization)
|
||||
|
||||
organization = Organization.create(params)
|
||||
add_organization_owner_attributes
|
||||
organization = Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification
|
||||
.allow_cross_database_modification_within_transaction(
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/438757'
|
||||
) do
|
||||
Organization.create(params)
|
||||
end
|
||||
|
||||
if organization.persisted?
|
||||
add_organization_owner(organization)
|
||||
|
||||
ServiceResponse.success(payload: { organization: organization })
|
||||
else
|
||||
error_creating(organization)
|
||||
|
|
@ -18,8 +22,8 @@ module Organizations
|
|||
|
||||
private
|
||||
|
||||
def add_organization_owner(organization)
|
||||
organization.organization_users.create(user: current_user, access_level: :owner)
|
||||
def add_organization_owner_attributes
|
||||
@params[:organization_users_attributes] = [{ user: current_user, access_level: :owner }]
|
||||
end
|
||||
|
||||
def error_no_permissions
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_primary_and_secondary_stores_for_shared_state
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134483
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429884
|
||||
milestone: '16.6'
|
||||
type: development
|
||||
group: group::scalability
|
||||
default_enabled: false
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_primary_store_as_default_for_shared_state
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134483
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429884
|
||||
milestone: '16.6'
|
||||
type: development
|
||||
group: group::scalability
|
||||
default_enabled: false
|
||||
|
|
@ -41,5 +41,5 @@ end
|
|||
# 2. Rails.cache
|
||||
# 3. HTTP clients
|
||||
Gitlab::Redis::ALL_CLASSES.each do |redis_instance|
|
||||
redis_instance.with { nil } unless redis_instance == Gitlab::Redis::ClusterSharedState
|
||||
redis_instance.with { nil }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: redis_hll_counters.quickactions.i_quickactions_remove_parent_monthly
|
||||
name: quickactions_remove_parent_monthly
|
||||
description: Count of MAU using the `/remove_parent` quick action
|
||||
product_section: dev
|
||||
product_stage: plan
|
||||
product_group: product_planning
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.9"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142174
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
options:
|
||||
events:
|
||||
- i_quickactions_remove_parent
|
||||
performance_indicator_type: []
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: redis_hll_counters.quickactions.i_quickactions_remove_parent_weekly
|
||||
name: quickactions_remove_parent_weekly
|
||||
description: Count of WAU using the `/remove_parent` quick action
|
||||
product_section: dev
|
||||
product_stage: plan
|
||||
product_group: product_planning
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.9"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142174
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
options:
|
||||
events:
|
||||
- i_quickactions_remove_parent
|
||||
performance_indicator_type: []
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexPackagesNugetSymbolsOnLowercaseSignatureAndFileName < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '16.9'
|
||||
|
||||
INDEX_NAME = 'idx_pkgs_nuget_symbols_on_lowercase_signature_and_file_name'
|
||||
|
||||
def up
|
||||
add_concurrent_index :packages_nuget_symbols, 'lower(signature), lower(file)', name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :packages_nuget_symbols, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
ef72ce15563a5841276fcbf79c02a3e657511f12b55cecf277d0c4287d15c363
|
||||
|
|
@ -32427,6 +32427,8 @@ CREATE INDEX idx_pkgs_installable_package_files_on_package_id_id_file_name ON pa
|
|||
|
||||
CREATE INDEX idx_pkgs_npm_metadata_caches_on_id_and_project_id_and_status ON packages_npm_metadata_caches USING btree (id) WHERE ((project_id IS NULL) AND (status = 0));
|
||||
|
||||
CREATE INDEX idx_pkgs_nuget_symbols_on_lowercase_signature_and_file_name ON packages_nuget_symbols USING btree (lower(signature), lower(file));
|
||||
|
||||
CREATE INDEX idx_pkgs_on_project_id_name_version_on_installable_terraform ON packages_packages USING btree (project_id, name, version, id) WHERE ((package_type = 12) AND (status = ANY (ARRAY[0, 1])));
|
||||
|
||||
CREATE INDEX idx_proj_feat_usg_on_jira_dvcs_cloud_last_sync_at_and_proj_id ON project_feature_usages USING btree (jira_dvcs_cloud_last_sync_at, project_id) WHERE (jira_dvcs_cloud_last_sync_at IS NOT NULL);
|
||||
|
|
|
|||
|
|
@ -757,11 +757,11 @@ See our [Redis guidelines](redis.md) for more information about how GitLab uses
|
|||
|
||||
#### Registry
|
||||
|
||||
- [Project page](https://github.com/docker/distribution/blob/master/README.md)
|
||||
- [Project page](https://gitlab.com/gitlab-org/container-registry)
|
||||
- Configuration:
|
||||
- [Omnibus](../update/upgrading_from_source.md#10-install-libraries-migrations-etc)
|
||||
- [Omnibus](../administration/packages/container_registry.md)
|
||||
- [Charts](https://docs.gitlab.com/charts/charts/registry/)
|
||||
- [Source](../administration/packages/container_registry.md#enable-the-container-registry)
|
||||
- [Source](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/configuration.md?ref_type=heads)
|
||||
- [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/registry.md)
|
||||
- Layer: Core Service (Processor)
|
||||
- GitLab.com: [GitLab container registry](../user/packages/container_registry/build_and_push_images.md#use-gitlab-cicd)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,21 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Support for Experiment, Beta, and Generally Available features
|
||||
|
||||
There are cases where GitLab would like to validate the edge-cases of scale, support, and maintenance burden of features in their current form for every designed use case.
|
||||
There are also scenarios where a feature is not complete enough to be considered an [MVC](https://handbook.gitlab.com/handbook/product/product-principles/#the-minimal-viable-change-mvc).
|
||||
In these cases, GitLab has the option to release features as Experiment, Beta, or Limited Availability, and users can opt-in and test the new experience.
|
||||
Features might not be fully documented or supported in the Experiment or Beta phases.
|
||||
|
||||
Please note that some features may not be aligned to these recommendations if they were developed before the recommendations were in place or if the group determined an alternative implementation approach was needed.
|
||||
|
||||
Product development teams should refrain from making changes that they reasonably believe could create significant risks or friction for GitLab users or the platform, such as:
|
||||
|
||||
- Risking damage or exfiltration of existing production data accessed by our users.
|
||||
- Destabilizing other parts of the application.
|
||||
- Introducing friction into high monthly active user (MAU) areas.
|
||||
|
||||
Some GitLab features are released as Experiment or Beta versions and are
|
||||
[not fully supported](https://about.gitlab.com/support/statement-of-support/#alpha-beta-features).
|
||||
[not fully supported](https://about.gitlab.com/support/statement-of-support/#experiment-beta-features).
|
||||
All other features are considered to be Generally Available (GA).
|
||||
|
||||
## Experiment
|
||||
|
|
@ -42,6 +55,7 @@ Beta features are:
|
|||
|
||||
- May not be ready for production use.
|
||||
- Support on a commercially-reasonable effort basis.
|
||||
- Not required or necessary for most features.
|
||||
- May be unstable.
|
||||
- Configuration and dependencies unlikely to change.
|
||||
- Features and functions unlikely to change. However, breaking changes may occur outside of major releases or with less notice than for Generally Available features.
|
||||
|
|
@ -75,5 +89,23 @@ The experimental features are only shown when people/organizations opt-in to exp
|
|||
|
||||
## All features are in production
|
||||
|
||||
All features that are available on GitLab.com are considered "in production."
|
||||
All features that are available on GitLab.com are considered "in production".
|
||||
Because all Experiment, Beta, and Generally Available features are available on GitLab.com, they are all considered to be in production.
|
||||
|
||||
## Experiment, Beta and Limited Availability Exit Criteria
|
||||
|
||||
To ensure the phases before General Availability are as short as possible each phase of Experiment, Beta and LA should include exit criteria.
|
||||
This encourages rapid iteration and reduces [cycle time](https://handbook.gitlab.com/handbook/values/#reduce-cycle-time).
|
||||
GitLab Product Managers will take the following into account when deciding what exit criteria to apply to their Experimental, Beta, and Limited Availability features:
|
||||
|
||||
- **Time**: Define an end date at which point the feature will be General Availability.
|
||||
- Consider setting a time-bound target metric that will define readiness for exit into GA (e.g. X number of customers retained MoM over 6 months after launch of Experiment, X% growth of free and paid users in three months since launch Beta, etc.)
|
||||
- Be mindful of balancing time to market, user experience, and richness of experience. Some Beta programs have lasted 1 milestone while other have lasted a couple of years.
|
||||
- **Feedback**: Define the minimum number of customers that have been onboarded and interviewed.
|
||||
- Consider also setting a time bound when using user feedback as an exit criteria for leaving a phases. If a given time period elapses and we can not solicit feedback from enough users, it is better to ship what we have and iterate on it as a GA at that point rather than maintain a pre-GA state.
|
||||
- **Limited Feature Completion**: Determine if there is functionality that should be completed before moving to General Availability.
|
||||
- Be wary of including "just one more" feature. Iteration will be easier and more effective with more feedback from more users so getting to General Availability is preferred.
|
||||
- **System Performance metrics**: Determine the criteria that the platform has shown before being ready for General Availability. Examples include response times and successfully handling a number of requests per second.
|
||||
- **Success criteria**: Not all features may reach GA. It is OK to pivot if early feedback indicates that a different direction would provide more value or a better user experience. If open questions must be answered to decide if the feature is worth putting in the product, list and answer those.
|
||||
|
||||
For the exit criteria of **AI features**, in addition to the above, see the [UX maturity requirements](https://handbook.gitlab.com/handbook/product/ai/ux-maturity/).
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ The following Rake tasks are available for use with GitLab:
|
|||
| [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. |
|
||||
| [Service Data](../development/internal_analytics/service_ping/troubleshooting.md#generate-service-ping) | Generate and troubleshoot [Service Ping](../development/internal_analytics/service_ping/index.md). |
|
||||
| [User management](user_management.md) | Perform user management tasks. |
|
||||
| [Webhooks administration](web_hooks.md) | Maintain project webhooks. |
|
||||
| [Webhook administration](web_hooks.md) | Maintain project webhooks. |
|
||||
| [X.509 signatures](x509_signatures.md) | Update X.509 commit signatures, which can be useful if the certificate store changed. |
|
||||
|
||||
To list all available Rake tasks:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Webhooks administration Rake tasks
|
||||
# Webhook administration Rake tasks
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ after the limits change in January, 2021:
|
|||
| **GitLab Pages** TLS connections (for a given **GitLab Pages domain**) | | **400** requests per **10 seconds** |
|
||||
| **Pipeline creation** requests (for a given **project, user, and commit**) | | **25** requests per minute |
|
||||
| **Alert integration endpoint** requests (for a given **project**) | | **3600** requests per hour |
|
||||
| **[Pull mirroring](../project/repository/mirror/pull.md)** intervals | **5** minutes | **5** minutes |
|
||||
|
||||
More details are available on the rate limits for
|
||||
[protected paths](#protected-paths-throttle) and
|
||||
|
|
|
|||
|
|
@ -329,6 +329,12 @@ For a safer development environment, you can use the [GitLab Development Kit (GD
|
|||
|
||||
You can [review recently triggered webhook payloads](#troubleshooting) in GitLab settings. For each webhook event, a detail page exists with information about the data GitLab sends and receives from the webhook endpoint.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Project hooks API](../../../api/projects.md#hooks)
|
||||
- [Group hooks API](../../../api/groups.md#hooks)
|
||||
- [System hooks API](../../../api/system_hooks.md)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
> - **Recent events** for group webhooks [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325642) in GitLab 15.3.
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
|
|||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current assignees with those specified. |
|
||||
| `/relabel ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current labels with those specified. |
|
||||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Remove due date. |
|
||||
| `/remove_parent` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Removes the parent work item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/434344) in GitLab 16.9. |
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
|
||||
| `/set_parent <work_item>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Set parent work item to `<work_item>`. The `<work_item>` value should be in the format of `#iid`, `group/project#iid`, or a URL to a work item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420798) in GitLab 16.5. |
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ In GitLab 13.10, if a project has the **Reject unsigned commits** push rule, the
|
|||
create commits through the GitLab Web IDE.
|
||||
|
||||
To allow committing through the Web IDE on a project with this push rule, a GitLab administrator
|
||||
must disable the feature flag `reject_unsigned_commits_by_gitlab`. [with a flag](../../../administration/feature_flags.md)
|
||||
must disable the feature flag `reject_unsigned_commits_by_gitlab` [with a flag](../../../administration/feature_flags.md).
|
||||
|
||||
```ruby
|
||||
Feature.disable(:reject_unsigned_commits_by_gitlab)
|
||||
|
|
|
|||
|
|
@ -79,16 +79,14 @@ module API
|
|||
|
||||
project_or_group_without_auth
|
||||
|
||||
# upcase the age part of the signature in case we received it in lowercase:
|
||||
# https://github.com/dotnet/symstore/blob/main/docs/specs/SSQP_Key_Conventions.md#key-formatting-basic-rules
|
||||
signature = declared_params[:signature].sub(/.{8}\z/, &:upcase)
|
||||
checksums = headers['Symbolchecksum'].scan(SHA256_REGEX).flatten
|
||||
|
||||
symbol = ::Packages::Nuget::Symbol
|
||||
.with_signature(signature)
|
||||
.with_file_name(declared_params[:file_name])
|
||||
.with_file_sha256(checksums)
|
||||
.first
|
||||
.find_by_signature_and_file_and_checksum(
|
||||
declared_params[:signature],
|
||||
declared_params[:file_name],
|
||||
checksums
|
||||
)
|
||||
|
||||
not_found!('Symbol') unless symbol
|
||||
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ module Gitlab
|
|||
cache_identity = Gitlab::Redis::Cache.with(&:inspect) # rubocop:disable CodeReuse/ActiveRecord -- This is not AR
|
||||
|
||||
Gitlab::Redis::SharedState.with do |redis|
|
||||
yield redis unless cache_identity == redis.default_store.inspect
|
||||
yield redis unless cache_identity == redis.inspect
|
||||
end
|
||||
|
||||
block_result
|
||||
|
|
|
|||
|
|
@ -40,6 +40,20 @@ module Gitlab
|
|||
@execution_message[:set_parent] = success_msg[:set_parent]
|
||||
end
|
||||
|
||||
desc { _('Remove work item parent') }
|
||||
explanation do
|
||||
format(
|
||||
_("Remove %{parent_ref} as this work item's parent."),
|
||||
parent_ref: work_item_parent.to_reference(quick_action_target)
|
||||
)
|
||||
end
|
||||
types WorkItem
|
||||
condition { work_item_parent.present? && can_admin_link? }
|
||||
command :remove_parent do
|
||||
@updates[:remove_parent] = true
|
||||
@execution_message[:remove_parent] = success_msg[:remove_parent]
|
||||
end
|
||||
|
||||
desc { _('Add children to work item') }
|
||||
explanation do |child_param|
|
||||
format(_("Add %{child_ref} to this work item as child(ren)."), child_ref: child_param)
|
||||
|
|
@ -126,10 +140,15 @@ module Gitlab
|
|||
type: _('Type changed successfully.'),
|
||||
promote_to: _("Work item promoted successfully."),
|
||||
set_parent: _('Work item parent set successfully'),
|
||||
remove_parent: _('Work item parent removed successfully'),
|
||||
add_child: _('Child work item(s) added successfully')
|
||||
}
|
||||
end
|
||||
|
||||
def work_item_parent
|
||||
quick_action_target.work_item_parent
|
||||
end
|
||||
|
||||
def supports_parent?
|
||||
::WorkItems::HierarchyRestriction.find_by_child_type_id(quick_action_target.work_item_type_id).present?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ module Gitlab
|
|||
ALL_CLASSES = [
|
||||
Gitlab::Redis::BufferedCounter,
|
||||
Gitlab::Redis::Cache,
|
||||
Gitlab::Redis::ClusterSharedState,
|
||||
Gitlab::Redis::DbLoadBalancing,
|
||||
Gitlab::Redis::FeatureFlag,
|
||||
Gitlab::Redis::Queues,
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Redis
|
||||
class ClusterSharedState < ::Gitlab::Redis::Wrapper
|
||||
class << self
|
||||
def config_fallback
|
||||
SharedState
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
module Gitlab
|
||||
module Redis
|
||||
class SharedState < ::Gitlab::Redis::MultiStoreWrapper
|
||||
def self.multistore
|
||||
MultiStore.new(ClusterSharedState.pool, pool, store_name)
|
||||
end
|
||||
class SharedState < ::Gitlab::Redis::Wrapper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5806,9 +5806,6 @@ msgstr ""
|
|||
msgid "Analytics|Updating visualization %{visualizationName}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Analytics|Usage overview for %{namespaceName} group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Analytics|Use the visualization designer to create custom visualizations. After you save a visualization, you can add it to a dashboard."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31394,6 +31391,9 @@ msgstr ""
|
|||
msgid "MlExperimentTracking|No name"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlExperimentTracking|Version"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|%d model"
|
||||
msgid_plural "MlModelRegistry|%d models"
|
||||
msgstr[0] ""
|
||||
|
|
@ -40787,6 +40787,9 @@ msgstr ""
|
|||
msgid "Remove %{displayReference}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove %{parent_ref} as this work item's parent."
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove %{ruleName}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40952,6 +40955,9 @@ msgstr ""
|
|||
msgid "Remove weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove work item parent"
|
||||
msgstr ""
|
||||
|
||||
msgid "Removed"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -45098,7 +45104,7 @@ msgstr ""
|
|||
msgid "SecurityReports|Report has expired"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Results show vulnerabilities introduced by the merge request, in addition to existing vulnerabilities from the latest successful pipeline in your project's default branch."
|
||||
msgid "SecurityReports|Results show vulnerability findings from the latest successful %{helpPageLinkStart}pipeline%{helpPageLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Scan details"
|
||||
|
|
@ -55758,6 +55764,9 @@ msgstr ""
|
|||
msgid "Work in progress limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Work item parent removed successfully"
|
||||
msgstr ""
|
||||
|
||||
msgid "Work item parent set successfully"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58158,6 +58167,9 @@ msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "ciReport|View all pipeline findings"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|View full report"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../../tooling/lib/tooling/check_ruby_syntax"
|
||||
|
||||
files = `git ls-files -z`.split("\0")
|
||||
|
||||
checker = Tooling::CheckRubySyntax.new(files)
|
||||
|
||||
puts format("Checking %{files} Ruby files...", files: checker.ruby_files.size)
|
||||
|
||||
errors = checker.run
|
||||
|
||||
puts
|
||||
|
||||
if errors.any?
|
||||
puts "Syntax errors found (#{errors.size}):"
|
||||
puts errors
|
||||
|
||||
exit 1
|
||||
else
|
||||
puts "No syntax errors found."
|
||||
|
||||
exit 0
|
||||
end
|
||||
|
|
@ -9,12 +9,11 @@ import SearchableList from '~/ml/model_registry/components/searchable_list.vue';
|
|||
import ModelVersionRow from '~/ml/model_registry/components/model_version_row.vue';
|
||||
import getModelVersionsQuery from '~/ml/model_registry/graphql/queries/get_model_versions.query.graphql';
|
||||
import EmptyState from '~/ml/model_registry/components/empty_state.vue';
|
||||
import { GRAPHQL_PAGE_SIZE, MODEL_ENTITIES } from '~/ml/model_registry/constants';
|
||||
import { MODEL_ENTITIES } from '~/ml/model_registry/constants';
|
||||
import {
|
||||
emptyModelVersionsQuery,
|
||||
modelVersionsQuery,
|
||||
graphqlModelVersions,
|
||||
graphqlPageInfo,
|
||||
} from '../graphql_mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
|
@ -79,11 +78,19 @@ describe('ModelVersionList', () => {
|
|||
});
|
||||
|
||||
describe('when list is loaded with data', () => {
|
||||
let resolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
mountComponent();
|
||||
resolver = jest.fn().mockResolvedValue(modelVersionsQuery());
|
||||
mountComponent({ resolver });
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('calls query only once on setup', () => {
|
||||
expect(resolver).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Passes items to list', () => {
|
||||
expect(findSearchableList().props('items')).toEqual(graphqlModelVersions);
|
||||
});
|
||||
|
|
@ -115,13 +122,22 @@ describe('ModelVersionList', () => {
|
|||
findSearchableList().vm.$emit('fetch-page', {
|
||||
after: 'eyJpZCI6IjIifQ',
|
||||
first: 30,
|
||||
id: 'gid://gitlab/Ml::Model/2',
|
||||
name: '1.0.0',
|
||||
orderBy: 'version',
|
||||
sort: 'asc',
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(resolver).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ after: graphqlPageInfo.endCursor, first: GRAPHQL_PAGE_SIZE }),
|
||||
expect.objectContaining({
|
||||
id: 'gid://gitlab/Ml::Model/2',
|
||||
after: 'eyJpZCI6IjIifQ',
|
||||
first: 30,
|
||||
version: '1.0.0',
|
||||
orderBy: 'VERSION',
|
||||
sort: 'ASC',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Redis::ClusterSharedState, feature_category: :redis do
|
||||
include_examples "redis_new_instance_shared_examples", 'cluster_shared_state', Gitlab::Redis::SharedState
|
||||
end
|
||||
|
|
@ -6,5 +6,4 @@ RSpec.describe Gitlab::Redis::SharedState do
|
|||
let(:instance_specific_config_file) { "config/redis.shared_state.yml" }
|
||||
|
||||
include_examples "redis_shared_examples"
|
||||
include_examples "multi_store_wrapper_shared_examples"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Stepable do
|
||||
let(:described_class) do
|
||||
Class.new do
|
||||
include Stepable
|
||||
|
||||
attr_writer :return_non_success
|
||||
|
||||
steps :method1, :method2, :method3
|
||||
|
||||
def execute
|
||||
execute_steps
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def method1(_result)
|
||||
{ status: :success }
|
||||
end
|
||||
|
||||
def method2(result)
|
||||
return { status: :not_a_success } if @return_non_success
|
||||
|
||||
result.merge({ status: :success, variable1: 'var1', excluded_variable: 'a' })
|
||||
end
|
||||
|
||||
def method3(result)
|
||||
result.except(:excluded_variable).merge({ status: :success, variable2: 'var2' })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:prepended_module) do
|
||||
Module.new do
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
prepended do
|
||||
steps :appended_method1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def appended_method1(previous_result)
|
||||
previous_result.merge({ status: :success })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
described_class.prepend(prepended_module)
|
||||
end
|
||||
|
||||
it 'stops after the first non success status' do
|
||||
subject.return_non_success = true
|
||||
|
||||
expect(subject).not_to receive(:method3)
|
||||
expect(subject).not_to receive(:appended_method1)
|
||||
|
||||
expect(subject.execute).to eq(
|
||||
status: :not_a_success,
|
||||
last_step: :method2
|
||||
)
|
||||
end
|
||||
|
||||
context 'when all methods return success' do
|
||||
it 'calls all methods in order' do
|
||||
expect(subject).to receive(:method1).and_call_original.ordered
|
||||
expect(subject).to receive(:method2).and_call_original.ordered
|
||||
expect(subject).to receive(:method3).and_call_original.ordered
|
||||
expect(subject).to receive(:appended_method1).and_call_original.ordered
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it 'merges variables returned by all steps' do
|
||||
expect(subject.execute).to eq(
|
||||
status: :success,
|
||||
variable1: 'var1',
|
||||
variable2: 'var2'
|
||||
)
|
||||
end
|
||||
|
||||
it 'can modify results of previous steps' do
|
||||
expect(subject.execute).not_to include(excluded_variable: 'a')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple stepable classes' do
|
||||
let(:other_class) do
|
||||
Class.new do
|
||||
include Stepable
|
||||
|
||||
steps :other_method1, :other_method2
|
||||
|
||||
private
|
||||
|
||||
def other_method1
|
||||
{ status: :success }
|
||||
end
|
||||
|
||||
def other_method2
|
||||
{ status: :success }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not leak steps' do
|
||||
expect(other_class.new.steps).to contain_exactly(:other_method1, :other_method2)
|
||||
expect(subject.steps).to contain_exactly(:method1, :method2, :method3, :appended_method1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -67,6 +67,7 @@ RSpec.describe Organizations::Organization, type: :model, feature_category: :cel
|
|||
|
||||
describe 'nested attributes' do
|
||||
it { is_expected.to accept_nested_attributes_for(:organization_detail) }
|
||||
it { is_expected.to accept_nested_attributes_for(:organization_users) }
|
||||
end
|
||||
|
||||
context 'when using scopes' do
|
||||
|
|
|
|||
|
|
@ -52,8 +52,16 @@ RSpec.describe Packages::Nuget::Symbol, type: :model, feature_category: :package
|
|||
let_it_be(:signature) { 'signature' }
|
||||
let_it_be(:symbol) { create(:nuget_symbol, signature: signature) }
|
||||
|
||||
it 'returns symbols with the given signature' do
|
||||
expect(with_signature).to eq([symbol])
|
||||
shared_examples 'returns symbols with the given signature' do
|
||||
it { is_expected.to contain_exactly(symbol) }
|
||||
end
|
||||
|
||||
it_behaves_like 'returns symbols with the given signature'
|
||||
|
||||
context 'when signature is in uppercase' do
|
||||
subject(:with_signature) { described_class.with_signature(signature.upcase) }
|
||||
|
||||
it_behaves_like 'returns symbols with the given signature'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -63,12 +71,22 @@ RSpec.describe Packages::Nuget::Symbol, type: :model, feature_category: :package
|
|||
let_it_be(:file_name) { 'file_name' }
|
||||
let_it_be(:symbol) { create(:nuget_symbol) }
|
||||
|
||||
shared_examples 'returns symbols with the given file_name' do
|
||||
it 'returns symbols with the given file_name' do
|
||||
expect(with_file_name).to contain_exactly(symbol)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
symbol.update_column(:file, file_name)
|
||||
end
|
||||
|
||||
it 'returns symbols with the given file_name' do
|
||||
expect(with_file_name).to eq([symbol])
|
||||
it_behaves_like 'returns symbols with the given file_name'
|
||||
|
||||
context 'when file_name is in uppercase' do
|
||||
subject(:with_file_name) { described_class.with_file_name(file_name.upcase) }
|
||||
|
||||
it_behaves_like 'returns symbols with the given file_name'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -82,6 +100,22 @@ RSpec.describe Packages::Nuget::Symbol, type: :model, feature_category: :package
|
|||
expect(with_file_sha256).to eq([symbol])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_by_signature_and_file_and_checksum' do
|
||||
subject { described_class.find_by_signature_and_file_and_checksum(signature, file_name, checksum) }
|
||||
|
||||
let_it_be(:signature) { 'signature' }
|
||||
let_it_be(:file_name) { 'file.pdb' }
|
||||
let_it_be(:checksum) { OpenSSL::Digest.hexdigest('SHA256', 'checksums') }
|
||||
let_it_be(:symbol) { create(:nuget_symbol, signature: signature, file_sha256: checksum) }
|
||||
let_it_be(:another_symbol) { create(:nuget_symbol) }
|
||||
|
||||
before do
|
||||
symbol.update_column(:file, file_name)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(symbol) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'callbacks' do
|
||||
|
|
|
|||
|
|
@ -426,6 +426,28 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
describe '/remove_parent' do
|
||||
let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/remove_parent" }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
create(:parent_link, work_item_parent: parent, work_item: noteable)
|
||||
end
|
||||
|
||||
it 'leaves the note empty' do
|
||||
expect(execute(note)).to be_empty
|
||||
end
|
||||
|
||||
it 'removes work item parent' do
|
||||
execute(note)
|
||||
|
||||
expect(noteable.valid?).to be_truthy
|
||||
expect(noteable.work_item_parent).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe '/promote_to' do
|
||||
shared_examples 'promotes work item' do |from:, to:|
|
||||
it 'leaves the note empty' do
|
||||
|
|
|
|||
|
|
@ -2770,6 +2770,42 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
|
|||
expect(updates).to eq(set_parent: parent)
|
||||
end
|
||||
end
|
||||
|
||||
context '/remove_parent command' do
|
||||
let_it_be_with_reload(:work_item) { create(:work_item, :task, project: project) }
|
||||
|
||||
let(:content) { "/remove_parent" }
|
||||
|
||||
context 'when a parent is not present' do
|
||||
it 'is empty' do
|
||||
_, explanations = service.explain(content, work_item)
|
||||
|
||||
expect(explanations).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a parent is present' do
|
||||
let_it_be(:parent) { create(:work_item, :issue, project: project) }
|
||||
|
||||
before do
|
||||
create(:parent_link, work_item_parent: parent, work_item: work_item)
|
||||
end
|
||||
|
||||
it 'returns correct explanation' do
|
||||
_, explanations = service.explain(content, work_item)
|
||||
|
||||
expect(explanations)
|
||||
.to contain_exactly("Remove #{parent.to_reference(work_item)} as this work item's parent.")
|
||||
end
|
||||
|
||||
it 'returns success message' do
|
||||
_, updates, message = service.execute(content, work_item)
|
||||
|
||||
expect(updates).to eq(remove_parent: true)
|
||||
expect(message).to eq('Work item parent removed successfully')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#explain' do
|
||||
|
|
|
|||
|
|
@ -7152,7 +7152,6 @@
|
|||
- './spec/models/concerns/sha_attribute_spec.rb'
|
||||
- './spec/models/concerns/sortable_spec.rb'
|
||||
- './spec/models/concerns/spammable_spec.rb'
|
||||
- './spec/models/concerns/stepable_spec.rb'
|
||||
- './spec/models/concerns/strip_attribute_spec.rb'
|
||||
- './spec/models/concerns/subscribable_spec.rb'
|
||||
- './spec/models/concerns/taggable_queries_spec.rb'
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ RSpec.shared_examples 'nuget symbol file endpoint' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with valid target' do
|
||||
shared_examples 'successful response' do
|
||||
it 'returns the symbol file' do
|
||||
subject
|
||||
|
||||
|
|
@ -771,6 +771,10 @@ RSpec.shared_examples 'nuget symbol file endpoint' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with valid target' do
|
||||
it_behaves_like 'successful response'
|
||||
end
|
||||
|
||||
context 'when target does not exist' do
|
||||
let(:target) { double(id: 1234567890) }
|
||||
|
||||
|
|
@ -797,6 +801,13 @@ RSpec.shared_examples 'nuget symbol file endpoint' do
|
|||
it_behaves_like 'returning response status', :bad_request
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signature & filename are in uppercase' do
|
||||
let(:filename) { symbol.file.filename.upcase }
|
||||
let(:signature) { symbol.signature.upcase }
|
||||
|
||||
it_behaves_like 'successful response'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with nuget_symbol_server_enabled setting disabled' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "fast_spec_helper"
|
||||
require "fileutils"
|
||||
require "rspec-parameterized"
|
||||
|
||||
require_relative "../../../../tooling/lib/tooling/check_ruby_syntax"
|
||||
|
||||
RSpec.describe Tooling::CheckRubySyntax, feature_category: :tooling do
|
||||
let(:files) { Dir.glob("**/*") }
|
||||
|
||||
subject(:checker) { described_class.new(files) }
|
||||
|
||||
around do |example|
|
||||
Dir.mktmpdir do |dir|
|
||||
Dir.chdir(dir) do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ruby_files" do
|
||||
subject { checker.ruby_files }
|
||||
|
||||
context "without files" do
|
||||
it { is_expected.to eq([]) }
|
||||
end
|
||||
|
||||
context "with files ending with .rb" do
|
||||
before do
|
||||
FileUtils.touch("foo.rb")
|
||||
FileUtils.touch("bar.rb")
|
||||
FileUtils.touch("baz.erb")
|
||||
end
|
||||
|
||||
it { is_expected.to contain_exactly("foo.rb", "bar.rb") }
|
||||
end
|
||||
|
||||
context "with special Ruby files" do
|
||||
let(:files) do
|
||||
%w[foo/Guardfile danger/Dangerfile gems/Gemfile Rakefile]
|
||||
end
|
||||
|
||||
before do
|
||||
files.each do |file|
|
||||
FileUtils.mkdir_p(File.dirname(file))
|
||||
FileUtils.touch(file)
|
||||
end
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(files) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#run" do
|
||||
subject(:errors) { checker.run }
|
||||
|
||||
shared_examples "no errors" do
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
|
||||
context "without files" do
|
||||
include_examples "no errors"
|
||||
end
|
||||
|
||||
context "with perfect Ruby code" do
|
||||
before do
|
||||
File.write("perfect.rb", "perfect = code")
|
||||
end
|
||||
|
||||
include_examples "no errors"
|
||||
end
|
||||
|
||||
context "with invalid Ruby code" do
|
||||
before do
|
||||
File.write("invalid.rb", "invalid,")
|
||||
end
|
||||
|
||||
it "has errors" do
|
||||
expect(errors).to include(a_kind_of(SyntaxError))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'set' # rubocop:disable Lint/RedundantRequireStatement -- Ruby 3.1 and earlier needs this. Drop this line after Ruby 3.2+ is only supported.
|
||||
|
||||
module Tooling
|
||||
# Checks passed files for valid Ruby syntax.
|
||||
#
|
||||
# It does not check for compile time warnings yet. See https://gitlab.com/-/snippets/1929968
|
||||
class CheckRubySyntax
|
||||
VALID_RUBYFILES = %w[Rakefile Dangerfile Gemfile Guardfile].to_set.freeze
|
||||
|
||||
attr_reader :files
|
||||
|
||||
def initialize(files)
|
||||
@files = files
|
||||
end
|
||||
|
||||
def ruby_files
|
||||
@ruby_files ||=
|
||||
@files.select do |file|
|
||||
file.end_with?(".rb") || VALID_RUBYFILES.include?(File.basename(file))
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
ruby_files.filter_map do |file|
|
||||
check_ruby(file)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_ruby(file)
|
||||
RubyVM::InstructionSequence.compile(File.open(file), file)
|
||||
|
||||
nil
|
||||
rescue SyntaxError => e
|
||||
e
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue