Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-12 15:09:17 +00:00
parent a99c04f018
commit 901ecdbf5c
65 changed files with 594 additions and 240 deletions

View File

@ -57,6 +57,22 @@ export default {
return this.isReference && this.nodeProps.referenceType === 'issue';
},
isLabel() {
return this.isReference && this.nodeProps.referenceType === 'label';
},
isEpic() {
return this.isReference && this.nodeProps.referenceType === 'epic';
},
isSnippet() {
return this.isReference && this.nodeProps.referenceType === 'snippet';
},
isVulnerability() {
return this.isReference && this.nodeProps.referenceType === 'vulnerability';
},
isMergeRequest() {
return this.isReference && this.nodeProps.referenceType === 'merge_request';
},
@ -86,10 +102,17 @@ export default {
case 'issue':
case 'merge_request':
return `${this.char}${item.iid}`;
case 'snippet':
return `${this.char}${item.id}`;
case 'milestone':
case 'label':
return `${this.char}${item.title}`;
case 'command':
return `${this.char}${item.name} `;
case 'epic':
return `${item.reference}`;
case 'vulnerability':
return `[vulnerability:${item.id}]`;
default:
return '';
}
@ -184,16 +207,32 @@ export default {
<small>{{ item.iid }}</small>
{{ item.title }}
</span>
<span v-if="isVulnerability || isSnippet">
<small>{{ item.id }}</small>
{{ item.title }}
</span>
<span v-if="isEpic">
<small>{{ item.reference }}</small>
{{ item.title }}
</span>
<span v-if="isMilestone">
{{ item.title }}
</span>
<span v-if="isLabel" class="gl-display-flex gl-align-items-center">
<span
data-testid="label-color-box"
class="gl-rounded-base gl-display-block gl-w-5 gl-h-5 gl-mr-3"
:style="{ backgroundColor: item.color }"
></span>
{{ item.title }}
</span>
<span v-if="isCommand">
/{{ item.name }} <small> {{ item.params[0] }} </small><br />
<em>
<small> {{ item.description }} </small>
</em>
</span>
<div v-if="isEmoji" class="gl-display-flex gl-flex gl-align-items-center">
<div v-if="isEmoji" class="gl-display-flex gl-align-items-center">
<div class="gl-pr-4 gl-font-lg">{{ item.e }}</div>
<div class="gl-flex-grow-1">
{{ item.name }}<br />

View File

@ -121,7 +121,6 @@ export default Node.create({
dataSource: gl.GfmAutoComplete?.dataSources.members,
nodeType: 'reference',
nodeProps: {
className: 'gfm gfm-project_member',
referenceType: 'user',
},
search: (query) => ({ name, username }) => find(name, query) || find(username, query),
@ -132,18 +131,56 @@ export default Node.create({
dataSource: gl.GfmAutoComplete?.dataSources.issues,
nodeType: 'reference',
nodeProps: {
className: 'gfm gfm-issue',
referenceType: 'issue',
},
search: (query) => ({ iid, title }) => find(iid, query) || find(title, query),
}),
createSuggestionPlugin({
editor: this.editor,
char: '$',
dataSource: gl.GfmAutoComplete?.dataSources.snippets,
nodeType: 'reference',
nodeProps: {
referenceType: 'snippet',
},
search: (query) => ({ id, title }) => find(id, query) || find(title, query),
}),
createSuggestionPlugin({
editor: this.editor,
char: '~',
dataSource: gl.GfmAutoComplete?.dataSources.labels,
nodeType: 'reference',
nodeProps: {
referenceType: 'label',
},
search: (query) => ({ title }) => find(title, query),
}),
createSuggestionPlugin({
editor: this.editor,
char: '&',
dataSource: gl.GfmAutoComplete?.dataSources.epics,
nodeType: 'reference',
nodeProps: {
referenceType: 'epic',
},
search: (query) => ({ iid, title }) => find(iid, query) || find(title, query),
}),
createSuggestionPlugin({
editor: this.editor,
char: '[vulnerability:',
dataSource: gl.GfmAutoComplete?.dataSources.vulnerabilities,
nodeType: 'reference',
nodeProps: {
referenceType: 'vulnerability',
},
search: (query) => ({ id, title }) => find(id, query) || find(title, query),
}),
createSuggestionPlugin({
editor: this.editor,
char: '!',
dataSource: gl.GfmAutoComplete?.dataSources.mergeRequests,
nodeType: 'reference',
nodeProps: {
className: 'gfm gfm-issue',
referenceType: 'merge_request',
},
search: (query) => ({ iid, title }) => find(iid, query) || find(title, query),
@ -154,7 +191,6 @@ export default Node.create({
dataSource: gl.GfmAutoComplete?.dataSources.milestones,
nodeType: 'reference',
nodeProps: {
className: 'gfm gfm-milestone',
referenceType: 'milestone',
},
search: (query) => ({ iid, title }) => find(iid, query) || find(title, query),

View File

@ -202,6 +202,7 @@ export const fetchDiffFilesMeta = ({ commit, state }) => {
const worker = new TreeWorker();
const urlParams = {
view: 'inline',
w: state.showWhitespace ? '0' : '1',
};
commit(types.SET_LOADING, true);

View File

@ -75,19 +75,6 @@ $item-remove-button-space: 42px;
}
}
.item-body,
.card-header {
.health-label-short {
max-width: 0;
}
}
.card-header {
.health-label-short {
display: initial;
}
}
.item-meta {
flex-basis: 100%;
font-size: $gl-font-size;
@ -212,11 +199,6 @@ $item-remove-button-space: 42px;
max-width: 90%;
}
.card-header {
.health-label-short {
max-width: 30px;
}
}
}
/* Small devices (landscape phones, 768px and up) */
@ -239,11 +221,6 @@ $item-remove-button-space: 42px;
}
}
.card-header {
.health-label-short {
max-width: 60px;
}
}
}
/* Medium devices (desktops, 992px and up) */
@ -257,12 +234,6 @@ $item-remove-button-space: 42px;
font-size: inherit; // Base size given to `item-meta` is `$gl-font-size-small`
}
}
.card-header {
.health-label-short {
max-width: 100px;
}
}
}
/* Large devices (large desktops, 1200px and up) */
@ -309,15 +280,3 @@ $item-remove-button-space: 42px;
flex-basis: auto;
}
}
@media only screen and (min-width: 1500px) {
.card-header {
.health-label-short {
display: none;
}
.health-label-long {
display: block;
}
}
}

View File

@ -554,7 +554,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def endpoint_metadata_url(project, merge_request)
params = request.query_parameters.merge(view: 'inline', diff_head: true)
params = request.query_parameters.merge(view: 'inline', diff_head: true, w: current_user&.show_whitespace_in_diffs ? '0' : '1')
diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params)
end

View File

