Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-12-05 03:13:51 +00:00
parent 35850d3d16
commit c20abe491c
15 changed files with 365 additions and 90 deletions

View File

@ -1 +1 @@
251acb8c75aa66481893bf775a44cba669ccc3c9
c0b07ba36fc8fc40830f061cb55a5c951a166e1c

View File

@ -1,6 +1,7 @@
<script>
import { GlButton, GlSprintf } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { visitUrl } from '~/lib/utils/url_utility';
import { STATUS_MERGED } from '~/issues/constants';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status';
@ -114,6 +115,13 @@ export default {
return this.userHasApproved && !this.userCanApprove && this.mr.state !== STATUS_MERGED;
},
approvalText() {
// Repeating a text of this to keep i18n easier to do (vs, construcing a compound string)
if (this.requireSamlAuthToApprove) {
return this.isApproved && this.approvedBy.length > 0
? s__('mrWidget|Approve additionally with SAML')
: s__('mrWidget|Approve with SAML');
}
return this.isApproved && this.approvedBy.length > 0
? s__('mrWidget|Approve additionally')
: s__('mrWidget|Approve');
@ -161,14 +169,20 @@ export default {
.join(', ')
.concat('.');
},
requireSamlAuthToApprove() {
return this.mr.requireSamlAuthToApprove;
},
},
methods: {
approve() {
if (this.requireSamlAuthToApprove) {
this.approveWithSamlAuth();
return;
}
if (this.requirePasswordToApprove) {
this.$root.$emit(BV_SHOW_MODAL, this.modalId);
return;
}
this.updateApproval(
() => this.service.approveMergeRequest(),
() =>
@ -179,6 +193,10 @@ export default {
),
);
},
approveWithSamlAuth() {
// Intentionally direct to SAML Identity Provider for renewed authorization even if SSO session exists
visitUrl(this.mr.samlApprovalPath);
},
approveWithAuth(data) {
this.updateApproval(
() => this.service.approveMergeRequestWithAuth(data),

View File

@ -35,7 +35,7 @@ To create a new user account with auditor access (or change an existing user):
To create a user account with auditor access:
1. On the left sidebar, at the bottom, select **Admin Area**.
1. On the left sidebar, select **Overview > Users**.
1. Select **Overview > Users**.
1. Create a new user or edit an existing one. Set **Access Level** to **Auditor**.
1. If you created a user, select **Create user**. For an existing user, select **Save changes**.

View File

@ -36,6 +36,174 @@ methods to export or back up your data yourself from GitLab.com.
Issues are stored in the database, and can't be stored in Git itself.
## GitLab backup archive creation process
When working with GitLab backups, you might need to know how GitLab creates backup archives. To create backup archives, GitLab:
1. If creating an incremental backup, extracts the previous backup archive and read its `backup_information.yml` file.
1. Updates or generates the `backup_information.yml` file.
1. Runs all backup sub-tasks:
- `db` to backup the GitLab PostgreSQL database (not Gitaly Cluster).
- `repositories` to back up Git repositories.
- `uploads` to back up attachments.
- `builds` to back up CI job output logs.
- `artifacts` to back up CI job artifacts.
- `pages` to back up page content.
- `lfs` to back up LFS objects.
- `terraform_state` to back up Terraform states.
- `registry` to back up container registry images.
- `packages` to back up packages.
- `ci_secure_files` to back up project-level secure files.
1. Archives the backup staging area into a tar file.
1. Optional. Uploads the new backup archive to object-storage.
1. Cleans up backup staging directory files that are now archived.
## Backup staging directory
The backup staging directory is a place to temporarily:
- Store backup artifacts on disk before the GitLab backup archive is created.
- Extract backup archives on disk before restoring a backup or creating an incremental backup.
This directory is the same directory where completed GitLab backup archives are created. When creating an untarred backup, the backup artifacts are left in this directory and no
archive is created.
Example backup staging directory with untarred backup:
```plaintext
backups/
├── 1701728344_2023_12_04_16.7.0-pre_gitlab_backup.tar
├── 1701728447_2023_12_04_16.7.0-pre_gitlab_backup.tar
├── artifacts.tar.gz
├── backup_information.yml
├── builds.tar.gz
├── ci_secure_files.tar.gz
├── db
│ ├── ci_database.sql.gz
│ └── database.sql.gz
├── lfs.tar.gz
├── packages.tar.gz
├── pages.tar.gz
├── repositories
│ ├── manifests/
│ ├── @hashed/
│ └── @snippets/
├── terraform_state.tar.gz
└── uploads.tar.gz
```
## `backup_information.yml` file
The `backup_information.yml` file saves all backup inputs that are not included in the backup itself. It includes information such as:
- The time the backup was created.
- The version of GitLab that generated the backup.
- Any options that were specified, such as skipped sub-tasks.
This information is used by some sub-tasks to determine how:
- To restore.
- To link data in the backup with external services (such as server-side repository backups).
This file is saved into the backup staging directory.
## Database backups
Database backups are created and restored by a GitLab backup sub-task called `db`. The database sub-task uses `pg_dump` to create [a SQL dump](https://www.postgresql.org/docs/14/backup-dump.html). The output of `pg_dump` is piped through `gzip` in order to create a compressed SQL file. This file is saved to the backup staging directory.
## Repository backups
Repository backups are created and restored by a GitLab backup sub-task called `repositories`. The repositories sub-task uses a Gitaly command
[`gitaly-backup`](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/gitaly-backup.md) to create Git repository backups:
- GitLab uses its database to tell `gitaly-backup` which repositories to back up.
- `gitaly-backup` then calls a series of RPCs on Gitaly to collect the repository backup data for each repository. This data is streamed into a directory structure in the GitLab backup staging directory.
```mermaid
sequenceDiagram
box Backup host
participant Repositories sub-task
participant gitaly-backup
end
Repositories sub-task->>+gitaly-backup: List of repositories
loop Each repository
gitaly-backup->>+Gitaly: ListRefs request
Gitaly->>-gitaly-backup: List of Git references
gitaly-backup->>+Gitaly: CreateBundleFromRefList request
Gitaly->>-gitaly-backup: Git bundle file
gitaly-backup->>+Gitaly: GetCustomHooks request
Gitaly->>-gitaly-backup: Custom hooks archive
end
gitaly-backup->>-Repositories sub-task: Success/failure
```
Storages configured to Gitaly Cluster are backed up the same as standalone Gitaly. When Gitaly Cluster receives the RPC calls from `gitaly-backup`, it is responsible for
rebuilding its own database. This means that there is no need to backup the Gitaly Cluster database separately. Because backups operate through RPCs, each repository is only backed
up once no matter the replication factor.
### Server-side repository backups
You can configure repository backups as server-side repository backups. When specified, `gitaly-backup` makes a single RPC call for each repository to create the backup. This RPC
does not transmit any repository data. Instead, the RPC triggers the Gitaly node that stores that physical repository to upload the backup data directly to object-storage. Because
the data is no longer transmitted through RPCs from Gitaly, server-side backups require much less network transfer and require no disk storage on the machine that is running the
backup Rake task. The backups stored on object-storage are linked to the created backup archive by [the backup name](backup_gitlab.md#backup-timestamp).
```mermaid
sequenceDiagram
box Backup host
participant Repositories sub-task
participant gitaly-backup
end
Repositories sub-task->>+gitaly-backup: List of repositories
loop Each repository
gitaly-backup->>+Gitaly: BackupRepository request
Gitaly->>+Object-storage: Git references file
Object-storage->>-Gitaly: Success/failure
Gitaly->>+Object-storage: Git bundle file
Object-storage->>-Gitaly: Success/failure
Gitaly->>+Object-storage: Custom hooks archive
Object-storage->>-Gitaly: Success/failure
Gitaly->>+Object-storage: Backup manifest file
Object-storage->>-Gitaly: Success/failure
Gitaly->>-gitaly-backup: Success/failure
end
gitaly-backup->>-Repositories sub-task: Success/failure
```
## File backups
The following GitLab backup sub-tasks back up files:
- `uploads`
- `builds`
- `artifacts`
- `pages`
- `lfs`
- `terraform_state`
- `registry`
- `packages`
- `ci_secure_files`
These file sub-tasks determine a set of files within a directory specific to the task. This set of files is then passed to `tar`
to create an archive. This archive is piped (not saved to disk) through `gzip` to save a compressed tar file to the backup staging directory.
Because backups are created from live instances, the files that tar is trying to archive can sometimes be modified while creating the backup. In this case, an alternate "copy"
strategy can be used. When this strategy is used, `rsync` is first used to create a copy of the files to back up. Then, these copies are passed to `tar` as usual. In this case,
the machine running the backup Rake task must have enough storage for the copied files and the compressed archive.
## Related features
- [Geo](../geo/index.md)

View File

@ -1542,50 +1542,3 @@ go_cloud_url = "s3://gitaly-backups?region=minio&endpoint=my.minio.local:8080&di
```
::EndTabs
## Configure negotiation timeouts
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/5574) in GitLab 16.5.
Gitaly supports configurable negotiation timeouts.
Negotiation timeouts can be configured for the `git-upload-pack(1)` and `git-upload-archive(1)`
operations, which are invoked by a Gitaly node when you execute the `git fetch` and
`git archive --remote` commands respectively. You might need to increase the negotiation timeout:
- For particularly large repositories.
- When performing these commands in parallel.
These timeouts affect only the [negotiation phase](https://git-scm.com/docs/pack-protocol/2.2.3#_packfile_negotiation) of
remote Git operations, not the entire transfer.
Valid values for timeouts follow the format of [`ParseDuration`](https://pkg.go.dev/time#ParseDuration) in Go.
How you configure negotiation timeouts depends on the type of installation you have:
::Tabs
:::TabTitle Linux package (Omnibus)
Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitaly['configuration'] = {
timeout: {
upload_pack_negotiation: '10m', # 10 minutes
upload_archive_negotiation: '20m', # 20 minutes
}
}
```
:::TabTitle Self-compiled (source)
Edit `/home/git/gitaly/config.toml`:
```toml
[timeout]
upload_pack_negotiation = "10m"
upload_archive_negotiation = "20m"
```
::EndTabs

View File

@ -6,23 +6,75 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Gitaly timeouts **(FREE SELF)**
[Gitaly](../gitaly/index.md) timeouts are configurable. The timeouts can be
configured to make sure that long-running Gitaly calls don't needlessly take up resources.
[Gitaly](../gitaly/index.md) provides two types of configurable timeouts:
To access Gitaly timeout settings:
- Call timeouts, configured by using the GitLab UI.
- Negotiation timeouts, configured by using Gitaly configuration files.
## Configure the call timeouts
Configure the following call timeouts to make sure that long-running Gitaly calls don't needlessly take up resources. To
configure the call timeouts:
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **Settings > Preferences**.
1. Expand the **Gitaly timeouts** section.
1. Set each timeout as required.
## Available timeouts
### Available call timeouts
The following timeouts are available.
Different call timeouts are available for different Gitaly operations.
| Timeout | Default | Description |
|:--------|:-----------|:------------|
| Default | 55 seconds | Timeout for most Gitaly calls (not enforced for `git` `fetch` and `push` operations, or Sidekiq jobs). For example, checking if a repository exists on disk. Makes sure that Gitaly calls made within a web request cannot exceed the entire request timeout. It should be shorter than the [worker timeout](../operations/puma.md#change-the-worker-timeout) that can be configured for [Puma](../../install/requirements.md#puma-settings). If a Gitaly call timeout exceeds the worker timeout, the remaining time from the worker timeout is used to avoid having to terminate the worker. |
| Fast | 10 seconds | Timeout for fast Gitaly operations used within requests, sometimes multiple times. For example, checking if a repository exists on disk. If fast operations exceed this threshold, there may be a problem with a storage shard. Failing fast can help maintain the stability of the GitLab instance. |
| Medium | 30 seconds | Timeout for Gitaly operations that should be fast (possibly within requests) but preferably not used multiple times within a request. For example, loading blobs. Timeout that should be set between Default and Fast. |
| Default | 55 seconds | Timeout for most Gitaly calls (not enforced for `git` `fetch` and `push` operations, or Sidekiq jobs). For example, checking if a repository exists on disk. Makes sure that Gitaly calls made in a web request cannot exceed the entire request timeout. It should be shorter than the [worker timeout](../operations/puma.md#change-the-worker-timeout) that can be configured for [Puma](../../install/requirements.md#puma-settings). If a Gitaly call timeout exceeds the worker timeout, the remaining time from the worker timeout is used to avoid having to terminate the worker. |
| Fast | 10 seconds | Timeout for fast Gitaly operations used in requests, sometimes multiple times. For example, checking if a repository exists on disk. If fast operations exceed this threshold, there may be a problem with a storage shard. Failing fast can help maintain the stability of the GitLab instance. |
| Medium | 30 seconds | Timeout for Gitaly operations that should be fast (possibly in requests) but preferably not used multiple times in a request. For example, loading blobs. Timeout that should be set between Default and Fast. |
You can also [configure negotiation timeouts](../gitaly/configure_gitaly.md#configure-negotiation-timeouts).
## Configure the negotiation timeouts
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/5574) in GitLab 16.5.
You might need to increase the negotiation timeout:
- For particularly large repositories.
- When performing these commands in parallel.
You can configure negotiation timeouts for:
- `git-upload-pack(1)`, which is invoked by a Gitaly node when you execute `git fetch`.
- `git-upload-archive(1)`, which is invoked by a Gitaly node when you execute `git archive --remote`.
To configure these timeouts:
::Tabs
:::TabTitle Linux package (Omnibus)
Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitaly['configuration'] = {
timeout: {
upload_pack_negotiation: '10m', # 10 minutes
upload_archive_negotiation: '20m', # 20 minutes
}
}
```
:::TabTitle Self-compiled (source)
Edit `/home/git/gitaly/config.toml`:
```toml
[timeout]
upload_pack_negotiation = "10m"
upload_archive_negotiation = "20m"
```
::EndTabs
For the values, use the format of [`ParseDuration`](https://pkg.go.dev/time#ParseDuration) in Go.
These timeouts affect only the [negotiation phase](https://git-scm.com/docs/pack-protocol/2.2.3#_packfile_negotiation) of
remote Git operations, not the entire transfer.

View File

@ -15,5 +15,5 @@ the following snippet in the rails console.
```ruby
u = User.find(1)
Project.last(100).each { |p| p.set_timestamps_for_create && p.add_maintainer(u, current_user: u) } # Change 100 to whatever number of projects you need access to
Project.last(100).each { |p| p.send(:set_timestamps_for_create) && p.add_maintainer(u, current_user: u) } # Change 100 to whatever number of projects you need access to
```

View File

@ -188,8 +188,8 @@ To export a report of merge request compliance violations for projects in a grou
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Compliance center**.
1. On the page, select the **Violations** tab.
1. On the Violations tab, select the **Export full report as CSV** action in the top right corner
1. Select the **Export** action in the top right corner
1. Select **Export violations report**
A report is compiled and delivered to your email inbox as an attachment.
@ -235,7 +235,8 @@ To generate the Chain of Custody report:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Compliance center**.
1. Select **List of all merge commits**.
1. Select the **Export** action in the top right corner
1. Select **Export chain of custody report**
Depending on your version of GitLab, the Chain of Custody report is either sent through email or available for download.
@ -251,9 +252,9 @@ To generate a commit-specific Chain of Custody report:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Compliance center**.
1. At the top of the compliance report, to the right of **List of all commits**, select the down arrow
(**{chevron-lg-down}**).
1. Enter the commit SHA, and then select **Export commit custody report**.
1. Select the **Export** action in the top right corner
1. Select **Export custody report of a specific commit**
1. Enter the commit SHA, and then select **Export custody report**.
Depending on your version of GitLab, the Chain of Custody report is either sent through email or available for download.
@ -358,8 +359,8 @@ To export a report of compliance frameworks on projects in a group:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Compliance center**.
1. On the page, select the **Projects** tab.
1. On the Frameworks tab, select the **Export as CSV** action in the top right corner
1. Select the **Export** action in the top right corner
1. Select **Export list of project frameworks**
A report is compiled and delivered to your email inbox as an attachment.

View File

@ -189,7 +189,7 @@ You might experience a `fatal: the remote end hung up unexpectedly` error when a
- The same large repository in parallel.
You can attempt to mitigate this issue by increasing the default negotiation timeout values. For more information, see
[Configure negotiation timeouts](../../../../administration/gitaly/configure_gitaly.md#configure-negotiation-timeouts).
[Configure the negotiation timeouts](../../../../administration/settings/gitaly_timeouts.md#configure-the-negotiation-timeouts).
## Optimize your repository

View File

@ -1439,9 +1439,6 @@ msgstr ""
msgid "(leave blank if you don't want to change it)"
msgstr ""
msgid "(max size 15 MB)"
msgstr ""
msgid "(no user)"
msgstr ""
@ -6111,6 +6108,9 @@ msgstr ""
msgid "Approval options"
msgstr ""
msgid "Approval rejected."
msgstr ""
msgid "Approval rules"
msgstr ""
@ -6272,7 +6272,7 @@ msgstr ""
msgid "ApprovalSettings|Remove approvals by Code Owners if their files changed"
msgstr ""
msgid "ApprovalSettings|Require user password to approve"
msgid "ApprovalSettings|Require user re-authentication (password or SAML) to approve"
msgstr ""
msgid "ApprovalSettings|There was an error loading merge request approval settings."
@ -12429,13 +12429,34 @@ msgstr ""
msgid "Completed in %{duration_seconds} seconds (%{relative_time})"
msgstr ""
msgid "Compliance Center|Export full report as CSV"
msgid "Compliance Center Export|(limited to 15 MB)"
msgstr ""
msgid "Compliance Center|Export merge request violations as CSV. You will be emailed after the export is processed."
msgid "Compliance Center Export|Example: 2dc6aa3"
msgstr ""
msgid "Compliance Center|Export projects as CSV. You will be emailed after the export is processed."
msgid "Compliance Center Export|Export as CSV"
msgstr ""
msgid "Compliance Center Export|Export chain of custody report"
msgstr ""
msgid "Compliance Center Export|Export custody report"
msgstr ""
msgid "Compliance Center Export|Export custody report of a specific commit"
msgstr ""
msgid "Compliance Center Export|Export list of project frameworks"
msgstr ""
msgid "Compliance Center Export|Export violations report"
msgstr ""
msgid "Compliance Center Export|Invalid hash"
msgstr ""
msgid "Compliance Center Export|Send email of the chosen report as CSV"
msgstr ""
msgid "Compliance Center|Frameworks"
@ -19975,9 +19996,6 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
msgid "Export commit custody report"
msgstr ""
msgid "Export group"
msgstr ""
@ -25935,9 +25953,6 @@ msgstr ""
msgid "Invalid format selected"
msgstr ""
msgid "Invalid hash"
msgstr ""
msgid "Invalid input, please avoid emoji"
msgstr ""
@ -28695,9 +28710,6 @@ msgstr ""
msgid "List available repositories"
msgstr ""
msgid "List of all commits"
msgstr ""
msgid "List of suitable GCP locations"
msgstr ""
@ -57854,6 +57866,12 @@ msgstr ""
msgid "mrWidget|Approve additionally"
msgstr ""
msgid "mrWidget|Approve additionally with SAML"
msgstr ""
msgid "mrWidget|Approve with SAML"
msgstr ""
msgid "mrWidget|Approved by"
msgstr ""

View File

@ -2,7 +2,13 @@
require 'spec_helper'
RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :controller do
RSpec
.describe(
Projects::MergeRequestsController,
'(JavaScript fixtures)',
type: :controller,
feature_category: :code_review_workflow
) do
include JavaScriptFixturesHelpers
let(:namespace) { create(:namespace, name: 'frontend-fixtures') }

View File

@ -4,6 +4,7 @@ import { GlButton, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { createMockSubscription as createMockApolloSubscription } from 'mock-apollo-client';
import approvedByCurrentUser from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql.json';
import { visitUrl } from '~/lib/utils/url_utility';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
@ -28,6 +29,10 @@ jest.mock('~/alert', () => ({
dismiss: mockAlertDismiss,
})),
}));
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(),
}));
const TEST_HELP_PATH = 'help/path';
const testApprovedBy = () => [1, 7, 10].map((id) => ({ id }));
@ -113,6 +118,7 @@ describe('MRWidget approvals', () => {
targetProjectFullPath: 'gitlab-org/gitlab',
id: 1,
iid: '1',
requireSamlAuthToApprove: false,
};
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
@ -172,6 +178,22 @@ describe('MRWidget approvals', () => {
category: 'primary',
});
});
describe('with SAML auth requried for approval', () => {
beforeEach(async () => {
const response = createCanApproveResponse();
mr.requireSamlAuthToApprove = true;
createComponent({}, { query: response });
await waitForPromises();
});
it('approve action is rendered with correct text', () => {
expect(findActionData()).toEqual({
variant: 'confirm',
text: 'Approve with SAML',
category: 'primary',
});
});
});
});
describe('and MR is approved', () => {
@ -194,6 +216,25 @@ describe('MRWidget approvals', () => {
});
});
describe('with approvers, with SAML auth requried for approval', () => {
beforeEach(async () => {
canApproveResponse.data.project.mergeRequest.approvedBy.nodes =
approvedByCurrentUser.data.project.mergeRequest.approvedBy.nodes;
canApproveResponse.data.project.mergeRequest.approvedBy.nodes[0].id = 69;
mr.requireSamlAuthToApprove = true;
createComponent({}, { query: canApproveResponse });
await waitForPromises();
});
it('approve additionally action is rendered with correct text', () => {
expect(findActionData()).toEqual({
variant: 'confirm',
text: 'Approve additionally with SAML',
category: 'secondary',
});
});
});
describe('with approvers', () => {
beforeEach(async () => {
canApproveResponse.data.project.mergeRequest.approvedBy.nodes =
@ -215,6 +256,25 @@ describe('MRWidget approvals', () => {
});
});
describe('when SAML auth is required and user clicks Approve with SAML', () => {
const fakeGroupSamlPath = '/example_group_saml';
beforeEach(async () => {
mr.requireSamlAuthToApprove = true;
mr.samlApprovalPath = fakeGroupSamlPath;
createComponent({}, { query: createCanApproveResponse() });
await waitForPromises();
});
it('redirects the user to the group SAML path', async () => {
const action = findAction();
action.vm.$emit('click');
await nextTick();
expect(visitUrl).toHaveBeenCalledWith(fakeGroupSamlPath);
});
});
describe('when approve action is clicked', () => {
beforeEach(async () => {
createComponent({}, { query: canApproveResponse });

View File

@ -16,7 +16,7 @@ RSpec.describe Gitlab::Utils::FileInfo, feature_category: :shared do
describe '.linked?' do
it 'raises an error when file does not exist' do
expect { subject.linked?('foo') }.to raise_error(Errno::ENOENT)
expect { subject.linked?("#{tmpdir}/foo") }.to raise_error(Errno::ENOENT)
end
shared_examples 'identifies a linked file' do
@ -56,7 +56,7 @@ RSpec.describe Gitlab::Utils::FileInfo, feature_category: :shared do
describe '.shares_hard_link?' do
it 'raises an error when file does not exist' do
expect { subject.shares_hard_link?('foo') }.to raise_error(Errno::ENOENT)
expect { subject.shares_hard_link?("#{tmpdir}/foo") }.to raise_error(Errno::ENOENT)
end
shared_examples 'identifies a file that shares a hard link' do

View File

@ -8,8 +8,6 @@ RSpec.describe API::MergeRequestApprovals, feature_category: :source_code_manage
let_it_be(:bot) { create(:user, :project_bot) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) }
let_it_be(:approver) { create :user }
let_it_be(:group) { create :group }
let(:merge_request) { create(:merge_request, :simple, author: user, source_project: project) }
describe 'GET :id/merge_requests/:merge_request_iid/approvals' do

View File

@ -13,6 +13,7 @@ RSpec.describe MergeRequests::ApprovalService, feature_category: :code_review_wo
before do
project.add_developer(user)
stub_feature_flags ff_require_saml_auth_to_approve: false
end
context 'with invalid approval' do