Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2c9bd42a67
commit
04eb990a84
|
|
@ -359,10 +359,10 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/jobs.md @marcel.amirault
|
||||
/doc/api/keys.md @aqualls
|
||||
/doc/api/labels.md @msedlakjakubowski
|
||||
/doc/api/license.md @kpaizee
|
||||
/doc/api/license.md @fneill
|
||||
/doc/api/linked_epics.md @msedlakjakubowski
|
||||
/doc/api/lint.md @marcel.amirault
|
||||
/doc/api/managed_licenses.md @kpaizee
|
||||
/doc/api/managed_licenses.md @fneill
|
||||
/doc/api/markdown.md @aqualls
|
||||
/doc/api/members.md @eread
|
||||
/doc/api/merge_request_approvals.md @aqualls
|
||||
|
|
|
|||
|
|
@ -198,32 +198,6 @@ Layout/HashAlignment:
|
|||
- 'spec/models/user_spec.rb'
|
||||
- 'spec/presenters/clusters/cluster_presenter_spec.rb'
|
||||
- 'spec/presenters/project_presenter_spec.rb'
|
||||
- 'spec/requests/api/ci/job_artifacts_spec.rb'
|
||||
- 'spec/requests/api/ci/jobs_spec.rb'
|
||||
- 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
|
||||
- 'spec/requests/api/feature_flags_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/config_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/group_variables_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/instance_variables_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/project_variables_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/runner_spec.rb'
|
||||
- 'spec/requests/api/graphql/ci/runners_spec.rb'
|
||||
- 'spec/requests/api/graphql/mutations/releases/update_spec.rb'
|
||||
- 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
|
||||
- 'spec/requests/api/graphql/project/terraform/state_spec.rb'
|
||||
- 'spec/requests/api/graphql/project/terraform/states_spec.rb'
|
||||
- 'spec/requests/api/graphql/query_spec.rb'
|
||||
- 'spec/requests/api/groups_spec.rb'
|
||||
- 'spec/requests/api/internal/base_spec.rb'
|
||||
- 'spec/requests/api/issues/get_group_issues_spec.rb'
|
||||
- 'spec/requests/api/projects_spec.rb'
|
||||
- 'spec/requests/api/suggestions_spec.rb'
|
||||
- 'spec/requests/api/unleash_spec.rb'
|
||||
- 'spec/requests/git_http_spec.rb'
|
||||
- 'spec/requests/oauth_tokens_spec.rb'
|
||||
- 'spec/requests/openid_connect_spec.rb'
|
||||
- 'spec/requests/projects/environments_controller_spec.rb'
|
||||
- 'spec/requests/projects/merge_requests_discussions_spec.rb'
|
||||
- 'spec/routing/project_routing_spec.rb'
|
||||
- 'spec/serializers/ci/lint/job_entity_spec.rb'
|
||||
- 'spec/serializers/container_repository_entity_spec.rb'
|
||||
|
|
|
|||
|
|
@ -18,5 +18,10 @@ module Types
|
|||
|
||||
field :issuable_dates_updated, subscription: Subscriptions::IssuableUpdated, null: true,
|
||||
description: 'Triggered when the due date or start date of an issuable is updated.'
|
||||
|
||||
field :merge_request_reviewers_updated,
|
||||
subscription: Subscriptions::IssuableUpdated,
|
||||
null: true,
|
||||
description: 'Triggered when the reviewers of a merge request are updated.'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -183,58 +183,6 @@ module NotificationRecipients
|
|||
add_recipients(target.subscribers(project), :subscription, NotificationReason::SUBSCRIBED)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def user_ids_notifiable_on(resource, notification_level = nil)
|
||||
return [] unless resource
|
||||
|
||||
scope = resource.notification_settings
|
||||
|
||||
if notification_level
|
||||
scope = scope.where(level: NotificationSetting.levels[notification_level])
|
||||
end
|
||||
|
||||
scope.pluck(:user_id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# Build a list of user_ids based on project notification settings
|
||||
def select_project_members_ids(global_setting, user_ids_global_level_watch)
|
||||
user_ids = user_ids_notifiable_on(project, :watch)
|
||||
|
||||
# If project setting is global, add to watch list if global setting is watch
|
||||
user_ids + (global_setting & user_ids_global_level_watch)
|
||||
end
|
||||
|
||||
# Build a list of user_ids based on group notification settings
|
||||
def select_group_members_ids(group, project_members, global_setting, user_ids_global_level_watch)
|
||||
uids = user_ids_notifiable_on(group, :watch)
|
||||
|
||||
# Group setting is global, add to user_ids list if global setting is watch
|
||||
uids + (global_setting & user_ids_global_level_watch) - project_members
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def user_ids_with_global_level_watch(ids)
|
||||
settings_with_global_level_of(:watch, ids).pluck(:user_id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def user_ids_with_global_level_custom(ids, action)
|
||||
settings_with_global_level_of(:custom, ids).pluck(:user_id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def settings_with_global_level_of(level, ids)
|
||||
NotificationSetting.where(
|
||||
user_id: ids,
|
||||
source_type: nil,
|
||||
level: NotificationSetting.levels[level]
|
||||
)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def add_labels_subscribers(labels: nil)
|
||||
return unless target.respond_to? :labels
|
||||
|
||||
|
|
|
|||
|
|
@ -1232,6 +1232,7 @@ GET /groups/:id/hooks/:hook_id
|
|||
"url": "http://example.com/hook",
|
||||
"group_id": 3,
|
||||
"push_events": true,
|
||||
"push_events_branch_filter": "",
|
||||
"issues_events": true,
|
||||
"confidential_issues_events": true,
|
||||
"merge_requests_events": true,
|
||||
|
|
@ -1258,10 +1259,11 @@ POST /groups/:id/hooks
|
|||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| -----------------------------| -------------- | ---------| ----------- |
|
||||
| -----------------------------| -------------- |----------| ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
|
||||
| `url` | string | yes | The hook URL |
|
||||
| `push_events` | boolean | no | Trigger hook on push events |
|
||||
| `push_events_branch_filter` | string | No | Trigger hook on push events for matching branches only. |
|
||||
| `issues_events` | boolean | no | Trigger hook on issues events |
|
||||
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
|
||||
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
|
||||
|
|
@ -1291,6 +1293,7 @@ PUT /groups/:id/hooks/:hook_id
|
|||
| `hook_id` | integer | yes | The ID of the group hook |
|
||||
| `url` | string | yes | The hook URL |
|
||||
| `push_events` | boolean | no | Trigger hook on push events |
|
||||
| `push_events_branch_filter` | string | No | Trigger hook on push events for matching branches only. |
|
||||
| `issues_events` | boolean | no | Trigger hook on issues events |
|
||||
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
|
||||
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ As an experiment, we want to introduce a `local` reviewer status for database re
|
|||
focusing on work from a team/stage, but not outside of it. This helps to focus and build great domain
|
||||
knowledge. We are not introducing changes to the reviewer roulette till we evaluate the impact and feedback from this
|
||||
experiment. We ask to respect reviewers who decline reviews based on their focus on `local` reviews. For tracking purposes,
|
||||
please use in your personal YAML file entry: `- reviewer database local` instead of `- reviewer database`.
|
||||
please use in your personal YAML file entry: `- reviewer database local` instead of `- reviewer database`.
|
||||
|
||||
### Approval guidelines
|
||||
|
||||
|
|
@ -330,7 +330,7 @@ Maintainers are the DRI of assuring that the acceptance criteria of a merge requ
|
|||
In general, [quality is everyone’s responsibility](https://about.gitlab.com/handbook/engineering/quality/),
|
||||
but maintainers of an MR are held responsible for **ensuring** that an MR meets those general quality standards.
|
||||
|
||||
If a maintainer feels that an MR is substantial enough, or requires a [domain expert](#domain-experts),
|
||||
If a maintainer feels that an MR is substantial enough, or requires a [domain expert](#domain-experts),
|
||||
maintainers have the discretion to request a review from another reviewer, or maintainer. Here are some
|
||||
examples of maintainers proactively doing this during review:
|
||||
|
||||
|
|
@ -419,7 +419,7 @@ first time.
|
|||
codebase. Thorough descriptions help all reviewers understand your request
|
||||
and test effectively.
|
||||
- If you know your change depends on another being merged first, note it in the
|
||||
description and set a [merge request dependency](../user/project/merge_requests/merge_request_dependencies.md).
|
||||
description and set a [merge request dependency](../user/project/merge_requests/dependencies.md).
|
||||
- Be grateful for the reviewer's suggestions. ("Good call. I'll make that change.")
|
||||
- Don't take it personally. The review is of the code, not of you.
|
||||
- Explain why the code exists. ("It's like that because of these reasons. Would
|
||||
|
|
@ -490,7 +490,7 @@ experience, refactors the existing code). Then:
|
|||
optionally resolve within the merge request or follow-up at a later stage.
|
||||
- There's a [Chrome/Firefox add-on](https://gitlab.com/conventionalcomments/conventional-comments-button) which you can use to apply [Conventional Comment](https://conventionalcomments.org/) prefixes.
|
||||
- Ensure there are no open dependencies. Check [linked issues](../user/project/issues/related_issues.md) for blockers. Clarify with the authors
|
||||
if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/merge_request_dependencies.md).
|
||||
if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/dependencies.md).
|
||||
- After a round of line notes, it can be helpful to post a summary note such as
|
||||
"Looks good to me", or "Just a couple things to address."
|
||||
- Let the author know if changes are required following your review.
|
||||
|
|
|
|||
|
|
@ -83,6 +83,31 @@ You can silence deprecation warnings by setting the environment variable
|
|||
SILENCE_DEPRECATIONS=1 bin/rspec spec/models/project_spec.rb
|
||||
```
|
||||
|
||||
### Test order
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93137) in GitLab 15.3.
|
||||
|
||||
All new spec files are run in [random order](https://gitlab.com/gitlab-org/gitlab/-/issues/337399)
|
||||
to surface flaky tests that are dependent on test order.
|
||||
|
||||
When randomized:
|
||||
|
||||
- We append the `(order random)` to example group description.
|
||||
- The used seed is shown in the spec output below the test suite summary. For example, `Randomized with seed 27443`.
|
||||
|
||||
For a list of spec files which are still run in defined order, see [`rspec_order_todo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/rspec_order_todo.yml).
|
||||
|
||||
To make spec files run in random order, check their order dependency with:
|
||||
|
||||
```shell
|
||||
scripts/rspec_check_order_dependence spec/models/project_spec.rb
|
||||
```
|
||||
|
||||
If the specs pass the check the script removes them from
|
||||
[`rspec_order_todo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/rspec_order_todo.yml) automatically.
|
||||
|
||||
If the specs fail the check they must be fixed before than can run in random order.
|
||||
|
||||
### Test speed
|
||||
|
||||
GitLab has a massive test suite that, without [parallelization](../pipelines.md#test-suite-parallelization), can take hours
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ Configure your groups to control group permissions and access.
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34370) in GitLab 12.8.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/224129) in GitLab 13.4.
|
||||
> - [Moved to Settings/Repository](https://gitlab.com/gitlab-org/gitlab/-/issues/220365) in GitLab 15.4.
|
||||
|
||||
Group push rules allow group maintainers to set
|
||||
[push rules](../project/repository/push_rules.md) for newly created projects in the specific group.
|
||||
|
||||
To configure push rules for a group:
|
||||
|
||||
1. Go to the groups's **Push Rules** page.
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Settings > Repository** page.
|
||||
1. Expand the **Pre-defined push rules** section.
|
||||
1. Select the settings you want.
|
||||
1. Select **Save Push Rules**.
|
||||
|
||||
|
|
@ -132,8 +135,8 @@ To prevent sharing outside of the group's hierarchy:
|
|||
|
||||
## Prevent a project from being shared with groups
|
||||
|
||||
Prevent projects in a group from
|
||||
[sharing a project with another group](../project/members/share_project_with_groups.md)
|
||||
Prevent projects in a group from
|
||||
[sharing a project with another group](../project/members/share_project_with_groups.md)
|
||||
to enable tighter control over project access.
|
||||
|
||||
To prevent a project from being shared with other groups:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
type: reference, concepts
|
||||
---
|
||||
|
||||
# Merge request dependencies **(PREMIUM)**
|
||||
|
||||
A single feature can span several merge requests, spread out across multiple projects,
|
||||
and the order in which the work merges can be significant. Use merge request dependencies
|
||||
when it's important to merge work in a specific order. Some examples:
|
||||
|
||||
- Ensure changes to a required library are merged before changes to a project that
|
||||
imports the library.
|
||||
- Prevent a documentation-only merge request from merging before the feature work
|
||||
is itself merged.
|
||||
- Require a merge request updating a permissions matrix to merge, before merging work
|
||||
from someone who hasn't yet been granted permissions.
|
||||
|
||||
If your project `me/myexample` imports a library from `myfriend/library`,
|
||||
you might want to update your project to use a new feature in `myfriend/library`.
|
||||
However, if you merge changes to your project before the external library adds the
|
||||
new feature, you would break the default branch in your project. A merge request
|
||||
dependency prevents your work from merging too soon:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A['me/myexample' project]
|
||||
B['myfriend/library' project]
|
||||
C[Merge request #1:<br>Create new version 2.5]
|
||||
D[Merge request #2:<br>Add version 2.5<br>to build]
|
||||
A-->|contains| D
|
||||
B---->|contains| C
|
||||
D-.->|depends on| C
|
||||
C-.->|blocks| D
|
||||
```
|
||||
|
||||
You could mark your `me/myexample` merge request as a [draft](drafts.md)
|
||||
and explain why in the comments. However, this approach is manual and does not scale, especially
|
||||
if your merge request relies on several others in multiple projects. Instead,
|
||||
use the draft (or ready) state to track the readiness of an individual
|
||||
merge request, and a merge request dependency to enforce merge order.
|
||||
|
||||
NOTE:
|
||||
Merge request dependencies are a **PREMIUM** feature, but this restriction is
|
||||
enforced only for the dependent merge request. A merge request in a **PREMIUM**
|
||||
project can depend on a merge request in a **FREE** project, but a merge request
|
||||
in a **FREE** project cannot be marked as dependent.
|
||||
|
||||
## View dependencies for a merge request
|
||||
|
||||
If a merge request is dependent on another, the merge request reports section shows
|
||||
information about the dependency:
|
||||
|
||||

|
||||
|
||||
To view dependency information on a merge request:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Scroll to the merge request reports area. Dependent merge requests display information
|
||||
about the total number of dependencies set, such as
|
||||
**(status-warning)** **Depends on 1 merge request being merged**.
|
||||
1. Select **Expand** to view the title, milestone, assignee, and pipeline status
|
||||
of each dependency.
|
||||
|
||||
Until your merge request's dependencies all merge, your merge request
|
||||
cannot be merged. The message
|
||||
**Merge blocked: you can only merge after the above items are resolved** displays.
|
||||
|
||||
### Closed merge requests
|
||||
|
||||
Closed merge requests still prevent their dependents from being merged, because
|
||||
a merge request can close regardless of whether or not the planned work actually merged.
|
||||
|
||||
If a merge request closes and the dependency is no longer relevant,
|
||||
remove it as a dependency to unblock the dependent merge request.
|
||||
|
||||
## Create a new dependent merge request
|
||||
|
||||
When you create a new merge request, you can prevent it from merging until after
|
||||
other specific work merges, even if the merge request is in a different project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role or be allowed to create merge requests in the project.
|
||||
- The dependent merge request must be in a project in a **PREMIUM** or higher tier.
|
||||
|
||||
To create a new merge request and mark it as dependent on another:
|
||||
|
||||
1. [Create a new merge request](creating_merge_requests.md).
|
||||
1. In **Merge request dependencies**, paste either the reference or the full URL
|
||||
to the merge requests that should merge before this work merges. References
|
||||
are in the form of `path/to/project!merge_request_id`.
|
||||
1. Select **Create merge request**.
|
||||
|
||||
## Edit a merge request to add a dependency
|
||||
|
||||
You can edit an existing merge request and mark it as dependent on another.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have at least the Developer role or be allowed to edit merge requests in the project.
|
||||
|
||||
To do this:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Select **Edit**.
|
||||
1. In **Merge request dependencies**, paste either the reference or the full URL
|
||||
to the merge requests that should merge before this work merges. References
|
||||
are in the form of `path/to/project!merge_request_id`.
|
||||
|
||||
## Remove a dependency from a merge request
|
||||
|
||||
You can edit a dependent merge request and remove a dependency.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have a role in the project that allows you to edit merge requests.
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Select **Edit**.
|
||||
1. Scroll to **Merge request dependencies** and select **Remove** next to the reference
|
||||
for each dependency you want to remove.
|
||||
|
||||
NOTE:
|
||||
Dependencies for merge requests you don't have access to are displayed as
|
||||
**1 inaccessible merge request**, and can be removed the same way.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### API support for managing merge request dependencies
|
||||
|
||||
No API support exists for managing dependencies. For more information, read
|
||||
[issue #12551](https://gitlab.com/gitlab-org/gitlab/-/issues/12551).
|
||||
|
||||
### Preserving dependencies on project import or export
|
||||
|
||||
Dependencies are not preserved when projects are imported or exported. For more
|
||||
information, read [issue #12549](https://gitlab.com/gitlab-org/gitlab/-/issues/12549).
|
||||
|
||||
### Complex merge order dependencies are unsupported
|
||||
|
||||
GitLab supports direct dependencies between merge requests, but does not support
|
||||
[indirect (nested) dependencies](https://gitlab.com/gitlab-org/gitlab/-/issues/11393).
|
||||
|
||||
Acceptable dependency patterns include:
|
||||
|
||||
- A single merge request can directly depend on a single merge request.
|
||||
- A single merge request can directly depend on multiple merge requests.
|
||||
- Multiple merge requests can directly depend on a single merge request.
|
||||
|
||||
The indirect, nested dependency between `myfriend/library!10` and `mycorp/example!100` shown in this example is not supported:
|
||||
|
||||
```mermaid
|
||||
graph LR;
|
||||
A[myfriend/library!10]-->|depends on| B[herfriend/another-lib!1]
|
||||
B-->|depends on| C[mycorp/example!100]
|
||||
```
|
||||
|
|
@ -66,7 +66,7 @@ After you have created the merge request, you can also:
|
|||
|
||||
- [Discuss](../../discussions/index.md) your implementation with your team in the merge request thread.
|
||||
- [Perform inline code reviews](reviews/index.md).
|
||||
- Add [merge request dependencies](merge_request_dependencies.md) to restrict it to be merged only when other merge requests have been merged.
|
||||
- Add [merge request dependencies](dependencies.md) to restrict it to be merged only when other merge requests have been merged.
|
||||
- Preview continuous integration [pipelines on the merge request widget](widgets.md).
|
||||
- Preview how your changes look directly on your deployed application with [Review Apps](widgets.md#live-preview-with-review-apps).
|
||||
- [Allow collaboration on merge requests across forks](allow_collaboration.md).
|
||||
|
|
|
|||
|
|
@ -1,163 +1,11 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
type: reference, concepts
|
||||
redirect_to: 'dependencies.md'
|
||||
remove_date: '2022-11-22'
|
||||
---
|
||||
|
||||
# Merge request dependencies **(PREMIUM)**
|
||||
This document was moved to [another location](dependencies.md).
|
||||
|
||||
A single feature can span several merge requests, spread out across multiple projects,
|
||||
and the order in which the work merges can be significant. Use merge request dependencies
|
||||
when it's important to merge work in a specific order. Some examples:
|
||||
|
||||
- Ensure changes to a required library are merged before changes to a project that
|
||||
imports the library.
|
||||
- Prevent a documentation-only merge request from merging before the feature work
|
||||
is itself merged.
|
||||
- Require a merge request updating a permissions matrix to merge, before merging work
|
||||
from someone who hasn't yet been granted permissions.
|
||||
|
||||
If your project `me/myexample` imports a library from `myfriend/library`,
|
||||
you might want to update your project to use a new feature in `myfriend/library`.
|
||||
However, if you merge changes to your project before the external library adds the
|
||||
new feature, you would break the default branch in your project. A merge request
|
||||
dependency prevents your work from merging too soon:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A['me/myexample' project]
|
||||
B['myfriend/library' project]
|
||||
C[Merge request #1:<br>Create new version 2.5]
|
||||
D[Merge request #2:<br>Add version 2.5<br>to build]
|
||||
A-->|contains| D
|
||||
B---->|contains| C
|
||||
D-.->|depends on| C
|
||||
C-.->|blocks| D
|
||||
```
|
||||
|
||||
You could mark your `me/myexample` merge request as a [draft](drafts.md)
|
||||
and explain why in the comments. However, this approach is manual and does not scale, especially
|
||||
if your merge request relies on several others in multiple projects. Instead,
|
||||
use the draft (or ready) state to track the readiness of an individual
|
||||
merge request, and a merge request dependency to enforce merge order.
|
||||
|
||||
NOTE:
|
||||
Merge request dependencies are a **PREMIUM** feature, but this restriction is
|
||||
enforced only for the dependent merge request. A merge request in a **PREMIUM**
|
||||
project can depend on a merge request in a **FREE** project, but a merge request
|
||||
in a **FREE** project cannot be marked as dependent.
|
||||
|
||||
## View dependencies for a merge request
|
||||
|
||||
If a merge request is dependent on another, the merge request reports section shows
|
||||
information about the dependency:
|
||||
|
||||

|
||||
|
||||
To view dependency information on a merge request:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Scroll to the merge request reports area. Dependent merge requests display information
|
||||
about the total number of dependencies set, such as
|
||||
**(status-warning)** **Depends on 1 merge request being merged**.
|
||||
1. Select **Expand** to view the title, milestone, assignee, and pipeline status
|
||||
of each dependency.
|
||||
|
||||
Until your merge request's dependencies all merge, your merge request
|
||||
cannot be merged. The message
|
||||
**Merge blocked: you can only merge after the above items are resolved** displays.
|
||||
|
||||
### Closed merge requests
|
||||
|
||||
Closed merge requests still prevent their dependents from being merged, because
|
||||
a merge request can close regardless of whether or not the planned work actually merged.
|
||||
|
||||
If a merge request closes and the dependency is no longer relevant,
|
||||
remove it as a dependency to unblock the dependent merge request.
|
||||
|
||||
## Create a new dependent merge request
|
||||
|
||||
When you create a new merge request, you can prevent it from merging until after
|
||||
other specific work merges, even if the merge request is in a different project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role or be allowed to create merge requests in the project.
|
||||
- The dependent merge request must be in a project in a **PREMIUM** or higher tier.
|
||||
|
||||
To create a new merge request and mark it as dependent on another:
|
||||
|
||||
1. [Create a new merge request](creating_merge_requests.md).
|
||||
1. In **Merge request dependencies**, paste either the reference or the full URL
|
||||
to the merge requests that should merge before this work merges. References
|
||||
are in the form of `path/to/project!merge_request_id`.
|
||||
1. Select **Create merge request**.
|
||||
|
||||
## Edit a merge request to add a dependency
|
||||
|
||||
You can edit an existing merge request and mark it as dependent on another.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have at least the Developer role or be allowed to edit merge requests in the project.
|
||||
|
||||
To do this:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Select **Edit**.
|
||||
1. In **Merge request dependencies**, paste either the reference or the full URL
|
||||
to the merge requests that should merge before this work merges. References
|
||||
are in the form of `path/to/project!merge_request_id`.
|
||||
|
||||
## Remove a dependency from a merge request
|
||||
|
||||
You can edit a dependent merge request and remove a dependency.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have a role in the project that allows you to edit merge requests.
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Merge requests** and identify your merge request.
|
||||
1. Select **Edit**.
|
||||
1. Scroll to **Merge request dependencies** and select **Remove** next to the reference
|
||||
for each dependency you want to remove.
|
||||
|
||||
NOTE:
|
||||
Dependencies for merge requests you don't have access to are displayed as
|
||||
**1 inaccessible merge request**, and can be removed the same way.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### API support for managing merge request dependencies
|
||||
|
||||
No API support exists for managing dependencies. For more information, read
|
||||
[issue #12551](https://gitlab.com/gitlab-org/gitlab/-/issues/12551).
|
||||
|
||||
### Preserving dependencies on project import or export
|
||||
|
||||
Dependencies are not preserved when projects are imported or exported. For more
|
||||
information, read [issue #12549](https://gitlab.com/gitlab-org/gitlab/-/issues/12549).
|
||||
|
||||
### Complex merge order dependencies are unsupported
|
||||
|
||||
GitLab supports direct dependencies between merge requests, but does not support
|
||||
[indirect (nested) dependencies](https://gitlab.com/gitlab-org/gitlab/-/issues/11393).
|
||||
|
||||
Acceptable dependency patterns include:
|
||||
|
||||
- A single merge request can directly depend on a single merge request.
|
||||
- A single merge request can directly depend on multiple merge requests.
|
||||
- Multiple merge requests can directly depend on a single merge request.
|
||||
|
||||
The indirect, nested dependency between `myfriend/library!10` and `mycorp/example!100` shown in this example is not supported:
|
||||
|
||||
```mermaid
|
||||
graph LR;
|
||||
A[myfriend/library!10]-->|depends on| B[herfriend/another-lib!1]
|
||||
B-->|depends on| C[mycorp/example!100]
|
||||
```
|
||||
<!-- This redirect file can be deleted after <2022-11-22>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## Usage: scripts/rspec_check_order_dependence <files...>
|
||||
#
|
||||
# List of RSpec files to be checked for their order dependency.
|
||||
#
|
||||
# If the files pass the following checks it's likely they are not
|
||||
# order-dependent and are removed from `spec/support/rspec_order_todo.yml`
|
||||
# to make them run in random order.
|
||||
#
|
||||
# The following checks are available:
|
||||
# * Run specs in _defined_ order
|
||||
# * Run specs in _reverse_ order
|
||||
# * Run specs in _random_ order
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <files...>"
|
||||
exit
|
||||
fi
|
||||
|
||||
TODO_YAML='./spec/support/rspec_order_todo.yml'
|
||||
RSPEC_ARGS=(--format progress)
|
||||
|
||||
abort() {
|
||||
echo "$@"
|
||||
echo "Aborting..."
|
||||
exit 1
|
||||
}
|
||||
|
||||
for file in "$@"
|
||||
do
|
||||
# Drop potential file prefix `./`
|
||||
file=${file#./}
|
||||
|
||||
# Match only the prefix so we can specify a directory to match all the files
|
||||
# under it. For example, `spec/rubocop` will match, test and remove all TODO
|
||||
# entries starting with `./spec/rubocop`.
|
||||
grep -E -- "- './$file" "$TODO_YAML" > /dev/null || abort "Could not find '$file' in '$TODO_YAML'"
|
||||
done
|
||||
|
||||
set -xe
|
||||
|
||||
bin/rspec --order defined "${RSPEC_ARGS[@]}" "$@"
|
||||
RSPEC_ORDER=reverse bin/rspec "${RSPEC_ARGS[@]}" "$@"
|
||||
bin/rspec --order random "${RSPEC_ARGS[@]}" "$@"
|
||||
|
||||
set +xe
|
||||
|
||||
green='\033[0;32m'
|
||||
clear='\033[0m' # No Color
|
||||
|
||||
echo -e "$green"
|
||||
echo "
|
||||
The files passed all checks!
|
||||
|
||||
They are likely not order-dependent and can be run in random order and thus
|
||||
are being removed from 'spec/support/rspec_order_todo.yml':
|
||||
"
|
||||
|
||||
for file in "$@"
|
||||
do
|
||||
# Drop potential file prefix `./`
|
||||
file=${file#./}
|
||||
|
||||
echo " * Removing '$file'"
|
||||
|
||||
# Escape forward slashes to make it compatible with sed below
|
||||
escaped_file=${file//\//\\/}
|
||||
|
||||
# We must use -i.bak to make sed work on Linux and MacOS.
|
||||
# See https://riptutorial.com/sed/topic/9436/bsd-macos-sed-vs--gnu-sed-vs--the-posix-sed-specification
|
||||
sed -i.bak "/- '.\/$escaped_file/d" "$TODO_YAML"
|
||||
rm "$TODO_YAML.bak"
|
||||
done
|
||||
|
||||
echo -e "$clear"
|
||||
|
|
@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['Subscription'] do
|
|||
issuable_title_updated
|
||||
issuable_labels_updated
|
||||
issuable_dates_updated
|
||||
merge_request_reviewers_updated
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields).only
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
let(:guest) { create(:project_member, :guest, project: project).user }
|
||||
|
||||
let!(:job) do
|
||||
create(:ci_build, :success, :tags, pipeline: pipeline,
|
||||
artifacts_expire_at: 1.day.since)
|
||||
create(:ci_build, :success, :tags, pipeline: pipeline, artifacts_expire_at: 1.day.since)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -535,8 +534,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
context 'with regular branch' do
|
||||
before do
|
||||
pipeline.reload
|
||||
pipeline.update!(ref: 'master',
|
||||
sha: project.commit('master').sha)
|
||||
pipeline.update!(ref: 'master', sha: project.commit('master').sha)
|
||||
|
||||
get_for_ref('master')
|
||||
end
|
||||
|
|
@ -579,8 +577,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
stub_artifacts_object_storage
|
||||
job.success
|
||||
|
||||
project.update!(visibility_level: visibility_level,
|
||||
public_builds: public_builds)
|
||||
project.update!(visibility_level: visibility_level, public_builds: public_builds)
|
||||
|
||||
get_artifact_file(artifact)
|
||||
end
|
||||
|
|
@ -676,8 +673,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
context 'with branch name containing slash' do
|
||||
before do
|
||||
pipeline.reload
|
||||
pipeline.update!(ref: 'improve/awesome',
|
||||
sha: project.commit('improve/awesome').sha)
|
||||
pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
|
||||
end
|
||||
|
||||
it 'returns a specific artifact file for a valid path', :sidekiq_might_not_need_inline do
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ RSpec.describe API::Ci::Jobs do
|
|||
end
|
||||
|
||||
let!(:job) do
|
||||
create(:ci_build, :success, :tags, pipeline: pipeline,
|
||||
artifacts_expire_at: 1.day.since)
|
||||
create(:ci_build, :success, :tags, pipeline: pipeline, artifacts_expire_at: 1.day.since)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -546,9 +546,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
|
||||
let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
|
||||
let!(:test_job) do
|
||||
create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'deploy',
|
||||
stage: 'deploy', stage_idx: 1,
|
||||
options: { script: ['bash'], dependencies: [job2.name] })
|
||||
create(:ci_build, :pending, :queued,
|
||||
pipeline: pipeline,
|
||||
name: 'deploy',
|
||||
stage: 'deploy',
|
||||
stage_idx: 1,
|
||||
options: { script: ['bash'], dependencies: [job2.name] })
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -570,9 +573,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
|
||||
let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
|
||||
let!(:empty_dependencies_job) do
|
||||
create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'empty_dependencies_job',
|
||||
stage: 'deploy', stage_idx: 1,
|
||||
options: { script: ['bash'], dependencies: [] })
|
||||
create(:ci_build, :pending, :queued,
|
||||
pipeline: pipeline,
|
||||
name: 'empty_dependencies_job',
|
||||
stage: 'deploy',
|
||||
stage_idx: 1,
|
||||
options: { script: ['bash'], dependencies: [] })
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -889,9 +895,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
describe 'a job with excluded artifacts' do
|
||||
context 'when excluded paths are defined' do
|
||||
let(:job) do
|
||||
create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'test',
|
||||
stage: 'deploy', stage_idx: 1,
|
||||
options: { artifacts: { paths: ['abc'], exclude: ['cde'] } })
|
||||
create(:ci_build, :pending, :queued,
|
||||
pipeline: pipeline,
|
||||
name: 'test',
|
||||
stage: 'deploy',
|
||||
stage_idx: 1,
|
||||
options: { artifacts: { paths: ['abc'], exclude: ['cde'] } })
|
||||
end
|
||||
|
||||
context 'when a runner supports this feature' do
|
||||
|
|
|
|||
|
|
@ -365,8 +365,8 @@ RSpec.describe API::FeatureFlags do
|
|||
describe 'PUT /projects/:id/feature_flags/:name' do
|
||||
context 'with a version 2 feature flag' do
|
||||
let!(:feature_flag) do
|
||||
create(:operations_feature_flag, :new_version_flag, project: project, active: true,
|
||||
name: 'feature1', description: 'old description')
|
||||
create(:operations_feature_flag, :new_version_flag,
|
||||
project: project, active: true, name: 'feature1', description: 'old description')
|
||||
end
|
||||
|
||||
it 'returns a 404 if the feature flag does not exist' do
|
||||
|
|
@ -591,8 +591,8 @@ RSpec.describe API::FeatureFlags do
|
|||
|
||||
it 'deletes a feature flag strategy' do
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'userWithId', parameters: { userIds: 'userA,userB' })
|
||||
strategy_b = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'userA,userB' })
|
||||
params = {
|
||||
strategies: [{
|
||||
id: strategy_a.id,
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ RSpec.describe 'Query.ciConfig' do
|
|||
{
|
||||
"name" => "docker",
|
||||
"size" => 1,
|
||||
"jobs" =>
|
||||
"jobs" =>
|
||||
{
|
||||
"nodes" => [
|
||||
{
|
||||
|
|
@ -206,7 +206,7 @@ RSpec.describe 'Query.ciConfig' do
|
|||
{
|
||||
"name" => "deploy_job",
|
||||
"size" => 1,
|
||||
"jobs" =>
|
||||
"jobs" =>
|
||||
{
|
||||
"nodes" => [
|
||||
{
|
||||
|
|
@ -332,7 +332,7 @@ RSpec.describe 'Query.ciConfig' do
|
|||
"only" => { "refs" => %w[branches tags] },
|
||||
"when" => "on_success",
|
||||
"tags" => [],
|
||||
"needs" => { "nodes" => [] } }
|
||||
"needs" => { "nodes" => [] } }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,14 @@ RSpec.describe 'Query.group(fullPath).ciVariables' do
|
|||
end
|
||||
|
||||
it "returns the group's CI variables" do
|
||||
variable = create(:ci_group_variable, group: group, key: 'TEST_VAR', value: 'test',
|
||||
masked: false, protected: true, raw: true, environment_scope: 'staging')
|
||||
variable = create(:ci_group_variable,
|
||||
group: group,
|
||||
key: 'TEST_VAR',
|
||||
value: 'test',
|
||||
masked: false,
|
||||
protected: true,
|
||||
raw: true,
|
||||
environment_scope: 'staging')
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RSpec.describe 'Query.ciVariables' do
|
|||
|
||||
it "returns the instance's CI variables" do
|
||||
variable = create(:ci_instance_variable, key: 'TEST_VAR', value: 'test',
|
||||
masked: false, protected: true, raw: true)
|
||||
masked: false, protected: true, raw: true)
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ RSpec.describe 'Query.project(fullPath).ciVariables' do
|
|||
|
||||
it "returns the project's CI variables" do
|
||||
variable = create(:ci_variable, project: project, key: 'TEST_VAR', value: 'test',
|
||||
masked: false, protected: true, raw: true, environment_scope: 'production')
|
||||
masked: false, protected: true, raw: true, environment_scope: 'production')
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,21 +9,49 @@ RSpec.describe 'Query.runner(id)' do
|
|||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let_it_be(:active_instance_runner) do
|
||||
create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
|
||||
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
|
||||
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom,
|
||||
maintenance_note: '**Test maintenance note**')
|
||||
create(:ci_runner, :instance,
|
||||
description: 'Runner 1',
|
||||
contacted_at: 2.hours.ago,
|
||||
active: true,
|
||||
version: 'adfe156',
|
||||
revision: 'a',
|
||||
locked: true,
|
||||
ip_address: '127.0.0.1',
|
||||
maximum_timeout: 600,
|
||||
access_level: 0,
|
||||
tag_list: %w[tag1 tag2],
|
||||
run_untagged: true,
|
||||
executor_type: :custom,
|
||||
maintenance_note: '**Test maintenance note**')
|
||||
end
|
||||
|
||||
let_it_be(:inactive_instance_runner) do
|
||||
create(:ci_runner, :instance, description: 'Runner 2', contacted_at: 1.day.ago, active: false,
|
||||
version: 'adfe157', revision: 'b', ip_address: '10.10.10.10', access_level: 1, run_untagged: true)
|
||||
create(:ci_runner, :instance,
|
||||
description: 'Runner 2',
|
||||
contacted_at: 1.day.ago,
|
||||
active: false,
|
||||
version: 'adfe157',
|
||||
revision: 'b',
|
||||
ip_address: '10.10.10.10',
|
||||
access_level: 1,
|
||||
run_untagged: true)
|
||||
end
|
||||
|
||||
let_it_be(:active_group_runner) do
|
||||
create(:ci_runner, :group, groups: [group], description: 'Group runner 1', contacted_at: 2.hours.ago,
|
||||
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
|
||||
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :shell)
|
||||
create(:ci_runner, :group,
|
||||
groups: [group],
|
||||
description: 'Group runner 1',
|
||||
contacted_at: 2.hours.ago,
|
||||
active: true,
|
||||
version: 'adfe156',
|
||||
revision: 'a',
|
||||
locked: true,
|
||||
ip_address: '127.0.0.1',
|
||||
maximum_timeout: 600,
|
||||
access_level: 0,
|
||||
tag_list: %w[tag1 tag2],
|
||||
run_untagged: true,
|
||||
executor_type: :shell)
|
||||
end
|
||||
|
||||
let_it_be(:active_project_runner) { create(:ci_runner, :project) }
|
||||
|
|
@ -159,8 +187,16 @@ RSpec.describe 'Query.runner(id)' do
|
|||
|
||||
with_them do
|
||||
let(:project_runner) do
|
||||
create(:ci_runner, :project, description: 'Runner 3', contacted_at: 1.day.ago, active: false, locked: is_locked,
|
||||
version: 'adfe157', revision: 'b', ip_address: '10.10.10.10', access_level: 1, run_untagged: true)
|
||||
create(:ci_runner, :project,
|
||||
description: 'Runner 3',
|
||||
contacted_at: 1.day.ago,
|
||||
active: false,
|
||||
locked: is_locked,
|
||||
version: 'adfe157',
|
||||
revision: 'b',
|
||||
ip_address: '10.10.10.10',
|
||||
access_level: 1,
|
||||
run_untagged: true)
|
||||
end
|
||||
|
||||
let(:query) do
|
||||
|
|
|
|||
|
|
@ -141,8 +141,13 @@ RSpec.describe 'Group.runners' do
|
|||
|
||||
describe 'edges' do
|
||||
let_it_be(:runner) do
|
||||
create(:ci_runner, :group, active: false, version: 'def', revision: '456',
|
||||
description: 'Project runner', groups: [group], ip_address: '127.0.0.1')
|
||||
create(:ci_runner, :group,
|
||||
active: false,
|
||||
version: 'def',
|
||||
revision: '456',
|
||||
description: 'Project runner',
|
||||
groups: [group],
|
||||
ip_address: '127.0.0.1')
|
||||
end
|
||||
|
||||
let(:query) do
|
||||
|
|
|
|||
|
|
@ -22,9 +22,14 @@ RSpec.describe 'Updating an existing release' do
|
|||
let_it_be(:milestones) { [milestone_12_3, milestone_12_4] }
|
||||
|
||||
let_it_be(:release) do
|
||||
create(:release, project: project, tag: tag_name, name: name,
|
||||
description: description, released_at: Time.parse(released_at).utc,
|
||||
created_at: Time.parse(created_at).utc, milestones: milestones)
|
||||
create(:release,
|
||||
project: project,
|
||||
tag: tag_name,
|
||||
name: name,
|
||||
description: description,
|
||||
released_at: Time.parse(released_at).utc,
|
||||
created_at: Time.parse(created_at).utc,
|
||||
milestones: milestones)
|
||||
end
|
||||
|
||||
let(:mutation_name) { :release_update }
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
|
|||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:stranger) { create(:user) }
|
||||
let_it_be(:old_version) do
|
||||
create(:design_version, issue: issue,
|
||||
created_designs: create_list(:design, 3, issue: issue))
|
||||
create(:design_version, issue: issue, created_designs: create_list(:design, 3, issue: issue))
|
||||
end
|
||||
|
||||
let_it_be(:version) do
|
||||
create(:design_version, issue: issue,
|
||||
modified_designs: old_version.designs,
|
||||
created_designs: create_list(:design, 2, issue: issue))
|
||||
create(:design_version,
|
||||
issue: issue,
|
||||
modified_designs: old_version.designs,
|
||||
created_designs: create_list(:design, 2, issue: issue))
|
||||
end
|
||||
|
||||
let(:current_user) { developer }
|
||||
|
|
|
|||
|
|
@ -60,17 +60,17 @@ RSpec.describe 'query a single terraform state' do
|
|||
expect(data).to match a_graphql_entity_for(
|
||||
terraform_state,
|
||||
:name,
|
||||
'lockedAt' => terraform_state.locked_at.iso8601,
|
||||
'createdAt' => terraform_state.created_at.iso8601,
|
||||
'updatedAt' => terraform_state.updated_at.iso8601,
|
||||
'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
|
||||
'lockedAt' => terraform_state.locked_at.iso8601,
|
||||
'createdAt' => terraform_state.created_at.iso8601,
|
||||
'updatedAt' => terraform_state.updated_at.iso8601,
|
||||
'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
|
||||
'latestVersion' => a_graphql_entity_for(
|
||||
latest_version,
|
||||
'serial' => eq(latest_version.version),
|
||||
'createdAt' => eq(latest_version.created_at.iso8601),
|
||||
'updatedAt' => eq(latest_version.updated_at.iso8601),
|
||||
'serial' => eq(latest_version.version),
|
||||
'createdAt' => eq(latest_version.created_at.iso8601),
|
||||
'updatedAt' => eq(latest_version.updated_at.iso8601),
|
||||
'createdByUser' => a_graphql_entity_for(latest_version.created_by_user),
|
||||
'job' => { 'name' => eq(latest_version.build.name) }
|
||||
'job' => { 'name' => eq(latest_version.build.name) }
|
||||
)
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,18 +64,18 @@ RSpec.describe 'query terraform states' do
|
|||
|
||||
expect(data['nodes']).to contain_exactly a_graphql_entity_for(
|
||||
terraform_state, :name,
|
||||
'lockedAt' => terraform_state.locked_at.iso8601,
|
||||
'createdAt' => terraform_state.created_at.iso8601,
|
||||
'updatedAt' => terraform_state.updated_at.iso8601,
|
||||
'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
|
||||
'lockedAt' => terraform_state.locked_at.iso8601,
|
||||
'createdAt' => terraform_state.created_at.iso8601,
|
||||
'updatedAt' => terraform_state.updated_at.iso8601,
|
||||
'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
|
||||
'latestVersion' => a_graphql_entity_for(
|
||||
latest_version,
|
||||
'serial' => eq(latest_version.version),
|
||||
'downloadPath' => eq(download_path),
|
||||
'createdAt' => eq(latest_version.created_at.iso8601),
|
||||
'updatedAt' => eq(latest_version.updated_at.iso8601),
|
||||
'serial' => eq(latest_version.version),
|
||||
'downloadPath' => eq(download_path),
|
||||
'createdAt' => eq(latest_version.created_at.iso8601),
|
||||
'updatedAt' => eq(latest_version.updated_at.iso8601),
|
||||
'createdByUser' => a_graphql_entity_for(latest_version.created_by_user),
|
||||
'job' => { 'name' => eq(latest_version.build.name) }
|
||||
'job' => { 'name' => eq(latest_version.build.name) }
|
||||
)
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ RSpec.describe 'Query' do
|
|||
design_at_version,
|
||||
'filename' => design_at_version.design.filename,
|
||||
'version' => a_graphql_entity_for(version, :sha),
|
||||
'design' => a_graphql_entity_for(design),
|
||||
'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
|
||||
'design' => a_graphql_entity_for(design),
|
||||
'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
|
||||
'project' => a_graphql_entity_for(project, :full_path)
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -540,9 +540,9 @@ RSpec.describe API::Groups do
|
|||
# Returns a Hash of visibility_level => Project pairs
|
||||
def add_projects_to_group(group, share_with: nil)
|
||||
projects = {
|
||||
public: create(:project, :public, namespace: group),
|
||||
public: create(:project, :public, namespace: group),
|
||||
internal: create(:project, :internal, namespace: group),
|
||||
private: create(:project, :private, namespace: group)
|
||||
private: create(:project, :private, namespace: group)
|
||||
}
|
||||
|
||||
if share_with
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ RSpec.describe API::Internal::Base do
|
|||
it 'returns an error message when expires_at contains an invalid date' do
|
||||
post api('/internal/personal_access_token'),
|
||||
params: {
|
||||
key_id: key.id,
|
||||
key_id: key.id,
|
||||
name: 'newtoken',
|
||||
scopes: ['api'],
|
||||
expires_at: 'invalid-date'
|
||||
|
|
@ -202,7 +202,7 @@ RSpec.describe API::Internal::Base do
|
|||
it 'returns an error message when it receives an invalid scope' do
|
||||
post api('/internal/personal_access_token'),
|
||||
params: {
|
||||
key_id: key.id,
|
||||
key_id: key.id,
|
||||
name: 'newtoken',
|
||||
scopes: %w(read_api badscope read_repository)
|
||||
},
|
||||
|
|
@ -217,7 +217,7 @@ RSpec.describe API::Internal::Base do
|
|||
|
||||
post api('/internal/personal_access_token'),
|
||||
params: {
|
||||
key_id: key.id,
|
||||
key_id: key.id,
|
||||
name: 'newtoken',
|
||||
scopes: %w(read_api read_repository)
|
||||
},
|
||||
|
|
@ -234,7 +234,7 @@ RSpec.describe API::Internal::Base do
|
|||
|
||||
post api('/internal/personal_access_token'),
|
||||
params: {
|
||||
key_id: key.id,
|
||||
key_id: key.id,
|
||||
name: 'newtoken',
|
||||
scopes: %w(read_api read_repository),
|
||||
expires_at: '9001-11-17'
|
||||
|
|
|
|||
|
|
@ -465,10 +465,10 @@ RSpec.describe API::Issues do
|
|||
|
||||
context 'with archived projects' do
|
||||
let_it_be(:archived_issue) do
|
||||
create(
|
||||
:issue, author: user, assignees: [user],
|
||||
project: create(:project, :public, :archived, creator_id: user.id, namespace: group)
|
||||
)
|
||||
create(:issue,
|
||||
author: user,
|
||||
assignees: [user],
|
||||
project: create(:project, :public, :archived, creator_id: user.id, namespace: group))
|
||||
end
|
||||
|
||||
it 'returns only non archived projects issues' do
|
||||
|
|
|
|||
|
|
@ -1249,9 +1249,10 @@ RSpec.describe API::Projects do
|
|||
stub_application_setting(import_sources: nil)
|
||||
|
||||
endpoint_url = "#{url}/info/refs?service=git-upload-pack"
|
||||
stub_full_request(endpoint_url, method: :get).to_return({ status: 200,
|
||||
body: '001e# service=git-upload-pack',
|
||||
headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
|
||||
stub_full_request(endpoint_url, method: :get).to_return(
|
||||
{ status: 200,
|
||||
body: '001e# service=git-upload-pack',
|
||||
headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
|
||||
|
||||
project_params = { import_url: url, path: 'path-project-Foo', name: 'Foo Project' }
|
||||
expect { post api('/projects', user), params: project_params }
|
||||
|
|
|
|||
|
|
@ -34,15 +34,14 @@ RSpec.describe API::Suggestions do
|
|||
end
|
||||
|
||||
let(:diff_note2) do
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
position: position2,
|
||||
project: project)
|
||||
create(:diff_note_on_merge_request, noteable: merge_request, position: position2, project: project)
|
||||
end
|
||||
|
||||
let(:suggestion) do
|
||||
create(:suggestion, note: diff_note,
|
||||
from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
|
||||
to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
|
||||
create(:suggestion,
|
||||
note: diff_note,
|
||||
from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
|
||||
to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
|
||||
end
|
||||
|
||||
let(:unappliable_suggestion) do
|
||||
|
|
@ -119,8 +118,8 @@ RSpec.describe API::Suggestions do
|
|||
describe "PUT /suggestions/batch_apply" do
|
||||
let(:suggestion2) do
|
||||
create(:suggestion, note: diff_note2,
|
||||
from_content: " \"PWD\" => path\n",
|
||||
to_content: " *** FOO ***\n")
|
||||
from_content: " \"PWD\" => path\n",
|
||||
to_content: " *** FOO ***\n")
|
||||
end
|
||||
|
||||
let(:url) { "/suggestions/batch_apply" }
|
||||
|
|
|
|||
|
|
@ -218,8 +218,7 @@ RSpec.describe API::Unleash do
|
|||
|
||||
context 'with version 2 feature flags' do
|
||||
it 'does not return a flag without any strategies' do
|
||||
create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
||||
|
|
@ -228,10 +227,8 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a flag with a default strategy' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'default', parameters: {})
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
create(:operations_scope, strategy: strategy, environment_scope: 'production')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
|
@ -248,10 +245,9 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a flag with a userWithId strategy' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'userWithId', parameters: { userIds: 'user123,user456' })
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user123,user456' })
|
||||
create(:operations_scope, strategy: strategy, environment_scope: 'production')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
|
@ -268,12 +264,13 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a flag with multiple strategies' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'userWithId', parameters: { userIds: 'user_a,user_b' })
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'gradualRolloutUserId', parameters: { groupId: 'default', percentage: '45' })
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user_a,user_b' })
|
||||
strategy_b = create(:operations_strategy,
|
||||
feature_flag: feature_flag,
|
||||
name: 'gradualRolloutUserId',
|
||||
parameters: { groupId: 'default', percentage: '45' })
|
||||
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
|
||||
create(:operations_scope, strategy: strategy_b, environment_scope: 'production')
|
||||
|
||||
|
|
@ -298,12 +295,12 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns only flags matching the environment scope' do
|
||||
feature_flag_a = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
feature_flag_a = create(:operations_feature_flag,
|
||||
project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag_a)
|
||||
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
|
||||
feature_flag_b = create(:operations_feature_flag, project: project,
|
||||
name: 'feature2', active: true, version: 2)
|
||||
feature_flag_b = create(:operations_feature_flag,
|
||||
project: project, name: 'feature2', active: true, version: 2)
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag_b)
|
||||
create(:operations_scope, strategy: strategy_b, environment_scope: 'staging')
|
||||
|
||||
|
|
@ -322,13 +319,11 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns only strategies matching the environment scope' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
|
||||
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'default', parameters: {})
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
create(:operations_scope, strategy: strategy_b, environment_scope: 'staging')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
|
@ -346,10 +341,12 @@ RSpec.describe API::Unleash do
|
|||
|
||||
it 'returns only flags for the given project' do
|
||||
project_b = create(:project)
|
||||
feature_flag_a = create(:operations_feature_flag, project: project, name: 'feature_a', active: true, version: 2)
|
||||
feature_flag_a = create(:operations_feature_flag,
|
||||
project: project, name: 'feature_a', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag_a)
|
||||
create(:operations_scope, strategy: strategy_a, environment_scope: 'sandbox')
|
||||
feature_flag_b = create(:operations_feature_flag, project: project_b, name: 'feature_b', active: true, version: 2)
|
||||
feature_flag_b = create(:operations_feature_flag,
|
||||
project: project_b, name: 'feature_b', active: true, version: 2)
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag_b)
|
||||
create(:operations_scope, strategy: strategy_b, environment_scope: 'sandbox')
|
||||
|
||||
|
|
@ -367,16 +364,16 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns all strategies with a matching scope' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy_a = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
|
||||
create(:operations_scope, strategy: strategy_a, environment_scope: '*')
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'default', parameters: {})
|
||||
strategy_b = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
create(:operations_scope, strategy: strategy_b, environment_scope: 'review/*')
|
||||
strategy_c = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'gradualRolloutUserId', parameters: { groupId: 'default', percentage: '15' })
|
||||
strategy_c = create(:operations_strategy,
|
||||
feature_flag: feature_flag,
|
||||
name: 'gradualRolloutUserId',
|
||||
parameters: { groupId: 'default', percentage: '15' })
|
||||
create(:operations_scope, strategy: strategy_c, environment_scope: 'review/patch-1')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'review/patch-1' }
|
||||
|
|
@ -395,10 +392,8 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a strategy with more than one matching scope' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'default', parameters: {})
|
||||
feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
create(:operations_scope, strategy: strategy, environment_scope: 'production')
|
||||
create(:operations_scope, strategy: strategy, environment_scope: '*')
|
||||
|
||||
|
|
@ -416,10 +411,9 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a disabled flag with a matching scope' do
|
||||
feature_flag = create(:operations_feature_flag, project: project,
|
||||
name: 'myfeature', active: false, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'default', parameters: {})
|
||||
feature_flag = create(:operations_feature_flag,
|
||||
project: project, name: 'myfeature', active: false, version: 2)
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
|
||||
create(:operations_scope, strategy: strategy, environment_scope: 'production')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
|
@ -436,12 +430,12 @@ RSpec.describe API::Unleash do
|
|||
end
|
||||
|
||||
it 'returns a userWithId strategy for a gitlabUserList strategy' do
|
||||
feature_flag = create(:operations_feature_flag, :new_version_flag, project: project,
|
||||
name: 'myfeature', active: true)
|
||||
user_list = create(:operations_feature_flag_user_list, project: project,
|
||||
name: 'My List', user_xids: 'user1,user2')
|
||||
strategy = create(:operations_strategy, feature_flag: feature_flag,
|
||||
name: 'gitlabUserList', parameters: {}, user_list: user_list)
|
||||
feature_flag = create(:operations_feature_flag, :new_version_flag,
|
||||
project: project, name: 'myfeature', active: true)
|
||||
user_list = create(:operations_feature_flag_user_list,
|
||||
project: project, name: 'My List', user_xids: 'user1,user2')
|
||||
strategy = create(:operations_strategy,
|
||||
feature_flag: feature_flag, name: 'gitlabUserList', parameters: {}, user_list: user_list)
|
||||
create(:operations_scope, strategy: strategy, environment_scope: 'production')
|
||||
|
||||
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ RSpec.describe 'Git HTTP requests' do
|
|||
canonical_project.add_maintainer(user)
|
||||
create(:merge_request,
|
||||
source_project: project,
|
||||
target_project: canonical_project,
|
||||
target_project: canonical_project,
|
||||
source_branch: 'fixes',
|
||||
allow_collaboration: true)
|
||||
end
|
||||
|
|
@ -1105,7 +1105,7 @@ RSpec.describe 'Git HTTP requests' do
|
|||
canonical_project.add_maintainer(user)
|
||||
create(:merge_request,
|
||||
source_project: project,
|
||||
target_project: canonical_project,
|
||||
target_project: canonical_project,
|
||||
source_branch: 'fixes',
|
||||
allow_collaboration: true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -78,11 +78,12 @@ RSpec.describe 'OAuth Tokens requests' do
|
|||
|
||||
context 'revoked refresh token' do
|
||||
let!(:existing_token) do
|
||||
create(:oauth_access_token, application: application,
|
||||
resource_owner_id: user.id,
|
||||
created_at: 2.hours.ago,
|
||||
revoked_at: 1.hour.ago,
|
||||
expires_in: 5)
|
||||
create(:oauth_access_token,
|
||||
application: application,
|
||||
resource_owner_id: user.id,
|
||||
created_at: 2.hours.ago,
|
||||
revoked_at: 1.hour.ago,
|
||||
expires_in: 5)
|
||||
end
|
||||
|
||||
it 'does not issue a new token' do
|
||||
|
|
|
|||
|
|
@ -23,24 +23,24 @@ RSpec.describe 'OpenID Connect requests' do
|
|||
|
||||
let(:id_token_claims) do
|
||||
{
|
||||
'sub' => user.id.to_s,
|
||||
'sub' => user.id.to_s,
|
||||
'sub_legacy' => hashed_subject
|
||||
}
|
||||
end
|
||||
|
||||
let(:user_info_claims) do
|
||||
{
|
||||
'name' => 'Alice',
|
||||
'nickname' => 'alice',
|
||||
'email' => 'public@example.com',
|
||||
'name' => 'Alice',
|
||||
'nickname' => 'alice',
|
||||
'email' => 'public@example.com',
|
||||
'email_verified' => true,
|
||||
'website' => 'https://example.com',
|
||||
'profile' => 'http://localhost/alice',
|
||||
'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png",
|
||||
'groups' => kind_of(Array),
|
||||
'https://gitlab.org/claims/groups/owner' => kind_of(Array),
|
||||
'website' => 'https://example.com',
|
||||
'profile' => 'http://localhost/alice',
|
||||
'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png",
|
||||
'groups' => kind_of(Array),
|
||||
'https://gitlab.org/claims/groups/owner' => kind_of(Array),
|
||||
'https://gitlab.org/claims/groups/maintainer' => kind_of(Array),
|
||||
'https://gitlab.org/claims/groups/developer' => kind_of(Array)
|
||||
'https://gitlab.org/claims/groups/developer' => kind_of(Array)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ RSpec.describe Projects::EnvironmentsController do
|
|||
deployer = create(:user)
|
||||
pipeline = create(:ci_pipeline, project: environment.project)
|
||||
build = create(:ci_build, environment: environment.name, pipeline: pipeline, user: deployer)
|
||||
create(:deployment, :success, environment: environment, deployable: build, user: deployer,
|
||||
project: project, sha: commit.sha)
|
||||
create(:deployment, :success,
|
||||
environment: environment, deployable: build, user: deployer, project: project, sha: commit.sha)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,12 +37,10 @@ RSpec.describe 'merge requests discussions' do
|
|||
it 'avoids N+1 DB queries', :request_store do
|
||||
send_request # warm up
|
||||
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
project: merge_request.project)
|
||||
create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project)
|
||||
control = ActiveRecord::QueryRecorder.new { send_request }
|
||||
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
project: merge_request.project)
|
||||
create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project)
|
||||
|
||||
expect do
|
||||
send_request
|
||||
|
|
@ -51,8 +49,7 @@ RSpec.describe 'merge requests discussions' do
|
|||
|
||||
it 'limits Gitaly queries', :request_store do
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
create_list(:diff_note_on_merge_request, 7, noteable: merge_request,
|
||||
project: merge_request.project)
|
||||
create_list(:diff_note_on_merge_request, 7, noteable: merge_request, project: merge_request.project)
|
||||
end
|
||||
|
||||
# The creations above write into the Gitaly counts
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "rspec_order"
|
||||
require_relative "helpers/stub_configuration"
|
||||
require_relative "helpers/stub_metrics"
|
||||
require_relative "helpers/stub_object_storage"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Support
|
||||
module RspecOrder
|
||||
TODO_YAML = File.join(__dir__, 'rspec_order_todo.yml')
|
||||
|
||||
module_function
|
||||
|
||||
def order_for(example_group)
|
||||
order_from_env || random_order(example_group)
|
||||
end
|
||||
|
||||
def order_from_env
|
||||
return @order_from_env if defined?(@order_from_env)
|
||||
|
||||
# Passing custom defined order via `--order NAME` is not supported.
|
||||
# For example, `--order reverse` does not work so we are passing it via
|
||||
# environment variable RSPEC_ORDER.
|
||||
@order_from_env = ENV['RSPEC_ORDER']
|
||||
end
|
||||
|
||||
def random_order(example_group)
|
||||
path = example_group.metadata.fetch(:file_path)
|
||||
|
||||
:random unless potential_order_dependent?(path)
|
||||
end
|
||||
|
||||
def potential_order_dependent?(path)
|
||||
@todo ||= YAML.load_file(TODO_YAML).to_set # rubocop:disable Gitlab/PredicateMemoization
|
||||
|
||||
@todo.include?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Useful to find order-dependent specs.
|
||||
config.register_ordering(:reverse, &:reverse)
|
||||
|
||||
# Randomization can be reproduced across test runs.
|
||||
Kernel.srand config.seed
|
||||
|
||||
config.on_example_group_definition do |example_group|
|
||||
order = Support::RspecOrder.order_for(example_group)
|
||||
|
||||
if order
|
||||
example_group.metadata[:order] = order.to_sym
|
||||
example_group.metadata[:description] += " (order #{order})"
|
||||
end
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue