diff options
205 files changed, 2189 insertions, 1027 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 49c4b059dbc..610a5ecba6d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -327,7 +327,7 @@ cloud-native-image: cache: {} script: - gem install gitlab --no-ri --no-rdoc - - ./trigger-build cng + - ./scripts/trigger-build cng only: - tags@gitlab-org/gitlab-ce - tags@gitlab-org/gitlab-ee @@ -104,7 +104,7 @@ gem 'hashie-forbidden_attributes' gem 'kaminari', '~> 1.0' # HAML -gem 'hamlit', '~> 2.6.1' +gem 'hamlit', '~> 2.8.8' # Files attachments gem 'carrierwave', '~> 1.2' diff --git a/Gemfile.lock b/Gemfile.lock index a889c4dc3a0..ee5da96665c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -381,8 +381,8 @@ GEM rake (>= 10, < 13) rubocop (>= 0.49.0) sysexits (~> 1.1) - hamlit (2.6.1) - temple (~> 0.7.6) + hamlit (2.8.8) + temple (>= 0.8.0) thor tilt hashdiff (0.3.4) @@ -889,7 +889,7 @@ GEM sys-filesystem (1.1.6) ffi sysexits (1.2.0) - temple (0.7.7) + temple (0.8.0) test-prof (0.2.5) test_after_commit (1.1.0) activerecord (>= 3.2) @@ -900,7 +900,7 @@ GEM rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.6) + tilt (2.0.8) timecop (0.8.1) timfel-krb5-auth (0.8.3) toml (0.1.2) @@ -1057,7 +1057,7 @@ DEPENDENCIES graphql (~> 1.8.0) grpc (~> 1.11.0) haml_lint (~> 0.26.0) - hamlit (~> 2.6.1) + hamlit (~> 2.8.8) hashie-forbidden_attributes health_check (~> 2.6.0) hipchat (~> 1.5.0) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 8c46b8c5916..a38ac856e2b 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -79,7 +79,7 @@ GEM babosa (1.0.2) base32 (0.3.2) batch-loader (1.2.1) - bcrypt (3.1.11) + bcrypt (3.1.12) bcrypt_pbkdf (1.0.0) benchmark-ips (2.3.0) better_errors (2.1.1) @@ -111,7 +111,7 @@ GEM capybara-screenshot (1.0.14) capybara (>= 1.0, < 3) launchy - carrierwave (1.2.1) + carrierwave (1.2.3) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) @@ -384,8 +384,8 @@ GEM rake (>= 10, < 13) rubocop (>= 0.49.0) sysexits (~> 1.1) - hamlit (2.6.1) - temple (~> 0.7.6) + hamlit (2.8.8) + temple (>= 0.8.0) thor tilt hashdiff (0.3.4) @@ -514,7 +514,7 @@ GEM net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) - nokogiri (1.8.2) + nokogiri (1.8.3) mini_portile2 (~> 2.3.0) nokogumbo (1.5.0) nokogiri @@ -811,7 +811,7 @@ GEM rubyzip (1.2.1) rufus-scheduler (3.4.0) et-orbi (~> 1.0) - rugged (0.27.1) + rugged (0.27.2) safe_yaml (1.0.4) sanitize (4.6.5) crass (~> 1.0.2) @@ -877,7 +877,7 @@ GEM activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (3.7.1) + sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.1) @@ -898,7 +898,7 @@ GEM sys-filesystem (1.1.6) ffi sysexits (1.2.0) - temple (0.7.7) + temple (0.8.0) test-prof (0.2.5) text (1.3.1) thin (1.7.0) @@ -1067,7 +1067,7 @@ DEPENDENCIES graphql (~> 1.8.0) grpc (~> 1.11.0) haml_lint (~> 0.26.0) - hamlit (~> 2.6.1) + hamlit (~> 2.8.8) hashie-forbidden_attributes health_check (~> 2.6.0) hipchat (~> 1.5.0) diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index eb0985e5603..0327fceb38d 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -63,7 +63,8 @@ export default { plainDiffPath: state => state.diffs.plainDiffPath, emailPatchPath: state => state.diffs.emailPatchPath, }), - ...mapGetters(['isParallelView', 'isNotesFetched']), + ...mapGetters('diffs', ['isParallelView']), + ...mapGetters(['isNotesFetched']), targetBranch() { return { branchName: this.targetBranchName, @@ -115,7 +116,7 @@ export default { this.adjustView(); }, methods: { - ...mapActions(['setBaseConfig', 'fetchDiffFiles']), + ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles']), fetchData() { this.fetchDiffFiles().catch(() => { createFlash(__('Something went wrong on our end. Please try again!')); diff --git a/app/assets/javascripts/diffs/components/changed_files.vue b/app/assets/javascripts/diffs/components/changed_files.vue index c5ef9fefc2f..9d29357d800 100644 --- a/app/assets/javascripts/diffs/components/changed_files.vue +++ b/app/assets/javascripts/diffs/components/changed_files.vue @@ -31,7 +31,7 @@ export default { }; }, computed: { - ...mapGetters(['isInlineView', 'isParallelView', 'areAllFilesCollapsed']), + ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'areAllFilesCollapsed']), sumAddedLines() { return this.sumValues('addedLines'); }, @@ -66,7 +66,7 @@ export default { document.removeEventListener('scroll', this.handleScroll); }, methods: { - ...mapActions(['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']), + ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']), pluralize, handleScroll() { if (!this.updating) { diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index b6af49c7e2e..02d5be1821b 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -22,7 +22,7 @@ export default { projectPath: state => state.diffs.projectPath, endpoint: state => state.diffs.endpoint, }), - ...mapGetters(['isInlineView', 'isParallelView']), + ...mapGetters('diffs', ['isInlineView', 'isParallelView']), diffMode() { const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]); return diffModes[diffModeKey] || diffModes.replaced; @@ -39,12 +39,12 @@ export default { <div class="diff-viewer"> <template v-if="isTextFile"> <inline-diff-view - v-show="isInlineView" + v-if="isInlineView" :diff-file="diffFile" :diff-lines="diffFile.highlightedDiffLines || []" /> <parallel-diff-view - v-show="isParallelView" + v-if="isParallelView" :diff-file="diffFile" :diff-lines="diffFile.parallelDiffLines || []" /> diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue index 39d535036f6..20483161033 100644 --- a/app/assets/javascripts/diffs/components/diff_discussions.vue +++ b/app/assets/javascripts/diffs/components/diff_discussions.vue @@ -15,9 +15,7 @@ export default { </script> <template> - <div - v-if="discussions.length" - > + <div> <div v-for="discussion in discussions" :key="discussion.id" diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index 108eefdac5f..060386c3ecb 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -47,6 +47,9 @@ export default { false, ); }, + showExpandMessage() { + return this.isCollapsed && !this.isLoadingCollapsedDiff && !this.file.tooLarge; + }, }, mounted() { document.addEventListener('scroll', this.handleScroll); @@ -55,7 +58,7 @@ export default { document.removeEventListener('scroll', this.handleScroll); }, methods: { - ...mapActions(['loadCollapsedDiff']), + ...mapActions('diffs', ['loadCollapsedDiff']), handleToggle() { const { collapsed, highlightedDiffLines, parallelDiffLines } = this.file; @@ -159,7 +162,7 @@ export default { </div> <diff-content - v-show="!isCollapsed" + v-if="!isCollapsed" :class="{ hidden: isCollapsed || file.tooLarge }" :diff-file="file" /> @@ -168,7 +171,7 @@ export default { class="diff-content loading" /> <div - v-show="isCollapsed && !isLoadingCollapsedDiff && !file.tooLarge" + v-if="showExpandMessage" class="nothing-here-block diff-collapsed" > {{ __('This diff is collapsed.') }} diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index a8e8732053b..1957698c6c1 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -145,6 +145,7 @@ export default { @click.stop="handleToggle" /> <a + v-once ref="titleWrapper" :href="titleLink" class="append-right-4" diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index a74ea4bfaaf..ad838a32518 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -108,7 +108,7 @@ export default { }, }, methods: { - ...mapActions(['loadMoreLines', 'showCommentForm']), + ...mapActions('diffs', ['loadMoreLines', 'showCommentForm']), handleCommentButton() { this.showCommentForm({ lineCode: this.lineCode }); }, @@ -189,6 +189,7 @@ export default { </button> <a v-if="lineNumber" + v-once :data-linenumber="lineNumber" :href="lineHref" > diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue index 6943b462e86..db380e68bd1 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -59,7 +59,8 @@ export default { } }, methods: { - ...mapActions(['cancelCommentForm', 'saveNote', 'fetchDiscussions']), + ...mapActions('diffs', ['cancelCommentForm']), + ...mapActions(['saveNote', 'refetchDiscussionById']), handleCancelCommentForm() { this.autosave.reset(); this.cancelCommentForm({ @@ -78,10 +79,10 @@ export default { }); this.saveNote(postData) - .then(() => { + .then(result => { const endpoint = this.getNotesDataByProp('discussionsPath'); - this.fetchDiscussions(endpoint) + this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id }) .then(() => { this.handleCancelCommentForm(); }) diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue index 5b08b161114..bd02b45a63c 100644 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue @@ -117,14 +117,6 @@ export default { <template> <td - v-if="isContentLine" - :class="lineType" - class="line_content" - v-html="normalizedLine.richText" - > - </td> - <td - v-else :class="classNameMap" > <diff-line-gutter-content diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue index 0e935f1d68e..1e8f2eecd76 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue @@ -31,22 +31,9 @@ export default { diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), ...mapGetters(['discussionsByLineCode']), - isDiscussionExpanded() { - if (!this.discussions.length) { - return false; - } - - return this.discussions.every(discussion => discussion.expanded); - }, - hasCommentForm() { - return this.diffLineCommentForms[this.line.lineCode]; - }, discussions() { return this.discussionsByLineCode[this.line.lineCode] || []; }, - shouldRender() { - return this.isDiscussionExpanded || this.hasCommentForm; - }, className() { return this.discussions.length ? '' : 'js-temp-notes-holder'; }, @@ -56,7 +43,6 @@ export default { <template> <tr - v-if="shouldRender" :class="className" class="notes_holder" > @@ -67,6 +53,7 @@ export default { <td class="notes_content"> <div class="content"> <diff-discussions + v-if="discussions.length" :discussions="discussions" /> <diff-line-note-form diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index a2470843ca6..8e4715c9862 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -36,7 +36,7 @@ export default { }; }, computed: { - ...mapGetters(['isInlineView']), + ...mapGetters('diffs', ['isInlineView']), isContextLine() { return this.line.type === CONTEXT_LINE_TYPE; }, @@ -94,11 +94,12 @@ export default { :is-hover="isHover" class="diff-line-num new_line" /> - <diff-table-cell + <td + v-once :class="line.type" - :diff-file="diffFile" - :line="line" - :is-content-line="true" - /> + class="line_content" + v-html="line.richText" + > + </td> </tr> </template> diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index 83569346cf8..9c1359f7c89 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -1,5 +1,5 @@ <script> -import { mapGetters } from 'vuex'; +import { mapGetters, mapState } from 'vuex'; import inlineDiffTableRow from './inline_diff_table_row.vue'; import inlineDiffCommentRow from './inline_diff_comment_row.vue'; import { trimFirstCharOfLineContent } from '../store/utils'; @@ -20,7 +20,11 @@ export default { }, }, computed: { - ...mapGetters(['commitId']), + ...mapGetters('diffs', ['commitId']), + ...mapGetters(['discussionsByLineCode']), + ...mapState({ + diffLineCommentForms: state => state.diffs.diffLineCommentForms, + }), normalizedDiffLines() { return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line)); }, @@ -31,6 +35,18 @@ export default { return window.gon.user_color_scheme; }, }, + methods: { + shouldRenderCommentRow(line) { + if (this.diffLineCommentForms[line.lineCode]) return true; + + const lineDiscussions = this.discussionsByLineCode[line.lineCode]; + if (lineDiscussions === undefined) { + return false; + } + + return lineDiscussions.every(discussion => discussion.expanded); + }, + }, }; </script> @@ -50,6 +66,7 @@ export default { :key="line.lineCode" /> <inline-diff-comment-row + v-if="shouldRenderCommentRow(line)" :diff-file="diffFile" :diff-lines="normalizedDiffLines" :line="line" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue index 5f33ec7a3c2..1e20792b647 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue @@ -55,13 +55,6 @@ export default { hasAnyExpandedDiscussion() { return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight; }, - shouldRenderDiscussionsRow() { - const hasDiscussion = this.hasDiscussion && this.hasAnyExpandedDiscussion; - const hasCommentFormOnLeft = this.diffLineCommentForms[this.leftLineCode]; - const hasCommentFormOnRight = this.diffLineCommentForms[this.rightLineCode]; - - return hasDiscussion || hasCommentFormOnLeft || hasCommentFormOnRight; - }, shouldRenderDiscussionsOnLeft() { return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft; }, @@ -81,7 +74,6 @@ export default { <template> <tr - v-if="shouldRenderDiscussionsRow" :class="className" class="notes_holder" > @@ -92,6 +84,7 @@ export default { class="content" > <diff-discussions + v-if="discussionsByLineCode[leftLineCode].length" :discussions="discussionsByLineCode[leftLineCode]" /> </div> @@ -112,6 +105,7 @@ export default { class="content" > <diff-discussions + v-if="discussionsByLineCode[rightLineCode].length" :discussions="discussionsByLineCode[rightLineCode]" /> </div> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index eb769584d74..b76fc63205b 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -40,7 +40,7 @@ export default { }; }, computed: { - ...mapGetters(['isParallelView']), + ...mapGetters('diffs', ['isParallelView']), isContextLine() { return this.line.left.type === CONTEXT_LINE_TYPE; }, @@ -113,17 +113,15 @@ export default { :diff-view-type="parallelDiffViewType" class="diff-line-num old_line" /> - <diff-table-cell + <td + v-once :id="line.left.lineCode" - :diff-file="diffFile" - :line="line" - :is-content-line="true" - :line-position="linePositionLeft" - :line-type="parallelViewLeftLineType" - :diff-view-type="parallelDiffViewType" + :class="parallelViewLeftLineType" class="line_content parallel left-side" @mousedown.native="handleParallelLineMouseDown" - /> + v-html="line.left.richText" + > + </td> <diff-table-cell :diff-file="diffFile" :line="line" @@ -135,16 +133,14 @@ export default { :diff-view-type="parallelDiffViewType" class="diff-line-num new_line" /> - <diff-table-cell + <td + v-once :id="line.right.lineCode" - :diff-file="diffFile" - :line="line" - :is-content-line="true" - :line-position="linePositionRight" - :line-type="line.right.type" - :diff-view-type="parallelDiffViewType" + :class="line.right.type" class="line_content parallel right-side" @mousedown.native="handleParallelLineMouseDown" - /> + v-html="line.right.richText" + > + </td> </tr> </template> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 89148eb5e18..216865474a6 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -1,5 +1,5 @@ <script> -import { mapGetters } from 'vuex'; +import { mapState, mapGetters } from 'vuex'; import parallelDiffTableRow from './parallel_diff_table_row.vue'; import parallelDiffCommentRow from './parallel_diff_comment_row.vue'; import { EMPTY_CELL_TYPE } from '../constants'; @@ -21,7 +21,11 @@ export default { }, }, computed: { - ...mapGetters(['commitId']), + ...mapGetters('diffs', ['commitId']), + ...mapGetters(['discussionsByLineCode']), + ...mapState({ + diffLineCommentForms: state => state.diffs.diffLineCommentForms, + }), parallelDiffLines() { return this.diffLines.map(line => { const parallelLine = Object.assign({}, line); @@ -48,6 +52,32 @@ export default { return window.gon.user_color_scheme; }, }, + methods: { + shouldRenderCommentRow(line) { + const leftLineCode = line.left.lineCode; + const rightLineCode = line.right.lineCode; + const discussions = this.discussionsByLineCode; + const leftDiscussions = discussions[leftLineCode]; + const rightDiscussions = discussions[rightLineCode]; + const hasDiscussion = leftDiscussions || rightDiscussions; + + const hasExpandedDiscussionOnLeft = leftDiscussions + ? leftDiscussions.every(discussion => discussion.expanded) + : false; + const hasExpandedDiscussionOnRight = rightDiscussions + ? rightDiscussions.every(discussion => discussion.expanded) + : false; + + if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) { + return true; + } + + const hasCommentFormOnLeft = this.diffLineCommentForms[leftLineCode]; + const hasCommentFormOnRight = this.diffLineCommentForms[rightLineCode]; + + return hasCommentFormOnLeft || hasCommentFormOnRight; + }, + }, }; </script> @@ -69,6 +99,7 @@ export default { :key="index" /> <parallel-diff-comment-row + v-if="shouldRenderCommentRow(line)" :key="line.left.lineCode || line.right.lineCode" :line="line" :diff-file="diffFile" diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js index c745320d532..90505f83b60 100644 --- a/app/assets/javascripts/diffs/store/modules/index.js +++ b/app/assets/javascripts/diffs/store/modules/index.js @@ -4,6 +4,7 @@ import mutations from '../mutations'; import createState from './diff_state'; export default { + namespaced: true, state: createState(), getters, actions, diff --git a/app/assets/javascripts/ide/lib/themes/gl_theme.js b/app/assets/javascripts/ide/lib/themes/gl_theme.js index 2fc96250c7d..439ae50448a 100644 --- a/app/assets/javascripts/ide/lib/themes/gl_theme.js +++ b/app/assets/javascripts/ide/lib/themes/gl_theme.js @@ -9,6 +9,7 @@ export default { 'diffEditor.insertedTextBackground': '#ddfbe6', 'diffEditor.removedTextBackground': '#f9d7dc', 'editor.selectionBackground': '#aad6f8', + 'editorIndentGuide.activeBackground': '#cccccc', }, }, }; diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index b6364318537..ad928484952 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -108,6 +108,11 @@ type: String, required: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, projectPath: { type: String, required: true, @@ -282,6 +287,7 @@ :issuable-templates="issuableTemplates" :markdown-docs-path="markdownDocsPath" :markdown-preview-path="markdownPreviewPath" + :markdown-version="markdownVersion" :project-path="projectPath" :project-namespace="projectNamespace" :show-delete-button="showDeleteButton" diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index 5f58f671c73..97acc5ba385 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -20,6 +20,11 @@ type: String, required: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, canAttachFile: { type: Boolean, required: false, @@ -47,6 +52,7 @@ <markdown-field :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" + :markdown-version="markdownVersion" :can-attach-file="canAttachFile" :enable-autocomplete="enableAutocomplete" > diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue index 5bfc072e3da..e509bb52f7d 100644 --- a/app/assets/javascripts/issue_show/components/form.vue +++ b/app/assets/javascripts/issue_show/components/form.vue @@ -35,6 +35,11 @@ type: String, required: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, projectPath: { type: String, required: true, @@ -97,6 +102,7 @@ :form-state="formState" :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" + :markdown-version="markdownVersion" :can-attach-file="canAttachFile" :enable-autocomplete="enableAutocomplete" /> diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 48cda28a1ae..8124ae6201f 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1251,13 +1251,15 @@ export default class Notes { var postUrl = $originalContentEl.data('postUrl'); var targetId = $originalContentEl.data('targetId'); var targetType = $originalContentEl.data('targetType'); + var markdownVersion = $originalContentEl.data('markdownVersion'); this.glForm = new GLForm($editForm.find('form'), this.enableGFM); $editForm .find('form') .attr('action', `${postUrl}?html=true`) - .attr('data-remote', 'true'); + .attr('data-remote', 'true') + .attr('data-markdown-version', markdownVersion); $editForm.find('.js-form-target-id').val(targetId); $editForm.find('.js-form-target-type').val(targetType); $editForm diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index c6a524f68cb..6612bc44e0b 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -34,6 +34,11 @@ export default { type: String, required: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, }, data() { return { @@ -344,6 +349,7 @@ Please check your network connection and try again.`; :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" :quick-actions-docs-path="quickActionsDocsPath" + :markdown-version="markdownVersion" :add-spacing-classes="false"> <textarea id="note-body" diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index d2db68df98e..6f4a0709825 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -92,6 +92,7 @@ export default { :is-editing="isEditing" :note-body="noteBody" :note-id="note.id" + :markdown-version="note.cached_markdown_version" @handleFormUpdate="handleFormUpdate" @cancelForm="formCancelHandler" /> diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index a4e3faa5d75..963e3a37b39 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -24,6 +24,11 @@ export default { required: false, default: 0, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, saveButtonTitle: { type: String, required: false, @@ -156,6 +161,7 @@ export default { <markdown-field :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" + :markdown-version="markdownVersion" :quick-actions-docs-path="quickActionsDocsPath" :add-spacing-classes="false"> <textarea diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index a8995021699..9b8713b40fb 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -43,6 +43,11 @@ export default { required: false, default: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, }, data() { return { @@ -192,6 +197,7 @@ export default { <comment-form :noteable-type="noteableType" + :markdown-version="markdownVersion" /> </div> </template> diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index eed3a82854d..6dd4c9d66ac 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => { const notesDataset = document.getElementById('js-vue-notes').dataset; const parsedUserData = JSON.parse(notesDataset.currentUserData); const noteableData = JSON.parse(notesDataset.noteableData); + const { markdownVersion } = notesDataset; let currentUserData = {}; noteableData.noteableType = notesDataset.noteableType; @@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => { return { noteableData, currentUserData, + markdownVersion, notesData: JSON.parse(notesDataset.notesData), }; }, @@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => { noteableData: this.noteableData, notesData: this.notesData, userData: this.currentUserData, + markdownVersion: this.markdownVersion, }, }); }, diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index 671fa4d7d22..b2bf86eea56 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -41,6 +41,15 @@ export const fetchDiscussions = ({ commit }, path) => commit(types.SET_INITIAL_DISCUSSIONS, discussions); }); +export const refetchDiscussionById = ({ commit }, { path, discussionId }) => + service + .fetchDiscussions(path) + .then(res => res.json()) + .then(discussions => { + const selectedDiscussion = discussions.find(discussion => discussion.id === discussionId); + if (selectedDiscussion) commit(types.UPDATE_DISCUSSION, selectedDiscussion); + }); + export const deleteNote = ({ commit }, note) => service.deleteNote(note.path).then(() => { commit(types.DELETE_NOTE, note); diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index e5e40ce07fa..a1849269010 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -114,7 +114,6 @@ export default { Object.assign(state, { discussions }); }, - [types.SET_LAST_FETCHED_AT](state, fetchedAt) { Object.assign(state, { lastFetchedAt: fetchedAt }); }, diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 0e973cab4d2..0964baf8954 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.showPreview = function ($form) { var mdText; + var markdownVersion; + var url; var preview = $form.find('.js-md-preview'); - var url = preview.data('url'); if (preview.hasClass('md-preview-loading')) { return; } + mdText = $form.find('textarea.markdown-area').val(); + markdownVersion = $form.attr('data-markdown-version'); + url = this.versionedPreviewPath(preview.data('url'), markdownVersion); if (mdText.trim().length === 0) { preview.text(this.emptyMessage); @@ -59,6 +63,14 @@ MarkdownPreview.prototype.showPreview = function ($form) { } }; +MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath, markdownVersion) { + if (typeof markdownVersion === 'undefined') { + return markdownPreviewPath; + } + + return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`; +}; + MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) { if (!url) { return; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 5e464f8a0e2..21f21232596 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -79,66 +79,62 @@ export default { </script> <template> - <div class="mr-widget-heading deploy-heading"> + <div class="mr-widget-heading deploy-heading append-bottom-default"> <div class="ci-widget media"> - <div class="ci-status-icon ci-status-icon-success"> - <span class="js-icon-link icon-link"> - <status-icon status="success" /> - </span> - </div> <div class="media-body"> <div class="deploy-body"> - <template v-if="hasDeploymentMeta"> - <span> - Deployed to - </span> - <a - :href="deployment.url" - target="_blank" - rel="noopener noreferrer nofollow" - class="deploy-link js-deploy-meta" + <div class="deployment-info"> + <template v-if="hasDeploymentMeta"> + <span> + Deployed to + </span> + <a + :href="deployment.url" + target="_blank" + rel="noopener noreferrer nofollow" + class="deploy-link js-deploy-meta" + > + {{ deployment.name }} + </a> + </template> + <span + v-tooltip + v-if="hasDeploymentTime" + :title="deployment.deployed_at_formatted" + class="js-deploy-time" > - {{ deployment.name }} - </a> - </template> - <template v-if="hasExternalUrls"> - <span> - on + {{ deployTimeago }} </span> + <memory-usage + v-if="hasMetrics" + :metrics-url="deployment.metrics_url" + :metrics-monitoring-url="deployment.metrics_monitoring_url" + /> + </div> + <div> <a + v-if="hasExternalUrls" :href="deployment.external_url" target="_blank" rel="noopener noreferrer nofollow" - class="deploy-link js-deploy-url" + class="deploy-link js-deploy-url btn btn-default btn-sm inline" > - {{ deployment.external_url_formatted }} - <icon - :size="16" - name="external-link" - /> + <span> + View app + <icon name="external-link" /> + </span> </a> - </template> - <span - v-tooltip - v-if="hasDeploymentTime" - :title="deployment.deployed_at_formatted" - class="js-deploy-time" - > - {{ deployTimeago }} - </span> - <loading-button - v-if="deployment.stop_url" - :loading="isStopping" - container-class="btn btn-default btn-sm prepend-left-default" - label="Stop environment" - @click="stopEnvironment" - /> + <loading-button + v-if="deployment.stop_url" + :loading="isStopping" + container-class="btn btn-default btn-sm inline prepend-left-4" + title="Stop environment" + @click="stopEnvironment" + > + <icon name="stop" /> + </loading-button> + </div> </div> - <memory-usage - v-if="hasMetrics" - :metrics-url="deployment.metrics_url" - :metrics-monitoring-url="deployment.metrics_monitoring_url" - /> </div> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue index 3ce9d8dc26a..c18b74743e4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue @@ -2,7 +2,7 @@ import tooltip from '~/vue_shared/directives/tooltip'; import { n__ } from '~/locale'; import { webIDEUrl } from '~/lib/utils/url_utility'; -import icon from '~/vue_shared/components/icon.vue'; +import Icon from '~/vue_shared/components/icon.vue'; import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; export default { @@ -11,7 +11,7 @@ export default { tooltip, }, components: { - icon, + Icon, clipboardButton, }, props: { @@ -54,104 +54,114 @@ export default { }; </script> <template> - <div class="mr-source-target"> - <div class="normal"> - <strong> - {{ s__("mrWidget|Request to merge") }} - <span - :class="{ 'label-truncated': isSourceBranchLong }" - :title="isSourceBranchLong ? mr.sourceBranch : ''" - :v-tooltip="isSourceBranchLong" - class="label-branch js-source-branch" - data-placement="bottom" - v-html="mr.sourceBranchLink" - > - </span> + <div class="mr-source-target append-bottom-default"> + <div class="git-merge-icon-container append-right-default"> + <icon name="git-merge" /> + </div> + <div class="git-merge-container d-flex"> + <div class="normal"> + <strong> + {{ s__("mrWidget|Request to merge") }} + <span + :class="{ 'label-truncated': isSourceBranchLong }" + :title="isSourceBranchLong ? mr.sourceBranch : ''" + :v-tooltip="isSourceBranchLong" + class="label-branch js-source-branch" + data-placement="bottom" + v-html="mr.sourceBranchLink" + > + </span> - <clipboard-button - :text="branchNameClipboardData" - :title="__('Copy branch name to clipboard')" - css-class="btn-default btn-transparent btn-clipboard" - /> + <clipboard-button + :text="branchNameClipboardData" + :title="__('Copy branch name to clipboard')" + css-class="btn-default btn-transparent btn-clipboard" + /> - {{ s__("mrWidget|into") }} + {{ s__("mrWidget|into") }} - <span - :v-tooltip="isTargetBranchLong" - :class="{ 'label-truncatedtooltip': isTargetBranchLong }" - :title="isTargetBranchLong ? mr.targetBranch : ''" - class="label-branch" - data-placement="bottom" - > - <a - :href="mr.targetBranchTreePath" - class="js-target-branch" + <span + :v-tooltip="isTargetBranchLong" + :class="{ 'label-truncatedtooltip': isTargetBranchLong }" + :title="isTargetBranchLong ? mr.targetBranch : ''" + class="label-branch" + data-placement="bottom" > - {{ mr.targetBranch }} - </a> - </span> - </strong> - <span - v-if="shouldShowCommitsBehindText" - class="diverged-commits-count" - > - (<a :href="mr.targetBranchPath">{{ commitsText }}</a>) - </span> - </div> + <a + :href="mr.targetBranchTreePath" + class="js-target-branch" + > + {{ mr.targetBranch }} + </a> + </span> + </strong> + <div + v-if="shouldShowCommitsBehindText" + class="diverged-commits-count" + > + <span class="monospace">{{ mr.sourceBranch }}</span> + is {{ commitsText }} + <span class="monospace">{{ mr.targetBranch }}</span> + </div> + </div> - <div v-if="mr.isOpen"> - <a - v-if="!mr.sourceBranchRemoved" - :href="webIdePath" - class="btn btn-sm btn-default inline js-web-ide" - > - {{ s__("mrWidget|Web IDE") }} - </a> - <button - :disabled="mr.sourceBranchRemoved" - data-target="#modal_merge_info" - data-toggle="modal" - class="btn btn-sm btn-default inline js-check-out-branch" - type="button" + <div + v-if="mr.isOpen" + class="branch-actions" > - {{ s__("mrWidget|Check out branch") }} - </button> - <span class="dropdown prepend-left-10"> + <a + v-if="!mr.sourceBranchRemoved" + :href="webIdePath" + class="btn btn-default inline js-web-ide d-none d-md-inline-block" + > + {{ s__("mrWidget|Open in Web IDE") }} + </a> <button + :disabled="mr.sourceBranchRemoved" + data-target="#modal_merge_info" + data-toggle="modal" + class="btn btn-default inline js-check-out-branch" type="button" - class="btn btn-sm inline dropdown-toggle" - data-toggle="dropdown" - aria-label="Download as" - aria-haspopup="true" - aria-expanded="false" > - <icon name="download" /> - <i - class="fa fa-caret-down" - aria-hidden="true"> - </i> + {{ s__("mrWidget|Check out branch") }} </button> - <ul class="dropdown-menu dropdown-menu-right"> - <li> - <a - :href="mr.emailPatchesPath" - class="js-download-email-patches" - download - > - {{ s__("mrWidget|Email patches") }} - </a> - </li> - <li> - <a - :href="mr.plainDiffPath" - class="js-download-plain-diff" - download - > - {{ s__("mrWidget|Plain diff") }} - </a> - </li> - </ul> - </span> + <span class="dropdown prepend-left-10"> + <button + type="button" + class="btn inline dropdown-toggle" + data-toggle="dropdown" + aria-label="Download as" + aria-haspopup="true" + aria-expanded="false" + > + <icon name="download" /> + <i + class="fa fa-caret-down" + aria-hidden="true"> + </i> + </button> + <ul class="dropdown-menu dropdown-menu-right"> + <li> + <a + :href="mr.emailPatchesPath" + class="js-download-email-patches" + download + > + {{ s__("mrWidget|Email patches") }} + </a> + </li> + <li> + <a + :href="mr.plainDiffPath" + class="js-download-plain-diff" + download + > + {{ s__("mrWidget|Plain diff") }} + </a> + </li> + </ul> + </span> + </div> </div> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index 2f0b5e12c12..4a3fd01fa39 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -26,6 +26,10 @@ export default { type: String, required: false, }, + sourceBranchLink: { + type: String, + required: false, + }, }, computed: { hasPipeline() { @@ -54,12 +58,18 @@ export default { <template> <div v-if="hasPipeline || hasCIError" - class="mr-widget-heading" + class="mr-widget-heading append-bottom-default" > <div class="ci-widget media"> <template v-if="hasCIError"> - <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10"> - <icon name="status_failed" /> + <div + class="add-border ci-status-icon ci-status-icon-failed ci-error + js-ci-error append-right-default" + > + <icon + :size="32" + name="status_failed_borderless" + /> </div> <div class="media-body"> Could not connect to the CI server. Please check your settings and try again @@ -68,50 +78,66 @@ export default { <template v-else-if="hasPipeline"> <a :href="status.details_path" - class="append-right-10" + class="align-self-start append-right-default" > - <ci-icon :status="status" /> + <ci-icon + :status="status" + :size="32" + :borderless="true" + class="add-border" + /> </a> + <div class="ci-widget-container d-flex"> + <div class="ci-widget-content"> + <div class="media-body"> + <div class="font-weight-bold"> + Pipeline + <a + :href="pipeline.path" + class="pipeline-id font-weight-normal pipeline-number" + >#{{ pipeline.id }}</a> - <div class="media-body"> - Pipeline - <a - :href="pipeline.path" - class="pipeline-id" - > - #{{ pipeline.id }} - </a> - - {{ pipeline.details.status.label }} + {{ pipeline.details.status.label }} - <template v-if="hasCommitInfo"> - for - - <a - :href="pipeline.commit.commit_path" - class="commit-sha js-commit-link" - > - {{ pipeline.commit.short_id }}</a>. - </template> - - <span class="mr-widget-pipeline-graph"> - <span - v-if="hasStages" - class="stage-cell" - > + <template v-if="hasCommitInfo"> + for + <a + :href="pipeline.commit.commit_path" + class="commit-sha js-commit-link font-weight-normal" + > + {{ pipeline.commit.short_id }}</a> + on + <span + class="label-branch" + v-html="sourceBranchLink" + > + </span> + </template> + </div> <div - v-for="(stage, i) in pipeline.details.stages" - :key="i" - class="stage-container dropdown js-mini-pipeline-graph" + v-if="pipeline.coverage" + class="coverage" > - <pipeline-stage :stage="stage" /> + Coverage {{ pipeline.coverage }}% </div> + </div> + </div> + <div> + <span class="mr-widget-pipeline-graph"> + <span + v-if="hasStages" + class="stage-cell" + > + <div + v-for="(stage, i) in pipeline.details.stages" + :key="i" + class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages" + > + <pipeline-stage :stage="stage" /> + </div> + </span> </span> - </span> - - <template v-if="pipeline.coverage"> - Coverage {{ pipeline.coverage }}% - </template> + </div> </div> </template> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue index 53c4dc8c8f4..55b87f3a8ec 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue @@ -43,6 +43,7 @@ <ci-icon v-else :status="statusObj" + :size="24" /> <button diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 09477da40b5..b5de3dd6d73 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -252,41 +252,44 @@ export default { :pipeline="mr.pipeline" :ci-status="mr.ciStatus" :has-ci="mr.hasCI" + :source-branch-link="mr.sourceBranchLink" /> <deployment v-for="deployment in mr.deployments" :key="deployment.id" :deployment="deployment" /> - <div class="mr-widget-section"> - <component - :is="componentName" - :mr="mr" - :service="service" - /> + <div class="mr-section-container"> + <div class="mr-widget-section"> + <component + :is="componentName" + :mr="mr" + :service="service" + /> - <section - v-if="mr.allowCollaboration" - class="mr-info-list mr-links" - > - {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }} - </section> + <section + v-if="mr.allowCollaboration" + class="mr-info-list mr-links" + > + {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }} + </section> - <mr-widget-related-links - v-if="shouldRenderRelatedLinks" - :state="mr.state" - :related-links="mr.relatedLinks" - /> + <mr-widget-related-links + v-if="shouldRenderRelatedLinks" + :state="mr.state" + :related-links="mr.relatedLinks" + /> - <source-branch-removal-status - v-if="shouldRenderSourceBranchRemovalStatus" - /> - </div> - <div - v-if="shouldRenderMergeHelp" - class="mr-widget-footer" - > - <mr-widget-merge-help /> + <source-branch-removal-status + v-if="shouldRenderSourceBranchRemovalStatus" + /> + </div> + <div + v-if="shouldRenderMergeHelp" + class="mr-widget-footer" + > + <mr-widget-merge-help /> + </div> </div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 298971a36b2..d62537021ca 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -1,5 +1,6 @@ <script> import $ from 'jquery'; + import { s__ } from '~/locale'; import Flash from '../../../flash'; import GLForm from '../../../gl_form'; import markdownHeader from './header.vue'; @@ -22,6 +23,11 @@ type: String, required: true, }, + markdownVersion: { + type: Number, + required: false, + default: 0, + }, addSpacingClasses: { type: Boolean, required: false, @@ -92,10 +98,11 @@ if (text) { this.markdownPreviewLoading = true; - this.$http.post(this.markdownPreviewPath, { text }) - .then(resp => resp.json()) - .then(data => this.renderMarkdown(data)) - .catch(() => new Flash('Error loading markdown preview')); + this.$http + .post(this.versionedPreviewPath(), { text }) + .then(resp => resp.json()) + .then(data => this.renderMarkdown(data)) + .catch(() => new Flash(s__('Error loading markdown preview'))); } else { this.renderMarkdown(); } @@ -119,6 +126,13 @@ $(this.$refs['markdown-preview']).renderGFM(); }); }, + + versionedPreviewPath() { + const { markdownPreviewPath, markdownVersion } = this; + return `${markdownPreviewPath}${ + markdownPreviewPath.indexOf('?') === -1 ? '?' : '&' + }markdown_version=${markdownVersion}`; + }, }, }; </script> diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 551a7e852ae..5d79610b21e 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -224,7 +224,10 @@ .form-control { position: relative; min-width: 200px; - padding: 5px 25px 6px 0; + padding-right: 25px; + padding-left: 0; + height: $input-height; + line-height: inherit; border-color: transparent; &:focus, diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 30314f3d6cb..d1f7ff4438b 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -3,12 +3,20 @@ svg { fill: $green-500; } + + &.add-border { + @include borderless-status-icon($green-500); + } } .ci-status-icon-failed { svg { fill: $gl-danger; } + + &.add-border { + @include borderless-status-icon($red-500); + } } .ci-status-icon-pending, @@ -17,12 +25,20 @@ svg { fill: $orange-500; } + + &.add-border { + @include borderless-status-icon($orange-500); + } } .ci-status-icon-running { svg { fill: $blue-400; } + + &.add-border { + @include borderless-status-icon($blue-400); + } } .ci-status-icon-canceled, @@ -30,6 +46,10 @@ svg { fill: $gl-text-color; } + + &.add-border { + @include borderless-status-icon($gl-text-color); + } } .ci-status-icon-created, @@ -38,6 +58,10 @@ svg { fill: $gray-darkest; } + + &.add-border { + @include borderless-status-icon($gray-darkest); + } } .ci-status-icon-manual { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 0b645eb811b..76ebfc22ef7 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -232,3 +232,10 @@ word-break: break-word; max-width: 100%; } + +@mixin borderless-status-icon($color) { + svg { + border: 1px solid $color; + border-radius: 50%; + } +} diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 9e77ea03a24..9874c928604 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -350,7 +350,8 @@ code { } .commit-sha, -.ref-name { +.ref-name, +.pipeline-number { @extend .monospace; font-size: 95%; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 7808f6d3a25..6cfa09b56a7 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -743,6 +743,7 @@ Pipeline Graph */ $stage-hover-bg: $gray-darker; $ci-action-icon-size: 22px; +$ci-action-icon-size-lg: 24px; $pipeline-dropdown-line-height: 20px; $pipeline-dropdown-status-icon-size: 18px; $ci-action-dropdown-button-size: 24px; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index efd730af558..c32049e1b33 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -15,16 +15,38 @@ } } +.mr-widget-heading { + position: relative; + border: 1px solid $border-color; + border-radius: 4px; + + &:not(.deploy-heading)::before { + content: ''; + border-left: 1px solid $theme-gray-200; + position: absolute; + left: 32px; + top: -17px; + height: 16px; + } +} + +.mr-section-container { + border: 1px solid $border-color; + border-radius: $border-radius-default; + border-top: 0; +} + +.mr-widget-heading, +.mr-widget-section, +.mr-widget-footer { + padding: $gl-padding; +} + .mr-state-widget { color: $gl-text-color; - border: 1px solid $border-color; - border-radius: 2px; - line-height: 28px; - .mr-widget-heading, .mr-widget-section, .mr-widget-footer { - padding: $gl-padding; border-top: solid 1px $border-color; } @@ -124,10 +146,17 @@ .ci-widget { color: $gl-text-color; display: flex; + align-items: center; + justify-content: space-between; @include media-breakpoint-down(xs) { flex-wrap: wrap; } + + .ci-widget-content { + display: flex; + align-items: center; + } } .mr-widget-icon { @@ -136,8 +165,6 @@ } .ci-status-icon svg { - width: $status-icon-size; - height: $status-icon-size; margin: 3px 0; position: relative; overflow: visible; @@ -145,8 +172,6 @@ } .mr-widget-pipeline-graph { - padding: 0 4px; - .dropdown-menu { z-index: 300; } @@ -157,7 +182,7 @@ } .normal { - line-height: 28px; + flex: 1; } .capitalize { @@ -168,7 +193,7 @@ @extend .ref-name; color: $gl-text-color; - font-weight: $gl-font-weight-bold; + font-weight: normal; overflow: hidden; word-break: break-all; @@ -192,6 +217,8 @@ } .mr-widget-body { + line-height: 28px; + @include clearfix; &.media > *:first-child { @@ -474,18 +501,66 @@ } } +.merge-request-details .content-block { + border-bottom: 0; +} + .mr-source-target { display: flex; flex-wrap: wrap; - justify-content: space-between; - align-items: center; - background-color: $gray-light; - border-radius: $border-radius-default $border-radius-default 0 0; - padding: $gl-padding / 2 $gl-padding; + border-radius: $border-radius-default; + padding: $gl-padding; + border: 1px solid $border-color; + min-height: 69px; + + @include media-breakpoint-up(md) { + align-items: center; + } .dropdown-toggle .fa { color: $gl-text-color; } + + .git-merge-icon-container { + border: 1px solid $theme-gray-400; + border-radius: 50%; + height: 32px; + width: 32px; + color: $theme-gray-700; + line-height: 28px; + + .ic-git-merge { + vertical-align: middle; + width: 31px; + } + } + + .git-merge-container { + justify-content: space-between; + flex: 1; + flex-direction: row; + align-items: center; + + @include media-breakpoint-down(md) { + flex-direction: column; + align-items: flex-start; + + .branch-actions { + margin-top: 16px; + } + } + + @include media-breakpoint-up(lg) { + .branch-actions { + align-self: center; + } + } + } + + .diverged-commits-count { + color: $gl-text-color-secondary; + font-size: 12px; + } } .card-new-merge-request { @@ -720,13 +795,25 @@ } .deploy-heading { + margin-top: -19px; + border-top-left-radius: 0; + border-top-right-radius: 0; + background-color: $gray-light; + + @include media-breakpoint-up(md) { + padding: $gl-padding-8 $gl-padding; + } + .media-body { min-width: 0; + font-size: 12px; + margin-left: 48px; } } .deploy-body { display: flex; + align-items: center; flex-wrap: wrap; @include media-breakpoint-up(xs) { @@ -734,6 +821,15 @@ white-space: nowrap; } + @include media-breakpoint-down(md) { + flex-direction: column; + align-items: flex-start; + + .deployment-info { + margin-bottom: $gl-padding; + } + } + > *:not(:last-child) { margin-right: .3em; } @@ -741,18 +837,22 @@ svg { vertical-align: text-top; } -} -.deploy-link { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - min-width: 100px; - max-width: 150px; + .deployment-info { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 100px; - @include media-breakpoint-up(xs) { - min-width: 0; - max-width: 100%; + @include media-breakpoint-up(xs) { + min-width: 0; + max-width: 100%; + } + } + + .btn svg { + fill: $theme-gray-700; } } @@ -772,3 +872,33 @@ } } } + +.ci-widget-container { + justify-content: space-between; + flex: 1; + flex-direction: row; + + @include media-breakpoint-down(md) { + flex-direction: column; + + .stage-cell .stage-container { + margin-top: 16px; + } + + .dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu { + transform: initial; + } + } + + .coverage { + font-size: 12px; + color: $theme-gray-700; + line-height: initial; + } + + .mini-pipeline-graph-dropdown-toggle, + .stage-cell .mini-pipeline-graph-dropdown-toggle svg { + height: $ci-action-icon-size-lg; + width: $ci-action-icon-size-lg; + } +} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 52332ac97dd..b68c89c25d8 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -301,6 +301,21 @@ border-bottom: 2px solid $border-color; } } + + //delete when all pipelines are updated to new size + &.mr-widget-pipeline-stages { + + .stage-container { + margin-left: 4px; + } + + &:not(:last-child) { + &::after { + width: 4px; + right: -4px; + top: 11px; + } + } + } } } diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb index 7ac63c914fa..99123fcb3b0 100644 --- a/app/controllers/concerns/preview_markdown.rb +++ b/app/controllers/concerns/preview_markdown.rb @@ -14,6 +14,8 @@ module PreviewMarkdown else {} end + markdown_params[:markdown_engine] = result[:markdown_engine] + render json: { body: view_context.markdown(result[:text], markdown_params), references: { diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ec3a5788ba1..f2abe27f60e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -2,6 +2,7 @@ class ProjectsController < Projects::ApplicationController include IssuableCollections include ExtractsPath include PreviewMarkdown + include SendFileUpload before_action :whitelist_query_limiting, only: [:create] before_action :authenticate_user!, except: [:index, :show, :activity, :refs] @@ -188,9 +189,9 @@ class ProjectsController < Projects::ApplicationController end def download_export - export_project_path = @project.export_project_path - - if export_project_path + if export_project_object_storage? + send_upload(@project.import_export_upload.export_file) + elsif export_project_path send_file export_project_path, disposition: 'attachment' else redirect_to( @@ -265,8 +266,6 @@ class ProjectsController < Projects::ApplicationController render json: options.to_json end - private - # Render project landing depending of which features are available # So if page is not availble in the list it renders the next page # @@ -424,4 +423,12 @@ class ProjectsController < Projects::ApplicationController def whitelist_query_limiting Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440') end + + def export_project_path + @export_project_path ||= @project.export_project_path + end + + def export_project_object_storage? + @project.export_project_object_exists? + end end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 353479776b8..7bbdc798ddd 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -249,6 +249,7 @@ module IssuablesHelper issuableRef: issuable.to_reference, markdownPreviewPath: preview_markdown_path(parent), markdownDocsPath: help_page_path('user/markdown'), + markdownVersion: issuable.cached_markdown_version, issuableTemplates: issuable_templates(issuable), initialTitleHtml: markdown_field(issuable, :title), initialTitleText: issuable.title, diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 39e7a7fd396..cbb971cf8b7 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -107,6 +107,7 @@ module MarkupHelper def markup(file_name, text, context = {}) context[:project] ||= @project + context[:markdown_engine] ||= :redcarpet html = context.delete(:rendered) || markup_unsafe(file_name, text, context) prepare_for_rendering(html, context) end @@ -120,7 +121,8 @@ module MarkupHelper project: @project, project_wiki: @project_wiki, page_slug: wiki_page.slug, - issuable_state_filter_enabled: true + issuable_state_filter_enabled: true, + markdown_engine: :redcarpet } html = diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 3fa2e5452c8..5404ead44f3 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -169,6 +169,7 @@ module NotesHelper registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'), newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'), markdownDocsPath: help_page_path('user/markdown'), + markdownVersion: issuable.cached_markdown_version, quickActionsDocsPath: help_page_path('user/project/quick_actions'), closePath: close_issuable_path(issuable), reopenPath: reopen_issuable_path(issuable), diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/app/mailers/previews/devise_mailer_preview.rb index d6588efc486..d6588efc486 100644 --- a/spec/mailers/previews/devise_mailer_preview.rb +++ b/app/mailers/previews/devise_mailer_preview.rb diff --git a/spec/mailers/previews/email_rejection_mailer_preview.rb b/app/mailers/previews/email_rejection_mailer_preview.rb index 639e8471232..639e8471232 100644 --- a/spec/mailers/previews/email_rejection_mailer_preview.rb +++ b/app/mailers/previews/email_rejection_mailer_preview.rb diff --git a/spec/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb index e32fd0bd120..3615cde8026 100644 --- a/spec/mailers/previews/notify_preview.rb +++ b/app/mailers/previews/notify_preview.rb @@ -153,7 +153,7 @@ class NotifyPreview < ActionMailer::Preview cleanup do note = yield - Notify.public_send(method, user.id, note) + Notify.public_send(method, user.id, note) # rubocop:disable GitlabSecurity/PublicSend end end diff --git a/spec/mailers/previews/repository_check_mailer_preview.rb b/app/mailers/previews/repository_check_mailer_preview.rb index 19d4eab1805..19d4eab1805 100644 --- a/spec/mailers/previews/repository_check_mailer_preview.rb +++ b/app/mailers/previews/repository_check_mailer_preview.rb diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 9f6358cecbe..b05bf909058 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -40,6 +40,18 @@ module CacheMarkdownField end end + class MarkdownEngine + def self.from_version(version = nil) + return :common_mark if version.nil? || version == 0 + + if version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START + :redcarpet + else + :common_mark + end + end + end + def skip_project_check? false end @@ -57,7 +69,7 @@ module CacheMarkdownField # Banzai is less strict about authors, so don't always have an author key context[:author] = self.author if self.respond_to?(:author) - context[:markdown_engine] = markdown_engine + context[:markdown_engine] = MarkdownEngine.from_version(latest_cached_markdown_version) context end @@ -123,14 +135,6 @@ module CacheMarkdownField end end - def markdown_engine - if latest_cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START - :redcarpet - else - :common_mark - end - end - included do cattr_reader :cached_markdown_fields do FieldData.new diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb index d58d7165969..606549b947f 100644 --- a/app/models/concerns/cacheable_attributes.rb +++ b/app/models/concerns/cacheable_attributes.rb @@ -7,7 +7,7 @@ module CacheableAttributes class_methods do def cache_key - "#{name}:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}".freeze + "#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze end # Can be overriden @@ -69,6 +69,6 @@ module CacheableAttributes end def cache! - Rails.cache.write(self.class.cache_key, self) + Rails.cache.write(self.class.cache_key, self, expires_in: 1.minute) end end diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb new file mode 100644 index 00000000000..60d53d6c2c8 --- /dev/null +++ b/app/models/import_export_upload.rb @@ -0,0 +1,13 @@ +class ImportExportUpload < ActiveRecord::Base + include WithUploads + include ObjectStorage::BackgroundMove + + belongs_to :project + + mount_uploader :import_file, ImportExportUploader + mount_uploader :export_file, ImportExportUploader + + def retrieve_upload(_identifier, paths) + Upload.find_by(model: self, path: paths) + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 8f40470de82..770262f6193 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -171,6 +171,7 @@ class Project < ActiveRecord::Base has_one :fork_network, through: :fork_network_member has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project + has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Merge Requests for target project should be removed with it has_many :merge_requests, foreign_key: 'target_project_id' @@ -1712,7 +1713,7 @@ class Project < ActiveRecord::Base :started elsif after_export_in_progress? :after_export_action - elsif export_project_path + elsif export_project_path || export_project_object_exists? :finished else :none @@ -1727,16 +1728,21 @@ class Project < ActiveRecord::Base import_export_shared.after_export_in_progress? end - def remove_exports - return nil unless export_path.present? - - FileUtils.rm_rf(export_path) + def remove_exports(path = export_path) + if path.present? + FileUtils.rm_rf(path) + elsif export_project_object_exists? + import_export_upload.remove_export_file! + import_export_upload.save + end end def remove_exported_project_file - return unless export_project_path.present? + remove_exports(export_project_path) + end - FileUtils.rm_f(export_project_path) + def export_project_object_exists? + Gitlab::ImportExport.object_storage? && import_export_upload&.export_file&.file end def full_path_slug diff --git a/app/models/repository.rb b/app/models/repository.rb index 22a4b73b4b0..7cd600fec5b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -564,7 +564,7 @@ class Repository end def rendered_readme - MarkupHelper.markup_unsafe(readme.name, readme.data, project: project) if readme + MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme end cache_method :rendered_readme diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb index ce0c31b5806..0e1f94a9f61 100644 --- a/app/serializers/note_entity.rb +++ b/app/serializers/note_entity.rb @@ -62,6 +62,8 @@ class NoteEntity < API::Entities::Note expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? } + expose :cached_markdown_version + private def current_user diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb index 74088b970c9..3702c3742ef 100644 --- a/app/services/import_export_clean_up_service.rb +++ b/app/services/import_export_clean_up_service.rb @@ -10,7 +10,9 @@ class ImportExportCleanUpService def execute Gitlab::Metrics.measure(:import_export_clean_up) do - next unless File.directory?(path) + clean_up_export_object_files + + break unless File.directory?(path) clean_up_export_files end @@ -21,4 +23,11 @@ class ImportExportCleanUpService def clean_up_export_files Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete)) end + + def clean_up_export_object_files + ImportExportUpload.where('updated_at < ?', mmin.minutes.ago).each do |upload| + upload.remove_export_file! + upload.save! + end + end end diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb index 4ee2c1796bd..6da4d9523cf 100644 --- a/app/services/preview_markdown_service.rb +++ b/app/services/preview_markdown_service.rb @@ -6,7 +6,8 @@ class PreviewMarkdownService < BaseService success( text: text, users: users, - commands: commands.join(' ') + commands: commands.join(' '), + markdown_engine: markdown_engine ) end @@ -42,4 +43,8 @@ class PreviewMarkdownService < BaseService def commands_target_id params[:quick_actions_target_id] end + + def markdown_engine + CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i) + end end diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb new file mode 100644 index 00000000000..213ac5c8011 --- /dev/null +++ b/app/uploaders/import_export_uploader.rb @@ -0,0 +1,15 @@ +class ImportExportUploader < AttachmentUploader + EXTENSION_WHITELIST = %w[tar.gz].freeze + + def extension_whitelist + EXTENSION_WHITELIST + end + + def move_to_store + true + end + + def move_to_cache + false + end +end diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index d8e32651b36..3aa8eb18bf3 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -61,7 +61,9 @@ - if header_link?(:sign_in) %li.nav-item %div - = link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in' + - sign_in_text = allow_signup? ? 'Sign in / Register' : 'Sign in' + = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in' + %button.navbar-toggler.d-block.d-sm-none{ type: 'button' } %span.sr-only Toggle navigation diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml index f4d4888bd15..aa980da7e95 100644 --- a/app/views/projects/_export.html.haml +++ b/app/views/projects/_export.html.haml @@ -31,7 +31,7 @@ %li Any encrypted tokens %p Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page. - - if project.export_project_path + - if project.export_status == :finished = link_to 'Download export', download_export_project_path(project), rel: 'nofollow', download: '', method: :get, class: "btn btn-default" = link_to 'Generate new export', generate_new_export_project_path(project), diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 2c5ffd85372..1e4e9450ffa 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,2 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @issue], + html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' }, + data: { markdown_version: @issue.cached_markdown_version } do |f| = render 'shared/issuable/form', f: f, issuable: @issue diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 179c1fcc684..5a59f956cb5 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,2 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], + html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' }, + data: { markdown_version: @merge_request.cached_markdown_version } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 4cc59718715..ace094a671a 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -1,4 +1,6 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @milestone], + html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'}, + data: { markdown_version: @milestone.cached_markdown_version } do |f| = form_errors(@milestone) .row .col-md-6 diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index d6f758608a0..8093cc2c2d7 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -11,7 +11,9 @@ %strong= @tag.name - = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'common-note-form release-form js-quick-submit' }) do |f| + = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), + html: { class: 'common-note-form release-form js-quick-submit' }, + data: { markdown_version: @release.cached_markdown_version }) do |f| = render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…" = render 'shared/notes/hints' diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 26fe1de31fe..de692466fe5 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,7 +1,9 @@ - commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}") - commit_message = commit_message % { page_title: @page.title } -= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, + html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' }, + data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f| = form_errors(@page) - if @page.persisted? diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml index 532712ee6d1..f3b56df0c96 100644 --- a/app/views/shared/hook_logs/_content.html.haml +++ b/app/views/shared/hook_logs/_content.html.haml @@ -30,7 +30,7 @@ %h5 Request body: %pre - :plain + :escaped #{JSON.pretty_generate(hook_log.request_data)} %h5 Response headers: %pre @@ -40,5 +40,5 @@ %h5 Response body: %pre - :plain + :escaped #{hook_log.response_body} diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index d4e8f30e458..f5464058bc0 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -52,7 +52,7 @@ .note-text.md = markdown_field(note, :note) = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago') - .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } } + .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore, markdown_version: note.cached_markdown_version } } #{note.note} - if note_editable = render 'shared/notes/edit', note: note diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 858adc8be37..5e5c050d5c3 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -2,7 +2,9 @@ = page_specific_javascript_tag('lib/ace.js') .snippet-form-holder - = form_for @snippet, url: url, html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" } do |f| + = form_for @snippet, url: url, + html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" }, + data: { markdown_version: @snippet.cached_markdown_version } do |f| = form_errors(@snippet) .form-group.row diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb index 051382a08a9..07559ea479b 100644 --- a/app/workers/repository_check/batch_worker.rb +++ b/app/workers/repository_check/batch_worker.rb @@ -4,9 +4,11 @@ module RepositoryCheck class BatchWorker include ApplicationWorker include RepositoryCheckQueue + include ExclusiveLeaseGuard RUN_TIME = 3600 BATCH_SIZE = 10_000 + LEASE_TIMEOUT = 1.hour attr_reader :shard_name @@ -16,6 +18,20 @@ module RepositoryCheck return unless Gitlab::CurrentSettings.repository_checks_enabled return unless Gitlab::ShardHealthCache.healthy_shard?(shard_name) + try_obtain_lease do + perform_repository_checks + end + end + + def lease_timeout + LEASE_TIMEOUT + end + + def lease_key + "repository_check_batch_worker:#{shard_name}" + end + + def perform_repository_checks start = Time.now # This loop will break after a little more than one hour ('a little @@ -26,7 +42,7 @@ module RepositoryCheck project_ids.each do |project_id| break if Time.now - start >= RUN_TIME - next unless try_obtain_lease(project_id) + next unless try_obtain_lease_for_project(project_id) SingleRepositoryWorker.new.perform(project_id) end @@ -60,7 +76,7 @@ module RepositoryCheck Project.where(repository_storage: shard_name) end - def try_obtain_lease(id) + def try_obtain_lease_for_project(id) # Use a 24-hour timeout because on servers/projects where 'git fsck' is # super slow we definitely do not want to run it twice in parallel. Gitlab::ExclusiveLease.new( diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb index 891a273afd7..96634f09a15 100644 --- a/app/workers/repository_check/dispatch_worker.rb +++ b/app/workers/repository_check/dispatch_worker.rb @@ -3,13 +3,22 @@ module RepositoryCheck include ApplicationWorker include CronjobQueue include ::EachShardWorker + include ExclusiveLeaseGuard + + LEASE_TIMEOUT = 1.hour def perform return unless Gitlab::CurrentSettings.repository_checks_enabled - each_eligible_shard do |shard_name| - RepositoryCheck::BatchWorker.perform_async(shard_name) + try_obtain_lease do + each_eligible_shard do |shard_name| + RepositoryCheck::BatchWorker.perform_async(shard_name) + end end end + + def lease_timeout + LEASE_TIMEOUT + end end end diff --git a/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml b/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml new file mode 100644 index 00000000000..908c7a238fd --- /dev/null +++ b/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml @@ -0,0 +1,5 @@ +--- +title: Add Object Storage to project export +merge_request: 20105 +author: +type: added diff --git a/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml b/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml new file mode 100644 index 00000000000..36a4b5f754d --- /dev/null +++ b/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml @@ -0,0 +1,5 @@ +--- +title: Resolve compatibility issues with node 6 +merge_request: 20461 +author: +type: fixed diff --git a/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml b/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml new file mode 100644 index 00000000000..f4267582f89 --- /dev/null +++ b/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml @@ -0,0 +1,6 @@ +--- +title: Stop relying on migrations in the CacheableAttributes cache key and cache attributes + for 1 minute instead +merge_request: 20389 +author: +type: fixed diff --git a/changelogs/unreleased/48976-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-gitlab-backgroundmigration-fixcrossprojectlabellinks-namespace.yml b/changelogs/unreleased/48976-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-gitlab-backgroundmigration-fixcrossprojectlabellinks-namespace.yml new file mode 100644 index 00000000000..e95536b213c --- /dev/null +++ b/changelogs/unreleased/48976-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-gitlab-backgroundmigration-fixcrossprojectlabellinks-namespace.yml @@ -0,0 +1,6 @@ +--- +title: "[Rails5] Fix 'Invalid single-table inheritance type: Group is not a subclass + of Gitlab::BackgroundMigration::FixCrossProjectLabelLinks::Namespace'" +merge_request: 20462 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml b/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml new file mode 100644 index 00000000000..f786d9e2235 --- /dev/null +++ b/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml @@ -0,0 +1,5 @@ +--- +title: Fixes base command used in Helm installations +merge_request: 20471 +author: +type: fixed diff --git a/changelogs/unreleased/fix-search-bar.yml b/changelogs/unreleased/fix-search-bar.yml new file mode 100644 index 00000000000..d4c0c1efddf --- /dev/null +++ b/changelogs/unreleased/fix-search-bar.yml @@ -0,0 +1,5 @@ +--- +title: Fix search bar text input alignment +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/jprovazn-upload-symlink.yml b/changelogs/unreleased/jprovazn-upload-symlink.yml new file mode 100644 index 00000000000..265791d332f --- /dev/null +++ b/changelogs/unreleased/jprovazn-upload-symlink.yml @@ -0,0 +1,5 @@ +--- +title: Add /uploads subdirectory to allowed upload paths. +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/perf-wiki-pattern-once.yml b/changelogs/unreleased/perf-wiki-pattern-once.yml new file mode 100644 index 00000000000..fb4085a06ae --- /dev/null +++ b/changelogs/unreleased/perf-wiki-pattern-once.yml @@ -0,0 +1,5 @@ +--- +title: Improve render performance of large wiki pages +merge_request: 20465 +author: Peter Leitzen +type: performance diff --git a/changelogs/unreleased/rails5-fix-48977.yml b/changelogs/unreleased/rails5-fix-48977.yml new file mode 100644 index 00000000000..bfd86f20e24 --- /dev/null +++ b/changelogs/unreleased/rails5-fix-48977.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 fix mysql milliseconds problem in specs +merge_request: 20464 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml new file mode 100644 index 00000000000..afd9865ee45 --- /dev/null +++ b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 mysql fix milliseconds problem in pull request importer spec +merge_request: 20475 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/rails5-update-gemfile-lock.yml b/changelogs/unreleased/rails5-update-gemfile-lock.yml new file mode 100644 index 00000000000..58931587fff --- /dev/null +++ b/changelogs/unreleased/rails5-update-gemfile-lock.yml @@ -0,0 +1,5 @@ +--- +title: Update Gemfile.rails5.lock with latest Gemfile.lock changes +merge_request: 20466 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/sh-fix-issue-47797-ce.yml b/changelogs/unreleased/sh-fix-issue-47797-ce.yml new file mode 100644 index 00000000000..456d96acacb --- /dev/null +++ b/changelogs/unreleased/sh-fix-issue-47797-ce.yml @@ -0,0 +1,5 @@ +--- +title: Fix handling of annotated tags when Gitaly is not in use +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml new file mode 100644 index 00000000000..39e10121507 --- /dev/null +++ b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml @@ -0,0 +1,5 @@ +--- +title: 'Update hamlit to fix ruby 2.5 incompatibilities, fixes #42045' +merge_request: +author: Matthew Dawson +type: fixed diff --git a/config/environments/development.rb b/config/environments/development.rb index 45a8c1add3e..23790b84e3c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -39,7 +39,7 @@ Rails.application.configure do config.action_mailer.delivery_method = :letter_opener_web # Don't make a mess when bootstrapping a development environment config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') - config.action_mailer.preview_path = 'spec/mailers/previews' + config.action_mailer.preview_path = 'app/mailers/previews' config.eager_load = false diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index e0779112850..ab109f5d04f 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -160,6 +160,9 @@ production: &base # aws_access_key_id: AWS_ACCESS_KEY_ID # aws_secret_access_key: AWS_SECRET_ACCESS_KEY # region: us-east-1 + # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4. + # endpoint: 'https://s3.amazonaws.com' # default: nil - Useful for S3 compliant services such as DigitalOcean Spaces + ## Git LFS lfs: @@ -180,6 +183,7 @@ production: &base # Use the following options to configure an AWS compatible host # host: 'localhost' # default: s3.amazonaws.com # endpoint: 'http://127.0.0.1:9000' # default: nil + # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4. # path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object' ## Uploads (attachments, avatars, etc...) @@ -197,6 +201,7 @@ production: &base provider: AWS aws_access_key_id: AWS_ACCESS_KEY_ID aws_secret_access_key: AWS_SECRET_ACCESS_KEY + aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4. region: us-east-1 # host: 'localhost' # default: s3.amazonaws.com # endpoint: 'http://127.0.0.1:9000' # default: nil diff --git a/db/migrate/20180625113853_create_import_export_uploads.rb b/db/migrate/20180625113853_create_import_export_uploads.rb new file mode 100644 index 00000000000..be42304b0ae --- /dev/null +++ b/db/migrate/20180625113853_create_import_export_uploads.rb @@ -0,0 +1,16 @@ +class CreateImportExportUploads < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :import_export_uploads do |t| + t.datetime_with_timezone :updated_at, null: false + + t.references :project, index: true, foreign_key: { on_delete: :cascade }, unique: true + + t.text :import_file + t.text :export_file + end + + add_index :import_export_uploads, :updated_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 8880ecf4f5c..1898dfc6022 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -949,6 +949,16 @@ ActiveRecord::Schema.define(version: 20180702120647) do add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree + create_table "import_export_uploads", force: :cascade do |t| + t.datetime_with_timezone "updated_at", null: false + t.integer "project_id" + t.text "import_file" + t.text "export_file" + end + + add_index "import_export_uploads", ["project_id"], name: "index_import_export_uploads_on_project_id", using: :btree + add_index "import_export_uploads", ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree + create_table "internal_ids", id: :bigserial, force: :cascade do |t| t.integer "project_id" t.integer "usage", null: false @@ -2252,6 +2262,7 @@ ActiveRecord::Schema.define(version: 20180702120647) do add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify add_foreign_key "gpg_signatures", "projects", on_delete: :cascade add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade + add_foreign_key "import_export_uploads", "projects", on_delete: :cascade add_foreign_key "internal_ids", "namespaces", name: "fk_162941d509", on_delete: :cascade add_foreign_key "internal_ids", "projects", on_delete: :cascade add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 9b1297ca4ba..c0a281382a5 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -49,8 +49,8 @@ supporting custom domains a secondary IP is not needed. Before proceeding with the Pages configuration, you will need to: -1. Have a separate domain under which the GitLab Pages will be served. In this - document we assume that to be `example.io`. +1. Have an exclusive root domain for serving GitLab Pages. Note that you cannot + use a subdomain of your GitLab's instance domain. 1. Configure a **wildcard DNS record**. 1. (Optional) Have a **wildcard certificate** for that domain if you decide to serve Pages under HTTPS. diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md index ecc4ac6b29b..7bd765a35e0 100644 --- a/doc/administration/raketasks/project_import_export.md +++ b/doc/administration/raketasks/project_import_export.md @@ -30,5 +30,12 @@ sudo gitlab-rake gitlab:import_export:data bundle exec rake gitlab:import_export:data RAILS_ENV=production ``` +In order to enable Object Storage on the Export, you can use the [feature flag][feature-flags]: + +``` +import_export_object_storage +``` + [ce-3050]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3050 +[feature-flags]: https://docs.gitlab.com/ee/api/features.html [tmp]: ../../development/shared_files.md diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 2f991d86614..9f6476edc34 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -64,7 +64,7 @@ future GitLab releases.** | **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` | | **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | | **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry | -| **CI_JOB_URL** | 11.0 | 0.5 | Job details URL | +| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL | | **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository | | **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | | **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used | @@ -82,7 +82,7 @@ future GitLab releases.** | **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built | | **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name | | **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. | -| **CI_PIPELINE_URL** | 11.0 | 0.5 | Pipeline details URL | +| **CI_PIPELINE_URL** | 11.1 | 0.5 | Pipeline details URL | | **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project | | **CI_PROJECT_VISIBILITY** | 10.3 | all | The project visibility (internal, private, public) | | **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry | diff --git a/doc/development/emails.md b/doc/development/emails.md index 73cac82caf0..35ada35babe 100644 --- a/doc/development/emails.md +++ b/doc/development/emails.md @@ -10,12 +10,12 @@ To view rendered emails "sent" in your development instance, visit Rails provides a way to preview our mailer templates in HTML and plaintext using dummy data. -The previews live in [`spec/mailers/previews`][previews] and can be viewed at +The previews live in [`app/mailers/previews`][previews] and can be viewed at [`/rails/mailers`](http://localhost:3000/rails/mailers). See the [Rails guides] for more info. -[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/spec/mailers/previews +[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/mailers/previews [Rails guides]: http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails ## Incoming email diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md index f7d703b8f0b..6323275426f 100644 --- a/doc/development/i18n/externalization.md +++ b/doc/development/i18n/externalization.md @@ -281,7 +281,7 @@ Now that the new content is marked for translation, we need to update the PO files with the following command: ```sh -bin/rake gettext:find +bin/rake gettext:regenerate ``` This command will update the `locale/gitlab.pot` file with the newly externalized @@ -292,16 +292,6 @@ file in. Once the changes are on master, they will be picked up by If there are merge conflicts in the `gitlab.pot` file, you can delete the file and regenerate it using the same command. Confirm that you are not deleting any strings accidentally by looking over the diff. -The command also updates the translation files for each language: `locale/*/gitlab.po` -These changes can be discarded, the language files will be updated by Crowdin -automatically. - -Discard all of them at once like this: - -```sh -git checkout locale/*/gitlab.po -``` - ### Validating PO files To make sure we keep our translation files up to date, there's a linter that is diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index 9d0d7348df9..ca8ebcdc984 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -11,6 +11,7 @@ are very appreciative of the work done by translators and proofreaders! - Chinese Traditional - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Weizhe Ding - [GitLab](https://gitlab.com/d.weizhe), [Crowdin](https://crowdin.com/profile/d.weizhe) + - Yi-Jyun Pan - [GitLab](https://gitlab.com/pan93412), [Crowdin](https://crowdin.com/profile/pan93412) - Chinese Traditional, Hong Kong - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Dutch diff --git a/doc/development/licensing.md b/doc/development/licensing.md index c06bc0d4731..ddaf636a742 100644 --- a/doc/development/licensing.md +++ b/doc/development/licensing.md @@ -60,7 +60,7 @@ Libraries with the following licenses are acceptable for use: ## Unacceptable Licenses -Libraries with the following licenses are unacceptable for use: +Libraries with the following licenses require legal approval for use: - [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects. - [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects. @@ -68,6 +68,26 @@ Libraries with the following licenses are unacceptable for use: - [Facebook BSD + PATENTS][Facebook]: is a 3-clause BSD license with a patent grant that has been deemed [Category X][x-list] by the Apache foundation. - [WTFPL][WTFPL]: is a public domain dedication [rejected by the OSI (3.2)][WTFPL-OSI]. Also has a strong language which is not in accordance with our diversity policy. +## GPL Cooperation Commitment + +Before filing or continuing to prosecute any legal proceeding or claim (other than a Defensive Action) arising from termination of a Covered License, GitLab commits to extend to the person or entity (“you”) accused of violating the Covered License the following provisions regarding cure and reinstatement, taken from GPL version 3. As used here, the term ‘this License’ refers to the specific Covered License being enforced. + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +GitLab intends this Commitment to be irrevocable, and binding and enforceable against GitLab and assignees of or successors to GitLab’s copyrights. + +GitLab may modify this Commitment by publishing a new edition on this page or a successor location. + +Definitions + +‘Covered License’ means the GNU General Public License, version 2 (GPLv2), the GNU Lesser General Public License, version 2.1 (LGPLv2.1), or the GNU Library General Public License, version 2 (LGPLv2), all as published by the Free Software Foundation. + +‘Defensive Action’ means a legal proceeding or claim that GitLab brings against you in response to a prior proceeding or claim initiated by you or your affiliate. + +GitLab means GitLab Inc. and its affiliates and subsidiaries. + ## Requesting Approval for Licenses Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please email `legal@gitlab.com` with the details. After a decision has been made, the original requestor is responsible for updating this document. diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md index 10e8059756d..dd8d95a3bca 100644 --- a/doc/gitlab-basics/create-project.md +++ b/doc/gitlab-basics/create-project.md @@ -30,6 +30,9 @@ idea to fill this in. - Changing the **Visibility Level** modifies the project's [viewing and access rights](../public_access/public_access.md) for users. + - Selecting the **Initialize repository with a README** option creates a + README so that the Git repository is initialized, has a default branch and + can be cloned. 1. Click **Create project**. diff --git a/doc/gitlab-basics/img/create_new_project_info.png b/doc/gitlab-basics/img/create_new_project_info.png Binary files differindex ce4f7d1204b..b4119dc046a 100644 --- a/doc/gitlab-basics/img/create_new_project_info.png +++ b/doc/gitlab-basics/img/create_new_project_info.png diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 2a14c0397ca..9094d1f2419 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -1,5 +1,8 @@ # Integrate your GitLab server with Bitbucket +NOTE: **Note:** +You need to [enable OmniAuth](omniauth.md) in order to use this. + Import projects from Bitbucket.org and login to your GitLab instance with your Bitbucket.org account. @@ -76,13 +79,13 @@ you to use. sudo -u git -H editor /home/git/gitlab/config/gitlab.yml ``` -1. Follow the [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) - for initial settings. 1. Add the Bitbucket provider configuration: For Omnibus packages: ```ruby + gitlab_rails['omniauth_enabled'] = true + gitlab_rails['omniauth_providers'] = [ { "name" => "bitbucket", @@ -96,10 +99,13 @@ you to use. For installations from source: ```yaml - - { name: 'bitbucket', - app_id: 'BITBUCKET_APP_KEY', - app_secret: 'BITBUCKET_APP_SECRET', - url: 'https://bitbucket.org/' } + omniauth: + enabled: true + providers: + - { name: 'bitbucket', + app_id: 'BITBUCKET_APP_KEY', + app_secret: 'BITBUCKET_APP_SECRET', + url: 'https://bitbucket.org/' } ``` --- @@ -121,6 +127,9 @@ well, the user will be returned to GitLab and will be signed in. Once the above configuration is set up, you can use Bitbucket to sign into GitLab and [start importing your projects][bb-import]. +If you don't want to enable signing in with Bitbucket but just want to import +projects from Bitbucket, you could [disable it in the admin panel](omniauth.md#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources). + [init-oauth]: omniauth.md#initial-omniauth-configuration [bb-import]: ../workflow/importing/import_projects_from_bitbucket.md [bb-old]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/doc/integration/bitbucket.md diff --git a/doc/integration/saml.md b/doc/integration/saml.md index db06efdae53..25f396bc9c4 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -1,5 +1,8 @@ # SAML OmniAuth Provider +NOTE: **Note:** +You need to [enable OmniAuth](omniauth.md) in order to use this. + GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as Microsoft ADFS to authenticate users. @@ -15,33 +18,33 @@ in your SAML IdP: For omnibus package: ```sh - sudo editor /etc/gitlab/gitlab.rb + sudo editor /etc/gitlab/gitlab.rb ``` For installations from source: ```sh - cd /home/git/gitlab + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml + sudo -u git -H editor config/gitlab.yml ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) - for initial settings. - 1. To allow your users to use SAML to sign up without having to manually create an account first, don't forget to add the following values to your configuration: For omnibus package: ```ruby - gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] - gitlab_rails['omniauth_block_auto_created_users'] = false + gitlab_rails['omniauth_enabled'] = true + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] + gitlab_rails['omniauth_block_auto_created_users'] = false ``` For installations from source: ```yaml + omniauth: + enabled: true allow_single_sign_on: ["saml"] block_auto_created_users: false ``` @@ -52,13 +55,13 @@ in your SAML IdP: For omnibus package: ```ruby - gitlab_rails['omniauth_auto_link_saml_user'] = true + gitlab_rails['omniauth_auto_link_saml_user'] = true ``` For installations from source: ```yaml - auto_link_saml_user: true + auto_link_saml_user: true ``` 1. Add the provider configuration: @@ -66,35 +69,37 @@ in your SAML IdP: For omnibus package: ```ruby - gitlab_rails['omniauth_providers'] = [ - { - name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://login.example.com/idp', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' - }, - label: 'Company Login' # optional label for SAML login button, defaults to "Saml" - } - ] - ``` - - For installations from source: - - ```yaml - - { - name: 'saml', - args: { + gitlab_rails['omniauth_providers'] = [ + { + name: 'saml', + args: { assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' }, - label: 'Company Login' # optional label for SAML login button, defaults to "Saml" - } + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" + } + ] + ``` + + For installations from source: + + ```yaml + omniauth: + providers: + - { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://login.example.com/idp', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' + }, + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" + } ``` 1. Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint @@ -140,8 +145,8 @@ This setting is only available on GitLab 8.7 and above. SAML login includes support for automatically identifying whether a user should be considered an [external](../user/permissions.md) user based on the user's group membership in the SAML identity provider. This feature **does not** allow you to -automatically add users to GitLab [Groups](../user/group/index.md), it simply -allows you to mark users as External if they are members of certain groups in the +automatically add users to GitLab [Groups](../user/group/index.md), it simply +allows you to mark users as External if they are members of certain groups in the Identity Provider. ### Requirements @@ -189,28 +194,28 @@ If you want some SAML authentication methods to count as 2FA on a per session ba 1. Edit `/etc/gitlab/gitlab.rb`: ```ruby - gitlab_rails['omniauth_providers'] = [ - { - name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://login.example.com/idp', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - upstream_two_factor_authn_contexts: - %w( - urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport - urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS - urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN - ) - - }, - label: 'Company Login' # optional label for SAML login button, defaults to "Saml" - } - ] + gitlab_rails['omniauth_providers'] = [ + { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://login.example.com/idp', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + upstream_two_factor_authn_contexts: + %w( + urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport + urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS + urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN + ) + + }, + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" + } + ] ``` - + 1. Save the file and [reconfigure][] GitLab for the changes to take effect. --- @@ -218,40 +223,41 @@ If you want some SAML authentication methods to count as 2FA on a per session ba **For installations from source:** 1. Edit `config/gitlab.yml`: - - ```yaml - - { - name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://login.example.com/idp', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - upstream_two_factor_authn_contexts: - [ - 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN' - ] - - }, - label: 'Company Login' # optional label for SAML login button, defaults to "Saml" - } + + ```yaml + omniauth: + providers: + - { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://login.example.com/idp', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + upstream_two_factor_authn_contexts: + [ + 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport', + 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS', + 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN' + ] + }, + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" + } ``` - + 1. Save the file and [restart GitLab][] for the changes ot take effect - + In addition to the changes in GitLab, make sure that your Idp is returning the `AuthnContext`. For example: ```xml - <saml:AuthnStatement> - <saml:AuthnContext> - <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef> - </saml:AuthnContext> - </saml:AuthnStatement> +<saml:AuthnStatement> + <saml:AuthnContext> + <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef> + </saml:AuthnContext> +</saml:AuthnStatement> ``` ## Customization diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index e8f4c73120c..81ee7338e4e 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -30,6 +30,12 @@ in users. Any logged in user will have [Guest](../user/permissions.md) permissions on the repository. +### Private projects + +Private projects can only be cloned and viewed by project members, and +they will only appear to project members on the public access directory +(`https://gitlab.example.com/public`). + ### How to change project visibility 1. Go to your project's **Settings** diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 8e87c896a72..bd199b55a61 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -9,7 +9,7 @@ > For the best result, we encourage you to check this document out as rendered by GitLab: [markdown.md] -_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content and Markdown files `.md` in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._ +_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content, wiki pages and Markdown files (`.md`) in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._ _Where there are significant differences, we will try to call them out in this document._ @@ -22,7 +22,7 @@ You can use GFM in the following areas: - merge requests - milestones - snippets (the snippet must be named with a `.md` extension) -- wiki pages +- wiki pages (currently only rendered by Redcarpet) - markdown documents inside the repository (currently only rendered by Redcarpet) You can also use other rich text files in GitLab. You might have to install a diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index b25b09f7b1f..baa6efe50d5 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -153,6 +153,13 @@ GitLab provides a one-click install for various applications which will be added directly to your configured cluster. Those applications are needed for [Review Apps](../../../ci/review_apps/index.md) and [deployments](../../../ci/environments.md). +NOTE: **Note:** +The applications will be installed in a dedicated namespace called +`gitlab-managed-apps`. In case you have added an existing Kubernetes cluster +with Tiller already installed, you should be careful as GitLab cannot +detect it. By installing it via the applications will result into having it +twice, which can lead to confusion during deployments. + | Application | GitLab version | Description | | ----------- | :------------: | ----------- | | [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | @@ -206,7 +213,7 @@ kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalanc > **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run: > ```bash -> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"`. +> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}". > ``` The output is the external IP address of your cluster. This information can then diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb index 5ef4e9d530c..15c57a2fc02 100644 --- a/lib/api/project_export.rb +++ b/lib/api/project_export.rb @@ -23,9 +23,13 @@ module API get ':id/export/download' do path = user_project.export_project_path - render_api_error!('404 Not found or has expired', 404) unless path - - present_disk_file!(path, File.basename(path), 'application/gzip') + if path + present_disk_file!(path, File.basename(path), 'application/gzip') + elsif user_project.export_project_object_exists? + present_carrierwave_file!(user_project.import_export_upload.export_file) + else + render_api_error!('404 Not found or has expired', 404) + end end desc 'Start export' do diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 60a12dca9d3..b39b11009b3 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -100,6 +100,11 @@ module Banzai ref_pattern = object_class.reference_pattern link_pattern = object_class.link_reference_pattern + # Compile often used regexps only once outside of the loop + ref_pattern_anchor = /\A#{ref_pattern}\z/ + link_pattern_start = /\A#{link_pattern}/ + link_pattern_anchor = /\A#{link_pattern}\z/ + nodes.each do |node| if text_node?(node) && ref_pattern replace_text_when_pattern_matches(node, ref_pattern) do |content| @@ -108,7 +113,7 @@ module Banzai elsif element_node?(node) yield_valid_link(node) do |link, inner_html| - if ref_pattern && link =~ /\A#{ref_pattern}\z/ + if ref_pattern && link =~ ref_pattern_anchor replace_link_node_with_href(node, link) do object_link_filter(link, ref_pattern, link_content: inner_html) end @@ -118,7 +123,7 @@ module Banzai next unless link_pattern - if link == inner_html && inner_html =~ /\A#{link_pattern}/ + if link == inner_html && inner_html =~ link_pattern_start replace_link_node_with_text(node, link) do object_link_filter(inner_html, link_pattern, link_reference: true) end @@ -126,7 +131,7 @@ module Banzai next end - if link =~ /\A#{link_pattern}\z/ + if link =~ link_pattern_anchor replace_link_node_with_href(node, link) do object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true) end diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index a9b04c183ad..e8dbde176ef 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -139,6 +139,11 @@ module ExtractsPath def lfs_blob_ids blob_ids = tree.blobs.map(&:id) + + # When current endpoint is a Blob then `tree.blobs` will be empty, it means we need to analyze + # the current Blob in order to determine if it's a LFS object + blob_ids = Array.wrap(@repo.blob_at(@commit.id, @path)&.id) if blob_ids.empty? # rubocop:disable Gitlab/ModuleWithInstanceVariables + @lfs_blob_ids = Gitlab::Git::Blob.batch_lfs_pointers(@project.repository, blob_ids).map(&:id) # rubocop:disable Gitlab/ModuleWithInstanceVariables end diff --git a/lib/gitlab.rb b/lib/gitlab.rb index b9a148f35bf..ab6b609d099 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -9,10 +9,6 @@ module Gitlab Settings end - def self.migrations_hash - @_migrations_hash ||= Digest::MD5.hexdigest(ActiveRecord::Migrator.get_all_versions.to_s) - end - def self.revision @_revision ||= begin if File.exist?(root.join("REVISION")) diff --git a/lib/gitlab/background_migration/fix_cross_project_label_links.rb b/lib/gitlab/background_migration/fix_cross_project_label_links.rb index fa68ba5cca7..0a12401c35f 100644 --- a/lib/gitlab/background_migration/fix_cross_project_label_links.rb +++ b/lib/gitlab/background_migration/fix_cross_project_label_links.rb @@ -11,6 +11,7 @@ module Gitlab end class Label < ActiveRecord::Base + self.inheritance_column = :_type_disabled self.table_name = 'labels' end @@ -27,6 +28,7 @@ module Gitlab end class Namespace < ActiveRecord::Base + self.inheritance_column = :_type_disabled self.table_name = 'namespaces' def self.groups_with_descendants_ids(start_id, stop_id) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 4ad106e7b0a..872e70f9a5d 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -42,6 +42,21 @@ module Gitlab !self.read_only? end + # check whether the underlying database is in read-only mode + def self.db_read_only? + if postgresql? + ActiveRecord::Base.connection.execute('SELECT pg_is_in_recovery()') + .first + .fetch('pg_is_in_recovery') == 't' + else + false + end + end + + def self.db_read_write? + !self.db_read_only? + end + def self.version @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] end diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb index 604bb11e712..96fa94d5790 100644 --- a/lib/gitlab/git/blob.rb +++ b/lib/gitlab/git/blob.rb @@ -50,13 +50,7 @@ module Gitlab end def raw(repository, sha) - Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled| - if is_enabled - repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE) - else - rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE) - end - end + repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE) end # Returns an array of Blob instances, specified in blob_references as @@ -207,16 +201,7 @@ module Gitlab return if @loaded_all_data - @data = Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled| - begin - if is_enabled - repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data - else - repository.lookup(id).content - end - end - end - + @data = repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data @loaded_all_data = true @loaded_size = @data.bytesize end diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index 36d56e411d8..4e2d817d12c 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -164,13 +164,8 @@ module Gitlab # relation to each other. The last 10 commits for a branch for example, # should go through .where def batch_by_oid(repo, oids) - repo.gitaly_migrate(:list_commits_by_oid, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - repo.gitaly_commit_client.list_commits_by_oid(oids) - else - oids.map { |oid| find(repo, oid) }.compact - end + repo.wrapped_gitaly_errors do + repo.gitaly_commit_client.list_commits_by_oid(oids) end end @@ -289,14 +284,7 @@ module Gitlab def deltas @deltas ||= begin - deltas = Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled| - if is_enabled - @repository.gitaly_commit_client.commit_deltas(self) - else - rugged_diff_from_parent.each_delta - end - end - + deltas = @repository.gitaly_commit_client.commit_deltas(self) deltas.map { |delta| Gitlab::Git::Diff.new(delta) } end end diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb index 6a601561c2a..219c69893ad 100644 --- a/lib/gitlab/git/diff_collection.rb +++ b/lib/gitlab/git/diff_collection.rb @@ -42,12 +42,10 @@ module Gitlab return if @overflow return if @iterator.nil? - Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled| - if is_enabled && @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher) - each_gitaly_patch(&block) - else - each_rugged_patch(&block) - end + if @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher) + each_gitaly_patch(&block) + else + each_serialized_patch(&block) end @populated = true @@ -118,7 +116,7 @@ module Gitlab end end - def each_rugged_patch + def each_serialized_patch i = @array.length @iterator.each do |raw| diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 29b3663a52a..25b0ab577da 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -439,31 +439,11 @@ module Gitlab raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}") end - gitaly_migrate(:find_commits) do |is_enabled| - if is_enabled - gitaly_commit_client.find_commits(options) - else - raw_log(options).map { |c| Commit.decorate(self, c) } - end + wrapped_gitaly_errors do + gitaly_commit_client.find_commits(options) end end - # Used in gitaly-ruby - def raw_log(options) - sha = - unless options[:all] - actual_ref = options[:ref] || root_ref - begin - sha_from_ref(actual_ref) - rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError - # Return an empty array if the ref wasn't found - return [] - end - end - - log_by_shell(sha, options) - end - def count_commits(options) options = process_count_commits_options(options.dup) @@ -555,13 +535,7 @@ module Gitlab # diff options. The +options+ hash can also include :break_rewrites to # split larger rewrites into delete/add pairs. def diff(from, to, options = {}, *paths) - iterator = gitaly_migrate(:diff_between) do |is_enabled| - if is_enabled - gitaly_commit_client.diff(from, to, options.merge(paths: paths)) - else - diff_patches(from, to, options, *paths) - end - end + iterator = gitaly_commit_client.diff(from, to, options.merge(paths: paths)) Gitlab::Git::DiffCollection.new(iterator, options) end @@ -651,7 +625,13 @@ module Gitlab end def update_branch(branch_name, user:, newrev:, oldrev:) - OperationService.new(user, self).update_branch(branch_name, newrev, oldrev) + gitaly_migrate(:operation_user_update_branch) do |is_enabled| + if is_enabled + gitaly_operations_client.user_update_branch(branch_name, user, newrev, oldrev) + else + OperationService.new(user, self).update_branch(branch_name, newrev, oldrev) + end + end end def rm_branch(branch_name, user:) @@ -1229,12 +1209,10 @@ module Gitlab end def find_commits_by_message(query, ref, path, limit, offset) - gitaly_migrate(:commits_by_message) do |is_enabled| - if is_enabled - find_commits_by_message_by_gitaly(query, ref, path, limit, offset) - else - find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) - end + wrapped_gitaly_errors do + gitaly_commit_client + .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset) + .map { |c| commit(c) } end end @@ -1244,12 +1222,8 @@ module Gitlab end def last_commit_for_path(sha, path) - gitaly_migrate(:last_commit_for_path) do |is_enabled| - if is_enabled - last_commit_for_path_by_gitaly(sha, path) - else - last_commit_for_path_by_rugged(sha, path) - end + wrapped_gitaly_errors do + gitaly_commit_client.last_commit_for_path(sha, path) end end @@ -1460,46 +1434,6 @@ module Gitlab end end - # Gitaly note: JV: although #log_by_shell shells out to Git I think the - # complexity is such that we should migrate it as Ruby before trying to - # do it in Go. - def log_by_shell(sha, options) - limit = options[:limit].to_i - offset = options[:offset].to_i - use_follow_flag = options[:follow] && options[:path].present? - - # We will perform the offset in Ruby because --follow doesn't play well with --skip. - # See: https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520 - offset_in_ruby = use_follow_flag && options[:offset].present? - limit += offset if offset_in_ruby - - cmd = %w[log] - cmd << "--max-count=#{limit}" - cmd << '--format=%H' - cmd << "--skip=#{offset}" unless offset_in_ruby - cmd << '--follow' if use_follow_flag - cmd << '--no-merges' if options[:skip_merges] - cmd << "--after=#{options[:after].iso8601}" if options[:after] - cmd << "--before=#{options[:before].iso8601}" if options[:before] - - if options[:all] - cmd += %w[--all --reverse] - else - cmd << sha - end - - # :path can be a string or an array of strings - if options[:path].present? - cmd << '--' - cmd += Array(options[:path]) - end - - raw_output, _status = run_git(cmd) - lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines - - lines.map! { |c| Rugged::Commit.new(rugged, c.strip) } - end - # We are trying to deprecate this method because it does a lot of work # but it seems to be used only to look up submodule URL's. # https://gitlab.com/gitlab-org/gitaly/issues/329 @@ -1629,11 +1563,6 @@ module Gitlab end end - def last_commit_for_path_by_rugged(sha, path) - sha = last_commit_id_for_path_by_shelling_out(sha, path) - commit(sha) - end - # Returns true if the given ref name exists # # Ref names must start with `refs/`. @@ -1826,35 +1755,6 @@ module Gitlab raise CommandError, @gitlab_projects.output end - def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) - ref ||= root_ref - - args = %W( - log #{ref} --pretty=%H --skip #{offset} - --max-count #{limit} --grep=#{query} --regexp-ignore-case - ) - args = args.concat(%W(-- #{path})) if path.present? - - git_log_results = run_git(args).first.lines - - git_log_results.map { |c| commit(c.chomp) }.compact - end - - def find_commits_by_message_by_gitaly(query, ref, path, limit, offset) - gitaly_commit_client - .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset) - .map { |c| commit(c) } - end - - def last_commit_for_path_by_gitaly(sha, path) - gitaly_commit_client.last_commit_for_path(sha, path) - end - - def last_commit_id_for_path_by_shelling_out(sha, path) - args = %W(rev-list --max-count=1 #{sha} -- #{path}) - run_git_with_timeout(args, Gitlab::Git::Popen::FAST_GIT_PROCESS_TIMEOUT).first.strip - end - def rugged_merge_base(from, to) rugged.merge_base(from, to) rescue Rugged::ReferenceError @@ -1868,35 +1768,6 @@ module Gitlab def sha_from_ref(ref) rev_parse_target(ref).oid end - - def build_git_cmd(*args) - object_directories = alternate_object_directories.join(File::PATH_SEPARATOR) - - env = { 'PWD' => self.path } - env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories if object_directories.present? - - [ - env, - ::Gitlab.config.git.bin_path, - *args, - { chdir: self.path } - ] - end - - def git_diff_cmd(old_rev, new_rev) - old_rev = old_rev == ::Gitlab::Git::BLANK_SHA ? ::Gitlab::Git::EMPTY_TREE_ID : old_rev - - build_git_cmd('diff', old_rev, new_rev, '--raw') - end - - def git_cat_file_cmd - format = '%(objectname) %(objectsize) %(rest)' - build_git_cmd('cat-file', "--batch-check=#{format}") - end - - def format_git_cat_file_script - File.expand_path('../support/format-git-cat-file-input', __FILE__) - end end end end diff --git a/lib/gitlab/git/support/format-git-cat-file-input b/lib/gitlab/git/support/format-git-cat-file-input deleted file mode 100755 index 2e93c646d0f..00000000000 --- a/lib/gitlab/git/support/format-git-cat-file-input +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby - -# This script formats the output of the `git diff <old_rev> <new_rev> --raw` -# command so it can be processed by `git cat-file` - -# We need to convert this: -# ":100644 100644 5f53439... 85bc2f9... R060\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee" -# To: -# "85bc2f9 R\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee" - -ARGF.each do |line| - _, _, old_blob_id, new_blob_id, rest = line.split(/\s/, 5) - - old_blob_id.gsub!(/[^\h]/, '') - new_blob_id.gsub!(/[^\h]/, '') - - # We can't pass '0000000...' to `git cat-file` given it will not return info about the deleted file - blob_id = new_blob_id =~ /\A0+\z/ ? old_blob_id : new_blob_id - - $stdout.puts "#{blob_id} #{rest}" -end diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index ab2c61f6782..555733d1834 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -68,6 +68,22 @@ module Gitlab raise Gitlab::Git::Repository::InvalidRef, ex end + def user_update_branch(branch_name, user, newrev, oldrev) + request = Gitaly::UserUpdateBranchRequest.new( + repository: @gitaly_repo, + branch_name: encode_binary(branch_name), + user: Gitlab::Git::User.from_gitlab(user).to_gitaly, + newrev: encode_binary(newrev), + oldrev: encode_binary(oldrev) + ) + + response = GitalyClient.call(@repository.storage, :operation_service, :user_update_branch, request) + + if pre_receive_error = response.pre_receive_error.presence + raise Gitlab::Git::PreReceiveError, pre_receive_error + end + end + def user_delete_branch(branch_name, user) request = Gitaly::UserDeleteBranchRequest.new( repository: @gitaly_repo, diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb index 53fe2f8e436..be3710c5b7f 100644 --- a/lib/gitlab/import_export.rb +++ b/lib/gitlab/import_export.rb @@ -40,6 +40,10 @@ module Gitlab "#{basename[0..FILENAME_LIMIT]}_export.tar.gz" end + def object_storage? + Feature.enabled?(:import_export_object_storage) + end + def version VERSION end diff --git a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb index aef371d81eb..83134bb0769 100644 --- a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb +++ b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb @@ -2,6 +2,7 @@ module Gitlab module ImportExport module AfterExportStrategies class BaseAfterExportStrategy + extend Gitlab::ImportExport::CommandLineUtil include ActiveModel::Validations extend Forwardable @@ -24,9 +25,10 @@ module Gitlab end def execute(current_user, project) - return unless project&.export_project_path - @project = project + + return unless @project.export_status == :finished + @current_user = current_user if invalid? @@ -51,9 +53,12 @@ module Gitlab end def self.lock_file_path(project) - return unless project&.export_path + return unless project.export_path || object_storage? - File.join(project.export_path, AFTER_EXPORT_LOCK_FILE_NAME) + lock_path = project.import_export_shared.archive_path + + mkdir_p(lock_path) + File.join(lock_path, AFTER_EXPORT_LOCK_FILE_NAME) end protected @@ -77,6 +82,10 @@ module Gitlab def log_validation_errors errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) } end + + def object_storage? + project.export_project_object_exists? + end end end end diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb index 938664a95a1..dce8f89c0ab 100644 --- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb +++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb @@ -38,14 +38,20 @@ module Gitlab private def send_file - export_file = File.open(project.export_project_path) - - Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options(export_file)) # rubocop:disable GitlabSecurity/PublicSend + Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options) # rubocop:disable GitlabSecurity/PublicSend ensure - export_file.close if export_file + export_file.close if export_file && !object_storage? + end + + def export_file + if object_storage? + project.import_export_upload.export_file.file.open + else + File.open(project.export_project_path) + end end - def send_file_options(export_file) + def send_file_options { body_stream: export_file, headers: headers @@ -53,7 +59,15 @@ module Gitlab end def headers - { 'Content-Length' => File.size(project.export_project_path).to_s } + { 'Content-Length' => export_size.to_s } + end + + def export_size + if object_storage? + project.import_export_upload.export_file.file.size + else + File.size(project.export_project_path) + end end end end diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb index 2daeba90a51..3cd153a4fd2 100644 --- a/lib/gitlab/import_export/saver.rb +++ b/lib/gitlab/import_export/saver.rb @@ -15,15 +15,22 @@ module Gitlab def save if compress_and_save remove_export_path + Rails.logger.info("Saved project export #{archive_file}") - archive_file + + save_on_object_storage if use_object_storage? else - @shared.error(Gitlab::ImportExport::Error.new("Unable to save #{archive_file} into #{@shared.export_path}")) + @shared.error(Gitlab::ImportExport::Error.new(error_message)) false end rescue => e @shared.error(e) false + ensure + if use_object_storage? + remove_archive + remove_export_path + end end private @@ -36,9 +43,29 @@ module Gitlab FileUtils.rm_rf(@shared.export_path) end + def remove_archive + FileUtils.rm_rf(@shared.archive_path) + end + def archive_file @archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project)) end + + def save_on_object_storage + upload = ImportExportUpload.find_or_initialize_by(project: @project) + + File.open(archive_file) { |file| upload.export_file = file } + + upload.save! + end + + def use_object_storage? + Gitlab::ImportExport.object_storage? + end + + def error_message + "Unable to save #{archive_file} into #{@shared.export_path}. Object storage enabled: #{use_object_storage?}" + end end end end diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb index 3d778da90c7..f9ebe53d6af 100644 --- a/lib/gitlab/kubernetes/helm/base_command.rb +++ b/lib/gitlab/kubernetes/helm/base_command.rb @@ -18,7 +18,7 @@ module Gitlab ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2) echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories - apk add -U ca-certificates openssl >/dev/null + apk add -U wget ca-certificates openssl >/dev/null wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null mv /tmp/linux-amd64/helm /usr/bin/ HEREDOC diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb index 9753be6d5c3..18f91db98fc 100644 --- a/lib/gitlab/middleware/multipart.rb +++ b/lib/gitlab/middleware/multipart.rb @@ -84,7 +84,7 @@ module Gitlab def open_file(params, key) ::UploadedFile.from_params( params, key, - Gitlab.config.uploads.storage_path) + [FileUploader.root, Gitlab.config.uploads.storage_path]) end end diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb index 4a99b7cca5c..8dca431c005 100644 --- a/lib/gitlab/middleware/read_only/controller.rb +++ b/lib/gitlab/middleware/read_only/controller.rb @@ -69,6 +69,7 @@ module Gitlab @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} end + # Overridden in EE module def whitelisted_routes grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route end diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 5cedd9e84c2..e222541992a 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -74,17 +74,10 @@ module Gitlab relative_path = name.dup relative_path << '.git' unless relative_path.end_with?('.git') - gitaly_migrate(:create_repository, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - repository = Gitlab::Git::Repository.new(storage, relative_path, '') - repository.gitaly_repository_client.create_repository - true - else - repo_path = File.join(Gitlab.config.repositories.storages[storage].legacy_disk_path, relative_path) - Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path) - end - end + repository = Gitlab::Git::Repository.new(storage, relative_path, '') + wrapped_gitaly_errors { repository.gitaly_repository_client.create_repository } + + true rescue => err # Once the Rugged codes gets removes this can be improved Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}") false @@ -448,7 +441,11 @@ module Gitlab end def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) - Gitlab::GitalyClient.migrate(method, status: status, &block) + wrapped_gitaly_errors { Gitlab::GitalyClient.migrate(method, status: status, &block) } + end + + def wrapped_gitaly_errors + yield rescue GRPC::NotFound, GRPC::BadStatus => e # Old Popen code returns [Error, output] to the caller, so we # need to do the same here... diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index e893e46ee86..55c899912f9 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -37,21 +37,14 @@ module Gitlab end def send_git_blob(repository, blob) - params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_raw_show, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) - { - 'GitalyServer' => gitaly_server_hash(repository), - 'GetBlobRequest' => { - repository: repository.gitaly_repository.to_h, - oid: blob.id, - limit: -1 - } - } - else - { - 'RepoPath' => repository.path_to_repo, - 'BlobId' => blob.id - } - end + params = { + 'GitalyServer' => gitaly_server_hash(repository), + 'GetBlobRequest' => { + repository: repository.gitaly_repository.to_h, + oid: blob.id, + limit: -1 + } + } [ SEND_DATA_HEADER, @@ -91,16 +84,12 @@ module Gitlab end def send_git_diff(repository, diff_refs) - params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_diff, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) - { - 'GitalyServer' => gitaly_server_hash(repository), - 'RawDiffRequest' => Gitaly::RawDiffRequest.new( - gitaly_diff_or_patch_hash(repository, diff_refs) - ).to_json - } - else - workhorse_diff_or_patch_hash(repository, diff_refs) - end + params = { + 'GitalyServer' => gitaly_server_hash(repository), + 'RawDiffRequest' => Gitaly::RawDiffRequest.new( + gitaly_diff_or_patch_hash(repository, diff_refs) + ).to_json + } [ SEND_DATA_HEADER, diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake index 21998dd2f5b..6df7fe81437 100644 --- a/lib/tasks/gettext.rake +++ b/lib/tasks/gettext.rake @@ -19,6 +19,23 @@ namespace :gettext do Rake::Task['gettext:po_to_json'].invoke end + task :regenerate do + # Remove all translated files, this speeds up finding + FileUtils.rm Dir['locale/**/gitlab.*'] + # remove the `pot` file to ensure it's completely regenerated + FileUtils.rm_f 'locale/gitlab.pot' + + Rake::Task['gettext:find'].invoke + + # leave only the required changes. + `git checkout -- locale/*/gitlab.po` + + puts <<~MSG + All done. Please commit the changes to `locale/gitlab.pot`. + + MSG + end + desc 'Lint all po files in `locale/' task lint: :environment do require 'simple_po_parser' @@ -50,13 +67,12 @@ namespace :gettext do end task :updated_check do + pot_file = 'locale/gitlab.pot' # Removing all pre-translated files speeds up `gettext:find` as the # files don't need to be merged. # Having `LC_MESSAGES/gitlab.mo files present also confuses the output. FileUtils.rm Dir['locale/**/gitlab.*'] - - # Make sure we start out with a clean pot.file - `git checkout -- locale/gitlab.pot` + FileUtils.rm_f pot_file # `gettext:find` writes touches to temp files to `stderr` which would cause # `static-analysis` to report failures. We can ignore these. @@ -64,18 +80,18 @@ namespace :gettext do Rake::Task['gettext:find'].invoke end - pot_diff = `git diff -- locale/gitlab.pot`.strip + pot_diff = `git diff -- #{pot_file} | grep -E '^(\\+|-)msgid'`.strip # reset the locale folder for potential next tasks `git checkout -- locale` if pot_diff.present? raise <<~MSG - Newly translated strings found, please add them to `gitlab.pot` by running: + Newly translated strings found, please add them to `#{pot_file}` by running: - rm locale/**/gitlab.*; bin/rake gettext:find; git checkout -- locale/*/gitlab.po + bin/rake gettext:regenerate - Then commit and push the resulting changes to `locale/gitlab.pot`. + Then commit and push the resulting changes to `#{pot_file}`. The diff was: diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb index 5dc85b2baea..4b9cb59eab5 100644 --- a/lib/uploaded_file.rb +++ b/lib/uploaded_file.rb @@ -28,7 +28,7 @@ class UploadedFile @tempfile = File.new(path, 'rb') end - def self.from_params(params, field, upload_path) + def self.from_params(params, field, upload_paths) unless params["#{field}.path"] raise InvalidPathError, "file is invalid" if params["#{field}.remote_id"] @@ -37,7 +37,8 @@ class UploadedFile file_path = File.realpath(params["#{field}.path"]) - unless self.allowed_path?(file_path, [upload_path, Dir.tmpdir].compact) + paths = Array(upload_paths) << Dir.tmpdir + unless self.allowed_path?(file_path, paths.compact) raise InvalidPathError, "insecure path used '#{file_path}'" end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0385761de45..ca488053a1f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-01 21:24+1000\n" -"PO-Revision-Date: 2018-07-01 21:24+1000\n" +"POT-Creation-Date: 2018-07-09 08:28+0200\n" +"PO-Revision-Date: 2018-07-09 08:28+0200\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" @@ -369,9 +369,18 @@ msgstr "" msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import." msgstr "" +msgid "An error accured whilst committing your changes." +msgstr "" + msgid "An error occured creating the new branch." msgstr "" +msgid "An error occured whilst fetching the job trace." +msgstr "" + +msgid "An error occured whilst fetching the latest pipline." +msgstr "" + msgid "An error occured whilst loading all the files." msgstr "" @@ -390,6 +399,9 @@ msgstr "" msgid "An error occured whilst loading the merge request." msgstr "" +msgid "An error occured whilst loading the pipelines jobs." +msgstr "" + msgid "An error occurred previewing the blob" msgstr "" @@ -1085,9 +1097,6 @@ msgstr "" msgid "ClusterIntegration|Add Kubernetes cluster" msgstr "" -msgid "ClusterIntegration|Add an existing Kubernetes cluster" -msgstr "" - msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration" msgstr "" @@ -1109,9 +1118,6 @@ msgstr "" msgid "ClusterIntegration|Certificate Authority bundle (PEM format)" msgstr "" -msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration" -msgstr "" - msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster." msgstr "" @@ -1139,18 +1145,6 @@ msgstr "" msgid "ClusterIntegration|Create Kubernetes cluster" msgstr "" -msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine" -msgstr "" - -msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab" -msgstr "" - -msgid "ClusterIntegration|Create on Google Kubernetes Engine" -msgstr "" - -msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster" -msgstr "" - msgid "ClusterIntegration|Enter the details for your Kubernetes cluster" msgstr "" @@ -2150,15 +2144,9 @@ msgstr "" msgid "Error Reporting and Logging" msgstr "" -msgid "Error committing changes. Please try again." -msgstr "" - msgid "Error fetching contributors data." msgstr "" -msgid "Error fetching job trace" -msgstr "" - msgid "Error fetching labels." msgstr "" @@ -2177,6 +2165,9 @@ msgstr "" msgid "Error loading last commit." msgstr "" +msgid "Error loading markdown preview" +msgstr "" + msgid "Error loading merge requests." msgstr "" @@ -2234,6 +2225,9 @@ msgstr "" msgid "Expand sidebar" msgstr "" +msgid "Explore groups" +msgstr "" + msgid "Explore projects" msgstr "" @@ -2449,6 +2443,27 @@ msgstr "" msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}." msgstr "" +msgid "GroupsDropdown|Frequently visited" +msgstr "" + +msgid "GroupsDropdown|Groups you visit often will appear here" +msgstr "" + +msgid "GroupsDropdown|Loading groups" +msgstr "" + +msgid "GroupsDropdown|Search your groups" +msgstr "" + +msgid "GroupsDropdown|Something went wrong on our end." +msgstr "" + +msgid "GroupsDropdown|Sorry, no groups matched your search" +msgstr "" + +msgid "GroupsDropdown|This feature requires browser localStorage support" +msgstr "" + msgid "GroupsEmptyState|A group is a collection of several projects." msgstr "" @@ -2631,7 +2646,7 @@ msgstr "" msgid "Introducing Cycle Analytics" msgstr "" -msgid "Issue Board" +msgid "Issue Boards" msgstr "" msgid "Issue events" @@ -2825,9 +2840,6 @@ msgstr "" msgid "Locked to current projects" msgstr "" -msgid "Login" -msgstr "" - msgid "Manage all notifications" msgstr "" @@ -2864,6 +2876,9 @@ msgstr "" msgid "Members" msgstr "" +msgid "Merge Request" +msgstr "" + msgid "Merge Request:" msgstr "" @@ -2906,12 +2921,42 @@ msgstr "" msgid "Messages" msgstr "" +msgid "Metrics" +msgstr "" + msgid "Metrics - Influx" msgstr "" msgid "Metrics - Prometheus" msgstr "" +msgid "Metrics|Check out the CI/CD documentation on deploying to an environment" +msgstr "" + +msgid "Metrics|Environment" +msgstr "" + +msgid "Metrics|Learn about environments" +msgstr "" + +msgid "Metrics|No deployed environments" +msgstr "" + +msgid "Metrics|There was an error fetching the environments data, please try again" +msgstr "" + +msgid "Metrics|There was an error getting deployment information." +msgstr "" + +msgid "Metrics|There was an error getting environments information." +msgstr "" + +msgid "Metrics|Unexpected deployment data response from prometheus endpoint" +msgstr "" + +msgid "Metrics|Unexpected metrics data response from prometheus endpoint" +msgstr "" + msgid "Milestone" msgstr "" @@ -2995,12 +3040,6 @@ msgid_plural "New Issues" msgstr[0] "" msgstr[1] "" -msgid "New Kubernetes Cluster" -msgstr "" - -msgid "New Kubernetes cluster" -msgstr "" - msgid "New Label" msgstr "" @@ -3217,6 +3256,9 @@ msgstr "" msgid "Only project members can comment." msgstr "" +msgid "Oops, are you sure?" +msgstr "" + msgid "Open in Xcode" msgstr "" @@ -3505,6 +3547,9 @@ msgstr "" msgid "Profiles|Account scheduled for removal." msgstr "" +msgid "Profiles|Add key" +msgstr "" + msgid "Profiles|Change username" msgstr "" @@ -3532,9 +3577,15 @@ msgstr "" msgid "Profiles|Path" msgstr "" +msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?" +msgstr "" + msgid "Profiles|Type your %{confirmationValue} to confirm:" msgstr "" +msgid "Profiles|Typically starts with \"ssh-rsa …\"" +msgstr "" + msgid "Profiles|Update username" msgstr "" @@ -3553,6 +3604,9 @@ msgstr "" msgid "Profiles|Your account is currently an owner in these groups:" msgstr "" +msgid "Profiles|e.g. My MacBook key" +msgstr "" + msgid "Profiles|your account" msgstr "" @@ -3640,9 +3694,6 @@ msgstr "" msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" -msgid "ProjectsDropdown|This feature requires browser localStorage support" -msgstr "" - msgid "PrometheusDashboard|Time" msgstr "" @@ -4528,12 +4579,6 @@ msgstr "" msgid "There are problems accessing Git storage: " msgstr "" -msgid "There was an error loading jobs" -msgstr "" - -msgid "There was an error loading latest pipeline" -msgstr "" - msgid "There was an error loading users activity calendar." msgstr "" @@ -4814,6 +4859,9 @@ msgstr "" msgid "Tip:" msgstr "" +msgid "Title" +msgstr "" + msgid "To GitLab" msgstr "" @@ -5182,6 +5230,9 @@ msgstr "" msgid "Yes" msgstr "" +msgid "Yes, add it" +msgstr "" + msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?" msgstr "" @@ -5441,6 +5492,9 @@ msgstr "" msgid "mrWidget|Merged by" msgstr "" +msgid "mrWidget|Open in Web IDE" +msgstr "" + msgid "mrWidget|Plain diff" msgstr "" @@ -5510,9 +5564,6 @@ msgstr "" msgid "mrWidget|This project is archived, write access has been disabled" msgstr "" -msgid "mrWidget|Web IDE" -msgstr "" - msgid "mrWidget|You can merge this merge request manually using the" msgstr "" diff --git a/package.json b/package.json index 1fce5d7623d..26b87c70e98 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "katex": "^0.8.3", "marked": "^0.3.12", "monaco-editor": "0.13.1", - "monaco-editor-webpack-plugin": "^1.2.1", + "monaco-editor-webpack-plugin": "^1.4.0", "mousetrap": "^1.4.6", "pikaday": "^1.6.1", "popper.js": "^1.14.3", diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js index 6e4e36b9b2d..b66ba885701 100644 --- a/scripts/frontend/prettier.js +++ b/scripts/frontend/prettier.js @@ -49,7 +49,7 @@ const stagedFiles = if (stagedFiles) { if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) { console.log('No matching staged files.'); - return; + process.exit(1); } console.log(`Matching staged Files : ${stagedFiles.length}`); } @@ -78,7 +78,7 @@ files = prettierIgnore.filter(files); if (!files.length) { console.log('No Files found to process with Prettier'); - return; + process.exit(1); } console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`); diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 34ed835a388..a2dfc43e9f7 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -790,23 +790,55 @@ describe ProjectsController do project.add_master(user) end - context 'when project export is enabled' do - it 'returns 302' do - get :download_export, namespace_id: project.namespace, id: project + context 'object storage disabled' do + before do + stub_feature_flags(import_export_object_storage: false) + end - expect(response).to have_gitlab_http_status(302) + context 'when project export is enabled' do + it 'returns 302' do + get :download_export, namespace_id: project.namespace, id: project + + expect(response).to have_gitlab_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + get :download_export, namespace_id: project.namespace, id: project + + expect(response).to have_gitlab_http_status(404) + end end end - context 'when project export is disabled' do + context 'object storage enabled' do before do - stub_application_setting(project_export_enabled?: false) + stub_feature_flags(import_export_object_storage: true) end - it 'returns 404' do - get :download_export, namespace_id: project.namespace, id: project + context 'when project export is enabled' do + it 'returns 302' do + get :download_export, namespace_id: project.namespace, id: project - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + get :download_export, namespace_id: project.namespace, id: project + + expect(response).to have_gitlab_http_status(404) + end end end end diff --git a/spec/factories/import_export_uploads.rb b/spec/factories/import_export_uploads.rb new file mode 100644 index 00000000000..7750d49b1d0 --- /dev/null +++ b/spec/factories/import_export_uploads.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :import_export_upload do + project { create(:project) } + end +end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index f6b05bac0e8..f77ded23b18 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -103,6 +103,22 @@ FactoryBot.define do end trait :with_export do + before(:create) do |_project, _evaluator| + allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { false } + allow(Feature).to receive(:enabled?).with('import_export_object_storage') { false } + end + + after(:create) do |project, _evaluator| + ProjectExportWorker.new.perform(project.creator.id, project.id) + end + end + + trait :with_object_export do + before(:create) do |_project, _evaluator| + allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { true } + allow(Feature).to receive(:enabled?).with('import_export_object_storage') { true } + end + after(:create) do |project, evaluator| ProjectExportWorker.new.perform(project.creator.id, project.id) end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 8a418356541..eb281cd2122 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -25,6 +25,7 @@ describe 'Import/Export - project export integration test', :js do before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + stub_feature_flags(import_export_object_storage: false) end after do diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index 7d056b0c140..9bb8a2063b5 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -5,6 +5,7 @@ describe 'Import/Export - Namespace export file cleanup', :js do before do allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + stub_feature_flags(import_export_object_storage: false) end after do diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz Binary files differindex ceba4dfec57..3b5df47e0b6 100644 --- a/spec/features/projects/import_export/test_project_export.tar.gz +++ b/spec/features/projects/import_export/test_project_export.tar.gz diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index ef15e7e1ff9..0bec5f185d6 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -9,6 +9,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do [relative link 1](../relative) [relative link 2](./relative) [relative link 3](./e/f/relative) +[spaced link](title with spaces) HEREDOC end @@ -42,6 +43,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end @@ -64,6 +66,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end @@ -86,6 +89,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end end @@ -119,6 +123,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end @@ -136,6 +141,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end @@ -153,6 +159,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>") end end end diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb index 74b693f3eff..f31457db92f 100644 --- a/spec/features/snippets/show_spec.rb +++ b/spec/features/snippets/show_spec.rb @@ -68,6 +68,26 @@ describe 'Snippet', :js do end end + context 'with cached Redcarpet html' do + let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) } + let(:file_name) { 'test.md' } + let(:content) { "1. one\n - sublist\n" } + + it 'renders correctly' do + expect(page).to have_xpath("//ol//li//ul") + end + end + + context 'with cached CommonMark html' do + let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) } + let(:file_name) { 'test.md' } + let(:content) { "1. one\n - sublist\n" } + + it 'renders correctly' do + expect(page).not_to have_xpath("//ol//li//ul") + end + end + context 'switching to the simple viewer' do before do find('.js-blob-viewer-switch-btn[data-viewer=simple]').click diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index b5bbb2c0ea5..3e2fb704bc6 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -14,4 +14,28 @@ describe 'User page' do expect(page).to have_link('Snippets') end end + + context 'signup disabled' do + it 'shows the sign in link' do + stub_application_setting(signup_enabled: false) + + visit(user_path(user)) + + page.within '.navbar-nav' do + expect(page).to have_link('Sign in') + end + end + end + + context 'signup enabled' do + it 'shows the sign in and register link' do + stub_application_setting(signup_enabled: true) + + visit(user_path(user)) + + page.within '.navbar-nav' do + expect(page).to have_link('Sign in / Register') + end + end + end end diff --git a/spec/fixtures/project_export.tar.gz b/spec/fixtures/project_export.tar.gz Binary files differnew file mode 100644 index 00000000000..72ab2d71f35 --- /dev/null +++ b/spec/fixtures/project_export.tar.gz diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index d246beb9888..f76ed4bfda4 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -184,6 +184,7 @@ describe IssuablesHelper do issuableRef: "##{issue.iid}", markdownPreviewPath: "/#{@project.full_path}/preview_markdown", markdownDocsPath: '/help/user/markdown', + markdownVersion: 11, issuableTemplates: [], projectPath: @project.path, projectNamespace: @project.namespace.path, diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 1a720aae55c..d5ed5c59c61 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -205,7 +205,9 @@ describe MarkupHelper do it "uses Wiki pipeline for markdown files" do allow(@wiki).to receive(:format).and_return(:markdown) - expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true) + expect(helper).to receive(:markdown_unsafe).with('wiki content', + pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", + issuable_state_filter_enabled: true, markdown_engine: :redcarpet) helper.render_wiki_content(@wiki) end @@ -236,19 +238,32 @@ describe MarkupHelper do expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8') end - it "delegates to #markdown_unsafe when file name corresponds to Markdown" do + it 'delegates to #markdown_unsafe when file name corresponds to Markdown' do expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true) expect(helper).to receive(:markdown_unsafe).and_return('NOEL') expect(helper.markup('foo.md', content)).to eq('NOEL') end - it "delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc" do + it 'delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc' do expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true) expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL') expect(helper.markup('foo.adoc', content)).to eq('NOEL') end + + it 'uses passed in rendered content' do + expect(helper).not_to receive(:gitlab_markdown?) + expect(helper).not_to receive(:markdown_unsafe) + + expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>') + end + + it 'defaults to Redcarpet' do + expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL') + + expect(helper.markup('foo.md', content)).to eq('NOEL') + end end describe '#first_line_in_markdown' do diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js index 1c1edfac68c..9b994543e19 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -31,12 +31,12 @@ describe('DiffFile', () => { describe('collapsed', () => { it('should not have file content', done => { - expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(0); + expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1); expect(vm.file.collapsed).toEqual(false); vm.file.collapsed = true; vm.$nextTick(() => { - expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(1); + expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0); done(); }); diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js index e1adf60962e..b02328dd359 100644 --- a/spec/javascripts/diffs/components/inline_diff_view_spec.js +++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js @@ -13,7 +13,7 @@ describe('InlineDiffView', () => { beforeEach(() => { const diffFile = getDiffFileMock(); - store.dispatch('setInlineDiffViewType'); + store.dispatch('diffs/setInlineDiffViewType'); component = createComponentWithStore(Vue.extend(InlineDiffView), store, { diffFile, diffLines: diffFile.highlightedDiffLines, diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index 1b040c924aa..be2a8ba67fe 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -165,6 +165,7 @@ export const note = { report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1', path: '/gitlab-org/gitlab-ce/notes/546', + cached_markdown_version: 11, }; export const discussionMock = { diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js index c82ba61a5b1..50c2b0e2bd0 100644 --- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js +++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js @@ -153,7 +153,7 @@ describe('Deployment component', () => { it('renders external URL', () => { expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url); - expect(el.querySelector('.js-deploy-url').innerText).toContain(deploymentMockData.external_url_formatted); + expect(el.querySelector('.js-deploy-url').innerText).toContain('View app'); }); it('renders stop button', () => { diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 3d36e46d863..61b7bd2c226 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -145,7 +145,7 @@ describe('MRWidgetHeader', () => { it('renders web ide button', () => { const button = vm.$el.querySelector('.js-web-ide'); - expect(button.textContent.trim()).toEqual('Web IDE'); + expect(button.textContent.trim()).toEqual('Open in Web IDE'); expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); }); @@ -154,7 +154,7 @@ describe('MRWidgetHeader', () => { const button = vm.$el.querySelector('.js-web-ide'); - expect(button.textContent.trim()).toEqual('Web IDE'); + expect(button.textContent.trim()).toEqual('Open in Web IDE'); expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); }); @@ -253,8 +253,8 @@ describe('MRWidgetHeader', () => { }); it('renders diverged commits info', () => { - expect(vm.$el.querySelector('.diverged-commits-count').textContent.trim()).toEqual( - '(12 commits behind)', + expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch( + /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/, ); }); }); diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb index ab14d77d552..a515d07b072 100644 --- a/spec/lib/banzai/filter/markdown_filter_spec.rb +++ b/spec/lib/banzai/filter/markdown_filter_spec.rb @@ -3,17 +3,61 @@ require 'spec_helper' describe Banzai::Filter::MarkdownFilter do include FilterSpecHelper - context 'code block' do - it 'adds language to lang attribute when specified' do - result = filter("```html\nsome code\n```") + describe 'markdown engine from context' do + it 'defaults to CommonMark' do + expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test') - expect(result).to start_with("<pre><code lang=\"html\">") + filter('test') end - it 'does not add language to lang attribute when not specified' do - result = filter("```\nsome code\n```") + it 'uses Redcarpet' do + expect_any_instance_of(Banzai::Filter::MarkdownEngines::Redcarpet).to receive(:render).and_return('test') - expect(result).to start_with("<pre><code>") + filter('test', { markdown_engine: :redcarpet }) + end + + it 'uses CommonMark' do + expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test') + + filter('test', { markdown_engine: :common_mark }) + end + end + + describe 'code block' do + context 'using CommonMark' do + before do + stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark) + end + + it 'adds language to lang attribute when specified' do + result = filter("```html\nsome code\n```") + + expect(result).to start_with("<pre><code lang=\"html\">") + end + + it 'does not add language to lang attribute when not specified' do + result = filter("```\nsome code\n```") + + expect(result).to start_with("<pre><code>") + end + end + + context 'using Redcarpet' do + before do + stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :redcarpet) + end + + it 'adds language to lang attribute when specified' do + result = filter("```html\nsome code\n```") + + expect(result).to start_with("\n<pre><code lang=\"html\">") + end + + it 'does not add language to lang attribute when not specified' do + result = filter("```\nsome code\n```") + + expect(result).to start_with("\n<pre><code>") + end end end end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index e13406d1972..8947e2ac4fb 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -203,4 +203,30 @@ describe ExtractsPath do expect(extract_ref_without_atom('foo.atom')).to eq(nil) end end + + describe '#lfs_blob_ids' do + shared_examples '#lfs_blob_ids' do + let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') } + let(:ref) { tag.target } + let(:params) { { ref: ref, path: 'README.md' } } + + before do + @project = create(:project, :repository) + end + + it 'handles annotated tags' do + assign_ref_vars + + expect(lfs_blob_ids).to eq([]) + end + end + + context 'when gitaly is enabled' do + it_behaves_like '#lfs_blob_ids' + end + + context 'when gitaly is disabled', :skip_gitaly_mock do + it_behaves_like '#lfs_blob_ids' + end + end end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 8bb246aa4bd..782e4e45a91 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -357,6 +357,35 @@ describe Gitlab::Database do end end + describe '.db_read_only?' do + context 'when using PostgreSQL' do + before do + allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original + expect(described_class).to receive(:postgresql?).and_return(true) + end + + it 'detects a read only database' do + allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "t" }]) + + expect(described_class.db_read_only?).to be_truthy + end + + it 'detects a read write database' do + allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "f" }]) + + expect(described_class.db_read_only?).to be_falsey + end + end + + context 'when using MySQL' do + before do + expect(described_class).to receive(:postgresql?).and_return(false) + end + + it { expect(described_class.db_read_only?).to be_falsey } + end + end + describe '#sanitize_timestamp' do let(:max_timestamp) { Time.at((1 << 31) - 1) } diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index b6061df349d..6900c4189b7 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -532,8 +532,8 @@ describe Gitlab::Git::Blob, seed_helper: true do subject { blob.load_all_data!(repository) } it 'loads missing data' do - expect(Gitlab::GitalyClient).to receive(:migrate) - .with(:git_blob_load_all_data).and_return(full_data) + expect(repository.gitaly_blob_client).to receive(:get_blob) + .and_return(double(:response, data: full_data)) subject @@ -544,8 +544,7 @@ describe Gitlab::Git::Blob, seed_helper: true do let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: full_data) } it "doesn't perform any loading" do - expect(Gitlab::GitalyClient).not_to receive(:migrate) - .with(:git_blob_load_all_data) + expect(repository.gitaly_blob_client).not_to receive(:get_blob) subject diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb index 9709f1f5646..031d1e87dc1 100644 --- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb @@ -53,6 +53,47 @@ describe Gitlab::GitalyClient::OperationService do end end + describe '#user_update_branch' do + let(:branch_name) { 'my-branch' } + let(:newrev) { '01e' } + let(:oldrev) { '01d' } + let(:request) do + Gitaly::UserUpdateBranchRequest.new( + repository: repository.gitaly_repository, + branch_name: branch_name, + newrev: newrev, + oldrev: oldrev, + user: gitaly_user + ) + end + let(:response) { Gitaly::UserUpdateBranchResponse.new } + + subject { client.user_update_branch(branch_name, user, newrev, oldrev) } + + it 'sends a user_update_branch message' do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_update_branch).with(request, kind_of(Hash)) + .and_return(response) + + subject + end + + context "when pre_receive_error is present" do + let(:response) do + Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed") + end + + it "throws a PreReceive exception" do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_update_branch).with(request, kind_of(Hash)) + .and_return(response) + + expect { subject }.to raise_error( + Gitlab::Git::PreReceiveError, "something failed") + end + end + end + describe '#user_delete_branch' do let(:branch_name) { 'my-branch' } let(:request) do diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb index 51fad6c6838..b2e544e6fed 100644 --- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb @@ -27,9 +27,9 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do milestone: double(:milestone, number: 4), user: double(:user, id: 4, login: 'alice'), assignee: double(:user, id: 4, login: 'alice'), - created_at: Time.zone.now, - updated_at: Time.zone.now, - merged_at: Time.zone.now + created_at: 1.second.ago, + updated_at: 1.second.ago, + merged_at: 1.second.ago ) end diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb new file mode 100644 index 00000000000..5059d68e54b --- /dev/null +++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb @@ -0,0 +1,105 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do + let!(:service) { described_class.new } + let!(:project) { create(:project, :with_object_export) } + let(:shared) { project.import_export_shared } + let!(:user) { create(:user) } + + describe '#execute' do + before do + allow(service).to receive(:strategy_execute) + stub_feature_flags(import_export_object_storage: true) + end + + it 'returns if project exported file is not found' do + allow(project).to receive(:export_project_object_exists?).and_return(false) + + expect(service).not_to receive(:strategy_execute) + + service.execute(user, project) + end + + it 'creates a lock file in the export dir' do + allow(service).to receive(:delete_after_export_lock) + + service.execute(user, project) + + expect(lock_path_exist?).to be_truthy + end + + context 'when the method succeeds' do + it 'removes the lock file' do + service.execute(user, project) + + expect(lock_path_exist?).to be_falsey + end + end + + context 'when the method fails' do + before do + allow(service).to receive(:strategy_execute).and_call_original + end + + context 'when validation fails' do + before do + allow(service).to receive(:invalid?).and_return(true) + end + + it 'does not create the lock file' do + expect(service).not_to receive(:create_or_update_after_export_lock) + + service.execute(user, project) + end + + it 'does not execute main logic' do + expect(service).not_to receive(:strategy_execute) + + service.execute(user, project) + end + + it 'logs validation errors in shared context' do + expect(service).to receive(:log_validation_errors) + + service.execute(user, project) + end + end + + context 'when an exception is raised' do + it 'removes the lock' do + expect { service.execute(user, project) }.to raise_error(NotImplementedError) + + expect(lock_path_exist?).to be_falsey + end + end + end + end + + describe '#log_validation_errors' do + it 'add the message to the shared context' do + errors = %w(test_message test_message2) + + allow(service).to receive(:invalid?).and_return(true) + allow(service.errors).to receive(:full_messages).and_return(errors) + + expect(shared).to receive(:add_error_message).twice.and_call_original + + service.execute(user, project) + + expect(shared.errors).to eq errors + end + end + + describe '#to_json' do + it 'adds the current strategy class to the serialized attributes' do + params = { param1: 1 } + result = params.merge(klass: described_class.to_s).to_json + + expect(described_class.new(params).to_json).to eq result + end + end + + def lock_path_exist? + File.exist?(described_class.lock_file_path(project)) + end +end diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb index ed54d87de4a..566b7f46c87 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb @@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do describe '#execute' do before do allow(service).to receive(:strategy_execute) + stub_feature_flags(import_export_object_storage: false) end it 'returns if project exported file is not found' do diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb index 5fe57d9987b..7f2e0a4ee2c 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb @@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do end describe '#execute' do - it 'removes the exported project file after the upload' do - allow(strategy).to receive(:send_file) - allow(strategy).to receive(:handle_response_error) + context 'without object storage' do + before do + stub_feature_flags(import_export_object_storage: false) + end + + it 'removes the exported project file after the upload' do + allow(strategy).to receive(:send_file) + allow(strategy).to receive(:handle_response_error) + + expect(project).to receive(:remove_exported_project_file) + + strategy.execute(user, project) + end + end + + context 'with object storage' do + before do + stub_feature_flags(import_export_object_storage: true) + end - expect(project).to receive(:remove_exported_project_file) + it 'removes the exported project file after the upload' do + allow(strategy).to receive(:send_file) + allow(strategy).to receive(:handle_response_error) - strategy.execute(user, project) + expect(project).to receive(:remove_exported_project_file) + + strategy.execute(user, project) + end end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 2ea66479c1b..084ce3066d6 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -293,6 +293,7 @@ project: - deploy_tokens - settings - ci_cd_settings +- import_export_upload award_emoji: - awardable - user diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb new file mode 100644 index 00000000000..02f1a4b81aa --- /dev/null +++ b/spec/lib/gitlab/import_export/saver_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' +require 'fileutils' + +describe Gitlab::ImportExport::Saver do + let!(:project) { create(:project, :public, name: 'project') } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let(:shared) { project.import_export_shared } + subject { described_class.new(project: project, shared: shared) } + + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + + FileUtils.mkdir_p(shared.export_path) + FileUtils.touch("#{shared.export_path}/tmp.bundle") + end + + after do + FileUtils.rm_rf(export_path) + end + + context 'local archive' do + it 'saves the repo to disk' do + stub_feature_flags(import_export_object_storage: false) + + subject.save + + expect(shared.errors).to be_empty + expect(Dir.empty?(shared.archive_path)).to be false + end + end + + context 'object storage' do + it 'saves the repo using object storage' do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(ImportExportUploader) + + subject.save + + expect(ImportExportUpload.find_by(project: project).export_file.url) + .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*]) + end + end +end diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb index b4837a1689a..f788f8ee276 100644 --- a/spec/lib/gitlab/middleware/multipart_spec.rb +++ b/spec/lib/gitlab/middleware/multipart_spec.rb @@ -75,6 +75,33 @@ describe Gitlab::Middleware::Multipart do it_behaves_like 'multipart upload files' end + it 'allows symlinks for uploads dir' do + Tempfile.open('two-levels') do |tempfile| + symlinked_dir = '/some/dir/uploads' + symlinked_path = File.join(symlinked_dir, File.basename(tempfile.path)) + env = post_env({ 'file' => symlinked_path }, { 'file.name' => original_filename, 'file.path' => symlinked_path }, Gitlab::Workhorse.secret, 'gitlab-workhorse') + + allow(FileUploader).to receive(:root).and_return(symlinked_dir) + allow(UploadedFile).to receive(:allowed_paths).and_return([symlinked_dir, Gitlab.config.uploads.storage_path]) + allow(File).to receive(:realpath).and_call_original + allow(File).to receive(:realpath).with(symlinked_dir).and_return(Dir.tmpdir) + allow(File).to receive(:realpath).with(symlinked_path).and_return(tempfile.path) + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with(symlinked_dir).and_return(true) + + # override Dir.tmpdir because this dir is in the list of allowed paths + # and it would match FileUploader.root path (which in this test is linked + # to /tmp too) + allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir')) + + expect(app).to receive(:call) do |env| + expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile) + end + + middleware.call(env) + end + end + def post_env(rewritten_fields, params, secret, issuer) token = JWT.encode({ 'iss' => issuer, 'rewritten_fields' => rewritten_fields }, secret, 'HS256') Rack::MockRequest.env_for( diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb index 5c398bc2063..8fbeaa065fa 100644 --- a/spec/lib/gitlab/middleware/read_only_spec.rb +++ b/spec/lib/gitlab/middleware/read_only_spec.rb @@ -4,28 +4,6 @@ describe Gitlab::Middleware::ReadOnly do include Rack::Test::Methods using RSpec::Parameterized::TableSyntax - RSpec::Matchers.define :be_a_redirect do - match do |response| - response.status == 301 - end - end - - RSpec::Matchers.define :disallow_request do - match do |middleware| - alert = middleware.env['rack.session'].to_hash - .dig('flash', 'flashes', 'alert') - - alert&.include?('You cannot perform write operations') - end - end - - RSpec::Matchers.define :disallow_request_in_json do - match do |response| - json_response = JSON.parse(response.body) - response.body.include?('You cannot perform write operations') && json_response.key?('message') - end - end - let(:rack_stack) do rack = Rack::Builder.new do use ActionDispatch::Session::CacheStore @@ -66,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do it 'expects PATCH requests to be disallowed' do response = request.patch('/test_request') - expect(response).to be_a_redirect + expect(response).to be_redirect expect(subject).to disallow_request end it 'expects PUT requests to be disallowed' do response = request.put('/test_request') - expect(response).to be_a_redirect + expect(response).to be_redirect expect(subject).to disallow_request end it 'expects POST requests to be disallowed' do response = request.post('/test_request') - expect(response).to be_a_redirect + expect(response).to be_redirect expect(subject).to disallow_request end it 'expects a internal POST request to be allowed after a disallowed request' do response = request.post('/test_request') - expect(response).to be_a_redirect + expect(response).to be_redirect response = request.post("/api/#{API::API.version}/internal") - expect(response).not_to be_a_redirect + expect(response).not_to be_redirect end it 'expects DELETE requests to be disallowed' do response = request.delete('/test_request') - expect(response).to be_a_redirect + expect(response).to be_redirect expect(subject).to disallow_request end @@ -105,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do expect(Rails.application.routes).to receive(:recognize_path).and_call_original response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch') - expect(response).to be_a_redirect + expect(response).to be_redirect expect(subject).to disallow_request end @@ -120,19 +98,19 @@ describe Gitlab::Middleware::ReadOnly do expect(Rails.application.routes).not_to receive(:recognize_path) response = request.post("/api/#{API::API.version}/internal") - expect(response).not_to be_a_redirect + expect(response).not_to be_redirect expect(subject).not_to disallow_request end it 'expects requests to sidekiq admin to be allowed' do response = request.post('/admin/sidekiq') - expect(response).not_to be_a_redirect + expect(response).not_to be_redirect expect(subject).not_to disallow_request response = request.get('/admin/sidekiq') - expect(response).not_to be_a_redirect + expect(response).not_to be_redirect expect(subject).not_to disallow_request end @@ -150,7 +128,7 @@ describe Gitlab::Middleware::ReadOnly do expect(Rails.application.routes).to receive(:recognize_path).and_call_original response = request.post(path) - expect(response).not_to be_a_redirect + expect(response).not_to be_redirect expect(subject).not_to disallow_request end end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 660671cefaf..8fdcfe79fb5 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -143,34 +143,22 @@ describe Gitlab::Workhorse do let(:diff_refs) { double(base_sha: "base", head_sha: "head") } subject { described_class.send_git_diff(repository, diff_refs) } - context 'when Gitaly workhorse_send_git_diff feature is enabled' do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) - - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-diff") - expect(params).to eq({ - 'GitalyServer' => { - address: Gitlab::GitalyClient.address(project.repository_storage), - token: Gitlab::GitalyClient.token(project.repository_storage) - }, - 'RawDiffRequest' => Gitaly::RawDiffRequest.new( - repository: repository.gitaly_repository, - left_commit_id: 'base', - right_commit_id: 'head' - ).to_json - }.deep_stringify_keys) - end - end - - context 'when Gitaly workhorse_send_git_diff feature is disabled', :disable_gitaly do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-diff") - expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") - end + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-diff") + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'RawDiffRequest' => Gitaly::RawDiffRequest.new( + repository: repository.gitaly_repository, + left_commit_id: 'base', + right_commit_id: 'head' + ).to_json + }.deep_stringify_keys) end end @@ -425,34 +413,22 @@ describe Gitlab::Workhorse do subject { described_class.send_git_blob(repository, blob) } - context 'when Gitaly workhorse_raw_show feature is enabled' do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) - - expect(key).to eq('Gitlab-Workhorse-Send-Data') - expect(command).to eq('git-blob') - expect(params).to eq({ - 'GitalyServer' => { - address: Gitlab::GitalyClient.address(project.repository_storage), - token: Gitlab::GitalyClient.token(project.repository_storage) - }, - 'GetBlobRequest' => { - repository: repository.gitaly_repository.to_h, - oid: blob.id, - limit: -1 - } - }.deep_stringify_keys) - end - end - - context 'when Gitaly workhorse_raw_show feature is disabled', :disable_gitaly do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) - expect(key).to eq('Gitlab-Workhorse-Send-Data') - expect(command).to eq('git-blob') - expect(params).to eq('RepoPath' => repository.path_to_repo, 'BlobId' => blob.id) - end + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-blob') + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'GetBlobRequest' => { + repository: repository.gitaly_repository.to_h, + oid: blob.id, + limit: -1 + } + }.deep_stringify_keys) end end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 2d75422ee68..da26d802688 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -370,4 +370,20 @@ describe CacheMarkdownField do end end end + + describe CacheMarkdownField::MarkdownEngine do + subject { lambda { |version| CacheMarkdownField::MarkdownEngine.from_version(version) } } + + it 'returns :common_mark as a default' do + expect(subject.call(nil)).to eq :common_mark + end + + it 'returns :common_mark' do + expect(subject.call(CacheMarkdownField::CACHE_COMMONMARK_VERSION)).to eq :common_mark + end + + it 'returns :redcarpet' do + expect(subject.call(CacheMarkdownField::CACHE_REDCARPET_VERSION)).to eq :redcarpet + end + end end diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb index c6331c5ec15..f8c2e29fadd 100644 --- a/spec/models/concerns/cacheable_attributes_spec.rb +++ b/spec/models/concerns/cacheable_attributes_spec.rb @@ -52,7 +52,7 @@ describe CacheableAttributes do describe '.cache_key' do it 'excludes cache attributes' do - expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}") + expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Rails.version}") end end diff --git a/spec/models/import_export_upload_spec.rb b/spec/models/import_export_upload_spec.rb new file mode 100644 index 00000000000..58af84b8a08 --- /dev/null +++ b/spec/models/import_export_upload_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe ImportExportUpload do + subject { described_class.new(project: create(:project)) } + + shared_examples 'stores the Import/Export file' do |method| + it 'stores the import file' do + subject.public_send("#{method}=", fixture_file_upload('spec/fixtures/project_export.tar.gz')) + + subject.save! + + url = "/uploads/-/system/import_export_upload/#{method}/#{subject.id}/project_export.tar.gz" + + expect(subject.public_send(method).url).to eq(url) + end + end + + context 'import' do + it_behaves_like 'stores the Import/Export file', :import_file + end + + context 'export' do + it_behaves_like 'stores the Import/Export file', :export_file + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index c3aa6cd6fed..b9512b81678 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2782,6 +2782,10 @@ describe Project do let(:legacy_project) { create(:project, :legacy_storage, :with_export) } let(:project) { create(:project, :with_export) } + before do + stub_feature_flags(import_export_object_storage: false) + end + it 'removes the exports directory for the project' do expect(File.exist?(project.export_path)).to be_truthy @@ -2830,12 +2834,14 @@ describe Project do let(:project) { create(:project, :with_export) } it 'removes the exported project file' do + stub_feature_flags(import_export_object_storage: false) + exported_file = project.export_project_path expect(File.exist?(exported_file)).to be_truthy - allow(FileUtils).to receive(:rm_f).and_call_original - expect(FileUtils).to receive(:rm_f).with(exported_file).and_call_original + allow(FileUtils).to receive(:rm_rf).and_call_original + expect(FileUtils).to receive(:rm_rf).with(exported_file).and_call_original project.remove_exported_project_file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 097144d04ce..57c408498dd 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2461,7 +2461,9 @@ describe User do it 'changes the namespace (just to compare to when username is not changed)' do expect do - user.update_attributes!(username: new_username) + Timecop.freeze(1.second.from_now) do + user.update_attributes!(username: new_username) + end end.to change { user.namespace.updated_at } end diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index 3834d27d0a9..a4615bd081f 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -192,6 +192,13 @@ describe API::ProjectExport do context 'when upload complete' do before do FileUtils.rm_rf(project_after_export.export_path) + + if project_after_export.export_project_object_exists? + upload = project_after_export.import_export_upload + + upload.remove_export_file! + upload.save + end end it_behaves_like '404 response' do @@ -261,6 +268,22 @@ describe API::ProjectExport do it_behaves_like 'get project export download not found' end end + + context 'when an uploader is used' do + before do + stub_uploads_object_storage(ImportExportUploader) + + [project, project_finished, project_after_export].each do |p| + p.add_master(user) + + upload = ImportExportUpload.new(project: p) + upload.export_file = fixture_file_upload('spec/fixtures/project_export.tar.gz', "`/tar.gz") + upload.save! + end + end + + it_behaves_like 'get project download by strategy' + end end describe 'POST /projects/:project_id/export' do diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index decb5d22f59..b8cdbc8c0f6 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -100,7 +100,11 @@ describe Ci::RetryBuildService do end describe '#execute' do - let(:new_build) { service.execute(build) } + let(:new_build) do + Timecop.freeze(1.second.from_now) do + service.execute(build) + end + end context 'when user has ability to execute build' do before do @@ -150,7 +154,11 @@ describe Ci::RetryBuildService do end describe '#reprocess' do - let(:new_build) { service.reprocess!(build) } + let(:new_build) do + Timecop.freeze(1.second.from_now) do + service.reprocess!(build) + end + end context 'when user has ability to execute build' do before do diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb index 1875d0448cd..d5fcef1246f 100644 --- a/spec/services/import_export_clean_up_service_spec.rb +++ b/spec/services/import_export_clean_up_service_spec.rb @@ -11,7 +11,6 @@ describe ImportExportCleanUpService do path = '/invalid/path/' stub_repository_downloads_path(path) - expect(File).to receive(:directory?).with(path + tmp_import_export_folder).and_return(false).at_least(:once) expect(service).not_to receive(:clean_up_export_files) service.execute @@ -38,6 +37,24 @@ describe ImportExportCleanUpService do end end + context 'with uploader exports' do + it 'removes old files' do + upload = create(:import_export_upload, + updated_at: 2.days.ago, + export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')) + + expect { service.execute }.to change { upload.reload.export_file.file.nil? }.to(true) + end + + it 'does not remove new files' do + upload = create(:import_export_upload, + updated_at: 1.hour.ago, + export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')) + + expect { service.execute }.not_to change { upload.reload.export_file.file.nil? } + end + end + def in_directory_with_files(mtime:) Dir.mktmpdir do |tmpdir| stub_repository_downloads_path(tmpdir) diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb index 64a9559791f..81dc7c57f4a 100644 --- a/spec/services/preview_markdown_service_spec.rb +++ b/spec/services/preview_markdown_service_spec.rb @@ -64,4 +64,16 @@ describe PreviewMarkdownService do expect(result[:commands]).to eq 'Sets time estimate to 2y.' end end + + it 'sets correct markdown engine' do + service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION }) + result = service.execute + + expect(result[:markdown_engine]).to eq :redcarpet + + service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION }) + result = service.execute + + expect(result[:markdown_engine]).to eq :common_mark + end end diff --git a/spec/support/matchers/disallow_request_matchers.rb b/spec/support/matchers/disallow_request_matchers.rb new file mode 100644 index 00000000000..db4d90e4fd0 --- /dev/null +++ b/spec/support/matchers/disallow_request_matchers.rb @@ -0,0 +1,15 @@ +RSpec::Matchers.define :disallow_request do + match do |middleware| + alert = middleware.env['rack.session'].to_hash + .dig('flash', 'flashes', 'alert') + + alert&.include?('You cannot perform write operations') + end +end + +RSpec::Matchers.define :disallow_request_in_json do + match do |response| + json_response = JSON.parse(response.body) + response.body.include?('You cannot perform write operations') && json_response.key?('message') + end +end diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb index 56e86a87ab9..05d53a13fd3 100644 --- a/spec/support/shared_examples/helm_generated_script.rb +++ b/spec/support/shared_examples/helm_generated_script.rb @@ -6,7 +6,7 @@ shared_examples 'helm commands' do ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2) echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories - apk add -U ca-certificates openssl >/dev/null + apk add -U wget ca-certificates openssl >/dev/null wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null mv /tmp/linux-amd64/helm /usr/bin/ EOS diff --git a/spec/uploaders/import_export_uploader_spec.rb b/spec/uploaders/import_export_uploader_spec.rb new file mode 100644 index 00000000000..51b173b682d --- /dev/null +++ b/spec/uploaders/import_export_uploader_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe ImportExportUploader do + let(:model) { build_stubbed(:import_export_upload) } + let(:upload) { create(:upload, model: model) } + + subject { described_class.new(model, :import_file) } + + context "object_store is REMOTE" do + before do + stub_uploads_object_storage + end + + include_context 'with storage', described_class::Store::REMOTE + + it_behaves_like 'builds correct paths', + store_dir: %r[import_export_upload/import_file/], + upload_path: %r[import_export_upload/import_file/] + end +end diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index 6bc551be9ad..ede271b2cdd 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -62,4 +62,12 @@ describe RepositoryCheck::BatchWorker do expect(subject.perform(shard_name)).to eq([]) end + + it 'does not run if the exclusive lease is taken' do + allow(subject).to receive(:try_obtain_lease).and_return(false) + + expect(subject).not_to receive(:perform_repository_checks) + + subject.perform(shard_name) + end end diff --git a/spec/workers/repository_check/dispatch_worker_spec.rb b/spec/workers/repository_check/dispatch_worker_spec.rb index 20a4f1f5344..7877429aa8f 100644 --- a/spec/workers/repository_check/dispatch_worker_spec.rb +++ b/spec/workers/repository_check/dispatch_worker_spec.rb @@ -11,6 +11,14 @@ describe RepositoryCheck::DispatchWorker do subject.perform end + it 'does nothing if the exclusive lease is taken' do + allow(subject).to receive(:try_obtain_lease).and_return(false) + + expect(RepositoryCheck::BatchWorker).not_to receive(:perform_async) + + subject.perform + end + it 'dispatches work to RepositoryCheck::BatchWorker' do expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once) diff --git a/vendor/gitignore/Autotools.gitignore b/vendor/gitignore/Autotools.gitignore index ffa6ecc3f9b..96d6ed2cfea 100644 --- a/vendor/gitignore/Autotools.gitignore +++ b/vendor/gitignore/Autotools.gitignore @@ -9,7 +9,7 @@ Makefile.in # http://www.gnu.org/software/autoconf -/autom4te.cache +autom4te.cache /autoscan.log /autoscan-*.log /aclocal.m4 @@ -39,4 +39,3 @@ m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 -autom4te.cache diff --git a/vendor/gitignore/CraftCMS.gitignore b/vendor/gitignore/CraftCMS.gitignore index a70d4772c46..0d81b397e35 100644 --- a/vendor/gitignore/CraftCMS.gitignore +++ b/vendor/gitignore/CraftCMS.gitignore @@ -1,3 +1,4 @@ -# Craft Storage (cache) [http://buildwithcraft.com/help/craft-storage-gitignore] +# Craft 2 Storage (https://craftcms.com/support/craft-storage-gitignore) +# not necessary for Craft 3 (https://github.com/craftcms/craft/issues/26) /craft/storage/* -!/craft/storage/logo/*
\ No newline at end of file +!/craft/storage/rebrand diff --git a/vendor/gitignore/Delphi.gitignore b/vendor/gitignore/Delphi.gitignore index 19864c6bbef..000ee5f104b 100644 --- a/vendor/gitignore/Delphi.gitignore +++ b/vendor/gitignore/Delphi.gitignore @@ -20,7 +20,7 @@ # Deployment Manager configuration file for your project. Added in Delphi XE2. # Uncomment this if it is not mobile development and you do not use remote debug feature. #*.deployproj -# +# # C++ object files produced when C/C++ Output file generation is configured. # Uncomment this if you are not using external objects (zlib library for example). #*.obj diff --git a/vendor/gitignore/Eagle.gitignore b/vendor/gitignore/Eagle.gitignore index 9afc324d6ae..28f0b9715e6 100644 --- a/vendor/gitignore/Eagle.gitignore +++ b/vendor/gitignore/Eagle.gitignore @@ -35,7 +35,6 @@ eagle.epf *.gpi *.pls *.ger -*.gpi *.xln *.drd diff --git a/vendor/gitignore/GWT.gitignore b/vendor/gitignore/GWT.gitignore index 07704e54bbc..a01e7fcd921 100644 --- a/vendor/gitignore/GWT.gitignore +++ b/vendor/gitignore/GWT.gitignore @@ -18,9 +18,6 @@ war/WEB-INF/classes/ #compilation logs .gwt/ -#caching for already compiled files -gwt-unitCache/ - #gwt junit compilation files www-test/ diff --git a/vendor/gitignore/Global/Backup.gitignore b/vendor/gitignore/Global/Backup.gitignore new file mode 100644 index 00000000000..825ce52db53 --- /dev/null +++ b/vendor/gitignore/Global/Backup.gitignore @@ -0,0 +1,5 @@ +*.bak +*.gho +*.ori +*.orig +*.tmp diff --git a/vendor/gitignore/Global/CodeKit.gitignore b/vendor/gitignore/Global/CodeKit.gitignore index bd9e67fcca2..09b84126cea 100644 --- a/vendor/gitignore/Global/CodeKit.gitignore +++ b/vendor/gitignore/Global/CodeKit.gitignore @@ -1,3 +1,4 @@ # General CodeKit files to ignore config.codekit +config.codekit3 /min diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore index 0eb8a5e8571..a65649a9ed2 100644 --- a/vendor/gitignore/Global/Eclipse.gitignore +++ b/vendor/gitignore/Global/Eclipse.gitignore @@ -23,7 +23,7 @@ local.properties # CDT-specific (C/C++ Development Tooling) .cproject -# CDT- autotools +# CDT- autotools .autotools # Java annotation processor (APT) @@ -47,6 +47,9 @@ local.properties # Code Recommenders .recommenders/ +# Annotation Processing +.apt_generated/ + # Scala IDE specific (Scala & Java development for Eclipse) .cache-main .scala_dependencies diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore index 4d5117a1d9d..0d95b087f19 100644 --- a/vendor/gitignore/Global/JetBrains.gitignore +++ b/vendor/gitignore/Global/JetBrains.gitignore @@ -4,6 +4,7 @@ # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml +.idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf @@ -20,9 +21,16 @@ .idea/**/gradle.xml .idea/**/libraries +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + # CMake -cmake-build-debug/ -cmake-build-release/ +cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore index d87a6bdbeeb..46a83d635ba 100644 --- a/vendor/gitignore/Global/Matlab.gitignore +++ b/vendor/gitignore/Global/Matlab.gitignore @@ -7,17 +7,20 @@ # Compiled MEX binaries (all platforms) *.mex* -# Packaged app and toolbox files -*.mlappinstall -*.mltbx - -# Generated helpsearch folders -helpsearch*/ +# Packaged app and toolbox files +*.mlappinstall +*.mltbx + +# Generated helpsearch folders +helpsearch*/ # Simulink code generation folders slprj/ sccprj/ +# Matlab code generation folders +codegen/ + # Simulink autosave extension *.autosave diff --git a/vendor/gitignore/Global/Patch.gitignore b/vendor/gitignore/Global/Patch.gitignore new file mode 100644 index 00000000000..6ffab9ad295 --- /dev/null +++ b/vendor/gitignore/Global/Patch.gitignore @@ -0,0 +1,2 @@ +*.orig +*.rej diff --git a/vendor/gitignore/Global/SynopsysVCS.gitignore b/vendor/gitignore/Global/SynopsysVCS.gitignore index eed2432fb78..ad751f6bd75 100644 --- a/vendor/gitignore/Global/SynopsysVCS.gitignore +++ b/vendor/gitignore/Global/SynopsysVCS.gitignore @@ -4,8 +4,8 @@ *.evcd *.fsdb -# Default name of the simulation executable. A different name can be -# specified with this switch (the associated daidir database name is +# Default name of the simulation executable. A different name can be +# specified with this switch (the associated daidir database name is # also taken from here): -o <path>/<filename> simv @@ -13,7 +13,7 @@ simv simv.daidir/ simv.db.dir/ -# Infrastructure necessary to co-simulate SystemC models with +# Infrastructure necessary to co-simulate SystemC models with # Verilog/VHDL models. An alternate directory may be specified with this # switch: -Mdir=<directory_path> csrc/ @@ -22,7 +22,7 @@ csrc/ # used to write all messages from simulation: -l <filename> *.log -# Coverage results (generated with urg) and database location. The +# Coverage results (generated with urg) and database location. The # following switch can also be used: urg -dir <coverage_directory>.vdb simv.vdb/ urgReport/ diff --git a/vendor/gitignore/Global/Vim.gitignore b/vendor/gitignore/Global/Vim.gitignore index 19cfe22f583..741518ffd24 100644 --- a/vendor/gitignore/Global/Vim.gitignore +++ b/vendor/gitignore/Global/Vim.gitignore @@ -1,7 +1,8 @@ # Swap [._]*.s[a-v][a-z] [._]*.sw[a-p] -[._]s[a-v][a-z] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] [._]sw[a-p] # Session diff --git a/vendor/gitignore/LabVIEW.gitignore b/vendor/gitignore/LabVIEW.gitignore index 122450865cf..31619f59814 100644 --- a/vendor/gitignore/LabVIEW.gitignore +++ b/vendor/gitignore/LabVIEW.gitignore @@ -14,3 +14,4 @@ # Metadata *.aliases *.lvlps +.cache/ diff --git a/vendor/gitignore/Maven.gitignore b/vendor/gitignore/Maven.gitignore index 5f2dbe11df9..e8d57d08088 100644 --- a/vendor/gitignore/Maven.gitignore +++ b/vendor/gitignore/Maven.gitignore @@ -7,6 +7,4 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties - -# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) -!/.mvn/wrapper/maven-wrapper.jar +.mvn/wrapper/maven-wrapper.jar diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore index 4454ba1b5bf..3a4c8581b3a 100644 --- a/vendor/gitignore/Node.gitignore +++ b/vendor/gitignore/Node.gitignore @@ -57,9 +57,15 @@ typings/ # dotenv environment variables file .env +# parcel-bundler cache (https://parceljs.org/) +.cache + # next.js build output .next +# nuxt.js build output +.nuxt + # vuepress build output .vuepress/dist diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore index 86de6aa3f5f..a0bd6b453a8 100644 --- a/vendor/gitignore/Objective-C.gitignore +++ b/vendor/gitignore/Objective-C.gitignore @@ -35,6 +35,9 @@ xcuserdata/ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage # diff --git a/vendor/gitignore/Perl6.gitignore b/vendor/gitignore/Perl6.gitignore new file mode 100644 index 00000000000..7b2c018a562 --- /dev/null +++ b/vendor/gitignore/Perl6.gitignore @@ -0,0 +1,7 @@ +# Gitignore for Perl 6 (http://www.perl6.org) +# As part of https://github.com/github/gitignore + +# precompiled files +.precomp +lib/.precomp + diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore index 312d1f652c6..b8e04d98e33 100644 --- a/vendor/gitignore/Swift.gitignore +++ b/vendor/gitignore/Swift.gitignore @@ -47,6 +47,9 @@ playground.xcworkspace # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage # diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore index 3d12d3f90ad..79a66f9ebfa 100644 --- a/vendor/gitignore/TeX.gitignore +++ b/vendor/gitignore/TeX.gitignore @@ -226,6 +226,9 @@ TSWLatexianTemp* # Texpad .texpadtmp +# LyX +*.lyx~ + # Kile *.backup @@ -241,6 +244,3 @@ TSWLatexianTemp* # standalone packages *.sta - -# generated if using elsarticle.cls -*.spl diff --git a/vendor/gitignore/Typo3.gitignore b/vendor/gitignore/Typo3.gitignore index cb024fefe99..200c2a2bf79 100644 --- a/vendor/gitignore/Typo3.gitignore +++ b/vendor/gitignore/Typo3.gitignore @@ -8,12 +8,15 @@ /typo3conf/temp_CACHED* /typo3conf/temp_fieldInfo.php /typo3conf/deprecation_*.log -/typo3conf/AdditionalConfiguration.php +/typo3conf/ENABLE_INSTALL_TOOL +/typo3conf/realurl_autoconf.php +/FIRST_INSTALL # Ignore system folders, you should have them symlinked. # If not comment out the following entries. /typo3 /typo3_src /typo3_src-* +/Packages /.htaccess /index.php # Ignore temp directory. diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore index 10fc2b4d825..cd90af3071a 100644 --- a/vendor/gitignore/Umbraco.gitignore +++ b/vendor/gitignore/Umbraco.gitignore @@ -19,7 +19,7 @@ !**/App_Data/[Pp]ackages/* !**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/* -# ImageProcessor DiskCache +# ImageProcessor DiskCache **/App_Data/cache/ # Ignore the Models Builder models out of date flag diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore index 1daca8b50d9..6582eaf9a11 100644 --- a/vendor/gitignore/UnrealEngine.gitignore +++ b/vendor/gitignore/UnrealEngine.gitignore @@ -1,9 +1,6 @@ # Visual Studio 2015 user specific files .vs/ -# Visual Studio 2015 database file -*.VC.db - # Compiled Object files *.slo *.lo diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore index 1e9abf78d69..f431ddc7cf5 100644 --- a/vendor/gitignore/VisualStudio.gitignore +++ b/vendor/gitignore/VisualStudio.gitignore @@ -220,7 +220,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -316,7 +316,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -325,5 +325,5 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 0d58a00482a..ee0df7711e7 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -105,11 +105,14 @@ code_quality: - code_quality artifacts: paths: [gl-code-quality-report.json] + only: + - branches except: variables: - $CODE_QUALITY_DISABLED license_management: + stage: test image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -121,6 +124,8 @@ license_management: - license_management artifacts: paths: [gl-license-management-report.json] + only: + - branches except: variables: - $LICENSE_MANAGEMENT_DISABLED @@ -161,6 +166,8 @@ sast: - sast artifacts: paths: [gl-sast-report.json] + only: + - branches except: variables: - $SAST_DISABLED @@ -178,6 +185,8 @@ dependency_scanning: - dependency_scanning artifacts: paths: [gl-dependency-scanning-report.json] + only: + - branches except: variables: - $DEPENDENCY_SCANNING_DISABLED @@ -195,6 +204,8 @@ container_scanning: - container_scanning artifacts: paths: [gl-container-scanning-report.json] + only: + - branches except: variables: - $CONTAINER_SCANNING_DISABLED @@ -365,6 +376,7 @@ production_manual: kubernetes: active variables: - $STAGING_ENABLED + - $CANARY_ENABLED except: variables: - $INCREMENTAL_ROLLOUT_ENABLED diff --git a/vendor/licenses.csv b/vendor/licenses.csv index dada0da0b31..7503160baa0 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -7,7 +7,7 @@ @babel/template,7.0.0-beta.44,MIT @babel/traverse,7.0.0-beta.44,MIT @babel/types,7.0.0-beta.44,MIT -@gitlab-org/gitlab-svgs,1.23.0,SEE LICENSE IN LICENSE +@gitlab-org/gitlab-svgs,1.25.0,SEE LICENSE IN LICENSE @sindresorhus/is,0.7.0,MIT @types/jquery,2.0.48,MIT @vue/component-compiler-utils,1.2.1,MIT @@ -35,7 +35,7 @@ abbrev,1.1.1,ISC accepts,1.3.4,MIT ace-rails-ap,4.1.2,MIT acorn,3.3.0,MIT -acorn,5.5.3,MIT +acorn,5.6.2,MIT acorn-dynamic-import,3.0.0,MIT acorn-jsx,3.0.1,MIT actionmailer,4.2.10,MIT @@ -51,14 +51,12 @@ addressparser,1.0.1,MIT aes_key_wrap,1.0.1,MIT after,0.8.2,MIT agent-base,2.1.1,MIT -ajv,4.11.8,MIT ajv,5.5.2,MIT ajv,6.1.1,MIT ajv-keywords,2.1.1,MIT ajv-keywords,3.1.0,MIT akismet,2.0.0,MIT align-text,0.1.4,MIT -allocations,1.0.5,MIT alphanum-sort,1.0.2,MIT amdefine,1.0.1,BSD-3-Clause OR MIT amqplib,0.5.2,MIT @@ -208,7 +206,7 @@ base64-js,1.2.3,MIT base64id,1.0.0,MIT batch,0.6.1,MIT batch-loader,1.2.1,MIT -bcrypt,3.1.11,MIT +bcrypt,3.1.12,MIT bcrypt-pbkdf,1.0.1,New BSD bcrypt_pbkdf,1.0.0,MIT better-assert,1.0.2,MIT @@ -220,7 +218,6 @@ bitsyntax,0.0.4,Unknown bl,1.1.2,MIT blackst0ne-mermaid,7.1.0-fixed,MIT blob,0.0.4,MIT* -block-stream,0.0.9,ISC bluebird,3.5.1,MIT bn.js,4.11.8,MIT body-parser,1.18.2,MIT @@ -269,7 +266,7 @@ camelcase-keys,2.1.0,MIT caniuse-api,1.6.1,MIT caniuse-db,1.0.30000649,CC-BY-4.0 capture-stack-trace,1.0.0,MIT -carrierwave,1.2.1,MIT +carrierwave,1.2.3,MIT caseless,0.11.0,Apache 2.0 caseless,0.12.0,Apache 2.0 cause,0.1,MIT @@ -399,6 +396,7 @@ dashdash,1.14.1,MIT data-uri-to-buffer,1.2.0,MIT date-format,1.2.0,MIT date-now,0.1.4,MIT +dateformat,3.0.3,MIT de-indent,1.0.2,MIT debug,2.2.0,MIT debug,2.6.8,MIT @@ -457,7 +455,7 @@ domelementtype,1.3.0,Simplified BSD domhandler,2.4.1,Simplified BSD domutils,1.6.2,Simplified BSD doorkeeper,4.3.2,MIT -doorkeeper-openid_connect,1.4.0,MIT +doorkeeper-openid_connect,1.5.0,MIT dot-prop,4.2.0,MIT double-ended-queue,2.1.0-0,MIT dropzone,4.2.0,MIT @@ -507,7 +505,7 @@ eslint-plugin-html,4.0.3,ISC eslint-plugin-import,2.12.0,MIT eslint-plugin-jasmine,2.2.0,MIT eslint-plugin-promise,3.8.0,ISC -eslint-plugin-vue,4.0.1,MIT +eslint-plugin-vue,4.5.0,MIT eslint-restricted-globals,0.1.1,MIT eslint-scope,3.7.1,Simplified BSD eslint-visitor-keys,1.0.0,Apache 2.0 @@ -515,10 +513,9 @@ espree,3.5.4,Simplified BSD esprima,2.7.3,Simplified BSD esprima,3.1.3,Simplified BSD esprima,4.0.0,Simplified BSD -esquery,1.0.0,New BSD -esrecurse,4.1.0,Simplified BSD +esquery,1.0.1,New BSD +esrecurse,4.2.1,Simplified BSD estraverse,1.9.3,Simplified BSD -estraverse,4.1.1,Simplified BSD estraverse,4.2.0,Simplified BSD esutils,2.0.2,Simplified BSD et-orbi,1.0.3,MIT @@ -607,11 +604,10 @@ fresh,0.5.2,MIT from,0.1.7,MIT from2,2.3.0,MIT fs-access,1.0.1,MIT +fs-minipass,1.2.5,ISC fs-write-stream-atomic,1.0.10,ISC fs.realpath,1.0.0,ISC -fsevents,1.1.3,MIT -fstream,1.0.11,ISC -fstream-ignore,1.0.5,ISC +fsevents,1.2.4,MIT ftp,0.3.10,MIT function-bind,1.1.1,MIT functional-red-black-tree,1.0.1,MIT @@ -630,14 +626,14 @@ get_process_mem,0.2.0,MIT getpass,0.1.7,MIT gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails_js,1.3.0,MIT -gitaly-proto,0.100.0,MIT +gitaly-proto,0.105.0,MIT github-linguist,5.3.3,MIT github-markup,1.7.0,MIT gitlab-flowdock-git-hook,1.0.1,MIT -gitlab-gollum-lib,4.2.7.2,MIT -gitlab-gollum-rugged_adapter,0.4.4,MIT +gitlab-gollum-lib,4.2.7.5,MIT +gitlab-gollum-rugged_adapter,0.4.4.1,MIT gitlab-grit,2.8.2,MIT -gitlab-markup,1.6.3,MIT +gitlab-markup,1.6.4,MIT gitlab_omniauth-ldap,2.0.4,MIT glob,5.0.15,ISC glob,7.1.2,ISC @@ -664,7 +660,7 @@ gpgme,2.0.13,LGPL-2.1+ graceful-fs,4.1.11,ISC grape,1.0.3,MIT grape-entity,0.7.1,MIT -grape-path-helpers,1.0.4,MIT +grape-path-helpers,1.0.5,MIT grape_logging,1.7.0,MIT graphiql-rails,1.4.10,MIT graphlib,2.1.1,MIT @@ -674,10 +670,8 @@ gzip-size,4.1.0,MIT hamlit,2.6.1,MIT handle-thing,1.2.5,MIT handlebars,4.0.6,MIT -har-schema,1.0.5,ISC har-schema,2.0.0,ISC har-validator,2.0.6,ISC -har-validator,4.2.1,ISC har-validator,5.0.3,ISC has,1.0.1,MIT has-ansi,2.0.0,MIT @@ -712,7 +706,7 @@ hosted-git-info,2.2.0,ISC hpack.js,2.1.6,MIT html-comment-regex,1.1.1,MIT html-entities,1.2.0,MIT -html-pipeline,2.7.1,MIT +html-pipeline,2.8.3,MIT html2text,0.2.0,MIT htmlentities,4.3.4,MIT htmlparser2,3.9.2,MIT @@ -739,13 +733,14 @@ icalendar,2.4.1,ruby ice_nine,0.11.2,MIT iconv-lite,0.4.15,MIT iconv-lite,0.4.19,MIT +iconv-lite,0.4.23,MIT icss-replace-symbols,1.1.0,ISC icss-utils,2.1.0,ISC ieee754,1.1.11,New BSD -ieee754,1.1.8,New BSD iferr,0.1.5,MIT ignore,3.3.8,MIT ignore-by-default,1.0.1,ISC +ignore-walk,3.0.1,ISC immediate,3.0.6,MIT import-lazy,2.1.0,MIT import-local,1.0.0,MIT @@ -865,16 +860,14 @@ jsesc,1.3.0,MIT jsesc,2.5.1,MIT json,1.8.6,ruby json-buffer,3.0.0,MIT -json-jwt,1.9.2,MIT +json-jwt,1.9.4,MIT json-parse-better-errors,1.0.2,MIT json-schema,0.2.3,BSD json-schema-traverse,0.3.1,MIT -json-stable-stringify,1.0.1,MIT json-stable-stringify-without-jsonify,1.0.1,MIT json-stringify-safe,5.0.1,ISC json3,3.3.2,MIT json5,0.5.1,MIT -jsonify,0.0.0,Public Domain jsonpointer,4.0.1,MIT jsprim,1.4.1,MIT jszip,3.1.3,(MIT OR GPL-3.0) @@ -992,12 +985,14 @@ minimatch,3.0.4,ISC minimist,0.0.10,MIT minimist,0.0.8,MIT minimist,1.2.0,MIT +minipass,2.3.3,ISC +minizlib,1.1.0,MIT mississippi,2.0.0,Simplified BSD mixin-deep,1.3.1,MIT mkdirp,0.5.1,MIT moment,2.19.2,MIT monaco-editor,0.13.1,MIT -monaco-editor-webpack-plugin,1.2.1,MIT +monaco-editor-webpack-plugin,1.4.0,MIT mousetrap,1.4.6,Apache 2.0 mousetrap-rails,1.4.6,"MIT,Apache" move-concurrently,1.0.1,ISC @@ -1012,9 +1007,10 @@ mustermann,1.0.2,MIT mustermann-grape,1.0.0,MIT mute-stream,0.0.7,ISC mysql2,0.4.10,MIT -nan,2.8.0,MIT +nan,2.10.0,MIT nanomatch,1.2.9,MIT natural-compare,1.4.0,MIT +needle,2.2.1,MIT negotiator,0.6.1,MIT neo-async,2.5.0,MIT net-ldap,0.16.0,MIT @@ -1024,7 +1020,7 @@ netrc,0.11.0,MIT nice-try,1.0.4,MIT node-forge,0.6.33,New BSD node-libs-browser,2.1.0,MIT -node-pre-gyp,0.6.39,New BSD +node-pre-gyp,0.10.0,New BSD node-uuid,1.4.8,MIT nodemailer,2.7.2,MIT nodemailer-direct-transport,3.3.2,MIT @@ -1034,7 +1030,8 @@ nodemailer-smtp-pool,2.8.2,MIT nodemailer-smtp-transport,2.7.2,MIT nodemailer-wellknown,0.1.10,MIT nodemon,1.17.3,MIT -nokogiri,1.8.2,MIT +nokogiri,1.8.3,MIT +nokogumbo,1.5.0,Apache 2.0 nopt,1.0.10,MIT nopt,3.0.6,ISC nopt,4.0.1,ISC @@ -1043,6 +1040,8 @@ normalize-path,2.1.1,MIT normalize-range,0.1.2,MIT normalize-url,1.9.1,MIT normalize-url,2.0.1,MIT +npm-bundled,1.0.3,ISC +npm-packlist,1.1.10,ISC npm-run-path,2.0.2,MIT npmlog,4.1.2,ISC null-check,1.0.0,MIT @@ -1076,7 +1075,7 @@ omniauth-oauth,1.1.0,MIT omniauth-oauth2,1.5.0,MIT omniauth-oauth2-generic,0.2.2,MIT omniauth-saml,1.10.0,MIT -omniauth-shibboleth,1.2.1,MIT +omniauth-shibboleth,1.3.0,MIT omniauth-twitter,1.4.0,MIT omniauth_crowd,2.2.3,MIT on-finished,2.3.0,MIT @@ -1137,7 +1136,6 @@ peek-pg,1.3.0,MIT peek-rblineprof,0.2.0,MIT peek-redis,1.2.0,MIT peek-sidekiq,1.0.3,MIT -performance-now,0.2.0,MIT performance-now,2.1.0,MIT pg,0.18.4,"BSD,ruby,GPL" pify,2.3.0,MIT @@ -1194,7 +1192,6 @@ premailer-rails,1.9.7,MIT prepend-http,1.0.4,MIT prepend-http,2.0.0,MIT preserve,0.2.0,MIT -prettier,1.11.1,MIT prettier,1.12.1,MIT prismjs,1.6.0,MIT private,0.1.8,MIT @@ -1222,7 +1219,6 @@ q,1.4.1,MIT q,1.5.0,MIT qjobs,1.2.0,MIT qs,6.2.3,New BSD -qs,6.4.0,New BSD qs,6.5.1,New BSD query-string,4.3.2,MIT query-string,5.1.1,MIT @@ -1303,11 +1299,9 @@ repeat-string,1.6.1,MIT repeating,2.0.1,MIT representable,3.0.4,MIT request,2.75.0,Apache 2.0 -request,2.81.0,Apache 2.0 request,2.83.0,Apache 2.0 request_store,1.3.1,MIT requestretry,1.13.0,MIT -require-all,2.2.0,MIT require-directory,2.1.1,MIT require-main-filename,1.0.1,ISC require-uncached,1.0.3,MIT @@ -1341,24 +1335,26 @@ ruby_parser,3.9.0,MIT rubyntlm,0.6.2,MIT rubypants,0.2.0,BSD rufus-scheduler,3.4.0,MIT -rugged,0.27.1,MIT +rugged,0.27.2,MIT run-async,2.3.0,MIT run-queue,1.0.3,ISC rx-lite,4.0.8,Apache 2.0 rx-lite-aggregates,4.0.8,Apache 2.0 rxjs,5.5.10,Apache 2.0 safe-buffer,5.1.1,MIT +safe-buffer,5.1.2,MIT safe-regex,1.1.0,MIT safe_yaml,1.0.4,MIT -sanitize,2.1.0,MIT +safer-buffer,2.1.2,MIT +sanitize,4.6.5,MIT sanitize-html,1.16.3,MIT sass,3.5.5,MIT sass-listen,4.0.0,MIT sass-rails,5.0.6,MIT sawyer,0.8.1,MIT sax,1.2.2,ISC +sax,1.2.4,ISC schema-utils,0.4.5,MIT -securecompare,1.0.0,MIT seed-fu,2.3.7,MIT select,1.1.2,MIT select-hose,2.0.0,MIT @@ -1433,7 +1429,7 @@ spdy-transport,2.0.20,MIT split,0.3.3,MIT split-string,3.1.0,MIT sprintf-js,1.0.3,New BSD -sprockets,3.7.1,MIT +sprockets,3.7.2,MIT sprockets-rails,3.2.1,MIT sql.js,0.4.0,MIT srcset,1.0.0,MIT @@ -1479,8 +1475,7 @@ sys-filesystem,1.1.6,Artistic 2.0 table,4.0.2,New BSD tapable,0.1.10,MIT tapable,1.0.0,MIT -tar,2.2.1,ISC -tar-pack,3.4.1,Simplified BSD +tar,4.4.4,ISC temple,0.7.7,MIT term-size,1.2.0,MIT test-exclude,4.2.1,ISC @@ -1535,7 +1530,6 @@ uglify-es,3.3.9,Simplified BSD uglify-js,2.8.29,Simplified BSD uglify-to-browserify,1.0.2,MIT uglifyjs-webpack-plugin,1.2.5,MIT -uid-number,0.0.6,ISC ultron,1.1.1,MIT undefsafe,2.0.2,MIT underscore,1.7.0,MIT @@ -1566,7 +1560,6 @@ url-parse,1.1.9,MIT url-parse-lax,1.0.0,MIT url-parse-lax,3.0.0,MIT url-to-options,1.0.1,MIT -url_safe_base64,0.2.2,MIT use,2.0.2,MIT useragent,2.2.1,MIT util,0.10.3,MIT @@ -1640,6 +1633,7 @@ xtend,4.0.1,MIT y18n,3.2.1,ISC y18n,4.0.0,ISC yallist,2.1.2,ISC +yallist,3.0.2,ISC yargs,11.0.0,MIT yargs,11.1.0,MIT yargs,3.10.0,MIT diff --git a/yarn.lock b/yarn.lock index 5df2a9a2eb8..d844ae4f8e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5272,9 +5272,9 @@ moment@2.x, moment@^2.18.1: version "2.19.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" -monaco-editor-webpack-plugin@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.2.1.tgz#577ed091420f422bb8f0ff3a8899dd82344da56d" +monaco-editor-webpack-plugin@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f" monaco-editor@0.13.1: version "0.13.1" |