Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-30 00:09:01 +00:00
parent 8308674afc
commit 2fdee6d838
41 changed files with 668 additions and 68 deletions

View File

@ -293,7 +293,7 @@
- name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:6.0-alpine
- name: elasticsearch:8.4.1
- name: elasticsearch:8.5.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "12"

View File

@ -1,35 +0,0 @@
---
# Cop supports --autocorrect.
RSpec/EmptyLineAfterExampleGroup:
Exclude:
- 'ee/spec/controllers/groups/clusters_controller_spec.rb'
- 'ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
- 'ee/spec/features/security/group/private_access_spec.rb'
- 'ee/spec/lib/gitlab/vulnerabilities/container_scanning_vulnerability_spec.rb'
- 'ee/spec/services/ee/gpg_keys/create_service_spec.rb'
- 'ee/spec/services/ee/issues/create_from_vulnerability_data_service_spec.rb'
- 'ee/spec/services/vulnerabilities/confirm_service_spec.rb'
- 'ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
- 'ee/spec/services/vulnerabilities/resolve_service_spec.rb'
- 'ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb'
- 'ee/spec/services/vulnerability_issue_links/create_service_spec.rb'
- 'ee/spec/services/vulnerability_issue_links/delete_service_spec.rb'
- 'spec/controllers/explore/projects_controller_spec.rb'
- 'spec/controllers/projects/notes_controller_spec.rb'
- 'spec/factories/projects/ci_feature_usages.rb'
- 'spec/features/security/group/internal_access_spec.rb'
- 'spec/features/security/group/private_access_spec.rb'
- 'spec/features/security/group/public_access_spec.rb'
- 'spec/helpers/blob_helper_spec.rb'
- 'spec/helpers/git_helper_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
- 'spec/lib/gitlab/blob_helper_spec.rb'
- 'spec/lib/gitlab/file_type_detection_spec.rb'
- 'spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- 'spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb'
- 'spec/models/note_spec.rb'
- 'spec/models/project_feature_spec.rb'
- 'spec/models/user_spec.rb'
- 'spec/models/zoom_meeting_spec.rb'
- 'spec/requests/api/projects_spec.rb'
- 'spec/routing/project_routing_spec.rb'

View File

@ -16,3 +16,5 @@ module PreferredLanguageSwitcher
Gitlab::CurrentSettings.default_preferred_language
end
end
PreferredLanguageSwitcher.prepend_mod

View File

@ -6,6 +6,13 @@ class ProjectExportJob < ApplicationRecord
validates :project, :jid, :status, presence: true
STATUS = {
queued: 0,
started: 1,
finished: 2,
failed: 3
}.freeze
state_machine :status, initial: :queued do
event :start do
transition [:queued] => :started
@ -19,9 +26,9 @@ class ProjectExportJob < ApplicationRecord
transition [:queued, :started] => :failed
end
state :queued, value: 0
state :started, value: 1
state :finished, value: 2
state :failed, value: 3
state :queued, value: STATUS[:queued]
state :started, value: STATUS[:started]
state :finished, value: STATUS[:finished]
state :failed, value: STATUS[:failed]
end
end

View File

@ -4,8 +4,6 @@ module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline, :logger
CreateError = Class.new(StandardError)
LOG_MAX_DURATION_THRESHOLD = 3.seconds
LOG_MAX_PIPELINE_SIZE = 2_000
LOG_MAX_CREATION_THRESHOLD = 20.seconds

View File