@ -52,7 +52,8 @@ module ResolvesMergeRequests
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }],
timelogs: [:timelogs],
pipelines: [:merge_request_diffs], # used by `recent_diff_head_shas` to load pipelines
committers: [merge_request_diff: [:merge_request_diff_commits]]
committers: [merge_request_diff: [:merge_request_diff_commits]],
suggested_reviewers: [:predictions]
}
end
end

View File

@ -17,6 +17,10 @@ module Types
field :value, GraphQL::Types::String,
null: true,
description: 'Value of the variable.'
field :value_options, [GraphQL::Types::String],
null: true,
description: 'Value options for the variable.'
end
end
end

View File

@ -58,7 +58,8 @@ module BulkImports
pipeline_name: pipeline[:pipeline],
minimum_source_version: minimum_version,
maximum_source_version: maximum_version,
source_version: source_version.to_s
source_version: source_version.to_s,
importer: 'gitlab_migration'
)
end

View File

@ -11,7 +11,7 @@ module IncidentManagement
@issuable = issuable
@param_errors = []
super(project: issuable.project, current_user: current_user, params: Hash(params))
super(project: issuable.project, current_user: current_user, params: params)
end
def execute

View File

@ -154,10 +154,13 @@ class IssuableBaseService < ::BaseProjectService
end
def filter_escalation_status(issuable)
status_params = params.delete(:escalation_status) || {}
status_params.permit! if status_params.respond_to?(:permit!)
result = ::IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService.new(
issuable,
current_user,
params.delete(:escalation_status)
status_params
).execute
return unless result.success? && result[:escalation_status].present?
@ -270,8 +273,9 @@ class IssuableBaseService < ::BaseProjectService
# To be overridden by subclasses
end
def after_update(issuable)
def after_update(issuable, old_associations)
handle_description_updated(issuable)
handle_label_changes(issuable, old_associations[:labels])
end
def handle_description_updated(issuable)
@ -327,7 +331,7 @@ class IssuableBaseService < ::BaseProjectService
affected_assignees = (old_associations[:assignees] + new_assignees) - (old_associations[:assignees] & new_assignees)
invalidate_cache_counts(issuable, users: affected_assignees.compact)
after_update(issuable)
after_update(issuable, old_associations)
issuable.create_new_cross_references!(current_user)
execute_hooks(
issuable,
@ -367,7 +371,8 @@ class IssuableBaseService < ::BaseProjectService
handle_task_changes(issuable)
invalidate_cache_counts(issuable, users: issuable.assignees.to_a)
after_update(issuable)
# not passing old_associations here to keep `update_task` as fast as possible
after_update(issuable, {})
execute_hooks(issuable, 'update', old_associations: nil)
if issuable.is_a?(MergeRequest)
@ -542,6 +547,8 @@ class IssuableBaseService < ::BaseProjectService
end
def has_label_changes?(issuable, old_labels)
return false if old_labels.nil?
Set.new(issuable.labels) != Set.new(old_labels)
end
@ -553,12 +560,15 @@ class IssuableBaseService < ::BaseProjectService
# override if needed
def handle_label_changes(issuable, old_labels)
return unless has_label_changes?(issuable, old_labels)
return false unless has_label_changes?(issuable, old_labels)
# reset to preserve the label sort order (title ASC)
issuable.labels.reset
GraphqlTriggers.issuable_labels_updated(issuable)
# return true here to avoid checking for label changes in sub classes
true
end
# override if needed

View File

@ -68,6 +68,19 @@ module Issues
rebalance_if_needed(issue)
end
def handle_escalation_status_change(issue)
return unless issue.supports_escalation?
if issue.escalation_status
::IncidentManagement::IssuableEscalationStatuses::AfterUpdateService.new(
issue,
current_user
).execute
else
::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute
end
end
def issuable_for_positioning(id, positioning_scope)
return unless id

View File

@ -65,7 +65,7 @@ module Issues
user_agent_detail_service.create
handle_add_related_issue(issue)
resolve_discussions_with_issue(issue)
create_escalation_status(issue)
handle_escalation_status_change(issue)
create_timeline_event(issue)
try_to_associate_contacts(issue)
@ -102,10 +102,6 @@ module Issues
attr_reader :spam_params, :extra_params
def create_escalation_status(issue)
::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute if issue.supports_escalation?
end
def create_timeline_event(issue)
return unless issue.incident?

View File

@ -63,7 +63,6 @@ module Issues
handle_assignee_changes(issue, old_assignees)
handle_confidential_change(issue)
handle_label_changes(issue, old_labels)
handle_added_labels(issue, old_labels)
handle_milestone_change(issue)
handle_added_mentions(issue, old_mentioned_users)
@ -201,15 +200,6 @@ module Issues
::IncidentManagement::AddSeveritySystemNoteWorker.perform_async(issue.id, current_user.id)
end
def handle_escalation_status_change(issue)
return unless issue.supports_escalation? && issue.escalation_status
::IncidentManagement::IssuableEscalationStatuses::AfterUpdateService.new(
issue,
current_user
).execute
end
def create_confidentiality_note(issue)
SystemNoteService.change_issue_confidentiality(issue, issue.project, current_user)
end

View File

@ -38,7 +38,6 @@ module MergeRequests
handle_target_branch_change(merge_request)
handle_milestone_change(merge_request)
handle_draft_status_change(merge_request, changed_fields)
handle_label_changes(merge_request, old_labels)
track_title_and_desc_edits(changed_fields)
track_discussion_lock_toggle(merge_request, changed_fields)
@ -71,7 +70,7 @@ module MergeRequests
MergeRequests::CloseService
end
def after_update(issuable)
def after_update(issuable, old_associations)
super
issuable.cache_merge_request_closes_issues!(current_user)
end

View File

@ -49,7 +49,7 @@ module WorkItems
super
end
def after_update(work_item)
def after_update(work_item, old_associations)
super
GraphqlTriggers.issuable_title_updated(work_item) if work_item.previous_changes.key?(:title)

View File

@ -18,7 +18,8 @@ module BulkImports
bulk_import_entity_id: entity_id,
bulk_import_id: bulk_import_id(entity_id),
current_stage: current_stage,
message: 'Stage running'
message: 'Stage running',
importer: 'gitlab_migration'
)
)
@ -30,7 +31,8 @@ module BulkImports
bulk_import_entity_id: entity_id,
bulk_import_id: bulk_import_id(entity_id),
current_stage: current_stage,
message: 'Stage starting'
message: 'Stage starting',
importer: 'gitlab_migration'
)
)
@ -47,12 +49,13 @@ module BulkImports
bulk_import_entity_id: entity_id,
bulk_import_id: bulk_import_id(entity_id),
current_stage: current_stage,
message: e.message
message: e.message,
importer: 'gitlab_migration'
)
)
Gitlab::ErrorTracking.track_exception(
e, bulk_import_entity_id: entity_id, bulk_import_id: bulk_import_id(entity_id)
e, bulk_import_entity_id: entity_id, bulk_import_id: bulk_import_id(entity_id), importer: 'gitlab_migration'
)
end

