Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-03-06 15:09:47 +00:00
parent e408c9c787
commit 96dfc10639
90 changed files with 948 additions and 282 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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"

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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"

View File

@ -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'

View File

@ -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

View File

@ -7,4 +7,4 @@ feature_categories:
- seat_cost_management
classes:
- GitlabSubscriptions::UserAddOnAssignment
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
b604c37383d4c1846c57920c6f3388ccf8ce62e52dc99700d26f3cae6c0a61b3

View File

@ -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

View File

@ -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
```

View File

@ -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`. |

View File

@ -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"}

View File

@ -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:

View File

@ -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).

View File

@ -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).

View File

@ -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...

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View 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

View File

@ -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

View File

@ -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?

View File

@ -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"

View File

@ -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

View File

@ -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');

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -32,6 +32,9 @@ spec:
---
variables:
ALLOW_FAILURE: $[[ inputs.allow_failure ]]
"$[[ inputs.job_prefix ]]-build":
stage: $[[ inputs.job_stage ]]
script:

View File

@ -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: [

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 },

View File

@ -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)

View File

@ -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