Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml21
-rw-r--r--.rubocop_todo/style/redundant_self.yml1
-rw-r--r--app/assets/javascripts/batch_comments/components/submit_dropdown.vue84
-rw-r--r--app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js6
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue39
-rw-r--r--app/assets/javascripts/pipelines/components/performance_insights_modal.vue171
-rw-r--r--app/assets/javascripts/pipelines/constants.js2
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql28
-rw-r--r--app/assets/javascripts/pipelines/utils.js21
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss7
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests/drafts_controller.rb18
-rw-r--r--app/finders/deployments_finder.rb1
-rw-r--r--app/graphql/types/ci/job_type.rb6
-rw-r--r--app/graphql/types/deployment_type.rb15
-rw-r--r--app/helpers/application_settings_helper.rb5
-rw-r--r--app/models/ci/trigger.rb6
-rw-r--r--app/models/concerns/approvable.rb3
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/resource_state_event.rb3
-rw-r--r--app/models/resource_timebox_event.rb3
-rw-r--r--app/models/user.rb5
-rw-r--r--app/serializers/merge_request_noteable_entity.rb6
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml8
-rw-r--r--app/views/admin/application_settings/general.html.haml2
-rw-r--r--config/feature_flags/development/escape_gitaly_refs.yml8
-rw-r--r--db/post_migrate/20220822094804_add_issues_authorization_index.rb15
-rw-r--r--db/schema_migrations/202208220948041
-rw-r--r--doc/administration/auth/ldap/ldap_synchronization.md8
-rw-r--r--doc/administration/pages/index.md6
-rw-r--r--doc/api/graphql/reference/index.md7
-rw-r--r--doc/topics/git/lfs/migrate_to_git_lfs.md2
-rw-r--r--doc/user/admin_area/moderate_users.md6
-rw-r--r--doc/user/group/import/index.md5
-rw-r--r--doc/user/group/settings/import_export.md1
-rw-r--r--doc/user/project/repository/branches/index.md3
-rw-r--r--doc/user/project/settings/import_export.md14
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/gitlab/encoding_helper.rb24
-rw-r--r--lib/gitlab/git.rb2
-rw-r--r--lib/gitlab/git/repository.rb10
-rw-r--r--lib/gitlab/git/tag.rb2
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb4
-rw-r--r--lib/gitlab/import_export/group/import_export.yml1
-rw-r--r--lib/gitlab/import_export/group/legacy_import_export.yml1
-rw-r--r--lib/gitlab/import_export/project/import_export.yml33
-rw-r--r--locale/gitlab.pot50
-rw-r--r--spec/controllers/admin/users_controller_spec.rb6
-rw-r--r--spec/controllers/projects/merge_requests/drafts_controller_spec.rb34
-rw-r--r--spec/features/admin/admin_settings_spec.rb33
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_noteable.json6
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/project.json70
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson2
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson2
-rw-r--r--spec/frontend/batch_comments/components/submit_dropdown_spec.js20
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js3
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js12
-rw-r--r--spec/frontend/pipelines/graph/graph_view_selector_spec.js59
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js242
-rw-r--r--spec/frontend/pipelines/performance_insights_modal_spec.js131
-rw-r--r--spec/frontend/pipelines/utils_spec.js44
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb15
-rw-r--r--spec/graphql/types/deployment_details_type_spec.rb2
-rw-r--r--spec/graphql/types/deployment_type_spec.rb2
-rw-r--r--spec/helpers/application_settings_helper_spec.rb8
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb30
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb55
-rw-r--r--spec/lib/gitlab/git_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml11
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml12
-rw-r--r--spec/models/ci/trigger_spec.rb17
-rw-r--r--spec/models/deployment_spec.rb16
-rw-r--r--spec/models/user_spec.rb12
-rw-r--r--spec/requests/api/graphql/environments/deployments_query_spec.rb141
-rw-r--r--spec/requests/api/users_spec.rb6
-rw-r--r--spec/workers/users/deactivate_dormant_users_worker_spec.rb10
82 files changed, 815 insertions, 901 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 486da5bb906..882ae337c60 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -130,27 +130,6 @@ variables:
REGISTRY_HOST: "registry.gitlab.com"
REGISTRY_GROUP: "gitlab-org"
- # Preparing custom clone path to reduce space used by all random forks
- # on GitLab.com's Shared Runners. Our main forks - especially the security
- # ones - will have this variable overwritten in the project settings, so that
- # a security-related code or code using our protected variables will be never
- # stored on the same path as the community forks.
- # Part of the solution for the `no space left on device` problem described at
- # https://gitlab.com/gitlab-org/gitlab/issues/197876.
- #
- # For this purpose the https://gitlab.com/gitlab-org-forks group was created
- # to host a placeholder for the `/builds/gitlab-org-forks` path and ensure
- # that no legitimate project will ever use it and - by mistake - execute its
- # job on a shared working directory. It also requires proper configuration of
- # the Runner that executes the job (which was prepared for our shared runners
- # by https://ops.gitlab.net/gitlab-cookbooks/chef-repo/-/merge_requests/3977).
- #
- # Because of all of that PLEASE DO NOT CHANGE THE PATH.
- #
- # For more details and reasoning that brought this change please check
- # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24887
- GIT_CLONE_PATH: "/builds/gitlab-org-forks/${CI_PROJECT_NAME}"
-
include:
- local: .gitlab/ci/*.gitlab-ci.yml
- remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/templates/merge_request_pipelines.yml'
diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml
index f14926c0bba..8c688dc89c1 100644
--- a/.rubocop_todo/style/redundant_self.yml
+++ b/.rubocop_todo/style/redundant_self.yml
@@ -47,7 +47,6 @@ Style/RedundantSelf:
- 'app/models/commit_status.rb'
- 'app/models/compare.rb'
- 'app/models/concerns/after_commit_queue.rb'
- - 'app/models/concerns/approvable.rb'
- 'app/models/concerns/atomic_internal_id.rb'
- 'app/models/concerns/avatarable.rb'
- 'app/models/concerns/awardable.rb'
diff --git a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
index 54b9953270b..c5b713b5447 100644
--- a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
@@ -1,7 +1,16 @@
<script>
import $ from 'jquery';
-import { GlDropdown, GlButton, GlIcon, GlForm, GlFormGroup, GlLink } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlButton,
+ GlIcon,
+ GlForm,
+ GlFormGroup,
+ GlLink,
+ GlFormCheckbox,
+} from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
+import { createAlert } from '~/flash';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
import Autosave from '~/autosave';
@@ -15,29 +24,46 @@ export default {
GlForm,
GlFormGroup,
GlLink,
+ GlFormCheckbox,
MarkdownField,
+ ApprovalPassword: () => import('ee_component/batch_comments/components/approval_password.vue'),
},
data() {
return {
isSubmitting: false,
- note: '',
+ noteData: {
+ noteable_type: '',
+ noteable_id: '',
+ note: '',
+ approve: false,
+ approval_password: '',
+ },
};
},
computed: {
...mapGetters(['getNotesData', 'getNoteableData', 'noteableType', 'getCurrentUserLastNote']),
},
+ watch: {
+ 'noteData.approve': function noteDataApproveWatch() {
+ setTimeout(() => {
+ this.repositionDropdown();
+ });
+ },
+ },
mounted() {
this.autosave = new Autosave(
$(this.$refs.textarea),
`submit_review_dropdown/${this.getNoteableData.id}`,
);
+ this.noteData.noteable_type = this.noteableType;
+ this.noteData.noteable_id = this.getNoteableData.id;
// We override the Bootstrap Vue click outside behaviour
// to allow for clicking in the autocomplete dropdowns
// without this override the submit dropdown will close
// whenever a item in the autocomplete dropdown is clicked
- const originalClickOutHandler = this.$refs.dropdown.$refs.dropdown.clickOutHandler;
- this.$refs.dropdown.$refs.dropdown.clickOutHandler = (e) => {
+ const originalClickOutHandler = this.$refs.submitDropdown.$refs.dropdown.clickOutHandler;
+ this.$refs.submitDropdown.$refs.dropdown.clickOutHandler = (e) => {
if (!e.target.closest('.atwho-container')) {
originalClickOutHandler(e);
}
@@ -45,26 +71,29 @@ export default {
},
methods: {
...mapActions('batchComments', ['publishReview']),
+ repositionDropdown() {
+ this.$refs.submitDropdown?.$refs.dropdown?.updatePopper();
+ },
async submitReview() {
- const noteData = {
- noteable_type: this.noteableType,
- noteable_id: this.getNoteableData.id,
- note: this.note,
- };
-
this.isSubmitting = true;
- await this.publishReview(noteData);
+ try {
+ await this.publishReview(this.noteData);
- this.autosave.reset();
+ this.autosave.reset();
- if (window.mrTabs && this.note) {
- window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
- window.mrTabs.tabShown('show');
+ if (window.mrTabs && this.note) {
+ window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
+ window.mrTabs.tabShown('show');
- setTimeout(() =>
- scrollToElement(document.getElementById(`note_${this.getCurrentUserLastNote.id}`)),
- );
+ setTimeout(() =>
+ scrollToElement(document.getElementById(`note_${this.getCurrentUserLastNote.id}`)),
+ );
+ }
+ } catch (e) {
+ if (e.data?.message) {
+ createAlert({ message: e.data.message, captureError: true });
+ }
}
this.isSubmitting = false;
@@ -79,8 +108,9 @@ export default {
<template>
<gl-dropdown
- ref="dropdown"
+ ref="submitDropdown"
right
+ dropup
class="submit-review-dropdown"
data-qa-selector="submit_review_dropdown"
variant="info"
@@ -110,7 +140,7 @@ export default {
<markdown-field
:is-submitting="isSubmitting"
:add-spacing-classes="false"
- :textarea-value="note"
+ :textarea-value="noteData.note"
:markdown-preview-path="getNoteableData.preview_note_path"
:markdown-docs-path="getNotesData.markdownDocsPath"
:quick-actions-docs-path="getNotesData.quickActionsDocsPath"
@@ -122,7 +152,7 @@ export default {
<textarea
id="review-note-body"
ref="textarea"
- v-model="note"
+ v-model="noteData.note"
dir="auto"
:disabled="isSubmitting"
name="review[note]"
@@ -139,6 +169,18 @@ export default {
</div>
</div>
</gl-form-group>
+ <template v-if="getNoteableData.current_user.can_approve">
+ <gl-form-checkbox v-model="noteData.approve" data-testid="approve_merge_request">
+ {{ __('Approve merge request') }}
+ </gl-form-checkbox>
+ <approval-password
+ v-if="getNoteableData.require_password_to_approve"
+ v-show="noteData.approve"
+ v-model="noteData.approval_password"
+ class="gl-mt-3"
+ data-testid="approve_password"
+ />
+ </template>
<div class="gl-display-flex gl-justify-content-start gl-mt-5">
<gl-button
:loading="isSubmitting"
diff --git a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
index a44b9827fe9..2b0aaa74e83 100644
--- a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
+++ b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
@@ -84,7 +84,11 @@ export const publishReview = ({ commit, dispatch, getters }, noteData = {}) => {
.publish(getters.getNotesData.draftsPublishPath, noteData)
.then(() => dispatch('updateDiscussionsAfterPublish'))
.then(() => commit(types.RECEIVE_PUBLISH_REVIEW_SUCCESS))
- .catch(() => commit(types.RECEIVE_PUBLISH_REVIEW_ERROR));
+ .catch((e) => {
+ commit(types.RECEIVE_PUBLISH_REVIEW_ERROR);
+
+ throw e.response;
+ });
};
export const updateDiscussionsAfterPublish = async ({ dispatch, getters, rootGetters }) => {
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index c2111d1f79d..655c9bcad6c 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -183,7 +183,6 @@ export default {
:data-user-id="author.id"
:data-username="author.username"
>
- <slot name="note-header-info"></slot>
<user-name-with-status
:name="authorName"
:availability="userAvailability(author)"
@@ -207,6 +206,7 @@ export default {
@mouseleave="handleUsernameMouseLeave"
><span class="note-headline-light">@{{ author.username }}</span>
</a>
+ <slot name="note-header-info"></slot>
<gitlab-team-member-badge v-if="author && author.is_gitlab_employee" />
</span>
</template>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index ed7ef72086a..83bf66a324d 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -440,7 +440,7 @@ export default {
</gl-avatar-link>
</div>
- <div v-else class="gl-float-left gl-pl-3 gl-mr-3 gl-md-pl-2 gl-md-pr-2">
+ <div v-else class="gl-float-left gl-pl-3 gl-md-pl-2">
<gl-avatar-link :href="author.path">
<gl-avatar
:src="author.avatar_url"
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
index 0458b914b58..7740924b058 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
@@ -1,7 +1,7 @@
<template>
<section class="settings gl-py-7">
- <div class="gl-lg-display-flex gl-gap-6">
- <div class="gl-lg-w-40p gl-pr-10 gl-flex-shrink-0">
+ <div class="row">
+ <div class="col-lg-4">
<h4>
<slot name="title"></slot>
</h4>
@@ -9,7 +9,7 @@
<slot name="description"></slot>
</p>
</div>
- <div class="gl-pt-3 gl-flex-grow-1">
+ <div class="col-lg-8 gl-pt-3">
<slot></slot>
</div>
</div>
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index 14872c34afb..f822e2c0874 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -281,7 +281,6 @@ export default {
:type="graphViewType"
:show-links="showLinks"
:tip-previously-dismissed="hoverTipPreviouslyDismissed"
- :is-pipeline-complete="pipeline.complete"
@dismissHoverTip="handleTipDismissal"
@updateViewType="updateViewType"
@updateShowLinksState="updateShowLinksState"
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
index a8c5d85f4ed..6d8c35f4482 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
@@ -1,33 +1,19 @@
<script>
-import {
- GlAlert,
- GlButton,
- GlButtonGroup,
- GlLoadingIcon,
- GlToggle,
- GlModalDirective,
-} from '@gitlab/ui';
+import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitlab/ui';
import { __, s__ } from '~/locale';
-import Tracking from '~/tracking';
-import PerformanceInsightsModal from '../performance_insights_modal.vue';
-import { performanceModalId } from '../../constants';
import { STAGE_VIEW, LAYER_VIEW } from './constants';
export default {
name: 'GraphViewSelector',
- performanceModalId,
+
components: {
GlAlert,
GlButton,
GlButtonGroup,
GlLoadingIcon,
GlToggle,
- PerformanceInsightsModal,
- },
- directives: {
- GlModal: GlModalDirective,
},
- mixins: [Tracking.mixin()],
+
props: {
showLinks: {
type: Boolean,
@@ -41,10 +27,6 @@ export default {
type: String,
required: true,
},
- isPipelineComplete: {
- type: Boolean,
- required: true,
- },
},
data() {
return {
@@ -59,7 +41,6 @@ export default {
hoverTipText: __('Tip: Hover over a job to see the jobs it depends on to run.'),
linksLabelText: s__('GraphViewType|Show dependencies'),
viewLabelText: __('Group jobs by'),
- performanceBtnText: __('Performance insights'),
},
views: {
[STAGE_VIEW]: {
@@ -150,9 +131,6 @@ export default {
this.$emit('updateShowLinksState', val);
});
},
- trackInsightsClick() {
- this.track('click_insights_button', { label: 'performance_insights' });
- },
},
};
</script>
@@ -178,15 +156,6 @@ export default {
</gl-button>
</gl-button-group>
- <gl-button
- v-if="isPipelineComplete"
- v-gl-modal="$options.performanceModalId"
- data-testid="pipeline-insights-btn"
- @click="trackInsightsClick"
- >
- {{ $options.i18n.performanceBtnText }}
- </gl-button>
-
<div v-if="showLinksToggle" class="gl-display-flex gl-align-items-center">
<gl-toggle
v-model="showLinksActive"
@@ -202,7 +171,5 @@ export default {
<gl-alert v-if="showTip" class="gl-my-5" variant="tip" @dismiss="dismissTip">
{{ $options.i18n.hoverTipText }}
</gl-alert>
-
- <performance-insights-modal />
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue b/app/assets/javascripts/pipelines/components/performance_insights_modal.vue
deleted file mode 100644
index fdbf0ca19bc..00000000000
--- a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<script>
-import { GlAlert, GlCard, GlLink, GlLoadingIcon, GlModal } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
-import { humanizeTimeInterval } from '~/lib/utils/datetime_utility';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import getPerformanceInsightsQuery from '../graphql/queries/get_performance_insights.query.graphql';
-import { performanceModalId } from '../constants';
-import { calculateJobStats, calculateSlowestFiveJobs } from '../utils';
-
-export default {
- name: 'PerformanceInsightsModal',
- i18n: {
- queuedCardHeader: s__('Pipeline|Longest queued job'),
- queuedCardHelp: s__(
- 'Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner',
- ),
- executedCardHeader: s__('Pipeline|Last executed job'),
- executedCardHelp: s__(
- 'Pipeline|The last executed job is the last job to start in the pipeline.',
- ),
- viewDependency: s__('Pipeline|View dependency'),
- slowJobsTitle: s__('Pipeline|Five slowest jobs'),
- feeback: __('Feedback issue'),
- insightsLimit: s__('Pipeline|Only able to show first 100 results'),
- },
- modal: {
- title: s__('Pipeline|Performance insights'),
- actionCancel: {
- text: __('Close'),
- attributes: {
- variant: 'confirm',
- },
- },
- },
- performanceModalId,
- components: {
- GlAlert,
- GlCard,
- GlLink,
- GlModal,
- GlLoadingIcon,
- HelpPopover,
- },
- inject: {
- pipelineIid: {
- default: '',
- },
- pipelineProjectPath: {
- default: '',
- },
- },
- apollo: {
- jobs: {
- query: getPerformanceInsightsQuery,
- variables() {
- return {
- fullPath: this.pipelineProjectPath,
- iid: this.pipelineIid,
- };
- },
- update(data) {
- return data.project?.pipeline?.jobs;
- },
- },
- },
- data() {
- return {
- jobs: null,
- };
- },
- computed: {
- longestQueuedJob() {
- return calculateJobStats(this.jobs, 'queuedDuration');
- },
- lastExecutedJob() {
- return calculateJobStats(this.jobs, 'startedAt');
- },
- slowestFiveJobs() {
- return calculateSlowestFiveJobs(this.jobs);
- },
- queuedDurationDisplay() {
- return humanizeTimeInterval(this.longestQueuedJob.queuedDuration);
- },
- showLimitMessage() {
- return this.jobs.pageInfo.hasNextPage;
- },
- },
-};
-</script>
-
-<template>
- <gl-modal
- :modal-id="$options.performanceModalId"
- :title="$options.modal.title"
- :action-cancel="$options.modal.actionCancel"
- >
- <gl-loading-icon v-if="$apollo.queries.jobs.loading" size="lg" />
-
- <template v-else>
- <gl-alert class="gl-mb-4" :dismissible="false">
- <p v-if="showLimitMessage" data-testid="limit-alert-text">
- {{ $options.i18n.insightsLimit }}
- </p>
- <gl-link href="https://gitlab.com/gitlab-org/gitlab/-/issues/365902" class="gl-mt-5">
- {{ $options.i18n.feeback }}
- </gl-link>
- </gl-alert>
-
- <div class="gl-display-flex gl-justify-content-space-between gl-mt-2 gl-mb-7">
- <gl-card class="gl-w-half gl-mr-7 gl-text-center">
- <template #header>
- <span class="gl-font-weight-bold">{{ $options.i18n.queuedCardHeader }}</span>
- <help-popover>
- {{ $options.i18n.queuedCardHelp }}
- </help-popover>
- </template>
- <div class="gl-display-flex gl-flex-direction-column">
- <span
- class="gl-font-weight-bold gl-font-size-h2 gl-mb-2"
- data-testid="insights-queued-card-data"
- >
- {{ queuedDurationDisplay }}
- </span>
- <gl-link
- :href="longestQueuedJob.detailedStatus.detailsPath"
- data-testid="insights-queued-card-link"
- >
- {{ longestQueuedJob.name }}
- </gl-link>
- </div>
- </gl-card>
- <gl-card class="gl-w-half gl-text-center" data-testid="insights-executed-card">
- <template #header>
- <span class="gl-font-weight-bold">{{ $options.i18n.executedCardHeader }}</span>
- <help-popover>
- {{ $options.i18n.executedCardHelp }}
- </help-popover>
- </template>
- <div class="gl-display-flex gl-flex-direction-column">
- <span
- class="gl-font-weight-bold gl-font-size-h2 gl-mb-2"
- data-testid="insights-executed-card-data"
- >
- {{ lastExecutedJob.name }}
- </span>
- <gl-link
- :href="lastExecutedJob.detailedStatus.detailsPath"
- data-testid="insights-executed-card-link"
- >
- {{ $options.i18n.viewDependency }}
- </gl-link>
- </div>
- </gl-card>
- </div>
-
- <div class="gl-mt-7">
- <span class="gl-font-weight-bold">{{ $options.i18n.slowJobsTitle }}</span>
- <div
- v-for="job in slowestFiveJobs"
- :key="job.name"
- class="gl-display-flex gl-justify-content-space-between gl-mb-3 gl-mt-3 gl-p-4 gl-border-t-1 gl-border-t-solid gl-border-b-0 gl-border-b-solid gl-border-gray-100"
- >
- <span data-testid="insights-slow-job-stage">{{ job.stage.name }}</span>
- <gl-link :href="job.detailedStatus.detailsPath" data-testid="insights-slow-job-link">{{
- job.name
- }}</gl-link>
- </div>
- </div>
- </template>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index 7b38f870cb6..0619ecaabbf 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -109,5 +109,3 @@ export const DEFAULT_FIELDS = [
columnClass: 'gl-w-20p',
},
];
-
-export const performanceModalId = 'performanceInsightsModal';
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql
deleted file mode 100644
index 25e990c8934..00000000000
--- a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql
+++ /dev/null
@@ -1,28 +0,0 @@
-query getPerformanceInsightsData($fullPath: ID!, $iid: ID!) {
- project(fullPath: $fullPath) {
- id
- pipeline(iid: $iid) {
- id
- jobs {
- pageInfo {
- hasNextPage
- }
- nodes {
- id
- duration
- detailedStatus {
- id
- detailsPath
- }
- name
- stage {
- id
- name
- }
- startedAt
- queuedDuration
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/pipelines/utils.js b/app/assets/javascripts/pipelines/utils.js
index 83e00b80426..588d15495ab 100644
--- a/app/assets/javascripts/pipelines/utils.js
+++ b/app/assets/javascripts/pipelines/utils.js
@@ -153,24 +153,3 @@ export const getPipelineDefaultTab = (url) => {
return null;
};
-
-export const calculateJobStats = (jobs, sortField) => {
- const jobNodes = [...jobs.nodes];
-
- const sorted = jobNodes.sort((a, b) => {
- return b[sortField] - a[sortField];
- });
-
- return sorted[0];
-};
-
-export const calculateSlowestFiveJobs = (jobs) => {
- const jobNodes = [...jobs.nodes];
- const limit = 5;
-
- return jobNodes
- .sort((a, b) => {
- return b.duration - a.duration;
- })
- .slice(0, limit);
-};
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 05397906b76..baed3694306 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -771,6 +771,7 @@ $tabs-holder-z-index: 250;
&.show .dropdown-menu {
width: calc(100vw - 20px);
max-width: 650px;
+ max-height: calc(100vh - 50px);
.gl-new-dropdown-inner {
max-height: none !important;
@@ -812,8 +813,7 @@ $tabs-holder-z-index: 250;
}
.md-preview-holder {
- max-height: 180px;
- height: 180px;
+ max-height: 172px;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index db07f16dfd0..4830ade72b0 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -164,7 +164,7 @@ $system-note-svg-size: 16px;
}
.note-body {
- padding: $gl-padding-4;
+ padding: $gl-padding-4 $gl-padding-4 $gl-padding-4 $gl-padding-8;
overflow-x: auto;
overflow-y: hidden;
@@ -305,7 +305,7 @@ $system-note-svg-size: 16px;
height: $system-note-icon-size;
border: 1px solid $gray-10;
border-radius: $system-note-icon-size;
- margin: -6px 20px 0 0;
+ margin: -6px 0 0;
svg {
width: $system-note-svg-size;
@@ -551,6 +551,7 @@ $system-note-svg-size: 16px;
.note-header {
display: flex;
justify-content: space-between;
+ align-items: center;
flex-wrap: wrap;
> .note-header-info,
@@ -581,7 +582,7 @@ $system-note-svg-size: 16px;
.note-header-info {
min-width: 0;
- padding-left: $gl-padding-4;
+ padding-left: $gl-padding-8;
&.discussion {
padding-bottom: 0;
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 5cc0c8f3970..1a57d271271 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -105,7 +105,7 @@ class Admin::UsersController < Admin::ApplicationController
return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked?
return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated?
return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal?
- return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated?
+ return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period }) unless user.can_be_deactivated?
user.deactivate
redirect_back_or_admin_user(notice: _("Successfully deactivated"))
diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb
index ff6b6bfaf27..58d462e8c9f 100644
--- a/app/controllers/projects/merge_requests/drafts_controller.rb
+++ b/app/controllers/projects/merge_requests/drafts_controller.rb
@@ -49,8 +49,16 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
def publish
result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true))
- if Feature.enabled?(:mr_review_submit_comment, @project) && create_note_params[:note]
- Notes::CreateService.new(@project, current_user, create_note_params).execute
+ if Feature.enabled?(:mr_review_submit_comment, @project)
+ ::Notes::CreateService.new(@project, current_user, create_note_params).execute if create_note_params[:note]
+
+ if Gitlab::Utils.to_boolean(approve_params[:approve])
+ success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request)
+
+ unless success
+ return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error
+ end
+ end
end
if result[:status] == :success
@@ -115,6 +123,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
end
end
+ def approve_params
+ params.permit(:approve)
+ end
+
def prepare_notes_for_rendering(notes)
return [] unless notes
@@ -148,3 +160,5 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
access_denied! unless can?(current_user, :create_note, merge_request)
end
end
+
+Projects::MergeRequests::DraftsController.prepend_mod
diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb
index e5e08d2971f..5b2139cb941 100644
--- a/app/finders/deployments_finder.rb
+++ b/app/finders/deployments_finder.rb
@@ -211,6 +211,7 @@ class DeploymentsFinder
environment: [],
deployable: {
job_artifacts: [],
+ user: [],
pipeline: {
project: {
route: [],
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 4ea9a016e74..ab6103d9469 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -92,6 +92,8 @@ module Types
description: 'Indicates the job is stuck.'
field :triggered, GraphQL::Types::Boolean, null: true,
description: 'Whether the job was triggered.'
+ field :web_path, GraphQL::Types::String, null: true,
+ description: 'Web path of the job.'
def kind
return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class)
@@ -181,6 +183,10 @@ module Types
::Gitlab::Routing.url_helpers.project_commits_path(object.project, ref_name)
end
+ def web_path
+ ::Gitlab::Routing.url_helpers.project_job_path(object.project, object)
+ end
+
def coverage
object&.coverage
end
diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb
index 1b61001fc10..70a3a4cb574 100644
--- a/app/graphql/types/deployment_type.rb
+++ b/app/graphql/types/deployment_type.rb
@@ -50,5 +50,20 @@ module Types
field :status,
Types::DeploymentStatusEnum,
description: 'Status of the deployment.'
+
+ field :commit,
+ Types::CommitType,
+ description: 'Commit details of the deployment.',
+ calls_gitaly: true
+
+ field :job,
+ Types::Ci::JobType,
+ description: 'Pipeline job of the deployment.',
+ method: :build
+
+ field :triggerer,
+ Types::UserType,
+ description: 'User who executed the deployment.',
+ method: :deployed_by
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 3293db970c8..a9d07adb5fb 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -437,7 +437,10 @@ module ApplicationSettingsHelper
:pipeline_limit_per_project_user_sha,
:invitation_flow_enforcement
].tap do |settings|
- settings << :deactivate_dormant_users unless Gitlab.com?
+ next if Gitlab.com?
+
+ settings << :deactivate_dormant_users
+ settings << :deactivate_dormant_users_period
end
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index c4db4754c52..1092b9c9564 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -6,6 +6,8 @@ module Ci
include Limitable
include IgnorableColumns
+ TRIGGER_TOKEN_PREFIX = 'glptt-'
+
ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22'
self.limit_name = 'pipeline_triggers'
@@ -22,7 +24,7 @@ module Ci
before_validation :set_default_values
def set_default_values
- self.token = SecureRandom.hex(15) if self.token.blank?
+ self.token = "#{TRIGGER_TOKEN_PREFIX}#{SecureRandom.hex(20)}" if self.token.blank?
end
def last_trigger_request
@@ -34,7 +36,7 @@ module Ci
end
def short_token
- token[0...4] if token.present?
+ token.delete_prefix(TRIGGER_TOKEN_PREFIX)[0...4] if token.present?
end
def can_access_project?
diff --git a/app/models/concerns/approvable.rb b/app/models/concerns/approvable.rb
index 10fc5e8870c..f18a3877fe0 100644
--- a/app/models/concerns/approvable.rb
+++ b/app/models/concerns/approvable.rb
@@ -27,12 +27,11 @@ module Approvable
scope :not_approved_by_users_with_usernames, -> (usernames) do
users = User.where(username: usernames).select(:id)
- self_table = self.arel_table
app_table = Approval.arel_table
where(
Approval.where(approvals: { user_id: users })
- .where(app_table[:merge_request_id].eq(self_table[:id]))
+ .where(app_table[:merge_request_id].eq(arel_table[:id]))
.select('true')
.arel.exists.not
)
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 325754f001a..a463448c206 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -222,6 +222,10 @@ class Deployment < ApplicationRecord
Ci::Build.where(id: deployable_ids)
end
+ def build
+ deployable if deployable.is_a?(::Ci::Build)
+ end
+
class << self
##
# FastDestroyAll concerns
diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb
index 5588897e792..6ebb9d5f176 100644
--- a/app/models/resource_state_event.rb
+++ b/app/models/resource_state_event.rb
@@ -3,8 +3,9 @@
class ResourceStateEvent < ResourceEvent
include IssueResourceEvent
include MergeRequestResourceEvent
+ include Importable
- validate :exactly_one_issuable
+ validate :exactly_one_issuable, unless: :importing?
belongs_to :source_merge_request, class_name: 'MergeRequest', foreign_key: :source_merge_request_id
diff --git a/app/models/resource_timebox_event.rb b/app/models/resource_timebox_event.rb
index 6bde8e63d35..26bf2a225d4 100644
--- a/app/models/resource_timebox_event.rb
+++ b/app/models/resource_timebox_event.rb
@@ -5,8 +5,9 @@ class ResourceTimeboxEvent < ResourceEvent
include IssueResourceEvent
include MergeRequestResourceEvent
+ include Importable
- validate :exactly_one_issuable
+ validate :exactly_one_issuable, unless: :importing?
enum action: {
add: 1,
diff --git a/app/models/user.rb b/app/models/user.rb
index 5e8ef306317..466a81a6a28 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -92,7 +92,6 @@ class User < ApplicationRecord
include ForcedEmailConfirmation
include RequireEmailVerification
- MINIMUM_INACTIVE_DAYS = 90
MINIMUM_DAYS_CREATED = 7
# Override Devise::Models::Trackable#update_tracked_fields!
@@ -488,7 +487,7 @@ class User < ApplicationRecord
scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) }
scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) }
scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) }
- scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) }
+ scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', Gitlab::CurrentSettings.deactivate_dormant_users_period.day.ago.to_date) }
scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) }
@@ -2322,7 +2321,7 @@ class User < ApplicationRecord
end
def no_recent_activity?
- last_active_at.to_i <= MINIMUM_INACTIVE_DAYS.days.ago.to_i
+ last_active_at.to_i <= Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_i
end
def update_highest_role?
diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb
index f8c8e3538da..bd0de3a72b9 100644
--- a/app/serializers/merge_request_noteable_entity.rb
+++ b/app/serializers/merge_request_noteable_entity.rb
@@ -40,6 +40,10 @@ class MergeRequestNoteableEntity < IssuableEntity
expose :can_update do |merge_request|
can?(current_user, :update_merge_request, merge_request)
end
+
+ expose :can_approve do |merge_request|
+ merge_request.can_be_approved_by?(current_user)
+ end
end
expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request|
@@ -65,3 +69,5 @@ class MergeRequestNoteableEntity < IssuableEntity
@presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) # rubocop: disable CodeReuse/Presenter
end
end
+
+MergeRequestNoteableEntity.prepend_mod_with('MergeRequestNoteableEntity')
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 76c25e4cf5a..686f8ff7b86 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -52,7 +52,13 @@
= f.label :deactivate_dormant_users, _('Dormant users'), class: 'label-bold'
- dormant_users_help_link = help_page_path('user/admin_area/moderate_users', anchor: 'automatically-deactivate-dormant-users')
- dormant_users_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: dormant_users_help_link }
- = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after 90 days of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: '</a>'.html_safe }
+ = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after a period of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: '</a>'.html_safe }
+ .form-group
+ = f.label :deactivate_dormant_users_period, _('Period of inactivity (days)'), class: 'label-light'
+ = f.number_field :deactivate_dormant_users_period, class: 'form-control gl-form-input', min: '1'
+ .form-text.text-muted
+ = _('Period of inactivity before deactivation')
+
.form-group
= f.label :personal_access_token_prefix, _('Personal Access Token prefix'), class: 'label-light'
= f.text_field :personal_access_token_prefix, placeholder: _('Maximum 20 characters'), class: 'form-control gl-form-input'
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 7535980151b..cd63873a893 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -13,7 +13,7 @@
.settings-content
= render 'visibility_and_access'
-%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content' } }
+%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content', testid: 'account-limit' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Account and limit')
diff --git a/config/feature_flags/development/escape_gitaly_refs.yml b/config/feature_flags/development/escape_gitaly_refs.yml
new file mode 100644
index 00000000000..bee62c669ee
--- /dev/null
+++ b/config/feature_flags/development/escape_gitaly_refs.yml
@@ -0,0 +1,8 @@
+---
+name: escape_gitaly_refs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91058
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366437
+milestone: '15.2'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/db/post_migrate/20220822094804_add_issues_authorization_index.rb b/db/post_migrate/20220822094804_add_issues_authorization_index.rb
new file mode 100644
index 00000000000..e09b5f8d93b
--- /dev/null
+++ b/db/post_migrate/20220822094804_add_issues_authorization_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIssuesAuthorizationIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'idx_open_issues_on_project_and_confidential_and_author_and_id'
+
+ def up
+ prepare_async_index :issues, [:project_id, :confidential, :author_id, :id], name: INDEX_NAME, where: 'state_id = 1'
+ end
+
+ def down
+ unprepare_async_index :issues, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20220822094804 b/db/schema_migrations/20220822094804
new file mode 100644
index 00000000000..6e4e3b0d5e1
--- /dev/null
+++ b/db/schema_migrations/20220822094804
@@ -0,0 +1 @@
+035e918bcb674fdf1300a5bccbad87806311e6de8589f2db57d7af9cd0108ee9 \ No newline at end of file
diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md
index 62706a9e3b9..e1f5855e066 100644
--- a/doc/administration/auth/ldap/ldap_synchronization.md
+++ b/doc/administration/auth/ldap/ldap_synchronization.md
@@ -38,9 +38,11 @@ fail. This means the user cannot sign in or push or pull code.
The process also updates the following user information:
-- Email address
-- SSH public keys (if `sync_ssh_keys` is set)
-- Kerberos identity (if Kerberos is enabled)
+- Name. Because of a [sync issue](https://gitlab.com/gitlab-org/gitlab/-/issues/342598), `name` is not synchronized if
+ [**Prevent users from changing their profile name**](../../../user/admin_area/settings/account_and_limit_settings.md#disable-user-profile-name-changes) is enabled.
+- Email address.
+- SSH public keys if `sync_ssh_keys` is set.
+- Kerberos identity if Kerberos is enabled.
### Adjust LDAP user sync schedule
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index e9d7b5087c5..bea6328ccdd 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -191,6 +191,12 @@ to use the HTTPS protocol.
WARNING:
Multiple wildcards for one instance is not supported. Only one wildcard per instance can be assigned.
+WARNING:
+GitLab Pages does not update the OAuth application if changes are made to the redirect URI.
+Before you reconfigure, remove the `gitlab_pages` section from `/etc/gitlab/gitlab-secrets.json`,
+then run `gitlab-ctl reconfigure`. For more information, read
+[GitLab Pages does not regenerate OAuth](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3947).
+
### Wildcard domains with TLS-terminating Load Balancer
**Requirements:**
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 28f23900839..7698cbc8178 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -10199,6 +10199,7 @@ CI/CD variables for a GitLab instance.
| <a id="cijobtags"></a>`tags` | [`[String!]`](#string) | Tags for the current job. |
| <a id="cijobtriggered"></a>`triggered` | [`Boolean`](#boolean) | Whether the job was triggered. |
| <a id="cijobuserpermissions"></a>`userPermissions` | [`JobPermissions!`](#jobpermissions) | Permissions for the current user on the resource. |
+| <a id="cijobwebpath"></a>`webPath` | [`String`](#string) | Web path of the job. |
### `CiJobArtifact`
@@ -11019,14 +11020,17 @@ The deployment of an environment.
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="deploymentcommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
| <a id="deploymentcreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
| <a id="deploymentfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
| <a id="deploymentid"></a>`id` | [`ID`](#id) | Global ID of the deployment. |
| <a id="deploymentiid"></a>`iid` | [`ID`](#id) | Project-level internal ID of the deployment. |
+| <a id="deploymentjob"></a>`job` | [`CiJob`](#cijob) | Pipeline job of the deployment. |
| <a id="deploymentref"></a>`ref` | [`String`](#string) | Git-Ref that the deployment ran on. |
| <a id="deploymentsha"></a>`sha` | [`String`](#string) | Git-SHA that the deployment ran on. |
| <a id="deploymentstatus"></a>`status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. |
| <a id="deploymenttag"></a>`tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. |
+| <a id="deploymenttriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
| <a id="deploymentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
### `DeploymentDetails`
@@ -11037,14 +11041,17 @@ The details of the deployment.
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="deploymentdetailscommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
| <a id="deploymentdetailscreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
| <a id="deploymentdetailsfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
| <a id="deploymentdetailsid"></a>`id` | [`ID`](#id) | Global ID of the deployment. |
| <a id="deploymentdetailsiid"></a>`iid` | [`ID`](#id) | Project-level internal ID of the deployment. |
+| <a id="deploymentdetailsjob"></a>`job` | [`CiJob`](#cijob) | Pipeline job of the deployment. |
| <a id="deploymentdetailsref"></a>`ref` | [`String`](#string) | Git-Ref that the deployment ran on. |
| <a id="deploymentdetailssha"></a>`sha` | [`String`](#string) | Git-SHA that the deployment ran on. |
| <a id="deploymentdetailsstatus"></a>`status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. |
| <a id="deploymentdetailstag"></a>`tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. |
+| <a id="deploymentdetailstriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
| <a id="deploymentdetailsupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
### `Design`
diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md
index 864615e7264..dda15845088 100644
--- a/doc/topics/git/lfs/migrate_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_to_git_lfs.md
@@ -9,7 +9,7 @@ description: "How to migrate an existing Git repository to Git LFS with BFG."
WARNING:
The following documentation is deprecated. We recommend using
-[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.1.ronn)
+[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.adoc)
instead of the method documented below.
Using Git LFS can help you to reduce the size of your Git
diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md
index ab581cd3aa8..bdb2fb30b06 100644
--- a/doc/user/admin_area/moderate_users.md
+++ b/doc/user/admin_area/moderate_users.md
@@ -169,12 +169,13 @@ Users can also be deactivated using the [GitLab API](../../api/users.md#deactiva
### Automatically deactivate dormant users
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0.
+> - Customizable time period [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.4
Administrators can enable automatic deactivation of users who either:
- Were created more than a week ago and have not signed in.
-- Have no activity in the last 90 days.
+- Have no activity for a specified period of time (defaults to 90 days).
To do this:
@@ -182,6 +183,7 @@ To do this:
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Under **Dormant users**, check **Deactivate dormant users after 90 days of inactivity**.
+1. Under **Period of inactivity (days)**, enter a period of time before deactivation.
1. Select **Save changes**.
When this feature is enabled, GitLab runs a job once a day to deactivate the dormant users.
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index e016aeb977d..0cadf89fb0c 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -88,6 +88,7 @@ migrated:
- Board Lists
- Boards
- Epics ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) in 13.7)
+ - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Finisher
- Group Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) in 13.9)
- Iterations ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) in 13.10)
@@ -116,6 +117,8 @@ On self-managed GitLab, migrating project resources are not available by default
- CI Pipelines ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339407) in GitLab 14.6)
- Designs ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339421) in GitLab 15.1)
- Issues ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267946) in GitLab 14.4)
+ - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339419) in GitLab 14.4)
- LFS Objects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339405) in GitLab 14.8)
- Members ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341886) in GitLab 14.8)
@@ -123,6 +126,8 @@ On self-managed GitLab, migrating project resources are not available by default
- Multiple merge request assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
- Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
- Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Merge request resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Migrate Push Rules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.6)
- Pull Requests (including external pull requests) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339409) in GitLab 14.5)
- Pipeline History ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339412) in GitLab 14.6)
diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md
index eb94a181647..9dce1f0f023 100644
--- a/doc/user/group/settings/import_export.md
+++ b/doc/user/group/settings/import_export.md
@@ -55,6 +55,7 @@ The following items are exported:
- Badges
- Subgroups (including all the aforementioned data)
- Epics
+ - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Events
- [Wikis](../../project/wiki/group.md)
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9)
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 6da2e5fc7ee..5e5a42a061b 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -59,7 +59,8 @@ To compare branches in a repository:
![Delete merged branches](img/delete_merged_branches.png)
This feature allows merged branches to be deleted in bulk. Only branches that
-have been merged and [are not protected](../../protected_branches.md) are deleted as part of
+have been merged into the project's default branch and
+[are not protected](../../protected_branches.md) are deleted as part of
this operation.
It's particularly useful to clean up old branches that were not deleted
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 4eeb7c5ba83..fa6fb2971d6 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -62,8 +62,18 @@ The following items are exported:
- Project and wiki repositories
- Project uploads
- Project configuration, excluding integrations
-- Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, time
- tracking, and other project entities
+- Issues
+ - Issue comments
+ - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+- Merge requests
+ - Merge request diffs
+ - Merge request comments
+ - Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+- Labels
+- Milestones
+- Snippets
+- Time tracking and other project entities
- Design Management files and data
- LFS objects
- Issue boards
diff --git a/lib/api/users.rb b/lib/api/users.rb
index c93c0f601a0..adaafa08aea 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -733,7 +733,7 @@ module API
unless user.can_be_deactivated?
forbidden!('A blocked user cannot be deactivated by the API') if user.blocked?
forbidden!('An internal user cannot be deactivated by the API') if user.internal?
- forbidden!("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ forbidden!("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
end
if user.deactivate
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index f26ab6e3ed1..34c674c3003 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -71,6 +71,21 @@ module Gitlab
encode_utf8(data, replace: UNICODE_REPLACEMENT_CHARACTER)
end
+ # This method escapes unsupported UTF-8 characters instead of deleting them
+ def encode_utf8_with_escaping!(message)
+ return encode!(message) if Feature.disabled?(:escape_gitaly_refs)
+
+ message = force_encode_utf8(message)
+ return message if message.valid_encoding?
+
+ unless message.valid_encoding?
+ message = message.chars.map { |char| char.valid_encoding? ? char : escape_chars(char) }.join
+ end
+
+ # encode and clean the bad chars
+ message.replace clean(message)
+ end
+
def encode_utf8(message, replace: "")
message = force_encode_utf8(message)
return message if message.valid_encoding?
@@ -145,6 +160,15 @@ module Gitlab
message.force_encoding("UTF-8")
end
+ # Escapes \x80 - \xFF characters not supported by UTF-8
+ def escape_chars(char)
+ bytes = char.bytes
+
+ return char unless bytes.one?
+
+ "%#{bytes.first.to_s(16).upcase}"
+ end
+
def clean(message, replace: "")
message.encode(
"UTF-16BE",
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 4b9f2ababc8..4b877bf44da 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -25,7 +25,7 @@ module Gitlab
include Gitlab::EncodingHelper
def ref_name(ref)
- encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
+ encode_utf8_with_escaping!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
end
def branch_name(ref)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index e920b7e5843..ddc43b1764b 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1011,16 +1011,20 @@ module Gitlab
end
def search_files_by_name(query, ref)
- safe_query = Regexp.escape(query.sub(%r{^/*}, ""))
+ safe_query = query.sub(%r{^/*}, "")
ref ||= root_ref
return [] if empty? || safe_query.blank?
- gitaly_repository_client.search_files_by_name(ref, safe_query)
+ gitaly_repository_client.search_files_by_name(ref, safe_query).map do |file|
+ Gitlab::EncodingHelper.encode_utf8(file)
+ end
end
def search_files_by_regexp(filter, ref = 'HEAD')
- gitaly_repository_client.search_files_by_regexp(ref, filter)
+ gitaly_repository_client.search_files_by_regexp(ref, filter).map do |file|
+ Gitlab::EncodingHelper.encode_utf8(file)
+ end
end
def find_commits_by_message(query, ref, path, limit, offset)
diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb
index 25895dc6728..5ed5158eeea 100644
--- a/lib/gitlab/git/tag.rb
+++ b/lib/gitlab/git/tag.rb
@@ -63,7 +63,7 @@ module Gitlab
end
def init_from_gitaly
- @name = encode!(@raw_tag.name.dup)
+ @name = encode_utf8_with_escaping!(@raw_tag.name.dup)
@target = @raw_tag.id
@message = message_from_gitaly_tag
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 42f9c165610..0a1a61363f1 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -104,7 +104,7 @@ module Gitlab
return unless branch
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
- Gitlab::Git::Branch.new(@repository, encode!(branch.name.dup), branch.target_commit.id, target_commit)
+ Gitlab::Git::Branch.new(@repository, branch.name.dup, branch.target_commit.id, target_commit)
end
def find_tag(tag_name)
@@ -273,7 +273,7 @@ module Gitlab
message.branches.map do |gitaly_branch|
Gitlab::Git::Branch.new(
@repository,
- encode!(gitaly_branch.name.dup),
+ gitaly_branch.name.dup,
gitaly_branch.commit_id,
commit_from_local_branches_response(gitaly_branch)
)
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index a58469cea57..109f1f85b1c 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -120,6 +120,7 @@ ee:
- events:
- :push_event_payload
- :system_note_metadata
+ - :resource_state_events
- boards:
- :board_assignee
- :milestone
diff --git a/lib/gitlab/import_export/group/legacy_import_export.yml b/lib/gitlab/import_export/group/legacy_import_export.yml
index 985e3af1e0c..4df481822a8 100644
--- a/lib/gitlab/import_export/group/legacy_import_export.yml
+++ b/lib/gitlab/import_export/group/legacy_import_export.yml
@@ -114,6 +114,7 @@ ee:
- :award_emoji
- events:
- :push_event_payload
+ - :resource_state_events
- boards:
- :board_assignee
- :milestone
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index c5b8f3fd35b..6dd5f7b98ea 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -29,6 +29,9 @@ tree:
- resource_label_events:
- label:
- :priorities
+ - resource_milestone_events:
+ - :milestone
+ - :resource_state_events
- designs:
- notes:
- :author
@@ -82,6 +85,9 @@ tree:
- resource_label_events:
- label:
- :priorities
+ - resource_milestone_events:
+ - :milestone
+ - :resource_state_events
- :external_pull_requests
- ci_pipelines:
- notes:
@@ -721,6 +727,22 @@ included_attributes:
- :build_git_strategy
- :build_enabled
- :security_and_compliance_enabled
+ resource_iteration_events:
+ - :user_id
+ - :action
+ - :created_at
+ resource_milestone_events:
+ - :user_id
+ - :action
+ - :created_at
+ - :state
+ resource_state_events:
+ - :user_id
+ - :state
+ - :created_at
+ - :source_commit
+ - :close_after_error_tracking_resolve
+ - :close_auto_resolve_prometheus_alert
# Do not include the following attributes for the models specified.
excluded_attributes:
@@ -989,6 +1011,17 @@ excluded_attributes:
milestone_releases:
- :milestone_id
- :release_id
+ resource_milestone_events:
+ - :id
+ - :issue_id
+ - :merge_request_id
+ - :milestone_id
+ resource_state_events:
+ - :id
+ - :issue_id
+ - :merge_request_id
+ - :epic_id
+ - :source_merge_request_id
methods:
notes:
- :type
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 97370eaee60..38d16eb4f17 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4035,6 +4035,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4873,6 +4876,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -12297,7 +12303,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -16409,9 +16415,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -28402,9 +28405,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28480,6 +28480,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation"
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29191,18 +29197,9 @@ msgstr ""
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
msgid "Pipeline|Manual"
msgstr ""
@@ -29215,18 +29212,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29284,12 +29275,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29320,9 +29305,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -40856,6 +40838,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -45572,6 +45557,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 515ad9daf36..bd70787e19f 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -270,19 +270,19 @@ RSpec.describe Admin::UsersController do
let(:user) { create(:user, **activity) }
context 'with no recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } }
it_behaves_like 'a request that deactivates the user'
end
context 'with recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } }
it 'does not deactivate the user' do
put :deactivate, params: { id: user.username }
user.reload
expect(user.deactivated?).to be_falsey
- expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
end
end
end
diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
index b9ede84157d..ea782309f93 100644
--- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Projects::MergeRequests::DraftsController do
include RepoHelpers
let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
+ let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project, author: create(:user)) }
let(:user) { project.first_owner }
let(:user2) { create(:user) }
@@ -417,6 +417,38 @@ RSpec.describe Projects::MergeRequests::DraftsController do
end
end
end
+
+ context 'approve merge request' do
+ before do
+ create(:draft_note, merge_request: merge_request, author: user)
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(mr_review_submit_comment: false)
+ end
+
+ it 'does not approve' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(merge_request.approvals.reload.size).to be(0)
+ end
+ end
+
+ context 'when feature flag is enabled' do
+ it 'approves merge request' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(merge_request.approvals.reload.size).to be(1)
+ end
+
+ it 'does not approve merge request' do
+ post :publish, params: params.merge!(approve: false)
+
+ expect(merge_request.approvals.reload.size).to be(0)
+ end
+ end
+ end
end
describe 'DELETE #destroy' do
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 8843e13026b..0f5d9892e66 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Account and Limit Settings' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
uncheck 'Gravatar enabled'
click_button 'Save changes'
end
@@ -112,7 +112,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Maximum export size' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
fill_in 'Maximum export size (MB)', with: 25
click_button 'Save changes'
end
@@ -122,7 +122,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Maximum import size' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
fill_in 'Maximum import size (MB)', with: 15
click_button 'Save changes'
end
@@ -150,16 +150,20 @@ RSpec.describe 'Admin updates settings' do
it 'does not expose the setting' do
expect(page).to have_no_selector('#application_setting_deactivate_dormant_users')
end
+
+ it 'does not expose the setting' do
+ expect(page).to have_no_selector('#application_setting_deactivate_dormant_users_period')
+ end
end
context 'when not Gitlab.com' do
let(:dot_com?) { false }
- it 'change Dormant users' do
- expect(page).to have_unchecked_field('Deactivate dormant users after 90 days of inactivity')
+ it 'changes Dormant users' do
+ expect(page).to have_unchecked_field('Deactivate dormant users after a period of inactivity')
expect(current_settings.deactivate_dormant_users).to be_falsey
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
check 'application_setting_deactivate_dormant_users'
click_button 'Save changes'
end
@@ -169,7 +173,22 @@ RSpec.describe 'Admin updates settings' do
page.refresh
expect(current_settings.deactivate_dormant_users).to be_truthy
- expect(page).to have_checked_field('Deactivate dormant users after 90 days of inactivity')
+ expect(page).to have_checked_field('Deactivate dormant users after a period of inactivity')
+ end
+
+ it 'change Dormant users period' do
+ expect(page).to have_field _('Period of inactivity (days)')
+
+ page.within(find('[data-testid="account-limit"]')) do
+ fill_in _('application_setting_deactivate_dormant_users_period'), with: '35'
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+
+ page.refresh
+
+ expect(page).to have_field _('Period of inactivity (days)'), with: '35'
end
end
end
diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
index 4ef19ed32c2..705ebcbd843 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
@@ -22,11 +22,13 @@
"type": "object",
"required": [
"can_create_note",
- "can_update"
+ "can_update",
+ "can_approve"
],
"properties": {
"can_create_note": { "type": "boolean" },
- "can_update": { "type": "boolean" }
+ "can_update": { "type": "boolean" },
+ "can_approve": { "type": "boolean" }
},
"additionalProperties": false
},
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json
index df571ab1326..f3fc69e4936 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json
@@ -394,7 +394,41 @@
"id": 1,
"issue_id": 40,
"sentry_issue_identifier": 1234567891
- }
+ },
+ "resource_milestone_events": [
+ {
+ "user_id": 1,
+ "action": "add",
+ "state": "opened",
+ "created_at": "2022-08-17T13:06:53.547Z",
+ "milestone": {
+ "title": "v4.0",
+ "description": "Totam quam laborum id magnam natus eaque aspernatur.",
+ "created_at": "2016-06-14T15:02:04.590Z",
+ "updated_at": "2016-06-14T15:02:04.590Z",
+ "state": "active",
+ "iid": 5
+ }
+ }
+ ],
+ "resource_state_events": [
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:16.838Z",
+ "state": "closed",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ },
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:17.702Z",
+ "state": "reopened",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ }
+ ]
},
{
"id": 39,
@@ -3215,6 +3249,40 @@
"created_at": "2020-01-07T11:21:21.235Z",
"updated_at": "2020-01-08T11:21:21.235Z"
}
+ ],
+ "resource_milestone_events": [
+ {
+ "user_id": 1,
+ "action": "add",
+ "state": "opened",
+ "created_at": "2022-08-17T13:06:53.547Z",
+ "milestone": {
+ "title": "v4.0",
+ "description": "Totam quam laborum id magnam natus eaque aspernatur.",
+ "created_at": "2016-06-14T15:02:04.590Z",
+ "updated_at": "2016-06-14T15:02:04.590Z",
+ "state": "active",
+ "iid": 5
+ }
+ }
+ ],
+ "resource_state_events": [
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:16.838Z",
+ "state": "closed",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ },
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:17.702Z",
+ "state": "reopened",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ }
]
},
{
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
index 2ebd1a78783..3955107865d 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
@@ -1,4 +1,4 @@
-{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891}}
+{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891},"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]}
{"id":39,"title":"Issue without assignees","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.233Z","updated_at":"2016-06-14T15:02:48.194Z","position":0,"branch_name":null,"description":"Voluptate vel reprehenderit facilis omnis voluptas magnam tenetur.","state":"opened","iid":9,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"notes":[{"id":359,"note":"Quo eius velit quia et id quam.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.009Z","updated_at":"2016-06-14T15:02:48.009Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":360,"note":"Nulla commodi ratione cumque id autem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.032Z","updated_at":"2016-06-14T15:02:48.032Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":361,"note":"Illum non ea sed dolores corrupti.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.056Z","updated_at":"2016-06-14T15:02:48.056Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":362,"note":"Facere dolores ipsum dolorum maiores omnis occaecati ab.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.082Z","updated_at":"2016-06-14T15:02:48.082Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":363,"note":"Quod laudantium similique sint aut est ducimus.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.113Z","updated_at":"2016-06-14T15:02:48.113Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":364,"note":"Aut omnis eos esse incidunt vero reiciendis.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.139Z","updated_at":"2016-06-14T15:02:48.139Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":365,"note":"Beatae dolore et doloremque asperiores sunt.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.162Z","updated_at":"2016-06-14T15:02:48.162Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":366,"note":"Doloribus ipsam ex delectus rerum libero recusandae modi repellendus.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.192Z","updated_at":"2016-06-14T15:02:48.192Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
{"id":38,"title":"Quasi adipisci non cupiditate dolorem quo qui earum sed.","author_id":6,"project_id":5,"created_at":"2016-06-14T15:02:08.154Z","updated_at":"2016-06-14T15:02:48.614Z","position":0,"branch_name":null,"description":"Ea recusandae neque autem tempora.","state":"closed","iid":8,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"label_links":[{"id":99,"label_id":2,"target_id":38,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"notes":[{"id":367,"note":"Accusantium fugiat et eaque quisquam esse corporis.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.235Z","updated_at":"2016-06-14T15:02:48.235Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":368,"note":"Ea labore eum nam qui laboriosam.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.261Z","updated_at":"2016-06-14T15:02:48.261Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":369,"note":"Accusantium quis sed molestiae et.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.294Z","updated_at":"2016-06-14T15:02:48.294Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":370,"note":"Corporis numquam a voluptatem pariatur asperiores dolorem delectus autem.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.523Z","updated_at":"2016-06-14T15:02:48.523Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":371,"note":"Ea accusantium maxime voluptas rerum.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.546Z","updated_at":"2016-06-14T15:02:48.546Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":372,"note":"Pariatur iusto et et excepturi similique ipsam eum.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.569Z","updated_at":"2016-06-14T15:02:48.569Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":373,"note":"Aliquam et culpa officia iste eius.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.591Z","updated_at":"2016-06-14T15:02:48.591Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":374,"note":"Ab id velit id unde laborum.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.613Z","updated_at":"2016-06-14T15:02:48.613Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
{"id":37,"title":"Cupiditate quo aut ducimus minima molestiae vero numquam possimus.","author_id":20,"project_id":5,"created_at":"2016-06-14T15:02:08.051Z","updated_at":"2016-06-14T15:02:48.854Z","position":0,"branch_name":null,"description":"Maiores architecto quos in dolorem.","state":"opened","iid":7,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"notes":[{"id":375,"note":"Quasi fugit qui sed eligendi aut quia.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.647Z","updated_at":"2016-06-14T15:02:48.647Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":376,"note":"Esse nesciunt voluptatem ex vero est consequatur.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.674Z","updated_at":"2016-06-14T15:02:48.674Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":377,"note":"Similique qui quas non aut et velit sequi in.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.696Z","updated_at":"2016-06-14T15:02:48.696Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":378,"note":"Eveniet ut cupiditate repellendus numquam in esse eius.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.720Z","updated_at":"2016-06-14T15:02:48.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":379,"note":"Velit est dolorem adipisci rerum sed iure.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.755Z","updated_at":"2016-06-14T15:02:48.755Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":380,"note":"Voluptatem ullam ab ut illo ut quo.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.793Z","updated_at":"2016-06-14T15:02:48.793Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":381,"note":"Voluptatem impedit beatae quasi ipsa earum consectetur.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.823Z","updated_at":"2016-06-14T15:02:48.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":382,"note":"Nihil officiis eaque incidunt sunt voluptatum excepturi.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.852Z","updated_at":"2016-06-14T15:02:48.852Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
index 3388e315922..887d7ab658b 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
@@ -1,4 +1,4 @@
-{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}]}
+{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}],"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]}
{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}],"merge_request_assignees":[],"merge_request_reviewers":[],"approvals":[]}
{"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","committed_date":"2016-01-19T13:22:56.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]}
{"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","committed_date":"2016-01-19T14:14:43.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n+<svg width=\"1300px\" height=\"680px\" viewBox=\"0 0 1300 680\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n+ <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->\n+ <title>wm</title>\n+ <desc>Created with Sketch.</desc>\n+ <defs>\n+ <path id=\"path-1\" d=\"M-69.8,1023.54607 L1675.19996,1023.54607 L1675.19996,0 L-69.8,0 L-69.8,1023.54607 L-69.8,1023.54607 Z\"></path>\n+ </defs>\n+ <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n+ <path d=\"M1300,680 L0,680 L0,0 L1300,0 L1300,680 L1300,680 Z\" id=\"bg\" fill=\"#30353E\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"gitlab_logo\" sketch:type=\"MSLayerGroup\" transform=\"translate(-262.000000, -172.000000)\">\n+ <g id=\"g10\" transform=\"translate(872.500000, 512.354581) scale(1, -1) translate(-872.500000, -512.354581) translate(0.000000, 0.290751)\">\n+ <g id=\"g12\" transform=\"translate(1218.022652, 440.744871)\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\">\n+ <path d=\"M-50.0233338,141.900706 L-69.07059,141.900706 L-69.0100967,0.155858152 L8.04444805,0.155858152 L8.04444805,17.6840847 L-49.9628405,17.6840847 L-50.0233338,141.900706 L-50.0233338,141.900706 Z\" id=\"path14\"></path>\n+ </g>\n+ <g id=\"g16\">\n+ <g id=\"g18-Clipped\">\n+ <mask id=\"mask-2\" sketch:name=\"path22\" fill=\"white\">\n+ <use xlink:href=\"#path-1\"></use>\n+ </mask>\n+ <g id=\"path22\"></g>\n+ <g id=\"g18\" mask=\"url(#mask-2)\">\n+ <g transform=\"translate(382.736659, 312.879425)\">\n+ <g id=\"g24\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(852.718192, 124.992771)\">\n+ <path d=\"M63.9833317,27.9148929 C59.2218085,22.9379001 51.2134221,17.9597442 40.3909323,17.9597442 C25.8888194,17.9597442 20.0453962,25.1013043 20.0453962,34.4074318 C20.0453962,48.4730484 29.7848226,55.1819277 50.5642821,55.1819277 C54.4602853,55.1819277 60.7364685,54.7492469 63.9833317,54.1002256 L63.9833317,27.9148929 L63.9833317,27.9148929 Z M44.2869356,113.827628 C28.9053426,113.827628 14.7975996,108.376082 3.78897657,99.301416 L10.5211864,87.6422957 C18.3131929,92.1866076 27.8374026,96.7320827 41.4728323,96.7320827 C57.0568452,96.7320827 63.9833317,88.7239978 63.9833317,75.3074024 L63.9833317,68.3821827 C60.9528485,69.0312039 54.6766653,69.4650479 50.7806621,69.4650479 C17.4476729,69.4650479 0.565379986,57.7791759 0.565379986,33.3245665 C0.565379986,11.4683685 13.9844297,0.43151772 34.3299658,0.43151772 C48.0351955,0.43151772 61.1692285,6.70771614 65.7143717,16.8780421 L69.1776149,3.02876588 L82.5978279,3.02876588 L82.5978279,75.5237428 C82.5978279,98.462806 72.6408582,113.827628 44.2869356,113.827628 L44.2869356,113.827628 Z\" id=\"path26\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g28\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(959.546624, 124.857151)\">\n+ <path d=\"M37.2266657,17.4468081 C30.0837992,17.4468081 23.8064527,18.3121698 19.0449295,20.4767371 L19.0449295,79.2306079 L19.0449295,86.0464943 C25.538656,91.457331 33.5470425,95.3526217 43.7203922,95.3526217 C62.1173451,95.3526217 69.2602116,82.3687072 69.2602116,61.3767077 C69.2602116,31.5135879 57.7885819,17.4468081 37.2266657,17.4468081 M45.2315622,113.963713 C28.208506,113.963713 19.0449295,102.384849 19.0449295,102.384849 L19.0449295,120.67143 L18.9844362,144.908535 L10.3967097,144.908535 L0.371103324,144.908535 L0.431596656,6.62629771 C9.73826309,2.73100702 22.5081728,0.567602823 36.3611458,0.567602823 C71.8579349,0.567602823 88.9566078,23.2891625 88.9566078,62.4584098 C88.9566078,93.4043948 73.1527248,113.963713 45.2315622,113.963713\" id=\"path30\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g32\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(509.576747, 125.294950)\">\n+ <path d=\"M68.636665,129.10638 C85.5189579,129.10638 96.3414476,123.480366 103.484314,117.853189 L111.669527,132.029302 C100.513161,141.811145 85.5073245,147.06845 69.5021849,147.06845 C29.0274926,147.06845 0.673569983,122.3975 0.673569983,72.6252464 C0.673569983,20.4709215 31.2622559,0.12910638 66.2553217,0.12910638 C83.7879179,0.12910638 98.7227909,4.24073748 108.462217,8.35236859 L108.063194,64.0763105 L108.063194,70.6502677 L108.063194,81.6057001 L56.1168719,81.6057001 L56.1168719,64.0763105 L89.2323178,64.0763105 L89.6313411,21.7701271 C85.3025779,19.6055598 77.7269514,17.8748364 67.554765,17.8748364 C39.4172223,17.8748364 20.5863462,35.5717154 20.5863462,72.8415868 C20.5863462,110.711628 40.0663623,129.10638 68.636665,129.10638\" id=\"path34\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g36\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(692.388992, 124.376085)\">\n+ <path d=\"M19.7766662,145.390067 L1.16216997,145.390067 L1.2226633,121.585642 L1.2226633,111.846834 L1.2226633,106.170806 L1.2226633,96.2656714 L1.2226633,39.5681976 L1.2226633,39.3518572 C1.2226633,16.4127939 11.1796331,1.04797161 39.5335557,1.04797161 C43.4504989,1.04797161 47.2836822,1.40388649 51.0051854,2.07965952 L51.0051854,18.7925385 C48.3109055,18.3796307 45.4351455,18.1446804 42.3476589,18.1446804 C26.763646,18.1446804 19.8371595,26.1516022 19.8371595,39.5681976 L19.8371595,96.2656714 L51.0051854,96.2656714 L51.0051854,111.846834 L19.8371595,111.846834 L19.7766662,145.390067 L19.7766662,145.390067 Z\" id=\"path38\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <path d=\"M646.318899,128.021188 L664.933395,128.021188 L664.933395,236.223966 L646.318899,236.223966 L646.318899,128.021188 L646.318899,128.021188 Z\" id=\"path40\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <path d=\"M646.318899,251.154944 L664.933395,251.154944 L664.933395,269.766036 L646.318899,269.766036 L646.318899,251.154944 L646.318899,251.154944 Z\" id=\"path42\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"g44\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.464170, 0.676006)\">\n+ <path d=\"M429.269989,169.815599 L405.225053,243.802859 L357.571431,390.440955 C355.120288,397.984955 344.444378,397.984955 341.992071,390.440955 L294.337286,243.802859 L136.094873,243.802859 L88.4389245,390.440955 C85.9877812,397.984955 75.3118715,397.984955 72.8595648,390.440955 L25.2059427,243.802859 L1.16216997,169.815599 C-1.03187664,163.067173 1.37156997,155.674379 7.11261982,151.503429 L215.215498,0.336141836 L423.319539,151.503429 C429.060589,155.674379 431.462873,163.067173 429.269989,169.815599\" id=\"path46\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g48\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(135.410135, 1.012147)\">\n+ <path d=\"M80.269998,0 L80.269998,0 L159.391786,243.466717 L1.14820997,243.466717 L80.269998,0 L80.269998,0 Z\" id=\"path50\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g52\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path54\"></g>\n+ </g>\n+ <g id=\"g56\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(24.893471, 1.012613)\">\n+ <path d=\"M190.786662,0 L111.664874,243.465554 L0.777106647,243.465554 L190.786662,0 L190.786662,0 Z\" id=\"path58\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g60\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <g id=\"path62\"></g>\n+ </g>\n+ <g id=\"g64\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.077245, 0.223203)\">\n+ <path d=\"M25.5933327,244.255313 L25.5933327,244.255313 L1.54839663,170.268052 C-0.644486651,163.519627 1.75779662,156.126833 7.50000981,151.957046 L215.602888,0.789758846 L25.5933327,244.255313 L25.5933327,244.255313 Z\" id=\"path66\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g68\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path70\"></g>\n+ </g>\n+ <g id=\"g72\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(25.670578, 244.478283)\">\n+ <path d=\"M0,0 L110.887767,0 L63.2329818,146.638096 C60.7806751,154.183259 50.1047654,154.183259 47.6536221,146.638096 L0,0 L0,0 Z\" id=\"path74\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g76\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <path d=\"M0,0 L79.121788,243.465554 L190.009555,243.465554 L0,0 L0,0 Z\" id=\"path78\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g80\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(214.902910, 0.223203)\">\n+ <path d=\"M190.786662,244.255313 L190.786662,244.255313 L214.831598,170.268052 C217.024481,163.519627 214.622198,156.126833 208.879985,151.957046 L0.777106647,0.789758846 L190.786662,244.255313 L190.786662,244.255313 Z\" id=\"path82\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g84\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(294.009575, 244.478283)\">\n+ <path d=\"M111.679997,0 L0.79222998,0 L48.4470155,146.638096 C50.8993221,154.183259 61.5752318,154.183259 64.0263751,146.638096 L111.679997,0 L111.679997,0 Z\" id=\"path86\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+</svg>\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]}
diff --git a/spec/frontend/batch_comments/components/submit_dropdown_spec.js b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
index 4f5ff797230..dc7ecb8e44d 100644
--- a/spec/frontend/batch_comments/components/submit_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
@@ -8,7 +8,7 @@ Vue.use(Vuex);
let wrapper;
let publishReview;
-function factory() {
+function factory({ canApprove = true } = {}) {
publishReview = jest.fn();
const store = new Vuex.Store({
@@ -17,7 +17,11 @@ function factory() {
markdownDocsPath: '/markdown/docs',
quickActionsDocsPath: '/quickactions/docs',
}),
- getNoteableData: () => ({ id: 1, preview_note_path: '/preview' }),
+ getNoteableData: () => ({
+ id: 1,
+ preview_note_path: '/preview',
+ current_user: { can_approve: canApprove },
+ }),
noteableType: () => 'merge_request',
},
modules: {
@@ -54,6 +58,8 @@ describe('Batch comments submit dropdown', () => {
noteable_type: 'merge_request',
noteable_id: 1,
note: 'Hello world',
+ approve: false,
+ approval_password: '',
});
});
@@ -66,4 +72,14 @@ describe('Batch comments submit dropdown', () => {
expect(findSubmitButton().props('loading')).toBe(true);
});
+
+ it.each`
+ canApprove | exists | existsText
+ ${true} | ${true} | ${'shows'}
+ ${false} | ${false} | ${'hides'}
+ `('it $existsText approve checkbox if can_approve is $canApprove', ({ canApprove, exists }) => {
+ factory({ canApprove });
+
+ expect(wrapper.findByTestId('approve_merge_request').exists()).toBe(exists);
+ });
});
diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
index 9f50b12bac2..6369ea9aa15 100644
--- a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
+++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
@@ -180,6 +180,7 @@ describe('Batch comments store actions', () => {
});
it('calls service with notes data', () => {
+ mock.onAny().reply(200);
jest.spyOn(axios, 'post');
return actions
@@ -192,7 +193,7 @@ describe('Batch comments store actions', () => {
it('dispatches error commits', () => {
mock.onAny().reply(500);
- return actions.publishReview({ dispatch, commit, getters, rootGetters }).then(() => {
+ return actions.publishReview({ dispatch, commit, getters, rootGetters }).catch(() => {
expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']);
expect(commit.mock.calls[1]).toEqual(['RECEIVE_PUBLISH_REVIEW_ERROR']);
});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 3eaf06e0656..34784a88ba9 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -30,16 +30,10 @@ import * as Api from '~/pipelines/components/graph_shared/api';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
import * as sentryUtils from '~/pipelines/utils';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { mockRunningPipelineHeaderData } from '../mock_data';
-import {
- mapCallouts,
- mockCalloutsResponse,
- mockPipelineResponse,
- mockPerformanceInsightsResponse,
-} from './mock_data';
+import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
const defaultProvide = {
graphqlResourceEtag: 'frog/amphibirama/etag/',
@@ -95,15 +89,11 @@ describe('Pipeline graph wrapper', () => {
const callouts = mapCallouts(calloutsList);
const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts));
const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData);
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
const requestHandlers = [
[getPipelineHeaderData, getPipelineHeaderDataHandler],
[getPipelineDetails, getPipelineDetailsHandler],
[getUserCallouts, getUserCalloutsHandler],
- [getPerformanceInsights, getPerformanceInsightsHandler],
];
const apolloProvider = createMockApollo(requestHandlers);
diff --git a/spec/frontend/pipelines/graph/graph_view_selector_spec.js b/spec/frontend/pipelines/graph/graph_view_selector_spec.js
index 1397500bdc7..4e79c7e73cc 100644
--- a/spec/frontend/pipelines/graph/graph_view_selector_spec.js
+++ b/spec/frontend/pipelines/graph/graph_view_selector_spec.js
@@ -1,19 +1,10 @@
import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
-import { mockPerformanceInsightsResponse } from './mock_data';
-
-Vue.use(VueApollo);
describe('the graph view selector component', () => {
let wrapper;
- let trackingSpy;
const findDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]');
const findViewTypeSelector = () => wrapper.findComponent(GlButtonGroup);
@@ -22,13 +13,11 @@ describe('the graph view selector component', () => {
const findSwitcherLoader = () => wrapper.find('[data-testid="switcher-loading-state"]');
const findToggleLoader = () => findDependenciesToggle().find(GlLoadingIcon);
const findHoverTip = () => wrapper.findComponent(GlAlert);
- const findPipelineInsightsBtn = () => wrapper.find('[data-testid="pipeline-insights-btn"]');
const defaultProps = {
showLinks: false,
tipPreviouslyDismissed: false,
type: STAGE_VIEW,
- isPipelineComplete: true,
};
const defaultData = {
@@ -38,14 +27,6 @@ describe('the graph view selector component', () => {
showLinksActive: false,
};
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
-
- const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]];
-
- const apolloProvider = createMockApollo(requestHandlers);
-
const createComponent = ({ data = {}, mountFn = shallowMount, props = {} } = {}) => {
wrapper = mountFn(GraphViewSelector, {
propsData: {
@@ -58,7 +39,6 @@ describe('the graph view selector component', () => {
...data,
};
},
- apolloProvider,
});
};
@@ -222,44 +202,5 @@ describe('the graph view selector component', () => {
expect(findHoverTip().exists()).toBe(false);
});
});
-
- describe('pipeline insights', () => {
- it.each`
- isPipelineComplete | shouldShow
- ${true} | ${true}
- ${false} | ${false}
- `(
- 'button should display $shouldShow if isPipelineComplete is $isPipelineComplete ',
- ({ isPipelineComplete, shouldShow }) => {
- createComponent({
- props: {
- isPipelineComplete,
- },
- });
-
- expect(findPipelineInsightsBtn().exists()).toBe(shouldShow);
- },
- );
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- createComponent();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks performance insights button click', () => {
- findPipelineInsightsBtn().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_insights_button', {
- label: 'performance_insights',
- });
- });
- });
});
});
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index 959bbcefc98..6124d67af09 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -1038,245 +1038,3 @@ export const triggerJob = {
action: null,
},
};
-
-export const mockPerformanceInsightsResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: 'gid://gitlab/Project/20',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/97',
- jobs: {
- __typename: 'CiJobConnection',
- pageInfo: {
- __typename: 'PageInfo',
- hasNextPage: false,
- },
- nodes: [
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Bridge/2502',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2502-2502',
- detailsPath: '/root/lots-of-jobs-project/-/pipelines/98',
- },
- name: 'trigger_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: null,
- queuedDuration: 424850.376278,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2501',
- duration: 10,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2501-2501',
- detailsPath: '/root/ci-project/-/jobs/2501',
- },
- name: 'artifact_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: '2022-07-01T16:31:41Z',
- queuedDuration: 2.621553,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2500',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2500-2500',
- detailsPath: '/root/ci-project/-/jobs/2500',
- },
- name: 'coverage_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:33Z',
- queuedDuration: 14.388869,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2499',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2499-2499',
- detailsPath: '/root/ci-project/-/jobs/2499',
- },
- name: 'test_job_two',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:28Z',
- queuedDuration: 15.792664,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2498',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2498-2498',
- detailsPath: '/root/ci-project/-/jobs/2498',
- },
- name: 'test_job_one',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:17Z',
- queuedDuration: 8.317072,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2497',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-2497-2497',
- detailsPath: '/root/ci-project/-/jobs/2497',
- },
- name: 'allow_failure_test_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:22Z',
- queuedDuration: 3.547553,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2496',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'manual-2496-2496',
- detailsPath: '/root/ci-project/-/jobs/2496',
- },
- name: 'test_manual_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: null,
- queuedDuration: null,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2495',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2495-2495',
- detailsPath: '/root/ci-project/-/jobs/2495',
- },
- name: 'large_log_output',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:31:11Z',
- queuedDuration: 79.128625,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2494',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2494-2494',
- detailsPath: '/root/ci-project/-/jobs/2494',
- },
- name: 'build_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:31:05Z',
- queuedDuration: 73.286895,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2493',
- duration: 16,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2493-2493',
- detailsPath: '/root/ci-project/-/jobs/2493',
- },
- name: 'wait_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:30:48Z',
- queuedDuration: 56.258856,
- },
- ],
- },
- },
- },
- },
-};
-
-export const mockPerformanceInsightsNextPageResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: 'gid://gitlab/Project/20',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/97',
- jobs: {
- __typename: 'CiJobConnection',
- pageInfo: {
- __typename: 'PageInfo',
- hasNextPage: true,
- },
- nodes: [
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Bridge/2502',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2502-2502',
- detailsPath: '/root/lots-of-jobs-project/-/pipelines/98',
- },
- name: 'trigger_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: null,
- queuedDuration: 424850.376278,
- },
- ],
- },
- },
- },
- },
-};
diff --git a/spec/frontend/pipelines/performance_insights_modal_spec.js b/spec/frontend/pipelines/performance_insights_modal_spec.js
deleted file mode 100644
index 8c802be7718..00000000000
--- a/spec/frontend/pipelines/performance_insights_modal_spec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { GlAlert, GlLink, GlModal } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import waitForPromises from 'helpers/wait_for_promises';
-import PerformanceInsightsModal from '~/pipelines/components/performance_insights_modal.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
-import {
- mockPerformanceInsightsResponse,
- mockPerformanceInsightsNextPageResponse,
-} from './graph/mock_data';
-
-Vue.use(VueApollo);
-
-describe('Performance insights modal', () => {
- let wrapper;
-
- const findModal = () => wrapper.findComponent(GlModal);
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findLink = () => wrapper.findComponent(GlLink);
- const findLimitText = () => wrapper.findByTestId('limit-alert-text');
- const findQueuedCardData = () => wrapper.findByTestId('insights-queued-card-data');
- const findQueuedCardLink = () => wrapper.findByTestId('insights-queued-card-link');
- const findExecutedCardData = () => wrapper.findByTestId('insights-executed-card-data');
- const findExecutedCardLink = () => wrapper.findByTestId('insights-executed-card-link');
- const findSlowJobsStage = (index) => wrapper.findAllByTestId('insights-slow-job-stage').at(index);
- const findSlowJobsLink = (index) => wrapper.findAllByTestId('insights-slow-job-link').at(index);
-
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
-
- const getPerformanceInsightsNextPageHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsNextPageResponse);
-
- const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]];
-
- const createComponent = (handlers = requestHandlers) => {
- wrapper = shallowMountExtended(PerformanceInsightsModal, {
- provide: {
- pipelineIid: '1',
- pipelineProjectPath: 'root/ci-project',
- },
- apolloProvider: createMockApollo(handlers),
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('without next page', () => {
- beforeEach(async () => {
- createComponent();
-
- await waitForPromises();
- });
-
- it('displays modal', () => {
- expect(findModal().exists()).toBe(true);
- });
-
- it('displays alert', () => {
- expect(findAlert().exists()).toBe(true);
- });
-
- it('displays feedback issue link', () => {
- expect(findLink().text()).toBe('Feedback issue');
- expect(findLink().attributes('href')).toBe(
- 'https://gitlab.com/gitlab-org/gitlab/-/issues/365902',
- );
- });
-
- it('does not display limit text', () => {
- expect(findLimitText().exists()).toBe(false);
- });
-
- describe('queued duration card', () => {
- it('displays card data', () => {
- expect(trimText(findQueuedCardData().text())).toBe('4.9 days');
- });
- it('displays card link', () => {
- expect(findQueuedCardLink().attributes('href')).toBe(
- '/root/lots-of-jobs-project/-/pipelines/98',
- );
- });
- });
-
- describe('executed duration card', () => {
- it('displays card data', () => {
- expect(trimText(findExecutedCardData().text())).toBe('trigger_job');
- });
- it('displays card link', () => {
- expect(findExecutedCardLink().attributes('href')).toBe(
- '/root/lots-of-jobs-project/-/pipelines/98',
- );
- });
- });
-
- describe('slow jobs', () => {
- it.each`
- index | expectedStage | expectedName | expectedLink
- ${0} | ${'build'} | ${'wait_job'} | ${'/root/ci-project/-/jobs/2493'}
- ${1} | ${'deploy'} | ${'artifact_job'} | ${'/root/ci-project/-/jobs/2501'}
- ${2} | ${'test'} | ${'allow_failure_test_job'} | ${'/root/ci-project/-/jobs/2497'}
- ${3} | ${'build'} | ${'large_log_output'} | ${'/root/ci-project/-/jobs/2495'}
- ${4} | ${'build'} | ${'build_job'} | ${'/root/ci-project/-/jobs/2494'}
- `(
- 'should display slow job correctly',
- ({ index, expectedStage, expectedName, expectedLink }) => {
- expect(findSlowJobsStage(index).text()).toBe(expectedStage);
- expect(findSlowJobsLink(index).text()).toBe(expectedName);
- expect(findSlowJobsLink(index).attributes('href')).toBe(expectedLink);
- },
- );
- });
- });
-
- describe('with next page', () => {
- it('displays limit text when there is a next page', async () => {
- createComponent([[getPerformanceInsights, getPerformanceInsightsNextPageHandler]]);
-
- await waitForPromises();
-
- expect(findLimitText().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/utils_spec.js b/spec/frontend/pipelines/utils_spec.js
index a82390fae22..1c23a7e4fcf 100644
--- a/spec/frontend/pipelines/utils_spec.js
+++ b/spec/frontend/pipelines/utils_spec.js
@@ -8,14 +8,10 @@ import {
removeOrphanNodes,
getMaxNodes,
} from '~/pipelines/components/parsing_utils';
-import { createNodeDict, calculateJobStats, calculateSlowestFiveJobs } from '~/pipelines/utils';
+import { createNodeDict } from '~/pipelines/utils';
import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
-import {
- generateResponse,
- mockPipelineResponse,
- mockPerformanceInsightsResponse,
-} from './graph/mock_data';
+import { generateResponse, mockPipelineResponse } from './graph/mock_data';
describe('DAG visualization parsing utilities', () => {
const nodeDict = createNodeDict(mockParsedGraphQLNodes);
@@ -162,40 +158,4 @@ describe('DAG visualization parsing utilities', () => {
expect(columns).toMatchSnapshot();
});
});
-
- describe('performance insights', () => {
- const {
- data: {
- project: {
- pipeline: { jobs },
- },
- },
- } = mockPerformanceInsightsResponse;
-
- describe('calculateJobStats', () => {
- const expectedJob = jobs.nodes[0];
-
- it('returns the job that spent this longest time queued', () => {
- expect(calculateJobStats(jobs, 'queuedDuration')).toEqual(expectedJob);
- });
-
- it('returns the job that was executed last', () => {
- expect(calculateJobStats(jobs, 'startedAt')).toEqual(expectedJob);
- });
- });
-
- describe('calculateSlowestFiveJobs', () => {
- it('returns the slowest five jobs of the pipeline', () => {
- const expectedJobs = [
- jobs.nodes[9],
- jobs.nodes[1],
- jobs.nodes[5],
- jobs.nodes[7],
- jobs.nodes[8],
- ];
-
- expect(calculateSlowestFiveJobs(jobs)).toEqual(expectedJobs);
- });
- });
- });
});
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index bc9e64282bc..b3dee082d1f 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Types::Ci::JobType do
+ include GraphqlHelpers
+
specify { expect(described_class.graphql_name).to eq('CiJob') }
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Job) }
@@ -45,8 +47,21 @@ RSpec.describe Types::Ci::JobType do
tags
triggered
userPermissions
+ webPath
]
expect(described_class).to have_graphql_fields(*expected_fields)
end
+
+ describe '#web_path' do
+ subject { resolve_field(:web_path, build, current_user: user, object_type: described_class) }
+
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, project: project, user: user) }
+
+ it 'returns the web path of the job' do
+ is_expected.to eq("/#{project.full_path}/-/jobs/#{build.id}")
+ end
+ end
end
diff --git a/spec/graphql/types/deployment_details_type_spec.rb b/spec/graphql/types/deployment_details_type_spec.rb
index 58756798ffb..4cbc4165f70 100644
--- a/spec/graphql/types/deployment_details_type_spec.rb
+++ b/spec/graphql/types/deployment_details_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['DeploymentDetails'] do
it 'has the expected fields' do
expected_fields = %w[
- id iid ref tag sha created_at updated_at finished_at status
+ id iid ref tag sha created_at updated_at finished_at status commit job triggerer
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/deployment_type_spec.rb b/spec/graphql/types/deployment_type_spec.rb
index 21e445c24b2..bf4be0523c6 100644
--- a/spec/graphql/types/deployment_type_spec.rb
+++ b/spec/graphql/types/deployment_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['Deployment'] do
it 'has the expected fields' do
expected_fields = %w[
- id iid ref tag sha created_at updated_at finished_at status
+ id iid ref tag sha created_at updated_at finished_at status commit job triggerer
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index 0304aac18ae..ba6caf7cf17 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -46,6 +46,10 @@ RSpec.describe ApplicationSettingsHelper do
expect(helper.visible_attributes).to include(:deactivate_dormant_users)
end
+ it 'contains :deactivate_dormant_users_period' do
+ expect(helper.visible_attributes).to include(:deactivate_dormant_users_period)
+ end
+
it 'contains rate limit parameters' do
expect(helper.visible_attributes).to include(*%i(
issues_create_limit notes_create_limit project_export_limit
@@ -63,6 +67,10 @@ RSpec.describe ApplicationSettingsHelper do
it 'does not contain :deactivate_dormant_users' do
expect(helper.visible_attributes).not_to include(:deactivate_dormant_users)
end
+
+ it 'does not contain :deactivate_dormant_users_period' do
+ expect(helper.visible_attributes).not_to include(:deactivate_dormant_users_period)
+ end
end
end
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index b0c67cdafe1..690396d4dbc 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -98,6 +98,36 @@ RSpec.describe Gitlab::EncodingHelper do
end
end
+ describe '#encode_utf8_with_escaping!' do
+ where(:input, :expected) do
+ "abcd" | "abcd"
+ "DzDzDz" | "DzDzDz"
+ "\xC7\xB2\xC7DzDzDz" | "Dz%C7DzDzDz"
+ "🐤🐤🐤🐤\xF0\x9F\x90" | "🐤🐤🐤🐤%F0%9F%90"
+ "\xD0\x9F\xD1\x80 \x90" | "Пр %90"
+ "\x41" | "A"
+ end
+
+ with_them do
+ it 'escapes invalid UTF-8' do
+ expect(ext_class.encode_utf8_with_escaping!(input.dup.force_encoding(Encoding::ASCII_8BIT))).to eq(expected)
+ expect(ext_class.encode_utf8_with_escaping!(input)).to eq(expected)
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(escape_gitaly_refs: false)
+ end
+
+ it 'uses #encode! method' do
+ expect(ext_class).to receive(:encode!).with('String')
+
+ ext_class.encode_utf8_with_escaping!('String')
+ end
+ end
+ end
+
describe '#encode_utf8' do
[
["nil", nil, nil],
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index a1fb8b70bd7..b35d1ea019b 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -673,6 +673,61 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#search_files_by_name' do
+ let(:ref) { 'master' }
+
+ subject(:result) { mutable_repository.search_files_by_name(query, ref) }
+
+ context 'when sending a valid name' do
+ let(:query) { 'files/ruby/popen.rb' }
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('files/ruby/popen.rb')
+ end
+ end
+
+ context 'when sending a name with space' do
+ let(:query) { 'file with space.md' }
+
+ before do
+ mutable_repository.multi_action(
+ user,
+ actions: [{ action: :create, file_path: "file with space.md", content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('file with space.md')
+ end
+ end
+
+ context 'when sending a name with special ASCII characters' do
+ let(:file_name) { 'Hello !@#$%^&*()' }
+ let(:query) { file_name }
+
+ before do
+ mutable_repository.multi_action(
+ user,
+ actions: [{ action: :create, file_path: file_name, content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly(file_name)
+ end
+ end
+
+ context 'when sending a non-existing name' do
+ let(:query) { 'please do not exist.md' }
+
+ it 'raises error' do
+ expect(result).to eql([])
+ end
+ end
+ end
+
describe '#find_remote_root_ref' do
it 'gets the remote root ref from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb
index f359679a930..0f6ef55b4b1 100644
--- a/spec/lib/gitlab/git_spec.rb
+++ b/spec/lib/gitlab/git_spec.rb
@@ -7,10 +7,18 @@ RSpec.describe Gitlab::Git do
let(:committer_name) { 'John Doe' }
describe '.ref_name' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5" }
+
it 'ensure ref is a valid UTF-8 string' do
- utf8_invalid_ref = Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5"
+ expect(described_class.ref_name(ref)).to eq("an_invalid_ref_%E5")
+ end
- expect(described_class.ref_name(utf8_invalid_ref)).to eq("an_invalid_ref_å")
+ context 'when ref contains characters \x80 - \xFF' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "\x90" }
+
+ it 'correctly converts it' do
+ expect(described_class.ref_name(ref)).to eq("%90")
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index c0be5b01cef..835199e0e3d 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -819,3 +819,14 @@ service_desk_setting:
approvals:
- user
- merge_request
+resource_milestone_events:
+ - user
+ - issue
+ - merge_request
+ - milestone
+resource_state_events:
+ - user
+ - issue
+ - merge_request
+ - source_merge_request
+ - epic
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index 47d7555c8f4..299e107c881 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -192,10 +192,26 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
expect(Issue.find_by(title: 'Voluptatem').resource_label_events).not_to be_empty
end
+ it 'restores issue resource milestone events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores issue resource state events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_state_events).not_to be_empty
+ end
+
it 'restores merge requests resource label events' do
expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty
end
+ it 'restores merge request resource milestone events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores merge request resource state events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_state_events).not_to be_empty
+ end
+
it 'restores suggestion' do
note = Note.find_by("note LIKE 'Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum%'")
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 6cfc24a8996..ac3997fd3ec 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -917,3 +917,15 @@ Approval:
- user_id
- created_at
- updated_at
+ResourceMilestoneEvent:
+ - user_id
+ - action
+ - state
+ - created_at
+ResourceStateEvent:
+ - user_id
+ - created_at
+ - state
+ - source_commit
+ - close_after_error_tracking_resolve
+ - close_auto_resolve_prometheus_alert
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 4ac8720780c..8517e583ec7 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe Ci::Trigger do
trigger = create(:ci_trigger_without_token, project: project)
expect(trigger.token).not_to be_nil
+ expect(trigger.token).to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX)
end
it 'does not set a random token if one provided' do
@@ -30,12 +31,22 @@ RSpec.describe Ci::Trigger do
end
describe '#short_token' do
- let(:trigger) { create(:ci_trigger, token: '12345678') }
+ let(:trigger) { create(:ci_trigger) }
subject { trigger.short_token }
- it 'returns shortened token' do
- is_expected.to eq('1234')
+ it 'returns shortened token without prefix' do
+ is_expected.not_to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX)
+ end
+
+ context 'token does not have a prefix' do
+ before do
+ trigger.token = '12345678'
+ end
+
+ it 'returns shortened token' do
+ is_expected.to eq('1234')
+ end
end
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index c5ce18739aa..2fdca7114eb 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -897,6 +897,22 @@ RSpec.describe Deployment do
end
end
+ describe '#build' do
+ let!(:deployment) { create(:deployment) }
+
+ subject { deployment.build }
+
+ it 'retrieves build for the deployment' do
+ is_expected.to eq(deployment.deployable)
+ end
+
+ it 'returns nil when the associated build is not found' do
+ deployment.update!(deployable_id: nil, deployable_type: nil)
+
+ is_expected.to be_nil
+ end
+ end
+
describe '#previous_deployment' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 537e680102a..8921b6d208a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -3810,8 +3810,8 @@ RSpec.describe User do
describe '#can_be_deactivated?' do
let(:activity) { {} }
let(:user) { create(:user, name: 'John Smith', **activity) }
- let(:day_within_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.pred.days.ago }
- let(:day_outside_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.next.days.ago }
+ let(:day_within_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago }
+ let(:day_outside_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago }
shared_examples 'not eligible for deactivation' do
it 'returns false' do
@@ -7193,8 +7193,8 @@ RSpec.describe User do
describe '.dormant' do
it 'returns dormant users' do
freeze_time do
- not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date
- too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date
+ not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date
+ too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date
create(:user, :deactivated, last_activity_on: too_long_ago)
@@ -7214,8 +7214,8 @@ RSpec.describe User do
describe '.with_no_activity' do
it 'returns users with no activity' do
freeze_time do
- active_not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date
- active_too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date
+ active_not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date
+ active_too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date
created_recently = (described_class::MINIMUM_DAYS_CREATED - 1).days.ago.to_date
created_not_recently = described_class::MINIMUM_DAYS_CREATED.days.ago.to_date
diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_query_spec.rb
index fbfd9c5d0ac..95cc83e9463 100644
--- a/spec/requests/api/graphql/environments/deployments_query_spec.rb
+++ b/spec/requests/api/graphql/environments/deployments_query_spec.rb
@@ -295,6 +295,147 @@ RSpec.describe 'Environments Deployments query' do
end
end
+ shared_examples_for 'avoids N+1 database queries' do
+ it 'does not increase the query count' do
+ create_deployments
+
+ baseline = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(query, context: { current_user: user })
+ end
+
+ create_deployments
+
+ multi = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(query, context: { current_user: user })
+ end
+
+ expect(multi).not_to exceed_query_limit(baseline)
+ end
+
+ def create_deployments
+ create_list(:deployment, 3, environment: environment, project: project).each do |deployment|
+ deployment.user = create(:user).tap { |u| project.add_developer(u) }
+ deployment.deployable =
+ create(:ci_build, project: project, environment: environment.name, deployment: deployment,
+ user: deployment.user)
+
+ deployment.save!
+ end
+ end
+ end
+
+ context 'when requesting commits of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ commit {
+ author {
+ avatarUrl
+ name
+ webPath
+ }
+ fullTitle
+ webPath
+ sha
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns commits of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.sha).to eq(deployment['commit']['sha'])
+ end
+ end
+ end
+
+ context 'when requesting triggerers of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ triggerer {
+ id
+ avatarUrl
+ name
+ webPath
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns triggerers of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.deployed_by.name).to eq(deployment['triggerer']['name'])
+ end
+ end
+ end
+
+ context 'when requesting jobs of deployments' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{environment.name}") {
+ deployments {
+ nodes {
+ iid
+ job {
+ id
+ status
+ name
+ webPath
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it_behaves_like 'avoids N+1 database queries'
+
+ it 'returns jobs of deployments' do
+ deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
+
+ deployments.each do |deployment|
+ deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
+
+ expect(deployment_in_record.build.to_global_id.to_s).to eq(deployment['job']['id'])
+ end
+ end
+ end
+
describe 'sorting and pagination' do
let(:data_path) { [:project, :environment, :deployments] }
let(:current_user) { user }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 26238a87209..331d8261dcf 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -3238,7 +3238,7 @@ RSpec.describe API::Users do
let(:user) { create(:user, **activity) }
context 'with no recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } }
it 'deactivates an active user' do
deactivate
@@ -3249,13 +3249,13 @@ RSpec.describe API::Users do
end
context 'with recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } }
it 'does not deactivate an active user' do
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
expect(user.reload.state).to eq('active')
end
end
diff --git a/spec/workers/users/deactivate_dormant_users_worker_spec.rb b/spec/workers/users/deactivate_dormant_users_worker_spec.rb
index 263ca31e0a0..a8318de669b 100644
--- a/spec/workers/users/deactivate_dormant_users_worker_spec.rb
+++ b/spec/workers/users/deactivate_dormant_users_worker_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
using RSpec::Parameterized::TableSyntax
describe '#perform' do
- let_it_be(:dormant) { create(:user, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) }
+ let_it_be(:dormant) { create(:user, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) }
let_it_be(:inactive) { create(:user, last_activity_on: nil, created_at: User::MINIMUM_DAYS_CREATED.days.ago.to_date) }
let_it_be(:inactive_recently_created) { create(:user, last_activity_on: nil, created_at: (User::MINIMUM_DAYS_CREATED - 1).days.ago.to_date) }
@@ -14,7 +14,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
it 'does not run for GitLab.com' do
expect(Gitlab).to receive(:com?).and_return(true)
- expect(Gitlab::CurrentSettings).not_to receive(:current_application_settings)
+ # Now makes a call to current settings to determine period of dormancy
worker.perform
@@ -48,7 +48,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
end
with_them do
it 'deactivates certain user types' do
- user = create(:user, user_type: user_type, state: :active, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
+ user = create(:user, user_type: user_type, state: :active, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
worker.perform
@@ -57,8 +57,8 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
end
it 'does not deactivate non-active users' do
- human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
- service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
+ human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
+ service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
worker.perform