View File

@ -42,7 +42,9 @@ module BulkImports
structured_payload(
log_attributes(exception, entity).merge(
bulk_import_id: entity.bulk_import_id,
bulk_import_entity_type: entity.source_type
bulk_import_entity_type: entity.source_type,
message: "Request to export #{entity.source_type} failed",
importer: 'gitlab_migration'
)
)
)
@ -83,7 +85,8 @@ module BulkImports
log_attributes(e, entity).merge(
message: 'Failed to fetch source entity id',
bulk_import_id: entity.bulk_import_id,
bulk_import_entity_type: entity.source_type
bulk_import_entity_type: entity.source_type,
importer: 'gitlab_migration'
)
)
)

View File

@ -21,7 +21,9 @@ module BulkImports
structured_payload(
bulk_import_entity_id: pipeline_tracker.entity.id,
bulk_import_id: pipeline_tracker.entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name
pipeline_name: pipeline_tracker.pipeline_name,
message: 'Pipeline starting',
importer: 'gitlab_migration'
)
)
@ -32,7 +34,8 @@ module BulkImports
bulk_import_entity_id: entity_id,
bulk_import_id: bulk_import_id(entity_id),
pipeline_tracker_id: pipeline_tracker_id,
message: 'Unstarted pipeline not found'
message: 'Unstarted pipeline not found',
importer: 'gitlab_migration'
)
)
end
@ -74,7 +77,8 @@ module BulkImports
bulk_import_entity_id: pipeline_tracker.entity.id,
bulk_import_id: pipeline_tracker.entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name,
message: exception.message
message: exception.message,
importer: 'gitlab_migration'
)
)
@ -82,7 +86,8 @@ module BulkImports
exception,
bulk_import_entity_id: pipeline_tracker.entity.id,
bulk_import_id: pipeline_tracker.entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name
pipeline_name: pipeline_tracker.pipeline_name,
importer: 'gitlab_migration'
)
BulkImports::Failure.create(
@ -150,7 +155,8 @@ module BulkImports
bulk_import_entity_id: pipeline_tracker.entity.id,
bulk_import_id: pipeline_tracker.entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name,
message: "Retrying error: #{exception.message}"
message: "Retrying error: #{exception.message}",
importer: 'gitlab_migration'
)
)
@ -165,7 +171,8 @@ module BulkImports
bulk_import_entity_id: pipeline_tracker.entity.id,
bulk_import_id: pipeline_tracker.entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name,
message: 'Skipping pipeline due to failed entity'
message: 'Skipping pipeline due to failed entity',
importer: 'gitlab_migration'
)
)

View File

@ -680,3 +680,18 @@ unlike with issues or merge requests.
```ruby
ApplicationSetting.current
```
### Open object in `irb`
Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object:
```ruby
Object.define_method(:irb) { binding.irb }
project = Project.last
# => #<Project id:2537 root/discard>>
project.irb
# Notice new context
irb(#<Project>)> web_url
# => "https://gitlab-example/root/discard"
```

View File