@ -0,0 +1,98 @@
# frozen_string_literal: true
module Projects
module ImportExport
class ParallelExportService
def initialize(export_job, current_user, after_export_strategy)
@export_job = export_job
@current_user = current_user
@after_export_strategy = after_export_strategy
@shared = project.import_export_shared
@logger = Gitlab::Export::Logger.build
end
def execute
log_info('Parallel project export started')
if save_exporters && save_export_archive
log_info('Parallel project export finished successfully')
execute_after_export_action(after_export_strategy)
else
notify_error
end
ensure
cleanup
end
private
attr_reader :export_job, :current_user, :after_export_strategy, :shared, :logger
delegate :project, to: :export_job
def execute_after_export_action(after_export_strategy)
return if after_export_strategy.execute(current_user, project)
notify_error
end
def exporters
[version_saver, exported_relations_merger]
end
def save_exporters
exporters.all? do |exporter|
log_info("Parallel project export - #{exporter.class.name} saver started")
exporter.save
end
end
def save_export_archive
Gitlab::ImportExport::Saver.save(exportable: project, shared: shared)
end
def version_saver
@version_saver ||= Gitlab::ImportExport::VersionSaver.new(shared: shared)
end
def exported_relations_merger
@relation_saver ||= Gitlab::ImportExport::Project::ExportedRelationsMerger.new(
export_job: export_job,
shared: shared)
end
def cleanup
FileUtils.rm_rf(shared.export_path) if File.exist?(shared.export_path)
FileUtils.rm_rf(shared.archive_path) if File.exist?(shared.archive_path)
end
def log_info(message)
logger.info(
message: message,
**log_base_data
)
end
def notify_error
logger.error(
message: 'Parallel project export error',
export_errors: shared.errors.join(', '),
export_job_id: export_job.id,
**log_base_data
)
NotificationService.new.project_not_exported(project, current_user, shared.errors)
end
def log_base_data
{
project_id: project.id,
project_name: project.name,
project_path: project.full_path
}
end
end
end
end

View File

@ -2,7 +2,7 @@
- ref = local_assigns.fetch(:ref, @ref)
- form_path = local_assigns.fetch(:form_path, switch_project_refs_path(@project))
- dropdown_toggle_text = @id || @project.default_branch
- dropdown_toggle_text = ref || @project.default_branch
- field_name = local_assigns.fetch(:field_name, 'ref')
= form_tag form_path, method: :get, class: "project-refs-form" do

View File

@ -3009,6 +3009,15 @@
:weight: 1
:idempotent: false
:tags: []
- :name: projects_import_export_parallel_project_export
:worker_name: Projects::ImportExport::ParallelProjectExportWorker
:feature_category: :importers
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :memory
:weight: 1
:idempotent: true
:tags: []
- :name: projects_import_export_relation_export
:worker_name: Projects::ImportExport::RelationExportWorker
:feature_category: :importers

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
module Projects
module ImportExport
class ParallelProjectExportWorker
include ApplicationWorker
include ExceptionBacktrace
idempotent!
data_consistency :always
deduplicate :until_executed
feature_category :importers
worker_resource_boundary :memory
urgency :low
loggable_arguments 1, 2
sidekiq_options retries: 3, dead: false, status_expiration: StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION
sidekiq_retries_exhausted do |job, exception|
export_job = ProjectExportJob.find(job['args'].first)
export_job.fail_op!
project = export_job.project
log_payload = {
message: 'Parallel project export error',
export_error: job['error_message'],
project_export_job_id: export_job.id,
project_name: project.name,
project_id: project.id
}
Gitlab::ExceptionLogFormatter.format!(exception, log_payload)
Gitlab::Export::Logger.error(log_payload)
end
def perform(project_export_job_id, user_id, after_export_strategy = {})
export_job = ProjectExportJob.find(project_export_job_id)
return if export_job.finished?
export_job.update_attribute(:jid, jid)
current_user = User.find(user_id)
after_export = build!(after_export_strategy)
export_service = ::Projects::ImportExport::ParallelExportService.new(export_job, current_user, after_export)
export_service.execute
export_job.finish!
rescue Gitlab::ImportExport::AfterExportStrategyBuilder::StrategyNotFoundError
export_job.fail_op!
end
private
def build!(after_export_strategy)
strategy_klass = after_export_strategy&.delete('klass')
Gitlab::ImportExport::AfterExportStrategyBuilder.build!(strategy_klass, after_export_strategy)
end
end
end
end

View File

@ -405,6 +405,8 @@
- 1
- - projects_git_garbage_collect
- 1
- - projects_import_export_parallel_project_export
- 1
- - projects_import_export_relation_export
- 1
- - projects_inactive_projects_deletion_notification

View File

