Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d6f2690cee
commit
eb9f5ce5d9
|
|
@ -466,6 +466,12 @@ Migration/BatchMigrationsPostOnly:
|
|||
- 'db/migrate/*.rb'
|
||||
- 'db/post_migrate/*.rb'
|
||||
|
||||
Migration/UnfinishedDependencies:
|
||||
Enabled: true
|
||||
Include:
|
||||
- 'db/migrate/*.rb'
|
||||
- 'db/post_migrate/*.rb'
|
||||
|
||||
BackgroundMigration/FeatureCategory:
|
||||
Enabled: true
|
||||
Include:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class ProjectAuthorization < ApplicationRecord
|
|||
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
|
||||
validates :user, uniqueness: { scope: :project }, presence: true
|
||||
|
||||
scope :for_project, ->(projects) { where(project: projects) }
|
||||
scope :non_guests, -> { where('access_level > ?', ::Gitlab::Access::GUEST) }
|
||||
|
||||
# TODO: To be removed after https://gitlab.com/gitlab-org/gitlab/-/issues/418205
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class Todo < ApplicationRecord
|
|||
scope :for_type, -> (type) { where(target_type: type) }
|
||||
scope :for_target, -> (id) { where(target_id: id) }
|
||||
scope :for_commit, -> (id) { where(commit_id: id) }
|
||||
scope :not_in_users, -> (user_ids) { where.not('todos.user_id' => user_ids) }
|
||||
scope :with_entity_associations, -> do
|
||||
preload(:target, :author, :note, group: :route, project: [:route, :group, { namespace: [:route, :owner] }, :project_setting])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,37 +4,6 @@ module Todos
|
|||
module Destroy
|
||||
class BaseService
|
||||
def execute
|
||||
return unless todos_to_remove?
|
||||
|
||||
::Gitlab::Database.allow_cross_joins_across_databases(url:
|
||||
'https://gitlab.com/gitlab-org/gitlab/-/issues/422045') do
|
||||
without_authorized(todos).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def without_authorized(items)
|
||||
items.where.not('todos.user_id' => authorized_users)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def authorized_users
|
||||
ProjectAuthorization.select(:user_id).where(project_id: project_ids)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def todos
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def project_ids
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def todos_to_remove?
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,54 +13,57 @@ module Todos
|
|||
|
||||
attr_reader :issues
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def initialize(issue_id: nil, project_id: nil)
|
||||
@issues =
|
||||
if issue_id
|
||||
Issue.where(id: issue_id)
|
||||
Issue.id_in(issue_id)
|
||||
elsif project_id
|
||||
project_confidential_issues(project_id)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def execute
|
||||
return unless todos_to_remove?
|
||||
|
||||
::Gitlab::Database.allow_cross_joins_across_databases(
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422045') do
|
||||
delete_todos
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delete_todos
|
||||
authorized_users = ProjectAuthorization.select(:user_id)
|
||||
.for_project(project_ids)
|
||||
.non_guests
|
||||
|
||||
todos.not_in_users(authorized_users).delete_all
|
||||
end
|
||||
|
||||
def project_confidential_issues(project_id)
|
||||
project = Project.find(project_id)
|
||||
|
||||
project.issues.confidential_only
|
||||
end
|
||||
|
||||
override :todos
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def todos
|
||||
Todo.joins_issue_and_assignees
|
||||
.where(target: issues)
|
||||
.where(issues: { confidential: true })
|
||||
.for_target(issues)
|
||||
.merge(Issue.confidential_only)
|
||||
.where('todos.user_id != issues.author_id')
|
||||
.where('todos.user_id != issue_assignees.user_id')
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
override :todos_to_remove?
|
||||
def todos_to_remove?
|
||||
issues&.any?(&:confidential?)
|
||||
end
|
||||
|
||||
override :project_ids
|
||||
def project_ids
|
||||
issues&.distinct&.select(:project_id)
|
||||
end
|
||||
|
||||
override :authorized_users
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def authorized_users
|
||||
ProjectAuthorization.select(:user_id)
|
||||
.where(project_id: project_ids)
|
||||
.where('access_level >= ?', Gitlab::Access::REPORTER)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,30 +7,31 @@ module Todos
|
|||
|
||||
attr_reader :group
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def initialize(group_id)
|
||||
@group = Group.find_by(id: group_id)
|
||||
@group = Group.find_by_id(group_id)
|
||||
end
|
||||
|
||||
def execute
|
||||
return unless todos_to_remove?
|
||||
|
||||
delete_todos
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
override :todos
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def todos
|
||||
Todo.where(group_id: group.id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
override :authorized_users
|
||||
def authorized_users
|
||||
User.from_union([
|
||||
def delete_todos
|
||||
authorized_users = User.from_union([
|
||||
group.project_users_with_descendants.select(:id),
|
||||
group.members_with_parents.select(:user_id)
|
||||
], remove_duplicates: false)
|
||||
|
||||
todos.not_in_users(authorized_users).delete_all
|
||||
end
|
||||
|
||||
def todos
|
||||
Todo.for_group(group.id)
|
||||
end
|
||||
|
||||
override :todos_to_remove?
|
||||
def todos_to_remove?
|
||||
group&.private?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,27 +7,32 @@ module Todos
|
|||
|
||||
attr_reader :project
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def initialize(project_id)
|
||||
@project = Project.find_by(id: project_id)
|
||||
@project = Project.find_by_id(project_id)
|
||||
end
|
||||
|
||||
def execute
|
||||
return unless todos_to_remove?
|
||||
|
||||
delete_todos
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
override :todos
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def todos
|
||||
Todo.where(project_id: project.id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
def delete_todos
|
||||
authorized_users = ProjectAuthorization.select(:user_id).for_project(project_ids)
|
||||
|
||||
todos.not_in_users(authorized_users).delete_all
|
||||
end
|
||||
|
||||
def todos
|
||||
Todo.for_project(project.id)
|
||||
end
|
||||
|
||||
override :project_ids
|
||||
def project_ids
|
||||
project.id
|
||||
end
|
||||
|
||||
override :todos_to_remove?
|
||||
def todos_to_remove?
|
||||
project&.private?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,6 +27,14 @@ module Todos
|
|||
|
||||
private
|
||||
|
||||
def without_authorized(items)
|
||||
items.not_in_users(authorized_users)
|
||||
end
|
||||
|
||||
def authorized_users
|
||||
ProjectAuthorization.select(:user_id).for_project(project_ids)
|
||||
end
|
||||
|
||||
def related_todos
|
||||
base_scope = Todo.for_project(project_id)
|
||||
base_scope = base_scope.for_user(user_id) if user_id
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropIndexNamespacesOnUpdatedAt < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :namespaces
|
||||
INDEX_NAME = :index_namespaces_on_updated_at
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
# Since adding the same index will be time consuming,
|
||||
# we have to create it asynchronously using 'prepare_async_index' helper.
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
0437aade771694981f1eca79c1bd8fed0857f1f10f1a616684fab3c906bd20b7
|
||||
|
|
@ -33199,8 +33199,6 @@ CREATE INDEX index_namespaces_on_traversal_ids_for_groups_btree ON namespaces US
|
|||
|
||||
CREATE INDEX index_namespaces_on_type_and_id ON namespaces USING btree (type, id);
|
||||
|
||||
CREATE INDEX index_namespaces_on_updated_at ON namespaces USING btree (updated_at);
|
||||
|
||||
CREATE INDEX index_namespaces_public_groups_name_id ON namespaces USING btree (name, id) WHERE (((type)::text = 'Group'::text) AND (visibility_level = 20));
|
||||
|
||||
CREATE INDEX index_namespaces_sync_events_on_namespace_id ON namespaces_sync_events USING btree (namespace_id);
|
||||
|
|
|
|||
|
|
@ -2174,7 +2174,7 @@ Example response:
|
|||
"user_id": 42,
|
||||
"active": true,
|
||||
"expires_at": "2020-10-15",
|
||||
"token": "ggbfKkC4n-Lujy8jwCR2"
|
||||
"token": "<your_new_access_token>"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,192 @@ The Vue app for rendering diffs uses many different Vue components, some of whic
|
|||
with other areas of the GitLab app. The below chart shows the direction for which components
|
||||
get rendered.
|
||||
|
||||
NOTE:
|
||||
[Issue #388843](https://gitlab.com/gitlab-org/gitlab/-/issues/388843) is open to
|
||||
generate a Mermaid graph of the components diagram. An image version of the
|
||||
diagram is available in the issue.
|
||||
This chart contains several types of items:
|
||||
|
||||
| Legend item | Interpretation |
|
||||
| ----------- | -------------- |
|
||||
| `xxx~~`, `ee-xxx~~` | A shortened directory path name. Can be found in `[ee]/app/assets/javascripts`, and omits `0..n` nested folders. |
|
||||
| Rectangular nodes | Files. |
|
||||
| Oval nodes | Plain language describing a deeper concept. |
|
||||
| Double-rectangular nodes | Simplified code branch. |
|
||||
| Diamond and circle nodes | Branches that have 2 (diamond) or 3+ (circle) options. |
|
||||
| Pendant / banner nodes (left notch, right square) | A parent directory to shorten nested paths. |
|
||||
| `./` | A path relative to the closest parent directory pendant node. Non-relative paths nested under parent pendant nodes are not in that directory. |
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
classDef code font-family: monospace;
|
||||
|
||||
A["diffs~~app.vue"]
|
||||
descVirtualScroller(["Virtual Scroller"])
|
||||
codeForFiles[["v-for(diffFiles)"]]
|
||||
B["diffs~~diff_file.vue"]
|
||||
C["diffs~~diff_file_header.vue"]
|
||||
D["diffs~~diff_stats.vue"]
|
||||
E["diffs~~diff_content.vue"]
|
||||
boolFileIsText{isTextFile}
|
||||
boolOnlyWhitespace{isWhitespaceOnly}
|
||||
boolNotDiffable{notDiffable}
|
||||
boolNoPreview{noPreview}
|
||||
descShowChanges(["Show button to "Show changes""])
|
||||
%% Non-text changes
|
||||
dirDiffViewer>"vue_shared~~diff_viewer"]
|
||||
F["./viewers/not_diffable.vue"]
|
||||
G["./viewers/no_preview.vue"]
|
||||
H["./diff_viewer.vue"]
|
||||
I["diffs~~diff_view.vue"]
|
||||
boolIsRenamed{isRenamed}
|
||||
boolIsModeChanged{isModeChanged}
|
||||
boolFileHasNoPath{hasNewPath}
|
||||
boolIsImage{isImage}
|
||||
J["./viewers/renamed.vue"]
|
||||
K["./viewers/mode_changed.vue"]
|
||||
descNoViewer(["No viewer is rendered"])
|
||||
L["./viewers/image_diff_viewer.vue"]
|
||||
M["./viewers/download.vue"]
|
||||
N["vue_shared~~download_diff_viewer.vue"]
|
||||
boolImageIsReplaced{isReplaced}
|
||||
O["vue_shared~~image_viewer.vue"]
|
||||
switchImageMode((image_diff_viewer.mode))
|
||||
P["./viewers/image_diff/onion_skin_viewer.vue"]
|
||||
Q["./viewers/image_diff/swipe_viewer.vue"]
|
||||
R["./viewers/image_diff/two_up_viewer.vue"]
|
||||
S["diffs~~image_diff_overlay.vue"]
|
||||
codeForImageDiscussions[["v-for(discussions)"]]
|
||||
T["vue_shared~~design_note_pin.vue"]
|
||||
U["vue_shared~~user_avatar_link.vue"]
|
||||
V["diffs~~diff_discussions.vue"]
|
||||
W["batch_comments~~diff_file_drafts.vue"]
|
||||
codeForTwoUpDiscussions[["v-for(discussions)"]]
|
||||
codeForTwoUpDrafts[["v-for(drafts)"]]
|
||||
X["notes~~notable_discussion.vue"]
|
||||
%% Text-file changes
|
||||
codeForDiffLines[["v-for(diffLines)"]]
|
||||
Y["diffs~~diff_expansion_cell.vue"]
|
||||
Z["diffs~~diff_row.vue"]
|
||||
AA["diffs~~diff_line.vue"]
|
||||
AB["batch_comments~~draft_note.vue"]
|
||||
AC["diffs~~diff_comment_cell.vue"]
|
||||
AD["diffs~~diff_gutter_avatars.vue"]
|
||||
AE["ee-diffs~~inline_findings_flag_switcher.vue"]
|
||||
AF["notes~~noteable_note.vue"]
|
||||
AG["unknown~~publish_button.vue"]
|
||||
AH["notes~~note_actions.vue"]
|
||||
AI["notes~~note_body.vue"]
|
||||
AJ["notes~~note_header.vue"]
|
||||
AK["notes~~reply_button.vue"]
|
||||
AL["notes~~note_awards_list.vue"]
|
||||
AM["notes~~note_edited_text.vue"]
|
||||
AN["notes~~note_form.vue"]
|
||||
AO["vue_shared~~awards_list.vue"]
|
||||
AP["emoji~~picker.vue"]
|
||||
AQ["emoji~~emoji_list.vue"]
|
||||
descEmojiVirtualScroll(["Virtual Scroller"])
|
||||
AR["emoji~~category.vue"]
|
||||
AS["emoji~emoji_category.vue"]
|
||||
AT["vue_shared~~markdown_editor.vue"]
|
||||
|
||||
class codeForFiles,codeForImageDiscussions code;
|
||||
class codeForTwoUpDiscussions,codeForTwoUpDrafts code;
|
||||
class codeForDiffLines code;
|
||||
%% Also apply code styling to this switch node
|
||||
class switchImageMode code;
|
||||
%% Also apply code styling to these boolean nodes
|
||||
class boolFileIsText,boolOnlyWhitespace,boolNotDiffable,boolNoPreview code;
|
||||
class boolIsRenamed,boolIsModeChanged,boolFileHasNoPath,boolIsImage code;
|
||||
class boolImageIsReplaced code;
|
||||
|
||||
A --> descVirtualScroller
|
||||
A -->|"Virtual Scroller is
|
||||
disabled when
|
||||
Find in page search
|
||||
(Cmd/Ctrl+f) is used."|codeForFiles
|
||||
descVirtualScroller --> codeForFiles
|
||||
codeForFiles --> B
|
||||
B --> C
|
||||
C --> D
|
||||
B --> E
|
||||
|
||||
%% File view flags cascade
|
||||
E --> boolFileIsText
|
||||
boolFileIsText --> |yes| I
|
||||
boolFileIsText --> |no| boolOnlyWhitespace
|
||||
|
||||
boolOnlyWhitespace --> |yes| descShowChanges
|
||||
boolOnlyWhitespace --> |no| dirDiffViewer
|
||||
|
||||
dirDiffViewer --> H
|
||||
|
||||
H --> boolNotDiffable
|
||||
|
||||
boolNotDiffable --> |yes| F
|
||||
boolNotDiffable --> |no| boolNoPreview
|
||||
|
||||
boolNoPreview --> |yes| G
|
||||
boolNoPreview --> |no| boolIsRenamed
|
||||
|
||||
boolIsRenamed --> |yes| J
|
||||
boolIsRenamed --> |no| boolIsModeChanged
|
||||
|
||||
boolIsModeChanged --> |yes| K
|
||||
boolIsModeChanged --> |no| boolFileHasNoPath
|
||||
|
||||
boolFileHasNoPath --> |yes| boolIsImage
|
||||
boolFileHasNoPath --> |no| descNoViewer
|
||||
|
||||
boolIsImage --> |yes| L
|
||||
boolIsImage --> |no| M
|
||||
M --> N
|
||||
|
||||
%% Image diff viewer
|
||||
L --> boolImageIsReplaced
|
||||
|
||||
boolImageIsReplaced --> |yes| switchImageMode
|
||||
boolImageIsReplaced --> |no| O
|
||||
|
||||
switchImageMode -->|"'twoup' (default)"| R
|
||||
switchImageMode -->|'onion'| P
|
||||
switchImageMode -->|'swipe'| Q
|
||||
|
||||
P & Q --> S
|
||||
S --> codeForImageDiscussions
|
||||
S --> AN
|
||||
|
||||
R-->|"Rendered in
|
||||
note container div"|U & W & V
|
||||
R --> S
|
||||
|
||||
V --> codeForTwoUpDiscussions
|
||||
W --> codeForTwoUpDrafts
|
||||
|
||||
%% This invisible link forces `noteable_discussion`
|
||||
%% to render above `design_note_pin`
|
||||
X ~~~ T
|
||||
|
||||
codeForTwoUpDrafts --> AB
|
||||
codeForImageDiscussions & codeForTwoUpDiscussions & codeForTwoUpDrafts --> T
|
||||
codeForTwoUpDiscussions --> X
|
||||
|
||||
%% Text file diff viewer
|
||||
I --> codeForDiffLines
|
||||
codeForDiffLines --> Z
|
||||
codeForDiffLines -->|"isMatchLine?"| Y
|
||||
codeForDiffLines -->|"hasCodeQuality?"| AA
|
||||
codeForDiffLines -->|"hasDraftNote(s)?"| AB
|
||||
|
||||
Z -->|"hasCodeQuality?"| AE
|
||||
Z -->|"hasDiscussions?"| AD
|
||||
|
||||
AA --> AC
|
||||
|
||||
%% Draft notes
|
||||
AB --> AF
|
||||
AF --> AH & AI & AJ
|
||||
AH --> AK
|
||||
AI --> AL & AM & AN
|
||||
AL --> AO --> AP --> AQ --> descEmojiVirtualScroll --> AR --> AS
|
||||
AN --> AT
|
||||
```
|
||||
|
||||
Some of the components are rendered more than others, but the main component is `diff_row.vue`.
|
||||
This component renders every diff line in a diff file. For performance reasons, this
|
||||
|
|
|
|||
|
|
@ -62,13 +62,15 @@ To enable the Facebook OmniAuth provider, you must:
|
|||
|
||||
1. On your GitLab server, open the configuration file:
|
||||
|
||||
- For Linux package installations:
|
||||
::Tabs
|
||||
|
||||
:::TabTitle Linux package installations
|
||||
|
||||
```shell
|
||||
sudo editor /etc/gitlab/gitlab.rb
|
||||
```
|
||||
|
||||
- For self-compiled installations:
|
||||
:::TabTitle Self-compiled installations
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
|
|
@ -76,13 +78,17 @@ To enable the Facebook OmniAuth provider, you must:
|
|||
sudo -u git -H editor config/gitlab.yml
|
||||
```
|
||||
|
||||
::EndTabs
|
||||
|
||||
1. Configure the [common settings](omniauth.md#configure-common-settings)
|
||||
to add `facebook` as a single sign-on provider. This enables Just-In-Time
|
||||
account provisioning for users who do not have an existing GitLab account.
|
||||
|
||||
1. Add the provider configuration:
|
||||
|
||||
- For Linux package installations:
|
||||
::Tabs
|
||||
|
||||
:::TabTitle Linux package installations
|
||||
|
||||
```ruby
|
||||
gitlab_rails['omniauth_providers'] = [
|
||||
|
|
@ -95,7 +101,7 @@ To enable the Facebook OmniAuth provider, you must:
|
|||
]
|
||||
```
|
||||
|
||||
- For self-compiled installations:
|
||||
:::TabTitle Self-compiled installations
|
||||
|
||||
```yaml
|
||||
- { name: 'facebook',
|
||||
|
|
@ -104,6 +110,8 @@ To enable the Facebook OmniAuth provider, you must:
|
|||
app_secret: 'YOUR_APP_SECRET' }
|
||||
```
|
||||
|
||||
::EndTabs
|
||||
|
||||
1. In the provide configuration, paste the following values:
|
||||
|
||||
1. `YOUR_APP_ID`: The **App ID** you copied in the previous step.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ with runner authentication tokens, which have replaced the deprecated registrati
|
|||
method that uses registration tokens. For more information, see
|
||||
[The new runner registration workflow](../../ci/runners/new_creation_workflow.md#the-new-runner-registration-workflow).
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
- GitLab Runner must be installed on your GitLab instance.
|
||||
- To create shared runners, you must be an administrator.
|
||||
|
|
@ -97,7 +97,7 @@ To create a runner configuration, you can use:
|
|||
|
||||
### With the GitLab REST API
|
||||
|
||||
Prerequisites:
|
||||
Before you begin, you need:
|
||||
|
||||
- The URL for your GitLab instance. For example, if your project is hosted on
|
||||
`gitlab.example.com/yourname/yourproject`, your GitLab instance URL is
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ To set up issue boards for multiple teams:
|
|||
1. [Create team issue boards](#create-team-issue-boards)
|
||||
1. [Create issues for features](#create-issues-for-features)
|
||||
|
||||
## Before you begin
|
||||
|
||||
- If you're using an existing group for this tutorial, make sure you have at least the Reporter role for the group.
|
||||
- If you're using an existing project for this tutorial, make sure you have at least the Reporter role for the project.
|
||||
|
||||
## The goal workflow
|
||||
|
||||
After you set up everything, the two teams will be able to hand off issues from one board to another, for example, like this:
|
||||
|
|
@ -55,11 +60,6 @@ To prepare for when your project grows, start by creating a group.
|
|||
You use groups to manage one or more related projects at the same time.
|
||||
You add your users as members in the group, and assign them a role.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- If you're using an existing group for this tutorial, make sure you have at least the Reporter role
|
||||
for the group.
|
||||
|
||||
To create a group:
|
||||
|
||||
1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New group**.
|
||||
|
|
@ -75,11 +75,6 @@ The main code development work happens in projects and their repositories.
|
|||
A project contains your code and pipelines, but also the issues that are used for planning your
|
||||
upcoming code changes.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- If you're using an existing project for this tutorial, make sure you have at least the Reporter role
|
||||
for the project.
|
||||
|
||||
To create a blank project:
|
||||
|
||||
1. In your group, on the left sidebar, at the top, select **Create new** (**{plus}**) and then select
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ In this tutorial, you:
|
|||
1. Create a [new project and apply the compliance framework](#create-a-new-project-and-apply-the-compliance-framework) to it.
|
||||
1. Combine [compliance pipeline configuration and regular pipeline configuration](#combine-pipeline-configurations).
|
||||
|
||||
Prerequisites:
|
||||
## Before you begin
|
||||
|
||||
- Permission to create new top-level groups.
|
||||
- You need permission to create new top-level groups.
|
||||
|
||||
## Create a new group
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ To configure GitLab Runner to use the GKE:
|
|||
1. [Install and configure the Kubernetes Operator](#install-and-configure-the-kubernetes-operator).
|
||||
1. Optional. [Verify that the configuration was successful](#verify-your-configuration).
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
Before you can configure GitLab Runner to use the GKE you must:
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ configuration:
|
|||
1. [Create and register a project runner](#create-and-register-a-project-runner).
|
||||
1. [Trigger a pipeline to run your runner](#trigger-a-pipeline-to-run-your-runner).
|
||||
|
||||
## Prerequisite
|
||||
## Before you begin
|
||||
|
||||
Before you can create, register, and run a runner, [GitLab Runner](https://docs.gitlab.com/runner/install/) must be installed on a local computer.
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ To set up dependency scanning:
|
|||
- [Resolve the high severity vulnerability](#resolve-the-high-severity-vulnerability).
|
||||
- [Test vulnerability detection in a merge request](#test-vulnerability-detection-in-a-merge-request).
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
Before you begin, make sure you have GitPod enabled. GitPod is an on-demand cloud development
|
||||
Make sure you have GitPod enabled. GitPod is an on-demand cloud development
|
||||
environment. For details, see [GitPod](../integration/gitpod.md). Alternatively you can use your
|
||||
own development setup. In this case you need to have Yarn and Node.js installed.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Dependency Scanning output can be exported to the CycloneDX JSON format.
|
|||
|
||||
This tutorial shows you how to generate a CycloneDX JSON SBOM for a pipeline, and then to upload it as a CI job artifact.
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
Set up Dependency Scanning. For detailed instructions, follow [the Dependency Scanning tutorial](dependency_scanning.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Here's an overview of what you're going to do:
|
|||
1. Build your Hugo site with a CI/CD pipeline.
|
||||
1. Deploy and view your Hugo site with GitLab Pages.
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
- An account on GitLab.com.
|
||||
- Familiarity with Git.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ To install a single node GitLab instance and configure it to be secure:
|
|||
1. [Configure GitLab](#configure-gitlab)
|
||||
1. [Next steps](#next-steps)
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
- A domain name, and a correct [setup of DNS](https://docs.gitlab.com/omnibus/settings/dns.html).
|
||||
- A Debian-based server with the following minimum specs:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ To set up GitLab for issue triage in a project:
|
|||
1. [Create an issue triage board](#create-an-issue-triage-board)
|
||||
1. [Create issues for features](#create-issues-for-features)
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
- If you're using an existing project for this tutorial, make sure you have at least the Reporter role
|
||||
for the project.
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ committing changes to a Git repository from the command line.
|
|||
|
||||
When you're done, you'll have a project where you can practice using Git.
|
||||
|
||||
## What you need
|
||||
|
||||
Before you begin:
|
||||
## Before you begin
|
||||
|
||||
- [Install Git on your local machine](../../topics/git/how_to_install_git/index.md).
|
||||
- Ensure you can sign in to an instance of GitLab. If your organization doesn't
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ You're going to create:
|
|||
1. A project in the organization for a specific piece of work, and add users to
|
||||
that project.
|
||||
|
||||
Prerequisites:
|
||||
## Before you begin
|
||||
|
||||
- You have administrator access to your self-managed GitLab instance.
|
||||
- Make sure you have administrator access to your self-managed GitLab instance.
|
||||
|
||||
## Create the organization parent group and subgroups
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ release branches, and creates a minimal approval workflow for the project:
|
|||
1. [Enforce CODEOWNER approval on branches](#enforce-codeowner-approval-on-branches)
|
||||
1. [Create the release branches](#create-the-release-branches)
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
- You must have the Maintainer or Owner role.
|
||||
- You need a list of managers and their email addresses.
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ In this tutorial, you:
|
|||
- [Link project B to the security policy project](#link-project-b-to-the-security-policy-project).
|
||||
- [Test the scan execution policy with project B](#test-the-scan-execution-policy-with-project-b).
|
||||
|
||||
Prerequisite:
|
||||
## Before you begin
|
||||
|
||||
- Permission to create new projects in an existing group.
|
||||
- You need permission to create new projects in an existing group.
|
||||
|
||||
## Create project A
|
||||
|
||||
|
|
|
|||
|
|
@ -9,19 +9,19 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
This tutorial shows you how to create and configure a [scan result policy](../../user/application_security/policies/scan-result-policies.md). These policies can be set to take action based on scan results.
|
||||
For example, in this tutorial, you'll set up a policy that requires approval from two specified users if a vulnerability is detected in a merge request.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
The namespace used for this tutorial must:
|
||||
|
||||
- Contain a minimum of three users, including your own. If you don't have two other users, you must first
|
||||
create them. For details, see [Creating users](../../user/profile/account/create_accounts.md).
|
||||
|
||||
To set up a scan result policy:
|
||||
|
||||
1. [Create a test project](#create-a-test-project).
|
||||
1. [Add a scan result policy](#add-a-scan-result-policy).
|
||||
1. [Test the scan result policy](#test-the-scan-result-policy).
|
||||
|
||||
## Before you begin
|
||||
|
||||
The namespace used for this tutorial must:
|
||||
|
||||
- Contain a minimum of three users, including your own. If you don't have two other users, you must first
|
||||
create them. For details, see [Creating users](../../user/profile/account/create_accounts.md).
|
||||
|
||||
## Create a test project
|
||||
|
||||
1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**.
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ To rewrite any number of commit messages:
|
|||
1. [Update the commit messages](#update-the-commit-messages).
|
||||
1. [Push the changes up to GitLab](#push-the-changes-up-to-gitlab).
|
||||
|
||||
## Prerequisites
|
||||
## Before you begin
|
||||
|
||||
You must have:
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,6 @@ If this list seems long and you're not sure where to start, then this tutorial i
|
|||
Follow along to learn how to set up an example website project, collaborate with other GitLab users,
|
||||
and use project-level analytics reports to evaluate the development of your project.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have the Owner role for the group in which you create the project.
|
||||
|
||||
Here's an overview of what we're going to do:
|
||||
|
||||
1. Create a project from a template.
|
||||
|
|
@ -28,6 +24,10 @@ Here's an overview of what we're going to do:
|
|||
1. Create an Insights report.
|
||||
1. View merge request and issue analytics.
|
||||
|
||||
## Before you begin
|
||||
|
||||
- You must have the Owner role for the group in which you create the project.
|
||||
|
||||
## Create a project from a template
|
||||
|
||||
First of all, you need to create a project in your group.
|
||||
|
|
|
|||
|
|
@ -177,6 +177,24 @@ You need at least the Developer role to move a wiki page:
|
|||
change the **Title** from `about` to `/about`.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Export a wiki page
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/414691) in GitLab 16.3 [with a flag](../../../administration/feature_flags.md) named `print_wiki`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `print_wiki`.
|
||||
On GitLab.com, this feature is not available.
|
||||
|
||||
You can export a wiki page as a PDF file:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project or group.
|
||||
1. Select **Plan > Wiki**.
|
||||
1. Go to the page you want to export.
|
||||
1. Select the vertical ellipsis (**{ellipsis_v}**), and then select **Print as PDF**.
|
||||
|
||||
A PDF of the wiki page is created.
|
||||
|
||||
## View history of a wiki page
|
||||
|
||||
The changes of a wiki page over time are recorded in the wiki's Git repository.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ class <%= migration_class_name %> < Gitlab::Database::Migration[<%= Gitlab::Data
|
|||
# comments:
|
||||
# disable_ddl_transaction!
|
||||
|
||||
# Add dependent 'batched_background_migrations.queued_migration_version' values.
|
||||
# DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = []
|
||||
|
||||
def change
|
||||
create_table :<%= table_name %> do |t|
|
||||
<% attributes.each do |attribute| -%>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ class <%= migration_class_name %> < Gitlab::Database::Migration[<%= Gitlab::Data
|
|||
# Visit: https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html
|
||||
# restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
# Add dependent 'batched_background_migrations.queued_migration_version' values.
|
||||
# DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = []
|
||||
|
||||
<%- if migration_action == 'add' -%>
|
||||
def change
|
||||
<% attributes.each do |attribute| -%>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ class <%= migration_class_name %> < Gitlab::Database::Migration[<%= Gitlab::Data
|
|||
# Visit: https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html
|
||||
# restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
# Add dependent 'batched_background_migrations.queued_migration_version' values.
|
||||
# DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = []
|
||||
|
||||
def up
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,3 +5,6 @@ feature_category: <%= feature_category %>
|
|||
introduced_by_url: # URL of the MR (or issue/commit) that introduced the migration
|
||||
milestone: <%= current_milestone %>
|
||||
queued_migration_version: <%= migration_number %>
|
||||
# Replace with the approximate date you think it's best to ensure the completion of this BBM.
|
||||
finalize_after: # yyyy-mm-dd
|
||||
finalized_by: # version of the migration that ensured this bbm
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
class BatchedBackgroundMigrations
|
||||
DICTIONARY_BASE_DIR = 'db/docs/batched_background_migrations'
|
||||
|
||||
attr_reader :queued_migration_version
|
||||
|
||||
class << self
|
||||
def dictionary_data
|
||||
@dictionary_data ||= Dir.glob("*.yml", base: DICTIONARY_BASE_DIR).each_with_object({}) do |file_name, data|
|
||||
dictionary = YAML.load_file(File.join(DICTIONARY_BASE_DIR, file_name))
|
||||
|
||||
next unless dictionary['queued_migration_version'].present?
|
||||
|
||||
data[dictionary['queued_migration_version'].to_s] = {
|
||||
finalize_after: dictionary['finalize_after'],
|
||||
finalized_by: dictionary['finalized_by'].to_s
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(queued_migration_version)
|
||||
@queued_migration_version = queued_migration_version
|
||||
end
|
||||
|
||||
def finalized_by
|
||||
self.class.dictionary_data.dig(queued_migration_version.to_s, :finalized_by)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../migration_helpers'
|
||||
require_relative '../../batched_background_migrations'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Migration
|
||||
# Checks if there are any unfinished dependent batched bg migrations
|
||||
class UnfinishedDependencies < RuboCop::Cop::Base
|
||||
include MigrationHelpers
|
||||
|
||||
NOT_FINALIZED_MSG = <<-MESSAGE.delete("\n").squeeze(' ').strip
|
||||
Dependent migration with queued version %{version} is not yet finalized.
|
||||
Consider finalizing the dependent migration and update it's finalized_by attr in the dictionary.
|
||||
MESSAGE
|
||||
|
||||
FINALIZED_BY_LATER_MIGRATION_MSG = <<-MESSAGE.delete("\n").squeeze(' ').strip
|
||||
Dependent migration with queued version %{version} is finalized by later migration,
|
||||
it has to be finalized before the current migration.
|
||||
MESSAGE
|
||||
|
||||
def_node_matcher :dependent_migration_versions, <<~PATTERN
|
||||
(casgn nil? :DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS (array $...))
|
||||
PATTERN
|
||||
|
||||
def on_casgn(node)
|
||||
return unless in_migration?(node)
|
||||
|
||||
migration_version = version(node)
|
||||
|
||||
dependent_migration_versions(node)&.each do |dependent_migration_version_node|
|
||||
dependent_migration_version = dependent_migration_version_node.value
|
||||
finalized_by_version = fetch_finalized_by(dependent_migration_version)
|
||||
|
||||
next if finalized_by_version.present? && finalized_by_version.to_s < migration_version.to_s
|
||||
|
||||
msg = finalized_by_version.present? ? FINALIZED_BY_LATER_MIGRATION_MSG : NOT_FINALIZED_MSG
|
||||
add_offense(node, message: format(msg, version: dependent_migration_version))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_finalized_by(queued_migration_version)
|
||||
BatchedBackgroundMigrations.new(queued_migration_version).finalized_by
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,3 +5,6 @@ feature_category: database
|
|||
introduced_by_url: # URL of the MR \(or issue/commit\) that introduced the migration
|
||||
milestone: [0-9\.]+
|
||||
queued_migration_version: [0-9]+
|
||||
# Replace with the approximate date you think it's best to ensure the completion of this BBM.
|
||||
finalize_after: # yyyy-mm-dd
|
||||
finalized_by: # version of the migration that ensured this bbm
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ class CreateModelGeneratorTestFoos < Gitlab::Database::Migration[2.1]
|
|||
# comments:
|
||||
# disable_ddl_transaction!
|
||||
|
||||
# Add dependent 'batched_background_migrations.queued_migration_version' values.
|
||||
# DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = []
|
||||
|
||||
def change
|
||||
create_table :model_generator_test_foos do |t|
|
||||
|
||||
|
|
|
|||
|
|
@ -83,8 +83,10 @@ RSpec.describe ProjectAuthorization, feature_category: :groups_and_projects do
|
|||
end
|
||||
|
||||
describe 'scopes' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, namespace: user.namespace) }
|
||||
|
||||
describe '.non_guests' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:project_original_owner_authorization) { project.owner.project_authorizations.first }
|
||||
let_it_be(:project_authorization_guest) { create(:project_authorization, :guest, project: project) }
|
||||
let_it_be(:project_authorization_reporter) { create(:project_authorization, :reporter, project: project) }
|
||||
|
|
@ -100,6 +102,28 @@ RSpec.describe ProjectAuthorization, feature_category: :groups_and_projects do
|
|||
].map(&:attributes))
|
||||
end
|
||||
end
|
||||
|
||||
describe '.for_project' do
|
||||
let_it_be(:project_2) { create(:project, namespace: user.namespace) }
|
||||
let_it_be(:project_3) { create(:project, namespace: user.namespace) }
|
||||
|
||||
let_it_be(:project_authorization_3) { project_3.project_authorizations.first }
|
||||
let_it_be(:project_authorization_2) { project_2.project_authorizations.first }
|
||||
let_it_be(:project_authorization) { project.project_authorizations.first }
|
||||
|
||||
it 'returns all records for the project' do
|
||||
expect(described_class.for_project(project).map(&:attributes)).to match_array([
|
||||
project_authorization
|
||||
].map(&:attributes))
|
||||
end
|
||||
|
||||
it 'returns all records for multiple projects' do
|
||||
expect(described_class.for_project([project, project_3]).map(&:attributes)).to match_array([
|
||||
project_authorization,
|
||||
project_authorization_3
|
||||
].map(&:attributes))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.insert_all' do
|
||||
|
|
|
|||
|
|
@ -396,6 +396,19 @@ RSpec.describe Todo do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.not_in_users' do
|
||||
it 'returns the expected todos' do
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
|
||||
todo1 = create(:todo, user: user1)
|
||||
todo2 = create(:todo, user: user1)
|
||||
create(:todo, user: user2)
|
||||
|
||||
expect(described_class.not_in_users(user2)).to contain_exactly(todo1, todo2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.for_group_ids_and_descendants' do
|
||||
it 'returns the todos for a group and its descendants' do
|
||||
parent_group = create(:group)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
|
||||
require_relative '../../rubocop/batched_background_migrations'
|
||||
|
||||
RSpec.describe RuboCop::BatchedBackgroundMigrations, feature_category: :database do
|
||||
let(:bbm_dictionary_file_name) { "#{described_class::DICTIONARY_BASE_DIR}/test_migration.yml" }
|
||||
let(:migration_version) { 20230307160250 }
|
||||
let(:finalized_by_version) { 20230307160255 }
|
||||
let(:bbm_dictionary_data) do
|
||||
{
|
||||
migration_job_name: 'TestMigration',
|
||||
feature_category: :database,
|
||||
introduced_by_url: 'https://test_url',
|
||||
milestone: 16.5,
|
||||
queued_migration_version: migration_version,
|
||||
finalized_by: finalized_by_version
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
File.open(bbm_dictionary_file_name, 'w') do |file|
|
||||
file.write(bbm_dictionary_data.stringify_keys.to_yaml)
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm(bbm_dictionary_file_name)
|
||||
end
|
||||
|
||||
subject(:batched_background_migration) { described_class.new(migration_version) }
|
||||
|
||||
describe '#finalized_by' do
|
||||
it 'returns the finalized_by version of the bbm with given version' do
|
||||
expect(batched_background_migration.finalized_by).to eq(finalized_by_version.to_s)
|
||||
end
|
||||
|
||||
it 'returns nothing for non-existing bbm dictionary' do
|
||||
expect(described_class.new('random').finalized_by).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'stringio'
|
||||
|
||||
require_relative '../support/helpers/next_instance_of'
|
||||
require 'rubocop_spec_helper'
|
||||
require_relative '../../rubocop/check_graceful_task'
|
||||
|
||||
RSpec.describe RuboCop::CheckGracefulTask do
|
||||
include NextInstanceOf
|
||||
|
||||
let(:output) { StringIO.new }
|
||||
|
||||
subject(:task) { described_class.new(output) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require_relative '../../../../rubocop/cop/migration/unfinished_dependencies'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Migration::UnfinishedDependencies, feature_category: :database do
|
||||
let(:version) { 20230307160250 }
|
||||
|
||||
let(:migration) do
|
||||
<<~RUBY
|
||||
class TestMigration < Gitlab::Database::Migration[2.1]
|
||||
def perform; end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
before do
|
||||
allow(cop).to receive(:in_migration?).and_return(true)
|
||||
|
||||
allow(cop).to receive(:version).and_return(version)
|
||||
end
|
||||
|
||||
shared_examples 'migration with rubocop offense' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(migration)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'migration without any rubocop offense' do
|
||||
it 'does not register any offense' do
|
||||
expect_no_offenses(migration)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without any dependent batched background migrations' do
|
||||
it_behaves_like 'migration without any rubocop offense'
|
||||
end
|
||||
|
||||
context 'with dependent batched background migrations' do
|
||||
let(:dependent_migration_versions) { [20230307160240] }
|
||||
|
||||
let(:migration) do
|
||||
<<~RUBY
|
||||
class TestMigration < Gitlab::Database::Migration[2.1]
|
||||
DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = #{dependent_migration_versions}
|
||||
|
||||
def perform; end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
context 'with unfinished dependent migration' do
|
||||
before do
|
||||
allow(cop).to receive(:fetch_finalized_by)
|
||||
.with(dependent_migration_versions.first)
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
it_behaves_like 'migration with rubocop offense' do
|
||||
let(:migration) do
|
||||
<<~RUBY
|
||||
class TestMigration < Gitlab::Database::Migration[2.1]
|
||||
DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = #{dependent_migration_versions}
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format(described_class::NOT_FINALIZED_MSG, version: dependent_migration_versions.first)}
|
||||
|
||||
def perform; end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with incorrectly finalized dependent migration' do
|
||||
let(:dependent_migration_versions) { [20230307160240, 20230307160230] }
|
||||
|
||||
before do
|
||||
allow(cop).to receive(:fetch_finalized_by)
|
||||
.with(dependent_migration_versions.first)
|
||||
.and_return(version - 10)
|
||||
|
||||
allow(cop).to receive(:fetch_finalized_by)
|
||||
.with(dependent_migration_versions.last)
|
||||
.and_return(version + 10)
|
||||
end
|
||||
|
||||
it_behaves_like 'migration with rubocop offense' do
|
||||
let(:migration) do
|
||||
<<~RUBY
|
||||
class TestMigration < Gitlab::Database::Migration[2.1]
|
||||
DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = #{dependent_migration_versions}
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format(described_class::FINALIZED_BY_LATER_MIGRATION_MSG, version: dependent_migration_versions.last)}
|
||||
|
||||
def perform; end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with properly finalized dependent background migrations' do
|
||||
before do
|
||||
allow_next_instance_of(RuboCop::BatchedBackgroundMigrations) do |bbms|
|
||||
allow(bbms).to receive(:finalized_by).and_return(version - 5)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'migration without any rubocop offense'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for non migrations' do
|
||||
before do
|
||||
allow(cop).to receive(:in_migration?).and_return(false)
|
||||
end
|
||||
|
||||
it_behaves_like 'migration without any rubocop offense'
|
||||
end
|
||||
end
|
||||
|
|
@ -8,6 +8,7 @@ require 'fast_spec_helper'
|
|||
require 'rubocop'
|
||||
require 'rubocop/rspec/shared_contexts/default_rspec_language_config_context'
|
||||
|
||||
require_relative 'support/helpers/next_instance_of'
|
||||
require_relative 'rubocop/support_workaround'
|
||||
|
||||
RSpec.configure do |config|
|
||||
|
|
@ -21,6 +22,7 @@ RSpec.configure do |config|
|
|||
|
||||
config.include RuboCop::RSpec::ExpectOffense, type: :rubocop
|
||||
config.include RuboCop::RSpec::ExpectOffense, type: :rubocop_rspec
|
||||
config.include NextInstanceOf
|
||||
|
||||
config.include_context 'config', type: :rubocop
|
||||
config.include_context 'with default RSpec/Language config', type: :rubocop_rspec
|
||||
|
|
|
|||
Loading…
Reference in New Issue