Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e408c9c787
commit
96dfc10639
|
|
@ -1047,8 +1047,9 @@
|
|||
"oneOf": [
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
"boolean",
|
||||
"number",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -1091,8 +1092,9 @@
|
|||
"oneOf": [
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
"boolean",
|
||||
"number",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -1119,8 +1121,9 @@
|
|||
"patternProperties": {
|
||||
".*": {
|
||||
"type": [
|
||||
"string",
|
||||
"number"
|
||||
"boolean",
|
||||
"number",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WorkItems
|
||||
class WorkItemUpdatedEvent < Gitlab::EventStore::Event
|
||||
def schema
|
||||
{
|
||||
'type' => 'object',
|
||||
'required' => %w[id namespace_id],
|
||||
'properties' => {
|
||||
'id' => { 'type' => 'integer' },
|
||||
'namespace_id' => { 'type' => 'integer' },
|
||||
'updated_attributes' => {
|
||||
'type' => 'array',
|
||||
'items' => {
|
||||
'type' => 'string'
|
||||
}
|
||||
},
|
||||
'updated_widgets' => {
|
||||
'type' => 'array',
|
||||
'items' => {
|
||||
'type' => 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2458,6 +2458,7 @@ class Project < ApplicationRecord
|
|||
.append(key: 'CI', value: 'true')
|
||||
.append(key: 'GITLAB_CI', value: 'true')
|
||||
.append(key: 'CI_COMPONENT_FQDN', value: Gitlab.config.gitlab_ci.component_fqdn)
|
||||
.append(key: 'CI_SERVER_FQDN', value: Gitlab.config.gitlab_ci.component_fqdn)
|
||||
.append(key: 'CI_SERVER_URL', value: Gitlab.config.gitlab.url)
|
||||
.append(key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host)
|
||||
.append(key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ module WorkItems
|
|||
updated_work_item = super
|
||||
|
||||
if updated_work_item.valid?
|
||||
publish_event(work_item)
|
||||
success(payload(work_item))
|
||||
else
|
||||
error(updated_work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(updated_work_item))
|
||||
|
|
@ -80,6 +81,19 @@ module WorkItems
|
|||
author: current_user
|
||||
)
|
||||
end
|
||||
|
||||
def publish_event(work_item)
|
||||
event = WorkItems::WorkItemUpdatedEvent.new(data: {
|
||||
id: work_item.id,
|
||||
namespace_id: work_item.namespace_id,
|
||||
updated_attributes: work_item.previous_changes&.keys&.map(&:to_s),
|
||||
updated_widgets: @widget_params&.keys&.map(&:to_s)
|
||||
}.tap(&:compact_blank!))
|
||||
|
||||
work_item.run_after_commit_or_now do
|
||||
Gitlab::EventStore.publish(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jekyll_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_harp_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_octopress_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_brunch_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_doxygen_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hyde_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_lektor_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jbake_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hexo_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_middleman_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hugo_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_pelican_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_nanoc_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_swaggerui_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jigsaw_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_metalsmith_monthl
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_gatsby_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_html_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_zola_monthly
|
|||
description: Count of pipelines using the Zola Pages template
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.1"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jekyll_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_harp_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_octopress_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_brunch_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_doxygen_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hyde_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_lektor_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jbake_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hexo_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_middleman_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_hugo_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_pelican_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_nanoc_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_swaggerui_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_jigsaw_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_metalsmith_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_gatsby_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_html_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_zola_weekly
|
|||
description: Count of pipelines using the Zola Pages template
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_authoring
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.1"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
migration_job_name: UpdateSbomComponentsNameBasedOnPep503
|
||||
description: Updates sbom_components.name in accordance with PEP503
|
||||
feature_category: software_composition_analysis
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145700
|
||||
milestone: '16.11'
|
||||
queued_migration_version: 20240223130548
|
||||
finalize_after: '2024-03-24'
|
||||
|
||||
|
|
@ -7,4 +7,6 @@ feature_categories:
|
|||
- subscription_management
|
||||
classes:
|
||||
- GitlabSubscriptions::AddOnPurchase
|
||||
gitlab_schema: gitlab_main
|
||||
gitlab_schema: gitlab_main_cell
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ feature_categories:
|
|||
- seat_cost_management
|
||||
classes:
|
||||
- GitlabSubscriptions::UserAddOnAssignment
|
||||
gitlab_schema: gitlab_main
|
||||
gitlab_schema: gitlab_main_cell
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
class AddAutoCanceledByPartitionIdToPCiBuildsSelfManaged < Gitlab::Database::Migration[2.1]
|
||||
enable_lock_retries!
|
||||
|
||||
# rubocop:disable Migration/AddColumnsToWideTables -- partitioning ci_builds table
|
||||
def up
|
||||
add_column :p_ci_builds, :auto_canceled_by_partition_id, :bigint, default: 100, null: false, if_not_exists: true
|
||||
end
|
||||
# rubocop:enable Migration/AddColumnsToWideTables
|
||||
|
||||
def down
|
||||
remove_column :p_ci_builds, :auto_canceled_by_partition_id, if_exists: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueUpdateSbomComponentsNameBasedOnPep503 < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.11'
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = "UpdateSbomComponentsNameBasedOnPep503"
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 2000
|
||||
SUB_BATCH_SIZE = 500
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:sbom_components,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :sbom_components, :id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
b604c37383d4c1846c57920c6f3388ccf8ce62e52dc99700d26f3cae6c0a61b3
|
||||
|
|
@ -254,7 +254,7 @@ To [Create a new group](../user/group/index.md#create-a-group) select **New grou
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340920) in GitLab 14.4.
|
||||
> - Merging topics [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366884) in GitLab 15.5.
|
||||
|
||||
[Topics](../user/project/settings/project_features_permissions.md#project-topics) are used to categorize and find similar projects.
|
||||
You can categorize and find similar projects with [topics](../user/project/project_topics.md).
|
||||
|
||||
### View all topics
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ For example:
|
|||
```yaml
|
||||
include:
|
||||
# include the component located in the current project from the current SHA
|
||||
- component: $CI_COMPONENT_FQDN/$CI_PROJECT_PATH/my-component@$CI_COMMIT_SHA
|
||||
- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/my-component@$CI_COMMIT_SHA
|
||||
inputs:
|
||||
stage: build
|
||||
|
||||
|
|
@ -473,7 +473,7 @@ For example, to create a component with `stage` configuration that can be define
|
|||
stages: [verify, deploy]
|
||||
|
||||
include:
|
||||
- component: $CI_COMPONENT_FQDN/gitlab-org/ruby-test@1.0
|
||||
- component: $CI_SERVER_FQDN/gitlab-org/ruby-test@1.0
|
||||
inputs:
|
||||
stage: verify
|
||||
```
|
||||
|
|
@ -506,7 +506,7 @@ For example, use `inputs` instead of variables to configure a scanner's output f
|
|||
|
||||
```yaml
|
||||
include:
|
||||
- component: $CI_COMPONENT_FQDN/my-scanner@1.0
|
||||
- component: $CI_SERVER_FQDN/my-scanner@1.0
|
||||
inputs:
|
||||
scanner-output: yaml
|
||||
```
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ as it can cause the pipeline to behave unexpectedly.
|
|||
| `CI_COMMIT_TAG_MESSAGE` | Pipeline | 15.5 | all | The commit tag message. Available only in pipelines for tags. |
|
||||
| `CI_COMMIT_TIMESTAMP` | Pipeline | 13.4 | all | The timestamp of the commit in the [ISO 8601](https://www.rfc-editor.org/rfc/rfc3339#appendix-A) format. For example, `2022-01-31T16:47:55Z`. [UTC by default](../../administration/timezone.md). |
|
||||
| `CI_COMMIT_TITLE` | Pipeline | 10.8 | all | The title of the commit. The full first line of the message. |
|
||||
| `CI_COMPONENT_FQDN` | Pipeline | 16.10 | all | The fully qualified domain name (FQDN) of the instance. For example `gitlab.example.com:8080`. |
|
||||
| `CI_CONCURRENT_ID` | Jobs only | all | 11.10 | The unique ID of build execution in a single executor. |
|
||||
| `CI_CONCURRENT_PROJECT_ID` | Jobs only | all | 11.10 | The unique ID of build execution in a single executor and project. |
|
||||
| `CI_CONFIG_PATH` | Pipeline | 9.4 | 0.5 | The path to the CI/CD configuration file. Defaults to `.gitlab-ci.yml`. Read-only inside a running pipeline. |
|
||||
|
|
@ -128,6 +127,7 @@ as it can cause the pipeline to behave unexpectedly.
|
|||
| `CI_RUNNER_SHORT_TOKEN` | Jobs only | all | 12.3 | The runner's unique ID, used to authenticate new job requests. In [GitLab 14.9](https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/2251) and later, the token contains a prefix, and the first 17 characters are used. Prior to 14.9, the first eight characters are used. |
|
||||
| `CI_RUNNER_TAGS` | Jobs only | 8.10 | 0.5 | A comma-separated list of the runner tags. |
|
||||
| `CI_RUNNER_VERSION` | Jobs only | all | 10.6 | The version of the GitLab Runner running the job. |
|
||||
| `CI_SERVER_FQDN` | Pipeline | 16.10 | all | The fully qualified domain name (FQDN) of the instance. For example `gitlab.example.com:8080`. |
|
||||
| `CI_SERVER_HOST` | Pipeline | 12.1 | all | The host of the GitLab instance URL, without protocol or port. For example `gitlab.example.com`. |
|
||||
| `CI_SERVER_NAME` | Pipeline | all | all | The name of CI/CD server that coordinates jobs. |
|
||||
| `CI_SERVER_PORT` | Pipeline | 12.8 | all | The port of the GitLab instance URL, without host or protocol. For example `8080`. |
|
||||
|
|
|
|||
|
|
@ -115,11 +115,11 @@ The cursor is created by encoding a JSON object which contains the relevant orde
|
|||
```ruby
|
||||
ordering = {"id"=>"72410125", "created_at"=>"2020-10-08 18:05:21.953398000 UTC"}
|
||||
json = ordering.to_json
|
||||
cursor = Base64Bp.urlsafe_encode64(json, padding: false)
|
||||
cursor = Base64.urlsafe_encode64(json, padding: false)
|
||||
|
||||
"eyJpZCI6IjcyNDEwMTI1IiwiY3JlYXRlZF9hdCI6IjIwMjAtMTAtMDggMTg6MDU6MjEuOTUzMzk4MDAwIFVUQyJ9"
|
||||
|
||||
json = Base64Bp.urlsafe_decode64(cursor)
|
||||
json = Base64.urlsafe_decode64(cursor)
|
||||
Gitlab::Json.parse(json)
|
||||
|
||||
{"id"=>"72410125", "created_at"=>"2020-10-08 18:05:21.953398000 UTC"}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ These scoring are based on Google's classifications in the [DORA 2022 Accelerate
|
|||
|
||||
#### Filter the DORA Performers score by project topics
|
||||
|
||||
When used in combination with a [YAML configuration](#using-yaml-configuration), you can filter the projects shown based on their assigned [topics](../project/settings/project_features_permissions.md#project-topics).
|
||||
When used in combination with a [YAML configuration](#using-yaml-configuration), you can filter the projects shown based on their assigned [topics](../project/project_topics.md).
|
||||
|
||||
```yaml
|
||||
panels:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Tenant Scale
|
||||
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
|
||||
---
|
||||
|
||||
# Project topics
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
Topics are labels that you can assign to projects to help you organize and find them.
|
||||
A topic is typically a short name that describes the content or purpose of a project.
|
||||
You can assign a topic to several projects.
|
||||
|
||||
For example, you can create and assign the topics `python` and `hackathon` to all projects that use Python and are intended for Hackathon contributions.
|
||||
|
||||
Topics assigned to a project are displayed in the **Project overview** and [**Projects**](working_with_projects.md#view-all-projects-for-the-instance) lists, below the project information description.
|
||||
|
||||
NOTE:
|
||||
Only users with access to the project can see the topics assigned to that project,
|
||||
but everyone (including unauthenticated users) can see the topics available on the GitLab instance.
|
||||
Do not include sensitive information in the name of a topic.
|
||||
|
||||
## Explore topics
|
||||
|
||||
To explore project topics:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Explore**.
|
||||
1. On the left sidebar, select **Topics**. The **Explore topics** page displays a list of all project topics.
|
||||
1. Optional. To filter topics by name, in the search box, enter your search criteria.
|
||||
1. To view the projects associated with a topic, select a topic.
|
||||
You can also access a topic page with the URL `https://gitlab.com/explore/projects/topics/<topic-name>`.
|
||||
|
||||
## Filter and sort topics
|
||||
|
||||
On the project topic page, you can filter the list of projects that have that topic by:
|
||||
|
||||
- Name
|
||||
- Language
|
||||
- Visibility
|
||||
- Owner
|
||||
- Archived projects
|
||||
|
||||
You can also sort the projects by:
|
||||
|
||||
- Date
|
||||
- Name
|
||||
- Number of stars
|
||||
|
||||
- To filter projects by name, in the search box, enter your search criteria.
|
||||
- To sort projects by other criteria, from the dropdown lists, select an option.
|
||||
|
||||
## Subscribe to a topic
|
||||
|
||||
If you want to know when new projects are added to a topic, you can use its RSS feed.
|
||||
|
||||
You can do this either from the **Explore topics** page or a project with topics.
|
||||
|
||||
To subscribe to a topic:
|
||||
|
||||
- From the **Explore topics** page:
|
||||
|
||||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Explore**.
|
||||
1. Select **Topics**.
|
||||
1. Select the topic you want to subscribe to.
|
||||
1. In the upper-right corner, select **Subscribe to the new projects feed** (**{rss}**).
|
||||
|
||||
- From a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. In the **Project overview** page, from the **Topics** list select the topic you want to subscribe to.
|
||||
1. In the upper-right corner, select **Subscribe to the new projects feed** (**{rss}**).
|
||||
|
||||
The results are displayed as an RSS feed in Atom format.
|
||||
The URL of the result contains a feed token and the list of projects that have the topic. You can add this URL to your feed reader.
|
||||
|
||||
## Assign topics to a project
|
||||
|
||||
To assign topics to a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings** > **General**.
|
||||
1. In the **Topics** text box, enter the project topics. Popular topics are suggested as you type.
|
||||
1. Select **Save changes**.
|
||||
|
||||
NOTE:
|
||||
The assigned topics are visible only to users with access to the project, but everyone can see which topics exist on the GitLab instance. Do not include sensitive information in the name of a topic.
|
||||
|
||||
## Administer topics
|
||||
|
||||
Instance administrators can administer all project topics from the
|
||||
[Admin Area's Topics page](../../administration/admin_area.md#administering-topics).
|
||||
|
|
@ -128,90 +128,3 @@ To set this default:
|
|||
1. Select **Settings > Merge requests**.
|
||||
1. Select **Enable "Delete source branch" option by default**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Project topics
|
||||
|
||||
Topics are labels that you can assign to projects to help you organize and find them.
|
||||
A topic is typically a short name that describes the content or purpose of a project.
|
||||
You can assign a topic to several projects.
|
||||
|
||||
For example, you can create and assign the topics `python` and `hackathon` to all projects that use Python and are intended for Hackathon contributions.
|
||||
|
||||
Topics assigned to a project are displayed in the **Project overview** and [**Projects**](../working_with_projects.md#view-all-projects-for-the-instance) lists, below the project information description.
|
||||
|
||||
NOTE:
|
||||
Only users with access to the project can see the topics assigned to that project,
|
||||
but everyone (including unauthenticated users) can see the topics available on the GitLab instance.
|
||||
Do not include sensitive information in the name of a topic.
|
||||
|
||||
### Explore topics
|
||||
|
||||
To explore project topics:
|
||||
|
||||
1. On the left sidebar, select **Search or go to**.
|
||||
1. Select **Explore**.
|
||||
1. On the left sidebar, select **Topics**. The **Explore topics** page displays a list of all project topics.
|
||||
1. Optional. To filter topics by name, in the search box, enter your search criteria.
|
||||
1. To view the projects associated with a topic, select a topic.
|
||||
You can also access a topic page with the URL `https://gitlab.com/explore/projects/topics/<topic-name>`.
|
||||
|
||||
### Filter and sort topics
|
||||
|
||||
On the project topic page, you can filter the list of projects that have that topic by:
|
||||
|
||||
- Name
|
||||
- Language
|
||||
- Visibility
|
||||
- Owner
|
||||
- Archived projects
|
||||
|
||||
You can also sort the projects by:
|
||||
|
||||
- Date
|
||||
- Name
|
||||
- Number of stars
|
||||
|
||||
- To filter projects by name, in the search box, enter your search criteria.
|
||||
- To sort projects by other criteria, from the dropdown lists, select an option.
|
||||
|
||||
### Subscribe to a topic
|
||||
|
||||
If you want to know when new projects are added to a topic, you can use its RSS feed.
|
||||
|
||||
You can do this either from the **Explore topics** page or a project with topics.
|
||||
|
||||
To subscribe to a topic:
|
||||
|
||||
- From the **Explore topics** page:
|
||||
|
||||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Explore**.
|
||||
1. Select **Topics**.
|
||||
1. Select the topic you want to subscribe to.
|
||||
1. In the upper-right corner, select **Subscribe to the new projects feed** (**{rss}**).
|
||||
|
||||
- From a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. In the **Project overview** page, from the **Topics** list select the topic you want to subscribe to.
|
||||
1. In the upper-right corner, select **Subscribe to the new projects feed** (**{rss}**).
|
||||
|
||||
The results are displayed as an RSS feed in Atom format.
|
||||
The URL of the result contains a feed token and the list of projects that have the topic. You can add this URL to your feed reader.
|
||||
|
||||
### Assign topics to a project
|
||||
|
||||
To assign topics to a project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings** > **General**.
|
||||
1. In the **Topics** text box, enter the project topics. Popular topics are suggested as you type.
|
||||
1. Select **Save changes**.
|
||||
|
||||
NOTE:
|
||||
The assigned topics are visible only to users with access to the project, but everyone can see which topics exist on the GitLab instance. Do not include sensitive information in the name of a topic.
|
||||
|
||||
### Administer topics
|
||||
|
||||
Instance administrators can administer all project topics from the
|
||||
[Admin Area's Topics page](../../../administration/admin_area.md#administering-topics).
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ module Gitlab
|
|||
autoload :VERSION, 'gitlab/backup/cli/version'
|
||||
autoload :Runner, 'gitlab/backup/cli/runner'
|
||||
autoload :Utils, 'gitlab/backup/cli/utils'
|
||||
autoload :Dependencies, 'gitlab/backup/cli/dependencies'
|
||||
autoload :Shell, 'gitlab/backup/cli/shell'
|
||||
|
||||
Error = Class.new(StandardError)
|
||||
# Your code goes here...
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Backup
|
||||
module Cli
|
||||
module Dependencies
|
||||
# Search on PATH or default locations for provided binary and return its fullpath
|
||||
#
|
||||
# @param [String] binary name
|
||||
# @return [String|False] full path to the binary file
|
||||
def self.find_executable(binary)
|
||||
executable_file = proc { |name| next name if File.file?(name) && File.executable?(name) }
|
||||
|
||||
# Retrieve PATH from ENV or use a fallback
|
||||
path = ENV['PATH']&.split(File::PATH_SEPARATOR) || %w[/usr/local/bin /usr/bin /bin]
|
||||
|
||||
# check binary against each PATH
|
||||
path.each do |dir|
|
||||
file = File.expand_path(binary, dir)
|
||||
|
||||
return file if executable_file.call(file)
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Check whether provided binary name exists on PATH or default locations
|
||||
#
|
||||
# @param [String] binary name
|
||||
# @return [Boolean] whether binary exists
|
||||
def self.executable_exist?(name)
|
||||
!!find_executable(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
autoload :Open3, 'open3'
|
||||
|
||||
module Gitlab
|
||||
module Backup
|
||||
module Cli
|
||||
module Shell
|
||||
autoload :Command, 'gitlab/backup/cli/shell/command'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Backup
|
||||
module Cli
|
||||
module Shell
|
||||
# Abstraction to control shell command execution
|
||||
# It provides an easier API to common usages
|
||||
class Command
|
||||
attr_reader :cmd_args, :env
|
||||
|
||||
# Result data structure from running a command
|
||||
#
|
||||
# @attr [String] stdout
|
||||
# @attr [String] stderr
|
||||
# @attr [Process::Status] status
|
||||
# @attr [Float] duration
|
||||
Result = Struct.new(:stdout, :stderr, :status, :duration, keyword_init: true)
|
||||
|
||||
# @example Usage
|
||||
# Shell.new('echo', 'Some amazing output').capture
|
||||
# @param [Array<String>] cmd_args
|
||||
# @param [Hash<String,String>] env
|
||||
def initialize(*cmd_args, env: {})
|
||||
@cmd_args = cmd_args
|
||||
@env = env
|
||||
end
|
||||
|
||||
# Execute a process and return its output and status
|
||||
#
|
||||
# @return [Command::Result] Captured output from executing a process
|
||||
def capture
|
||||
start = Time.now
|
||||
stdout, stderr, status = Open3.capture3(env, *cmd_args)
|
||||
duration = Time.now - start
|
||||
|
||||
Result.new(stdout: stdout, stderr: stderr, status: status, duration: duration)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,7 @@ module Gitlab
|
|||
module Cli
|
||||
module Utils
|
||||
autoload :PgDump, 'gitlab/backup/cli/utils/pg_dump'
|
||||
autoload :Tar, 'gitlab/backup/cli/utils/tar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Backup
|
||||
module Cli
|
||||
module Utils
|
||||
# Run tar command to create or extract content from an archive
|
||||
class Tar
|
||||
# Returns the version of tar command available
|
||||
#
|
||||
# @return [String] the first line of `--version` output
|
||||
def version
|
||||
version = Shell::Command.new(cmd, '--version').capture.stdout.dup
|
||||
version.force_encoding('locale').split("\n").first
|
||||
end
|
||||
|
||||
def cmd
|
||||
@cmd ||= if gtar_available?
|
||||
# In BSD/Darwin we can get GNU tar by running 'gtar' instead
|
||||
'gtar'
|
||||
else
|
||||
'tar'
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gtar_available?
|
||||
Dependencies.executable_exist?('gtar')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Backup::Cli::Dependencies do
|
||||
let(:bin_path) { Dir.mktmpdir('dependencies', temp_path) }
|
||||
|
||||
before do
|
||||
stub_env('PATH', bin_path)
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rmtree(bin_path)
|
||||
end
|
||||
|
||||
describe '.find_executable' do
|
||||
it 'returns the full path of the executable' do
|
||||
executable = create_dummy_executable('dummy')
|
||||
|
||||
expect(described_class.find_executable('dummy')).to eq(executable)
|
||||
end
|
||||
|
||||
it 'returns nil when executable cant be found' do
|
||||
expect(described_class.find_executable('non-existent')).to be_nil
|
||||
end
|
||||
|
||||
it 'also finds by absolute path' do
|
||||
executable = create_dummy_executable('dummy')
|
||||
|
||||
expect(described_class.find_executable(executable)).to eq(executable)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.executable_exist?' do
|
||||
it 'returns true if an executable exists in the PATH' do
|
||||
create_dummy_executable('dummy')
|
||||
|
||||
expect(described_class.executable_exist?('dummy')).to be_truthy
|
||||
end
|
||||
|
||||
it 'returns false when no exectuable can be found' do
|
||||
expect(described_class.executable_exist?('non-existent')).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
def create_dummy_executable(name)
|
||||
filepath = File.join(bin_path, name)
|
||||
|
||||
FileUtils.touch(filepath)
|
||||
File.chmod(0o755, filepath)
|
||||
|
||||
filepath
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Backup::Cli::Shell::Command do
|
||||
let(:envdata) do
|
||||
{ 'CUSTOM' => 'data' }
|
||||
end
|
||||
|
||||
subject(:command) { described_class }
|
||||
|
||||
describe '#initialize' do
|
||||
it 'accepts required attributes' do
|
||||
expect { command.new('ls', '-l') }.not_to raise_exception
|
||||
end
|
||||
|
||||
it 'accepts optional attributes' do
|
||||
expect { command.new('ls', '-l', env: envdata) }.not_to raise_exception
|
||||
end
|
||||
end
|
||||
|
||||
describe '#capture' do
|
||||
it 'returns stdout from executed command' do
|
||||
expected_output = 'my custom content'
|
||||
|
||||
result = command.new('echo', expected_output).capture
|
||||
|
||||
expect(result.stdout.chomp).to eq(expected_output)
|
||||
expect(result.stderr).to be_empty
|
||||
end
|
||||
|
||||
it 'returns stderr from executed command' do
|
||||
expected_output = 'my custom error content'
|
||||
|
||||
result = command.new('sh', '-c', "echo #{expected_output} > /dev/stderr").capture
|
||||
|
||||
expect(result.stdout).to be_empty
|
||||
expect(result.stderr.chomp).to eq(expected_output)
|
||||
end
|
||||
|
||||
it 'returns a Process::Status from the executed command' do
|
||||
result = command.new('pwd').capture
|
||||
|
||||
expect(result.status).to be_a(Process::Status)
|
||||
expect(result.status).to respond_to(:exited?, :termsig, :stopsig, :exitstatus, :success?, :pid)
|
||||
end
|
||||
|
||||
it 'returns the execution duration' do
|
||||
result = command.new('sleep 0.1').capture
|
||||
|
||||
expect(result.duration).to be > 0.1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Backup::Cli::Utils::Tar do
|
||||
subject(:tar) { described_class.new }
|
||||
|
||||
describe '#version' do
|
||||
it 'returns a tar version' do
|
||||
expect(tar.version).to match(/tar \(GNU tar\) \d+\.\d+/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Backup::Cli do
|
||||
it "has a version number" do
|
||||
expect(Gitlab::Backup::Cli::VERSION).not_to be nil
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "gitlab/backup/cli"
|
||||
require 'tmpdir'
|
||||
require 'fileutils'
|
||||
|
||||
# Load spec support code
|
||||
Dir['spec/support/**/*.rb'].each { |f| load f }
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Enable flags like --only-failures and --next-failure
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
def spec_path
|
||||
Pathname.new(__dir__).join('..').expand_path
|
||||
end
|
||||
|
||||
def temp_path
|
||||
spec_path.join('..', 'tmp').expand_path
|
||||
end
|
||||
|
||||
def stub_env(var, return_value)
|
||||
stub_const('ENV', ENV.to_hash.merge(var => return_value))
|
||||
end
|
||||
|
|
@ -389,8 +389,7 @@ module Backup
|
|||
end
|
||||
|
||||
def tar_version
|
||||
tar_version, _ = Gitlab::Popen.popen(%w[tar --version])
|
||||
tar_version.dup.force_encoding('locale').split("\n").first
|
||||
Gitlab::Backup::Cli::Utils::Tar.new.version
|
||||
end
|
||||
|
||||
def backup_file?(file)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class UpdateSbomComponentsNameBasedOnPep503 < BatchedMigrationJob
|
||||
operation_name :update_component_name_based_on_pep_503
|
||||
scope_to ->(relation) { relation.where(purl_type: 8).where("name LIKE ?", "%.%") }
|
||||
feature_category :software_composition_analysis
|
||||
|
||||
INDEX_NAME = 'index_sbom_components_on_component_type_name_and_purl_type'
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
sub_batch.each do |component|
|
||||
component.update!(name: normalized_name(component.name))
|
||||
rescue ActiveRecord::RecordNotUnique => e # rubocop:disable BackgroundMigration/AvoidSilentRescueExceptions -- this is only silent when related to INDEX_NAME
|
||||
raise unless e.message.include?(INDEX_NAME)
|
||||
|
||||
Gitlab::BackgroundMigration::Logger.warn(
|
||||
message: "Error updating sbom_component name based on #{INDEX_NAME}",
|
||||
model_id: component.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalized_name(name)
|
||||
name.gsub(Sbom::PackageUrl::Normalizer::PYPI_REGEX, '-')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,13 +16,13 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def applies_to?(config)
|
||||
Gitlab::Config::Entry::Validators::AlphanumericValidator.validate(config)
|
||||
Gitlab::Config::Entry::Validators::ScalarValidator.validate(config)
|
||||
end
|
||||
end
|
||||
|
||||
validations do
|
||||
validates :key, alphanumeric: true
|
||||
validates :config, alphanumeric: true
|
||||
validates :config, scalar: true
|
||||
end
|
||||
|
||||
def value
|
||||
|
|
@ -52,7 +52,7 @@ module Gitlab
|
|||
|
||||
validations do
|
||||
validates :key, alphanumeric: true
|
||||
validates :config_value, alphanumeric: true, allow_nil: true
|
||||
validates :config_value, scalar: true, allow_nil: true
|
||||
validates :config_description, alphanumeric: true, allow_nil: true
|
||||
validates :config_expand, boolean: true, allow_nil: true
|
||||
validates :config_options, array_of_strings: true, allow_nil: true
|
||||
|
|
|
|||
|
|
@ -364,6 +364,19 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
class ScalarValidator < ActiveModel::EachValidator
|
||||
def self.validate(value)
|
||||
value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Integer) ||
|
||||
value.is_a?(Float) || [true, false].include?(value)
|
||||
end
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
unless self.class.validate(value)
|
||||
record.errors.add(attribute, 'must be a scalar')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ExpressionValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
unless value.is_a?(String) && ::Gitlab::Ci::Pipeline::Expression::Statement.new(value).valid?
|
||||
|
|
|
|||
|
|
@ -59141,7 +59141,7 @@ msgstr ""
|
|||
msgid "ciReport|No code quality issues found"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|Parsing schema failed. Check the output of the scanner."
|
||||
msgid "ciReport|Parsing schema failed. Check the validity of your .gitlab-ci.yml content."
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|RPS"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module RuboCop
|
|||
# Tables with large number of columns (> 50 on GitLab.com as of 01/2021)
|
||||
WIDE_TABLES = %i[
|
||||
ci_builds
|
||||
p_ci_builds
|
||||
namespaces
|
||||
projects
|
||||
users
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import postcss from 'postcss';
|
|||
import { compile, Logger } from 'sass';
|
||||
import glob from 'glob';
|
||||
/* eslint-disable import/extensions */
|
||||
import tailwindcss from 'tailwindcss/lib/plugin.js';
|
||||
import tailwindConfig from '../../../config/tailwind.config.js';
|
||||
import IS_EE from '../../../config/helpers/is_ee_env.js';
|
||||
import IS_JH from '../../../config/helpers/is_jh_env.js';
|
||||
/* eslint-enable import/extensions */
|
||||
|
|
@ -165,8 +167,11 @@ function resolveCompilationTargets() {
|
|||
return Object.fromEntries([...result.entries()].map((entry) => entry.reverse()));
|
||||
}
|
||||
|
||||
function createPostCSSProcessor() {
|
||||
return postcss([autoprefixer()]);
|
||||
function createPostCSSProcessors() {
|
||||
return {
|
||||
tailwind: postcss([tailwindcss(tailwindConfig), autoprefixer()]),
|
||||
default: postcss([autoprefixer()]),
|
||||
};
|
||||
}
|
||||
|
||||
export async function compileAllStyles({ shouldWatch = false }) {
|
||||
|
|
@ -174,7 +179,7 @@ export async function compileAllStyles({ shouldWatch = false }) {
|
|||
|
||||
const compilationTargets = resolveCompilationTargets();
|
||||
|
||||
const processor = createPostCSSProcessor();
|
||||
const processors = createPostCSSProcessors();
|
||||
|
||||
const sassCompilerOptions = {
|
||||
loadPaths: resolveLoadPaths(),
|
||||
|
|
@ -194,6 +199,22 @@ export async function compileAllStyles({ shouldWatch = false }) {
|
|||
fileWatcher = watch([]);
|
||||
}
|
||||
|
||||
async function postProcessCSS(content, source) {
|
||||
const processor = content.css.includes('@apply') ? processors.tailwind : processors.default;
|
||||
|
||||
return processor.process(content.css, {
|
||||
from: source,
|
||||
map: content.sourceMap
|
||||
? {
|
||||
from: source,
|
||||
prev: content.sourceMap,
|
||||
inline: true,
|
||||
sourcesContent: true,
|
||||
}
|
||||
: false,
|
||||
});
|
||||
}
|
||||
|
||||
async function compileSCSSFile(source, dest) {
|
||||
console.log(`\tcompiling source ${source} to ${dest}`);
|
||||
let content = compile(source, sassCompilerOptions);
|
||||
|
|
@ -207,17 +228,7 @@ export async function compileAllStyles({ shouldWatch = false }) {
|
|||
}
|
||||
}
|
||||
}
|
||||
content = await processor.process(content.css, {
|
||||
from: source,
|
||||
map: content.sourceMap
|
||||
? {
|
||||
from: source,
|
||||
prev: content.sourceMap,
|
||||
inline: true,
|
||||
sourcesContent: true,
|
||||
}
|
||||
: false,
|
||||
});
|
||||
content = await postProcessCSS(content, source);
|
||||
// Create target folder if it doesn't exist
|
||||
await mkdir(path.dirname(dest), { recursive: true });
|
||||
await writeFile(dest, content.css, 'utf-8');
|
||||
|
|
|
|||
|
|
@ -1151,4 +1151,13 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tar_version' do
|
||||
it 'returns a version matching expected format' do
|
||||
tar_version = subject.send(:tar_version)
|
||||
|
||||
expect(tar_version).to be_a(String)
|
||||
expect(tar_version).to match(/tar \(GNU tar\) [0-9]\.[0-9]+/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::UpdateSbomComponentsNameBasedOnPep503, feature_category: :software_composition_analysis do
|
||||
let(:components) { table(:sbom_components) }
|
||||
|
||||
before do
|
||||
%w[aws-cdk.region-info azure.identity backports.cached-property backports.csv].each do |input_name|
|
||||
components.create!(name: input_name, purl_type: 8, component_type: 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject(:perform_migration) do
|
||||
described_class.new(
|
||||
start_id: components.first.id,
|
||||
end_id: components.last.id,
|
||||
batch_table: :sbom_components,
|
||||
batch_column: :id,
|
||||
sub_batch_size: components.count,
|
||||
pause_ms: 0,
|
||||
connection: ActiveRecord::Base.connection
|
||||
).perform
|
||||
end
|
||||
|
||||
let(:expected_names) { %w[aws-cdk-region-info azure-identity backports-cached-property backports-csv] }
|
||||
|
||||
it 'successfully updates name according to PEP 0503' do
|
||||
expect(Gitlab::BackgroundMigration::Logger).not_to receive(:warn)
|
||||
|
||||
perform_migration
|
||||
|
||||
expect(components.pluck(:name)).to eq(expected_names)
|
||||
end
|
||||
|
||||
context 'with existing record in regards to name, purl_type and component_type' do
|
||||
before do
|
||||
components.create!(name: 'aws-cdk-region-info', purl_type: 8, component_type: 0)
|
||||
end
|
||||
|
||||
it 'raises ActiveRecord::RecordNotUnique' do
|
||||
expect(Gitlab::BackgroundMigration::Logger).to receive(:warn)
|
||||
|
||||
perform_migration
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Config::Entry::Variable do
|
||||
RSpec.describe Gitlab::Ci::Config::Entry::Variable, feature_category: :pipeline_composition do
|
||||
let(:config) { {} }
|
||||
let(:metadata) { {} }
|
||||
|
||||
|
|
@ -17,45 +17,22 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
|
|||
end
|
||||
|
||||
describe 'SimpleVariable' do
|
||||
context 'when config is a string' do
|
||||
let(:config) { 'value' }
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
describe '#valid?' do
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
|
||||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
|
||||
it { is_expected.to eq('value') }
|
||||
end
|
||||
where(:config, :valid_result) do
|
||||
'string' | true
|
||||
:symbol | true
|
||||
true | true
|
||||
false | true
|
||||
2 | true
|
||||
2.2 | true
|
||||
[] | false
|
||||
{} | false
|
||||
end
|
||||
|
||||
context 'when config is an integer' do
|
||||
let(:config) { 1 }
|
||||
|
||||
describe '#valid?' do
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
|
||||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
|
||||
it { is_expected.to eq('1') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config is an array' do
|
||||
let(:config) { [] }
|
||||
|
||||
describe '#valid?' do
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
describe '#errors' do
|
||||
subject(:errors) { entry.errors }
|
||||
|
||||
it { is_expected.to include 'variable definition must be either a string or a hash' }
|
||||
with_them do
|
||||
it 'validates the config data type' do
|
||||
expect(entry.valid?).to be(valid_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -101,69 +78,32 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
|
|||
it { is_expected.to eq(value: 'value', description: 'description') }
|
||||
end
|
||||
|
||||
context 'when config value is a symbol' do
|
||||
let(:config) { { value: :value, description: 'description' } }
|
||||
context 'when validating config value data type' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
let(:config) { { value: value, description: 'description' } }
|
||||
|
||||
it { is_expected.to eq('value') }
|
||||
where(:value, :valid_result) do
|
||||
'string' | true
|
||||
:symbol | true
|
||||
true | true
|
||||
false | true
|
||||
2 | true
|
||||
2.2 | true
|
||||
[] | false
|
||||
{} | false
|
||||
end
|
||||
|
||||
describe '#value_with_data' do
|
||||
subject(:value_with_data) { entry.value_with_data }
|
||||
with_them do
|
||||
it 'casts valid values to a string' do
|
||||
expect(entry.valid?).to be(valid_result)
|
||||
expect(entry.value).to eq(value.to_s) if valid_result
|
||||
expect(entry.value_with_data).to eq({ value: value.to_s }) if valid_result
|
||||
|
||||
it { is_expected.to eq(value: 'value') }
|
||||
end
|
||||
|
||||
describe '#value_with_prefill_data' do
|
||||
subject(:value_with_prefill_data) { entry.value_with_prefill_data }
|
||||
|
||||
it { is_expected.to eq(value: 'value', description: 'description') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config value is an integer' do
|
||||
let(:config) { { value: 123, description: 'description' } }
|
||||
|
||||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
|
||||
it { is_expected.to eq('123') }
|
||||
end
|
||||
|
||||
describe '#value_with_data' do
|
||||
subject(:value_with_data) { entry.value_with_data }
|
||||
|
||||
it { is_expected.to eq(value: '123') }
|
||||
end
|
||||
|
||||
describe '#value_with_prefill_data' do
|
||||
subject(:value_with_prefill_data) { entry.value_with_prefill_data }
|
||||
|
||||
it { is_expected.to eq(value: '123', description: 'description') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config description is a symbol' do
|
||||
let(:config) { { value: 'value', description: :description } }
|
||||
|
||||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
|
||||
it { is_expected.to eq('value') }
|
||||
end
|
||||
|
||||
describe '#value_with_data' do
|
||||
subject(:value_with_data) { entry.value_with_data }
|
||||
|
||||
it { is_expected.to eq(value: 'value') }
|
||||
end
|
||||
|
||||
describe '#value_with_prefill_data' do
|
||||
subject(:value_with_prefill_data) { entry.value_with_prefill_data }
|
||||
|
||||
it { is_expected.to eq(value: 'value', description: :description) }
|
||||
if valid_result
|
||||
expect(entry.value_with_prefill_data).to eq({ value: value.to_s, description: 'description' })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Config::Entry::Variables do
|
||||
RSpec.describe Gitlab::Ci::Config::Entry::Variables, feature_category: :pipeline_composition do
|
||||
let(:config) { {} }
|
||||
let(:metadata) { {} }
|
||||
|
||||
|
|
@ -103,8 +103,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
|
|||
|
||||
context 'when value is a boolean' do
|
||||
let(:config) { { 'VAR1' => true } }
|
||||
let(:result) { { 'VAR1' => 'true' } }
|
||||
|
||||
it_behaves_like 'invalid config', /must be either a string or a hash/
|
||||
it_behaves_like 'valid config'
|
||||
end
|
||||
|
||||
context 'when entry config value has unallowed value key-value pair and value is a string' do
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ spec:
|
|||
|
||||
---
|
||||
|
||||
variables:
|
||||
ALLOW_FAILURE: $[[ inputs.allow_failure ]]
|
||||
|
||||
"$[[ inputs.job_prefix ]]-build":
|
||||
stage: $[[ inputs.job_stage ]]
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ RSpec.describe ::Gitlab::Ci::Config::Yaml::Loader, feature_category: :pipeline_c
|
|||
|
||||
let(:expected_config) do
|
||||
{
|
||||
variables: {
|
||||
ALLOW_FAILURE: false
|
||||
},
|
||||
'my-job-build': {
|
||||
stage: 'build',
|
||||
script: [
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, featur
|
|||
value: 'true' },
|
||||
{ key: 'CI_COMPONENT_FQDN',
|
||||
value: Gitlab.config.gitlab_ci.component_fqdn },
|
||||
{ key: 'CI_SERVER_FQDN',
|
||||
value: Gitlab.config.gitlab_ci.component_fqdn },
|
||||
{ key: 'CI_SERVER_URL',
|
||||
value: Gitlab.config.gitlab.url },
|
||||
{ key: 'CI_SERVER_HOST',
|
||||
|
|
|
|||
|
|
@ -3308,8 +3308,8 @@ module Gitlab
|
|||
it_behaves_like 'returns errors', 'variables config should be a hash'
|
||||
end
|
||||
|
||||
context 'returns errors if variables is not a map of key-value strings' do
|
||||
let(:config) { YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) }
|
||||
context 'returns errors if variables is not a map of scalars' do
|
||||
let(:config) { YAML.dump({ variables: { test: [] }, rspec: { script: "test" } }) }
|
||||
|
||||
it_behaves_like 'returns errors', 'variable definition must be either a string or a hash'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -135,4 +135,34 @@ RSpec.describe Gitlab::Config::Entry::Validators, feature_category: :pipeline_co
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe described_class::ScalarValidator do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:config, :valid_result) do
|
||||
'string' | true
|
||||
:symbol | true
|
||||
true | true
|
||||
false | true
|
||||
2 | true
|
||||
2.2 | true
|
||||
[] | false
|
||||
{} | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
klass.instance_eval do
|
||||
validates :config, scalar: %i[foo bar]
|
||||
end
|
||||
|
||||
allow(instance).to receive(:config).and_return(config)
|
||||
end
|
||||
|
||||
it 'validates the instance' do
|
||||
expect(instance.valid?).to be(valid_result)
|
||||
expect(instance.errors.messages_for(:config)).to contain_exactly('must be a scalar') unless valid_result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,24 +11,26 @@ RSpec.describe 'cross-database foreign keys' do
|
|||
# should be added as a comment along with the name of the column.
|
||||
let!(:allowed_cross_database_foreign_keys) do
|
||||
[
|
||||
'gitlab_subscriptions.hosted_plan_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422012
|
||||
'group_import_states.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421210
|
||||
'identities.saml_provider_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422010
|
||||
'issues.author_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issues.closed_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issues.updated_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issue_assignees.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'lfs_file_locks.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/430838
|
||||
'merge_requests.assignee_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.updated_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.merge_user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.author_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'namespace_commit_emails.email_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429804
|
||||
'namespace_commit_emails.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429804
|
||||
'path_locks.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429380
|
||||
'protected_branch_push_access_levels.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/431054
|
||||
'protected_branch_merge_access_levels.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/431055
|
||||
'user_group_callouts.user_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/421287
|
||||
'gitlab_subscriptions.hosted_plan_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422012
|
||||
'group_import_states.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421210
|
||||
'identities.saml_provider_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422010
|
||||
'issues.author_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issues.closed_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issues.updated_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'issue_assignees.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422154
|
||||
'lfs_file_locks.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/430838
|
||||
'merge_requests.assignee_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.updated_by_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.merge_user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'merge_requests.author_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/422080
|
||||
'namespace_commit_emails.email_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429804
|
||||
'namespace_commit_emails.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429804
|
||||
'path_locks.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/429380
|
||||
'protected_branch_push_access_levels.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/431054
|
||||
'protected_branch_merge_access_levels.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/431055
|
||||
'user_group_callouts.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421287
|
||||
'subscription_user_add_on_assignments.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
|
||||
'subscription_add_on_purchases.subscription_add_on_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :cell do
|
|||
'labels.group_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/434356
|
||||
'pages_domains.project_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/442178,
|
||||
'remote_mirrors.project_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444643
|
||||
'path_locks.project_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/444643
|
||||
'path_locks.project_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444643
|
||||
'subscription_add_on_purchases.namespace_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/444338
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
|
|||
end
|
||||
|
||||
def decoded_cursor(cursor)
|
||||
Gitlab::Json.parse(Base64Bp.urlsafe_decode64(cursor))
|
||||
Gitlab::Json.parse(Base64.urlsafe_decode64(cursor))
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -299,7 +299,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
|
|||
end
|
||||
|
||||
context 'when an invalid cursor is provided' do
|
||||
let(:arguments) { { before: Base64Bp.urlsafe_encode64('invalidcursor', padding: false) } }
|
||||
let(:arguments) { { before: Base64.urlsafe_encode64('invalidcursor', padding: false) } }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject.sliced_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueUpdateSbomComponentsNameBasedOnPep503, feature_category: :software_composition_analysis do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :sbom_components,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2401,6 +2401,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
{ key: 'CI', value: 'true', public: true, masked: false },
|
||||
{ key: 'GITLAB_CI', value: 'true', public: true, masked: false },
|
||||
{ key: 'CI_COMPONENT_FQDN', value: Gitlab.config.gitlab_ci.component_fqdn, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_FQDN', value: Gitlab.config.gitlab_ci.component_fqdn, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_URL', value: Gitlab.config.gitlab.url, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host, public: true, masked: false },
|
||||
{ key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s, public: true, masked: false },
|
||||
|
|
|
|||
|
|
@ -38,9 +38,41 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'publish WorkItems::WorkItemUpdatedEvent event' do |attributes: nil, widgets: nil|
|
||||
it do
|
||||
expect { expect(update_work_item[:status]).to eq(:success) }
|
||||
.to publish_event(WorkItems::WorkItemUpdatedEvent)
|
||||
.with({
|
||||
id: work_item.id,
|
||||
namespace_id: work_item.namespace.id,
|
||||
updated_attributes: attributes,
|
||||
updated_widgets: widgets
|
||||
}.tap(&:compact_blank!))
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'do not publish WorkItems::WorkItemUpdatedEvent event' do
|
||||
it do
|
||||
expect { update_work_item }.not_to publish_event(WorkItems::WorkItemUpdatedEvent)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event'
|
||||
|
||||
context 'when applying quick actions' do
|
||||
let(:opts) { { description: "/shrug" } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
context 'when work item type is not the default Issue' do
|
||||
before do
|
||||
task_type = WorkItems::Type.default_by_type(:task)
|
||||
|
|
@ -68,6 +100,15 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when title is changed' do
|
||||
let(:opts) { { title: 'changed' } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
lock_version
|
||||
title
|
||||
title_html
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
it 'triggers issuable_title_updated graphql subscription' do
|
||||
expect(GraphqlTriggers).to receive(:issuable_title_updated).with(work_item).and_call_original
|
||||
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).to receive(:track_work_item_title_changed_action).with(author: current_user)
|
||||
|
|
@ -91,6 +132,17 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when title is not changed' do
|
||||
let(:opts) { { description: 'changed' } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
it 'does not trigger issuable_title_updated graphql subscription' do
|
||||
expect(GraphqlTriggers).not_to receive(:issuable_title_updated)
|
||||
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).not_to receive(:track_work_item_title_changed_action)
|
||||
|
|
@ -107,6 +159,13 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when dates are changed' do
|
||||
let(:opts) { { start_date: Date.today } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
start_date
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
it 'tracks users updating work item dates' do
|
||||
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).to receive(:track_work_item_date_changed_action).with(author: current_user)
|
||||
|
||||
|
|
@ -121,6 +180,17 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when decription is changed' do
|
||||
let(:opts) { { description: 'description changed' } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
it 'triggers GraphQL description updated subscription' do
|
||||
expect(GraphqlTriggers).to receive(:issuable_description_updated).with(work_item).and_call_original
|
||||
|
||||
|
|
@ -146,6 +216,14 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when state_event is close' do
|
||||
let(:opts) { { state_event: 'close' } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
closed_at
|
||||
closed_by_id
|
||||
state_id
|
||||
updated_at
|
||||
]
|
||||
|
||||
it 'closes the work item' do
|
||||
expect do
|
||||
update_work_item
|
||||
|
|
@ -161,6 +239,13 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
work_item.close!
|
||||
end
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
closed_at
|
||||
state_id
|
||||
updated_at
|
||||
]
|
||||
|
||||
it 'reopens the work item' do
|
||||
expect do
|
||||
update_work_item
|
||||
|
|
@ -226,6 +311,20 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
context 'for the description widget' do
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
description_widget
|
||||
]
|
||||
|
||||
it 'updates the description of the work item' do
|
||||
update_work_item
|
||||
|
||||
|
|
@ -280,6 +379,20 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'for start and due date widget' do
|
||||
let(:updated_date) { 1.week.from_now.to_date }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
description_widget
|
||||
]
|
||||
|
||||
context 'when due_date is updated' do
|
||||
let(:widget_params) { { start_and_due_date_widget: { due_date: updated_date } } }
|
||||
|
||||
|
|
@ -309,6 +422,18 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
|
||||
let(:widget_params) { { hierarchy_widget: { children: [child_work_item] } } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
title
|
||||
title_html
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
hierarchy_widget
|
||||
]
|
||||
|
||||
it 'updates the children of the work item' do
|
||||
expect do
|
||||
update_work_item
|
||||
|
|
@ -342,6 +467,8 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
context 'when work item validation fails' do
|
||||
let(:opts) { { title: '' } }
|
||||
|
||||
it_behaves_like 'do not publish WorkItems::WorkItemUpdatedEvent event'
|
||||
|
||||
it 'returns validation errors' do
|
||||
expect(update_work_item[:message]).to contain_exactly("Title can't be blank")
|
||||
end
|
||||
|
|
@ -361,6 +488,16 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
|
||||
let(:widget_params) { { milestone_widget: { milestone_id: milestone.id } } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
milestone_id
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
milestone_widget
|
||||
]
|
||||
|
||||
context 'when milestone is updated' do
|
||||
it "triggers 'issuableMilestoneUpdated'" do
|
||||
expect(work_item.milestone).to eq(nil)
|
||||
|
|
@ -392,9 +529,32 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
let_it_be(:user_todo) { create(:todo, target: work_item, user: developer, project: project, state: :pending) }
|
||||
let_it_be(:other_todo) { create(:todo, target: work_item, user: create(:user), project: project, state: :pending) }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
description
|
||||
description_html
|
||||
last_edited_at
|
||||
last_edited_by_id
|
||||
lock_version
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
description_widget
|
||||
]
|
||||
|
||||
context 'when action is mark_as_done' do
|
||||
let(:widget_params) { { current_user_todos_widget: { action: 'mark_as_done' } } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
updated_at
|
||||
updated_by_id
|
||||
],
|
||||
widgets: %w[
|
||||
current_user_todos_widget
|
||||
]
|
||||
|
||||
it 'marks current user todo as done' do
|
||||
expect do
|
||||
update_work_item
|
||||
|
|
@ -429,6 +589,12 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
|
|||
let(:label) { create(:label, project: project) }
|
||||
let(:opts) { { label_ids: [label1.id] } }
|
||||
|
||||
it_behaves_like 'publish WorkItems::WorkItemUpdatedEvent event',
|
||||
attributes: %w[
|
||||
updated_at
|
||||
updated_by_id
|
||||
]
|
||||
|
||||
it 'tracks users updating work item labels' do
|
||||
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).to receive(:track_work_item_labels_changed_action).with(author: current_user)
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,14 @@ module RSpec
|
|||
return if actual.blank? || expected.blank?
|
||||
|
||||
values_match?(actual.keys, expected.keys) &&
|
||||
actual.keys.all? { |key| values_match?(expected[key], actual[key]) }
|
||||
actual.keys.all? do |key|
|
||||
case expected[key]
|
||||
when Array
|
||||
values_match?(expected[key].sort, actual[key].sort)
|
||||
else
|
||||
values_match?(expected[key], actual[key])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def published_events_description
|
||||
|
|
|
|||
Loading…
Reference in New Issue