diff options
52 files changed, 542 insertions, 130 deletions
diff --git a/app/assets/javascripts/abuse_reports/components/links_to_spam_input.vue b/app/assets/javascripts/abuse_reports/components/links_to_spam_input.vue new file mode 100644 index 00000000000..02fe131553c --- /dev/null +++ b/app/assets/javascripts/abuse_reports/components/links_to_spam_input.vue @@ -0,0 +1,68 @@ +<script> +import { GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +export default { + name: 'LinksToSpamInput', + components: { + GlButton, + GlFormGroup, + GlFormInput, + }, + i18n: { + label: s__('ReportAbuse|Link to spam'), + description: s__('ReportAbuse|URL of this user posting spam'), + addAnotherText: s__('ReportAbuse|Add another link'), + }, + props: { + previousLinks: { + type: Array, + required: false, + default: () => [], + }, + }, + data() { + return { + links: this.previousLinks.length > 0 ? this.previousLinks : [''], + }; + }, + methods: { + addAnotherInput() { + this.links.push(''); + }, + }, +}; +</script> +<template> + <div> + <template v-for="(link, index) in links"> + <div :key="index" class="row"> + <div class="col-lg-8"> + <gl-form-group class="gl-mt-5"> + <template #label> + <div class="gl-pb-2"> + {{ $options.i18n.label }} + </div> + <div class="gl-font-weight-normal"> + {{ $options.i18n.description }} + </div> + </template> + <gl-form-input + v-model.trim="links[index]" + type="url" + name="abuse_report[links_to_spam][]" + autocomplete="off" + /> + </gl-form-group> + </div> + </div> + </template> + <div class="row"> + <div class="col-lg-8"> + <gl-button variant="link" icon="plus" class="gl-float-right" @click="addAnotherInput"> + {{ $options.i18n.addAnotherText }} + </gl-button> + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/abuse_reports/index.js b/app/assets/javascripts/abuse_reports/index.js new file mode 100644 index 00000000000..fff4ad8daa0 --- /dev/null +++ b/app/assets/javascripts/abuse_reports/index.js @@ -0,0 +1,22 @@ +import Vue from 'vue'; +import LinksToSpamInput from './components/links_to_spam_input.vue'; + +export const initLinkToSpam = () => { + const el = document.getElementById('js-links-to-spam'); + + if (!el) return false; + + const { links } = el.dataset; + + return new Vue({ + el, + name: 'LinksToSpamRoot', + render(createElement) { + return createElement(LinksToSpamInput, { + props: { + previousLinks: JSON.parse(links), + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue index 31185e31f48..1b354940a37 100644 --- a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue +++ b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue @@ -1,5 +1,5 @@ <script> -import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui'; +import { GlIcon, GlDisclosureDropdown, GlButton } from '@gitlab/ui'; import { mapActions, mapGetters, mapState } from 'vuex'; import { setUrlParams, visitUrl } from '~/lib/utils/url_utility'; import PreviewItem from './preview_item.vue'; @@ -7,23 +7,30 @@ import DraftsCount from './drafts_count.vue'; export default { components: { - GlDropdown, - GlDropdownItem, GlIcon, PreviewItem, DraftsCount, + GlDisclosureDropdown, + GlButton, }, computed: { ...mapState('diffs', ['viewDiffsFileByFile']), ...mapGetters('batchComments', ['draftsCount', 'sortedDrafts']), ...mapGetters(['getNoteableData']), + listItems() { + return this.sortedDrafts.map((item, index) => ({ + text: item.id.toString(), + action: () => { + this.onClickDraft(item); + }, + last: index === this.sortedDrafts.length - 1, + ...item, + })); + }, }, methods: { ...mapActions('diffs', ['setCurrentFileHash']), ...mapActions('batchComments', ['scrollToDraft']), - isLast(index) { - return index === this.sortedDrafts.length - 1; - }, isOnLatestDiff(draft) { return draft.position?.head_sha === this.getNoteableData.diff_head_sha; }, @@ -45,23 +52,23 @@ export default { </script> <template> - <gl-dropdown - :header-text="n__('%d pending comment', '%d pending comments', draftsCount)" - dropup - data-qa-selector="review_preview_dropdown" - > - <template #button-content> - {{ __('Pending comments') }} - <drafts-count variant="neutral" /> - <gl-icon class="dropdown-chevron" name="chevron-up" /> + <gl-disclosure-dropdown :items="listItems" dropup data-qa-selector="review_preview_dropdown"> + <template #toggle> + <gl-button + >{{ __('Pending comments') }} <drafts-count variant="neutral" /><gl-icon + class="dropdown-chevron" + name="chevron-up" + /></gl-button> + </template> + + <template #header> + <p class="gl-dropdown-header-top"> + {{ n__('%d pending comment', '%d pending comments', draftsCount) }} + </p> + </template> + + <template #list-item="{ item }"> + <preview-item :draft="item" :is-last="item.last" @click="onClickDraft(item)" /> </template> - <gl-dropdown-item - v-for="(draft, index) in sortedDrafts" - :key="draft.id" - data-testid="preview-item" - @click="onClickDraft(draft)" - > - <preview-item :draft="draft" :is-last="isLast(index)" /> - </gl-dropdown-item> - </gl-dropdown> + </gl-disclosure-dropdown> </template> diff --git a/app/assets/javascripts/pages/abuse_reports/index.js b/app/assets/javascripts/pages/abuse_reports/index.js new file mode 100644 index 00000000000..feceeb0b10a --- /dev/null +++ b/app/assets/javascripts/pages/abuse_reports/index.js @@ -0,0 +1,3 @@ +import { initLinkToSpam } from '~/abuse_reports'; + +initLinkToSpam(); diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 378da9ef146..f6b4fbac8d5 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -55,7 +55,7 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url) + params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url, links_to_spam: []) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 924de0ee7ea..895a9a00624 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -10,7 +10,7 @@ class Projects::RawController < Projects::ApplicationController prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:blob) } - before_action :set_ref_and_path + before_action :assign_ref_vars before_action :require_non_empty_project before_action :authorize_read_code! before_action :check_show_rate_limit!, only: [:show], unless: :external_storage_request? diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 33ce37ef4fb..a9ff5d8a3bf 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -47,8 +47,18 @@ class Projects::RepositoriesController < Projects::ApplicationController end def set_cache_headers - expires_in cache_max_age(archive_metadata['CommitId']), public: Guest.can?(:download_code, project) - fresh_when(etag: archive_metadata['ArchivePath']) + commit_id = archive_metadata['CommitId'] + + if Feature.enabled?(:improve_blobs_cache_headers) + expires_in(cache_max_age(commit_id), + public: Guest.can?(:download_code, project), must_revalidate: true, stale_if_error: 5.minutes, + stale_while_revalidate: 1.minute, 's-maxage': 1.minute) + + fresh_when(strong_etag: [commit_id, archive_metadata['ArchivePath']]) + else + expires_in cache_max_age(commit_id), public: Guest.can?(:download_code, project) + fresh_when(etag: archive_metadata['ArchivePath']) + end end def archive_not_modified? diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index ee0c23ef31e..be9d49ab4e2 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -4,6 +4,8 @@ class AbuseReport < ApplicationRecord include CacheMarkdownField include Sortable + MAX_CHAR_LIMIT_URL = 512 + cache_markdown_field :message, pipeline: :single_line belongs_to :reporter, class_name: 'User' @@ -23,13 +25,23 @@ class AbuseReport < ApplicationRecord validates :reported_from_url, allow_blank: true, - length: { maximum: 512 }, + length: { maximum: MAX_CHAR_LIMIT_URL }, addressable_url: { dns_rebind_protection: true, blocked_message: 'is an invalid URL. You can try reporting the abuse again, ' \ 'or contact a GitLab administrator for help.' } + validates :links_to_spam, + allow_blank: true, + length: { + maximum: 20, + message: N_("exceeds the limit of %{count} links") + } + + before_validation :filter_empty_strings_from_links_to_spam + validate :links_to_spam_contains_valid_urls + scope :by_user, ->(user) { where(user_id: user) } scope :with_users, -> { includes(:reporter, :user) } @@ -64,4 +76,34 @@ class AbuseReport < ApplicationRecord AbuseReportMailer.notify(self.id).deliver_later end + + private + + def filter_empty_strings_from_links_to_spam + return if links_to_spam.blank? + + links_to_spam.reject!(&:empty?) + end + + def links_to_spam_contains_valid_urls + return if links_to_spam.blank? + + links_to_spam.each do |link| + Gitlab::UrlBlocker.validate!( + link, + schemes: %w[http https], + allow_localhost: true, + dns_rebind_protection: true + ) + + next unless link.length > MAX_CHAR_LIMIT_URL + + errors.add( + :links_to_spam, + format(_('contains URLs that exceed the %{limit} character limit'), limit: MAX_CHAR_LIMIT_URL) + ) + end + rescue ::Gitlab::UrlBlocker::BlockedUrlError + errors.add(:links_to_spam, _('only supports valid HTTP(S) URLs')) + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b5daf104cfe..a91c55e858b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -194,9 +194,7 @@ class MergeRequest < ApplicationRecord end before_transition any => :merged do |merge_request| - if ::Feature.enabled?(:reset_merge_error_on_transition, merge_request.project) - merge_request.merge_error = nil - end + merge_request.merge_error = nil end after_transition any => :opened do |merge_request| diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml index 393021ed93c..8b9bbfd0a59 100644 --- a/app/views/abuse_reports/new.html.haml +++ b/app/views/abuse_reports/new.html.haml @@ -8,7 +8,9 @@ = _("A member of the abuse team will review your report as soon as possible.") = gitlab_ui_form_for @abuse_report, html: { class: 'js-quick-submit'} do |f| - = form_errors(@abuse_report) + .row + .col-lg-8 + = form_errors(@abuse_report) = f.hidden_field :user_id = f.hidden_field :category @@ -23,6 +25,7 @@ .col-lg-8 = f.label :reported_from = f.text_field :reported_from_url, class: "form-control", readonly: true + #js-links-to-spam{ data: { links: Array(@abuse_report.links_to_spam) } } .form-group.row .col-lg-8 = f.label :reason diff --git a/config/feature_flags/development/reset_merge_error_on_transition.yml b/config/feature_flags/development/reset_merge_error_on_transition.yml deleted file mode 100644 index bb0b25d1666..00000000000 --- a/config/feature_flags/development/reset_merge_error_on_transition.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: reset_merge_error_on_transition -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106942 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385859 -milestone: '15.8' -type: development -group: group::code review -default_enabled: false diff --git a/db/migrate/20221229064959_add_links_to_spam_to_abuse_reports.rb b/db/migrate/20221229064959_add_links_to_spam_to_abuse_reports.rb new file mode 100644 index 00000000000..af4761644c4 --- /dev/null +++ b/db/migrate/20221229064959_add_links_to_spam_to_abuse_reports.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddLinksToSpamToAbuseReports < Gitlab::Database::Migration[2.1] + def change + add_column :abuse_reports, :links_to_spam, :text, array: true, null: false, default: [] + end +end diff --git a/db/migrate/20230106014423_add_constraint_to_links_to_spam.rb b/db/migrate/20230106014423_add_constraint_to_links_to_spam.rb new file mode 100644 index 00000000000..b3d7ff3755a --- /dev/null +++ b/db/migrate/20230106014423_add_constraint_to_links_to_spam.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddConstraintToLinksToSpam < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + CONSTRAINT_NAME = "abuse_reports_links_to_spam_length_check" + + def up + add_check_constraint :abuse_reports, "CARDINALITY(links_to_spam) <= 20", CONSTRAINT_NAME + end + + def down + remove_check_constraint :abuse_reports, CONSTRAINT_NAME + end +end diff --git a/db/schema_migrations/20221229064959 b/db/schema_migrations/20221229064959 new file mode 100644 index 00000000000..545aa310068 --- /dev/null +++ b/db/schema_migrations/20221229064959 @@ -0,0 +1 @@ +052f83d45f263bc95b80081af9c3086b6677b49e503ddc11770f444d7abd7e45
\ No newline at end of file diff --git a/db/schema_migrations/20230106014423 b/db/schema_migrations/20230106014423 new file mode 100644 index 00000000000..f4fc4137ffd --- /dev/null +++ b/db/schema_migrations/20230106014423 @@ -0,0 +1 @@ +1551efcbb268bdb564647fb36ad700b995a8296229a858a3c82cb36ff3cff673
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 04f0b7d18bb..098d166b1c7 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10587,6 +10587,8 @@ CREATE TABLE abuse_reports ( cached_markdown_version integer, category smallint DEFAULT 1 NOT NULL, reported_from_url text DEFAULT ''::text NOT NULL, + links_to_spam text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT abuse_reports_links_to_spam_length_check CHECK ((cardinality(links_to_spam) <= 20)), CONSTRAINT check_ab1260fa6c CHECK ((char_length(reported_from_url) <= 512)) ); diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md index 26acab510cf..8bb060b3a08 100644 --- a/doc/administration/geo/replication/datatypes.md +++ b/doc/administration/geo/replication/datatypes.md @@ -87,7 +87,7 @@ GitLab does not require a special file system and can work with: Geo triggers garbage collection in Gitaly to [deduplicate forked repositories](../../../development/git_object_deduplication.md#git-object-deduplication-and-gitlab-geo) on Geo secondary sites. -Communication is done via Gitaly's own gRPC API, with three possible ways of synchronization: +The Gitaly gRPC API does the communication, with three possible ways of synchronization: - Using regular Git clone/fetch from one Geo site to another (with special authentication). - Using repository snapshots (for when the first method fails or repository is corrupt). diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md index 51d6e9ae1fd..81a96ca698c 100644 --- a/doc/administration/operations/puma.md +++ b/doc/administration/operations/puma.md @@ -223,7 +223,7 @@ from the Linux package and is no longer supported. Puma has a multi-thread architecture that uses less memory than a multi-process application server like Unicorn. On GitLab.com, we saw a 40% reduction in memory -consumption. Most Rails application requests normally include a proportion of I/O wait time. +consumption. Most Rails application requests usually include a proportion of I/O wait time. During I/O wait time, MRI Ruby releases the GVL to other threads. Multi-threaded Puma can therefore still serve more requests than a single process. diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md index 514a0b255c5..46683053510 100644 --- a/doc/ci/environments/index.md +++ b/doc/ci/environments/index.md @@ -330,7 +330,7 @@ Note the following: NOTE: For Windows runners, using `echo` to write to `.env` files may fail. Using the PowerShell `Add-Content`command -will help in such cases. For example: +helps in such cases. For example: ```powershell Add-Content -Path deploy.env -Value "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" @@ -534,7 +534,7 @@ In this example, if the configuration is not identical: Also in the example, `GIT_STRATEGY` is set to `none`. If the `stop_review_app` job is [automatically triggered](../environments/index.md#stop-an-environment), -the runner won't try to check out the code after the branch is deleted. +the runner doesn't try to check out the code after the branch is deleted. The `stop_review_app` job **must** have the following keywords defined: @@ -975,7 +975,7 @@ the `review/feature-1` spec takes precedence over `review/*` and `*` specs. ### Rename an environment -> Renaming environments through the UI was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68550) in GitLab 14.3. Renaming environments through the API was deprecated and [will be removed](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 15.0. +> Renaming environments through the UI was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68550) in GitLab 14.3. Renaming environments through the API was deprecated and [is planned to be removed](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 15.0. Renaming an environment through the UI is not possible. Instead, you need to delete the old environment and create a new one: @@ -1124,7 +1124,7 @@ To fix this, use one of the following solutions: Starting from GitLab 14.5, GitLab [deletes old deployment refs](#archive-old-deployments) to keep your Git repository performant. -If you have to restore archived Git-refs, please ask an administrator of your self-managed GitLab instance +If you have to restore archived Git-refs, ask an administrator of your self-managed GitLab instance to execute the following command on Rails console: ```ruby diff --git a/doc/ci/examples/semantic-release.md b/doc/ci/examples/semantic-release.md index 1fa526e787a..8ae4cf61e37 100644 --- a/doc/ci/examples/semantic-release.md +++ b/doc/ci/examples/semantic-release.md @@ -35,7 +35,7 @@ You can also view or fork the complete [example source](https://gitlab.com/gitla } ``` -1. Update the `files` key with glob patterns that selects all files that should be included in the published module. More information about `files` can be found [in npm's documentation](https://docs.npmjs.com/cli/v6/configuring-npm/package-json/#files). +1. Update the `files` key with glob patterns that selects all files that should be included in the published module. More information about `files` can be found [in the npm documentation](https://docs.npmjs.com/cli/v6/configuring-npm/package-json/#files). 1. Add a `.gitignore` file to the project to avoid committing `node_modules`: diff --git a/doc/ci/services/index.md b/doc/ci/services/index.md index bbd2f822481..c33288772aa 100644 --- a/doc/ci/services/index.md +++ b/doc/ci/services/index.md @@ -9,7 +9,7 @@ type: index # Services **(FREE)** When you configure CI/CD, you specify an image, which is used to create the container -where your jobs will run. To specify this image, you use the `image` keyword. +where your jobs run. To specify this image, you use the `image` keyword. You can specify an additional image by using the `services` keyword. This additional image is used to create another container, which is available to the first container. @@ -343,7 +343,7 @@ services: command: ["/usr/bin/super-sql", "run"] ``` -The syntax of `command` is similar to [Dockerfile's `CMD`](https://docs.docker.com/engine/reference/builder/#cmd). +The syntax of `command` is similar to [Dockerfile `CMD`](https://docs.docker.com/engine/reference/builder/#cmd). ## Using `services` with `docker run` (Docker-in-Docker) side-by-side @@ -417,10 +417,10 @@ Accepted values are: - Enabled: `TRUE`, `true`, `True` - Disabled: `FALSE`, `false`, `False` -Any other values will result in an error message and effectively disable the feature. +Any other values result in an error message and effectively disable the feature. -When enabled, logs for _all_ service containers will be captured and streamed into the jobs trace log concurrently with -other logs. Logs from each container will be prefixed with the container's aliases, and displayed in a different color. +When enabled, logs for _all_ service containers are captured and streamed into the jobs trace log concurrently with +other logs. Logs from each container are prefixed with the container's aliases, and displayed in a different color. NOTE: You may want to adjust the logging level in the service container for which you want to capture logs since the default diff --git a/doc/ci/testing/test_coverage_visualization.md b/doc/ci/testing/test_coverage_visualization.md index 9bff8dbf780..e2a1a4a2c5e 100644 --- a/doc/ci/testing/test_coverage_visualization.md +++ b/doc/ci/testing/test_coverage_visualization.md @@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w With the help of [GitLab CI/CD](../index.md), you can collect the test coverage information of your favorite testing or coverage-analysis tool, and visualize -this information inside the file diff view of your merge requests (MRs). This will allow you +this information inside the file diff view of your merge requests (MRs). This allows you to see which lines are covered by tests, and which lines still require coverage, before the MR is merged. @@ -64,7 +64,7 @@ You must configure these separately. A limit of 100 `<source>` nodes for Cobertura format XML files applies. If your Cobertura report exceeds 100 nodes, there can be mismatches or no matches in the merge request diff view. -A single Cobertura XML file can be no more than 10MiB. For large projects, split the Cobertura XML into +A single Cobertura XML file can be no more than 10 MiB. For large projects, split the Cobertura XML into smaller files. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/328772) for more details. When submitting many files, it can take a few minutes for coverage to show on a merge request. diff --git a/doc/development/database/query_recorder.md b/doc/development/database/query_recorder.md index dfaaf8afcde..bae211c1618 100644 --- a/doc/development/database/query_recorder.md +++ b/doc/development/database/query_recorder.md @@ -148,3 +148,4 @@ There are multiple ways to find the source of queries. - [Performance guidelines](../performance.md) - [Merge request performance guidelines - Query counts](../merge_request_concepts/performance.md#query-counts) - [Merge request performance guidelines - Cached queries](../merge_request_concepts/performance.md#cached-queries) +- [RedisCommands::Recorder](../redis.md#n1-calls-problem) For testing `N+1` calls in Redis diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md index e7922b54e02..a98ba3e4ee8 100644 --- a/doc/development/documentation/workflow.md +++ b/doc/development/documentation/workflow.md @@ -15,6 +15,21 @@ Anyone can contribute to the GitLab documentation! You can create a merge reques If you are working on a feature or enhancement, use the [feature workflow process described in the GitLab Handbook](https://about.gitlab.com/handbook/product/ux/technical-writing/workflow/#documentation-for-a-product-change). +## Do not use ChatGPT or AI-generated content for the docs + +GitLab documentation is distributed under the [CC BY-SA 4.0 license](https://creativecommons.org/licenses/by-sa/4.0/), which presupposes that GitLab owns the documentation. + +Under current law in the US and the EU, it’s possible that AI-generated works might either: + +- not be owned by anyone because they weren't created by a human, or +- belong to the AI training data’s creator, if the AI verbatim reproduces content that it trained on + +If the documentation contains AI-generated content, GitLab probably wouldn't own this content, which would risk invalidating the CC BY-SA 4.0 license. + +Contributions to GitLab documentation are made under either our [DCO or our CLA terms](https://about.gitlab.com/community/contribute/dco-cla/). In both, contributors have to make certain certifications about the authorship of their work that they can't validly make for AI-generated text. + +For these reasons, do not add AI-generated content to the documentation. + ## How to update the docs If you are not a GitLab team member, or do not have the Developer role for the GitLab repository, to update GitLab documentation: diff --git a/doc/development/redis.md b/doc/development/redis.md index 75c7df0737b..45712b14c0a 100644 --- a/doc/development/redis.md +++ b/doc/development/redis.md @@ -142,6 +142,59 @@ those that use the most memory. Currently this is not run automatically for the GitLab.com Redis instances, but is run manually on an as-needed basis. +## N+1 calls problem + +> Introduced in [`spec/support/helpers/redis_commands/recorder.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/helpers/redis_commands/recorder.rb) via [`f696f670`](https://gitlab.com/gitlab-org/gitlab/-/commit/f696f670005435472354a3dc0c01aa271aef9e32) + +`RedisCommands::Recorder` is a tool for detecting Redis N+1 calls problem from tests. + +Redis is often used for caching purposes. Usually, cache calls are lightweight and +cannot generate enough load to affect the Redis instance. However, it is still +possible to trigger expensive cache recalculations without knowing that. Use this +tool to analyze Redis calls, and define expected limits for them. + +### Create a test + +It is implemented as a [`ActiveSupport::Notifications`](https://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) instrumenter. + +You can create a test that verifies that a testable code only makes +a single Redis call: + +```ruby +it 'avoids N+1 Redis calls' do + control = RedisCommands::Recorder.new { visit_page } + + expect(control.count).to eq(1) +end +``` + +or a test that verifies the number of specific Redis calls: + +```ruby +it 'avoids N+1 sadd Redis calls' do + control = RedisCommands::Recorder.new { visit_page } + + expect(control.by_command(:sadd).count).to eq(1) +end +``` + +You can also provide a pattern to capture only specific Redis calls: + +```ruby +it 'avoids N+1 Redis calls to forks_count key' do + control = RedisCommands::Recorder.new(pattern: 'forks_count') { visit_page } + + expect(control.count).to eq(1) +end +``` + +These tests can help to identify N+1 problems related to Redis calls, +and make sure that the fix for them works as expected. + +### See also + +- [Database query recorder](database/query_recorder.md) + ## Utility classes We have some extra classes to help with specific use cases. These are diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bd77f3b3267..5417d393ae8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -35419,6 +35419,12 @@ msgstr "" msgid "Report your license usage data to GitLab" msgstr "" +msgid "ReportAbuse|Add another link" +msgstr "" + +msgid "ReportAbuse|Link to spam" +msgstr "" + msgid "ReportAbuse|Report abuse to administrator" msgstr "" @@ -35446,6 +35452,9 @@ msgstr "" msgid "ReportAbuse|They're violating a copyright or trademark." msgstr "" +msgid "ReportAbuse|URL of this user posting spam" +msgstr "" + msgid "ReportAbuse|Why are you reporting this user?" msgstr "" @@ -49807,6 +49816,9 @@ msgstr "" msgid "container registry images" msgstr "" +msgid "contains URLs that exceed the %{limit} character limit" +msgstr "" + msgid "contains URLs that exceed the 1024 character limit (%{urls})" msgstr "" @@ -49955,6 +49967,9 @@ msgstr "" msgid "exceeds the limit of %{bytes} bytes for directory name \"%{dirname}\"" msgstr "" +msgid "exceeds the limit of %{count} links" +msgstr "" + msgid "expired on %{timebox_due_date}" msgstr "" @@ -50765,6 +50780,9 @@ msgstr "" msgid "only available on top-level groups." msgstr "" +msgid "only supports valid HTTP(S) URLs" +msgstr "" + msgid "open issue" msgstr "" diff --git a/package.json b/package.json index cf415808e85..c4d42196e4b 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@cubejs-client/vue": "^0.31.19", "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", - "@gitlab/fonts": "^1.1.0", + "@gitlab/fonts": "^1.1.2", "@gitlab/svgs": "3.17.0", "@gitlab/ui": "52.13.1", "@gitlab/visual-review-tools": "1.7.3", diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 1c9aafacbd9..a21727e5691 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::RawController do +RSpec.describe Projects::RawController, feature_category: :source_code_management do include RepoHelpers let_it_be(:project) { create(:project, :public, :repository) } @@ -23,13 +23,13 @@ RSpec.describe Projects::RawController do subject { get_show } - shared_examples 'single Gitaly request' do - it 'makes a single Gitaly request', :request_store, :clean_gitlab_redis_cache do + shared_examples 'limited number of Gitaly request' do + it 'makes a limited number of Gitaly request', :request_store, :clean_gitlab_redis_cache do # Warm up to populate repository cache get_show RequestStore.clear! - expect { get_show }.to change { Gitlab::GitalyClient.get_request_count }.by(1) + expect { get_show }.to change { Gitlab::GitalyClient.get_request_count }.by(2) end end @@ -57,7 +57,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'project cache control headers' it_behaves_like 'content disposition headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'image header' do @@ -73,7 +73,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'project cache control headers' it_behaves_like 'content disposition headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'with LFS files' do @@ -82,7 +82,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'a controller that can serve LFS files' it_behaves_like 'project cache control headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'when the endpoint receives requests above the limit' do @@ -239,8 +239,10 @@ RSpec.describe Projects::RawController do end describe 'caching' do + let(:ref) { project.default_branch } + def request_file - get(:show, params: { namespace_id: project.namespace, project_id: project, id: 'master/README.md' }) + get(:show, params: { namespace_id: project.namespace, project_id: project, id: "#{ref}/README.md" }) end it 'sets appropriate caching headers' do @@ -254,6 +256,21 @@ RSpec.describe Projects::RawController do ) end + context 'when a blob access by permalink' do + let(:ref) { project.commit.id } + + it 'sets appropriate caching headers with longer max-age' do + sign_in create(:user) + request_file + + expect(response.headers['ETag']).to eq("\"bdd5aa537c1e1f6d1b66de4bac8a6132\"") + expect(response.cache_control[:no_store]).to be_nil + expect(response.header['Cache-Control']).to eq( + 'max-age=3600, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) + end + end + context 'when a public project has private repo' do let(:project) { create(:project, :public, :repository, :repository_private) } let(:user) { create(:user, maintainer_projects: [project]) } diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 928428b5caf..bc2a3abc2d6 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Projects::RepositoriesController do +RSpec.describe Projects::RepositoriesController, feature_category: :source_code_management do let_it_be(:project) { create(:project, :repository) } describe 'POST create' do @@ -143,7 +143,9 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect(response.header['ETag']).to be_present - expect(response.header['Cache-Control']).to include('max-age=60, public') + expect(response.header['Cache-Control']).to eq( + 'max-age=60, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end context 'and repo is private' do @@ -154,7 +156,9 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect(response.header['ETag']).to be_present - expect(response.header['Cache-Control']).to include('max-age=60, private') + expect(response.header['Cache-Control']).to eq( + 'max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end end end @@ -164,7 +168,48 @@ RSpec.describe Projects::RepositoriesController do get_archive('ddd0f15ae83993f5cb66a927a28673882e99100b') expect(response).to have_gitlab_http_status(:ok) - expect(response.header['Cache-Control']).to include('max-age=3600') + expect(response.header['Cache-Control']).to eq( + 'max-age=3600, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) + end + end + + context 'when improve_blobs_cache_headers is disabled' do + before do + stub_feature_flags(improve_blobs_cache_headers: false) + end + + context 'when project is public' do + let(:project) { create(:project, :repository, :public) } + + it 'sets appropriate caching headers' do + get_archive + + expect(response).to have_gitlab_http_status(:ok) + expect(response.header['ETag']).to be_present + expect(response.header['Cache-Control']).to eq('max-age=60, public') + end + + context 'and repo is private' do + let(:project) { create(:project, :repository, :public, :repository_private) } + + it 'sets appropriate caching headers' do + get_archive + + expect(response).to have_gitlab_http_status(:ok) + expect(response.header['ETag']).to be_present + expect(response.header['Cache-Control']).to eq('max-age=60, private') + end + end + end + + context 'when ref is a commit SHA' do + it 'max-age is set to 3600 in Cache-Control header' do + get_archive('ddd0f15ae83993f5cb66a927a28673882e99100b') + + expect(response).to have_gitlab_http_status(:ok) + expect(response.header['Cache-Control']).to eq('max-age=3600, private') + end end end diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb index 4ae9b4def8e..355fb142994 100644 --- a/spec/factories/abuse_reports.rb +++ b/spec/factories/abuse_reports.rb @@ -6,5 +6,6 @@ FactoryBot.define do user message { 'User sends spam' } reported_from_url { 'http://gitlab.com' } + links_to_spam { ['https://gitlab.com/issue1', 'https://gitlab.com/issue2'] } end end diff --git a/spec/frontend/abuse_reports/components/links_to_spam_input_spec.js b/spec/frontend/abuse_reports/components/links_to_spam_input_spec.js new file mode 100644 index 00000000000..c0c87dd1383 --- /dev/null +++ b/spec/frontend/abuse_reports/components/links_to_spam_input_spec.js @@ -0,0 +1,65 @@ +import { GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui'; +import { nextTick } from 'vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + +import LinksToSpamInput from '~/abuse_reports/components/links_to_spam_input.vue'; + +describe('LinksToSpamInput', () => { + let wrapper; + + const createComponent = (props) => { + wrapper = shallowMountExtended(LinksToSpamInput, { + propsData: { + ...props, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + const findAllFormGroups = () => wrapper.findAllComponents(GlFormGroup); + const findLinkInput = () => wrapper.findComponent(GlFormInput); + const findAddAnotherButton = () => wrapper.findComponent(GlButton); + + describe('Form Input', () => { + it('renders only one input field initially', () => { + expect(findAllFormGroups()).toHaveLength(1); + }); + + it('is of type URL and has a name attribute', () => { + expect(findLinkInput().attributes()).toMatchObject({ + type: 'url', + name: 'abuse_report[links_to_spam][]', + value: '', + }); + }); + + describe('when add another link button is clicked', () => { + it('adds another input', async () => { + findAddAnotherButton().vm.$emit('click'); + + await nextTick(); + + expect(findAllFormGroups()).toHaveLength(2); + }); + }); + + describe('when previously added links are passed to the form as props', () => { + beforeEach(() => { + createComponent({ previousLinks: ['https://gitlab.com'] }); + }); + + it('renders the input field with the value of the link pre-filled', () => { + expect(findAllFormGroups()).toHaveLength(1); + + expect(findLinkInput().attributes()).toMatchObject({ + type: 'url', + name: 'abuse_report[links_to_spam][]', + value: 'https://gitlab.com', + }); + }); + }); + }); +}); diff --git a/spec/frontend/batch_comments/components/preview_dropdown_spec.js b/spec/frontend/batch_comments/components/preview_dropdown_spec.js index 283632cb560..f86e003ab5f 100644 --- a/spec/frontend/batch_comments/components/preview_dropdown_spec.js +++ b/spec/frontend/batch_comments/components/preview_dropdown_spec.js @@ -1,9 +1,11 @@ import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { GlDisclosureDropdown } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import { TEST_HOST } from 'helpers/test_constants'; import { visitUrl } from '~/lib/utils/url_utility'; import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue'; +import PreviewItem from '~/batch_comments/components/preview_item.vue'; jest.mock('~/lib/utils/url_utility', () => ({ visitUrl: jest.fn(), @@ -17,6 +19,8 @@ let wrapper; const setCurrentFileHash = jest.fn(); const scrollToDraft = jest.fn(); +const findPreviewItem = () => wrapper.findComponent(PreviewItem); + function factory({ viewDiffsFileByFile = false, draftsCount = 1, sortedDrafts = [] } = {}) { const store = new Vuex.Store({ modules: { @@ -42,16 +46,13 @@ function factory({ viewDiffsFileByFile = false, draftsCount = 1, sortedDrafts = }, }); - wrapper = shallowMountExtended(PreviewDropdown, { + wrapper = shallowMount(PreviewDropdown, { store, + stubs: { GlDisclosureDropdown }, }); } describe('Batch comments preview dropdown', () => { - afterEach(() => { - wrapper.destroy(); - }); - describe('clicking draft', () => { it('toggles active file when viewDiffsFileByFile is true', async () => { factory({ @@ -59,12 +60,15 @@ describe('Batch comments preview dropdown', () => { sortedDrafts: [{ id: 1, file_hash: 'hash' }], }); - wrapper.findByTestId('preview-item').vm.$emit('click'); + findPreviewItem().vm.$emit('click'); await nextTick(); expect(setCurrentFileHash).toHaveBeenCalledWith(expect.anything(), 'hash'); - expect(scrollToDraft).toHaveBeenCalledWith(expect.anything(), { id: 1, file_hash: 'hash' }); + expect(scrollToDraft).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ id: 1, file_hash: 'hash' }), + ); }); it('calls scrollToDraft', async () => { @@ -73,11 +77,14 @@ describe('Batch comments preview dropdown', () => { sortedDrafts: [{ id: 1 }], }); - wrapper.findByTestId('preview-item').vm.$emit('click'); + findPreviewItem().vm.$emit('click'); await nextTick(); - expect(scrollToDraft).toHaveBeenCalledWith(expect.anything(), { id: 1 }); + expect(scrollToDraft).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ id: 1 }), + ); }); it('changes window location to navigate to commit', async () => { @@ -86,7 +93,7 @@ describe('Batch comments preview dropdown', () => { sortedDrafts: [{ id: 1, position: { head_sha: '1234' } }], }); - wrapper.findByTestId('preview-item').vm.$emit('click'); + findPreviewItem().vm.$emit('click'); await nextTick(); diff --git a/spec/frontend/batch_comments/components/publish_dropdown_spec.js b/spec/frontend/batch_comments/components/publish_dropdown_spec.js index e89934c0192..44d7b56c14f 100644 --- a/spec/frontend/batch_comments/components/publish_dropdown_spec.js +++ b/spec/frontend/batch_comments/components/publish_dropdown_spec.js @@ -1,4 +1,4 @@ -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlDisclosureDropdown } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; @@ -12,29 +12,30 @@ Vue.use(Vuex); describe('Batch comments publish dropdown component', () => { let wrapper; + const draft = createDraft(); function createComponent() { const store = createStore(); - store.state.batchComments.drafts.push(createDraft(), { ...createDraft(), id: 2 }); + store.state.batchComments.drafts.push(draft, { ...draft, id: 2 }); wrapper = shallowMount(PreviewDropdown, { store, + stubs: { GlDisclosureDropdown }, }); } - afterEach(() => { - wrapper.destroy(); - }); - it('renders list of drafts', () => { createComponent(); - expect(wrapper.findAllComponents(GlDropdownItem).length).toBe(2); + expect(wrapper.findComponent(GlDisclosureDropdown).props('items')).toMatchObject([ + draft, + { ...draft, id: 2 }, + ]); }); it('renders draft count in dropdown title', () => { createComponent(); - expect(wrapper.findComponent(GlDropdown).props('headerText')).toEqual('2 pending comments'); + expect(wrapper.findComponent(GlDisclosureDropdown).text()).toEqual('2 pending comments'); }); }); diff --git a/spec/lib/gitlab/import_export/import_export_equivalence_spec.rb b/spec/lib/gitlab/import_export/import_export_equivalence_spec.rb index 18f2e8f80d7..d03c21d6b4f 100644 --- a/spec/lib/gitlab/import_export/import_export_equivalence_spec.rb +++ b/spec/lib/gitlab/import_export/import_export_equivalence_spec.rb @@ -13,7 +13,7 @@ require 'spec_helper' # - randomly generated fields like tokens # # as these are expected to change between import/export cycles. -RSpec.describe Gitlab::ImportExport do +RSpec.describe Gitlab::ImportExport, feature_category: :importers do include ImportExport::CommonUtil include ConfigurationHelper include ImportExport::ProjectTreeExpectations diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb index ac646087a95..6053df8ba97 100644 --- a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb @@ -9,7 +9,7 @@ require 'spec_helper' -RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer do +RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer, feature_category: :importers do let_it_be(:importable, reload: true) do create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') end diff --git a/spec/lib/gitlab/usage/service_ping_report_spec.rb b/spec/lib/gitlab/usage/service_ping_report_spec.rb index 7a37a31b195..ee2469ea463 100644 --- a/spec/lib/gitlab/usage/service_ping_report_spec.rb +++ b/spec/lib/gitlab/usage/service_ping_report_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_caching do +RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_caching, feature_category: :service_ping do include UsageDataHelpers let(:usage_data) { { uuid: "1111", counts: { issue: 0 } }.deep_stringify_keys } diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb index 34f8e5b2a2f..6391b003096 100644 --- a/spec/lib/gitlab/usage_data_metrics_spec.rb +++ b/spec/lib/gitlab/usage_data_metrics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::UsageDataMetrics, :with_license do +RSpec.describe Gitlab::UsageDataMetrics, :with_license, feature_category: :service_ping do describe '.uncached_data' do subject { described_class.uncached_data } diff --git a/spec/lib/release_highlights/validator_spec.rb b/spec/lib/release_highlights/validator_spec.rb index dd1b3aa4803..7cfeffb095a 100644 --- a/spec/lib/release_highlights/validator_spec.rb +++ b/spec/lib/release_highlights/validator_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ReleaseHighlights::Validator do +RSpec.describe ReleaseHighlights::Validator, feature_category: :experimentation_adoption do let(:validator) { described_class.new(file: yaml_path) } let(:yaml_path) { 'spec/fixtures/whats_new/valid.yml' } let(:invalid_yaml_path) { 'spec/fixtures/whats_new/invalid.yml' } diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index b07fafabbb5..7995cc36383 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -43,6 +43,41 @@ RSpec.describe AbuseReport, feature_category: :insider_threat do it { is_expected.not_to allow_value(javascript).for(:reported_from_url) } it { is_expected.to allow_value('http://localhost:9000').for(:reported_from_url) } it { is_expected.to allow_value('https://gitlab.com').for(:reported_from_url) } + + it { is_expected.to allow_value([]).for(:links_to_spam) } + it { is_expected.to allow_value(nil).for(:links_to_spam) } + it { is_expected.to allow_value('').for(:links_to_spam) } + + it { is_expected.to allow_value(['https://gitlab.com']).for(:links_to_spam) } + it { is_expected.to allow_value(['http://localhost:9000']).for(:links_to_spam) } + + it { is_expected.not_to allow_value(['spam']).for(:links_to_spam) } + it { is_expected.not_to allow_value(['http://localhost:9000', 'spam']).for(:links_to_spam) } + + it { is_expected.to allow_value(['https://gitlab.com'] * 20).for(:links_to_spam) } + it { is_expected.not_to allow_value(['https://gitlab.com'] * 21).for(:links_to_spam) } + + it { + is_expected.to allow_value([ + "https://gitlab.com/#{SecureRandom.alphanumeric(493)}" + ]).for(:links_to_spam) + } + + it { + is_expected.not_to allow_value([ + "https://gitlab.com/#{SecureRandom.alphanumeric(494)}" + ]).for(:links_to_spam) + } + end + + describe 'before_validation' do + context 'when links to spam contains empty strings' do + let(:report) { create(:abuse_report, links_to_spam: ['', 'https://gitlab.com']) } + + it 'removes empty strings' do + expect(report.links_to_spam).to match_array(['https://gitlab.com']) + end + end end describe '#remove_user' do diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index be64d72e031..c5e28337139 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' -RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do +RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching, +feature_category: :kubernetes_management do include ReactiveCachingHelpers include KubernetesHelpers diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index f8b7c840249..9fe8b960c8b 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -4575,30 +4575,12 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev end describe 'transition to merged' do - context 'when reset_merge_error_on_transition feature flag is on' do - before do - stub_feature_flags(reset_merge_error_on_transition: true) - end - - it 'resets the merge error' do - subject.update!(merge_error: 'temp') - - expect { subject.mark_as_merged }.to change { subject.merge_error.present? } - .from(true) - .to(false) - end - end + it 'resets the merge error' do + subject.update!(merge_error: 'temp') - context 'when reset_merge_error_on_transition feature flag is off' do - before do - stub_feature_flags(reset_merge_error_on_transition: false) - end - - it 'does not reset the merge error' do - subject.update!(merge_error: 'temp') - - expect { subject.mark_as_merged }.not_to change { subject.merge_error.present? } - end + expect { subject.mark_as_merged }.to change { subject.merge_error.present? } + .from(true) + .to(false) end end diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb index 918ff6aa154..b0088e44087 100644 --- a/spec/models/namespace/traversal_hierarchy_spec.rb +++ b/spec/models/namespace/traversal_hierarchy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Namespace::TraversalHierarchy, type: :model do +RSpec.describe Namespace::TraversalHierarchy, type: :model, feature_category: :subgroups do let!(:root) { create(:group, :with_hierarchy) } describe '.for_namespace' do diff --git a/spec/scripts/lib/glfm/update_example_snapshots_spec.rb b/spec/scripts/lib/glfm/update_example_snapshots_spec.rb index 58e016b6d68..bfc25877f98 100644 --- a/spec/scripts/lib/glfm/update_example_snapshots_spec.rb +++ b/spec/scripts/lib/glfm/update_example_snapshots_spec.rb @@ -28,7 +28,7 @@ require_relative '../../../../scripts/lib/glfm/update_example_snapshots' # Also, the textual content of the individual fixture file entries is also crafted to help # indicate which scenarios which they are covering. # rubocop:disable RSpec/MultipleMemoizedHelpers -RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do +RSpec.describe Glfm::UpdateExampleSnapshots, '#process', feature_category: :team_planning do subject { described_class.new } # GLFM input files diff --git a/spec/scripts/lib/glfm/update_specification_spec.rb b/spec/scripts/lib/glfm/update_specification_spec.rb index ed5650e7310..92434b37515 100644 --- a/spec/scripts/lib/glfm/update_specification_spec.rb +++ b/spec/scripts/lib/glfm/update_specification_spec.rb @@ -26,7 +26,7 @@ require_relative '../../../support/helpers/next_instance_of' # should run in sub-second time when the Spring pre-loader is used. This allows # logic which is not directly related to the slow sub-processes to be TDD'd with a # very rapid feedback cycle. -RSpec.describe Glfm::UpdateSpecification, '#process' do +RSpec.describe Glfm::UpdateSpecification, '#process', feature_category: :team_planning do include NextInstanceOf subject { described_class.new } diff --git a/spec/services/git/wiki_push_service_spec.rb b/spec/services/git/wiki_push_service_spec.rb index 878a5c4ccf0..b076b2d51ef 100644 --- a/spec/services/git/wiki_push_service_spec.rb +++ b/spec/services/git/wiki_push_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Git::WikiPushService, services: true do +RSpec.describe Git::WikiPushService, services: true, feature_category: :wiki do include RepoHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 38ab7b6e2ee..97a3b338069 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ImportService do +RSpec.describe Projects::ImportService, feature_category: :importers do let!(:project) { create(:project) } let(:user) { project.creator } diff --git a/spec/tasks/gitlab/check_rake_spec.rb b/spec/tasks/gitlab/check_rake_spec.rb index aee03059120..74cc5dd6d7c 100644 --- a/spec/tasks/gitlab/check_rake_spec.rb +++ b/spec/tasks/gitlab/check_rake_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -RSpec.describe 'check.rake', :silence_stdout do +RSpec.describe 'check.rake', :silence_stdout, feature_category: :gitaly do before do Rake.application.rake_require 'tasks/gitlab/check' diff --git a/spec/tasks/gitlab/seed/group_seed_rake_spec.rb b/spec/tasks/gitlab/seed/group_seed_rake_spec.rb index 2f57e875f5f..43351031414 100644 --- a/spec/tasks/gitlab/seed/group_seed_rake_spec.rb +++ b/spec/tasks/gitlab/seed/group_seed_rake_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -RSpec.describe 'gitlab:seed:group_seed rake task', :silence_stdout do +RSpec.describe 'gitlab:seed:group_seed rake task', :silence_stdout, feature_category: :subgroups do let(:username) { 'group_seed' } let!(:user) { create(:user, username: username) } let(:task_params) { [2, username] } diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb index 38a031178ae..a2546b8d033 100644 --- a/spec/tasks/gitlab/storage_rake_spec.rb +++ b/spec/tasks/gitlab/storage_rake_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -RSpec.describe 'rake gitlab:storage:*', :silence_stdout do +RSpec.describe 'rake gitlab:storage:*', :silence_stdout, feature_category: :pods do before do Rake.application.rake_require 'tasks/gitlab/storage' diff --git a/spec/tasks/gitlab/usage_data_rake_spec.rb b/spec/tasks/gitlab/usage_data_rake_spec.rb index 95ebaf6ea24..4e84018d5ff 100644 --- a/spec/tasks/gitlab/usage_data_rake_spec.rb +++ b/spec/tasks/gitlab/usage_data_rake_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -RSpec.describe 'gitlab:usage data take tasks', :silence_stdout do +RSpec.describe 'gitlab:usage data take tasks', :silence_stdout, feature_category: :service_ping do include StubRequests include UsageDataHelpers diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb index 6b5985a2a8a..4255e16b0e4 100644 --- a/spec/tasks/gitlab/workhorse_rake_spec.rb +++ b/spec/tasks/gitlab/workhorse_rake_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -RSpec.describe 'gitlab:workhorse namespace rake task', :silence_stdout do +RSpec.describe 'gitlab:workhorse namespace rake task', :silence_stdout, feature_category: :source_code_management do before :all do Rake.application.rake_require 'tasks/gitlab/workhorse' end diff --git a/yarn.lock b/yarn.lock index 94da9aa70fc..b6be99348ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1126,10 +1126,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984" integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q== -"@gitlab/fonts@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@gitlab/fonts/-/fonts-1.1.0.tgz#141db7d7c8e72113307614932d64103d3a68668c" - integrity sha512-5XNDJke0ElrMRG5iLWOirFRSLQhf9oNpPx5uie1j2l8QU6RLkMAE0nXG6cwJ82mgAV49ZcqLXXSopWvuUg8Y9Q== +"@gitlab/fonts@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@gitlab/fonts/-/fonts-1.1.2.tgz#b557dcc9b5f266934c024d39c500ffb58aea0feb" + integrity sha512-//wtklHUWO6AMctAGnuZq4MRx0khaoSjVgVsKBlTrZT5+iW2X7P+uXrfgfdQ1QW7bepjMVKFUBUDjaXTFLtrSw== "@gitlab/stylelint-config@4.1.0": version "4.1.0" |