@ -29,7 +29,7 @@ POST /bulk_imports
| `entities[source_full_path]` | String | yes | Source full path of the entity to import. |
| `entities[destination_name]` | String | yes | Deprecated: Use :destination_slug instead. Destination slug for the entity. |
| `entities[destination_slug]` | String | yes | Destination slug for the entity. |
| `entities[destination_namespace]` | String | no | Destination namespace for the entity. |
| `entities[destination_namespace]` | String | yes | Destination namespace for the entity. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/bulk_imports" \

View File

@ -0,0 +1,248 @@
---
stage: Package
group: Package Registry
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Publish packages with Yarn
Publish npm packages in your project's Package Registry using Yarn. Then install the
packages whenever you need to use them as a dependency.
Learn how to build a [yarn](../workflows/build_packages.md#yarn) package.
You can get started with Yarn 2 by following the [Yarn documentation](https://yarnpkg.com/getting-started/install/).
## Publish to GitLab Package Registry
### Authentication to the Package Registry
You need a token to publish a package. Different tokens are available depending on what you're trying to
achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
- If your organization uses two-factor authentication (2FA), you must use a personal access token with the scope set to `api`.
- If you publish a package via CI/CD pipelines, you must use a CI job token.
Create a token and save it to use later in the process.
### Naming convention
Depending on how you install the package, you may need to adhere to the naming convention.
You can use one of two API endpoints to install packages:
- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
- **Project-level**: Use when you have a few npm packages, and they are not in the same GitLab group.
If you plan to install a package through the [project level](#install-from-the-project-level), you do not have to
adhere to the naming convention.
If you plan to install a package through the [instance level](#install-from-the-instance-level), then you must name
your package with a [scope](https://docs.npmjs.com/misc/scope/). Scoped packages begin with a `@` and have the
`@owner/package-name` format. You can set up the scope for your package in the `.yarnrc.yml` file and by using the
`publishConfig` option in the `package.json`.
- The value used for the `@scope` is the root of the project that hosts the packages and not the root
of the project with the package's source code. The scope should be lowercase.
- The package name can be anything you want
| Project URL | Package Registry in | Scope | Full package name |
| ------------------------------------------------------- | ------------------- | --------- | ---------------------- |
| `https://gitlab.com/my-org/engineering-group/analytics` | Analytics | `@my-org` | `@my-org/package-name` |
### Configuring `.yarnrc.yml` to publish from the project level
To publish with the project-level npm endpoint, set the following configuration in
`.yarnrc.yml`:
```yaml
npmScopes:
foo:
npmRegistryServer: 'https://<your_domain>/api/v4/projects/<your_project_id>/packages/npm/'
npmPublishRegistry: 'https://<your_domain>/api/v4/projects/<your_project_id>/packages/npm/'
npmRegistries:
//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:
npmAlwaysAuth: true
npmAuthToken: '<your_token>'
```
In this configuration:
- Replace `<your_domain>` with your domain name.
- Replace `<your_project_id>` with your project's ID, which you can find on the project's home page.
- Replace `<your_token>` with a deploy token, group access token, project access token, or personal access token.
### Configuring `.yarnrc.yml` to publish from the instance level
For the instance-level npm endpoint, use this Yarn 2 configuration in `.yarnrc.yml`:
```yaml
npmScopes:
<scope>:
npmRegistryServer: 'https://<your_domain>/api/v4/packages/npm/'
npmRegistries:
//gitlab.example.com/api/v4/packages/npm/:
npmAlwaysAuth: true
npmAuthToken: '<your_token>'
```
In this configuration:
- Replace `<your_domain>` with your domain name.
- Your scope is `<scope>`, without `@`.
- Replace `<your_token>` with a deploy token, group access token, project access token, or personal access token.
### Publishing a package via the command line
Publish a package:
```shell
npm publish
```
Your package should now publish to the Package Registry.
### Publishing via a CI/CD pipeline
In the GitLab project that houses your `yarnrc.yml`, edit or create a `.gitlab-ci.yml` file. For example:
```yaml
image: node:latest
stages:
- deploy
deploy:
stage: deploy
script:
- npm publish
```
Your package should now publish to the Package Registry when the pipeline runs.
## Install a package
If multiple packages have the same name and version, the most recently-published package is retrieved when you install a package.
You can install a package from a GitLab project or instance:
- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
- **Project-level**: Use when you have a few npm packages, and they are not in the same GitLab group.
### Install from the instance level
WARNING:
You must use packages published with the scoped [naming convention](#naming-convention) when you install a package from the instance level.
1. Authenticate to the Package Registry
If you install a package from a private project, you must authenticate to the Package Registry. Skip this step if the project is not private.
```shell
npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
```
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
1. Set the registry
```shell
npm config set @scope:registry https://your_domain_name.com/api/v4/packages/npm/
```
- Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
1. Install the package
```shell
yarn add @scope/my-package
```
### Install from the project level
1. Authenticate to the Package Registry
If you install a package from a private project, you must authenticate to the Package Registry. Skip this step if the project is not private.
```shell
npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
```
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_project_id` is your project ID, found on the project's home page.
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
1. Set the registry
```shell
npm config set @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/
```
- Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from.
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
- Replace `your_project_id` is your project ID, found on the project's home page.
1. Install the package
```shell
yarn add @scope/my-package
```
## Helpful hints
For full helpful hints information, refer to the [npm documentation](../npm_registry/index.md#helpful-hints).
### Supported CLI commands
The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
(`yarn`):
- `npm install`: Install npm packages.
- `npm publish`: Publish an npm package to the registry.
- `npm dist-tag add`: Add a dist-tag to an npm package.
- `npm dist-tag ls`: List dist-tags for a package.
- `npm dist-tag rm`: Delete a dist-tag.
- `npm ci`: Install npm packages directly from your `package-lock.json` file.
- `npm view`: Show package metadata.
- `yarn add`: Install an npm package.
- `yarn update`: Update your dependencies.
## Troubleshooting
For full troubleshooting information, refer to the [npm documentation](../npm_registry/index.md#troubleshooting).
### Error running Yarn with the Package Registry for the npm registry
If you are using [Yarn](https://classic.yarnpkg.com/en/) with the npm registry, you may get
an error message like:
```shell
yarn install v1.15.2
warning package.json: No license field
info No lockfile found.
warning XXX: No license field
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
error An unexpected error occurred: "https://gitlab.example.com/api/v4/projects/XXX/packages/npm/XXX/XXX/-/XXX/XXX-X.X.X.tgz: Request failed \"404 Not Found\"".
info If you think this is a bug, please open a bug report with the information provided in "/Users/XXX/gitlab-migration/module-util/yarn-error.log".
info Visit https://classic.yarnpkg.com/en/docs/cli/install for documentation about this command
```
In this case, try adding this to your `.npmrc` file (and replace `<your_token>`
with your personal access token or deploy token):
```plaintext
//gitlab.example.com/api/v4/projects/:_authToken=<your_token>
```
You can also use `yarn config` instead of `npm config` when setting your auth-token dynamically:
```shell
yarn config set '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>"
yarn config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
```

View File

@ -76,7 +76,7 @@ For each project and group you can select one of the following levels:
| Participate | Receive notifications for threads you have participated in. |
| On mention | Receive notifications when you are [mentioned](../discussions/index.md#mentions) in a comment. |
| Disabled | Receive no notifications. |
| Custom | Receive notifications for selected events. |
| Custom | Receive notifications for selected events and threads you have participated in. |
### Global notification settings

View File

@ -214,19 +214,3 @@ Prerequisites:
- A deploy token with `read_registry` and `write_registry` scopes.
Follow the dependency proxy [authentication instructions](../../packages/dependency_proxy/index.md).
## Troubleshooting
### Error: `api error: Repository or object not found:`
When using a group deploy token to clone from LFS objects, you might get `404 Not Found` responses
and this error message. This occurs because of a bug, documented in
[issue 235398](https://gitlab.com/gitlab-org/gitlab/-/issues/235398).
```plaintext
api error: Repository or object not found:
https://<URL-with-token>.git/info/lfs/objects/batch
Check that it exists and that you have proper access to it
```
The workaround is to use a project deploy token.

View File

@ -22032,6 +22032,9 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
msgid "Integrations|Enable slash commands and notifications for a Slack workspace."
msgstr ""
msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
msgstr ""

View File

@ -245,9 +245,9 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
it 'expands multiple queue groups correctly' do
expected_workers =
if Gitlab.ee?
[%w[chat_notification], %w[project_export projects_import_export_relation_export project_template_export]]
[%w[chat_notification], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export project_template_export]]
else
[%w[chat_notification], %w[project_export projects_import_export_relation_export]]
[%w[chat_notification], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export]]
end
expect(Gitlab::SidekiqCluster)

View File

@ -101,6 +101,7 @@ RSpec.describe Explore::ProjectsController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when topic exists' do
before do
create(:topic, name: 'topic1')

View File

@ -757,6 +757,7 @@ RSpec.describe Projects::NotesController do
expect { put :update, params: request_params }.to change { note.reload.note }
end
end
context "doesnt update the note" do
let(:issue) { create(:issue, :confidential, project: project) }
let(:note) { create(:note, noteable: issue, project: project) }

View File

@ -4,5 +4,21 @@ FactoryBot.define do
factory :project_export_job do
project
jid { SecureRandom.hex(8) }
trait :queued do
status { ProjectExportJob::STATUS[:queued] }
end
trait :started do
status { ProjectExportJob::STATUS[:started] }
end
trait :finished do
status { ProjectExportJob::STATUS[:finished] }
end
trait :failed do
status { ProjectExportJob::STATUS[:failed] }
end
end
end

View File

@ -4,6 +4,7 @@ FactoryBot.define do
factory :project_ci_feature_usage, class: 'Projects::CiFeatureUsage' do
project factory: :project
feature { :code_coverage } # rubocop: disable RSpec/EmptyExampleGroup
default_branch { false }
end
end

View File

@ -27,9 +27,11 @@ RSpec.describe 'Internal Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -47,9 +49,11 @@ RSpec.describe 'Internal Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -69,9 +73,11 @@ RSpec.describe 'Internal Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -89,9 +95,11 @@ RSpec.describe 'Internal Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -109,9 +117,11 @@ RSpec.describe 'Internal Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }

View File

@ -27,9 +27,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -47,9 +49,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -69,9 +73,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -89,9 +95,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -109,9 +117,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
@ -135,9 +145,11 @@ RSpec.describe 'Private Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }

View File

@ -27,9 +27,11 @@ RSpec.describe 'Public Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -47,9 +49,11 @@ RSpec.describe 'Public Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -69,9 +73,11 @@ RSpec.describe 'Public Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -89,9 +95,11 @@ RSpec.describe 'Public Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_allowed_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
@ -109,9 +117,11 @@ RSpec.describe 'Public Group access', feature_category: :permissions do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed_for(:admin) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_denied_for(:admin) }
end
it { is_expected.to be_allowed_for(:owner).of(group) }
it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }

View File

@ -80,6 +80,7 @@ RSpec.describe BlobHelper do
end
end
end
context 'viewer related' do
include FakeBlobHelpers

View File

@ -15,11 +15,13 @@ RSpec.describe GitHelper do
it { expect(strip_signature).to eq("Version 1.69.0\n\n") }
end
context 'strips PGP MESSAGE' do
let(:strip_signature) { helper.strip_signature( pgp_message_tag ) }
it { expect(strip_signature).to eq("Version 1.69.0\n\n") }
end
context 'strips SIGNED MESSAGE' do
let(:strip_signature) { helper.strip_signature( x509_message_tag ) }

View File

@ -96,7 +96,7 @@ RSpec.describe 'lograge', type: :request do
skip_memory_instrumentation!
end
it 'logs memory usage metrics' do
it 'logs memory usage metrics', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384081' do
expect(Lograge.formatter).to receive(:call)
.with(a_hash_including(:mem_objects))
.and_call_original

View File

@ -89,6 +89,7 @@ RSpec.describe BulkImports::Projects::Pipelines::IssuesPipeline do
expect(award_emoji.user).to eq(user)
end
end
context 'issue state' do
let(:issue_attributes) { { 'state' => 'closed' } }

View File

@ -68,6 +68,7 @@ RSpec.describe Gitlab::BlobHelper do
expect(blob.image?).to be_falsey
end
end
context 'with a .webp file' do
it 'returns true' do
expect(webp_blob.image?).to be_truthy

View File

@ -31,6 +31,7 @@ RSpec.describe Gitlab::FileTypeDetection do
expect(described_class.extension_match?('my/file.foo', extensions)).to eq(true)
end
end
context 'when class is an uploader' do
let(:uploader) do
example_uploader = Class.new(CarrierWave::Uploader::Base) do

View File

@ -129,7 +129,7 @@ RSpec.describe Gitlab::InstrumentationHelper do
skip_memory_instrumentation!
end
it 'logs memory usage metrics' do
it 'logs memory usage metrics', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384081' do
subject
expect(payload).to include(

View File

@ -10,7 +10,7 @@ RSpec.describe Gitlab::Memory::Instrumentation do
end
describe '.available?' do
it 'returns true' do
it 'returns true', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384081' do
expect(described_class).to be_available
end
end
@ -18,7 +18,7 @@ RSpec.describe Gitlab::Memory::Instrumentation do
describe '.start_thread_memory_allocations' do
subject { described_class.start_thread_memory_allocations }
it 'a hash is returned' do
it 'a hash is returned', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384081' do
is_expected.to be_a(Hash)
end
@ -47,7 +47,7 @@ RSpec.describe Gitlab::Memory::Instrumentation do
expect(described_class).to receive(:measure_thread_memory_allocations).and_call_original
end
it 'a hash is returned' do
it 'a hash is returned', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/384081' do
result = subject
expect(result).to include(
mem_objects: be > 1000,

View File

@ -452,6 +452,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
expect(subject).to eq("current_rss(#{rss}) > soft_limit_rss(#{soft_limit}) longer than GRACE_BALLOON_SECONDS(#{grace_balloon_seconds})")
end
end
context 'deadline not exceeded' do
let(:deadline_exceeded) { false }

View File

@ -237,6 +237,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
expect(subject.set_token(instance, 'my-value')).to eq 'my-value'
end
end
context 'when encryption is optional' do
let(:options) { { encrypted: :optional } }

View File

@ -579,6 +579,7 @@ RSpec.describe Note do
expect(commit_note.confidential?).to be_falsy
end
end
context 'when note is confidential' do
it 'is true even when a noteable is not confidential' do
issue = create(:issue, confidential: false)

View File

@ -291,6 +291,7 @@ RSpec.describe ProjectFeature do
end
end
end
# rubocop:disable Gitlab/FeatureAvailableUsage
describe '#feature_available?' do
let(:features) { ProjectFeature::FEATURES }

View File

@ -3217,6 +3217,7 @@ RSpec.describe User do
expect(described_class.find_by_full_path('unknown')).to eq(nil)
end
end
context 'with the follow_redirects option set to true' do
it 'returns nil' do
expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil)

View File

@ -29,6 +29,7 @@ RSpec.describe ZoomMeeting do
expect(meetings_added).not_to include(removed_meeting.id)
end
end
describe '.removed_from_issue' do
it 'gets only removed meetings' do
meetings_removed = described_class.removed_from_issue.pluck(:id)

View File

@ -4687,6 +4687,7 @@ RSpec.describe API::Projects do
end
end
end
describe 'PUT /projects/:id/transfer' do
context 'when authenticated as owner' do
let(:group) { create :group }

View File

@ -301,6 +301,7 @@ RSpec.describe 'project routing' do
expect(get('/gitlab/gitlabhq/-/merge_requests/1/conflicts')).to route_to('projects/merge_requests/conflicts#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
end
# raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
# project_snippets GET /:project_id/snippets(.:format) snippets#index
# new_project_snippet GET /:project_id/snippets/new(.:format) snippets#new

View File

@ -0,0 +1,98 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::ImportExport::ParallelExportService, feature_category: :importers do
let_it_be(:user) { create(:user) }
let(:export_job) { create(:project_export_job) }
let(:after_export_strategy) { Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy.new }
let(:project) { export_job.project }
before do
allow_next_instance_of(Gitlab::ImportExport::Project::ExportedRelationsMerger) do |saver|
allow(saver).to receive(:save).and_return(true)
end
allow_next_instance_of(Gitlab::ImportExport::VersionSaver) do |saver|
allow(saver).to receive(:save).and_return(true)
end
end
describe '#execute' do
subject(:service) { described_class.new(export_job, user, after_export_strategy) }
it 'creates a project export archive file' do
expect(Gitlab::ImportExport::Saver).to receive(:save)
.with(exportable: project, shared: project.import_export_shared)
service.execute
end
it 'logs export progress' do
allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
logger = service.instance_variable_get(:@logger)
messages = [
'Parallel project export started',
'Parallel project export - Gitlab::ImportExport::VersionSaver saver started',
'Parallel project export - Gitlab::ImportExport::Project::ExportedRelationsMerger saver started',
'Parallel project export finished successfully'
]
messages.each do |message|
expect(logger).to receive(:info).ordered.with(hash_including(message: message))
end
service.execute
end
it 'executes after export stragegy on export success' do
allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
expect(after_export_strategy).to receive(:execute)
service.execute
end
it 'ensures files are cleaned up' do
shared = project.import_export_shared
FileUtils.mkdir_p(shared.archive_path)
FileUtils.mkdir_p(shared.export_path)
allow(Gitlab::ImportExport::Saver).to receive(:save).and_raise(StandardError)
expect { service.execute }.to raise_error(StandardError)
expect(File.exist?(shared.export_path)).to eq(false)
expect(File.exist?(shared.archive_path)).to eq(false)
end
context 'when export fails' do
it 'notifies the error to the user' do
allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(false)
allow(project.import_export_shared).to receive(:errors).and_return(['Error'])
expect_next_instance_of(NotificationService) do |instance|
expect(instance).to receive(:project_not_exported).with(project, user, ['Error'])
end
service.execute
end
end
context 'when after export stragegy fails' do
it 'notifies the error to the user' do
allow(Gitlab::ImportExport::Saver).to receive(:save).and_return(true)
allow(after_export_strategy).to receive(:execute).and_return(false)
allow(project.import_export_shared).to receive(:errors).and_return(['Error'])
expect_next_instance_of(NotificationService) do |instance|
expect(instance).to receive(:project_not_exported).with(project, user, ['Error'])
end
service.execute
end
end
end
end

View File

@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::ImportExport::ParallelProjectExportWorker, feature_category: :importers do
let_it_be(:user) { create(:user) }
let(:export_job) { create(:project_export_job, :started) }
let(:after_export_strategy) { {} }
let(:job_args) { [export_job.id, user.id, after_export_strategy] }
before do
allow_next_instance_of(described_class) do |job|
allow(job).to receive(:jid) { SecureRandom.hex(8) }
end
end
describe '#perform' do
it_behaves_like 'an idempotent worker' do
it 'sets the export job status to finished' do
subject
expect(export_job.reload.finished?).to eq(true)
end
end
context 'when after export strategy does not exist' do
let(:after_export_strategy) { { 'klass' => 'InvalidStrategy' } }
it 'sets the export job status to failed' do
described_class.new.perform(*job_args)
expect(export_job.reload.failed?).to eq(true)
end
end
end
describe '.sidekiq_retries_exhausted' do
let(:job) { { 'args' => job_args, 'error_message' => 'Error message' } }
it 'sets export_job status to failed' do
described_class.sidekiq_retries_exhausted_block.call(job)
expect(export_job.reload.failed?).to eq(true)
end
it 'logs an error message' do
expect_next_instance_of(Gitlab::Export::Logger) do |logger|
expect(logger).to receive(:error).with(
hash_including(
message: 'Parallel project export error',
export_error: 'Error message'
)
)
end
described_class.sidekiq_retries_exhausted_block.call(job)
end
end
end