@ -62,21 +62,6 @@ Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now
```
## Open object in `irb`
Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object:
```ruby
Object.define_method(:irb) { binding.irb }
project = Project.last
# => #<Project id:2537 root/discard>>
project.irb
# Notice new context
irb(#<Project>)> web_url
# => "https://gitlab-example/root/discard"
```
## Time an operation
```ruby
@ -359,37 +344,6 @@ repeat the above procedure after,
and report the output to
[GitLab Support](https://about.gitlab.com/support/).
## Repository
### Search sequence of pushes to a repository
If it seems that a commit has gone "missing", search the sequence of pushes to a repository.
[This StackOverflow article](https://stackoverflow.com/questions/13468027/the-mystery-of-the-missing-commit-across-merges)
describes how you can end up in this state without a force push. Another cause can be a misconfigured [server hook](../server_hooks.md) that changes a HEAD ref via a `git reset` operation.
If you look at the output from the sample code below for the target branch, you
see a discontinuity in the from/to commits as you step through the output. The `commit_from` of each new push should equal the `commit_to` of the previous push. A break in that sequence indicates one or more commits have been "lost" from the repository history.
The following example checks the last 100 pushes and prints the `commit_from` and `commit_to` entries:
```ruby
p = Project.find_by_full_path('u/p')
p.events.pushed_action.last(100).each do |e|
printf "%-20.20s %8s...%8s (%s)
", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
end
```
Example output showing break in sequence at line 4:
```plaintext
master f21b07713251e04575908149bdc8ac1f105aabc3...6bc56c1f46244792222f6c85b11606933af171de (root)
master 6bc56c1f46244792222f6c85b11606933af171de...132da6064f5d3453d445fd7cb452b148705bdc1b (root)
master 132da6064f5d3453d445fd7cb452b148705bdc1b...a62e1e693150a2e46ace0ce696cd4a52856dfa65 (root)
master 58b07b719a4b0039fec810efa52f479ba1b84756...f05321a5b5728bd8a89b7bf530aa44043c951dce (root)
master f05321a5b5728bd8a89b7bf530aa44043c951dce...7d02e575fd790e76a3284ee435368279a5eb3773 (root)
```
## Mirrors
### Find mirrors with "bad decrypt" errors

View File

@ -10406,6 +10406,7 @@ CI/CD config variables.
| <a id="ciconfigvariabledescription"></a>`description` | [`String`](#string) | Description for the CI/CD config variable. |
| <a id="ciconfigvariablekey"></a>`key` | [`String`](#string) | Name of the variable. |
| <a id="ciconfigvariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
| <a id="ciconfigvariablevalueoptions"></a>`valueOptions` | [`[String!]`](#string) | Value options for the variable. |
### `CiGroup`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

View File

@ -24,6 +24,8 @@ We use the following terms to describe components and properties of the Pods arc
A Pod is a set of infrastructure components that contains multiple top-level namespaces that belong to different organizations. The components include both datastores (PostgreSQL, Redis etc.) and stateless services (web etc.). The infrastructure components provided within a Pod are shared among organizations and their top-level namespaces but not shared with other Pods. This isolation of infrastructure components means that Pods are independent from each other.
![Term Pod](term-pod.png)
#### Pod properties
- Each pod is independent from the others
@ -40,6 +42,14 @@ Discouraged synonyms: GitLab instance, cluster, shard
A cluster is a collection of Pods.
![Term Cluster](term-cluster.png)
#### Cluster properties
- A cluster holds cluster-wide metadata, for example Users, Routes, Settings.
Discouraged synonyms: whale
### Organizations
GitLab references [Organizations in the initial set up](../../../topics/set_up_organization.md) and users can add a (free text) organization to their profile. There is no Organization entity established in the GitLab codebase.
@ -54,6 +64,9 @@ Organizations work under the following assumptions:
1. Features need to work within an organization.
1. Only few features need to work across organizations.
1. Users understand that the majority of pages they view are only scoped to a single organization at a time.
1. Organizations are located on a single pod.
![Term Organization](term-organization.png)
#### Organization properties
@ -81,6 +94,8 @@ Top-level namespaces may [be replaced by workspaces](https://gitlab.com/gitlab-o
Discouraged synonyms: Root-level namespace
![Term Top-level Namespace](term-top-level-namespace.png)
#### Top-level namespace properties
- Top-level namespaces belonging to an organization are located on the same Pod
@ -174,7 +189,7 @@ Organizations solve the following problems:
1. Self-managed instances would set a default organization.
1. Organizations can control user-profiles in a central way. This could be achieved by having an organization specific user-profile. Such a profile makes it possible for the organization administrators to control the user role in a company, enforce user emails, or show a graphical indicator of a user being part of the organization. An example would be a "GitLab Employee stamp" on comments.
![Move to Organizations](2022-10-05-Pods-Organizations-Iteration0.png)
![Move to Organizations](iteration0-organizations-introduction.png)
#### Why would customers opt-in to Organizations?

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -172,8 +172,12 @@ To create a group runner:
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **CI/CD > Runners**.
1. Note the URL and token.
1. [Register the runner](https://docs.gitlab.com/runner/register/).
1. In the top-right corner, select **Register a group runner**.
1. Select **Show runner installation and registration instructions**.
These instructions include the token, URL, and a command to register a runner.
Alternately, you can copy the registration token and follow the documentation for
how to [register a runner](https://docs.gitlab.com/runner/register/).
### View and manage group runners

View File

@ -14,10 +14,6 @@ Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS
of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a
build environment.
CI/CD minutes used on GitLab SaaS macOS runners are included in your CI/CD minute consumption totals. CI jobs that run on macOS **will** consume CI minutes at a faster rate than CI jobs on the GitLab SaaS runners on Linux.
Refer to the CI/CD minutes [cost factor](../../../ci/pipelines/cicd_minutes.md#cost-factor) for the cost factor applied to the GitLab SaaS macOS runners.
Jobs handled by macOS shared runners on GitLab.com **time out after 2 hours**, regardless of the timeout configured in a project.
## Access request process

View File

@ -2165,7 +2165,7 @@ This example creates four paths of execution:
explicitly defined for all jobs that use the `needs` keyword, or are referenced
in a job's `needs` section.
- In GitLab 13.9 and older, if `needs` refers to a job that might not be added to
a pipeline because of `only`, `except`, or `rules`, the pipeline might fail to create.
a pipeline because of `only`, `except`, or `rules`, the pipeline might fail to create. In GitLab 13.10 and later, use the [`needs:optional`](#needsoptional) keyword to resolve a failed pipeline creation.
#### `needs:artifacts`

View File

@ -96,6 +96,9 @@ when transitioning to the `pending` state. This job is responsible for creating
a multi-project or child pipeline. The workflow loop starts again
from the `CreatePipelineService` every time a downstream pipeline is triggered.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
You can watch a walkthrough of the architecture in [CI Backend Architectural Walkthrough](https://www.youtube.com/watch?v=ew4BwohS5OY).
## Job scheduling
When a Pipeline is created all its jobs are created at once for all stages, with an initial state of `created`. This makes it possible to visualize the full content of a pipeline.

View File

@ -149,7 +149,7 @@ with [domain expertise](#domain-experts).
| `~UX` user-facing changes (*3*) | [Product Designer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_UX). Refer to the [design and user interface guidelines](contributing/design.md) for details. |
| Adding a new JavaScript library (*1*) | - [Frontend foundations member](https://about.gitlab.com/direction/ecosystem/foundations/) if the library significantly increases the [bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md).<br/>- A [legal department member](https://about.gitlab.com/handbook/legal/) if the license used by the new library hasn't been approved for use in GitLab.<br/><br/>More information about license compatibility can be found in our [GitLab Licensing and Compatibility documentation](licensing.md). |
| A new dependency or a file system change | - [Distribution team member](https://about.gitlab.com/company/team/). See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/development/enablement/systems/distribution/#how-to-work-with-distribution) for more details.<br/>- For Rubygems, request an [AppSec review](gemfile.md#request-an-appsec-review). |
| `~documentation` changes | [Technical writer](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments) based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages). |
| `~documentation` or `~UI text` changes | [Technical writer](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments) based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages). |
| Changes to development guidelines | Follow the [review process](development_processes.md#development-guidelines-review) and get the approvals accordingly. |
| End-to-end **and** non-end-to-end changes (*4*) | [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors). |
| Only End-to-end changes (*4*) **or** if the MR author is a [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors) | [Quality maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_qa). |

View File

@ -335,8 +335,10 @@ If you update the `CODEOWNERS` file, close the merge request and create a new on
### User not shown as possible approver
A user might not show as an approver on the Code Owner merge request approval rules.
A user might not show as an approver on the Code Owner merge request approval rules
if any of these conditions are true:
This result occurs when a rule prevents the specific user from approving the merge request.
Check the project
[merge request approval setting](merge_requests/approvals/settings.md#edit-merge-request-approval-settings).
- A rule prevents the specific user from approving the merge request.
Check the project [merge request approval](merge_requests/approvals/settings.md#edit-merge-request-approval-settings) settings.
- A Code Owner group has a visibility of **private**, and the current user is not a
member of the Code Owner group.

View File

@ -306,3 +306,33 @@ The same approach should also allow misidentified file types to be fixed.
```
`*.txt` files have an entry in the heuristics file. This example prevents parsing of these files.
### Search sequence of pushes to a repository
If it seems that a commit has gone "missing", search the sequence of pushes to a repository.
[This StackOverflow article](https://stackoverflow.com/questions/13468027/the-mystery-of-the-missing-commit-across-merges)
describes how you can end up in this state without a force push. Another cause can be a misconfigured [server hook](../../../administration/server_hooks.md) that changes a HEAD ref in a `git reset` operation.
If you look at the output from the sample code below for the target branch, you
see a discontinuity in the from/to commits as you step through the output.
The `commit_from` of each new push should equal the `commit_to` of the previous push.
A break in that sequence indicates one or more commits have been "lost" from the repository history.
Using the [rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session), the following example checks the last 100 pushes and prints the `commit_from` and `commit_to` entries:
```ruby
p = Project.find_by_full_path('project/path')
p.events.pushed_action.last(100).each do |e|
puts "%-20.20s %8s...%8s (%s)", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
end ; nil
```
Example output showing break in sequence at line 4:
```plaintext
master f21b07713251e04575908149bdc8ac1f105aabc3...6bc56c1f46244792222f6c85b11606933af171de root
master 6bc56c1f46244792222f6c85b11606933af171de...132da6064f5d3453d445fd7cb452b148705bdc1b root
master 132da6064f5d3453d445fd7cb452b148705bdc1b...a62e1e693150a2e46ace0ce696cd4a52856dfa65 root
master 58b07b719a4b0039fec810efa52f479ba1b84756...f05321a5b5728bd8a89b7bf530aa44043c951dce root
master f05321a5b5728bd8a89b7bf530aa44043c951dce...7d02e575fd790e76a3284ee435368279a5eb3773 root
```

View File

@ -28,7 +28,8 @@ module BulkImports
bulk_import_entity_id: context.entity.id,
bulk_import_entity_type: context.entity.source_type,
pipeline_class: self.class.name,
message: "Entity #{entity.status_name}"
message: "Entity #{entity.status_name}",
importer: 'gitlab_migration'
)
context.portable.try(:after_import)

View File

@ -54,7 +54,8 @@ module BulkImports
skip!(
'Skipping pipeline due to failed entity',
pipeline_step: step,
step_class: class_name
step_class: class_name,
importer: 'gitlab_migration'
)
rescue BulkImports::NetworkError => e
if e.retriable?(context.tracker)
@ -111,7 +112,9 @@ module BulkImports
bulk_import_id: context.bulk_import_id,
pipeline_step: step,
exception_class: exception.class.to_s,
exception_message: exception.message
exception_message: exception.message,
message: "Pipeline failed",
importer: 'gitlab_migration'
)
BulkImports::Failure.create(attributes)
@ -135,7 +138,8 @@ module BulkImports
bulk_import_entity_id: context.entity.id,
bulk_import_entity_type: context.entity.source_type,
pipeline_class: pipeline,
context_extra: context.extra
context_extra: context.extra,
importer: 'gitlab_migration'
}
defaults

View File

@ -50,7 +50,7 @@ module Gitlab
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.',
metadata: { allowed_value_data: %i[value description] },
metadata: { allowed_value_data: %i[value description], allow_array_value: true },
reserved: true
entry :stages, Entry::Stages,

View File

@ -10,6 +10,7 @@ module Gitlab
class Variable < ::Gitlab::Config::Entry::Simplifiable
strategy :SimpleVariable, if: -> (config) { SimpleVariable.applies_to?(config) }
strategy :ComplexVariable, if: -> (config) { ComplexVariable.applies_to?(config) }
strategy :ComplexArrayVariable, if: -> (config) { ComplexArrayVariable.applies_to?(config) }
class SimpleVariable < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
@ -39,7 +40,7 @@ module Gitlab
class << self
def applies_to?(config)
config.is_a?(Hash)
config.is_a?(Hash) && !config[:value].is_a?(Array)
end
end
@ -86,6 +87,34 @@ module Gitlab
end
end
class ComplexArrayVariable < ComplexVariable
include ::Gitlab::Config::Entry::Validatable
class << self
def applies_to?(config)
config.is_a?(Hash) && config[:value].is_a?(Array)
end
end
validations do
validates :config_value, array_of_strings: true, allow_nil: false, if: :config_value_defined?
validate do
next if opt(:allow_array_value)
errors.add(:config, 'value must be an alphanumeric string')
end
end
def value
config_value.first
end
def value_with_data
super.merge(value_options: config_value).compact
end
end
class UnknownStrategy < ::Gitlab::Config::Entry::Node
def errors
["variable definition must be either a string or a hash"]

View File

@ -36,7 +36,7 @@ module Gitlab
end
def composable_metadata
{ allowed_value_data: opt(:allowed_value_data) }
{ allowed_value_data: opt(:allowed_value_data), allow_array_value: opt(:allow_array_value) }
end
end
end

View File

@ -151,7 +151,7 @@ module Gitlab
def limit_value
# note: only first _or_ last can be specified, not both
@limit_value ||= [first, last, max_page_size, GitlabSchema.default_max_page_size].compact.min
@limit_value ||= [first, last, max_page_size || GitlabSchema.default_max_page_size].compact.min
end
def loaded?(items)

View File

@ -89,12 +89,12 @@ module Sidebars
::Sidebars::MenuItem.new(
title: _('Google Cloud'),
link: project_google_cloud_configuration_path(context.project),
active_routes: { controller: [
:configuration,
:service_accounts,
:databases,
:deployments,
:gcp_regions
active_routes: { controller: %w[
projects/google_cloud/configuration
projects/google_cloud/service_accounts
projects/google_cloud/databases
projects/google_cloud/deployments
projects/google_cloud/gcp_regions
] },
item_id: :google_cloud
)

View File

@ -46905,9 +46905,6 @@ msgstr ""
msgid "at least the Reporter role, the author, and assignees"
msgstr ""
msgid "at risk"
msgstr ""
msgid "attach a new file"
msgstr ""
@ -48294,9 +48291,6 @@ msgstr ""
msgid "my-topic"
msgstr ""
msgid "need attention"
msgstr ""
msgid "needs to be between 10 minutes and 1 month"
msgstr ""
@ -48345,9 +48339,6 @@ msgstr ""
msgid "nounSeries|%{item}, and %{lastItem}"
msgstr ""
msgid "on track"
msgstr ""
msgid "only %{parent_types} can be parent of Task."
msgstr ""

View File

@ -77,7 +77,8 @@ RSpec.describe Projects::MergeRequestsController do
merge_request,
'json',
diff_head: true,
view: 'inline'))
view: 'inline',
w: '0'))
end
context 'when diff files were cleaned' do

View File

@ -1,17 +1,21 @@
import { GlAvatarLabeled, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SuggestionsDropdown from '~/content_editor/components/suggestions_dropdown.vue';
describe('~/content_editor/components/suggestions_dropdown', () => {
let wrapper;
const buildWrapper = ({ propsData = {} } = {}) => {
wrapper = shallowMount(SuggestionsDropdown, {
propsData: {
nodeType: 'reference',
...propsData,
},
});
const buildWrapper = ({ propsData } = {}) => {
wrapper = extendedWrapper(
shallowMount(SuggestionsDropdown, {
propsData: {
nodeType: 'reference',
command: jest.fn(),
...propsData,
},
}),
);
};
const exampleUser = { username: 'root', avatar_url: 'root_avatar.png', type: 'User' };
@ -23,6 +27,25 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
description: 'Set due date',
params: ['<in 2 days | this Friday | December 31st>'],
};
const exampleEpic = {
iid: 8884,
title: '❓ Remote Development | Solution validation',
reference: 'gitlab-org&8884',
};
const exampleLabel = {
title: 'devops::create',
color: '#E44D2A',
type: 'GroupLabel',
textColor: '#FFFFFF',
};
const exampleVulnerability = {
id: 60850147,
title: 'System procs network activity',
};
const exampleSnippet = {
id: 2420859,
title: 'Project creation QueryRecorder logs',
};
const exampleEmoji = {
c: 'people',
e: '😃',
@ -40,13 +63,17 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
describe('on item select', () => {
it.each`
nodeType | referenceType | char | reference | insertedText | insertedProps
${'reference'} | ${'user'} | ${'@'} | ${exampleUser} | ${`@root`} | ${{}}
${'reference'} | ${'issue'} | ${'#'} | ${exampleIssue} | ${`#123`} | ${{}}
${'reference'} | ${'merge_request'} | ${'!'} | ${exampleMergeRequest} | ${`!224`} | ${{}}
${'reference'} | ${'milestone'} | ${'%'} | ${exampleMilestone} | ${`%1.3`} | ${{}}
${'reference'} | ${'command'} | ${'/'} | ${exampleCommand} | ${'/due '} | ${{}}
${'emoji'} | ${'emoji'} | ${':'} | ${exampleEmoji} | ${`😃`} | ${insertedEmojiProps}
nodeType | referenceType | char | reference | insertedText | insertedProps
${'reference'} | ${'user'} | ${'@'} | ${exampleUser} | ${`@root`} | ${{}}
${'reference'} | ${'issue'} | ${'#'} | ${exampleIssue} | ${`#123`} | ${{}}
${'reference'} | ${'merge_request'} | ${'!'} | ${exampleMergeRequest} | ${`!224`} | ${{}}
${'reference'} | ${'milestone'} | ${'%'} | ${exampleMilestone} | ${`%1.3`} | ${{}}
${'reference'} | ${'command'} | ${'/'} | ${exampleCommand} | ${'/due '} | ${{}}
${'reference'} | ${'epic'} | ${'&'} | ${exampleEpic} | ${`gitlab-org&8884`} | ${{}}
${'reference'} | ${'label'} | ${'~'} | ${exampleLabel} | ${`~devops::create`} | ${{}}
${'reference'} | ${'vulnerability'} | ${'[vulnerability:'} | ${exampleVulnerability} | ${`[vulnerability:60850147]`} | ${{}}
${'reference'} | ${'snippet'} | ${'$'} | ${exampleSnippet} | ${`$2420859`} | ${{}}
${'emoji'} | ${'emoji'} | ${':'} | ${exampleEmoji} | ${`😃`} | ${insertedEmojiProps}
`(
'runs a command to insert the selected $referenceType',
({ char, nodeType, referenceType, reference, insertedText, insertedProps }) => {
@ -80,24 +107,21 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
describe('rendering user references', () => {
it('displays avatar labeled component', () => {
const testUser = exampleUser;
buildWrapper({
propsData: {
char: '@',
command: jest.fn(),
nodeType: 'reference',
nodeProps: {
referenceType: 'user',
},
items: [testUser],
items: [exampleUser],
},
});
expect(wrapper.findComponent(GlAvatarLabeled).attributes()).toEqual(
expect.objectContaining({
label: testUser.username,
label: exampleUser.username,
shape: 'circle',
src: testUser.avatar_url,
src: exampleUser.avatar_url,
}),
);
});
@ -112,7 +136,6 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
buildWrapper({
propsData: {
char,
command: jest.fn(),
nodeType: 'reference',
nodeProps: {
referenceType,
@ -127,13 +150,68 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
});
});
describe.each`
referenceType | char | reference
${'snippet'} | ${'$'} | ${exampleSnippet}
${'vulnerability'} | ${'[vulnerability:'} | ${exampleVulnerability}
`('rendering $referenceType references', ({ referenceType, char, reference }) => {
it(`displays ${referenceType} ID and title`, () => {
buildWrapper({
propsData: {
char,
nodeProps: {
referenceType,
},
items: [reference],
},
});
expect(wrapper.text()).toContain(`${reference.id}`);
expect(wrapper.text()).toContain(`${reference.title}`);
});
});
describe('rendering label references', () => {
it('displays label title and color', () => {
buildWrapper({
propsData: {
char: '~',
nodeProps: {
referenceType: 'label',
},
items: [exampleLabel],
},
});
expect(wrapper.text()).toContain(`${exampleLabel.title}`);
expect(wrapper.findByTestId('label-color-box').attributes().style).toEqual(
`background-color: rgb(228, 77, 42);`, // #E44D2A
);
});
});
describe('rendering epic references', () => {
it('displays epic title and reference', () => {
buildWrapper({
propsData: {
char: '&',
nodeProps: {
referenceType: 'epic',
},
items: [exampleEpic],
},
});
expect(wrapper.text()).toContain(`${exampleEpic.reference}`);
expect(wrapper.text()).toContain(`${exampleEpic.title}`);
});
});
describe('rendering a command (quick action)', () => {
it('displays command name with a slash', () => {
buildWrapper({
propsData: {
char: '/',
command: jest.fn(),
nodeType: 'reference',
nodeProps: {
referenceType: 'command',
},
@ -168,7 +246,6 @@ describe('~/content_editor/components/suggestions_dropdown', () => {
buildWrapper({
propsData: {
char: ':',
command: jest.fn(),
nodeType: 'emoji',
nodeProps: {},
items: testEmojis,

View File

@ -178,7 +178,7 @@ describe('DiffsStoreActions', () => {
});
describe('fetchDiffFilesMeta', () => {
const endpointMetadata = '/fetch/diffs_metadata.json?view=inline';
const endpointMetadata = '/fetch/diffs_metadata.json?view=inline&w=0';
const noFilesData = { ...diffMetadata };
beforeEach(() => {
@ -191,7 +191,7 @@ describe('DiffsStoreActions', () => {
return testAction(
diffActions.fetchDiffFilesMeta,
{},
{ endpointMetadata, diffViewType: 'inline' },
{ endpointMetadata, diffViewType: 'inline', showWhitespace: true },
[
{ type: types.SET_LOADING, payload: true },
{ type: types.SET_LOADING, payload: false },

View File

@ -17,7 +17,8 @@ RSpec.describe BulkImports::Common::Pipelines::EntityFinisher do
bulk_import_entity_id: entity.id,
bulk_import_entity_type: entity.source_type,
pipeline_class: described_class.name,
message: 'Entity finished'
message: 'Entity finished',
importer: 'gitlab_migration'
)
end

View File

@ -60,7 +60,9 @@ RSpec.describe BulkImports::Pipeline::Runner do
pipeline_step: :extractor,
pipeline_class: 'BulkImports::MyPipeline',
exception_class: exception_class,
exception_message: exception_message
exception_message: exception_message,
message: "Pipeline failed",
importer: 'gitlab_migration'
)
)
end
@ -89,7 +91,8 @@ RSpec.describe BulkImports::Pipeline::Runner do
log_params(
context,
message: 'Aborting entity migration due to pipeline failure',
pipeline_class: 'BulkImports::MyPipeline'
pipeline_class: 'BulkImports::MyPipeline',
importer: 'gitlab_migration'
)
)
end
@ -293,6 +296,7 @@ RSpec.describe BulkImports::Pipeline::Runner do
bulk_import_id: context.bulk_import_id,
bulk_import_entity_id: context.entity.id,
bulk_import_entity_type: context.entity.source_type,
importer: 'gitlab_migration',
context_extra: context.extra
}.merge(extra)
end

View File

@ -34,7 +34,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: 'image:1.0',
default: {},
services: ['postgres:9.1', 'mysql:5.5'],
variables: { VAR: 'root', VAR2: { value: 'val 2', description: 'this is var 2' } },
variables: {
VAR: 'root',
VAR2: { value: 'val 2', description: 'this is var 2' },
VAR3: { value: %w[val3 val3b], description: 'this is var 3' }
},
after_script: ['make clean'],
stages: %w(build pages release),
cache: { key: 'k', untracked: true, paths: ['public/'] },
@ -83,7 +87,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
it 'sets correct variables value' do
expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2')
expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2', 'VAR3' => 'val3')
end
describe '#leaf?' do

View File

@ -127,20 +127,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
end
end
context 'when config value is an array' do
let(:config) { { value: ['value'], description: 'description' } }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
subject(:errors) { entry.errors }
it { is_expected.to include 'var1 config value must be an alphanumeric string' }
end
end
context 'when config description is a symbol' do
let(:config) { { value: 'value', description: :description } }
@ -209,4 +195,42 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do
end
end
end
describe 'ComplexArrayVariable' do
context 'when allow_array_value metadata is false' do
let(:config) { { value: %w[value value2], description: 'description' } }
let(:metadata) { { allow_array_value: false } }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
subject(:errors) { entry.errors }
it { is_expected.to include 'var1 config value must be an alphanumeric string' }
end
end
context 'when allow_array_value metadata is true' do
let(:config) { { value: %w[value value2], description: 'description' } }
let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#value' do
subject(:value) { entry.value }
it { is_expected.to eq('value') }
end
describe '#value_with_data' do
subject(:value_with_data) { entry.value_with_data }
it { is_expected.to eq(value: 'value', description: 'description', value_options: %w[value value2]) }
end
end
end
end

View File

@ -98,6 +98,62 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'invalid config', /must be either a string or a hash/
end
context 'when entry config value has unallowed value key-value pair and value is a string' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value', description: 'variable 1' } }
end
context 'when there is no allowed_value_data metadata' do
it_behaves_like 'invalid config', /variable_1 config must be a string/
end
context 'when metadata has allow_array_value and allowed_value_data' do
let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
let(:result) do
{ 'VARIABLE_1' => 'value' }
end
it_behaves_like 'valid config'
describe '#value_with_data' do
it 'returns variable with data' do
expect(entry.value_with_data).to eq(
'VARIABLE_1' => { value: 'value', description: 'variable 1' }
)
end
end
end
end
context 'when entry config value has key-value pair and value is an array' do
let(:config) do
{ 'VARIABLE_1' => { value: %w[value1 value2], description: 'variable 1' } }
end
context 'when there is no allowed_value_data metadata' do
it_behaves_like 'invalid config', /variable_1 config value must be an alphanumeric string/
end
context 'when metadata has allow_array_value and allowed_value_data' do
let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } }
let(:result) do
{ 'VARIABLE_1' => 'value1' }
end
it_behaves_like 'valid config'
describe '#value_with_data' do
it 'returns variable with data' do
expect(entry.value_with_data).to eq(
'VARIABLE_1' => { value: 'value1', value_options: %w[value1 value2], description: 'variable 1' }
)
end
end
end
end
context 'when entry config value has key-value pair and hash' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },

View File

@ -51,6 +51,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
before do
stub_feature_flags(graphql_keyset_pagination_without_next_page_query: false)
allow(GitlabSchema).to receive(:default_max_page_size).and_return(2)
end
it 'invokes an extra query for the next page check' do

View File

@ -23,6 +23,7 @@ RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
ciConfigVariables(sha: "#{sha}") {
key
value
valueOptions
description
}
}
@ -52,14 +53,22 @@ RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
post_graphql(query, current_user: user)
expect(graphql_data.dig('project', 'ciConfigVariables')).to contain_exactly(
{
'key' => 'KEY_VALUE_VAR',
'value' => 'value x',
'valueOptions' => nil,
'description' => 'value of KEY_VALUE_VAR'
},
{
'key' => 'DB_NAME',
'value' => 'postgres',
'valueOptions' => nil,
'description' => nil
},
{
'key' => 'ENVIRONMENT_VAR',
'value' => 'env var value',
'valueOptions' => ['env var value', 'env var value2'],
'description' => 'env var description'
}
)

View File

@ -77,6 +77,7 @@ RSpec.describe BulkImports::CreatePipelineTrackersService do
message: 'Pipeline skipped as source instance version not compatible with pipeline',
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import_id,
importer: 'gitlab_migration',
pipeline_name: 'PipelineClass4',
minimum_source_version: '15.1.0',
maximum_source_version: nil,
@ -87,6 +88,7 @@ RSpec.describe BulkImports::CreatePipelineTrackersService do
message: 'Pipeline skipped as source instance version not compatible with pipeline',
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import_id,
importer: 'gitlab_migration',
pipeline_name: 'PipelineClass5',
minimum_source_version: '16.0.0',
maximum_source_version: nil,

View File

@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService do
RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService, factory_default: :keep do
let_it_be(:project) { create_default(:project) }
let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, :triggered) }
let_it_be(:user_with_permissions) { create(:user) }
@ -10,7 +11,7 @@ RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateServ
let(:issue) { escalation_status.issue }
let(:status) { :acknowledged }
let(:params) { { status: status } }
let(:service) { IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService.new(issue, current_user, params) }
let(:service) { described_class.new(issue, current_user, params) }
subject(:result) { service.execute }
@ -71,9 +72,17 @@ RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateServ
end
end
context 'when called without params' do
context 'when called nil params' do
let(:params) { nil }
it 'raises an exception' do
expect { result }.to raise_error NoMethodError
end
end
context 'when called without params' do
let(:params) { {} }
it_behaves_like 'successful response', {}
end

View File

@ -852,6 +852,24 @@ RSpec.describe Issues::UpdateService, :mailer do
service.execute(issue)
end
# At the moment of writting old associations are not necessary for update_task
# and doing this will prevent fetching associations from the DB and comparing old and new labels
it 'does not pass old_associations to the after_update method' do
params = {
update_task: {
index: 1,
checked: false,
line_source: '- [x] Task 1',
line_number: 1
}
}
service = described_class.new(project: project, current_user: user, params: params)
expect(service).to receive(:after_update).with(issue, {})
service.execute(issue)
end
it 'creates system note about task status change' do
note1 = find_note('marked the checklist item **Task 1** as completed')
note2 = find_note('marked the checklist item **Task 2** as completed')

View File

@ -108,6 +108,24 @@ RSpec.describe WorkItems::UpdateService do
end
end
context 'when labels are updated' do
let_it_be(:label_a) { create(:label, project: project) }
let_it_be(:label_b) { create(:label, project: project) }
let(:issuable) { work_item }
it_behaves_like 'broadcasting issuable labels updates' do
def update_issuable(update_params)
described_class.new(
project: project,
current_user: current_user,
params: update_params,
spam_params: spam_params,
widget_params: widget_params
).execute(work_item)
end
end
end
context 'when updating state_event' do
context 'when state_event is close' do
let(:opts) { { state_event: 'close' } }

View File

@ -7,9 +7,12 @@ before_script:
- bundle exec rake db:create
variables:
KEY_VALUE_VAR:
value: 'value x'
description: 'value of KEY_VALUE_VAR'
DB_NAME: postgres
ENVIRONMENT_VAR:
value: 'env var value'
value: ['env var value', 'env var value2']
description: 'env var description'
stages:

View File

@ -10008,7 +10008,6 @@
- './spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb'
- './spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb'
- './spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb'
- './spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb'
- './spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb'
- './spec/services/incident_management/pager_duty/process_webhook_service_spec.rb'
- './spec/services/incident_management/timeline_events/create_service_spec.rb'

View File

@ -47,7 +47,7 @@ RSpec.shared_examples 'broadcasting issuable labels updates' do
it 'triggers the GraphQL subscription' do
expect(GraphqlTriggers).to receive(:issuable_labels_updated).with(issuable)
update_issuable({ add_label_ids: [label_b.id] })
update_issuable(add_label_ids: [label_b.id])
end
end
@ -55,7 +55,7 @@ RSpec.shared_examples 'broadcasting issuable labels updates' do
it 'triggers the GraphQL subscription' do
expect(GraphqlTriggers).to receive(:issuable_labels_updated).with(issuable)
update_issuable({ remove_label_ids: [label_a.id] })
update_issuable(remove_label_ids: [label_a.id])
end
end
@ -63,7 +63,7 @@ RSpec.shared_examples 'broadcasting issuable labels updates' do
it 'does not trigger the GraphQL subscription' do
expect(GraphqlTriggers).not_to receive(:issuable_labels_updated).with(issuable)
update_issuable({ label_ids: [label_a.id] })
update_issuable(label_ids: [label_a.id])
end
end
end

View File

@ -40,7 +40,8 @@ RSpec.describe BulkImports::EntityWorker do
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'current_stage' => nil,
'message' => 'Stage starting'
'message' => 'Stage starting',
'importer' => 'gitlab_migration'
)
)
end
@ -70,7 +71,8 @@ RSpec.describe BulkImports::EntityWorker do
hash_including(
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'current_stage' => nil
'current_stage' => nil,
'importer' => 'gitlab_migration'
)
)
@ -81,14 +83,20 @@ RSpec.describe BulkImports::EntityWorker do
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'current_stage' => nil,
'message' => 'Error!'
'message' => 'Error!',
'importer' => 'gitlab_migration'
)
)
end
expect(Gitlab::ErrorTracking)
.to receive(:track_exception)
.with(exception, bulk_import_entity_id: entity.id, bulk_import_id: entity.bulk_import_id)
.with(
exception,
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import_id,
importer: 'gitlab_migration'
)
subject
end
@ -105,7 +113,8 @@ RSpec.describe BulkImports::EntityWorker do
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'current_stage' => 0,
'message' => 'Stage running'
'message' => 'Stage running',
'importer' => 'gitlab_migration'
)
)
end
@ -133,7 +142,8 @@ RSpec.describe BulkImports::EntityWorker do
hash_including(
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'current_stage' => 0
'current_stage' => 0,
'importer' => 'gitlab_migration'
)
)
end

View File

@ -45,7 +45,8 @@ RSpec.describe BulkImports::ExportRequestWorker do
'exception_message' => 'Export error',
'correlation_id_value' => anything,
'bulk_import_id' => bulk_import.id,
'bulk_import_entity_type' => entity.source_type
'bulk_import_entity_type' => entity.source_type,
'importer' => 'gitlab_migration'
)
).twice
@ -104,7 +105,8 @@ RSpec.describe BulkImports::ExportRequestWorker do
'exception_message' => "undefined method `model_id' for nil:NilClass",
'correlation_id_value' => anything,
'bulk_import_id' => bulk_import.id,
'bulk_import_entity_type' => entity.source_type
'bulk_import_entity_type' => entity.source_type,
'importer' => 'gitlab_migration'
)
).twice

View File

@ -38,7 +38,8 @@ RSpec.describe BulkImports::PipelineWorker do
hash_including(
'pipeline_name' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id
'bulk_import_id' => entity.bulk_import_id,
'importer' => 'gitlab_migration'
)
)
end
@ -86,7 +87,8 @@ RSpec.describe BulkImports::PipelineWorker do
'pipeline_tracker_id' => pipeline_tracker.id,
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'message' => 'Unstarted pipeline not found'
'message' => 'Unstarted pipeline not found',
'importer' => 'gitlab_migration'
)
)
end
@ -124,7 +126,8 @@ RSpec.describe BulkImports::PipelineWorker do
'pipeline_name' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'message' => 'Error!'
'message' => 'Error!',
'importer' => 'gitlab_migration'
)
)
end
@ -134,8 +137,9 @@ RSpec.describe BulkImports::PipelineWorker do
.with(
instance_of(StandardError),
bulk_import_entity_id: entity.id,
bulk_import_id: entity.bulk_import.id,
pipeline_name: pipeline_tracker.pipeline_name
bulk_import_id: entity.bulk_import_id,
pipeline_name: pipeline_tracker.pipeline_name,
importer: 'gitlab_migration'
)
expect(BulkImports::EntityWorker)
@ -184,7 +188,8 @@ RSpec.describe BulkImports::PipelineWorker do
'pipeline_name' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'message' => 'Skipping pipeline due to failed entity'
'message' => 'Skipping pipeline due to failed entity',
'importer' => 'gitlab_migration'
)
)
end
@ -231,7 +236,8 @@ RSpec.describe BulkImports::PipelineWorker do
hash_including(
'pipeline_name' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id
'bulk_import_id' => entity.bulk_import_id,
'importer' => 'gitlab_migration'
)
)
end
@ -356,7 +362,8 @@ RSpec.describe BulkImports::PipelineWorker do
'pipeline_name' => 'NdjsonPipeline',
'bulk_import_entity_id' => entity.id,
'message' => 'Pipeline timeout',
'bulk_import_id' => entity.bulk_import_id
'bulk_import_id' => entity.bulk_import_id,
'importer' => 'gitlab_migration'
)
)
end
@ -384,7 +391,8 @@ RSpec.describe BulkImports::PipelineWorker do
'pipeline_name' => 'NdjsonPipeline',
'bulk_import_entity_id' => entity.id,
'message' => 'Export from source instance failed: Error!',
'bulk_import_id' => entity.bulk_import_id
'bulk_import_id' => entity.bulk_import_id,
'importer' => 'gitlab_migration'
)
)
end

View File

@ -108,7 +108,7 @@ require (
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/api v0.74.0 // indirect

View File

@ -1280,8 +1280,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=