diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-25 06:10:51 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-25 06:10:51 +0000 |
commit | c1ccb69fc9b1f07a00d3310f5fbd2e4622db9482 (patch) | |
tree | 1b8edbc6540c554c4d9878fa3e8b7c4f2102baea | |
parent | e66e16c73cda415ccd03ac0a1818a58ddc4429d7 (diff) | |
download | gitlab-ce-c1ccb69fc9b1f07a00d3310f5fbd2e4622db9482.tar.gz |
Add latest changes from gitlab-org/gitlab@master
38 files changed, 570 insertions, 375 deletions
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index bda203f30ac..ac05540aad7 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -213,10 +213,6 @@ linters: - 'app/views/projects/mattermosts/new.html.haml' - 'app/views/projects/merge_requests/_commits.html.haml' - 'app/views/projects/merge_requests/_mr_title.html.haml' - - 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml' - - 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml' - - 'app/views/projects/merge_requests/conflicts/_submit_form.html.haml' - - 'app/views/projects/merge_requests/conflicts/show.html.haml' - 'app/views/projects/merge_requests/creations/_diffs.html.haml' - 'app/views/projects/merge_requests/creations/_new_compare.html.haml' - 'app/views/projects/merge_requests/creations/_new_submit.html.haml' diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index a0fee1b064b..408a414ea16 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -1,11 +1,11 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import IssueCardInner from './issue_card_inner.vue'; +import BoardCardInner from './board_card_inner.vue'; export default { name: 'BoardCard', components: { - IssueCardInner, + BoardCardInner, }, props: { list: { @@ -76,6 +76,6 @@ export default { class="board-card gl-p-5 gl-rounded-base" @mouseup="toggleIssue($event)" > - <issue-card-inner :list="list" :issue="issue" :update-filters="true" /> + <board-card-inner :list="list" :item="issue" :update-filters="true" /> </li> </template> diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index e5ea30df767..ae1a82b9c4a 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -2,7 +2,7 @@ import { GlLabel, GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { sortBy } from 'lodash'; import { mapActions, mapState } from 'vuex'; -import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner'; +import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner'; import { isScopedLabel } from '~/lib/utils/common_utils'; import { updateHistory } from '~/lib/utils/url_utility'; import { sprintf, __, n__ } from '~/locale'; @@ -26,10 +26,10 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - mixins: [issueCardInner], + mixins: [boardCardInner], inject: ['groupId', 'rootPath', 'scopedLabelsAvailable'], props: { - issue: { + item: { type: Object, required: true, }, @@ -52,19 +52,19 @@ export default { }; }, computed: { - ...mapState(['isShowingLabels']), + ...mapState(['isShowingLabels', 'isEpicBoard']), cappedAssignees() { // e.g. maxRender is 4, // Render up to all 4 assignees if there are only 4 assigness // Otherwise render up to the limitBeforeCounter - if (this.issue.assignees.length <= this.maxRender) { - return this.issue.assignees.slice(0, this.maxRender); + if (this.item.assignees.length <= this.maxRender) { + return this.item.assignees.slice(0, this.maxRender); } - return this.issue.assignees.slice(0, this.limitBeforeCounter); + return this.item.assignees.slice(0, this.limitBeforeCounter); }, numberOverLimit() { - return this.issue.assignees.length - this.limitBeforeCounter; + return this.item.assignees.length - this.limitBeforeCounter; }, assigneeCounterTooltip() { const { numberOverLimit, maxCounter } = this; @@ -79,31 +79,35 @@ export default { return `+${this.numberOverLimit}`; }, shouldRenderCounter() { - if (this.issue.assignees.length <= this.maxRender) { + if (this.item.assignees.length <= this.maxRender) { return false; } - return this.issue.assignees.length > this.numberOverLimit; + return this.item.assignees.length > this.numberOverLimit; }, - issueId() { - if (this.issue.iid) { - return `#${this.issue.iid}`; + itemPrefix() { + return this.isEpicBoard ? '&' : '#'; + }, + + itemId() { + if (this.item.iid) { + return `${this.itemPrefix}${this.item.iid}`; } return false; }, showLabelFooter() { - return this.isShowingLabels && this.issue.labels.find(this.showLabel); + return this.isShowingLabels && this.item.labels.find(this.showLabel); }, - issueReferencePath() { - const { referencePath, groupId } = this.issue; - return !groupId ? referencePath.split('#')[0] : null; + itemReferencePath() { + const { referencePath } = this.item; + return referencePath.split(this.itemPrefix)[0]; }, orderedLabels() { - return sortBy(this.issue.labels.filter(this.isNonListLabel), 'title'); + return sortBy(this.item.labels.filter(this.isNonListLabel), 'title'); }, blockedLabel() { - if (this.issue.blockedByCount) { - return n__(`Blocked by %d issue`, `Blocked by %d issues`, this.issue.blockedByCount); + if (this.item.blockedByCount) { + return n__(`Blocked by %d issue`, `Blocked by %d issues`, this.item.blockedByCount); } return __('Blocked issue'); }, @@ -160,7 +164,7 @@ export default { <div class="gl-display-flex" dir="auto"> <h4 class="board-card-title gl-mb-0 gl-mt-0"> <gl-icon - v-if="issue.blocked" + v-if="item.blocked" v-gl-tooltip name="issue-block" :title="blockedLabel" @@ -169,7 +173,7 @@ export default { data-testid="issue-blocked-icon" /> <gl-icon - v-if="issue.confidential" + v-if="item.confidential" v-gl-tooltip name="eye-slash" :title="__('Confidential')" @@ -177,11 +181,11 @@ export default { :aria-label="__('Confidential')" /> <a - :href="issue.path || issue.webUrl || ''" - :title="issue.title" + :href="item.path || item.webUrl || ''" + :title="item.title" class="js-no-trigger" @mousemove.stop - >{{ issue.title }}</a + >{{ item.title }}</a > </h4> </div> @@ -205,29 +209,30 @@ export default { class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden js-board-card-number-container" > <span - v-if="issue.referencePath" + v-if="item.referencePath" class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3" + :class="{ 'gl-font-base': isEpicBoard }" > <tooltip-on-truncate - v-if="issueReferencePath" - :title="issueReferencePath" + v-if="itemReferencePath" + :title="itemReferencePath" placement="bottom" - class="board-issue-path gl-text-truncate gl-font-weight-bold" - >{{ issueReferencePath }}</tooltip-on-truncate + class="board-item-path gl-text-truncate gl-font-weight-bold" + >{{ itemReferencePath }}</tooltip-on-truncate > - #{{ issue.iid }} + {{ itemId }} </span> <span class="board-info-items gl-mt-3 gl-display-inline-block"> <issue-due-date - v-if="issue.dueDate" - :date="issue.dueDate" - :closed="issue.closed || Boolean(issue.closedAt)" + v-if="item.dueDate" + :date="item.dueDate" + :closed="item.closed || Boolean(item.closedAt)" /> - <issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" /> + <issue-time-estimate v-if="item.timeEstimate" :estimate="item.timeEstimate" /> <issue-card-weight - v-if="validIssueWeight" - :weight="issue.weight" - @click="filterByWeight(issue.weight)" + v-if="validIssueWeight(item)" + :weight="item.weight" + @click="filterByWeight(item.weight)" /> </span> </div> diff --git a/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue b/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue index f9a726134a3..3381e4c3a7d 100644 --- a/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue +++ b/app/assets/javascripts/boards/components/board_card_layout_deprecated.vue @@ -3,13 +3,12 @@ import { mapActions, mapGetters } from 'vuex'; import { ISSUABLE } from '~/boards/constants'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import boardsStore from '../stores/boards_store'; -import IssueCardInner from './issue_card_inner.vue'; import IssueCardInnerDeprecated from './issue_card_inner_deprecated.vue'; export default { name: 'BoardCardLayout', components: { - IssueCardInner: gon.features?.graphqlBoardLists ? IssueCardInner : IssueCardInnerDeprecated, + IssueCardInner: IssueCardInnerDeprecated, }, mixins: [glFeatureFlagMixin()], props: { diff --git a/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue b/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue index 069cc2cda22..2652fac1818 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue @@ -2,7 +2,7 @@ import { GlLabel, GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { sortBy } from 'lodash'; import { mapState } from 'vuex'; -import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner'; +import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner'; import { isScopedLabel } from '~/lib/utils/common_utils'; import { sprintf, __, n__ } from '~/locale'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; @@ -24,7 +24,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - mixins: [issueCardInner], + mixins: [boardCardInner], inject: ['groupId', 'rootPath'], props: { issue: { @@ -207,7 +207,7 @@ export default { /> <issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" /> <issue-card-weight - v-if="validIssueWeight" + v-if="validIssueWeight(issue)" :weight="issue.weight" @click="filterByWeight(issue.weight)" /> diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue index bf69f8140d5..e66cae0ce18 100644 --- a/app/assets/javascripts/boards/components/modal/list.vue +++ b/app/assets/javascripts/boards/components/modal/list.vue @@ -2,11 +2,11 @@ import { GlIcon } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import ModalStore from '../../stores/modal_store'; -import IssueCardInner from '../issue_card_inner.vue'; +import BoardCardInner from '../board_card_inner.vue'; export default { components: { - IssueCardInner, + BoardCardInner, GlIcon, }, props: { @@ -126,7 +126,7 @@ export default { class="board-card position-relative p-3 rounded" @click="toggleIssue($event, issue)" > - <issue-card-inner :issue="issue" /> + <board-card-inner :item="issue" /> <gl-icon v-if="issue.selected" :aria-label="'Issue #' + issue.id + ' selected'" diff --git a/app/assets/javascripts/boards/mixins/issue_card_inner.js b/app/assets/javascripts/boards/mixins/board_card_inner.js index 04e971b756d..a6f278f3bc9 100644 --- a/app/assets/javascripts/boards/mixins/issue_card_inner.js +++ b/app/assets/javascripts/boards/mixins/board_card_inner.js @@ -1,10 +1,8 @@ export default { - computed: { + methods: { validIssueWeight() { return false; }, - }, - methods: { filterByWeight() {}, }, }; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue b/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue new file mode 100644 index 00000000000..16a7cfb2ba8 --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue @@ -0,0 +1,217 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { __ } from '~/locale'; +import FileIcon from '~/vue_shared/components/file_icon.vue'; +import DiffFileEditor from './components/diff_file_editor.vue'; +import InlineConflictLines from './components/inline_conflict_lines.vue'; +import ParallelConflictLines from './components/parallel_conflict_lines.vue'; + +/** + * NOTE: Most of this component is directly using $root, rather than props or a better data store. + * This is BAD and one shouldn't copy that behavior. Similarly a lot of the classes below should + * be replaced with GitLab UI components. + * + * We are just doing it temporarily in order to migrate the template from HAML => Vue in an iterative manner + * and are going to clean it up as part of: + * + * https://gitlab.com/gitlab-org/gitlab/-/issues/321090 + * + */ +export default { + components: { + GlSprintf, + FileIcon, + DiffFileEditor, + InlineConflictLines, + ParallelConflictLines, + }, + inject: ['mergeRequestPath', 'sourceBranchPath'], + i18n: { + commitStatSummary: __('Showing %{conflict} between %{sourceBranch} and %{targetBranch}'), + resolveInfo: __( + 'You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}', + ), + }, +}; +</script> +<template> + <div id="conflicts"> + <div v-if="$root.isLoading" class="loading"> + <div class="spinner spinner-md"></div> + </div> + <div v-if="$root.hasError" class="nothing-here-block"> + {{ $root.conflictsData.errorMessage }} + </div> + <template v-if="!$root.isLoading && !$root.hasError"> + <div class="content-block oneline-block files-changed"> + <div v-if="$root.showDiffViewTypeSwitcher" class="inline-parallel-buttons"> + <div class="btn-group"> + <button + :class="{ active: !$root.isParallel }" + class="btn gl-button" + @click="$root.handleViewTypeChange('inline')" + > + {{ __('Inline') }} + </button> + <button + :class="{ active: $root.isParallel }" + class="btn gl-button" + @click="$root.handleViewTypeChange('parallel')" + > + {{ __('Side-by-side') }} + </button> + </div> + </div> + <div class="js-toggle-container"> + <div class="commit-stat-summary"> + <gl-sprintf :message="$options.i18n.commitStatSummary"> + <template #conflict> + <strong class="cred"> + {{ $root.conflictsCountText }} + </strong> + </template> + <template #sourceBranch> + <strong class="ref-name"> + {{ $root.conflictsData.sourceBranch }} + </strong> + </template> + <template #targetBranch> + <strong class="ref-name"> + {{ $root.conflictsData.targetBranch }} + </strong> + </template> + </gl-sprintf> + </div> + </div> + </div> + <div class="files-wrapper"> + <div class="files"> + <div + v-for="file in $root.conflictsData.files" + :key="file.blobPath" + class="diff-file file-holder conflict" + > + <div class="js-file-title file-title file-title-flex-parent cursor-default"> + <div class="file-header-content"> + <file-icon :file-name="file.filePath" :size="18" css-classes="gl-mr-2" /> + <strong class="file-title-name">{{ file.filePath }}</strong> + </div> + <div class="file-actions d-flex align-items-center gl-ml-auto gl-align-self-start"> + <div v-if="file.type === 'text'" class="btn-group gl-mr-3"> + <button + :class="{ active: file.resolveMode === 'interactive' }" + class="btn gl-button" + type="button" + @click="$root.onClickResolveModeButton(file, 'interactive')" + > + {{ __('Interactive mode') }} + </button> + <button + :class="{ active: file.resolveMode === 'edit' }" + class="btn gl-button" + type="button" + @click="$root.onClickResolveModeButton(file, 'edit')" + > + {{ __('Edit inline') }} + </button> + </div> + <a :href="file.blobPath" class="btn gl-button view-file"> + <gl-sprintf :message="__('View file @ %{commitSha}')"> + <template #commitSha> + {{ $root.conflictsData.shortCommitSha }} + </template> + </gl-sprintf> + </a> + </div> + </div> + <div class="diff-content diff-wrap-lines"> + <div + v-show=" + !$root.isParallel && file.resolveMode === 'interactive' && file.type === 'text' + " + class="file-content" + > + <inline-conflict-lines :file="file" /> + </div> + <div + v-show=" + $root.isParallel && file.resolveMode === 'interactive' && file.type === 'text' + " + class="file-content" + > + <parallel-conflict-lines :file="file" /> + </div> + <div v-show="file.resolveMode === 'edit' || file.type === 'text-editor'"> + <diff-file-editor + :file="file" + :on-accept-discard-confirmation="$root.acceptDiscardConfirmation" + :on-cancel-discard-confirmation="$root.cancelDiscardConfirmation" + /> + </div> + </div> + </div> + </div> + </div> + <hr /> + <div class="resolve-conflicts-form"> + <div class="form-group row"> + <div class="col-md-4"> + <h4> + {{ __('Resolve conflicts on source branch') }} + </h4> + <div class="resolve-info"> + <gl-sprintf :message="$options.i18n.resolveInfo"> + <template #use_ours> + <code>{{ s__('MergeConflict|Use ours') }}</code> + </template> + <template #use_theirs> + <code>{{ s__('MergeConflict|Use theirs') }}</code> + </template> + <template #branch_name> + <a class="ref-name" :href="sourceBranchPath"> + {{ $root.conflictsData.sourceBranch }} + </a> + </template> + </gl-sprintf> + </div> + </div> + <div class="col-md-8"> + <label class="label-bold" for="commit-message"> + {{ __('Commit message') }} + </label> + <div class="commit-message-container"> + <div class="max-width-marker"></div> + <textarea + id="commit-message" + v-model="$root.conflictsData.commitMessage" + class="form-control js-commit-message" + rows="5" + ></textarea> + </div> + </div> + </div> + <div class="form-group row"> + <div class="offset-md-4 col-md-8"> + <div class="row"> + <div class="col-6"> + <button + :disabled="!$root.readyToCommit" + class="btn gl-button btn-success js-submit-button" + type="button" + @click="$root.commit()" + > + <span>{{ $root.commitButtonText }}</span> + </button> + </div> + <div class="col-6 text-right"> + <a :href="mergeRequestPath" class="gl-button btn btn-default"> + {{ __('Cancel') }} + </a> + </div> + </div> + </div> + </div> + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index a0db0faa592..4b73dd317cd 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -1,18 +1,11 @@ -// This is a true violation of @gitlab/no-runtime-template-compiler, as it -// relies on app/views/projects/merge_requests/conflicts/show.html.haml for its -// template. -/* eslint-disable @gitlab/no-runtime-template-compiler */ import $ from 'jquery'; import Vue from 'vue'; import { __ } from '~/locale'; -import FileIcon from '~/vue_shared/components/file_icon.vue'; import { deprecatedCreateFlash as createFlash } from '../flash'; import initIssuableSidebar from '../init_issuable_sidebar'; import './merge_conflict_store'; import syntaxHighlight from '../syntax_highlight'; -import DiffFileEditor from './components/diff_file_editor.vue'; -import InlineConflictLines from './components/inline_conflict_lines.vue'; -import ParallelConflictLines from './components/parallel_conflict_lines.vue'; +import MergeConflictsResolverApp from './merge_conflict_resolver_app.vue'; import MergeConflictsService from './merge_conflict_service'; export default function initMergeConflicts() { @@ -24,15 +17,15 @@ export default function initMergeConflicts() { resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath, }); + const { sourceBranchPath, mergeRequestPath } = conflictsEl.dataset; + initIssuableSidebar(); - gl.MergeConflictsResolverApp = new Vue({ - el: '#conflicts', - components: { - FileIcon, - DiffFileEditor, - InlineConflictLines, - ParallelConflictLines, + return new Vue({ + el: conflictsEl, + provide: { + sourceBranchPath, + mergeRequestPath, }, data: mergeConflictsStore.state, computed: { @@ -103,5 +96,8 @@ export default function initMergeConflicts() { }); }, }, + render(createElement) { + return createElement(MergeConflictsResolverApp); + }, }); } diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js index 3a06d0faa3e..bde0007ec6a 100644 --- a/app/assets/javascripts/pages/projects/boards/index.js +++ b/app/assets/javascripts/pages/projects/boards/index.js @@ -2,8 +2,6 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initBoards from '~/boards'; import UsersSelect from '~/users_select'; -document.addEventListener('DOMContentLoaded', () => { - new UsersSelect(); // eslint-disable-line no-new - new ShortcutsNavigation(); // eslint-disable-line no-new - initBoards(); -}); +new UsersSelect(); // eslint-disable-line no-new +new ShortcutsNavigation(); // eslint-disable-line no-new +initBoards(); diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss index 620b37914d8..a157c22a302 100644 --- a/app/assets/stylesheets/page_bundles/boards.scss +++ b/app/assets/stylesheets/page_bundles/boards.scss @@ -515,7 +515,7 @@ } } -.board-issue-path.js-show-tooltip { +.board-item-path.js-show-tooltip { cursor: help; } diff --git a/app/graphql/types/blob_viewers/type_enum.rb b/app/graphql/types/blob_viewers/type_enum.rb index 35e659197e5..3cb5c76d881 100644 --- a/app/graphql/types/blob_viewers/type_enum.rb +++ b/app/graphql/types/blob_viewers/type_enum.rb @@ -6,9 +6,9 @@ module Types graphql_name 'BlobViewersType' description 'Types of blob viewers' - value 'rich', value: :rich - value 'simple', value: :simple - value 'auxiliary', value: :auxiliary + value 'rich', value: :rich, description: 'Rich blob viewers type.' + value 'simple', value: :simple, description: 'Simple blob viewers type.' + value 'auxiliary', value: :auxiliary, description: 'Auxiliary blob viewers type.' end end end diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb index e3f02bf85f0..b3b172f9df2 100644 --- a/app/services/users/build_service.rb +++ b/app/services/users/build_service.rb @@ -83,7 +83,8 @@ module Users :location, :public_email, :user_type, - :note + :note, + :view_diffs_file_by_file ] end diff --git a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml deleted file mode 100644 index cb1cb41eb71..00000000000 --- a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -.content-block.oneline-block.files-changed{ "v-if" => "!isLoading && !hasError" } - .inline-parallel-buttons{ "v-if" => "showDiffViewTypeSwitcher" } - .btn-group - %button.btn.gl-button{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" } - = _('Inline') - %button.btn.gl-button{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" } - = _('Side-by-side') - - .js-toggle-container - .commit-stat-summary - = _('Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}').html_safe % { conflict_start: '<strong class="cred">'.html_safe, ref_start: '<strong class="ref-name">'.html_safe, strong_end: '</strong>'.html_safe, conflicts_text: '{{conflictsCountText}}', source_branch: '{{conflictsData.sourceBranch}}', target_branch: '{{conflictsData.targetBranch}}' } diff --git a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml deleted file mode 100644 index 220ddf1bad3..00000000000 --- a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -.file-actions.d-flex.align-items-center.gl-ml-auto.gl-align-self-start - .btn-group.gl-mr-3{ "v-if" => "file.type === 'text'" } - %button.btn.gl-button{ ":class" => "{ 'active': file.resolveMode == 'interactive' }", - '@click' => "onClickResolveModeButton(file, 'interactive')", - type: 'button' } - = _('Interactive mode') - %button.btn.gl-button{ ':class' => "{ 'active': file.resolveMode == 'edit' }", - '@click' => "onClickResolveModeButton(file, 'edit')", - type: 'button' } - = _('Edit inline') - %a.btn.gl-button.view-file{ ":href" => "file.blobPath" } - = _('View file @%{commit_sha}') % { commit_sha: '{{conflictsData.shortCommitSha}}' } diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml deleted file mode 100644 index 87356f33b1e..00000000000 --- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- branch_name = link_to @merge_request.source_branch, project_tree_path(@merge_request.project, @merge_request.source_branch), class: "ref-name" -- translation =_('You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}') % { use_ours: '<code>Use Ours</code>', use_theirs: '<code>Use Theirs</code>', branch_name: branch_name } - -%hr -.resolve-conflicts-form - .form-group.row - .col-md-4 - %h4= _('Resolve conflicts on source branch') - .resolve-info{ "v-pre": true } - = translation.html_safe - .col-md-8 - %label.label-bold{ "for" => "commit-message" } - #{ _('Commit message') } - .commit-message-container - .max-width-marker - %textarea.form-control.js-commit-message#commit-message{ "v-model" => "conflictsData.commitMessage", "rows" => "5" } - .form-group.row - .offset-md-4.col-md-8 - .row - .col-6 - %button.btn.gl-button.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" } - %span {{commitButtonText}} - .col-6.text-right - = link_to "Cancel", project_merge_request_path(@merge_request.project, @merge_request), class: "gl-button btn btn-default" diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index f1191bf220d..e02f126d165 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -1,5 +1,6 @@ - page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") - add_page_specific_style 'page_bundles/merge_conflicts' + = render "projects/merge_requests/mr_title" .merge-request-details.issuable-details @@ -7,30 +8,7 @@ = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, source_branch: @merge_request.source_branch -#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json), - resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request) } } - .loading{ "v-if" => "isLoading" } - .spinner.spinner-md - - .nothing-here-block{ "v-if" => "hasError" } - {{conflictsData.errorMessage}} - - = render partial: "projects/merge_requests/conflicts/commit_stats" - - .files-wrapper{ "v-if" => "!isLoading && !hasError" } - .files - .diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" } - .js-file-title.file-title.file-title-flex-parent.cursor-default - .file-header-content - %file-icon{ ':file-name': 'file.filePath', ':size': '18', 'css-classes': 'gl-mr-2' } - %strong.file-title-name {{file.filePath}} - = render partial: 'projects/merge_requests/conflicts/file_actions' - .diff-content.diff-wrap-lines - .file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } - %inline-conflict-lines{ ":file" => "file" } - .file-content{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } - %parallel-conflict-lines{ ":file" => "file" } - %div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" } - %diff-file-editor{ ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" } - - = render partial: "projects/merge_requests/conflicts/submit_form" +#conflicts{ data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json), + resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request), + source_branch_path: project_tree_path(@merge_request.project, @merge_request.source_branch), + merge_request_path: project_merge_request_path(@merge_request.project, @merge_request) } } diff --git a/changelogs/unreleased/284116-sync-or-remove-single-file-mode-user-preference.yml b/changelogs/unreleased/284116-sync-or-remove-single-file-mode-user-preference.yml new file mode 100644 index 00000000000..1eddb7ab59e --- /dev/null +++ b/changelogs/unreleased/284116-sync-or-remove-single-file-mode-user-preference.yml @@ -0,0 +1,5 @@ +--- +title: Support "view_diffs_file_by_file" param in the Users create and update API +merge_request: 54595 +author: +type: added diff --git a/changelogs/unreleased/x509-cert-loading.yml b/changelogs/unreleased/x509-cert-loading.yml new file mode 100644 index 00000000000..76fa6907730 --- /dev/null +++ b/changelogs/unreleased/x509-cert-loading.yml @@ -0,0 +1,5 @@ +--- +title: Forcibly load OpenSSL::X509::DEFAULT_CERT_FILE +merge_request: 54569 +author: +type: fixed diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 7ff7cc62e31..576441c37ca 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -217,56 +217,56 @@ and take effect after you [reconfigure GitLab](../restart_gitlab.md#omnibus-gitl Most of these settings don't need to be configured manually unless you need more granular control over how the Pages daemon runs and serves content in your environment. -| Setting | Description | -| ------- | ----------- | -| `pages_external_url` | The URL where GitLab Pages is accessible, including protocol (HTTP / HTTPS). If `https://` is used, additional configuration is required. See [Wildcard domains with TLS support](#wildcard-domains-with-tls-support) and [Custom domains with TLS support](#custom-domains-with-tls-support) for details. -| `gitlab_pages[]` | | -| `access_control` | Whether to enable [access control](index.md#access-control). -| `api_secret_key` | Full path to file with secret key used to authenticate with the GitLab API. Auto-generated when left unset. -| `artifacts_server` | Enable viewing [artifacts](../job_artifacts.md) in GitLab Pages. -| `artifacts_server_timeout` | Timeout (in seconds) for a proxied request to the artifacts server. -| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API. -| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`. -| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration. -| `dir` | Working directory for configuration and secrets files. -| `enable` | Enable or disable GitLab Pages on the current system. -| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. -| `external_https` | Configure Pages to bind to one or more secondary IP addresses, serving HTTPS requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_https`. -| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10s). -| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30s). -| `domain_config_source` | Domain configuration source (default: `auto`) -| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab. -| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab. -| `auth_scope` | The OAuth application scope to use for authentication. Must match GitLab Pages OAuth application settings. Leave blank to use `api` scope by default. -| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`. -| `headers` | Specify any additional http headers that should be sent to the client with each response. -| `inplace_chroot` | On [systems that don't support bind-mounts](index.md#additional-configuration-for-docker-container), this instructs GitLab Pages to `chroot` into its `pages_path` directory. Some caveats exist when using in-place `chroot`; refer to the GitLab Pages [README](https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md#caveats) for more information. -| `insecure_ciphers` | Use default list of cipher suites, may contain insecure ones like 3DES and RC4. -| `internal_gitlab_server` | Internal GitLab server address used exclusively for API requests. Useful if you want to send that traffic over an internal load balancer. Defaults to GitLab `external_url`. -| `listen_proxy` | The addresses to listen on for reverse-proxy requests. Pages binds to these addresses' network sockets and receives incoming requests from them. Sets the value of `proxy_pass` in `$nginx-dir/conf/gitlab-pages.conf`. -| `log_directory` | Absolute path to a log directory. -| `log_format` | The log output format: `text` or `json`. -| `log_verbose` | Verbose logging, true/false. -| `max_connections` | Limit on the number of concurrent connections to the HTTP, HTTPS or proxy listeners. -| `metrics_address` | The address to listen on for metrics requests. -| `redirect_http` | Redirect pages from HTTP to HTTPS, true/false. -| `sentry_dsn` | The address for sending Sentry crash reporting to. -| `sentry_enabled` | Enable reporting and logging with Sentry, true/false. -| `sentry_environment` | The environment for Sentry crash reporting. -| `status_uri` | The URL path for a status page, for example, `/@status`. -| `tls_max_version` | Specifies the maximum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2"). -| `tls_min_version` | Specifies the minimum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2"). -| `use_http2` | Enable HTTP2 support. -| `gitlab_pages['env'][]` | | -| `http_proxy` | Configure GitLab Pages to use an HTTP Proxy to mediate traffic between Pages and GitLab. Sets an environment variable `http_proxy` when starting Pages daemon. -| `gitlab_rails[]` | | -| `pages_domain_verification_cron_worker` | Schedule for verifying custom GitLab Pages domains. -| `pages_domain_ssl_renewal_cron_worker` | Schedule for obtaining and renewing SSL certificates through Let's Encrypt for GitLab Pages domains. -| `pages_domain_removal_cron_worker` | Schedule for removing unverified custom GitLab Pages domains. -| `pages_path` | The directory on disk where pages are stored, defaults to `GITLAB-RAILS/shared/pages`. -| `pages_nginx[]` | | -| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains). -| `FF_ENABLE_REDIRECTS` | Feature flag to disable redirects (enabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#disable-redirects) for more information. | +| Setting | Description | +|-----------------------------------------|-------------| +| `pages_external_url` | The URL where GitLab Pages is accessible, including protocol (HTTP / HTTPS). If `https://` is used, additional configuration is required. See [Wildcard domains with TLS support](#wildcard-domains-with-tls-support) and [Custom domains with TLS support](#custom-domains-with-tls-support) for details. | +| **`gitlab_pages[]`** | | +| `access_control` | Whether to enable [access control](index.md#access-control). | +| `api_secret_key` | Full path to file with secret key used to authenticate with the GitLab API. Auto-generated when left unset. | +| `artifacts_server` | Enable viewing [artifacts](../job_artifacts.md) in GitLab Pages. | +| `artifacts_server_timeout` | Timeout (in seconds) for a proxied request to the artifacts server. | +| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API. | +| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`. | +| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration. | +| `dir` | Working directory for configuration and secrets files. | +| `enable` | Enable or disable GitLab Pages on the current system. | +| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. | +| `external_https` | Configure Pages to bind to one or more secondary IP addresses, serving HTTPS requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_https`. | +| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10s). | +| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30s). | +| `domain_config_source` | Domain configuration source (default: `auto`) | +| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab. | +| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab. | +| `auth_scope` | The OAuth application scope to use for authentication. Must match GitLab Pages OAuth application settings. Leave blank to use `api` scope by default. | +| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`. | +| `headers` | Specify any additional http headers that should be sent to the client with each response. | +| `inplace_chroot` | On [systems that don't support bind-mounts](index.md#additional-configuration-for-docker-container), this instructs GitLab Pages to `chroot` into its `pages_path` directory. Some caveats exist when using in-place `chroot`; refer to the GitLab Pages [README](https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md#caveats) for more information. | +| `insecure_ciphers` | Use default list of cipher suites, may contain insecure ones like 3DES and RC4. | +| `internal_gitlab_server` | Internal GitLab server address used exclusively for API requests. Useful if you want to send that traffic over an internal load balancer. Defaults to GitLab `external_url`. | +| `listen_proxy` | The addresses to listen on for reverse-proxy requests. Pages binds to these addresses' network sockets and receives incoming requests from them. Sets the value of `proxy_pass` in `$nginx-dir/conf/gitlab-pages.conf`. | +| `log_directory` | Absolute path to a log directory. | +| `log_format` | The log output format: `text` or `json`. | +| `log_verbose` | Verbose logging, true/false. | +| `max_connections` | Limit on the number of concurrent connections to the HTTP, HTTPS or proxy listeners. | +| `metrics_address` | The address to listen on for metrics requests. | +| `redirect_http` | Redirect pages from HTTP to HTTPS, true/false. | +| `sentry_dsn` | The address for sending Sentry crash reporting to. | +| `sentry_enabled` | Enable reporting and logging with Sentry, true/false. | +| `sentry_environment` | The environment for Sentry crash reporting. | +| `status_uri` | The URL path for a status page, for example, `/@status`. | +| `tls_max_version` | Specifies the maximum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2"). | +| `tls_min_version` | Specifies the minimum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2"). | +| `use_http2` | Enable HTTP2 support. | +| **`gitlab_pages['env'][]`** | | +| `http_proxy` | Configure GitLab Pages to use an HTTP Proxy to mediate traffic between Pages and GitLab. Sets an environment variable `http_proxy` when starting Pages daemon. | +| **`gitlab_rails[]`** | | +| `pages_domain_verification_cron_worker` | Schedule for verifying custom GitLab Pages domains. | +| `pages_domain_ssl_renewal_cron_worker` | Schedule for obtaining and renewing SSL certificates through Let's Encrypt for GitLab Pages domains. | +| `pages_domain_removal_cron_worker` | Schedule for removing unverified custom GitLab Pages domains. | +| `pages_path` | The directory on disk where pages are stored, defaults to `GITLAB-RAILS/shared/pages`. | +| **`pages_nginx[]`** | | +| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains). | +| `FF_ENABLE_REDIRECTS` | Feature flag to disable redirects (enabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#disable-redirects) for more information. | --- diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md index b08f661fd14..1cd33711556 100644 --- a/doc/administration/repository_storage_types.md +++ b/doc/administration/repository_storage_types.md @@ -125,57 +125,61 @@ The output includes the project ID and the project name. For example: > [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/1606) in GitLab 12.1. -WARNING: -Do not run `git prune` or `git gc` in pool repositories! This can -cause data loss in "real" repositories that depend on the pool in -question. +Object pools are repositories used to deduplicate forks of public and internal projects and +contain the objects from the source project. Using `objects/info/alternates`, the source project and +forks use the object pool for shared objects. For more information, see +[How Git object deduplication works in GitLab](../development/git_object_deduplication.md). -Forks of public and internal projects are deduplicated by creating a third repository, the -object pool, containing the objects from the source project. Using -`objects/info/alternates`, the source project and forks use the object pool for -shared objects. Objects are moved from the source project to the object pool -when housekeeping is run on the source project. +Objects are moved from the source project to the object pool when housekeeping is run on the source +project. Object pool repositories are stored similarly to regular repositories: ```ruby # object pool paths "@pools/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git" ``` -### Hashed storage coverage migration - -Files stored in an S3-compatible endpoint do not have the downsides -mentioned earlier, if they are not prefixed with `#{namespace}/#{project_name}`. -This is true for CI Cache and LFS Objects. - -In the table below, you can find the coverage of the migration to the hashed storage. - -| Storable Object | Legacy storage | Hashed storage | S3 Compatible | GitLab Version | -| --------------- | -------------- | -------------- | ------------- | -------------- | -| Repository | Yes | Yes | - | 10.0 | -| Attachments | Yes | Yes | - | 10.2 | -| Avatars | Yes | No | - | - | -| Pages | Yes | No | - | - | -| Docker Registry | Yes | No | - | - | -| CI Build Logs | No | No | - | - | -| CI Artifacts | No | No | Yes | 9.4 / 10.6 | -| CI Cache | No | No | Yes | - | -| LFS Objects | Yes | Similar | Yes | 10.0 / 10.7 | -| Repository pools| No | Yes | - | 11.6 | +WARNING: +Do not run `git prune` or `git gc` in object pool repositories. This can cause data loss in the +regular repositories that depend on the object pool. + +### Object storage support + +This table shows which storable objects are storable in each storage type: + +| Storable object | Legacy storage | Hashed storage | S3 compatible | GitLab version | +|:-----------------|:---------------|:---------------|:--------------|:---------------| +| Repository | Yes | Yes | - | 10.0 | +| Attachments | Yes | Yes | - | 10.2 | +| Avatars | Yes | No | - | - | +| Pages | Yes | No | - | - | +| Docker Registry | Yes | No | - | - | +| CI/CD job logs | No | No | - | - | +| CI/CD artifacts | No | No | Yes | 9.4 / 10.6 | +| CI/CD cache | No | No | Yes | - | +| LFS objects | Yes | Similar | Yes | 10.0 / 10.7 | +| Repository pools | No | Yes | - | 11.6 | + +Files stored in an S3-compatible endpoint can have the same advantages as +[hashed storage](#hashed-storage), as long as they are not prefixed with +`#{namespace}/#{project_name}`. This is true for CI/CD cache and LFS objects. #### Avatars -Each file is stored in a folder with its `id` from the database. The filename is always `avatar.png` for user avatars. -When avatar is replaced, `Upload` model is destroyed and a new one takes place with different `id`. +Each file is stored in a directory that matches the `id` assigned to it in the database. The +filename is always `avatar.png` for user avatars. When an avatar is replaced, the `Upload` model is +destroyed and a new one takes place with a different `id`. + +#### CI/CD artifacts -#### CI artifacts +CI/CD artifacts are: -CI Artifacts are S3 compatible since **9.4** (GitLab Premium), and available in GitLab Free since -**10.6**. +- S3-compatible since GitLab 9.4, initially available in [GitLab Premium](https://about.gitlab.com/pricing/). +- Available in [GitLab Free](https://about.gitlab.com/pricing/) since GitLab 10.6. #### LFS objects [LFS Objects in GitLab](../topics/git/lfs/index.md) implement a similar -storage pattern using 2 chars, 2 level folders, following Git's own implementation: +storage pattern using two characters and two-level folders, following Git's own implementation: ```ruby "shared/lfs-objects/#{oid[0..1}/#{oid[2..3]}/#{oid[4..-1]}" @@ -184,40 +188,32 @@ storage pattern using 2 chars, 2 level folders, following Git's own implementati "shared/lfs-objects/89/09/029eb962194cfb326259411b22ae3f4a814b5be4f80651735aeef9f3229c" ``` -LFS objects are also [S3 compatible](lfs/index.md#storing-lfs-objects-in-remote-object-storage). +LFS objects are also [S3-compatible](lfs/index.md#storing-lfs-objects-in-remote-object-storage). ## Legacy storage WARNING: -In GitLab 13.0, hashed storage is enabled by default and the legacy storage is -deprecated. If you haven't migrated yet, check the -[migration instructions](raketasks/storage.md#migrate-to-hashed-storage). -Support for legacy storage is scheduled to be removed in GitLab 14.0. If you're on GitLab -13.0 and later, switching new projects to legacy storage is not possible. -The option to choose between hashed and legacy storage in the admin area has -been disabled. - -Legacy storage is the storage behavior prior to version 10.0. For historical -reasons, GitLab replicated the same mapping structure from the projects URLs: - -- Project's repository: `#{namespace}/#{project_name}.git` -- Project's wiki: `#{namespace}/#{project_name}.wiki.git` - -This structure enables you to migrate from existing solutions to GitLab, and -for Administrators to find where the repository is stored. - -This approach also has some drawbacks: - -Storage location concentrates a huge number of top-level namespaces. The -impact can be reduced by the introduction of -[multiple storage paths](repository_storage_paths.md). - -Because backups are a snapshot of the same URL mapping, if you try to recover a -very old backup, you need to verify whether any project has taken the place of -an old removed or renamed project sharing the same URL. This means that -`mygroup/myproject` from your backup may not be the same original project that -is at that same URL today. - -Any change in the URL needs to be reflected on disk (when groups / users or -projects are renamed). This can add a lot of load in big installations, -especially if using any type of network based file system. +In GitLab 13.0, legacy storage is deprecated. If you haven't migrated to hashed storage yet, check +the [migration instructions](raketasks/storage.md#migrate-to-hashed-storage). Support for legacy +storage is [scheduled to be removed](https://gitlab.com/gitlab-org/gitaly/-/issues/1690) in GitLab +14.0. In GitLab 13.0 and later, switching new projects to legacy storage is not possible. The +option to choose between hashed and legacy storage in the Admin Area is disabled. + +Legacy storage was the storage behavior prior to version GitLab 10.0. For historical reasons, +GitLab replicated the same mapping structure from the projects URLs: + +- Project's repository: `#{namespace}/#{project_name}.git`. +- Project's wiki: `#{namespace}/#{project_name}.wiki.git`. + +This structure enabled you to migrate from existing solutions to GitLab, and for Administrators to +find where the repository was stored. This approach also had some drawbacks: + +- Storage location concentrated a large number of top-level namespaces. The impact could be + reduced by [multiple repository storage paths](repository_storage_paths.md). +- Because backups were a snapshot of the same URL mapping, if you tried to recover a very old + backup, you needed to verify whether any project had taken the place of an old removed or renamed + project sharing the same URL. This meant that `mygroup/myproject` from your backup may not have + been the same original project that was at that same URL today. +- Any change in the URL needed to be reflected on disk (when groups, users, or projects were + renamed. This could add a lot of load in big installations, especially if using any type of + network-based file system. diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md index 0f60c43ef9e..6baa9c2ed7b 100644 --- a/doc/administration/troubleshooting/elasticsearch.md +++ b/doc/administration/troubleshooting/elasticsearch.md @@ -294,7 +294,7 @@ Beyond that, you will want to review the error. If it is: GitLab support. - An OS issue, you will want to reach out to your systems administrator. - A `Faraday::TimeoutError (execution expired)` error **and** you're using a proxy, - [set a custom `gitlab_rails['env']` environment variable, called `no_proxy`](https://docs.gitlab.com/omnibus/settings/environment-variables.html) + [set a custom `gitlab_rails['env']` environment variable, called `no_proxy`](https://docs.gitlab.com/omnibus/settings/environment-variables.html) with the IP address of your Elasticsearch host. ### Troubleshooting performance diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b1fa37e4d06..aba9825efc5 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -5021,9 +5021,9 @@ Types of blob viewers. | Value | Description | | ----- | ----------- | -| `auxiliary` | | -| `rich` | | -| `simple` | | +| `auxiliary` | Auxiliary blob viewers type. | +| `rich` | Rich blob viewers type. | +| `simple` | Simple blob viewers type. | ### CiConfigStatus diff --git a/doc/api/settings.md b/doc/api/settings.md index 53cb1335a54..4d1b66f40ab 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -335,7 +335,7 @@ listed in the descriptions of the relevant settings. | `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. | | `project_export_enabled` | boolean | no | Enable project export. | | `prometheus_metrics_enabled` | boolean | no | Enable Prometheus metrics. | -| `protected_ci_variables` | boolean | no | Environment variables are protected by default. | +| `protected_ci_variables` | boolean | no | CI/CD variables are protected by default. | | `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab runs a background job that produces pseudonymized CSVs of the GitLab database to upload to your configured object storage directory. | `push_event_activities_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push events are created. [Bulk push events are created](../user/admin_area/settings/push_event_activities_limit.md) if it surpasses that value. | | `push_event_hooks_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether webhooks and services fire or not. Webhooks and services aren't submitted if it surpasses that value. | diff --git a/doc/api/users.md b/doc/api/users.md index 5b966d6ba87..b8917f3e215 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -448,6 +448,7 @@ Parameters: | `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) | | `twitter` | No | Twitter account | | `username` | Yes | Username | +| `view_diffs_file_by_file` | No | Flag indicating the user sees only one file diff per page | | `website_url` | No | Website URL | ## User modification @@ -489,6 +490,7 @@ Parameters: | `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) | | `twitter` | No | Twitter account | | `username` | No | Username | +| `view_diffs_file_by_file` | No | Flag indicating the user sees only one file diff per page | | `website_url` | No | Website URL | On password update, the user is forced to change it upon next login. diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md index 00993cc2932..d9ff88aef60 100644 --- a/doc/development/git_object_deduplication.md +++ b/doc/development/git_object_deduplication.md @@ -1,7 +1,8 @@ --- -stage: none -group: unassigned +stage: Create +group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +type: reference --- # How Git object deduplication works in GitLab diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md index f6575ba4173..7a869ed071a 100644 --- a/doc/user/project/merge_requests/browser_performance_testing.md +++ b/doc/user/project/merge_requests/browser_performance_testing.md @@ -93,7 +93,7 @@ that you can later download and analyze. This implementation always takes the la Browser Performance artifact available. If [GitLab Pages](../pages/index.md) is enabled, you can view the report directly in your browser. -You can also customize the jobs with environment variables: +You can also customize the jobs with CI/CD variables: - `SITESPEED_IMAGE`: Configure the Docker image to use for the job (default `sitespeedio/sitespeed.io`), but not the image version. - `SITESPEED_VERSION`: Configure the version of the Docker image to use for the job (default `14.1.0`). @@ -118,7 +118,7 @@ performance: > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27599) in GitLab 13.0. You can configure the sensitivity of degradation alerts to avoid getting alerts for minor drops in metrics. -This is done by setting the `DEGRADATION_THRESHOLD` variable. In the example below, the alert only shows up +This is done by setting the `DEGRADATION_THRESHOLD` CI/CD variable. In the example below, the alert only shows up if the `Total Score` metric degrades by 5 points or more: ```yaml @@ -189,7 +189,7 @@ GitLab version: - In GitLab 12.4 [a job template was made available](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml). - In 13.2 the feature was renamed from `Performance` to `Browser Performance` with -additional template variables. The job name in the template is still `performance` +additional template CI/CD variables. The job name in the template is still `performance` for compatibility reasons, but may be renamed to match in a future iteration. - For 11.5 to 12.3 no template is available and the job has to be defined manually as follows: diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md index 451561cc22f..e80961464ba 100644 --- a/doc/user/project/merge_requests/code_quality.md +++ b/doc/user/project/merge_requests/code_quality.md @@ -89,7 +89,7 @@ scans your source code for code quality issues. The report is saved as a that you can later download and analyze. It's also possible to override the URL to the Code Quality image by -setting the `CODE_QUALITY_IMAGE` variable. This is particularly useful if you want +setting the `CODE_QUALITY_IMAGE` CI/CD variable. This is particularly useful if you want to lock in a specific version of Code Quality, or use a fork of it: ```yaml @@ -235,12 +235,12 @@ was chosen as an operational decision by the runner team, instead of exposing `d ### Disabling the code quality job -The `code_quality` job doesn't run if the `$CODE_QUALITY_DISABLED` environment -variable is present. Please refer to the environment variables [documentation](../../../ci/variables/README.md) +The `code_quality` job doesn't run if the `$CODE_QUALITY_DISABLED` CI/CD variable +is present. Please refer to the CI/CD variables [documentation](../../../ci/variables/README.md) to learn more about how to define one. -To disable the `code_quality` job, add `CODE_QUALITY_DISABLED` as a custom environment -variable. This can be done: +To disable the `code_quality` job, add `CODE_QUALITY_DISABLED` as a custom CI/CD variable. +This can be done: - For the whole project, [in the project settings](../../../ci/variables/README.md#create-a-custom-variable-in-the-ui) or [CI/CD configuration](../../../ci/variables/README.md#create-a-custom-variable-in-the-ui). @@ -364,7 +364,7 @@ After the Code Quality job completes: In [GitLab 13.6 and later](https://gitlab.com/gitlab-org/ci-cd/codequality/-/issues/10), it is possible to generate an HTML report file by setting the `REPORT_FORMAT` -variable to `html`. This is useful if you just want to view the report in a more +CI/CD variable to `html`. This is useful if you just want to view the report in a more human-readable format or to publish this artifact on GitLab Pages for even easier reviewing. diff --git a/doc/user/project/merge_requests/load_performance_testing.md b/doc/user/project/merge_requests/load_performance_testing.md index 9154897d42d..e8062fadcfe 100644 --- a/doc/user/project/merge_requests/load_performance_testing.md +++ b/doc/user/project/merge_requests/load_performance_testing.md @@ -104,8 +104,8 @@ An example configuration workflow: 1. Set up GitLab Runner to run Docker containers, like the [Docker-in-Docker workflow](../../../ci/docker/using_docker_build.md#use-the-docker-executor-with-the-docker-image-docker-in-docker). -1. Configure the default Load Performance Testing CI job in your `.gitlab-ci.yml` file. - You need to include the template and configure it with variables: +1. Configure the default Load Performance Testing CI/CD job in your `.gitlab-ci.yml` file. + You need to include the template and configure it with CI/CD variables: ```yaml include: @@ -153,7 +153,7 @@ but it can be extended to work with [review apps](../../../ci/review_apps) or [dynamic environments](../../../ci/environments) with a few extra steps. The best approach is to capture the dynamic URL in a [`.env` file](https://docs.docker.com/compose/env-file/) -as a job artifact to be shared, then use a custom environment variable we've provided named `K6_DOCKER_OPTIONS` +as a job artifact to be shared, then use a custom CI/CD variable we've provided named `K6_DOCKER_OPTIONS` to configure the k6 Docker container to use the file. With this, k6 can then use any environment variables from the `.env` file in scripts using standard JavaScript, such as: ``http.get(`${__ENV.ENVIRONMENT_URL}`)``. diff --git a/lib/api/users.rb b/lib/api/users.rb index 9b394ad1cfd..b2f99bb18dc 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -58,6 +58,7 @@ module API optional :color_scheme_id, type: Integer, desc: 'The color scheme for the file viewer' optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile' optional :note, type: String, desc: 'Admin note for this user' + optional :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page' all_or_none_of :extern_uid, :provider use :optional_params_ee diff --git a/lib/gitlab/x509/signature.rb b/lib/gitlab/x509/signature.rb index 7d4d4d9d13a..edff1540cb3 100644 --- a/lib/gitlab/x509/signature.rb +++ b/lib/gitlab/x509/signature.rb @@ -52,6 +52,12 @@ module Gitlab strong_memoize(:cert_store) do store = OpenSSL::X509::Store.new store.set_default_paths + + if Feature.enabled?(:x509_forced_cert_loading, type: :ops) + # Forcibly load the default cert file because the OpenSSL library seemingly ignores it + store.add_file(OpenSSL::X509::DEFAULT_CERT_FILE) if File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE) + end + # valid_signing_time? checks the time attributes already # this flag is required, otherwise expired certificates would become # unverified when notAfter within certificate attribute is reached diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e73a8f3ed0c..92ee07da74c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -27387,7 +27387,7 @@ msgid_plural "Showing %d events" msgstr[0] "" msgstr[1] "" -msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}" +msgid "Showing %{conflict} between %{sourceBranch} and %{targetBranch}" msgstr "" msgid "Showing %{count} of %{total} projects" @@ -32776,9 +32776,6 @@ msgstr "" msgid "View file @ %{commitSha}" msgstr "" -msgid "View file @%{commit_sha}" -msgstr "" - msgid "View full dashboard" msgstr "" diff --git a/spec/frontend/boards/issue_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js index b9f84fed6b3..ae5bbfd825f 100644 --- a/spec/frontend/boards/issue_card_inner_spec.js +++ b/spec/frontend/boards/board_card_inner_spec.js @@ -1,7 +1,7 @@ import { GlLabel } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { range } from 'lodash'; -import IssueCardInner from '~/boards/components/issue_card_inner.vue'; +import BoardCardInner from '~/boards/components/board_card_inner.vue'; import eventHub from '~/boards/eventhub'; import defaultStore from '~/boards/stores'; import { updateHistory } from '~/lib/utils/url_utility'; @@ -10,7 +10,7 @@ import { mockLabelList } from './mock_data'; jest.mock('~/lib/utils/url_utility'); jest.mock('~/boards/eventhub'); -describe('Issue card component', () => { +describe('Board card component', () => { const user = { id: 1, name: 'testing 123', @@ -31,11 +31,11 @@ describe('Issue card component', () => { let list; const createWrapper = (props = {}, store = defaultStore) => { - wrapper = mount(IssueCardInner, { + wrapper = mount(BoardCardInner, { store, propsData: { list, - issue, + item: issue, ...props, }, stubs: { @@ -63,7 +63,7 @@ describe('Issue card component', () => { weight: 1, }; - createWrapper({ issue, list }); + createWrapper({ item: issue, list }); }); afterEach(() => { @@ -103,8 +103,8 @@ describe('Issue card component', () => { describe('confidential issue', () => { beforeEach(() => { wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), confidential: true, }, }); @@ -119,8 +119,8 @@ describe('Issue card component', () => { describe('with avatar', () => { beforeEach(() => { wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), assignees: [user], updateData(newData) { Object.assign(this, newData); @@ -146,8 +146,8 @@ describe('Issue card component', () => { }); it('renders the avatar using avatarUrl property', async () => { - wrapper.props('issue').updateData({ - ...wrapper.props('issue'), + wrapper.props('item').updateData({ + ...wrapper.props('item'), assignees: [ { id: '1', @@ -172,8 +172,8 @@ describe('Issue card component', () => { global.gon.default_avatar_url = 'default_avatar'; wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), assignees: [ { id: 1, @@ -201,8 +201,8 @@ describe('Issue card component', () => { describe('multiple assignees', () => { beforeEach(() => { wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), assignees: [ { id: 2, @@ -233,7 +233,7 @@ describe('Issue card component', () => { describe('more than three assignees', () => { beforeEach(() => { - const { assignees } = wrapper.props('issue'); + const { assignees } = wrapper.props('item'); assignees.push({ id: 5, name: 'user5', @@ -242,8 +242,8 @@ describe('Issue card component', () => { }); wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), assignees, }, }); @@ -259,7 +259,7 @@ describe('Issue card component', () => { it('renders 99+ avatar counter', async () => { const assignees = [ - ...wrapper.props('issue').assignees, + ...wrapper.props('item').assignees, ...range(5, 103).map((i) => ({ id: i, name: 'name', @@ -268,8 +268,8 @@ describe('Issue card component', () => { })), ]; wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), assignees, }, }); @@ -283,7 +283,7 @@ describe('Issue card component', () => { describe('labels', () => { beforeEach(() => { - wrapper.setProps({ issue: { ...issue, labels: [list.label, label1] } }); + wrapper.setProps({ item: { ...issue, labels: [list.label, label1] } }); }); it('does not render list label but renders all other labels', () => { @@ -295,7 +295,7 @@ describe('Issue card component', () => { }); it('does not render label if label does not have an ID', async () => { - wrapper.setProps({ issue: { ...issue, labels: [label1, { title: 'closed' }] } }); + wrapper.setProps({ item: { ...issue, labels: [label1, { title: 'closed' }] } }); await wrapper.vm.$nextTick(); @@ -307,8 +307,8 @@ describe('Issue card component', () => { describe('blocked', () => { beforeEach(() => { wrapper.setProps({ - issue: { - ...wrapper.props('issue'), + item: { + ...wrapper.props('item'), blocked: true, }, }); diff --git a/spec/frontend/boards/components/board_card_deprecated_spec.js b/spec/frontend/boards/components/board_card_deprecated_spec.js index 6be84f6f111..266cbc7106d 100644 --- a/spec/frontend/boards/components/board_card_deprecated_spec.js +++ b/spec/frontend/boards/components/board_card_deprecated_spec.js @@ -7,7 +7,7 @@ import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import BoardCardDeprecated from '~/boards/components/board_card_deprecated.vue'; -import issueCardInner from '~/boards/components/issue_card_inner.vue'; +import issueCardInner from '~/boards/components/issue_card_inner_deprecated.vue'; import eventHub from '~/boards/eventhub'; import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; diff --git a/spec/frontend/boards/components/board_card_layout_deprecated_spec.js b/spec/frontend/boards/components/board_card_layout_deprecated_spec.js index 426c5289ba6..9853c9f434f 100644 --- a/spec/frontend/boards/components/board_card_layout_deprecated_spec.js +++ b/spec/frontend/boards/components/board_card_layout_deprecated_spec.js @@ -11,7 +11,7 @@ import '~/boards/models/label'; import '~/boards/models/assignee'; import '~/boards/models/list'; import BoardCardLayout from '~/boards/components/board_card_layout_deprecated.vue'; -import issueCardInner from '~/boards/components/issue_card_inner.vue'; +import issueCardInner from '~/boards/components/issue_card_inner_deprecated.vue'; import { ISSUABLE } from '~/boards/constants'; import boardsVuexStore from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js index d68e0f9fc23..a1d7a7de2fd 100644 --- a/spec/frontend/boards/components/board_card_spec.js +++ b/spec/frontend/boards/components/board_card_spec.js @@ -2,7 +2,7 @@ import { createLocalVue, shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import BoardCard from '~/boards/components/board_card.vue'; -import IssueCardInner from '~/boards/components/issue_card_inner.vue'; +import BoardCardInner from '~/boards/components/board_card_inner.vue'; import { inactiveId } from '~/boards/constants'; import { mockLabelList, mockIssue } from '../mock_data'; @@ -38,7 +38,7 @@ describe('Board card layout', () => { wrapper = shallowMount(BoardCard, { localVue, stubs: { - IssueCardInner, + BoardCardInner, }, store, propsData: { diff --git a/spec/lib/gitlab/x509/signature_spec.rb b/spec/lib/gitlab/x509/signature_spec.rb index ac6f7e49fe0..2ac9c1f3a3b 100644 --- a/spec/lib/gitlab/x509/signature_spec.rb +++ b/spec/lib/gitlab/x509/signature_spec.rb @@ -11,6 +11,65 @@ RSpec.describe Gitlab::X509::Signature do } end + shared_examples "a verified signature" do + it 'returns a verified signature if email does match' do + signature = described_class.new( + X509Helpers::User1.signed_commit_signature, + X509Helpers::User1.signed_commit_base_data, + X509Helpers::User1.certificate_email, + X509Helpers::User1.signed_commit_time + ) + + expect(signature.x509_certificate).to have_attributes(certificate_attributes) + expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) + expect(signature.verified_signature).to be_truthy + expect(signature.verification_status).to eq(:verified) + end + + it 'returns an unverified signature if email does not match' do + signature = described_class.new( + X509Helpers::User1.signed_commit_signature, + X509Helpers::User1.signed_commit_base_data, + "gitlab@example.com", + X509Helpers::User1.signed_commit_time + ) + + expect(signature.x509_certificate).to have_attributes(certificate_attributes) + expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) + expect(signature.verified_signature).to be_truthy + expect(signature.verification_status).to eq(:unverified) + end + + it 'returns an unverified signature if email does match and time is wrong' do + signature = described_class.new( + X509Helpers::User1.signed_commit_signature, + X509Helpers::User1.signed_commit_base_data, + X509Helpers::User1.certificate_email, + Time.new(2020, 2, 22) + ) + + expect(signature.x509_certificate).to have_attributes(certificate_attributes) + expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) + expect(signature.verified_signature).to be_falsey + expect(signature.verification_status).to eq(:unverified) + end + + it 'returns an unverified signature if certificate is revoked' do + signature = described_class.new( + X509Helpers::User1.signed_commit_signature, + X509Helpers::User1.signed_commit_base_data, + X509Helpers::User1.certificate_email, + X509Helpers::User1.signed_commit_time + ) + + expect(signature.verification_status).to eq(:verified) + + signature.x509_certificate.revoked! + + expect(signature.verification_status).to eq(:unverified) + end + end + context 'commit signature' do let(:certificate_attributes) do { @@ -30,62 +89,25 @@ RSpec.describe Gitlab::X509::Signature do allow(OpenSSL::X509::Store).to receive(:new).and_return(store) end - it 'returns a verified signature if email does match' do - signature = described_class.new( - X509Helpers::User1.signed_commit_signature, - X509Helpers::User1.signed_commit_base_data, - X509Helpers::User1.certificate_email, - X509Helpers::User1.signed_commit_time - ) - - expect(signature.x509_certificate).to have_attributes(certificate_attributes) - expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) - expect(signature.verified_signature).to be_truthy - expect(signature.verification_status).to eq(:verified) - end + it_behaves_like "a verified signature" + end - it 'returns an unverified signature if email does not match' do - signature = described_class.new( - X509Helpers::User1.signed_commit_signature, - X509Helpers::User1.signed_commit_base_data, - "gitlab@example.com", - X509Helpers::User1.signed_commit_time - ) + context 'with the certificate defined by OpenSSL::X509::DEFAULT_CERT_FILE' do + before do + store = OpenSSL::X509::Store.new + certificate = OpenSSL::X509::Certificate.new(X509Helpers::User1.trust_cert) + file_path = Rails.root.join("tmp/cert.pem").to_s - expect(signature.x509_certificate).to have_attributes(certificate_attributes) - expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) - expect(signature.verified_signature).to be_truthy - expect(signature.verification_status).to eq(:unverified) - end + File.open(file_path, "wb") do |f| + f.print certificate.to_pem + end - it 'returns an unverified signature if email does match and time is wrong' do - signature = described_class.new( - X509Helpers::User1.signed_commit_signature, - X509Helpers::User1.signed_commit_base_data, - X509Helpers::User1.certificate_email, - Time.new(2020, 2, 22) - ) + stub_const("OpenSSL::X509::DEFAULT_CERT_FILE", file_path) - expect(signature.x509_certificate).to have_attributes(certificate_attributes) - expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) - expect(signature.verified_signature).to be_falsey - expect(signature.verification_status).to eq(:unverified) + allow(OpenSSL::X509::Store).to receive(:new).and_return(store) end - it 'returns an unverified signature if certificate is revoked' do - signature = described_class.new( - X509Helpers::User1.signed_commit_signature, - X509Helpers::User1.signed_commit_base_data, - X509Helpers::User1.certificate_email, - X509Helpers::User1.signed_commit_time - ) - - expect(signature.verification_status).to eq(:verified) - - signature.x509_certificate.revoked! - - expect(signature.verification_status).to eq(:unverified) - end + it_behaves_like "a verified signature" end context 'without trusted certificate within store' do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 4a7232d6316..2a7689eaddf 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -952,6 +952,18 @@ RSpec.describe API::Users do expect(new_user.private_profile?).to eq(true) end + it "creates user with view_diffs_file_by_file" do + post api('/users', admin), params: attributes_for(:user, view_diffs_file_by_file: true) + + expect(response).to have_gitlab_http_status(:created) + + user_id = json_response['id'] + new_user = User.find(user_id) + + expect(new_user).not_to eq(nil) + expect(new_user.user_preference.view_diffs_file_by_file?).to eq(true) + end + it "does not create user with invalid email" do post api('/users', admin), params: { @@ -1266,6 +1278,13 @@ RSpec.describe API::Users do expect(user.reload.private_profile).to eq(true) end + it "updates viewing diffs file by file" do + put api("/users/#{user.id}", admin), params: { view_diffs_file_by_file: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(user.reload.user_preference.view_diffs_file_by_file?).to eq(true) + end + it "updates private profile to false when nil is given" do user.update!(private_profile: true) |