diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2018-07-09 10:21:18 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-07-09 10:21:18 +0100 |
commit | 81b9b6f4095a430d2afe54a95ea1fa172fe9c0e1 (patch) | |
tree | 8d15b3a4ca984bff193ee0807ab48d7e1cad7d78 | |
parent | 156a9d39131b6193e0e6bc5ec7f1683305166fa8 (diff) | |
parent | dc71b4004bd9773d03d5a4eeaba2db883e5a2251 (diff) | |
download | gitlab-ce-81b9b6f4095a430d2afe54a95ea1fa172fe9c0e1.tar.gz |
Merge branch 'master' into 48960-namespace-diff-module
* master: (29 commits)
Update the dependencies license list for 11.1.0
Update .gitignore, .gitlab-ci.yml, and Dockerfile templates for 11.1.0
This updates only the actual new discussion and not the full tree , which leads to a very costly full rerender
Resolve "MR Refactor: Improve performance by setting v-once"
Changed Inline + Parallel Views to v-if instead of v-show
add basic export to fix timeout problem on import_file_spec.rb
Add changelog entry for !20465
Improve render performance of large wiki pages
Refactor rspec matchers in read_only_spec.rb
add CHANGELOG.md entry for !20461
resolve node 6 compatibility issues
Add missing foreign key in import_export_uploads
Redesign for mr widget info and pipelines section
Use proper markdown rendering for previews
remove extra tick for eks docs
Make it clear that we need to enable omniauth for SAML and Bitbucket
Add GPL Commitment language
Add ExclusiveLease guards for RepositoryCheck::{DispatchWorker,BatchWorker}
Ability to check if underlying database is read only
fix spec
...
143 files changed, 1650 insertions, 581 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index ff58e45400b..02d5be1821b 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -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_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 a73f898e10b..ad838a32518 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -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 fcbb42b101e..db380e68bd1 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -60,7 +60,7 @@ export default { }, methods: { ...mapActions('diffs', ['cancelCommentForm']), - ...mapActions(['saveNote', 'fetchDiscussions']), + ...mapActions(['saveNote', 'refetchDiscussionById']), handleCancelCommentForm() { this.autosave.reset(); this.cancelCommentForm({ @@ -79,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_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index 7188371692f..8e4715c9862 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -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/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index 236a51be355..b76fc63205b 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -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/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/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 5f9894f1168..7cd600fec5b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -83,7 +83,7 @@ class Repository @raw_repository&.cleanup end - # Return absolute path to repository + # Don't use this! It's going away. Use Gitaly to read or write from repos. def path_to_repo @path_to_repo ||= begin @@ -250,7 +250,7 @@ class Repository # This will still fail if the file is corrupted (e.g. 0 bytes) raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false) rescue Gitlab::Git::CommandError => ex - Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}" + Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" end def kept_around?(sha) @@ -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/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/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/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/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/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/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/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/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/user/project/clusters/index.md b/doc/user/project/clusters/index.md index b25b09f7b1f..377e285a731 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -206,7 +206,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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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" |