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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-26 15:10:53 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-26 15:10:53 +0300
commitff579119e2ecf2608370a1f24c4d791d28f269d9 (patch)
tree6f510ae943600102faf9dbc54df0f3e7f96c3417
parent2c49951e8c1f4fb95d15cac3dd0677d6882d2add (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/diffs/components/app.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue9
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue8
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue127
-rw-r--r--app/assets/javascripts/diffs/store/actions.js6
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js5
-rw-r--r--app/assets/javascripts/diffs/store/utils.js149
-rw-r--r--app/assets/javascripts/vue_shared/components/url_sync.vue4
-rw-r--r--app/assets/stylesheets/components/batch_comments/review_bar.scss122
-rw-r--r--app/controllers/admin/application_settings_controller.rb2
-rw-r--r--app/controllers/admin/services_controller.rb2
-rw-r--r--app/controllers/groups/settings/integrations_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/service.rb10
-rw-r--r--app/services/projects/after_rename_service.rb20
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/delete_diff_files_worker.rb4
-rw-r--r--app/workers/pages_transfer_worker.rb20
-rw-r--r--changelogs/unreleased/240921-fix-merge-request-diff-files-count.yml5
-rw-r--r--changelogs/unreleased/ph-240235-moveReviewCSS.yml5
-rw-r--r--config/feature_flags/development/async_pages_move_project_rename.yml7
-rw-r--r--config/feature_flags/development/unified_diff_lines.yml7
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--db/post_migrate/20200806100713_schedule_populate_resolved_on_default_branch_column.rb29
-rw-r--r--db/schema_migrations/202008061007131
-rw-r--r--doc/development/contributing/index.md5
-rw-r--r--doc/development/testing_guide/best_practices.md24
-rw-r--r--lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb12
-rw-r--r--lib/gitlab/ci/config/entry/job.rb34
-rw-r--r--lib/gitlab/ci/config/entry/jobs.rb10
-rw-r--r--lib/gitlab/ci/config/entry/root.rb2
-rw-r--r--lib/gitlab/ci/features.rb8
-rw-r--r--lib/gitlab/danger/roulette.rb2
-rw-r--r--lib/gitlab/pages_transfer.rb19
-rw-r--r--spec/controllers/projects/ci/lints_controller_spec.rb7
-rw-r--r--spec/features/admin/services/admin_visits_service_templates_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb4
-rw-r--r--spec/lib/gitlab/pages_transfer_spec.rb137
-rw-r--r--spec/models/service_spec.rb8
-rw-r--r--spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb2
-rw-r--r--spec/services/projects/after_rename_service_spec.rb30
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/workers/delete_diff_files_worker_spec.rb6
-rw-r--r--spec/workers/pages_transfer_worker_spec.rb38
50 files changed, 681 insertions, 262 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 3680bebfdd0..9ed27edd71a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -203,7 +203,7 @@ export default {
}
},
diffViewType() {
- if (this.needsReload() || this.needsFirstLoad()) {
+ if (!this.glFeatures.unifiedDiffLines && (this.needsReload() || this.needsFirstLoad())) {
this.refetchDiffData();
}
this.adjustView();
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 087a558efdc..5df82242d2b 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -1,6 +1,7 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
@@ -32,7 +33,7 @@ export default {
userAvatarLink,
DiffFileDrafts,
},
- mixins: [diffLineNoteFormMixin, draftCommentsMixin],
+ mixins: [diffLineNoteFormMixin, draftCommentsMixin, glFeatureFlagsMixin()],
props: {
diffFile: {
type: Object,
@@ -114,7 +115,11 @@ export default {
<inline-diff-view
v-if="isInlineView"
:diff-file="diffFile"
- :diff-lines="diffFile.highlighted_diff_lines || []"
+ :diff-lines="
+ glFeatures.unifiedDiffLines
+ ? diffFile.parallel_diff_lines
+ : diffFile.highlighted_diff_lines || []
+ "
:help-page-path="helpPagePath"
/>
<parallel-diff-view
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
index 9f27758ed27..785c84499f8 100644
--- a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -3,6 +3,7 @@ import { mapState, mapActions } from 'vuex';
import { GlIcon } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import * as utils from '../store/utils';
import tooltip from '../../vue_shared/directives/tooltip';
@@ -28,6 +29,7 @@ export default {
components: {
GlIcon,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
fileHash: {
type: String,
@@ -59,7 +61,11 @@ export default {
},
computed: {
...mapState({
- diffViewType: state => state.diffs.diffViewType,
+ diffViewType(state) {
+ return this.glFeatures.unifiedDiffLines
+ ? PARALLEL_DIFF_VIEW_TYPE
+ : state.diffs.diffViewType;
+ },
diffFiles: state => state.diffs.diffFiles,
}),
canExpandUp() {
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index e82d06ee385..e5197977bba 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -1,5 +1,6 @@
<script>
import { mapGetters, mapState } from 'vuex';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import InlineDraftCommentRow from '~/batch_comments/components/inline_draft_comment_row.vue';
import inlineDiffTableRow from './inline_diff_table_row.vue';
@@ -14,7 +15,7 @@ export default {
InlineDraftCommentRow,
inlineDiffExpansionRow,
},
- mixins: [draftCommentsMixin],
+ mixins: [draftCommentsMixin, glFeatureFlagsMixin()],
props: {
diffFile: {
type: Object,
@@ -63,37 +64,99 @@ export default {
<col />
</colgroup>
<tbody>
- <template v-for="(line, index) in diffLines">
- <inline-diff-expansion-row
- :key="`expand-${index}`"
- :file-hash="diffFile.file_hash"
- :context-lines-path="diffFile.context_lines_path"
- :line="line"
- :is-top="index === 0"
- :is-bottom="index + 1 === diffLinesLength"
- />
- <inline-diff-table-row
- :key="`${line.line_code || index}`"
- :file-hash="diffFile.file_hash"
- :file-path="diffFile.file_path"
- :line="line"
- :is-bottom="index + 1 === diffLinesLength"
- :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
- />
- <inline-diff-comment-row
- :key="`icr-${line.line_code || index}`"
- :diff-file-hash="diffFile.file_hash"
- :line="line"
- :help-page-path="helpPagePath"
- :has-draft="shouldRenderDraftRow(diffFile.file_hash, line) || false"
- />
- <inline-draft-comment-row
- v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
- :key="`draft_${index}`"
- :draft="draftForLine(diffFile.file_hash, line)"
- :diff-file="diffFile"
- :line="line"
- />
+ <template v-if="glFeatures.unifiedDiffLines">
+ <template v-for="({ left, right }, index) in diffLines">
+ <inline-diff-expansion-row
+ :key="`expand-${index}`"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
+ :line="left || right"
+ :is-top="index === 0"
+ :is-bottom="index + 1 === diffLinesLength"
+ />
+ <template v-if="left">
+ <inline-diff-table-row
+ :key="`${left.line_code || index}`"
+ :file-hash="diffFile.file_hash"
+ :file-path="diffFile.file_path"
+ :line="left"
+ :is-bottom="index + 1 === diffLinesLength"
+ :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
+ />
+ <inline-diff-comment-row
+ :key="`icr-${left.line_code || index}`"
+ :diff-file-hash="diffFile.file_hash"
+ :line="left"
+ :help-page-path="helpPagePath"
+ :has-draft="shouldRenderDraftRow(diffFile.file_hash, left) || false"
+ />
+ <inline-draft-comment-row
+ v-if="shouldRenderDraftRow(diffFile.file_hash, left)"
+ :key="`draft_${index}`"
+ :draft="draftForLine(diffFile.file_hash, left)"
+ :diff-file="diffFile"
+ :line="left"
+ />
+ </template>
+ <template v-if="right && right.type === 'new'">
+ <inline-diff-table-row
+ :key="`new-${right.line_code || index}`"
+ :file-hash="diffFile.file_hash"
+ :file-path="diffFile.file_path"
+ :line="right"
+ :is-bottom="index + 1 === diffLinesLength"
+ :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
+ />
+ <inline-diff-comment-row
+ :key="`new-icr-${right.line_code || index}`"
+ :diff-file-hash="diffFile.file_hash"
+ :line="right"
+ :help-page-path="helpPagePath"
+ :has-draft="shouldRenderDraftRow(diffFile.file_hash, right) || false"
+ />
+ <inline-draft-comment-row
+ v-if="shouldRenderDraftRow(diffFile.file_hash, right)"
+ :key="`new-draft_${index}`"
+ :draft="draftForLine(diffFile.file_hash, right)"
+ :diff-file="diffFile"
+ :line="right"
+ />
+ </template>
+ </template>
+ </template>
+ <template v-else>
+ <template v-for="(line, index) in diffLines">
+ <inline-diff-expansion-row
+ :key="`expand-${index}`"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
+ :line="line"
+ :is-top="index === 0"
+ :is-bottom="index + 1 === diffLinesLength"
+ />
+ <inline-diff-table-row
+ :key="`${line.line_code || index}`"
+ :file-hash="diffFile.file_hash"
+ :file-path="diffFile.file_path"
+ :line="line"
+ :is-bottom="index + 1 === diffLinesLength"
+ :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
+ />
+ <inline-diff-comment-row
+ :key="`icr-${line.line_code || index}`"
+ :diff-file-hash="diffFile.file_hash"
+ :line="line"
+ :help-page-path="helpPagePath"
+ :has-draft="shouldRenderDraftRow(diffFile.file_hash, line) || false"
+ />
+ <inline-draft-comment-row
+ v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
+ :key="`draft_${index}`"
+ :draft="draftForLine(diffFile.file_hash, line)"
+ :diff-file="diffFile"
+ :line="line"
+ />
+ </template>
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index d5581474c9b..8e26f5e9ffe 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -74,7 +74,7 @@ export const fetchDiffFiles = ({ state, commit }) => {
let returnData;
if (state.useSingleDiffStyle) {
- urlParams.view = state.diffViewType;
+ urlParams.view = window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType;
}
commit(types.SET_LOADING, true);
@@ -114,7 +114,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
};
if (state.useSingleDiffStyle) {
- urlParams.view = state.diffViewType;
+ urlParams.view = window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType;
}
commit(types.SET_BATCH_LOADING, true);
@@ -178,7 +178,7 @@ export const fetchDiffFilesMeta = ({ commit, state }) => {
const urlParams = {};
if (state.useSingleDiffStyle) {
- urlParams.view = state.diffViewType;
+ urlParams.view = window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType;
}
commit(types.SET_LOADING, true);
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 0d41f1c2178..3b03cce86fd 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -1,4 +1,5 @@
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import {
findDiffFile,
addLineReferences,
@@ -154,7 +155,9 @@ export default {
addContextLines({
inlineLines: diffFile.highlighted_diff_lines,
parallelLines: diffFile.parallel_diff_lines,
- diffViewType: state.diffViewType,
+ diffViewType: window.gon?.features?.unifiedDiffLines
+ ? PARALLEL_DIFF_VIEW_TYPE
+ : state.diffViewType,
contextLines: lines,
bottom,
lineNumbers,
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index e1dc75ad2c7..9286d7fdcb0 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -20,6 +20,77 @@ import {
} from '../constants';
import { prepareRawDiffFile } from '../diff_file';
+export const isAdded = line => ['new', 'new-nonewline'].includes(line.type);
+export const isRemoved = line => ['old', 'old-nonewline'].includes(line.type);
+export const isUnchanged = line => !line.type;
+export const isMeta = line => ['match', 'new-nonewline', 'old-nonewline'].includes(line.type);
+
+/**
+ * Pass in the inline diff lines array which gets converted
+ * to the parallel diff lines.
+ * This allows for us to convert inline diff lines to parallel
+ * on the frontend without needing to send any requests
+ * to the API.
+ *
+ * This method has been taken from the already existing backend
+ * implementation at lib/gitlab/diff/parallel_diff.rb
+ *
+ * @param {Object[]} diffLines - inline diff lines
+ *
+ * @returns {Object[]} parallel lines
+ */
+export const parallelizeDiffLines = (diffLines = []) => {
+ let freeRightIndex = null;
+ const lines = [];
+
+ for (let i = 0, diffLinesLength = diffLines.length, index = 0; i < diffLinesLength; i += 1) {
+ const line = diffLines[i];
+
+ if (isRemoved(line)) {
+ lines.push({
+ [LINE_POSITION_LEFT]: line,
+ [LINE_POSITION_RIGHT]: null,
+ });
+
+ if (freeRightIndex === null) {
+ // Once we come upon a new line it can be put on the right of this old line
+ freeRightIndex = index;
+ }
+ index += 1;
+ } else if (isAdded(line)) {
+ if (freeRightIndex !== null) {
+ // If an old line came before this without a line on the right, this
+ // line can be put to the right of it.
+ lines[freeRightIndex].right = line;
+
+ // If there are any other old lines on the left that don't yet have
+ // a new counterpart on the right, update the free_right_index
+ const nextFreeRightIndex = freeRightIndex + 1;
+ freeRightIndex = nextFreeRightIndex < index ? nextFreeRightIndex : null;
+ } else {
+ lines.push({
+ [LINE_POSITION_LEFT]: null,
+ [LINE_POSITION_RIGHT]: line,
+ });
+
+ freeRightIndex = null;
+ index += 1;
+ }
+ } else if (isMeta(line) || isUnchanged(line)) {
+ // line in the right panel is the same as in the left one
+ lines.push({
+ [LINE_POSITION_LEFT]: line,
+ [LINE_POSITION_RIGHT]: line,
+ });
+
+ freeRightIndex = null;
+ index += 1;
+ }
+ }
+
+ return lines;
+};
+
export function findDiffFile(files, match, matchKey = 'file_hash') {
return files.find(file => file[matchKey] === match);
}
@@ -280,6 +351,13 @@ function mergeTwoFiles(target, source) {
}
function ensureBasicDiffFileLines(file) {
+ if (window.gon?.features?.unifiedDiffLines) {
+ return Object.assign(file, {
+ highlighted_diff_lines: [],
+ parallel_diff_lines: parallelizeDiffLines(file.highlighted_diff_lines || []),
+ });
+ }
+
const missingInline = !file.highlighted_diff_lines;
const missingParallel = !file.parallel_diff_lines;
@@ -717,74 +795,3 @@ export const getDefaultWhitespace = (queryString, cookie) => {
if (cookie === NO_SHOW_WHITESPACE) return false;
return true;
};
-
-export const isAdded = line => ['new', 'new-nonewline'].includes(line.type);
-export const isRemoved = line => ['old', 'old-nonewline'].includes(line.type);
-export const isUnchanged = line => !line.type;
-export const isMeta = line => ['match', 'new-nonewline', 'old-nonewline'].includes(line.type);
-
-/**
- * Pass in the inline diff lines array which gets converted
- * to the parallel diff lines.
- * This allows for us to convert inline diff lines to parallel
- * on the frontend without needing to send any requests
- * to the API.
- *
- * This method has been taken from the already existing backend
- * implementation at lib/gitlab/diff/parallel_diff.rb
- *
- * @param {Object[]} diffLines - inline diff lines
- *
- * @returns {Object[]} parallel lines
- */
-export const parallelizeDiffLines = (diffLines = []) => {
- let freeRightIndex = null;
- const lines = [];
-
- for (let i = 0, diffLinesLength = diffLines.length, index = 0; i < diffLinesLength; i += 1) {
- const line = diffLines[i];
-
- if (isRemoved(line)) {
- lines.push({
- [LINE_POSITION_LEFT]: line,
- [LINE_POSITION_RIGHT]: null,
- });
-
- if (freeRightIndex === null) {
- // Once we come upon a new line it can be put on the right of this old line
- freeRightIndex = index;
- }
- index += 1;
- } else if (isAdded(line)) {
- if (freeRightIndex !== null) {
- // If an old line came before this without a line on the right, this
- // line can be put to the right of it.
- lines[freeRightIndex].right = line;
-
- // If there are any other old lines on the left that don't yet have
- // a new counterpart on the right, update the free_right_index
- const nextFreeRightIndex = freeRightIndex + 1;
- freeRightIndex = nextFreeRightIndex < index ? nextFreeRightIndex : null;
- } else {
- lines.push({
- [LINE_POSITION_LEFT]: null,
- [LINE_POSITION_RIGHT]: line,
- });
-
- freeRightIndex = null;
- index += 1;
- }
- } else if (isMeta(line) || isUnchanged(line)) {
- // line in the right panel is the same as in the left one
- lines.push({
- [LINE_POSITION_LEFT]: line,
- [LINE_POSITION_RIGHT]: line,
- });
-
- freeRightIndex = null;
- index += 1;
- }
- }
-
- return lines;
-};
diff --git a/app/assets/javascripts/vue_shared/components/url_sync.vue b/app/assets/javascripts/vue_shared/components/url_sync.vue
index 389d42f0829..2844d9e9e94 100644
--- a/app/assets/javascripts/vue_shared/components/url_sync.vue
+++ b/app/assets/javascripts/vue_shared/components/url_sync.vue
@@ -1,6 +1,6 @@
<script>
import { historyPushState } from '~/lib/utils/common_utils';
-import { setUrlParams } from '~/lib/utils/url_utility';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
export default {
props: {
@@ -14,7 +14,7 @@ export default {
immediate: true,
deep: true,
handler(newQuery) {
- historyPushState(setUrlParams(newQuery, window.location.href, true));
+ historyPushState(mergeUrlParams(newQuery, window.location.href, { spreadArrays: true }));
},
},
},
diff --git a/app/assets/stylesheets/components/batch_comments/review_bar.scss b/app/assets/stylesheets/components/batch_comments/review_bar.scss
new file mode 100644
index 00000000000..76bf7ac81e8
--- /dev/null
+++ b/app/assets/stylesheets/components/batch_comments/review_bar.scss
@@ -0,0 +1,122 @@
+.review-bar-component {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ background: $white;
+ z-index: 300;
+ padding: 7px 0 6px; // to keep aligned with "collapse sidebar" button on the left sidebar
+ border-top: 1px solid $border-color;
+ padding-left: $contextual-sidebar-width;
+ padding-right: $gutter_collapsed_width;
+ transition: padding $sidebar-transition-duration;
+
+ .page-with-icon-sidebar & {
+ padding-left: $contextual-sidebar-collapsed-width;
+ }
+
+ .right-sidebar-expanded & {
+ padding-right: $gutter_width;
+ }
+
+ @media (max-width: map-get($grid-breakpoints, sm)-1) {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .dropdown {
+ margin-left: $grid-size;
+ }
+}
+
+.review-bar-content {
+ max-width: $limited-layout-width;
+ padding: 0 $gl-padding;
+ width: 100%;
+ margin: 0 auto;
+}
+
+.review-preview-dropdown {
+ .review-preview-item.menu-item {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 8px 16px;
+ cursor: pointer;
+
+ &:not(.is-last) {
+ border-bottom: 1px solid $list-border;
+ }
+ }
+
+ .dropdown-menu {
+ top: auto;
+ bottom: 36px;
+
+ &.show {
+ max-height: 400px;
+
+ @include media-breakpoint-down(xs) {
+ width: calc(100vw - 32px);
+ }
+ }
+ }
+
+ .dropdown-content {
+ max-height: 300px;
+ }
+
+ .dropdown-title {
+ padding: $grid-size 25px $gl-padding;
+ margin-bottom: 0;
+ }
+
+ .dropdown-footer {
+ margin-top: 0;
+ }
+
+ .dropdown-menu-close {
+ top: 6px;
+ }
+}
+
+.review-preview-dropdown-toggle {
+ svg.s16 {
+ width: 15px;
+ height: 15px;
+ margin-top: -1px;
+ top: 3px;
+ margin-left: 4px;
+ }
+}
+
+.review-preview-item-header {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ margin-bottom: 4px;
+
+ > .bold {
+ display: flex;
+ min-width: 0;
+ line-height: 16px;
+ }
+}
+
+.review-preview-item-footer {
+ display: flex;
+ align-items: center;
+ margin-top: 4px;
+}
+
+.review-preview-item-content {
+ width: 100%;
+
+ p {
+ display: block;
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ margin-bottom: 0;
+ }
+}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 7e92940ff30..fc3d0053859 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -32,7 +32,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def integrations
- @integrations = Service.find_or_initialize_all(Service.instances).sort_by(&:title)
+ @integrations = Service.find_or_initialize_all(Service.for_instance).sort_by(&:title)
end
def update
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 1bc82e98ab8..1f4250639c4 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -8,7 +8,7 @@ class Admin::ServicesController < Admin::ApplicationController
def index
@services = Service.find_or_create_templates.sort_by(&:title)
- @existing_instance_types = Service.instances.pluck(:type) # rubocop: disable CodeReuse/ActiveRecord
+ @existing_instance_types = Service.for_instance.pluck(:type) # rubocop: disable CodeReuse/ActiveRecord
end
def edit
diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb
index 844b19a4343..103ab860aac 100644
--- a/app/controllers/groups/settings/integrations_controller.rb
+++ b/app/controllers/groups/settings/integrations_controller.rb
@@ -8,7 +8,7 @@ module Groups
before_action :authorize_admin_group!
def index
- @integrations = Service.find_or_initialize_all(Service.by_group(group)).sort_by(&:title)
+ @integrations = Service.find_or_initialize_all(Service.for_group(group)).sort_by(&:title)
end
private
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index e77d2f0f5ee..43baa849007 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -40,6 +40,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)
push_frontend_feature_flag(:hide_jump_to_next_unresolved_in_threads, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project)
+ push_frontend_feature_flag(:unified_diff_lines, @project)
end
before_action do
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index b70340a98cd..de0dbd295c1 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -150,10 +150,10 @@ class MergeRequestDiff < ApplicationRecord
# All diff information is collected from repository after object is created.
# It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing?
- after_create :set_count_columns
after_create_commit :set_as_latest_diff, unless: :importing?
after_save :update_external_diff_store
+ after_save :set_count_columns
def self.find_by_diff_refs(diff_refs)
find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha)
diff --git a/app/models/project.rb b/app/models/project.rb
index e1b6a9c41dd..f55bb30964a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2533,11 +2533,11 @@ class Project < ApplicationRecord
end
def services_templates
- @services_templates ||= Service.templates
+ @services_templates ||= Service.for_template
end
def services_instances
- @services_instances ||= Service.instances
+ @services_instances ||= Service.for_instance
end
def closest_namespace_setting(name)
diff --git a/app/models/service.rb b/app/models/service.rb
index b877f8b4143..75ad2cdbc83 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -63,9 +63,9 @@ class Service < ApplicationRecord
scope :active, -> { where(active: true) }
scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) }
- scope :by_group, -> (group) { where(group_id: group, type: available_services_types) }
- scope :templates, -> { where(template: true, type: available_services_types) }
- scope :instances, -> { where(instance: true, type: available_services_types) }
+ scope :for_group, -> (group) { where(group_id: group, type: available_services_types) }
+ scope :for_template, -> { where(template: true, type: available_services_types) }
+ scope :for_instance, -> { where(instance: true, type: available_services_types) }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
@@ -291,11 +291,11 @@ class Service < ApplicationRecord
def self.find_or_create_templates
create_nonexistent_templates
- templates
+ for_template
end
private_class_method def self.create_nonexistent_templates
- nonexistent_services = list_nonexistent_services_for(templates)
+ nonexistent_services = list_nonexistent_services_for(for_template)
return if nonexistent_services.empty?
# Create within a transaction to perform the lowest possible SQL queries.
diff --git a/app/services/projects/after_rename_service.rb b/app/services/projects/after_rename_service.rb
index 2a35a07d555..f9022b3afe3 100644
--- a/app/services/projects/after_rename_service.rb
+++ b/app/services/projects/after_rename_service.rb
@@ -96,9 +96,23 @@ module Projects
.rename_project(path_before, project_path, namespace_full_path)
end
- Gitlab::PagesTransfer
- .new
- .rename_project(path_before, project_path, namespace_full_path)
+ if ::Feature.enabled?(:async_pages_move_project_rename, project)
+ # Block will be evaluated in the context of project so we need
+ # to bind to a local variable to capture it, as the instance
+ # variable and method aren't available on Project
+ path_before_local = @path_before
+
+ project.run_after_commit_or_now do
+ Gitlab::PagesTransfer
+ .new
+ .async
+ .rename_project(path_before_local, path, namespace.full_path)
+ end
+ else
+ Gitlab::PagesTransfer
+ .new
+ .rename_project(path_before, project_path, namespace_full_path)
+ end
end
def log_completion
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 57f09fbfc2f..5ce588c9dd8 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1540,6 +1540,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: pages_transfer
+ :feature_category: :pages
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: pages_update_configuration
:feature_category: :pages
:has_external_dependencies:
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index a6759a9d7c4..a31cf650b83 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -12,11 +12,11 @@ class DeleteDiffFilesWorker # rubocop:disable Scalability/IdempotentWorker
return if merge_request_diff.without_files?
MergeRequestDiff.transaction do
- merge_request_diff.clean!
-
MergeRequestDiffFile
.where(merge_request_diff_id: merge_request_diff.id)
.delete_all
+
+ merge_request_diff.clean!
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/workers/pages_transfer_worker.rb b/app/workers/pages_transfer_worker.rb
new file mode 100644
index 00000000000..f78564cc69d
--- /dev/null
+++ b/app/workers/pages_transfer_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class PagesTransferWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ TransferFailedError = Class.new(StandardError)
+
+ feature_category :pages
+ loggable_arguments 0, 1
+
+ def perform(method, args)
+ return unless Gitlab::PagesTransfer::Async::METHODS.include?(method)
+
+ result = Gitlab::PagesTransfer.new.public_send(method, *args) # rubocop:disable GitlabSecurity/PublicSend
+
+ # If result isn't truthy, the move failed. Promote this to an
+ # exception so that it will be logged and retried appropriately
+ raise TransferFailedError unless result
+ end
+end
diff --git a/changelogs/unreleased/240921-fix-merge-request-diff-files-count.yml b/changelogs/unreleased/240921-fix-merge-request-diff-files-count.yml
new file mode 100644
index 00000000000..34193383cdd
--- /dev/null
+++ b/changelogs/unreleased/240921-fix-merge-request-diff-files-count.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect merge request diff file count after deletion
+merge_request: 40384
+author:
+type: fixed
diff --git a/changelogs/unreleased/ph-240235-moveReviewCSS.yml b/changelogs/unreleased/ph-240235-moveReviewCSS.yml
new file mode 100644
index 00000000000..0fca2e71a85
--- /dev/null
+++ b/changelogs/unreleased/ph-240235-moveReviewCSS.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed merge request review styles not loading in FOSS
+merge_request: 40479
+author:
+type: fixed
diff --git a/config/feature_flags/development/async_pages_move_project_rename.yml b/config/feature_flags/development/async_pages_move_project_rename.yml
new file mode 100644
index 00000000000..6f5d30a6420
--- /dev/null
+++ b/config/feature_flags/development/async_pages_move_project_rename.yml
@@ -0,0 +1,7 @@
+---
+name: async_pages_move_project_rename
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40087
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235802
+group: team::Scalability
+type: development
+default_enabled: false
diff --git a/config/feature_flags/development/unified_diff_lines.yml b/config/feature_flags/development/unified_diff_lines.yml
new file mode 100644
index 00000000000..a676f0732dd
--- /dev/null
+++ b/config/feature_flags/development/unified_diff_lines.yml
@@ -0,0 +1,7 @@
+---
+name: unified_diff_lines
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40131
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241188
+group: group::source code
+type: development
+default_enabled: false
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 2e04522c824..a3e98c77746 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -180,6 +180,8 @@
- 1
- - pages_remove
- 1
+- - pages_transfer
+ - 1
- - pages_update_configuration
- 1
- - personal_access_tokens
diff --git a/db/post_migrate/20200806100713_schedule_populate_resolved_on_default_branch_column.rb b/db/post_migrate/20200806100713_schedule_populate_resolved_on_default_branch_column.rb
deleted file mode 100644
index 396b95257e8..00000000000
--- a/db/post_migrate/20200806100713_schedule_populate_resolved_on_default_branch_column.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-class SchedulePopulateResolvedOnDefaultBranchColumn < ActiveRecord::Migration[6.0]
- include Gitlab::Database::MigrationHelpers
-
- DOWNTIME = false
- BATCH_SIZE = 100
- DELAY_INTERVAL = 5.minutes.to_i
- MIGRATION_CLASS = 'PopulateResolvedOnDefaultBranchColumn'
-
- disable_ddl_transaction!
-
- def up
- return unless run_migration?
-
- EE::Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn::Vulnerability.distinct.each_batch(of: BATCH_SIZE, column: :project_id) do |batch, index|
- project_ids = batch.pluck(:project_id)
- migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, project_ids)
- end
- end
-
- def down; end
-
- private
-
- def run_migration?
- Gitlab.ee? && table_exists?(:projects) && table_exists?(:vulnerabilities)
- end
-end
diff --git a/db/schema_migrations/20200806100713 b/db/schema_migrations/20200806100713
deleted file mode 100644
index cdfcfe5e022..00000000000
--- a/db/schema_migrations/20200806100713
+++ /dev/null
@@ -1 +0,0 @@
-fdcce45050f972d8edf2c645022f517ff6b9f4c76767e6cebe45a11fe34dd388 \ No newline at end of file
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index cea9043a333..7e10e152304 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -67,6 +67,11 @@ we credit the original author by adding a changelog entry crediting the author
and optionally include the original author on at least one of the commits
within the MR.
+## Closing policy for inactive bugs
+
+GitLab values the time spent by contributors on reporting bugs. However, if a bug remains inactive for a very long period,
+it will qualify for auto-closure. Please refer to the [auto-close inactive bugs](https://about.gitlab.com/handbook/engineering/quality/triage-operations/#auto-close-inactive-bugs) section in our handbook to understand the complete workflow.
+
## Helping others
Help other GitLab users when you can.
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index a4b51d191eb..8a1ee74a7fc 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -57,14 +57,24 @@ bundle exec guard
When using spring and guard together, use `SPRING=1 bundle exec guard` instead to make use of spring.
-Use [Factory Doctor](https://test-prof.evilmartians.io/#/profilers/factory_doctor) to find cases on un-necessary database manipulation, which can cause slow tests.
+Use [Factory Doctor](https://test-prof.evilmartians.io/#/profilers/factory_doctor) to find cases where database persistence is not needed in a given test.
```shell
# run test for path
FDOC=1 bin/rspec spec/[path]/[to]/[spec].rb
```
-[Factory Profiler](https://test-prof.evilmartians.io/#/profilers/factory_prof) can help to identify unnecessary factory creation.
+A common change is to use `build` instead of `create`:
+
+```ruby
+# Old
+let(:project) { create(:project) }
+
+# New
+let(:project) { build(:project) }
+```
+
+[Factory Profiler](https://test-prof.evilmartians.io/#/profilers/factory_prof) can help to identify repetitive database persistance via factories.
```shell
# run test for path
@@ -74,14 +84,14 @@ FPROF=1 bin/rspec spec/[path]/[to]/[spec].rb
FPROF=flamegraph bin/rspec spec/[path]/[to]/[spec].rb
```
-A common change is to use [`let_it_be`](#common-test-setup).
+A common change is to use [`let_it_be`](#common-test-setup):
```ruby
- # Old
- let(:project) { create(:project) }
+# Old
+let(:project) { create(:project) }
- # New
- let_it_be(:project) { create(:project) }
+# New
+let_it_be(:project) { create(:project) }
```
### General guidelines
diff --git a/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb b/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
deleted file mode 100644
index eb72ef1de33..00000000000
--- a/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module BackgroundMigration
- # rubocop:disable Style/Documentation
- class PopulateResolvedOnDefaultBranchColumn
- def perform(*); end
- end
- end
-end
-
-Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn')
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index f960cec1f26..ecc2c5cb729 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -122,39 +122,9 @@ module Gitlab
:needs, :retry, :parallel, :start_in,
:interruptible, :timeout, :resource_group, :release
- Matcher = Struct.new(:name, :config) do
- def applies?
- job_is_not_hidden? &&
- config_is_a_hash? &&
- has_job_keys?
- end
-
- private
-
- def job_is_not_hidden?
- !name.to_s.start_with?('.')
- end
-
- def config_is_a_hash?
- config.is_a?(Hash)
- end
-
- def has_job_keys?
- if name == :default
- config.key?(:script)
- else
- (ALLOWED_KEYS & config.keys).any?
- end
- end
- end
-
def self.matching?(name, config)
- if Gitlab::Ci::Features.job_entry_matches_all_keys?
- Matcher.new(name, config).applies?
- else
- !name.to_s.start_with?('.') &&
- config.is_a?(Hash) && config.key?(:script)
- end
+ !name.to_s.start_with?('.') &&
+ config.is_a?(Hash) && config.key?(:script)
end
def self.visible?
diff --git a/lib/gitlab/ci/config/entry/jobs.rb b/lib/gitlab/ci/config/entry/jobs.rb
index 1d3036189b0..b5ce42969a5 100644
--- a/lib/gitlab/ci/config/entry/jobs.rb
+++ b/lib/gitlab/ci/config/entry/jobs.rb
@@ -14,8 +14,8 @@ module Gitlab
validates :config, type: Hash
validate do
- unless has_valid_jobs?
- errors.add(:config, 'should contain valid jobs')
+ each_unmatched_job do |name|
+ errors.add(name, 'config should implement a script: or a trigger: keyword')
end
unless has_visible_job?
@@ -23,9 +23,9 @@ module Gitlab
end
end
- def has_valid_jobs?
- config.all? do |name, value|
- Jobs.find_type(name, value)
+ def each_unmatched_job
+ config.each do |name, value|
+ yield(name) unless Jobs.find_type(name, value)
end
end
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index 19d6a470941..2d93f1ab06e 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -134,7 +134,7 @@ module Gitlab
@jobs_config = @config
.except(*self.class.reserved_nodes_names)
.select do |name, config|
- Entry::Jobs.find_type(name, config).present?
+ Entry::Jobs.find_type(name, config).present? || ALLOWED_KEYS.exclude?(name)
end
@config = @config.except(*@jobs_config.keys)
diff --git a/lib/gitlab/ci/features.rb b/lib/gitlab/ci/features.rb
index ca3a927f5a3..386788c751f 100644
--- a/lib/gitlab/ci/features.rb
+++ b/lib/gitlab/ci/features.rb
@@ -64,10 +64,6 @@ module Gitlab
::Feature.enabled?(:ci_plan_needs_size_limit, project, default_enabled: true)
end
- def self.job_entry_matches_all_keys?
- ::Feature.enabled?(:ci_job_entry_matches_all_keys)
- end
-
def self.lint_creates_pipeline_with_dry_run?(project)
::Feature.enabled?(:ci_lint_creates_pipeline_with_dry_run, project, default_enabled: true)
end
@@ -76,10 +72,6 @@ module Gitlab
::Feature.enabled?(:reset_ci_minutes_for_all_namespaces, default_enabled: false)
end
- def self.expand_names_for_cross_pipeline_artifacts?(project)
- ::Feature.enabled?(:ci_expand_names_for_cross_pipeline_artifacts, project)
- end
-
def self.project_transactionless_destroy?(project)
Feature.enabled?(:project_transactionless_destroy, project, default_enabled: false)
end
diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb
index 2e6181d1cab..a6866868e6c 100644
--- a/lib/gitlab/danger/roulette.rb
+++ b/lib/gitlab/danger/roulette.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative 'teammate'
-require_relative 'request_helper'
+require_relative 'request_helper' unless defined?(Gitlab::Danger::RequestHelper)
module Gitlab
module Danger
diff --git a/lib/gitlab/pages_transfer.rb b/lib/gitlab/pages_transfer.rb
index a70dc826f97..8ae0ec5a78a 100644
--- a/lib/gitlab/pages_transfer.rb
+++ b/lib/gitlab/pages_transfer.rb
@@ -1,7 +1,26 @@
# frozen_string_literal: true
+# To make a call happen in a new Sidekiq job, add `.async` before the call. For
+# instance:
+#
+# PagesTransfer.new.async.move_namespace(...)
+#
module Gitlab
class PagesTransfer < ProjectTransfer
+ class Async
+ METHODS = %w[move_namespace move_project rename_project rename_namespace].freeze
+
+ METHODS.each do |meth|
+ define_method meth do |*args|
+ PagesTransferWorker.perform_async(meth, args)
+ end
+ end
+ end
+
+ def async
+ @async ||= Async.new
+ end
+
def root_dir
Gitlab.config.pages.path
end
diff --git a/spec/controllers/projects/ci/lints_controller_spec.rb b/spec/controllers/projects/ci/lints_controller_spec.rb
index 7c10b386169..20025c9b045 100644
--- a/spec/controllers/projects/ci/lints_controller_spec.rb
+++ b/spec/controllers/projects/ci/lints_controller_spec.rb
@@ -150,7 +150,10 @@ RSpec.describe Projects::Ci::LintsController do
it 'assigns result with errors' do
subject
- expect(assigns[:result].errors).to eq(['root config contains unknown keys: rubocop'])
+ expect(assigns[:result].errors).to match_array([
+ 'jobs rubocop config should implement a script: or a trigger: keyword',
+ 'jobs config should contain at least one visible job'
+ ])
end
context 'with dry_run mode' do
@@ -159,7 +162,7 @@ RSpec.describe Projects::Ci::LintsController do
it 'assigns result with errors' do
subject
- expect(assigns[:result].errors).to eq(['root config contains unknown keys: rubocop'])
+ expect(assigns[:result].errors).to eq(['jobs rubocop config should implement a script: or a trigger: keyword'])
end
end
end
diff --git a/spec/features/admin/services/admin_visits_service_templates_spec.rb b/spec/features/admin/services/admin_visits_service_templates_spec.rb
index 8e02538ece0..a37e57304aa 100644
--- a/spec/features/admin/services/admin_visits_service_templates_spec.rb
+++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'Admin visits service templates' do
let(:admin) { create(:user, :admin) }
- let(:slack_service) { Service.templates.find { |s| s.type == 'SlackService' } }
+ let(:slack_service) { Service.for_template.find { |s| s.type == 'SlackService' } }
before do
sign_in(admin)
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index ca02eaee0a0..ab760b107f8 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -74,16 +74,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
it { is_expected.to be_falsey }
end
- context 'when config does not contain script' do
- let(:name) { :build }
-
- let(:config) do
- { before_script: "cd ${PROJ_DIR} " }
- end
-
- it { is_expected.to be_truthy }
- end
-
context 'when using the default job without script' do
let(:name) { :default }
let(:config) do
@@ -104,14 +94,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
it { is_expected.to be_truthy }
end
-
- context 'there are no shared keys between jobs and bridges' do
- subject(:shared_values) do
- described_class::ALLOWED_KEYS & Gitlab::Ci::Config::Entry::Bridge::ALLOWED_KEYS
- end
-
- it { is_expected.to be_empty }
- end
end
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 8561bd330b7..ac6b589ec6b 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Jobs do
let(:config) { { rspec: nil } }
it 'reports error' do
- expect(entry.errors).to include "jobs config should contain valid jobs"
+ expect(entry.errors).to include 'jobs rspec config should implement a script: or a trigger: keyword'
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 140b3c4f55b..252bda6461d 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -344,9 +344,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
describe '#errors' do
- it 'reports errors about missing script' do
+ it 'reports errors about missing script or trigger' do
expect(root.errors)
- .to include "root config contains unknown keys: rspec"
+ .to include 'jobs rspec config should implement a script: or a trigger: keyword'
end
end
end
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 03bb76676e0..a895ac45bc6 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe Gitlab::Ci::Lint do
it 'returns a result with errors' do
expect(subject).not_to be_valid
- expect(subject.errors).to include(/root config contains unknown keys/)
+ expect(subject.errors).to include(/jobs build config should implement a script: or a trigger: keyword/)
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 99e6bdc6625..6e8652b3857 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -2409,7 +2409,7 @@ module Gitlab
context 'returns error if job configuration is invalid' do
let(:config) { YAML.dump({ extra: "bundle update" }) }
- it_behaves_like 'returns errors', 'root config contains unknown keys: extra'
+ it_behaves_like 'returns errors', 'jobs extra config should implement a script: or a trigger: keyword'
end
context 'returns errors if services configuration is not correct' do
@@ -2427,7 +2427,7 @@ module Gitlab
context 'returns errors if the job script is not defined' do
let(:config) { YAML.dump({ rspec: { before_script: "test" } }) }
- it_behaves_like 'returns errors', "jobs:rspec script can't be blank"
+ it_behaves_like 'returns errors', 'jobs rspec config should implement a script: or a trigger: keyword'
end
context 'returns errors if there are no visible jobs defined' do
diff --git a/spec/lib/gitlab/pages_transfer_spec.rb b/spec/lib/gitlab/pages_transfer_spec.rb
new file mode 100644
index 00000000000..4f0ee76b244
--- /dev/null
+++ b/spec/lib/gitlab/pages_transfer_spec.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::PagesTransfer do
+ describe '#async' do
+ let(:async) { subject.async }
+
+ context 'when receiving an allowed method' do
+ it 'schedules a PagesTransferWorker', :aggregate_failures do
+ described_class::Async::METHODS.each do |meth|
+ expect(PagesTransferWorker)
+ .to receive(:perform_async).with(meth, %w[foo bar])
+
+ async.public_send(meth, 'foo', 'bar')
+ end
+ end
+ end
+
+ context 'when receiving a private method' do
+ it 'raises NoMethodError' do
+ expect { async.move('foo', 'bar') }.to raise_error(NoMethodError)
+ end
+ end
+
+ context 'when receiving a non-existent method' do
+ it 'raises NoMethodError' do
+ expect { async.foo('bar') }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ RSpec.shared_examples 'moving a pages directory' do |parameter|
+ let!(:pages_path_before) { project.pages_path }
+ let(:config_path_before) { File.join(pages_path_before, 'config.json') }
+ let(:pages_path_after) { project.reload.pages_path }
+ let(:config_path_after) { File.join(pages_path_after, 'config.json') }
+
+ before do
+ FileUtils.mkdir_p(pages_path_before)
+ FileUtils.touch(config_path_before)
+ end
+
+ after do
+ FileUtils.remove_entry(pages_path_before, true)
+ FileUtils.remove_entry(pages_path_after, true)
+ end
+
+ it 'moves the directory' do
+ subject.public_send(meth, *args)
+
+ expect(File.exist?(config_path_before)).to be(false)
+ expect(File.exist?(config_path_after)).to be(true)
+ end
+
+ it 'returns false if it fails to move the directory' do
+ # Move the directory once, so it can't be moved again
+ subject.public_send(meth, *args)
+
+ expect(subject.public_send(meth, *args)).to be(false)
+ end
+ end
+
+ describe '#move_namespace' do
+ # Can't use let_it_be because we change the path
+ let(:group_1) { create(:group) }
+ let(:group_2) { create(:group) }
+ let(:subgroup) { create(:group, parent: group_1) }
+ let(:project) { create(:project, group: subgroup) }
+ let(:new_path) { "#{group_2.path}/#{subgroup.path}" }
+ let(:meth) { 'move_namespace' }
+
+ # Store the path before we change it
+ let!(:args) { [project.path, subgroup.full_path, new_path] }
+
+ before do
+ # We need to skip hooks, otherwise the directory will be moved
+ # via an ActiveRecord callback
+ subgroup.update_columns(parent_id: group_2.id)
+ subgroup.route.update!(path: new_path)
+ end
+
+ include_examples 'moving a pages directory'
+ end
+
+ describe '#move_project' do
+ # Can't use let_it_be because we change the path
+ let(:group_1) { create(:group) }
+ let(:group_2) { create(:group) }
+ let(:project) { create(:project, group: group_1) }
+ let(:new_path) { group_2.path }
+ let(:meth) { 'move_project' }
+ let(:args) { [project.path, group_1.full_path, group_2.full_path] }
+
+ include_examples 'moving a pages directory' do
+ before do
+ project.update!(group: group_2)
+ end
+ end
+ end
+
+ describe '#rename_project' do
+ # Can't use let_it_be because we change the path
+ let(:project) { create(:project) }
+ let(:new_path) { project.path.succ }
+ let(:meth) { 'rename_project' }
+
+ # Store the path before we change it
+ let!(:args) { [project.path, new_path, project.namespace.full_path] }
+
+ include_examples 'moving a pages directory' do
+ before do
+ project.update!(path: new_path)
+ end
+ end
+ end
+
+ describe '#rename_namespace' do
+ # Can't use let_it_be because we change the path
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let(:new_path) { project.namespace.full_path.succ }
+ let(:meth) { 'rename_namespace' }
+
+ # Store the path before we change it
+ let!(:args) { [project.namespace.full_path, new_path] }
+
+ before do
+ # We need to skip hooks, otherwise the directory will be moved
+ # via an ActiveRecord callback
+ group.update_columns(path: new_path)
+ group.route.update!(path: new_path)
+ end
+
+ include_examples 'moving a pages directory'
+ end
+end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 6dddcb0eca4..0c356fc5118 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -92,12 +92,12 @@ RSpec.describe Service do
end
end
- describe '.by_group' do
+ describe '.for_group' do
let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) }
let!(:service2) { create(:jira_service) }
it 'returns the right group service' do
- expect(described_class.by_group(group)).to match_array([service1])
+ expect(described_class.for_group(group)).to match_array([service1])
end
end
@@ -215,11 +215,11 @@ RSpec.describe Service do
describe '.find_or_initialize_all' do
shared_examples 'service instances' do
it 'returns the available service instances' do
- expect(Service.find_or_initialize_all(Service.instances).pluck(:type)).to match_array(Service.available_services_types)
+ expect(Service.find_or_initialize_all(Service.for_instance).pluck(:type)).to match_array(Service.available_services_types)
end
it 'does not create service instances' do
- expect { Service.find_or_initialize_all(Service.instances) }.not_to change { Service.count }
+ expect { Service.find_or_initialize_all(Service.for_instance) }.not_to change { Service.count }
end
end
diff --git a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
index 3be5ac1f739..b5b3832ac00 100644
--- a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
+++ b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
@@ -101,7 +101,7 @@ RSpec.describe Ci::CreatePipelineService do
end
it 'contains only errors' do
- error_message = 'root config contains unknown keys: invalid'
+ error_message = 'jobs invalid config should implement a script: or a trigger: keyword'
expect(pipeline.yaml_errors).to eq(error_message)
expect(pipeline.error_messages.map(&:content)).to contain_exactly(error_message)
expect(pipeline.errors.full_messages).to contain_exactly(error_message)
diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb
index 52136b37c66..11e3604c38a 100644
--- a/spec/services/projects/after_rename_service_spec.rb
+++ b/spec/services/projects/after_rename_service_spec.rb
@@ -62,11 +62,21 @@ RSpec.describe Projects::AfterRenameService do
context 'gitlab pages' do
before do
- expect(project_storage).to receive(:rename_repo) { true }
+ allow(project_storage).to receive(:rename_repo) { true }
+ end
+
+ context 'when async_pages_move_project_rename is disabled' do
+ it 'moves pages folder to new location' do
+ stub_feature_flags(async_pages_move_project_rename: false)
+
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ service_execute
+ end
end
- it 'moves pages folder to new location' do
- expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+ it 'schedules a move of the pages directory' do
+ expect(PagesTransferWorker).to receive(:perform_async).with('rename_project', anything)
service_execute
end
@@ -160,8 +170,18 @@ RSpec.describe Projects::AfterRenameService do
end
context 'gitlab pages' do
- it 'moves pages folder to new location' do
- expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+ context 'when async_pages_move_project_rename is disabled' do
+ it 'moves pages folder to new location' do
+ stub_feature_flags(async_pages_move_project_rename: false)
+
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ service_execute
+ end
+ end
+
+ it 'schedules a move of the pages directory' do
+ expect(PagesTransferWorker).to receive(:perform_async).with('rename_project', anything)
service_execute
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e7c0a4a43ed..adf21c1c965 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -194,6 +194,10 @@ RSpec.configure do |config|
stub_feature_flags(vue_issuable_sidebar: false)
stub_feature_flags(vue_issuable_epic_sidebar: false)
+ # The following can be removed once we are confident the
+ # unified diff lines works as expected
+ stub_feature_flags(unified_diff_lines: false)
+
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
diff --git a/spec/workers/delete_diff_files_worker_spec.rb b/spec/workers/delete_diff_files_worker_spec.rb
index b3b01588e0b..cf26dbabb97 100644
--- a/spec/workers/delete_diff_files_worker_spec.rb
+++ b/spec/workers/delete_diff_files_worker_spec.rb
@@ -19,6 +19,12 @@ RSpec.describe DeleteDiffFilesWorker do
.from('collected').to('without_files')
end
+ it 'resets the files_count of the diff' do
+ expect { described_class.new.perform(merge_request_diff.id) }
+ .to change { merge_request_diff.reload.files_count }
+ .from(20).to(0)
+ end
+
it 'does nothing if diff was already marked as "without_files"' do
merge_request_diff.clean!
diff --git a/spec/workers/pages_transfer_worker_spec.rb b/spec/workers/pages_transfer_worker_spec.rb
new file mode 100644
index 00000000000..248a3713bf6
--- /dev/null
+++ b/spec/workers/pages_transfer_worker_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PagesTransferWorker do
+ describe '#perform' do
+ Gitlab::PagesTransfer::Async::METHODS.each do |meth|
+ context "when method is #{meth}" do
+ let(:args) { [1, 2, 3] }
+
+ it 'calls the service with the given arguments' do
+ expect_next_instance_of(Gitlab::PagesTransfer) do |service|
+ expect(service).to receive(meth).with(*args).and_return(true)
+ end
+
+ subject.perform(meth, args)
+ end
+
+ it 'raises an error when the service returns false' do
+ expect_next_instance_of(Gitlab::PagesTransfer) do |service|
+ expect(service).to receive(meth).with(*args).and_return(false)
+ end
+
+ expect { subject.perform(meth, args) }
+ .to raise_error(described_class::TransferFailedError)
+ end
+ end
+ end
+
+ describe 'when method is not allowed' do
+ it 'does nothing' do
+ expect(Gitlab::PagesTransfer).not_to receive(:new)
+
+ subject.perform('object_id', [])
+ end
+ end
+ end
+end