diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-10 09:11:07 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-10 09:11:07 +0000 |
commit | 16e3c17d3f24314a7a4dfd3c7189701eeb40bf16 (patch) | |
tree | 2da0fa39326060f30b468570b396904bc4ec29b7 | |
parent | ff2b80a5548b308f31acce94069bb3fd2e0480c3 (diff) | |
download | gitlab-ce-16e3c17d3f24314a7a4dfd3c7189701eeb40bf16.tar.gz |
Add latest changes from gitlab-org/gitlab@master
55 files changed, 363 insertions, 183 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index e30d9959fb8..016be4af500 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -123,6 +123,7 @@ - "scripts/review_apps/base-config.yaml" - "scripts/review_apps/review-apps.sh" - "scripts/trigger-build" + - "{,ee/,jh/}{bin,config}/**/*.rb" .ci-qa-patterns: &ci-qa-patterns - ".gitlab-ci.yml" diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue index e8fdf4bb0c2..dd40add6376 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue @@ -56,7 +56,7 @@ export default { const labelLink = h( GlLink, { - class: 'gl-display-flex gl-align-items-center label-item gl-text-black-normal', + class: 'gl-display-flex gl-align-items-center label-item gl-text-body', on: { click: () => { listeners.clickLabel(label); diff --git a/app/controllers/projects/work_items_controller.rb b/app/controllers/projects/work_items_controller.rb index 9d83fcbd26b..1bd2762f277 100644 --- a/app/controllers/projects/work_items_controller.rb +++ b/app/controllers/projects/work_items_controller.rb @@ -8,5 +8,6 @@ class Projects::WorkItemsController < Projects::ApplicationController feature_category :not_owned def index + render_404 unless Feature.enabled?(:work_items, project, default_enabled: :yaml) end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 1cd3e4d0ee8..88562458fba 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -1031,9 +1031,10 @@ module Ci # Consider this object to have a structural integrity problems def doom! - update_columns( - status: :failed, - failure_reason: :data_integrity_failure) + transaction do + update_columns(status: :failed, failure_reason: :data_integrity_failure) + all_queuing_entries.delete_all + end end def degradation_threshold diff --git a/config/metrics/counts_28d/20210216184450_i_code_review_user_create_mr_from_issue_monthly.yml b/config/metrics/counts_28d/20210216184450_i_code_review_user_create_mr_from_issue_monthly.yml index 6f9ef7d5a67..68334ef0c55 100644 --- a/config/metrics/counts_28d/20210216184450_i_code_review_user_create_mr_from_issue_monthly.yml +++ b/config/metrics/counts_28d/20210216184450_i_code_review_user_create_mr_from_issue_monthly.yml @@ -12,7 +12,7 @@ data_source: redis_hll instrumentation_class: RedisHLLMetric options: events: - - i_code_review_user_create_mr + - i_code_review_user_create_mr_from_issue distribution: - ce - ee diff --git a/config/metrics/counts_7d/20210216175146_i_code_review_user_create_mr_comment_weekly.yml b/config/metrics/counts_7d/20210216175146_i_code_review_user_create_mr_comment_weekly.yml index 96b77fd1b8b..0d3fbea4d50 100644 --- a/config/metrics/counts_7d/20210216175146_i_code_review_user_create_mr_comment_weekly.yml +++ b/config/metrics/counts_7d/20210216175146_i_code_review_user_create_mr_comment_weekly.yml @@ -13,7 +13,7 @@ data_source: redis_hll instrumentation_class: RedisHLLMetric options: events: - - i_code_review_user_create_mr + - i_code_review_user_create_mr_comment distribution: - ce - ee diff --git a/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml b/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml index 5cb3291fc3f..bedada35d03 100644 --- a/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml +++ b/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml @@ -15,7 +15,7 @@ data_source: redis_hll instrumentation_class: RedisHLLMetric options: events: - - p_ci_templates_auto_devops + - p_ci_templates_auto_devops_deploy distribution: - ce - ee diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md index fa343f7eb40..05aa026eca1 100644 --- a/doc/administration/geo/setup/database.md +++ b/doc/administration/geo/setup/database.md @@ -521,7 +521,7 @@ For instructions about how to set up Patroni on the primary site, see the #### Configuring Patroni cluster for a Geo secondary site -In a Geo secondary site, the main PostgreSQL database is a read-only replica of the primary site’s PostgreSQL database. +In a Geo secondary site, the main PostgreSQL database is a read-only replica of the primary site's PostgreSQL database. If you are currently using `repmgr` on your Geo primary site, see [these instructions](#migrating-from-repmgr-to-patroni) for migrating from `repmgr` to Patroni. @@ -651,7 +651,7 @@ Refer to your preferred Load Balancer's documentation for further guidance. ##### Step 3. Configure a PgBouncer node on the secondary site A production-ready and highly available configuration requires at least -three Consul nodes, a minimum of one PgBouncer node, but it’s recommended to have +three Consul nodes, a minimum of one PgBouncer node, but it's recommended to have one per database node. An internal load balancer (TCP) is required when there is more than one PgBouncer service nodes. The internal load balancer provides a single endpoint for connecting to the PgBouncer cluster. For more information, diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md index f9ef87dd257..4de48aa3f14 100644 --- a/doc/administration/housekeeping.md +++ b/doc/administration/housekeeping.md @@ -62,6 +62,12 @@ Housekeeping also [removes unreferenced LFS files](../raketasks/cleanup.md#remov from your project on the same schedule as the `git gc` operation, freeing up storage space for your project. +WARNING: +Running `git gc` or `git repack` commands manually in the +[repository folder](repository_storage_types.md#from-project-name-to-hashed-path) +is discouraged. If the created pack files get incorrect access rights (that is, owned by the wrong user) +browsing to the project page might result in `404` and `503` errors. + ## How housekeeping handles pool repositories Housekeeping for pool repositories is handled differently from standard repositories. It is @@ -76,7 +82,7 @@ This is the current call stack by which it is invoked: 1. `ObjectPoolService#fetch` 1. `Gitaly::FetchIntoObjectPoolRequest` -To manually invoke it from a Rails console if needed, you can call +To manually invoke it from a [Rails console](operations/rails_console.md) if needed, you can call `project.pool_repository.object_pool.fetch`. This is a potentially long-running task, though Gitaly times out in about 8 hours. diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md index 56bf711f187..ea780072ca3 100644 --- a/doc/administration/raketasks/check.md +++ b/doc/administration/raketasks/check.md @@ -204,12 +204,20 @@ See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details. The following are solutions to problems you might discover using the Rake tasks documented above. -### Dangling commits +### Dangling objects -`gitlab:git:fsck` can find dangling commits. To fix them, try -[enabling housekeeping](../housekeeping.md). +The `gitlab:git:fsck` task can find dangling objects such as: -If the issue persists, try triggering `gc` via the +```plaintext +dangling blob a12... +dangling commit b34... +dangling tag c56... +dangling tree d78... +``` + +To delete them, try [running housekeeping](../housekeeping.md). + +If the issue persists, try triggering garbage collection via the [Rails Console](../operations/rails_console.md#starting-a-rails-console-session): ```ruby @@ -217,6 +225,13 @@ p = Project.find_by_path("project-name") Repositories::HousekeepingService.new(p, :gc).execute ``` +If the dangling objects are younger than the 2 weeks default grace period, +and you don't want to wait until they expire automatically, run: + +```ruby +Repositories::HousekeepingService.new(p, :prune).execute +``` + ### Delete references to missing remote uploads `gitlab-rake gitlab:uploads:check VERBOSE=1` detects remote objects that do not exist because they were diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index d74da8aa574..0fd597e6a2d 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -2390,7 +2390,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 65d59422da8..f500434d75b 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -2402,7 +2402,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index 0af4dbc8a7f..99dd29c3a83 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -995,7 +995,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index f4ae01c7442..da36968f053 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -2124,7 +2124,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index b262545b27d..b071b48cbd9 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -2413,7 +2413,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 666d18a66fc..4dfe628039a 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -2094,7 +2094,7 @@ The following tables and diagram detail the hybrid environment using the same fo as the normal environment above. First are the components that run in Kubernetes. The recommendation at this time is to -use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +use Google Cloud's Kubernetes Engine (GKE) and associated machine types, but the memory and CPU requirements should translate to most other providers. We hope to update this in the future with further specific cloud provider details. diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md index ae3b58d0764..6d8c256d5aa 100644 --- a/doc/api/job_artifacts.md +++ b/doc/api/job_artifacts.md @@ -20,7 +20,7 @@ GET /projects/:id/jobs/:job_id/artifacts |-------------|----------------|----------|--------------------------------------------------------------------------------------------------------------| | `id` | integer/string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | | `job_id` | integer | yes | ID of a job. | -| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/index.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request using the `PRIVATE-TOKEN` header: @@ -85,7 +85,7 @@ Parameters | `id` | integer/string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | | `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. | | `job` | string | yes | The name of the job. | -| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/index.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request using the `PRIVATE-TOKEN` header: @@ -146,7 +146,7 @@ Parameters | `id` | integer/string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | | `job_id` | integer | yes | The unique job identifier. | | `artifact_path` | string | yes | Path to a file inside the artifacts archive. | -| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/index.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request: @@ -188,7 +188,7 @@ Parameters: | `ref_name` | string | yes | Branch or tag name in repository. `HEAD` or `SHA` references are not supported. | | `artifact_path` | string | yes | Path to a file inside the artifacts archive. | | `job` | string | yes | The name of the job. | -| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/index.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request: diff --git a/doc/api/packages.md b/doc/api/packages.md index 73092e68c82..a75b2e376fa 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -4,7 +4,7 @@ group: Package info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Packages API +# Packages API **(FREE)** This is the API documentation of [GitLab Packages](../administration/packages/index.md). diff --git a/doc/api/pages.md b/doc/api/pages.md index ef6523520de..f81a3c3c72b 100644 --- a/doc/api/pages.md +++ b/doc/api/pages.md @@ -4,7 +4,7 @@ group: Release info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Pages API +# Pages API **(FREE)** Endpoints for managing [GitLab Pages](https://about.gitlab.com/stages-devops-lifecycle/pages/). diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md index 46d92db9853..47a8df3875e 100644 --- a/doc/api/pages_domains.md +++ b/doc/api/pages_domains.md @@ -4,7 +4,7 @@ group: Release info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Pages domains API +# Pages domains API **(FREE)** Endpoints for connecting custom domain(s) and TLS certificates in [GitLab Pages](https://about.gitlab.com/stages-devops-lifecycle/pages/). diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md index 4949bf504fa..b96ee81f673 100644 --- a/doc/api/personal_access_tokens.md +++ b/doc/api/personal_access_tokens.md @@ -4,14 +4,14 @@ group: Compliance info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Personal access tokens API +# Personal access tokens API **(FREE)** You can read more about [personal access tokens](../user/profile/personal_access_tokens.md#personal-access-tokens). ## List personal access tokens -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3. -> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.6. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in GitLab 13.3. +> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) from GitLab Ultimate to GitLab Free in 13.6. Get a list of personal access tokens. @@ -70,8 +70,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a ## Revoke a personal access token -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216004) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3. -> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.6. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216004) in GitLab 13.3. +> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) from GitLab Ultimate to GitLab Free in 13.6. Revoke a personal access token. diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md index 55d368336cb..f3c30a414ea 100644 --- a/doc/api/pipelines.md +++ b/doc/api/pipelines.md @@ -214,7 +214,7 @@ Sample response: ### Get a pipeline's test report summary -> Introduced in [GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65471) +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65471) in GitLab 14.2. NOTE: This API route is part of the [Unit test report](../ci/unit_test_reports.md) feature. diff --git a/doc/api/plan_limits.md b/doc/api/plan_limits.md index c89c7b46d54..52152dd6e14 100644 --- a/doc/api/plan_limits.md +++ b/doc/api/plan_limits.md @@ -4,7 +4,7 @@ group: Access info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Plan limits API **(FREE)** +# Plan limits API **(FREE SELF)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54232) in GitLab 13.10. diff --git a/doc/api/project_aliases.md b/doc/api/project_aliases.md index 1638bb644c2..0d130f6f484 100644 --- a/doc/api/project_aliases.md +++ b/doc/api/project_aliases.md @@ -7,7 +7,7 @@ type: reference, api # Project Aliases API **(PREMIUM SELF)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3264) in GitLab 12.1. All methods require administrator authorization. diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md index 2b4976510bb..bb05e4788d0 100644 --- a/doc/api/project_clusters.md +++ b/doc/api/project_clusters.md @@ -294,7 +294,7 @@ Parameters: | `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes | | `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. | | `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project | -| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM)** | +| `environment_scope` | string | no | The associated environment to the cluster | NOTE: `name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md index e596b25ca22..2dcef40aacb 100644 --- a/doc/api/project_level_variables.md +++ b/doc/api/project_level_variables.md @@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference, api --- -# Project-level Variables API +# Project-level Variables API **(FREE)** ## List project variables diff --git a/doc/api/project_vulnerabilities.md b/doc/api/project_vulnerabilities.md index 2035d17aa3f..7ba359587f6 100644 --- a/doc/api/project_vulnerabilities.md +++ b/doc/api/project_vulnerabilities.md @@ -7,7 +7,7 @@ type: reference, api # Project Vulnerabilities API **(ULTIMATE)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in GitLab 12.6. WARNING: This API is in an alpha stage and considered unstable. diff --git a/doc/api/projects.md b/doc/api/projects.md index a510f05df58..29e3cdf6cbf 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -55,7 +55,7 @@ GET /projects | `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). | | `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, `last_activity_at`, or `similarity` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for admins. `similarity` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332890) in GitLab 14.1) is only available when searching and is limited to projects that the current user is a member of. Default is `created_at`. | | `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. | -| `repository_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). | +| `repository_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in GitLab 11.2). | | `repository_storage` | string | **{dotted-circle}** No | Limit results to projects stored on `repository_storage`. _(admins only)_ | | `search_namespaces` | boolean | **{dotted-circle}** No | Include ancestor namespaces when matching search criteria. Default is `false`. | | `search` | string | **{dotted-circle}** No | Return list of projects matching the search criteria. | @@ -65,7 +65,7 @@ GET /projects | `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. | | `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `topics` attribute. | | `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. | -| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). | +| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in GitLab 11.2). | | `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ | | `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. | | `with_merge_requests_enabled` | boolean | **{dotted-circle}** No | Limit by enabled merge requests feature. | @@ -2428,7 +2428,7 @@ POST /projects/:id/housekeeping ## Push Rules **(PREMIUM)** -### Get project push rules **(PREMIUM)** +### Get project push rules Get the [push rules](../push_rules/push_rules.md#enabling-push-rules) of a project. @@ -2474,7 +2474,7 @@ parameters: } ``` -### Add project push rule **(PREMIUM)** +### Add project push rule Adds a push rule to a specified project. @@ -2486,7 +2486,7 @@ POST /projects/:id/push_rule |-----------------------------------------|----------------|------------------------|-------------| | `author_email_regex` | string | **{dotted-circle}** No | All commit author emails must match this, for example `@my-company.com$`. | | `branch_name_regex` | string | **{dotted-circle}** No | All branch names must match this, for example `(feature|hotfix)\/*`. | -| `commit_committer_check` **(PREMIUM)** | boolean | **{dotted-circle}** No | Users can only push commits to this repository that were committed with one of their own verified emails. | +| `commit_committer_check` | boolean | **{dotted-circle}** No | Users can only push commits to this repository that were committed with one of their own verified emails. | | `commit_message_negative_regex` | string | **{dotted-circle}** No | No commit message is allowed to match this, for example `ssh\:\/\/`. | | `commit_message_regex` | string | **{dotted-circle}** No | All commit messages must match this, for example `Fixed \d+\..*`. | | `deny_delete_tag` | boolean | **{dotted-circle}** No | Deny deleting a tag. | @@ -2495,9 +2495,9 @@ POST /projects/:id/push_rule | `max_file_size` | integer | **{dotted-circle}** No | Maximum file size (MB). | | `member_check` | boolean | **{dotted-circle}** No | Restrict commits by author (email) to existing GitLab users. | | `prevent_secrets` | boolean | **{dotted-circle}** No | GitLab rejects any files that are likely to contain secrets. | -| `reject_unsigned_commits` **(PREMIUM)** | boolean | **{dotted-circle}** No | Reject commit when it's not signed through GPG. | +| `reject_unsigned_commits` | boolean | **{dotted-circle}** No | Reject commit when it's not signed through GPG. | -### Edit project push rule **(PREMIUM)** +### Edit project push rule Edits a push rule for a specified project. @@ -2509,7 +2509,7 @@ PUT /projects/:id/push_rule |-----------------------------------------|----------------|------------------------|-------------| | `author_email_regex` | string | **{dotted-circle}** No | All commit author emails must match this, for example `@my-company.com$`. | | `branch_name_regex` | string | **{dotted-circle}** No | All branch names must match this, for example `(feature|hotfix)\/*`. | -| `commit_committer_check` **(PREMIUM)** | boolean | **{dotted-circle}** No | Users can only push commits to this repository that were committed with one of their own verified emails. | +| `commit_committer_check` | boolean | **{dotted-circle}** No | Users can only push commits to this repository that were committed with one of their own verified emails. | | `commit_message_negative_regex` | string | **{dotted-circle}** No | No commit message is allowed to match this, for example `ssh\:\/\/`. | | `commit_message_regex` | string | **{dotted-circle}** No | All commit messages must match this, for example `Fixed \d+\..*`. | | `deny_delete_tag` | boolean | **{dotted-circle}** No | Deny deleting a tag. | @@ -2518,7 +2518,7 @@ PUT /projects/:id/push_rule | `max_file_size` | integer | **{dotted-circle}** No | Maximum file size (MB). | | `member_check` | boolean | **{dotted-circle}** No | Restrict commits by author (email) to existing GitLab users. | | `prevent_secrets` | boolean | **{dotted-circle}** No | GitLab rejects any files that are likely to contain secrets. | -| `reject_unsigned_commits` **(PREMIUM)** | boolean | **{dotted-circle}** No | Reject commits when they are not GPG signed. | +| `reject_unsigned_commits` | boolean | **{dotted-circle}** No | Reject commits when they are not GPG signed. | ### Delete project push rule diff --git a/doc/api/protected_environments.md b/doc/api/protected_environments.md index 9a64e676ad9..82bb1e55e77 100644 --- a/doc/api/protected_environments.md +++ b/doc/api/protected_environments.md @@ -7,7 +7,7 @@ type: concepts, howto # Protected environments API **(PREMIUM)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30595) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.8. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30595) in GitLab 12.8. ## Valid access levels diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md index b98dfa1ff69..70c22d566e5 100644 --- a/doc/ci/jobs/ci_job_token.md +++ b/doc/ci/jobs/ci_job_token.md @@ -25,7 +25,7 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints - [Terraform plan](../../user/infrastructure/index.md). The token has the same permissions to access the API as the user that triggers the -pipeline. Therefore, this user must be assigned to [a role that has the required privileges](../../user/permissions.md). +pipeline. Therefore, this user must be assigned to [a role that has the required privileges](../../user/permissions.md#gitlab-cicd-permissions). The token is valid only while the pipeline job runs. After the job finishes, you can't use the token anymore. @@ -123,3 +123,43 @@ To disable it: ```ruby Feature.disable(:ci_scoped_job_token) ``` + +## Trigger a multi-project pipeline by using a CI job token + +> `CI_JOB_TOKEN` for multi-project pipelines was [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) from GitLab Premium to GitLab Free in 12.4. + +You can use the `CI_JOB_TOKEN` to trigger [multi-project pipelines](../pipelines/multi_project_pipelines.md) +from a CI/CD job. A pipeline triggered this way creates a dependent pipeline relation +that is visible on the [pipeline graph](../pipelines/multi_project_pipelines.md#multi-project-pipeline-visualization). + +For example: + +```yaml +trigger_pipeline: + stage: deploy + script: + - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline" + rules: + - if: $CI_COMMIT_TAG +``` + +If you use the `CI_PIPELINE_SOURCE` [predefined CI/CD variable](../variables/predefined_variables.md) +in a pipeline triggered this way, [the value is `pipeline` (not `triggered`)](../triggers/index.md#authentication-tokens). + +## Download an artifact from a different pipeline **(PREMIUM)** + +> `CI_JOB_TOKEN` for artifacts download with the API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346) in GitLab 9.5. + +You can use the `CI_JOB_TOKEN` to access artifacts from a job created by a previous +pipeline. You must specify which job you want to retrieve the artifacts from: + +```yaml +build_submodule: + stage: test + script: + - apt update && apt install -y unzip + - curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/main/download?job=test&job_token=$CI_JOB_TOKEN" + - unzip artifacts.zip +``` + +Read more about the [jobs artifacts API](../../api/job_artifacts.md#download-the-artifacts-archive). diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md index 014032c7d12..ad2bbbc883c 100644 --- a/doc/ci/jobs/job_control.md +++ b/doc/ci/jobs/job_control.md @@ -225,7 +225,7 @@ check the value of the `$CI_PIPELINE_SOURCE` variable: | `pipeline` | For [multi-project pipelines](../pipelines/multi_project_pipelines.md) created by [using the API with `CI_JOB_TOKEN`](../pipelines/multi_project_pipelines.md#create-multi-project-pipelines-by-using-the-api), or the [`trigger`](../yaml/index.md#trigger) keyword. | | `push` | For pipelines triggered by a `git push` event, including for branches and tags. | | `schedule` | For [scheduled pipelines](../pipelines/schedules.md). | -| `trigger` | For pipelines created by using a [trigger token](../triggers/index.md#trigger-token). | +| `trigger` | For pipelines created by using a [trigger token](../triggers/index.md#authentication-tokens). | | `web` | For pipelines created by using **Run pipeline** button in the GitLab UI, from the project's **CI/CD > Pipelines** section. | | `webide` | For pipelines created by using the [WebIDE](../../user/project/web_ide/index.md). | @@ -335,7 +335,7 @@ to control when to add jobs to pipelines. In the following example, `job` runs only for: - Git tags -- [Triggers](../triggers/index.md#trigger-token) +- [Triggers](../triggers/index.md#authentication-tokens) - [Scheduled pipelines](../pipelines/schedules.md) ```yaml diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md index 3007d91d1b4..d31ddcf736e 100644 --- a/doc/ci/pipelines/multi_project_pipelines.md +++ b/doc/ci/pipelines/multi_project_pipelines.md @@ -273,7 +273,7 @@ upstream_bridge: > [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) to GitLab Free in 12.4. -When you use the [`CI_JOB_TOKEN` to trigger pipelines](../triggers/index.md#ci-job-token), +When you use the [`CI_JOB_TOKEN` to trigger pipelines](../jobs/ci_job_token.md), GitLab recognizes the source of the job token. The pipelines become related, so you can visualize their relationships on pipeline graphs. diff --git a/doc/ci/triggers/index.md b/doc/ci/triggers/index.md index 353e0b6e643..a2dd4ac91d5 100644 --- a/doc/ci/triggers/index.md +++ b/doc/ci/triggers/index.md @@ -14,8 +14,8 @@ tag) with an API call. The following methods of authentication are supported: -- [Trigger token](#trigger-token) -- [CI job token](#ci-job-token) +- Trigger tokens: A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger). +- [CI job tokens](../jobs/ci_job_token.md). If using the `$CI_PIPELINE_SOURCE` [predefined CI/CD variable](../variables/predefined_variables.md) to limit which jobs run in a pipeline, the value could be either `pipeline` or `trigger`, @@ -28,71 +28,6 @@ depending on which trigger method is used. This also applies when using the `pipelines` or `triggers` keywords with the legacy [`only/except` basic syntax](../yaml/index.md#only--except). -### Trigger token - -A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger). - -WARNING: -Passing plain text tokens in public projects is a security issue. Potential -attackers can impersonate the user that exposed their trigger token publicly in -their `.gitlab-ci.yml` file. Use [CI/CD variables](../variables/index.md) -to protect trigger tokens. - -### CI job token - -You can use the `CI_JOB_TOKEN` [CI/CD variable](../variables/index.md#predefined-cicd-variables) (used to authenticate -with the [GitLab Container Registry](../../user/packages/container_registry/index.md)) in the following cases. - -#### When used with multi-project pipelines - -> - Use of `CI_JOB_TOKEN` for multi-project pipelines was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2017) in GitLab Premium 9.3. -> - Use of `CI_JOB_TOKEN` for multi-project pipelines was [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) from GitLab Premium to GitLab Free in 12.4. - -This way of triggering can only be used when invoked inside `.gitlab-ci.yml`, -and it creates a dependent pipeline relation visible on the -[pipeline graph](../pipelines/multi_project_pipelines.md). For example: - -```yaml -trigger_pipeline: - stage: deploy - script: - - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline" - rules: - - if: $CI_COMMIT_TAG -``` - -Pipelines triggered that way also expose a special variable: -`CI_PIPELINE_SOURCE=pipeline`. - -Read more about the [pipelines trigger API](../../api/pipeline_triggers.md). - -#### When a pipeline depends on the artifacts of another pipeline **(PREMIUM)** - -> The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2346) in GitLab 9.5. - -With the introduction of dependencies between different projects, one of -them may need to access artifacts created by a previous one. This process -must be granted for authorized accesses, and it can be done using the -`CI_JOB_TOKEN` variable that identifies a specific job. For example: - -```yaml -build_submodule: - image: debian - stage: test - script: - - apt update && apt install -y unzip - - curl --location --output artifacts.zip "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/main/download?job=test&job_token=$CI_JOB_TOKEN" - - unzip artifacts.zip - rules: - - if: $CI_COMMIT_TAG -``` - -This allows you to use that for multi-project pipelines and download artifacts -from any project to which you have access as this follows the same principles -with the [permission model](../../user/permissions.md#job-permissions). - -Read more about the [jobs API](../../api/job_artifacts.md#download-the-artifacts-archive). - ## Adding a new trigger Go to your @@ -106,6 +41,12 @@ overview of the time the triggers were last used. ![Triggers page overview](img/triggers_page.png) +WARNING: +Passing plain text tokens in public projects is a security issue. Potential +attackers can impersonate the user that exposed their trigger token publicly in +their `.gitlab-ci.yml` file. Use [CI/CD variables](../variables/index.md) +to protect trigger tokens. + ## Revoking a trigger You can revoke a trigger any time by going at your project's diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 958c347410a..b6f5e3988c9 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -1367,7 +1367,7 @@ pipeline based on branch names or pipeline types. | `pushes` | For pipelines triggered by a `git push` event, including for branches and tags. | | `schedules` | For [scheduled pipelines](../pipelines/schedules.md). | | `tags` | When the Git reference for a pipeline is a tag. | - | `triggers` | For pipelines created by using a [trigger token](../triggers/index.md#trigger-token). | + | `triggers` | For pipelines created by using a [trigger token](../triggers/index.md#authentication-tokens). | | `web` | For pipelines created by using **Run pipeline** button in the GitLab UI, from the project's **CI/CD > Pipelines** section. | **Example of `only:refs` and `except:refs`**: diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 28d02524dc7..2cba68b90e5 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -440,7 +440,7 @@ WARNING: subsequent revisions for anything that would be spotted after that. - For merge requests that have had [Squash and merge](../user/project/merge_requests/squash_and_merge.md#squash-and-merge) set, - the squashed commit’s default commit message is taken from the merge request title. + the squashed commit's default commit message is taken from the merge request title. You're encouraged to [select a commit with a more informative commit message](../user/project/merge_requests/squash_and_merge.md) before merging. Thanks to **Pipeline for Merged Results**, authors no longer have to rebase their diff --git a/doc/development/database_review.md b/doc/development/database_review.md index 2746d9f6582..4698478d309 100644 --- a/doc/development/database_review.md +++ b/doc/development/database_review.md @@ -128,7 +128,9 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac - Write the raw SQL in the MR description. Preferably formatted nicely with [pgFormatter](https://sqlformat.darold.net) or [paste.depesz.com](https://paste.depesz.com) and using regular quotes + <!-- vale off --> (for example, `"projects"."id"`) and avoiding smart quotes (for example, `“projects”.“id”`). + <!-- vale on --> - In case of queries generated dynamically by using parameters, there should be one raw SQL query for each variation. For example, a finder for issues that may take as a parameter an optional filter on projects, diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index d9b908b3823..83755848706 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -474,6 +474,7 @@ Follow these guidelines for punctuation: | Use serial commas (Oxford commas) before the final **and** or **or** in a list of three or more items. (Tested in [`OxfordComma.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/OxfordComma.yml).) | You can create new issues, merge requests, and milestones. | | Always add a space before and after dashes when using it in a sentence (for replacing a comma, for example). | You should try this - or not. | | When a colon is part of a sentence, always use lowercase after the colon. | Linked issues: a way to create a relationship between issues. | +| Do not use typographer's quotes. Use straight quotes instead. (Tested in [`NonStandardQuotes.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/NonStandardQuotes.yml).) | "It's the questions we can't answer that teach us the most"---Patrick Rothfuss | <!-- vale gitlab.Repetition = YES --> @@ -765,6 +766,7 @@ Valid for Markdown content only, not for front matter entries: For other punctuation rules, refer to the [Pajamas Design System Punctuation section](https://design.gitlab.com/content/punctuation/). +This is overridden by the [documentation-specific punctuation rules](#punctuation). ## Headings diff --git a/doc/development/img/elasticsearch_architecture.svg b/doc/development/img/elasticsearch_architecture.svg index 2f38f9b04ee..516214c8b8e 100644 --- a/doc/development/img/elasticsearch_architecture.svg +++ b/doc/development/img/elasticsearch_architecture.svg @@ -1 +1 @@ -<svg version="1.2" width="210mm" height="297mm" viewBox="0 0 21000 29700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><defs class="ClipPathGroup"><clipPath id="a" clipPathUnits="userSpaceOnUse"><path d="M0 0h21000v29700H0z"/></clipPath></defs><g class="SlideGroup"><g class="Slide" clip-path="url(#a)"><g class="Page"><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 5575h3051v1651H1975z"/><path fill="#FFF" d="M3500 7200H2000V5600h3000v1600H3500z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 7200H2000V5600h3000v1600H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2778" y="6311"><tspan>Snippet</tspan></tspan><tspan class="TextPosition" x="2099" y="6785"><tspan>(ActiveRecord)</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1475 3975h4051v3551H1475z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 7500H1500V4000h4000v3500H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="1788" y="5048"><tspan>ApplicationSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M5975 4675h8051v701H5975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M6000 5350h4000v-650h4000"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M5975 5325h8051v1101H5975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M6000 5350h4000v1050h4000"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1075 2875h4951v4951H1075z"/><path fill="none" stroke="#F33" stroke-width="50" d="M3550 7800H1100V2900h4900v4900H3550z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="1946" y="3514"><tspan fill="#C9211E">SnippetsSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 12175h3051v1651H1975z"/><path fill="#FFF" d="M3500 13800H2000v-1600h3000v1600H3500z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 13800H2000v-1600h3000v1600H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2778" y="12911"><tspan>Snippet</tspan></tspan><tspan class="TextPosition" x="2099" y="13385"><tspan>(ActiveRecord)</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1075 10775h4951v3251H1075z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000H1100v-3200h4900v3200H3550z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2511" y="11461"><tspan>Application</tspan></tspan><tspan class="TextPosition" x="1933" y="11935"><tspan>VersionedSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M3525 13975h4501v7451H3525z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000v7400h4450"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 14075h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 14900h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14720" y="14648"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 13075h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 15200h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13799" y="13731"><tspan fill="#C9211E">V12p1::SnippetClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M7975 14575h3051v1851H7975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M9500 16400H8000v-1800h3000v1800H9500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="8277" y="15411"><tspan>MultiVersion-</tspan></tspan><tspan class="TextPosition" x="8429" y="15885"><tspan>ClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 16875h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 17700h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14720" y="17448"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 15875h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 18000h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13799" y="16531"><tspan fill="#C9211E">V12p2::SnippetClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 14125h2451v1401h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 15500h1463v-1350h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 15475h2451v1501h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 15500h1463v1450h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M3525 13975h4501v1551H3525z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000v1500h4450"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 19975h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 20800h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14445" y="20548"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 18975h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 21100h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13505" y="19631"><tspan fill="#C9211E">V12p1::SnippetInstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M7975 20275h3051v2251H7975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M9500 22500H8000v-2200h3000v2200H9500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="8277" y="21311"><tspan>MultiVersion-</tspan></tspan><tspan class="TextPosition" x="8154" y="21785"><tspan>InstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 22775h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 23600h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14445" y="23348"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 21775h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 23900h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13505" y="22431"><tspan fill="#C9211E">V12p2::SnippetInstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 20025h2451v1401h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 21400h1463v-1350h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 21375h2451v1501h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 21400h1463v1450h937"/></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M900 1600h10697v879H900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="564" font-weight="400"><tspan class="TextPosition" x="1150" y="2233"><tspan>Standard elasticsearch-rails setup</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M900 9300h7683v879H900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="564" font-weight="400"><tspan class="TextPosition" x="1150" y="9933"><tspan>GitLab multi-indices setup</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M3400 21300h4821v1197H3400z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="4250" y="21840"><tspan fill="gray">(instance method)</tspan></tspan><tspan class="TextPosition" x="3651" y="22264"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M3380 15400h4821v1197H3380z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="4512" y="15940"><tspan fill="gray">(class method)</tspan></tspan><tspan class="TextPosition" x="3631" y="16364"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M9000 3500h4821v1197H9000z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="10132" y="4040"><tspan fill="gray">(class method)</tspan></tspan><tspan class="TextPosition" x="9251" y="4464"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M9000 6400h4821v1197H9000z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="9850" y="6940"><tspan fill="gray">(instance method)</tspan></tspan><tspan class="TextPosition" x="9251" y="7364"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 25175h2051v851H1975z"/><path fill="none" stroke="#999" stroke-width="50" d="M3000 26000H2000v-800h2000v800H3000z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2634" y="25748"><tspan fill="gray">Foo</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4400 25200h7101v726H4400z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="4650" y="25710"><tspan>elasticsearch-rails’ internal class</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4400 26400h8601v1200H4400z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="4650" y="26910"><tspan>where model-specific logic is</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 26275h2051v851H1975z"/><path fill="none" stroke="#F33" stroke-width="50" d="M3000 27100H2000v-800h2000v800H3000z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="2613" y="26848"><tspan fill="#C9211E">Foo</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4900 17289h5901v2312H4900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="370" font-weight="400"><tspan class="TextPosition" x="7236" y="17748"><tspan fill="gray">Write operations like </tspan></tspan><tspan class="TextPosition" x="5323" y="18159"><tspan fill="gray">indexing/updating are forwarded </tspan></tspan><tspan class="TextPosition" x="8024" y="18570"><tspan fill="gray">to all instances.</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="370" font-weight="400"><tspan class="TextPosition" x="5501" y="18981"><tspan fill="gray">Read operations are forwarded </tspan></tspan><tspan class="TextPosition" x="7126" y="19392"><tspan fill="gray">to specified instance.</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10785 15769h1422v2691h-1422z"/><path fill="none" stroke="#999" stroke-width="30" d="M10800 18444c1429 0 934-1618 1119-2337"/><path fill="#999" d="M12206 15769l-460 293 267 217 193-510z"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10785 18429h1528v2862h-1528z"/><path fill="none" stroke="#999" stroke-width="30" d="M10800 18444c1509 0 970 1782 1200 2526"/><path fill="#999" d="M12312 21290l-227-496-252 235 479 261z"/></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M1800 24000h7101v807H1800z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="494" font-weight="700"><tspan class="TextPosition" x="2050" y="24574"><tspan>Legend</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13975 4275h5085v851h-5085z"/><path fill="none" stroke="#999" stroke-width="50" d="M16517 5100h-2517v-800h5034v800h-2517z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14737" y="4848"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13975 5975h5085v851h-5085z"/><path fill="none" stroke="#999" stroke-width="50" d="M16517 6800h-2517v-800h5034v800h-2517z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14462" y="6548"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g></g></g></g></svg>
\ No newline at end of file +<svg version="1.2" width="210mm" height="297mm" viewBox="0 0 21000 29700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><defs class="ClipPathGroup"><clipPath id="a" clipPathUnits="userSpaceOnUse"><path d="M0 0h21000v29700H0z"/></clipPath></defs><g class="SlideGroup"><g class="Slide" clip-path="url(#a)"><g class="Page"><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 5575h3051v1651H1975z"/><path fill="#FFF" d="M3500 7200H2000V5600h3000v1600H3500z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 7200H2000V5600h3000v1600H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2778" y="6311"><tspan>Snippet</tspan></tspan><tspan class="TextPosition" x="2099" y="6785"><tspan>(ActiveRecord)</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1475 3975h4051v3551H1475z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 7500H1500V4000h4000v3500H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="1788" y="5048"><tspan>ApplicationSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M5975 4675h8051v701H5975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M6000 5350h4000v-650h4000"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M5975 5325h8051v1101H5975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M6000 5350h4000v1050h4000"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1075 2875h4951v4951H1075z"/><path fill="none" stroke="#F33" stroke-width="50" d="M3550 7800H1100V2900h4900v4900H3550z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="1946" y="3514"><tspan fill="#C9211E">SnippetsSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 12175h3051v1651H1975z"/><path fill="#FFF" d="M3500 13800H2000v-1600h3000v1600H3500z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3500 13800H2000v-1600h3000v1600H3500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2778" y="12911"><tspan>Snippet</tspan></tspan><tspan class="TextPosition" x="2099" y="13385"><tspan>(ActiveRecord)</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1075 10775h4951v3251H1075z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000H1100v-3200h4900v3200H3550z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2511" y="11461"><tspan>Application</tspan></tspan><tspan class="TextPosition" x="1933" y="11935"><tspan>VersionedSearch</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M3525 13975h4501v7451H3525z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000v7400h4450"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 14075h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 14900h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14720" y="14648"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 13075h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 15200h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13799" y="13731"><tspan fill="#C9211E">V12p1::SnippetClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M7975 14575h3051v1851H7975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M9500 16400H8000v-1800h3000v1800H9500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="8277" y="15411"><tspan>MultiVersion-</tspan></tspan><tspan class="TextPosition" x="8429" y="15885"><tspan>ClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 16875h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 17700h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14720" y="17448"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 15875h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 18000h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13799" y="16531"><tspan fill="#C9211E">V12p2::SnippetClassProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 14125h2451v1401h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 15500h1463v-1350h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 15475h2451v1501h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 15500h1463v1450h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M3525 13975h4501v1551H3525z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M3550 14000v1500h4450"/></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 19975h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 20800h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14445" y="20548"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 18975h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 21100h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13505" y="19631"><tspan fill="#C9211E">V12p1::SnippetInstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M7975 20275h3051v2251H7975z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M9500 22500H8000v-2200h3000v2200H9500z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="8277" y="21311"><tspan>MultiVersion-</tspan></tspan><tspan class="TextPosition" x="8154" y="21785"><tspan>InstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M14008 22775h4985v851h-4985z"/><path fill="none" stroke="#999" stroke-width="50" d="M16500 23600h-2467v-800h4934v800h-2467z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14445" y="23348"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13375 21775h6251v2151h-6251z"/><path fill="none" stroke="#F33" stroke-width="50" d="M16500 23900h-3100v-2100h6200v2100h-3100z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="13505" y="22431"><tspan fill="#C9211E">V12p2::SnippetInstanceProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 20025h2451v1401h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 21400h1463v-1350h937"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10975 21375h2451v1501h-2451z"/><path fill="none" stroke="#3465A4" stroke-width="50" d="M11000 21400h1463v1450h937"/></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M900 1600h10697v879H900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="564" font-weight="400"><tspan class="TextPosition" x="1150" y="2233"><tspan>Standard elasticsearch-rails setup</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M900 9300h7683v879H900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="564" font-weight="400"><tspan class="TextPosition" x="1150" y="9933"><tspan>GitLab multi-indices setup</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M3400 21300h4821v1197H3400z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="4250" y="21840"><tspan fill="gray">(instance method)</tspan></tspan><tspan class="TextPosition" x="3651" y="22264"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M3380 15400h4821v1197H3380z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="4512" y="15940"><tspan fill="gray">(class method)</tspan></tspan><tspan class="TextPosition" x="3631" y="16364"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M9000 3500h4821v1197H9000z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="10132" y="4040"><tspan fill="gray">(class method)</tspan></tspan><tspan class="TextPosition" x="9251" y="4464"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M9000 6400h4821v1197H9000z"/><text class="TextShape"><tspan class="TextParagraph" font-size="388" font-weight="400"><tspan class="TextPosition" x="9850" y="6940"><tspan fill="gray">(instance method)</tspan></tspan><tspan class="TextPosition" x="9251" y="7364"><tspan font-family="Courier" font-size="423">__elasticsearch__</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 25175h2051v851H1975z"/><path fill="none" stroke="#999" stroke-width="50" d="M3000 26000H2000v-800h2000v800H3000z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="2634" y="25748"><tspan fill="gray">Foo</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4400 25200h7101v726H4400z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="4650" y="25710"><tspan>elasticsearch-rails' internal class</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4400 26400h8601v1200H4400z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="4650" y="26910"><tspan>where model-specific logic is</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M1975 26275h2051v851H1975z"/><path fill="none" stroke="#F33" stroke-width="50" d="M3000 27100H2000v-800h2000v800H3000z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="700"><tspan class="TextPosition" x="2613" y="26848"><tspan fill="#C9211E">Foo</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M4900 17289h5901v2312H4900z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="370" font-weight="400"><tspan class="TextPosition" x="7236" y="17748"><tspan fill="gray">Write operations like </tspan></tspan><tspan class="TextPosition" x="5323" y="18159"><tspan fill="gray">indexing/updating are forwarded </tspan></tspan><tspan class="TextPosition" x="8024" y="18570"><tspan fill="gray">to all instances.</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="370" font-weight="400"><tspan class="TextPosition" x="5501" y="18981"><tspan fill="gray">Read operations are forwarded </tspan></tspan><tspan class="TextPosition" x="7126" y="19392"><tspan fill="gray">to specified instance.</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10785 15769h1422v2691h-1422z"/><path fill="none" stroke="#999" stroke-width="30" d="M10800 18444c1429 0 934-1618 1119-2337"/><path fill="#999" d="M12206 15769l-460 293 267 217 193-510z"/></g><g class="com.sun.star.drawing.ConnectorShape"><path class="BoundingBox" fill="none" d="M10785 18429h1528v2862h-1528z"/><path fill="none" stroke="#999" stroke-width="30" d="M10800 18444c1509 0 970 1782 1200 2526"/><path fill="#999" d="M12312 21290l-227-496-252 235 479 261z"/></g><g class="com.sun.star.drawing.TextShape"><path class="BoundingBox" fill="none" d="M1800 24000h7101v807H1800z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="494" font-weight="700"><tspan class="TextPosition" x="2050" y="24574"><tspan>Legend</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13975 4275h5085v851h-5085z"/><path fill="none" stroke="#999" stroke-width="50" d="M16517 5100h-2517v-800h5034v800h-2517z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14737" y="4848"><tspan fill="gray">ClassMethodProxy</tspan></tspan></tspan></text></g><g class="com.sun.star.drawing.CustomShape"><path class="BoundingBox" fill="none" d="M13975 5975h5085v851h-5085z"/><path fill="none" stroke="#999" stroke-width="50" d="M16517 6800h-2517v-800h5034v800h-2517z"/><text class="TextShape"><tspan class="TextParagraph" font-family="Arial, sans-serif" font-size="423" font-weight="400"><tspan class="TextPosition" x="14462" y="6548"><tspan fill="gray">InstanceMethodProxy</tspan></tspan></tspan></text></g></g></g></g></svg>
\ No newline at end of file diff --git a/doc/user/application_security/dast/browser_based.md b/doc/user/application_security/dast/browser_based.md index e8fbc17327c..5094ccd2196 100644 --- a/doc/user/application_security/dast/browser_based.md +++ b/doc/user/application_security/dast/browser_based.md @@ -19,7 +19,7 @@ The browser-based crawler works by loading the target application into a special such as clicking on a link or filling in a form. For each action found, the crawler will execute it, take a new snapshot and determine what in the page changed from the previous snapshot. Crawling continues by taking more snapshots and finding subsequent actions. -The benefit of crawling by following user actions in a browser is that the crawler can interact with the target application much like a real user would, identifying complex flows that traditional web crawlers don’t understand. This results in better coverage of the website. +The benefit of crawling by following user actions in a browser is that the crawler can interact with the target application much like a real user would, identifying complex flows that traditional web crawlers don't understand. This results in better coverage of the website. Using the browser-based crawler should provide greater coverage for most web applications, compared with the current DAST AJAX crawler. The new crawler replaces the AJAX crawler and is specifically designed to maximize crawl coverage in modern web applications. While both crawlers are currently used in conjunction with the existing DAST scanner, the combination of the browser-based crawler with the current DAST scanner is much more effective at finding and testing every page in an application. diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md index 5890267d629..edfd0333d54 100644 --- a/doc/user/application_security/dependency_list/index.md +++ b/doc/user/application_security/dependency_list/index.md @@ -49,7 +49,7 @@ can also be sorted by name or by the packager that installed them. If a dependency has known vulnerabilities, view them by clicking the arrow next to the dependency's name or the badge that indicates how many known vulnerabilities exist. For each vulnerability, its severity and description appears below it. To view more details of a vulnerability, -select the vulnerability’s description. The [vulnerability's details](../vulnerabilities) page is opened. +select the vulnerability's description. The [vulnerability's details](../vulnerabilities) page is opened. ### Dependency paths @@ -78,8 +78,8 @@ You can download your project's full list of dependencies and their details in ### In the UI -You can download your project’s list of dependencies and their details in JSON format by selecting the **Export** button. Note that the dependency list only shows the results of the last successful pipeline to run on the default branch. +You can download your project's list of dependencies and their details in JSON format by selecting the **Export** button. Note that the dependency list only shows the results of the last successful pipeline to run on the default branch. ### Using the API -You can download your project’s list of dependencies [using the API](../../../api/dependencies.md#list-project-dependencies). Note this only provides the dependencies identified by the gemnasium family of analyzers and [not any other of the GitLab dependency analyzers](../dependency_scanning/analyzers.md). +You can download your project's list of dependencies [using the API](../../../api/dependencies.md#list-project-dependencies). Note this only provides the dependencies identified by the gemnasium family of analyzers and [not any other of the GitLab dependency analyzers](../dependency_scanning/analyzers.md). diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index 7f81771a203..baec5678b25 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -271,7 +271,7 @@ under your project's settings: ## DAST On-Demand Scans -If you don’t want scans running in your normal DevOps process you can use on-demand scans instead. For more details, see [on-demand scans](dast/index.md#on-demand-scans). This feature is only available for DAST. If you run an on-demand scan against the default branch, it is reported as a "successful pipeline" and these results are included in the security dashboard and vulnerability report. +If you don't want scans running in your normal DevOps process you can use on-demand scans instead. For more details, see [on-demand scans](dast/index.md#on-demand-scans). This feature is only available for DAST. If you run an on-demand scan against the default branch, it is reported as a "successful pipeline" and these results are included in the security dashboard and vulnerability report. ## Security report validation diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index af4d32c703b..1db2165cd5d 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -154,7 +154,7 @@ To use CI/CD to authenticate, you can use: docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY ``` -- A [CI job token](../../../ci/triggers/index.md#ci-job-token). +- A [CI job token](../../../ci/jobs/ci_job_token.md). ```shell docker login -u $CI_JOB_USER -p $CI_JOB_TOKEN $CI_REGISTRY diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index 091bbcf81dc..14e6f4dad3a 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -524,7 +524,7 @@ To avoid the time sync issue, enable time synchronization in the device that gen 1. Go to Settings. 1. Select General. 1. Select Date & Time. - 1. Enable Set Automatically. If it’s already enabled, disable it, wait a few seconds, and re-enable. + 1. Enable Set Automatically. If it's already enabled, disable it, wait a few seconds, and re-enable. <!-- ## Troubleshooting diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index 1e1fb032d8d..b0e53ac3794 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -126,6 +126,10 @@ module API expose :keep_latest_artifacts_available?, as: :keep_latest_artifact # rubocop: disable CodeReuse/ActiveRecord + def self.preload_resource(project) + ActiveRecord::Associations::Preloader.new.preload(project, project_group_links: { group: :route }) + end + def self.preload_relation(projects_relation, options = {}) # Preloading topics, should be done with using only `:topics`, # as `:topics` are defined as: `has_many :topics, through: :taggings` diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 28bcb382ecf..a92d904be84 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -152,6 +152,12 @@ module API ProjectsFinder.new(current_user: current_user, params: project_params).execute end + def present_project(project, options = {}) + options[:with].preload_resource(project) if options[:with].respond_to?(:preload_resource) + + present project, options + end + def present_projects(projects, options = {}) verify_statistics_order_by_projects! @@ -264,9 +270,9 @@ module API project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? - present project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, project), - current_user: current_user + present_project project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, project), + current_user: current_user else if project.errors[:limit_reached].present? error!(project.errors[:limit_reached], 403) @@ -301,9 +307,9 @@ module API project = ::Projects::CreateService.new(user, attrs).execute if project.saved? - present project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, project), - current_user: current_user + present_project project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, project), + current_user: current_user else render_validation_error!(project) end @@ -336,7 +342,7 @@ module API project, options = with_custom_attributes(user_project, options) - present project, options + present_project project, options end desc 'Fork new project for the current user or provided namespace.' do @@ -376,9 +382,11 @@ module API if forked_project.errors.any? conflict!(forked_project.errors.messages) else - present forked_project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, forked_project), - current_user: current_user + present_project forked_project, { + with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, forked_project), + current_user: current_user + } end end @@ -427,9 +435,9 @@ module API result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute if result[:status] == :success - present user_project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, user_project), - current_user: current_user + present_project user_project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, user_project), + current_user: current_user else render_validation_error!(user_project) end @@ -443,7 +451,7 @@ module API ::Projects::UpdateService.new(user_project, current_user, archived: true).execute - present user_project, with: Entities::Project, current_user: current_user + present_project user_project, with: Entities::Project, current_user: current_user end desc 'Unarchive a project' do @@ -454,7 +462,7 @@ module API ::Projects::UpdateService.new(user_project, current_user, archived: false).execute - present user_project, with: Entities::Project, current_user: current_user + present_project user_project, with: Entities::Project, current_user: current_user end desc 'Star a project' do @@ -467,7 +475,7 @@ module API current_user.toggle_star(user_project) user_project.reset - present user_project, with: Entities::Project, current_user: current_user + present_project user_project, with: Entities::Project, current_user: current_user end end @@ -479,7 +487,7 @@ module API current_user.toggle_star(user_project) user_project.reset - present user_project, with: Entities::Project, current_user: current_user + present_project user_project, with: Entities::Project, current_user: current_user else not_modified! end @@ -528,7 +536,7 @@ module API result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project) if result - present user_project.reset, with: Entities::Project, current_user: current_user + present_project user_project.reset, with: Entities::Project, current_user: current_user else render_api_error!("Project already forked", 409) if user_project.forked? end @@ -698,7 +706,7 @@ module API result = ::Projects::TransferService.new(user_project, current_user).execute(namespace) if result - present user_project, with: Entities::Project, current_user: current_user + present_project user_project, with: Entities::Project, current_user: current_user else render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400) end diff --git a/lib/gitlab/ci/variables/collection.rb b/lib/gitlab/ci/variables/collection.rb index ef9ba1b73c7..73d27399680 100644 --- a/lib/gitlab/ci/variables/collection.rb +++ b/lib/gitlab/ci/variables/collection.rb @@ -10,7 +10,7 @@ module Gitlab def initialize(variables = [], errors = nil) @variables = [] - @variables_by_key = {} + @variables_by_key = Hash.new { |h, k| h[k] = [] } @errors = errors variables.each { |variable| self.append(variable) } @@ -19,7 +19,7 @@ module Gitlab def append(resource) item = Collection::Item.fabricate(resource) @variables.append(item) - @variables_by_key[item[:key]] = item + @variables_by_key[item[:key]] << item self end @@ -46,7 +46,12 @@ module Gitlab end def [](key) - @variables_by_key[key] + all(key)&.last + end + + def all(key) + vars = @variables_by_key[key] + vars unless vars.empty? end def size @@ -72,7 +77,7 @@ module Gitlab match = Regexp.last_match if match[:key] # we matched variable - if variable = @variables_by_key[match[:key]] + if variable = self[match[:key]] variable.value elsif keep_undefined match[0] diff --git a/lib/gitlab/ci/variables/collection/sort.rb b/lib/gitlab/ci/variables/collection/sort.rb index 90a929b8a07..62637825c15 100644 --- a/lib/gitlab/ci/variables/collection/sort.rb +++ b/lib/gitlab/ci/variables/collection/sort.rb @@ -42,7 +42,7 @@ module Gitlab depends_on = var_item.depends_on return unless depends_on - depends_on.filter_map { |var_ref_name| @collection[var_ref_name] }.each(&block) + depends_on.filter_map { |var_ref_name| @collection.all(var_ref_name) }.flatten.each(&block) end end end diff --git a/lib/gitlab/graphql/loaders/full_path_model_loader.rb b/lib/gitlab/graphql/loaders/full_path_model_loader.rb index 26c1ce64a83..7f9013c6e4c 100644 --- a/lib/gitlab/graphql/loaders/full_path_model_loader.rb +++ b/lib/gitlab/graphql/loaders/full_path_model_loader.rb @@ -5,19 +5,20 @@ module Gitlab module Loaders # Suitable for use to find resources that expose `where_full_path_in`, # such as Project, Group, Namespace + # full path is always converted to lowercase for case-insensitive results class FullPathModelLoader attr_reader :model_class, :full_path def initialize(model_class, full_path) @model_class = model_class - @full_path = full_path + @full_path = full_path.downcase end def find BatchLoader::GraphQL.for(full_path).batch(key: model_class) do |full_paths, loader, args| # `with_route` avoids an N+1 calculating full_path args[:key].where_full_path_in(full_paths).with_route.each do |model_instance| - loader.call(model_instance.full_path, model_instance) + loader.call(model_instance.full_path.downcase, model_instance) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e13803067b0..c5f2a7b6846 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -22888,8 +22888,10 @@ msgstr "" msgid "No webhooks found, add one in the form above." msgstr "" -msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription." -msgstr "" +msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} day to renew your subscription." +msgid_plural "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} days to renew your subscription." +msgstr[0] "" +msgstr[1] "" msgid "No. of commits" msgstr "" @@ -33230,7 +33232,7 @@ msgstr "" msgid "Thanks for your purchase!" msgstr "" -msgid "That is ok, I do not want to renew" +msgid "That's OK, I don't want to renew" msgstr "" msgid "That's it, well done!" @@ -38771,10 +38773,10 @@ msgstr "" msgid "Your %{host} account was signed in to from a new location" msgstr "" -msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}." +msgid "Your %{strong}%{plan_name}%{strong_close} subscription expires on %{strong}%{expires_on}%{strong_close}. After that date, you cannot create issues or merge requests, or use many other features." msgstr "" -msgid "Your %{strong}%{plan_name}%{strong_close} subscription will expire on %{strong}%{expires_on}%{strong_close}. After that, you will not be able to create issues or merge requests as well as many other features." +msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}." msgstr "" msgid "Your CI/CD configuration syntax is invalid. View Lint tab for more details." @@ -39053,8 +39055,10 @@ msgstr "" msgid "Your subscription has been downgraded." msgstr "" -msgid "Your subscription will expire in %{remaining_days}." -msgstr "" +msgid "Your subscription will expire in %{remaining_days} day." +msgid_plural "Your subscription will expire in %{remaining_days} days." +msgstr[0] "" +msgstr[1] "" msgid "Your username is %{username}." msgstr "" diff --git a/spec/graphql/resolvers/group_resolver_spec.rb b/spec/graphql/resolvers/group_resolver_spec.rb index a03e7854177..ed406d14772 100644 --- a/spec/graphql/resolvers/group_resolver_spec.rb +++ b/spec/graphql/resolvers/group_resolver_spec.rb @@ -20,10 +20,15 @@ RSpec.describe Resolvers::GroupResolver do end it 'resolves an unknown full_path to nil' do - result = batch_sync { resolve_group('unknown/project') } + result = batch_sync { resolve_group('unknown/group') } expect(result).to be_nil end + + it 'treats group full path as case insensitive' do + result = batch_sync { resolve_group(group1.full_path.upcase) } + expect(result).to eq group1 + end end def resolve_group(full_path) diff --git a/spec/graphql/resolvers/project_resolver_spec.rb b/spec/graphql/resolvers/project_resolver_spec.rb index d0661c27b95..cd3fdc788e6 100644 --- a/spec/graphql/resolvers/project_resolver_spec.rb +++ b/spec/graphql/resolvers/project_resolver_spec.rb @@ -25,6 +25,11 @@ RSpec.describe Resolvers::ProjectResolver do expect(result).to be_nil end + + it 'treats project full path as case insensitive' do + result = batch_sync { resolve_project(project1.full_path.upcase) } + expect(result).to eq project1 + end end it 'does not increase complexity depending on number of load limits' do diff --git a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb index 01eef673c35..7e4e9602a92 100644 --- a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb @@ -5,20 +5,10 @@ require 'rspec-parameterized' RSpec.describe Gitlab::Ci::Variables::Collection::Sort do describe '#initialize with non-Collection value' do - context 'when FF :variable_inside_variable is disabled' do - subject { Gitlab::Ci::Variables::Collection::Sort.new([]) } + subject { Gitlab::Ci::Variables::Collection::Sort.new([]) } - it 'raises ArgumentError' do - expect { subject }.to raise_error(ArgumentError, /Collection object was expected/) - end - end - - context 'when FF :variable_inside_variable is enabled' do - subject { Gitlab::Ci::Variables::Collection::Sort.new([]) } - - it 'raises ArgumentError' do - expect { subject }.to raise_error(ArgumentError, /Collection object was expected/) - end + it 'raises ArgumentError' do + expect { subject }.to raise_error(ArgumentError, /Collection object was expected/) end end @@ -182,5 +172,33 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do expect { subject }.to raise_error(TSort::Cyclic) end end + + context 'with overridden variables' do + let(:variables) do + [ + { key: 'PROJECT_VAR', value: '$SUBGROUP_VAR' }, + { key: 'SUBGROUP_VAR', value: '$TOP_LEVEL_GROUP_NAME' }, + { key: 'SUBGROUP_VAR', value: '$SUB_GROUP_NAME' }, + { key: 'TOP_LEVEL_GROUP_NAME', value: 'top-level-group' }, + { key: 'SUB_GROUP_NAME', value: 'vars-in-vars-subgroup' } + ] + end + + let(:collection) { Gitlab::Ci::Variables::Collection.new(variables) } + + subject do + Gitlab::Ci::Variables::Collection::Sort.new(collection).tsort.map { |v| { v[:key] => v.value } } + end + + it 'preserves relative order of overridden variables' do + is_expected.to eq([ + { 'TOP_LEVEL_GROUP_NAME' => 'top-level-group' }, + { 'SUBGROUP_VAR' => '$TOP_LEVEL_GROUP_NAME' }, + { 'SUB_GROUP_NAME' => 'vars-in-vars-subgroup' }, + { 'SUBGROUP_VAR' => '$SUB_GROUP_NAME' }, + { 'PROJECT_VAR' => '$SUBGROUP_VAR' } + ]) + end + end end end diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index abda27f0d6e..7ba98380986 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -123,17 +123,102 @@ RSpec.describe Gitlab::Ci::Variables::Collection do end describe '#[]' do - variable = { key: 'VAR', value: 'value', public: true, masked: false } + subject { Gitlab::Ci::Variables::Collection.new(variables)[var_name] } - collection = described_class.new([variable]) + shared_examples 'an array access operator' do + context 'for a non-existent variable name' do + let(:var_name) { 'UNKNOWN_VAR' } - it 'returns nil for a non-existent variable name' do - expect(collection['UNKNOWN_VAR']).to be_nil + it 'returns nil' do + is_expected.to be_nil + end + end + + context 'for an existent variable name' do + let(:var_name) { 'VAR' } + + it 'returns the last Item' do + is_expected.to be_an_instance_of(Gitlab::Ci::Variables::Collection::Item) + expect(subject.to_runner_variable).to eq(variables.last) + end + end + end + + context 'with variable key with single entry' do + let(:variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false } + ] + end + + it_behaves_like 'an array access operator' + end + + context 'with variable key with multiple entries' do + let(:variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false }, + { key: 'VAR', value: 'override value', public: true, masked: false } + ] + end + + it_behaves_like 'an array access operator' end + end + + describe '#all' do + subject { described_class.new(variables).all(var_name) } - it 'returns Item for an existent variable name' do - expect(collection['VAR']).to be_an_instance_of(Gitlab::Ci::Variables::Collection::Item) - expect(collection['VAR'].to_runner_variable).to eq(variable) + shared_examples 'a method returning all known variables or nil' do + context 'for a non-existent variable name' do + let(:var_name) { 'UNKNOWN_VAR' } + + it 'returns nil' do + is_expected.to be_nil + end + end + + context 'for an existing variable name' do + let(:var_name) { 'VAR' } + + it 'returns all expected Items' do + is_expected.to eq(expected_variables.map { |v| Gitlab::Ci::Variables::Collection::Item.fabricate(v) }) + end + end + end + + context 'with variable key with single entry' do + let(:variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false } + ] + end + + it_behaves_like 'a method returning all known variables or nil' do + let(:expected_variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false } + ] + end + end + end + + context 'with variable key with multiple entries' do + let(:variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false }, + { key: 'VAR', value: 'override value', public: true, masked: false } + ] + end + + it_behaves_like 'a method returning all known variables or nil' do + let(:expected_variables) do + [ + { key: 'VAR', value: 'value', public: true, masked: false }, + { key: 'VAR', value: 'override value', public: true, masked: false } + ] + end + end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 0dbd2b13305..1e06d566c80 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -5269,4 +5269,23 @@ RSpec.describe Ci::Build do build.ensure_trace_metadata! end end + + describe '#doom!' do + subject { build.doom! } + + let_it_be(:build) { create(:ci_build, :queued) } + + it 'updates status and failure_reason', :aggregate_failures do + subject + + expect(build.status).to eq("failed") + expect(build.failure_reason).to eq("data_integrity_failure") + end + + it 'drops associated pending build' do + subject + + expect(build.reload.queuing_entry).not_to be_present + end + end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 3622eedfed5..80bccdfee0c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -2616,6 +2616,23 @@ RSpec.describe API::Projects do expect(json_response).to have_key 'service_desk_enabled' expect(json_response).to have_key 'service_desk_address' end + + context 'when project is shared to multiple groups' do + it 'avoids N+1 queries' do + create(:project_group_link, project: project) + get api("/projects/#{project.id}", user) + + control = ActiveRecord::QueryRecorder.new do + get api("/projects/#{project.id}", user) + end + + create(:project_group_link, project: project) + + expect do + get api("/projects/#{project.id}", user) + end.not_to exceed_query_limit(control) + end + end end describe 'GET /projects/:id/users' do |