Commit 81b9b6f4 authored by Filipa Lacerda's avatar Filipa Lacerda

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
  ...
parents 156a9d39 dc71b400
...@@ -39,12 +39,12 @@ export default { ...@@ -39,12 +39,12 @@ export default {
<div class="diff-viewer"> <div class="diff-viewer">
<template v-if="isTextFile"> <template v-if="isTextFile">
<inline-diff-view <inline-diff-view
v-show="isInlineView" v-if="isInlineView"
:diff-file="diffFile" :diff-file="diffFile"
:diff-lines="diffFile.highlightedDiffLines || []" :diff-lines="diffFile.highlightedDiffLines || []"
/> />
<parallel-diff-view <parallel-diff-view
v-show="isParallelView" v-if="isParallelView"
:diff-file="diffFile" :diff-file="diffFile"
:diff-lines="diffFile.parallelDiffLines || []" :diff-lines="diffFile.parallelDiffLines || []"
/> />
......
...@@ -145,6 +145,7 @@ export default { ...@@ -145,6 +145,7 @@ export default {
@click.stop="handleToggle" @click.stop="handleToggle"
/> />
<a <a
v-once
ref="titleWrapper" ref="titleWrapper"
:href="titleLink" :href="titleLink"
class="append-right-4" class="append-right-4"
......
...@@ -189,6 +189,7 @@ export default { ...@@ -189,6 +189,7 @@ export default {
</button> </button>
<a <a
v-if="lineNumber" v-if="lineNumber"
v-once
:data-linenumber="lineNumber" :data-linenumber="lineNumber"
:href="lineHref" :href="lineHref"
> >
......
...@@ -60,7 +60,7 @@ export default { ...@@ -60,7 +60,7 @@ export default {
}, },
methods: { methods: {
...mapActions('diffs', ['cancelCommentForm']), ...mapActions('diffs', ['cancelCommentForm']),
...mapActions(['saveNote', 'fetchDiscussions']), ...mapActions(['saveNote', 'refetchDiscussionById']),
handleCancelCommentForm() { handleCancelCommentForm() {
this.autosave.reset(); this.autosave.reset();
this.cancelCommentForm({ this.cancelCommentForm({
...@@ -79,10 +79,10 @@ export default { ...@@ -79,10 +79,10 @@ export default {
}); });
this.saveNote(postData) this.saveNote(postData)
.then(() => { .then(result => {
const endpoint = this.getNotesDataByProp('discussionsPath'); const endpoint = this.getNotesDataByProp('discussionsPath');
this.fetchDiscussions(endpoint) this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id })
.then(() => { .then(() => {
this.handleCancelCommentForm(); this.handleCancelCommentForm();
}) })
......
...@@ -117,14 +117,6 @@ export default { ...@@ -117,14 +117,6 @@ export default {
<template> <template>
<td <td
v-if="isContentLine"
:class="lineType"
class="line_content"
v-html="normalizedLine.richText"
>
</td>
<td
v-else
:class="classNameMap" :class="classNameMap"
> >
<diff-line-gutter-content <diff-line-gutter-content
......
...@@ -94,11 +94,12 @@ export default { ...@@ -94,11 +94,12 @@ export default {
:is-hover="isHover" :is-hover="isHover"
class="diff-line-num new_line" class="diff-line-num new_line"
/> />
<diff-table-cell <td
v-once
:class="line.type" :class="line.type"
:diff-file="diffFile" class="line_content"
:line="line" v-html="line.richText"
:is-content-line="true" >
/> </td>
</tr> </tr>
</template> </template>
...@@ -113,17 +113,15 @@ export default { ...@@ -113,17 +113,15 @@ export default {
:diff-view-type="parallelDiffViewType" :diff-view-type="parallelDiffViewType"
class="diff-line-num old_line" class="diff-line-num old_line"
/> />
<diff-table-cell <td
v-once
:id="line.left.lineCode" :id="line.left.lineCode"
:diff-file="diffFile" :class="parallelViewLeftLineType"
:line="line"
:is-content-line="true"
:line-position="linePositionLeft"
:line-type="parallelViewLeftLineType"
:diff-view-type="parallelDiffViewType"
class="line_content parallel left-side" class="line_content parallel left-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown.native="handleParallelLineMouseDown"
/> v-html="line.left.richText"
>
</td>
<diff-table-cell <diff-table-cell
:diff-file="diffFile" :diff-file="diffFile"
:line="line" :line="line"
...@@ -135,16 +133,14 @@ export default { ...@@ -135,16 +133,14 @@ export default {
:diff-view-type="parallelDiffViewType" :diff-view-type="parallelDiffViewType"
class="diff-line-num new_line" class="diff-line-num new_line"
/> />
<diff-table-cell <td
v-once
:id="line.right.lineCode" :id="line.right.lineCode"
:diff-file="diffFile" :class="line.right.type"
:line="line"
:is-content-line="true"
:line-position="linePositionRight"
:line-type="line.right.type"
:diff-view-type="parallelDiffViewType"
class="line_content parallel right-side" class="line_content parallel right-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown.native="handleParallelLineMouseDown"
/> v-html="line.right.richText"
>
</td>
</tr> </tr>
</template> </template>
...@@ -108,6 +108,11 @@ ...@@ -108,6 +108,11 @@
type: String, type: String,
required: true, required: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
projectPath: { projectPath: {
type: String, type: String,
required: true, required: true,
...@@ -282,6 +287,7 @@ ...@@ -282,6 +287,7 @@
:issuable-templates="issuableTemplates" :issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-version="markdownVersion"
:project-path="projectPath" :project-path="projectPath"
:project-namespace="projectNamespace" :project-namespace="projectNamespace"
:show-delete-button="showDeleteButton" :show-delete-button="showDeleteButton"
......
...@@ -20,6 +20,11 @@ ...@@ -20,6 +20,11 @@
type: String, type: String,
required: true, required: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
canAttachFile: { canAttachFile: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -47,6 +52,7 @@ ...@@ -47,6 +52,7 @@
<markdown-field <markdown-field
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-version="markdownVersion"
:can-attach-file="canAttachFile" :can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete" :enable-autocomplete="enableAutocomplete"
> >
......
...@@ -35,6 +35,11 @@ ...@@ -35,6 +35,11 @@
type: String, type: String,
required: true, required: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
projectPath: { projectPath: {
type: String, type: String,
required: true, required: true,
...@@ -97,6 +102,7 @@ ...@@ -97,6 +102,7 @@
:form-state="formState" :form-state="formState"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-version="markdownVersion"
:can-attach-file="canAttachFile" :can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete" :enable-autocomplete="enableAutocomplete"
/> />
......
...@@ -1251,13 +1251,15 @@ export default class Notes { ...@@ -1251,13 +1251,15 @@ export default class Notes {
var postUrl = $originalContentEl.data('postUrl'); var postUrl = $originalContentEl.data('postUrl');
var targetId = $originalContentEl.data('targetId'); var targetId = $originalContentEl.data('targetId');
var targetType = $originalContentEl.data('targetType'); var targetType = $originalContentEl.data('targetType');
var markdownVersion = $originalContentEl.data('markdownVersion');
this.glForm = new GLForm($editForm.find('form'), this.enableGFM); this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
$editForm $editForm
.find('form') .find('form')
.attr('action', `${postUrl}?html=true`) .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-id').val(targetId);
$editForm.find('.js-form-target-type').val(targetType); $editForm.find('.js-form-target-type').val(targetType);
$editForm $editForm
......
...@@ -34,6 +34,11 @@ export default { ...@@ -34,6 +34,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
}, },
data() { data() {
return { return {
...@@ -344,6 +349,7 @@ Please check your network connection and try again.`; ...@@ -344,6 +349,7 @@ Please check your network connection and try again.`;
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath" :quick-actions-docs-path="quickActionsDocsPath"
:markdown-version="markdownVersion"
:add-spacing-classes="false"> :add-spacing-classes="false">
<textarea <textarea
id="note-body" id="note-body"
......
...@@ -92,6 +92,7 @@ export default { ...@@ -92,6 +92,7 @@ export default {
:is-editing="isEditing" :is-editing="isEditing"
:note-body="noteBody" :note-body="noteBody"
:note-id="note.id" :note-id="note.id"
:markdown-version="note.cached_markdown_version"
@handleFormUpdate="handleFormUpdate" @handleFormUpdate="handleFormUpdate"
@cancelForm="formCancelHandler" @cancelForm="formCancelHandler"
/> />
......
...@@ -24,6 +24,11 @@ export default { ...@@ -24,6 +24,11 @@ export default {
required: false, required: false,
default: 0, default: 0,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
saveButtonTitle: { saveButtonTitle: {
type: String, type: String,
required: false, required: false,
...@@ -156,6 +161,7 @@ export default { ...@@ -156,6 +161,7 @@ export default {
<markdown-field <markdown-field
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-version="markdownVersion"
:quick-actions-docs-path="quickActionsDocsPath" :quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false"> :add-spacing-classes="false">
<textarea <textarea
......
...@@ -43,6 +43,11 @@ export default { ...@@ -43,6 +43,11 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
}, },
data() { data() {
return { return {
...@@ -192,6 +197,7 @@ export default { ...@@ -192,6 +197,7 @@ export default {
<comment-form <comment-form
:noteable-type="noteableType" :noteable-type="noteableType"
:markdown-version="markdownVersion"
/> />
</div> </div>
</template> </template>
...@@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
const notesDataset = document.getElementById('js-vue-notes').dataset; const notesDataset = document.getElementById('js-vue-notes').dataset;
const parsedUserData = JSON.parse(notesDataset.currentUserData); const parsedUserData = JSON.parse(notesDataset.currentUserData);
const noteableData = JSON.parse(notesDataset.noteableData); const noteableData = JSON.parse(notesDataset.noteableData);
const { markdownVersion } = notesDataset;
let currentUserData = {}; let currentUserData = {};
noteableData.noteableType = notesDataset.noteableType; noteableData.noteableType = notesDataset.noteableType;
...@@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
return { return {
noteableData, noteableData,
currentUserData, currentUserData,
markdownVersion,
notesData: JSON.parse(notesDataset.notesData), notesData: JSON.parse(notesDataset.notesData),
}; };
}, },
...@@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => {
noteableData: this.noteableData, noteableData: this.noteableData,
notesData: this.notesData, notesData: this.notesData,
userData: this.currentUserData, userData: this.currentUserData,
markdownVersion: this.markdownVersion,
}, },
}); });
}, },
......
...@@ -41,6 +41,15 @@ export const fetchDiscussions = ({ commit }, path) => ...@@ -41,6 +41,15 @@ export const fetchDiscussions = ({ commit }, path) =>
commit(types.SET_INITIAL_DISCUSSIONS, discussions); 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) => export const deleteNote = ({ commit }, note) =>
service.deleteNote(note.path).then(() => { service.deleteNote(note.path).then(() => {
commit(types.DELETE_NOTE, note); commit(types.DELETE_NOTE, note);
......
...@@ -114,7 +114,6 @@ export default { ...@@ -114,7 +114,6 @@ export default {
Object.assign(state, { discussions }); Object.assign(state, { discussions });
}, },
[types.SET_LAST_FETCHED_AT](state, fetchedAt) { [types.SET_LAST_FETCHED_AT](state, fetchedAt) {
Object.assign(state, { lastFetchedAt: fetchedAt }); Object.assign(state, { lastFetchedAt: fetchedAt });
}, },
......
...@@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {}; ...@@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {};
MarkdownPreview.prototype.showPreview = function ($form) { MarkdownPreview.prototype.showPreview = function ($form) {
var mdText; var mdText;
var markdownVersion;
var url;
var preview = $form.find('.js-md-preview'); var preview = $form.find('.js-md-preview');
var url = preview.data('url');
if (preview.hasClass('md-preview-loading')) { if (preview.hasClass('md-preview-loading')) {
return; return;
} }
mdText = $form.find('textarea.markdown-area').val(); 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) { if (mdText.trim().length === 0) {
preview.text(this.emptyMessage); preview.text(this.emptyMessage);
...@@ -59,6 +63,14 @@ MarkdownPreview.prototype.showPreview = function ($form) { ...@@ -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) { MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
if (!url) { if (!url) {
return; return;
......
...@@ -79,66 +79,62 @@ export default { ...@@ -79,66 +79,62 @@ export default {
</script> </script>
<template> <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-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="media-body">
<div class="deploy-body"> <div class="deploy-body">
<template v-if="hasDeploymentMeta"> <div class="deployment-info">
<span> <template v-if="hasDeploymentMeta">
Deployed to <span>
</span> Deployed to
<a </span>
:href="deployment.url" <a
target="_blank" :href="deployment.url"
rel="noopener noreferrer nofollow" target="_blank"
class="deploy-link js-deploy-meta" 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 }} {{ deployTimeago }}
</a>
</template>
<template v-if="hasExternalUrls">
<span>
on
</span> </span>
<memory-usage
v-if="hasMetrics"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
<div>
<a <a
v-if="hasExternalUrls"
:href="deployment.external_url" :href="deployment.external_url"
target="_blank" target="_blank"
rel="noopener noreferrer nofollow" 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 }} <span>
<icon View app
:size="16" <icon name="external-link" />
name="external-link" </span>
/>
</a> </a>
</template> <loading-button
<span v-if="deployment.stop_url"
v-tooltip :loading="isStopping"
v-if="hasDeploymentTime" container-class="btn btn-default btn-sm inline prepend-left-4"
:title="deployment.deployed_at_formatted" title="Stop environment"
class="js-deploy-time" @click="stopEnvironment"
> >
{{ deployTimeago }} <icon name="stop" />
</span> </loading-button>
<loading-button </div>
v-if="deployment.stop_url"
:loading="isStopping"
container-class="btn btn-default btn-sm prepend-left-default"
label="Stop environment"
@click="stopEnvironment"
/>
</div> </div>
<memory-usage
v-if="hasMetrics"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import { n__ } from '~/locale'; import { n__ } from '~/locale';
import { webIDEUrl } from '~/lib/utils/url_utility'; 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'; import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default { export default {
...@@ -11,7 +11,7 @@ export default { ...@@ -11,7 +11,7 @@ export default {
tooltip, tooltip,
}, },
components: { components: {
icon, Icon,
clipboardButton, clipboardButton,
}, },
props: { props: {
...@@ -54,104 +54,114 @@ export default { ...@@ -54,104 +54,114 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="mr-source-target"> <div class="mr-source-target append-bottom-default">
<div class="normal"> <div class="git-merge-icon-container append-right-default">
<strong> <icon name="git-merge" />
{{ s__("mrWidget|Request to merge") }} </div>
<span <div class="git-merge-container d-flex">
:class="{ 'label-truncated': isSourceBranchLong }" <div class="normal">
:title="isSourceBranchLong ? mr.sourceBranch : ''" <strong>
:v-tooltip="isSourceBranchLong" {{ s__("mrWidget|Request to merge") }}
class="label-branch js-source-branch" <span
data-placement="bottom" :class="{ 'label-truncated': isSourceBranchLong }"
v-html="mr.sourceBranchLink" :title="isSourceBranchLong ? mr.sourceBranch : ''"
> :v-tooltip="isSourceBranchLong"
</span> class="label-branch js-source-branch"
data-placement="bottom"
v-html="mr.sourceBranchLink"
>
</span>
<clipboard-button <clipboard-button
:text="branchNameClipboardData" :text="branchNameClipboardData"
:title="__('Copy branch name to clipboard')" :title="__('Copy branch name to clipboard')"
css-class="btn-default btn-transparent btn-clipboard" css-class="btn-default btn-transparent btn-clipboard"
/> />
{{ s__("mrWidget|into") }} {{ s__("mrWidget|into") }}
<span <span
:v-tooltip="isTargetBranchLong" :v-tooltip="isTargetBranchLong"
:class="{ 'label-truncatedtooltip': isTargetBranchLong }" :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
:title="isTargetBranchLong ? mr.targetBranch : ''" :title="isTargetBranchLong ? mr.targetBranch : ''"
class="label-branch" class="label-branch"
data-placement="bottom" data-placement="bottom"
>
<a
:href="mr.targetBranchTreePath"
class="js-target-branch"
> >
{{ mr.targetBranch }} <a
</a> :href="mr.targetBranchTreePath"
</span> class="js-target-branch"
</strong> >
<span {{ mr.targetBranch }}
v-if="shouldShowCommitsBehindText" </a>
class="diverged-commits-count" </span>
> </strong>
(<a :href="mr.targetBranchPath">{{ commitsText }}</a>) <div
</span> v-if="shouldShowCommitsBehindText"
</div> 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"> <div
<a v-if="mr.isOpen"
v-if="!mr.sourceBranchRemoved" class="branch-actions"
: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"
> >
{{ s__("mrWidget|Check out branch") }} <a
</button> v-if="!mr.sourceBranchRemoved"
<span class="dropdown prepend-left-10"> :href="webIdePath"
class="btn btn-default inline js-web-ide d-none d-md-inline-block"
>
{{ s__("mrWidget|Open in Web IDE") }}
</a>
<button <button
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
class="btn btn-default inline js-check-out-branch"
type="button" 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" /> {{ s__("mrWidget|Check out branch") }}
<i
class="fa fa-caret-down"
aria-hidden="true">
</i>
</button> </button>
<ul class="dropdown-menu dropdown-menu-right"> <span class="dropdown prepend-left-10">
<li> <button
<a type="button"
:href="mr.emailPatchesPath" class="btn inline dropdown-toggle"
class="js-download-email-patches" data-toggle="dropdown"
download aria-label="Download as"
> aria-haspopup="true"
{{ s__("mrWidget|Email patches") }} aria-expanded="false"
</a> >
</li> <icon name="download" />
<li> <i
<a class="fa fa-caret-down"
:href="mr.plainDiffPath" aria-hidden="true">
class="js-download-plain-diff" </i>
download </button>
> <ul class="dropdown-menu dropdown-menu-right">
{{ s__("mrWidget|Plain diff") }} <li>
</a> <a
</li> :href="mr.emailPatchesPath"
</ul> class="js-download-email-patches"
</span> 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>
</div> </div>
</template> </template>
...@@ -26,6 +26,10 @@ export default { ...@@ -26,6 +26,10 @@ export default {
type: String, type: String,
required: false, required: false,
}, },
sourceBranchLink: {
type: String,
required: false,
},
}, },
computed: { computed: {
hasPipeline() { hasPipeline() {
...@@ -54,12 +58,18 @@ export default { ...@@ -54,12 +58,18 @@ export default {
<template> <template>
<div <div
v-if="hasPipeline || hasCIError" v-if="hasPipeline || hasCIError"
class="mr-widget-heading" class="mr-widget-heading append-bottom-default"
> >
<div class="ci-widget media"> <div class="ci-widget media">
<template v-if="hasCIError"> <template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10"> <div
<icon name="status_failed" /> 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>
<div class="media-body"> <div class="media-body">
Could not connect to the CI server. Please check your settings and try again Could not connect to the CI server. Please check your settings and try again
...@@ -68,50 +78,66 @@ export default { ...@@ -68,50 +78,66 @@ export default {
<template v-else-if="hasPipeline"> <template v-else-if="hasPipeline">
<a <a
:href="status.details_path" :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> </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.details.status.label }}
Pipeline
<a
:href="pipeline.path"
class="pipeline-id"
>
#{{ pipeline.id }}
</a>
{{ pipeline.details.status.label }}
<template v-if="hasCommitInfo"> <template v-if="hasCommitInfo">
for for
<a
<a :href="pipeline.commit.commit_path"
:href="pipeline.commit.commit_path" class="commit-sha js-commit-link font-weight-normal"
class="commit-sha js-commit-link" >
> {{ pipeline.commit.short_id }}</a>
{{ pipeline.commit.short_id }}</a>. on
</template> <span
class="label-branch"
<span class="mr-widget-pipeline-graph"> v-html="sourceBranchLink"
<span >
v-if="hasStages" </span>
class="stage-cell" </template>
> </div>
<div <div
v-for="(stage, i) in pipeline.details.stages" v-if="pipeline.coverage"
:key="i" class="coverage"
class="stage-container dropdown js-mini-pipeline-graph"
> >
<pipeline-stage :stage="stage" /> Coverage {{ pipeline.coverage }}%
</div> </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>
</span> </div>
<template v-if="pipeline.coverage">
Coverage {{ pipeline.coverage }}%
</template>
</div> </div>
</template> </template>
</div> </div>
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
<ci-icon <ci-icon
v-else v-else
:status="statusObj" :status="statusObj"
:size="24"
/> />
<button <button
......
...@@ -252,41 +252,44 @@ export default { ...@@ -252,41 +252,44 @@ export default {
:pipeline="mr.pipeline" :pipeline="mr.pipeline"
:ci-status="mr.ciStatus" :ci-status="mr.ciStatus"
:has-ci="mr.hasCI" :has-ci="mr.hasCI"
:source-branch-link="mr.sourceBranchLink"
/> />
<deployment <deployment
v-for="deployment in mr.deployments" v-for="deployment in mr.deployments"
:key="deployment.id" :key="deployment.id"
:deployment="deployment" :deployment="deployment"
/> />
<div class="mr-widget-section"> <div class="mr-section-container">
<component <div class="mr-widget-section">
:is="componentName" <component
:mr="mr" :is="componentName"
:service="service" :mr="mr"
/> :service="service"
/>
<section <section
v-if="mr.allowCollaboration" v-if="mr.allowCollaboration"
class="mr-info-list mr-links" class="mr-info-list mr-links"
> >
{{ s__("mrWidget|Allows commits from members who can merge to the target branch") }} {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
</section> </section>
<mr-widget-related-links <mr-widget-related-links
v-if="shouldRenderRelatedLinks" v-if="shouldRenderRelatedLinks"
:state="mr.state" :state="mr.state"
:related-links="mr.relatedLinks" :related-links="mr.relatedLinks"
/> />
<source-branch-removal-status <source-branch-removal-status
v-if="shouldRenderSourceBranchRemovalStatus" v-if="shouldRenderSourceBranchRemovalStatus"
/> />
</div> </div>
<div <div
v-if="shouldRenderMergeHelp" v-if="shouldRenderMergeHelp"
class="mr-widget-footer" class="mr-widget-footer"
> >
<mr-widget-merge-help /> <mr-widget-merge-help />
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import { s__ } from '~/locale';
import Flash from '../../../flash'; import Flash from '../../../flash';
import GLForm from '../../../gl_form'; import GLForm from '../../../gl_form';
import markdownHeader from './header.vue'; import markdownHeader from './header.vue';
...@@ -22,6 +23,11 @@ ...@@ -22,6 +23,11 @@
type: String, type: String,
required: true, required: true,
}, },
markdownVersion: {
type: Number,
required: false,
default: 0,
},
addSpacingClasses: { addSpacingClasses: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -92,10 +98,11 @@ ...@@ -92,10 +98,11 @@
if (text) { if (text) {
this.markdownPreviewLoading = true; this.markdownPreviewLoading = true;
this.$http.post(this.markdownPreviewPath, { text }) this.$http
.then(resp => resp.json()) .post(this.versionedPreviewPath(), { text })
.then(data => this.renderMarkdown(data)) .then(resp => resp.json())
.catch(() => new Flash('Error loading markdown preview')); .then(data => this.renderMarkdown(data))
.catch(() => new Flash(s__('Error loading markdown preview')));
} else { } else {
this.renderMarkdown(); this.renderMarkdown();
} }
...@@ -119,6 +126,13 @@ ...@@ -119,6 +126,13 @@
$(this.$refs['markdown-preview']).renderGFM(); $(this.$refs['markdown-preview']).renderGFM();
}); });
}, },
versionedPreviewPath() {
const { markdownPreviewPath, markdownVersion } = this;
return `${markdownPreviewPath}${
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
}markdown_version=${markdownVersion}`;
},
}, },
}; };
</script> </script>
......
...@@ -3,12 +3,20 @@ ...@@ -3,12 +3,20 @@
svg { svg {
fill: $green-500; fill: $green-500;
} }
&.add-border {
@include borderless-status-icon($green-500);
}
} }
.ci-status-icon-failed { .ci-status-icon-failed {
svg { svg {
fill: $gl-danger; fill: $gl-danger;
} }
&.add-border {
@include borderless-status-icon($red-500);
}
} }
.ci-status-icon-pending, .ci-status-icon-pending,
...@@ -17,12 +25,20 @@ ...@@ -17,12 +25,20 @@
svg { svg {
fill: $orange-500; fill: $orange-500;
} }
&.add-border {
@include borderless-status-icon($orange-500);
}
} }
.ci-status-icon-running { .ci-status-icon-running {
svg { svg {
fill: $blue-400; fill: $blue-400;
} }
&.add-border {
@include borderless-status-icon($blue-400);
}
} }
.ci-status-icon-canceled, .ci-status-icon-canceled,
...@@ -30,6 +46,10 @@ ...@@ -30,6 +46,10 @@
svg { svg {
fill: $gl-text-color; fill: $gl-text-color;
} }
&.add-border {
@include borderless-status-icon($gl-text-color);
}
} }
.ci-status-icon-created, .ci-status-icon-created,
...@@ -38,6 +58,10 @@ ...@@ -38,6 +58,10 @@
svg { svg {
fill: $gray-darkest; fill: $gray-darkest;
} }
&.add-border {
@include borderless-status-icon($gray-darkest);
}
} }
.ci-status-icon-manual { .ci-status-icon-manual {
......
...@@ -232,3 +232,10 @@ ...@@ -232,3 +232,10 @@
word-break: break-word; word-break: break-word;
max-width: 100%; max-width: 100%;
} }
@mixin borderless-status-icon($color) {
svg {
border: 1px solid $color;
border-radius: 50%;
}
}
...@@ -350,7 +350,8 @@ code { ...@@ -350,7 +350,8 @@ code {
} }
.commit-sha, .commit-sha,
.ref-name { .ref-name,
.pipeline-number {
@extend .monospace; @extend .monospace;
font-size: 95%; font-size: 95%;
} }
......
...@@ -743,6 +743,7 @@ Pipeline Graph ...@@ -743,6 +743,7 @@ Pipeline Graph
*/ */
$stage-hover-bg: $gray-darker; $stage-hover-bg: $gray-darker;
$ci-action-icon-size: 22px; $ci-action-icon-size: 22px;
$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px; $pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px; $pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px; $ci-action-dropdown-button-size: 24px;
......
...@@ -15,16 +15,38 @@ ...@@ -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 { .mr-state-widget {
color: $gl-text-color; color: $gl-text-color;
border: 1px solid $border-color;
border-radius: 2px;
line-height: 28px;
.mr-widget-heading,
.mr-widget-section, .mr-widget-section,
.mr-widget-footer { .mr-widget-footer {
padding: $gl-padding;
border-top: solid 1px $border-color; border-top: solid 1px $border-color;
} }
...@@ -124,10 +146,17 @@ ...@@ -124,10 +146,17 @@
.ci-widget { .ci-widget {
color: $gl-text-color; color: $gl-text-color;
display: flex; display: flex;
align-items: center;
justify-content: space-between;
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
flex-wrap: wrap; flex-wrap: wrap;
} }
.ci-widget-content {
display: flex;
align-items: center;
}
} }
.mr-widget-icon { .mr-widget-icon {
...@@ -136,8 +165,6 @@ ...@@ -136,8 +165,6 @@
} }
.ci-status-icon svg { .ci-status-icon svg {
width: $status-icon-size;
height: $status-icon-size;
margin: 3px 0; margin: 3px 0;
position: relative; position: relative;
overflow: visible; overflow: visible;
...@@ -145,8 +172,6 @@ ...@@ -145,8 +172,6 @@
} }
.mr-widget-pipeline-graph { .mr-widget-pipeline-graph {
padding: 0 4px;
.dropdown-menu { .dropdown-menu {
z-index: 300; z-index: 300;
} }
...@@ -157,7 +182,7 @@ ...@@ -157,7 +182,7 @@
} }
.normal { .normal {
line-height: 28px; flex: 1;
} }
.capitalize { .capitalize {
...@@ -168,7 +193,7 @@ ...@@ -168,7 +193,7 @@
@extend .ref-name; @extend .ref-name;
color: $gl-text-color; color: $gl-text-color;
font-weight: $gl-font-weight-bold; font-weight: normal;
overflow: hidden; overflow: hidden;
word-break: break-all; word-break: break-all;
...@@ -192,6 +217,8 @@ ...@@ -192,6 +217,8 @@
} }
.mr-widget-body { .mr-widget-body {
line-height: 28px;
@include clearfix; @include clearfix;
&.media > *:first-child { &.media > *:first-child {
...@@ -474,18 +501,66 @@ ...@@ -474,18 +501,66 @@
} }
} }
.merge-request-details .content-block {
border-bottom: 0;
}
.mr-source-target { .mr-source-target {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; border-radius: $border-radius-default;
align-items: center; padding: $gl-padding;
background-color: $gray-light; border: 1px solid $border-color;
border-radius: $border-radius-default $border-radius-default 0 0; min-height: 69px;
padding: $gl-padding / 2 $gl-padding;
@include media-breakpoint-up(md) {
align-items: center;
}
.dropdown-toggle .fa { .dropdown-toggle .fa {
color: $gl-text-color; 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 { .card-new-merge-request {
...@@ -720,13 +795,25 @@ ...@@ -720,13 +795,25 @@
} }
.deploy-heading { .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 { .media-body {
min-width: 0; min-width: 0;
font-size: 12px;
margin-left: 48px;
} }
} }
.deploy-body { .deploy-body {
display: flex; display: flex;
align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
@include media-breakpoint-up(xs) { @include media-breakpoint-up(xs) {
...@@ -734,6 +821,15 @@ ...@@ -734,6 +821,15 @@
white-space: nowrap; white-space: nowrap;
} }
@include media-breakpoint-down(md) {
flex-direction: column;
align-items: flex-start;
.deployment-info {
margin-bottom: $gl-padding;
}
}
> *:not(:last-child) { > *:not(:last-child) {
margin-right: .3em; margin-right: .3em;
} }
...@@ -741,18 +837,22 @@ ...@@ -741,18 +837,22 @@
svg { svg {
vertical-align: text-top; vertical-align: text-top;
} }
}
.deploy-link { .deployment-info {
white-space: nowrap; flex: 1;
overflow: hidden; white-space: nowrap;
text-overflow: ellipsis; overflow: hidden;
min-width: 100px; text-overflow: ellipsis;
max-width: 150px; min-width: 100px;
@include media-breakpoint-up(xs) { @include media-breakpoint-up(xs) {
min-width: 0; min-width: 0;
max-width: 100%; max-width: 100%;
}
}
.btn svg {
fill: $theme-gray-700;
} }
} }
...@@ -772,3 +872,33 @@ ...@@ -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;
}
}
...@@ -301,6 +301,21 @@ ...@@ -301,6 +301,21 @@
border-bottom: 2px solid $border-color; 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;
}
}
}
} }
} }
......
...@@ -14,6 +14,8 @@ module PreviewMarkdown ...@@ -14,6 +14,8 @@ module PreviewMarkdown
else {} else {}
end end
markdown_params[:markdown_engine] = result[:markdown_engine]
render json: { render json: {
body: view_context.markdown(result[:text], markdown_params), body: view_context.markdown(result[:text], markdown_params),
references: { references: {
......
...@@ -2,6 +2,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -2,6 +2,7 @@ class ProjectsController < Projects::ApplicationController
include IssuableCollections include IssuableCollections
include ExtractsPath include ExtractsPath
include PreviewMarkdown include PreviewMarkdown
include SendFileUpload
before_action :whitelist_query_limiting, only: [:create] before_action :whitelist_query_limiting, only: [:create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs] before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
...@@ -188,9 +189,9 @@ class ProjectsController < Projects::ApplicationController ...@@ -188,9 +189,9 @@ class ProjectsController < Projects::ApplicationController
end end
def download_export def download_export
export_project_path = @project.export_project_path if export_project_object_storage?
send_upload(@project.import_export_upload.export_file)
if export_project_path elsif export_project_path
send_file export_project_path, disposition: 'attachment' send_file export_project_path, disposition: 'attachment'
else else
redirect_to( redirect_to(
...@@ -265,8 +266,6 @@ class ProjectsController < Projects::ApplicationController ...@@ -265,8 +266,6 @@ class ProjectsController < Projects::ApplicationController
render json: options.to_json render json: options.to_json
end end
private
# Render project landing depending of which features are available # Render project landing depending of which features are available
# So if page is not availble in the list it renders the next page # So if page is not availble in the list it renders the next page
# #
...@@ -424,4 +423,12 @@ class ProjectsController < Projects::ApplicationController ...@@ -424,4 +423,12 @@ class ProjectsController < Projects::ApplicationController
def whitelist_query_limiting def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440') Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
end 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 end
...@@ -249,6 +249,7 @@ module IssuablesHelper ...@@ -249,6 +249,7 @@ module IssuablesHelper
issuableRef: issuable.to_reference, issuableRef: issuable.to_reference,
markdownPreviewPath: preview_markdown_path(parent), markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'), markdownDocsPath: help_page_path('user/markdown'),
markdownVersion: issuable.cached_markdown_version,
issuableTemplates: issuable_templates(issuable), issuableTemplates: issuable_templates(issuable),
initialTitleHtml: markdown_field(issuable, :title), initialTitleHtml: markdown_field(issuable, :title),
initialTitleText: issuable.title, initialTitleText: issuable.title,
......
...@@ -107,6 +107,7 @@ module MarkupHelper ...@@ -107,6 +107,7 @@ module MarkupHelper
def markup(file_name, text, context = {}) def markup(file_name, text, context = {})
context[:project] ||= @project context[:project] ||= @project
context[:markdown_engine] ||= :redcarpet
html = context.delete(:rendered) || markup_unsafe(file_name, text, context) html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context) prepare_for_rendering(html, context)
end end
...@@ -120,7 +121,8 @@ module MarkupHelper ...@@ -120,7 +121,8 @@ module MarkupHelper
project: @project, project: @project,
project_wiki: @project_wiki, project_wiki: @project_wiki,
page_slug: wiki_page.slug, page_slug: wiki_page.slug,
issuable_state_filter_enabled: true issuable_state_filter_enabled: true,
markdown_engine: :redcarpet
} }
html = html =
......
...@@ -169,6 +169,7 @@ module NotesHelper ...@@ -169,6 +169,7 @@ module NotesHelper
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'), registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'), newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),
markdownDocsPath: help_page_path('user/markdown'), markdownDocsPath: help_page_path('user/markdown'),
markdownVersion: issuable.cached_markdown_version,
quickActionsDocsPath: help_page_path('user/project/quick_actions'), quickActionsDocsPath: help_page_path('user/project/quick_actions'),
closePath: close_issuable_path(issuable), closePath: close_issuable_path(issuable),
reopenPath: reopen_issuable_path(issuable), reopenPath: reopen_issuable_path(issuable),
......
...@@ -153,7 +153,7 @@ class NotifyPreview < ActionMailer::Preview ...@@ -153,7 +153,7 @@ class NotifyPreview < ActionMailer::Preview
cleanup do cleanup do
note = yield note = yield
Notify.public_send(method, user.id, note) Notify.public_send(method, user.id, note) # rubocop:disable GitlabSecurity/PublicSend
end end
end end
......
...@@ -40,6 +40,18 @@ module CacheMarkdownField ...@@ -40,6 +40,18 @@ module CacheMarkdownField
end end
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? def skip_project_check?
false false
end end
...@@ -57,7 +69,7 @@ module CacheMarkdownField ...@@ -57,7 +69,7 @@ module CacheMarkdownField
# Banzai is less strict about authors, so don't always have an author key # Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author) 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 context
end end
...@@ -123,14 +135,6 @@ module CacheMarkdownField ...@@ -123,14 +135,6 @@ module CacheMarkdownField
end end
end end
def markdown_engine
if latest_cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
:redcarpet
else
:common_mark
end
end
included do included do
cattr_reader :cached_markdown_fields do cattr_reader :cached_markdown_fields do
FieldData.new FieldData.new
......
...@@ -7,7 +7,7 @@ module CacheableAttributes ...@@ -7,7 +7,7 @@ module CacheableAttributes
class_methods do class_methods do
def cache_key def cache_key
"#{name}:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}".freeze "#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
end end
# Can be overriden # Can be overriden
...@@ -69,6 +69,6 @@ module CacheableAttributes ...@@ -69,6 +69,6 @@ module CacheableAttributes
end end
def cache! def cache!
Rails.cache.write(self.class.cache_key, self) Rails.cache.write(self.class.cache_key, self, expires_in: 1.minute)
end end
end end
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
...@@ -171,6 +171,7 @@ class Project < ActiveRecord::Base ...@@ -171,6 +171,7 @@ class Project < ActiveRecord::Base
has_one :fork_network, through: :fork_network_member has_one :fork_network, through: :fork_network_member
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project 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 # Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id' has_many :merge_requests, foreign_key: 'target_project_id'
...@@ -1712,7 +1713,7 @@ class Project < ActiveRecord::Base ...@@ -1712,7 +1713,7 @@ class Project < ActiveRecord::Base
:started :started
elsif after_export_in_progress? elsif after_export_in_progress?
:after_export_action :after_export_action
elsif export_project_path elsif export_project_path || export_project_object_exists?
:finished :finished
else else
:none :none
...@@ -1727,16 +1728,21 @@ class Project < ActiveRecord::Base ...@@ -1727,16 +1728,21 @@ class Project < ActiveRecord::Base
import_export_shared.after_export_in_progress? import_export_shared.after_export_in_progress?
end end
def remove_exports def remove_exports(path = export_path)
return nil unless export_path.present? if path.present?
FileUtils.rm_rf(path)
FileUtils.rm_rf(export_path) elsif export_project_object_exists?
import_export_upload.remove_export_file!
import_export_upload.save
end
end end
def remove_exported_project_file 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 end
def full_path_slug def full_path_slug
......
...@@ -83,7 +83,7 @@ class Repository ...@@ -83,7 +83,7 @@ class Repository
@raw_repository&.cleanup @raw_repository&.cleanup
end 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 def path_to_repo
@path_to_repo ||= @path_to_repo ||=
begin begin
...@@ -250,7 +250,7 @@ class Repository ...@@ -250,7 +250,7 @@ class Repository
# This will still fail if the file is corrupted (e.g. 0 bytes) # 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) raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
rescue Gitlab::Git::CommandError => ex 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 end
def kept_around?(sha) def kept_around?(sha)
...@@ -564,7 +564,7 @@ class Repository ...@@ -564,7 +564,7 @@ class Repository
end end
def rendered_readme 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 end
cache_method :rendered_readme cache_method :rendered_readme
......
...@@ -62,6 +62,8 @@ class NoteEntity < API::Entities::Note ...@@ -62,6 +62,8 @@ class NoteEntity < API::Entities::Note
expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? } expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? }
expose :cached_markdown_version
private private
def current_user def current_user
......
...@@ -10,7 +10,9 @@ class ImportExportCleanUpService ...@@ -10,7 +10,9 @@ class ImportExportCleanUpService
def execute def execute
Gitlab::Metrics.measure(:import_export_clean_up) do 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 clean_up_export_files
end end
...@@ -21,4 +23,11 @@ class ImportExportCleanUpService ...@@ -21,4 +23,11 @@ class ImportExportCleanUpService
def clean_up_export_files def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete)) Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end 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 end
...@@ -6,7 +6,8 @@ class PreviewMarkdownService < BaseService ...@@ -6,7 +6,8 @@ class PreviewMarkdownService < BaseService
success( success(
text: text, text: text,
users: users, users: users,
commands: commands.join(' ') commands: commands.join(' '),
markdown_engine: markdown_engine
) )
end end
...@@ -42,4 +43,8 @@ class PreviewMarkdownService < BaseService ...@@ -42,4 +43,8 @@ class PreviewMarkdownService < BaseService
def commands_target_id def commands_target_id
params[:quick_actions_target_id] params[:quick_actions_target_id]
end end
def markdown_engine
CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
end
end end
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
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
%li Any encrypted tokens %li Any encrypted tokens
%p %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. 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), = link_to 'Download export', download_export_project_path(project),
rel: 'nofollow', download: '', method: :get, class: "btn btn-default" rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
= link_to 'Generate new export', generate_new_export_project_path(project), = link_to 'Generate new export', generate_new_export_project_path(project),
......
= 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 = render 'shared/issuable/form', f: f, issuable: @issue
= 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 = render 'shared/issuable/form', f: f, issuable: @merge_request
= 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) = form_errors(@milestone)
.row .row
.col-md-6 .col-md-6
......
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
%strong= @tag.name %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 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 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
= render 'shared/notes/hints' = render 'shared/notes/hints'
......
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}") - commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
- commit_message = commit_message % { page_title: @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) = form_errors(@page)
- if @page.persisted? - if @page.persisted?
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
.note-text.md .note-text.md
= markdown_field(note, :note) = markdown_field(note, :note)
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago') = 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} #{note.note}
- if note_editable - if note_editable
= render 'shared/notes/edit', note: note = render 'shared/notes/edit', note: note
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
.snippet-form-holder .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_errors(@snippet)
.form-group.row .form-group.row
......
...@@ -4,9 +4,11 @@ module RepositoryCheck ...@@ -4,9 +4,11 @@ module RepositoryCheck
class BatchWorker class BatchWorker
include ApplicationWorker include ApplicationWorker
include RepositoryCheckQueue include RepositoryCheckQueue
include ExclusiveLeaseGuard
RUN_TIME = 3600 RUN_TIME = 3600
BATCH_SIZE = 10_000 BATCH_SIZE = 10_000
LEASE_TIMEOUT = 1.hour
attr_reader :shard_name attr_reader :shard_name
...@@ -16,6 +18,20 @@ module RepositoryCheck ...@@ -16,6 +18,20 @@ module RepositoryCheck
return unless Gitlab::CurrentSettings.repository_checks_enabled return unless Gitlab::CurrentSettings.repository_checks_enabled
return unless Gitlab::ShardHealthCache.healthy_shard?(shard_name) 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 start = Time.now
# This loop will break after a little more than one hour ('a little # This loop will break after a little more than one hour ('a little
...@@ -26,7 +42,7 @@ module RepositoryCheck ...@@ -26,7 +42,7 @@ module RepositoryCheck
project_ids.each do |project_id| project_ids.each do |project_id|
break if Time.now - start >= RUN_TIME 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) SingleRepositoryWorker.new.perform(project_id)
end end
...@@ -60,7 +76,7 @@ module RepositoryCheck ...@@ -60,7 +76,7 @@ module RepositoryCheck
Project.where(repository_storage: shard_name) Project.where(repository_storage: shard_name)
end 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 # 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. # super slow we definitely do not want to run it twice in parallel.
Gitlab::ExclusiveLease.new( Gitlab::ExclusiveLease.new(
......
...@@ -3,13 +3,22 @@ module RepositoryCheck ...@@ -3,13 +3,22 @@ module RepositoryCheck
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
include ::EachShardWorker include ::EachShardWorker
include ExclusiveLeaseGuard
LEASE_TIMEOUT = 1.hour
def perform def perform
return unless Gitlab::CurrentSettings.repository_checks_enabled return unless Gitlab::CurrentSettings.repository_checks_enabled
each_eligible_shard do |shard_name| try_obtain_lease do
RepositoryCheck::BatchWorker.perform_async(shard_name) each_eligible_shard do |shard_name|
RepositoryCheck::BatchWorker.perform_async(shard_name)
end
end end
end end
def lease_timeout
LEASE_TIMEOUT
end
end end
end end
---
title: Add Object Storage to project export
merge_request: 20105
author:
type: added
---
title: Resolve compatibility issues with node 6
merge_request: 20461
author:
type: fixed
---
title: Stop relying on migrations in the CacheableAttributes cache key and cache attributes
for 1 minute instead
merge_request: 20389
author:
type: fixed
---
title: Improve render performance of large wiki pages
merge_request: 20465
author: Peter Leitzen
type: performance
...@@ -39,7 +39,7 @@ Rails.application.configure do ...@@ -39,7 +39,7 @@ Rails.application.configure do
config.action_mailer.delivery_method = :letter_opener_web config.action_mailer.delivery_method = :letter_opener_web
# Don't make a mess when bootstrapping a development environment # Don't make a mess when bootstrapping a development environment
config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') 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 config.eager_load = false
......
...@@ -160,6 +160,9 @@ production: &base ...@@ -160,6 +160,9 @@ production: &base
# aws_access_key_id: AWS_ACCESS_KEY_ID # aws_access_key_id: AWS_ACCESS_KEY_ID
# aws_secret_access_key: AWS_SECRET_ACCESS_KEY # aws_secret_access_key: AWS_SECRET_ACCESS_KEY
# region: us-east-1 # 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 ## Git LFS
lfs: lfs:
...@@ -180,6 +183,7 @@ production: &base ...@@ -180,6 +183,7 @@ production: &base
# Use the following options to configure an AWS compatible host # Use the following options to configure an AWS compatible host
# host: 'localhost' # default: s3.amazonaws.com # host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil # 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' # path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
## Uploads (attachments, avatars, etc...) ## Uploads (attachments, avatars, etc...)
...@@ -197,6 +201,7 @@ production: &base ...@@ -197,6 +201,7 @@ production: &base
provider: AWS provider: AWS
aws_access_key_id: AWS_ACCESS_KEY_ID aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY 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 region: us-east-1
# host: 'localhost' # default: s3.amazonaws.com # host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil # endpoint: 'http://127.0.0.1:9000' # default: nil
......
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
...@@ -949,6 +949,16 @@ ActiveRecord::Schema.define(version: 20180702120647) do ...@@ -949,6 +949,16 @@ ActiveRecord::Schema.define(version: 20180702120647) do
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree 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| create_table "internal_ids", id: :bigserial, force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
t.integer "usage", null: false t.integer "usage", null: false
...@@ -2252,6 +2262,7 @@ ActiveRecord::Schema.define(version: 20180702120647) do ...@@ -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", "gpg_keys", on_delete: :nullify
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade 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 "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", "namespaces", name: "fk_162941d509", on_delete: :cascade
add_foreign_key "internal_ids", "projects", on_delete: :cascade add_foreign_key "internal_ids", "projects", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
......
...@@ -30,5 +30,12 @@ sudo gitlab-rake gitlab:import_export:data ...@@ -30,5 +30,12 @@ sudo gitlab-rake gitlab:import_export:data
bundle exec rake gitlab:import_export:data RAILS_ENV=production 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 [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 [tmp]: ../../development/shared_files.md
...@@ -10,12 +10,12 @@ To view rendered emails "sent" in your development instance, visit ...@@ -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 Rails provides a way to preview our mailer templates in HTML and plaintext using
dummy data. 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). [`/rails/mailers`](http://localhost:3000/rails/mailers).
See the [Rails guides] for more info. 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 [Rails guides]: http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails
## Incoming email ## Incoming email
......
...@@ -60,7 +60,7 @@ Libraries with the following licenses are acceptable for use: ...@@ -60,7 +60,7 @@ Libraries with the following licenses are acceptable for use:
## Unacceptable Licenses ## 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 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. - [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: ...@@ -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. - [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. - [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 ## 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. 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.
......
# Integrate your GitLab server with Bitbucket # 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 Import projects from Bitbucket.org and login to your GitLab instance with your
Bitbucket.org account. Bitbucket.org account.
...@@ -76,13 +79,13 @@ you to use. ...@@ -76,13 +79,13 @@ you to use.
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml 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: 1. Add the Bitbucket provider configuration:
For Omnibus packages: For Omnibus packages:
```ruby ```ruby
gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_providers'] = [ gitlab_rails['omniauth_providers'] = [
{ {
"name" => "bitbucket", "name" => "bitbucket",
...@@ -96,10 +99,13 @@ you to use. ...@@ -96,10 +99,13 @@ you to use.
For installations from source: For installations from source:
```yaml ```yaml
- { name: 'bitbucket', omniauth:
app_id: 'BITBUCKET_APP_KEY', enabled: true
app_secret: 'BITBUCKET_APP_SECRET', providers:
url: 'https://bitbucket.org/' } - { 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. ...@@ -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 Once the above configuration is set up, you can use Bitbucket to sign into
GitLab and [start importing your projects][bb-import]. 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 [init-oauth]: omniauth.md#initial-omniauth-configuration
[bb-import]: ../workflow/importing/import_projects_from_bitbucket.md [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 [bb-old]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/doc/integration/bitbucket.md
......
# SAML OmniAuth Provider # 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 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 GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as
Microsoft ADFS to authenticate users. Microsoft ADFS to authenticate users.
...@@ -15,33 +18,33 @@ in your SAML IdP: ...@@ -15,33 +18,33 @@ in your SAML IdP:
For omnibus package: For omnibus package:
```sh ```sh
sudo editor /etc/gitlab/gitlab.rb sudo editor /etc/gitlab/gitlab.rb
``` ```
For installations from source: For installations from source:
```sh ```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 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: an account first, don't forget to add the following values to your configuration:
For omnibus package: For omnibus package:
```ruby ```ruby
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_block_auto_created_users'] = false gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
gitlab_rails['omniauth_block_auto_created_users'] = false
``` ```
For installations from source: For installations from source:
```yaml ```yaml
omniauth:
enabled: true
allow_single_sign_on: ["saml"] allow_single_sign_on: ["saml"]
block_auto_created_users: false block_auto_created_users: false
``` ```
...@@ -52,13 +55,13 @@ in your SAML IdP: ...@@ -52,13 +55,13 @@ in your SAML IdP:
For omnibus package: For omnibus package:
```ruby ```ruby
gitlab_rails['omniauth_auto_link_saml_user'] = true gitlab_rails['omniauth_auto_link_saml_user'] = true
``` ```
For installations from source: For installations from source:
```yaml ```yaml
auto_link_saml_user: true auto_link_saml_user: true
``` ```
1. Add the provider configuration: 1. Add the provider configuration:
...@@ -66,35 +69,37 @@ in your SAML IdP: ...@@ -66,35 +69,37 @@ in your SAML IdP:
For omnibus package: For omnibus package:
```ruby ```ruby
gitlab_rails['omniauth_providers'] = [ gitlab_rails['omniauth_providers'] = [
{ {
name: 'saml', name: 'saml',
args: { 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: {
assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', 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_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', idp_sso_target_url: 'https://login.example.com/idp',
issuer: 'https://gitlab.example.com', issuer: 'https://gitlab.example.com',
name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' 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 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. ...@@ -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 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 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 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 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 allows you to mark users as External if they are members of certain groups in the
Identity Provider. Identity Provider.
### Requirements ### Requirements
...@@ -189,28 +194,28 @@ If you want some SAML authentication methods to count as 2FA on a per session ba ...@@ -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`: 1. Edit `/etc/gitlab/gitlab.rb`:
```ruby ```ruby
gitlab_rails['omniauth_providers'] = [ gitlab_rails['omniauth_providers'] = [
{ {
name: 'saml', name: 'saml',
args: { args: {
assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', 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_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', idp_sso_target_url: 'https://login.example.com/idp',
issuer: 'https://gitlab.example.com', issuer: 'https://gitlab.example.com',
name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
upstream_two_factor_authn_contexts: upstream_two_factor_authn_contexts:
%w( %w(
urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport 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:SecondFactorOTPSMS
urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN
) )
}, },
label: 'Company Login' # optional label for SAML login button, defaults to "Saml" 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. 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 ...@@ -218,40 +223,41 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
**For installations from source:** **For installations from source:**
1. Edit `config/gitlab.yml`: 1. Edit `config/gitlab.yml`:
```yaml ```yaml
- { omniauth:
name: 'saml', providers:
args: { - {
assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', name: 'saml',
idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', args: {
idp_sso_target_url: 'https://login.example.com/idp', assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
issuer: 'https://gitlab.example.com', idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', idp_sso_target_url: 'https://login.example.com/idp',
upstream_two_factor_authn_contexts: issuer: 'https://gitlab.example.com',
[ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport', upstream_two_factor_authn_contexts:
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS', [
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN' '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" },
} 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 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 In addition to the changes in GitLab, make sure that your Idp is returning the
`AuthnContext`. For example: `AuthnContext`. For example:
```xml ```xml
<saml:AuthnStatement> <saml:AuthnStatement>
<saml:AuthnContext> <saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef>
</saml:AuthnContext> </saml:AuthnContext>
</saml:AuthnStatement> </saml:AuthnStatement>
``` ```
## Customization ## Customization
......
...@@ -206,7 +206,7 @@ kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalanc ...@@ -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: > **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
> ```bash > ```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 The output is the external IP address of your cluster. This information can then
......
...@@ -23,9 +23,13 @@ module API ...@@ -23,9 +23,13 @@ module API
get ':id/export/download' do get ':id/export/download' do
path = user_project.export_project_path path = user_project.export_project_path
render_api_error!('404 Not found or has expired', 404) unless path if path
present_disk_file!(path, File.basename(path), 'application/gzip')
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 end
desc 'Start export' do desc 'Start export' do
......
...@@ -100,6 +100,11 @@ module Banzai ...@@ -100,6 +100,11 @@ module Banzai
ref_pattern = object_class.reference_pattern ref_pattern = object_class.reference_pattern
link_pattern = object_class.link_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| nodes.each do |node|
if text_node?(node) && ref_pattern if text_node?(node) && ref_pattern
replace_text_when_pattern_matches(node, ref_pattern) do |content| replace_text_when_pattern_matches(node, ref_pattern) do |content|
...@@ -108,7 +113,7 @@ module Banzai ...@@ -108,7 +113,7 @@ module Banzai
elsif element_node?(node) elsif element_node?(node)
yield_valid_link(node) do |link, inner_html| 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 replace_link_node_with_href(node, link) do
object_link_filter(link, ref_pattern, link_content: inner_html) object_link_filter(link, ref_pattern, link_content: inner_html)
end end
...@@ -118,7 +123,7 @@ module Banzai ...@@ -118,7 +123,7 @@ module Banzai
next unless link_pattern 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 replace_link_node_with_text(node, link) do
object_link_filter(inner_html, link_pattern, link_reference: true) object_link_filter(inner_html, link_pattern, link_reference: true)
end end
...@@ -126,7 +131,7 @@ module Banzai ...@@ -126,7 +131,7 @@ module Banzai
next next
end end
if link =~ /\A#{link_pattern}\z/ if link =~ link_pattern_anchor
replace_link_node_with_href(node, link) do replace_link_node_with_href(node, link) do
object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true) object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true)
end end
......
...@@ -9,10 +9,6 @@ module Gitlab ...@@ -9,10 +9,6 @@ module Gitlab
Settings Settings
end end
def self.migrations_hash
@_migrations_hash ||= Digest::MD5.hexdigest(ActiveRecord::Migrator.get_all_versions.to_s)
end
def self.revision def self.revision
@_revision ||= begin @_revision ||= begin
if File.exist?(root.join("REVISION")) if File.exist?(root.join("REVISION"))
......
...@@ -42,6 +42,21 @@ module Gitlab ...@@ -42,6 +42,21 @@ module Gitlab
!self.read_only? !self.read_only?
end 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 def self.version
@version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
end end
......
...@@ -40,6 +40,10 @@ module Gitlab ...@@ -40,6 +40,10 @@ module Gitlab
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz" "#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end end
def object_storage?
Feature.enabled?(:import_export_object_storage)
end
def version def version
VERSION VERSION
end end
......
...@@ -2,6 +2,7 @@ module Gitlab ...@@ -2,6 +2,7 @@ module Gitlab
module ImportExport module ImportExport
module AfterExportStrategies module AfterExportStrategies
class BaseAfterExportStrategy class BaseAfterExportStrategy
extend Gitlab::ImportExport::CommandLineUtil
include ActiveModel::Validations include ActiveModel::Validations
extend Forwardable extend Forwardable
...@@ -24,9 +25,10 @@ module Gitlab ...@@ -24,9 +25,10 @@ module Gitlab
end end
def execute(current_user, project) def execute(current_user, project)
return unless project&.export_project_path
@project = project @project = project
return unless @project.export_status == :finished
@current_user = current_user @current_user = current_user
if invalid? if invalid?
...@@ -51,9 +53,12 @@ module Gitlab ...@@ -51,9 +53,12 @@ module Gitlab
end end
def self.lock_file_path(project) 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 end
protected protected
...@@ -77,6 +82,10 @@ module Gitlab ...@@ -77,6 +82,10 @@ module Gitlab
def log_validation_errors def log_validation_errors
errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) } errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) }
end end
def object_storage?
project.export_project_object_exists?
end
end end
end end
end end
......
...@@ -38,14 +38,20 @@ module Gitlab ...@@ -38,14 +38,20 @@ module Gitlab
private private
def send_file def send_file
export_file = File.open(project.export_project_path) Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options) # rubocop:disable GitlabSecurity/PublicSend
Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options(export_file)) # rubocop:disable GitlabSecurity/PublicSend
ensure 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 end
def send_file_options(export_file) def send_file_options
{ {
body_stream: export_file, body_stream: export_file,
headers: headers headers: headers
...@@ -53,7 +59,15 @@ module Gitlab ...@@ -53,7 +59,15 @@ module Gitlab
end end
def headers 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 end
end end
......
...@@ -15,15 +15,22 @@ module Gitlab ...@@ -15,15 +15,22 @@ module Gitlab
def save def save
if compress_and_save if compress_and_save
remove_export_path remove_export_path
Rails.logger.info("Saved project export #{archive_file}") Rails.logger.info("Saved project export #{archive_file}")
archive_file
save_on_object_storage if use_object_storage?
else 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 false
end end
rescue => e rescue => e
@shared.error(e) @shared.error(e)
false false
ensure
if use_object_storage?
remove_archive
remove_export_path
end
end end
private private
...@@ -36,9 +43,29 @@ module Gitlab ...@@ -36,9 +43,29 @@ module Gitlab
FileUtils.rm_rf(@shared.export_path) FileUtils.rm_rf(@shared.export_path)
end end
def remove_archive
FileUtils.rm_rf(@shared.archive_path)
end
def archive_file def archive_file
@archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project)) @archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
end 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 end
end end
...@@ -69,6 +69,7 @@ module Gitlab ...@@ -69,6 +69,7 @@ module Gitlab
@route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {} @route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {}
end end
# Overridden in EE module
def whitelisted_routes def whitelisted_routes
grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route
end end
......
...@@ -790,23 +790,55 @@ describe ProjectsController do ...@@ -790,23 +790,55 @@ describe ProjectsController do
project.add_master(user) project.add_master(user)
end end
context 'when project export is enabled' do context 'object storage disabled' do
it 'returns 302' do before do
get :download_export, namespace_id: project.namespace, id: project 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
end end
context 'when project export is disabled' do context 'object storage enabled' do
before do before do
stub_application_setting(project_export_enabled?: false) stub_feature_flags(import_export_object_storage: true)
end end
it 'returns 404' do context 'when project export is enabled' do
get :download_export, namespace_id: project.namespace, id: project 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 end
end end
......
FactoryBot.define do
factory :import_export_upload do
project { create(:project) }
end
end
...@@ -103,6 +103,22 @@ FactoryBot.define do ...@@ -103,6 +103,22 @@ FactoryBot.define do
end end
trait :with_export do 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| after(:create) do |project, evaluator|
ProjectExportWorker.new.perform(project.creator.id, project.id) ProjectExportWorker.new.perform(project.creator.id, project.id)
end end
......
...@@ -25,6 +25,7 @@ describe 'Import/Export - project export integration test', :js do ...@@ -25,6 +25,7 @@ describe 'Import/Export - project export integration test', :js do
before do before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
stub_feature_flags(import_export_object_storage: false)
end end
after do after do
......
...@@ -5,6 +5,7 @@ describe 'Import/Export - Namespace export file cleanup', :js do ...@@ -5,6 +5,7 @@ describe 'Import/Export - Namespace export file cleanup', :js do
before do before do
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
stub_feature_flags(import_export_object_storage: false)
end end
after do after do
......
...@@ -9,6 +9,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -9,6 +9,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
[relative link 1](../relative) [relative link 1](../relative)
[relative link 2](./relative) [relative link 2](./relative)
[relative link 3](./e/f/relative) [relative link 3](./e/f/relative)
[spaced link](title with spaces)
HEREDOC HEREDOC
end end
...@@ -42,6 +43,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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
end end
...@@ -64,6 +66,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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 end
...@@ -86,6 +89,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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 end
end end
...@@ -119,6 +123,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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
end end
...@@ -136,6 +141,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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 end
...@@ -153,6 +159,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do ...@@ -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/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/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/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 end
end end
......
...@@ -68,6 +68,26 @@ describe 'Snippet', :js do ...@@ -68,6 +68,26 @@ describe 'Snippet', :js do
end end
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 context 'switching to the simple viewer' do
before do before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
......
...@@ -184,6 +184,7 @@ describe IssuablesHelper do ...@@ -184,6 +184,7 @@ describe IssuablesHelper do
issuableRef: "##{issue.iid}", issuableRef: "##{issue.iid}",
markdownPreviewPath: "/#{@project.full_path}/preview_markdown", markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
markdownDocsPath: '/help/user/markdown', markdownDocsPath: '/help/user/markdown',
markdownVersion: 11,
issuableTemplates: [], issuableTemplates: [],
projectPath: @project.path, projectPath: @project.path,
projectNamespace: @project.namespace.path, projectNamespace: @project.namespace.path,
......
...@@ -205,7 +205,9 @@ describe MarkupHelper do ...@@ -205,7 +205,9 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown) 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) helper.render_wiki_content(@wiki)
end end
...@@ -236,19 +238,32 @@ describe MarkupHelper do ...@@ -236,19 +238,32 @@ describe MarkupHelper do
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8') expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
end 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(:gitlab_markdown?).with('foo.md').and_return(true)
expect(helper).to receive(:markdown_unsafe).and_return('NOEL') expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
expect(helper.markup('foo.md', content)).to eq('NOEL') expect(helper.markup('foo.md', content)).to eq('NOEL')
end 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?).with('foo.adoc').and_return(true)
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL') expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
expect(helper.markup('foo.adoc', content)).to eq('NOEL') expect(helper.markup('foo.adoc', content)).to eq('NOEL')
end 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 end
describe '#first_line_in_markdown' do describe '#first_line_in_markdown' do
......
...@@ -165,6 +165,7 @@ export const note = { ...@@ -165,6 +165,7 @@ export const note = {
report_abuse_path: report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1', '/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', path: '/gitlab-org/gitlab-ce/notes/546',
cached_markdown_version: 11,
}; };
export const discussionMock = { export const discussionMock = {
......
...@@ -153,7 +153,7 @@ describe('Deployment component', () => { ...@@ -153,7 +153,7 @@ describe('Deployment component', () => {
it('renders external URL', () => { it('renders external URL', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.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', () => { it('renders stop button', () => {
......
...@@ -145,7 +145,7 @@ describe('MRWidgetHeader', () => { ...@@ -145,7 +145,7 @@ describe('MRWidgetHeader', () => {
it('renders web ide button', () => { it('renders web ide button', () => {
const button = vm.$el.querySelector('.js-web-ide'); 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'); expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
}); });
...@@ -154,7 +154,7 @@ describe('MRWidgetHeader', () => { ...@@ -154,7 +154,7 @@ describe('MRWidgetHeader', () => {
const button = vm.$el.querySelector('.js-web-ide'); 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'); expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
}); });
...@@ -253,8 +253,8 @@ describe('MRWidgetHeader', () => { ...@@ -253,8 +253,8 @@ describe('MRWidgetHeader', () => {
}); });
it('renders diverged commits info', () => { it('renders diverged commits info', () => {
expect(vm.$el.querySelector('.diverged-commits-count').textContent.trim()).toEqual( expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch(
'(12 commits behind)', /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/,
); );
}); });
}); });
......
...@@ -3,17 +3,61 @@ require 'spec_helper' ...@@ -3,17 +3,61 @@ require 'spec_helper'
describe Banzai::Filter::MarkdownFilter do describe Banzai::Filter::MarkdownFilter do
include FilterSpecHelper include FilterSpecHelper
context 'code block' do describe 'markdown engine from context' do
it 'adds language to lang attribute when specified' do it 'defaults to CommonMark' do
result = filter("```html\nsome code\n```") 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 end
it 'does not add language to lang attribute when not specified' do it 'uses Redcarpet' do
result = filter("```\nsome code\n```") 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 end
end end
...@@ -357,6 +357,35 @@ describe Gitlab::Database do ...@@ -357,6 +357,35 @@ describe Gitlab::Database do
end end
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 describe '#sanitize_timestamp' do
let(:max_timestamp) { Time.at((1 << 31) - 1) } let(:max_timestamp) { Time.at((1 << 31) - 1) }
......
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
...@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do ...@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
describe '#execute' do describe '#execute' do
before do before do
allow(service).to receive(:strategy_execute) allow(service).to receive(:strategy_execute)
stub_feature_flags(import_export_object_storage: false)
end end
it 'returns if project exported file is not found' do it 'returns if project exported file is not found' do
......
...@@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do ...@@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
end end
describe '#execute' do describe '#execute' do
it 'removes the exported project file after the upload' do context 'without object storage' do
allow(strategy).to receive(:send_file) before do
allow(strategy).to receive(:handle_response_error) 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 end
end end
...@@ -293,6 +293,7 @@ project: ...@@ -293,6 +293,7 @@ project:
- deploy_tokens - deploy_tokens
- settings - settings
- ci_cd_settings - ci_cd_settings
- import_export_upload
award_emoji: award_emoji:
- awardable - awardable
- user - user
......
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
...@@ -4,28 +4,6 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -4,28 +4,6 @@ describe Gitlab::Middleware::ReadOnly do
include Rack::Test::Methods include Rack::Test::Methods
using RSpec::Parameterized::TableSyntax 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 let(:rack_stack) do
rack = Rack::Builder.new do rack = Rack::Builder.new do
use ActionDispatch::Session::CacheStore use ActionDispatch::Session::CacheStore
...@@ -66,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -66,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do
it 'expects PATCH requests to be disallowed' do it 'expects PATCH requests to be disallowed' do
response = request.patch('/test_request') response = request.patch('/test_request')
expect(response).to be_a_redirect expect(response).to be_redirect
expect(subject).to disallow_request expect(subject).to disallow_request
end end
it 'expects PUT requests to be disallowed' do it 'expects PUT requests to be disallowed' do
response = request.put('/test_request') response = request.put('/test_request')
expect(response).to be_a_redirect expect(response).to be_redirect
expect(subject).to disallow_request expect(subject).to disallow_request
end end
it 'expects POST requests to be disallowed' do it 'expects POST requests to be disallowed' do
response = request.post('/test_request') response = request.post('/test_request')
expect(response).to be_a_redirect expect(response).to be_redirect
expect(subject).to disallow_request expect(subject).to disallow_request
end end
it 'expects a internal POST request to be allowed after a disallowed request' do it 'expects a internal POST request to be allowed after a disallowed request' do
response = request.post('/test_request') response = request.post('/test_request')
expect(response).to be_a_redirect expect(response).to be_redirect
response = request.post("/api/#{API::API.version}/internal") response = request.post("/api/#{API::API.version}/internal")
expect(response).not_to be_a_redirect expect(response).not_to be_redirect
end end
it 'expects DELETE requests to be disallowed' do it 'expects DELETE requests to be disallowed' do
response = request.delete('/test_request') response = request.delete('/test_request')
expect(response).to be_a_redirect expect(response).to be_redirect
expect(subject).to disallow_request expect(subject).to disallow_request
end end
...@@ -105,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -105,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch') 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 expect(subject).to disallow_request
end end
...@@ -120,19 +98,19 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -120,19 +98,19 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).not_to receive(:recognize_path) expect(Rails.application.routes).not_to receive(:recognize_path)
response = request.post("/api/#{API::API.version}/internal") 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 expect(subject).not_to disallow_request
end end
it 'expects requests to sidekiq admin to be allowed' do it 'expects requests to sidekiq admin to be allowed' do
response = request.post('/admin/sidekiq') response = request.post('/admin/sidekiq')
expect(response).not_to be_a_redirect expect(response).not_to be_redirect
expect(subject).not_to disallow_request expect(subject).not_to disallow_request
response = request.get('/admin/sidekiq') response = request.get('/admin/sidekiq')
expect(response).not_to be_a_redirect expect(response).not_to be_redirect
expect(subject).not_to disallow_request expect(subject).not_to disallow_request
end end
...@@ -150,7 +128,7 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -150,7 +128,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post(path) response = request.post(path)
expect(response).not_to be_a_redirect expect(response).not_to be_redirect
expect(subject).not_to disallow_request expect(subject).not_to disallow_request
end end
end end
......
...@@ -370,4 +370,20 @@ describe CacheMarkdownField do ...@@ -370,4 +370,20 @@ describe CacheMarkdownField do
end end
end 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 end
...@@ -52,7 +52,7 @@ describe CacheableAttributes do ...@@ -52,7 +52,7 @@ describe CacheableAttributes do
describe '.cache_key' do describe '.cache_key' do
it 'excludes cache attributes' 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
end end
......
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
...@@ -2782,6 +2782,10 @@ describe Project do ...@@ -2782,6 +2782,10 @@ describe Project do
let(:legacy_project) { create(:project, :legacy_storage, :with_export) } let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :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 it 'removes the exports directory for the project' do
expect(File.exist?(project.export_path)).to be_truthy expect(File.exist?(project.export_path)).to be_truthy
...@@ -2830,12 +2834,14 @@ describe Project do ...@@ -2830,12 +2834,14 @@ describe Project do
let(:project) { create(:project, :with_export) } let(:project) { create(:project, :with_export) }
it 'removes the exported project file' do it 'removes the exported project file' do
stub_feature_flags(import_export_object_storage: false)
exported_file = project.export_project_path exported_file = project.export_project_path
expect(File.exist?(exported_file)).to be_truthy expect(File.exist?(exported_file)).to be_truthy
allow(FileUtils).to receive(:rm_f).and_call_original allow(FileUtils).to receive(:rm_rf).and_call_original
expect(FileUtils).to receive(:rm_f).with(exported_file).and_call_original expect(FileUtils).to receive(:rm_rf).with(exported_file).and_call_original
project.remove_exported_project_file project.remove_exported_project_file
......
...@@ -192,6 +192,13 @@ describe API::ProjectExport do ...@@ -192,6 +192,13 @@ describe API::ProjectExport do
context 'when upload complete' do context 'when upload complete' do
before do before do
FileUtils.rm_rf(project_after_export.export_path) 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 end
it_behaves_like '404 response' do it_behaves_like '404 response' do
...@@ -261,6 +268,22 @@ describe API::ProjectExport do ...@@ -261,6 +268,22 @@ describe API::ProjectExport do
it_behaves_like 'get project export download not found' it_behaves_like 'get project export download not found'
end end
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 end
describe 'POST /projects/:project_id/export' do describe 'POST /projects/:project_id/export' do
......
...@@ -11,7 +11,6 @@ describe ImportExportCleanUpService do ...@@ -11,7 +11,6 @@ describe ImportExportCleanUpService do
path = '/invalid/path/' path = '/invalid/path/'
stub_repository_downloads_path(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) expect(service).not_to receive(:clean_up_export_files)
service.execute service.execute
...@@ -38,6 +37,24 @@ describe ImportExportCleanUpService do ...@@ -38,6 +37,24 @@ describe ImportExportCleanUpService do
end end
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:) def in_directory_with_files(mtime:)
Dir.mktmpdir do |tmpdir| Dir.mktmpdir do |tmpdir|
stub_repository_downloads_path(tmpdir) stub_repository_downloads_path(tmpdir)
......
...@@ -64,4 +64,16 @@ describe PreviewMarkdownService do ...@@ -64,4 +64,16 @@ describe PreviewMarkdownService do
expect(result[:commands]).to eq 'Sets time estimate to 2y.' expect(result[:commands]).to eq 'Sets time estimate to 2y.'
end end
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 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
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
...@@ -62,4 +62,12 @@ describe RepositoryCheck::BatchWorker do ...@@ -62,4 +62,12 @@ describe RepositoryCheck::BatchWorker do
expect(subject.perform(shard_name)).to eq([]) expect(subject.perform(shard_name)).to eq([])
end 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 end
...@@ -11,6 +11,14 @@ describe RepositoryCheck::DispatchWorker do ...@@ -11,6 +11,14 @@ describe RepositoryCheck::DispatchWorker do
subject.perform subject.perform
end 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 it 'dispatches work to RepositoryCheck::BatchWorker' do
expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once) expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once)
......
...@@ -9,7 +9,7 @@ Makefile.in ...@@ -9,7 +9,7 @@ Makefile.in
# http://www.gnu.org/software/autoconf # http://www.gnu.org/software/autoconf
/autom4te.cache autom4te.cache
/autoscan.log /autoscan.log
/autoscan-*.log /autoscan-*.log
/aclocal.m4 /aclocal.m4
...@@ -39,4 +39,3 @@ m4/ltoptions.m4 ...@@ -39,4 +39,3 @@ m4/ltoptions.m4
m4/ltsugar.m4 m4/ltsugar.m4
m4/ltversion.m4 m4/ltversion.m4
m4/lt~obsolete.m4 m4/lt~obsolete.m4
autom4te.cache
# 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/*
!/craft/storage/logo/* !/craft/storage/rebrand
\ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
# Deployment Manager configuration file for your project. Added in Delphi XE2. # 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. # Uncomment this if it is not mobile development and you do not use remote debug feature.
#*.deployproj #*.deployproj
# #
# C++ object files produced when C/C++ Output file generation is configured. # 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). # Uncomment this if you are not using external objects (zlib library for example).
#*.obj #*.obj
......
...@@ -35,7 +35,6 @@ eagle.epf ...@@ -35,7 +35,6 @@ eagle.epf
*.gpi *.gpi
*.pls *.pls
*.ger *.ger
*.gpi
*.xln *.xln
*.drd *.drd
......
...@@ -18,9 +18,6 @@ war/WEB-INF/classes/ ...@@ -18,9 +18,6 @@ war/WEB-INF/classes/
#compilation logs #compilation logs
.gwt/ .gwt/
#caching for already compiled files
gwt-unitCache/
#gwt junit compilation files #gwt junit compilation files
www-test/ www-test/
......
*.bak
*.gho
*.ori
*.orig
*.tmp
# General CodeKit files to ignore # General CodeKit files to ignore
config.codekit config.codekit
config.codekit3
/min /min
...@@ -23,7 +23,7 @@ local.properties ...@@ -23,7 +23,7 @@ local.properties
# CDT-specific (C/C++ Development Tooling) # CDT-specific (C/C++ Development Tooling)
.cproject .cproject
# CDT- autotools # CDT- autotools
.autotools .autotools
# Java annotation processor (APT) # Java annotation processor (APT)
...@@ -47,6 +47,9 @@ local.properties ...@@ -47,6 +47,9 @@ local.properties
# Code Recommenders # Code Recommenders
.recommenders/ .recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse) # Scala IDE specific (Scala & Java development for Eclipse)
.cache-main .cache-main
.scala_dependencies .scala_dependencies
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# User-specific stuff # User-specific stuff
.idea/**/workspace.xml .idea/**/workspace.xml
.idea/**/tasks.xml .idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries .idea/**/dictionaries
.idea/**/shelf .idea/**/shelf
...@@ -20,9 +21,16 @@ ...@@ -20,9 +21,16 @@
.idea/**/gradle.xml .idea/**/gradle.xml
.idea/**/libraries .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
cmake-build-debug/ cmake-build-*/
cmake-build-release/
# Mongo Explorer plugin # Mongo Explorer plugin
.idea/**/mongoSettings.xml .idea/**/mongoSettings.xml
......
...@@ -7,17 +7,20 @@ ...@@ -7,17 +7,20 @@
# Compiled MEX binaries (all platforms) # Compiled MEX binaries (all platforms)
*.mex* *.mex*
# Packaged app and toolbox files # Packaged app and toolbox files
*.mlappinstall *.mlappinstall
*.mltbx *.mltbx
# Generated helpsearch folders # Generated helpsearch folders
helpsearch*/ helpsearch*/
# Simulink code generation folders # Simulink code generation folders
slprj/ slprj/
sccprj/ sccprj/
# Matlab code generation folders
codegen/
# Simulink autosave extension # Simulink autosave extension
*.autosave *.autosave
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
*.evcd *.evcd
*.fsdb *.fsdb
# Default name of the simulation executable. A different name can be # Default name of the simulation executable. A different name can be
# specified with this switch (the associated daidir database name is # specified with this switch (the associated daidir database name is
# also taken from here): -o <path>/<filename> # also taken from here): -o <path>/<filename>
simv simv
...@@ -13,7 +13,7 @@ simv ...@@ -13,7 +13,7 @@ simv
simv.daidir/ simv.daidir/
simv.db.dir/ 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 # Verilog/VHDL models. An alternate directory may be specified with this
# switch: -Mdir=<directory_path> # switch: -Mdir=<directory_path>
csrc/ csrc/
...@@ -22,7 +22,7 @@ csrc/ ...@@ -22,7 +22,7 @@ csrc/
# used to write all messages from simulation: -l <filename> # used to write all messages from simulation: -l <filename>
*.log *.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 # following switch can also be used: urg -dir <coverage_directory>.vdb
simv.vdb/ simv.vdb/
urgReport/ urgReport/
......
# Swap # Swap
[._]*.s[a-v][a-z] [._]*.s[a-v][a-z]
[._]*.sw[a-p] [._]*.sw[a-p]
[._]s[a-v][a-z] [._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p] [._]sw[a-p]
# Session # Session
......
...@@ -14,3 +14,4 @@ ...@@ -14,3 +14,4 @@
# Metadata # Metadata
*.aliases *.aliases
*.lvlps *.lvlps
.cache/
...@@ -7,6 +7,4 @@ release.properties ...@@ -7,6 +7,4 @@ release.properties
dependency-reduced-pom.xml dependency-reduced-pom.xml
buildNumber.properties buildNumber.properties
.mvn/timing.properties .mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
!/.mvn/wrapper/maven-wrapper.jar
...@@ -57,9 +57,15 @@ typings/ ...@@ -57,9 +57,15 @@ typings/
# dotenv environment variables file # dotenv environment variables file
.env .env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output # next.js build output
.next .next
# nuxt.js build output
.nuxt
# vuepress build output # vuepress build output
.vuepress/dist .vuepress/dist
......
...@@ -35,6 +35,9 @@ xcuserdata/ ...@@ -35,6 +35,9 @@ xcuserdata/
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# #
# Pods/ # Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage # Carthage
# #
......
# Gitignore for Perl 6 (http://www.perl6.org)
# As part of https://github.com/github/gitignore
# precompiled files
.precomp
lib/.precomp
...@@ -47,6 +47,9 @@ playground.xcworkspace ...@@ -47,6 +47,9 @@ playground.xcworkspace
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# #
# Pods/ # Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage # Carthage
# #
......
...@@ -226,6 +226,9 @@ TSWLatexianTemp* ...@@ -226,6 +226,9 @@ TSWLatexianTemp*
# Texpad # Texpad
.texpadtmp .texpadtmp
# LyX
*.lyx~
# Kile # Kile
*.backup *.backup
...@@ -241,6 +244,3 @@ TSWLatexianTemp* ...@@ -241,6 +244,3 @@ TSWLatexianTemp*
# standalone packages # standalone packages
*.sta *.sta
# generated if using elsarticle.cls
*.spl
...@@ -8,12 +8,15 @@ ...@@ -8,12 +8,15 @@
/typo3conf/temp_CACHED* /typo3conf/temp_CACHED*
/typo3conf/temp_fieldInfo.php /typo3conf/temp_fieldInfo.php
/typo3conf/deprecation_*.log /typo3conf/deprecation_*.log
/typo3conf/AdditionalConfiguration.php /typo3conf/ENABLE_INSTALL_TOOL
/typo3conf/realurl_autoconf.php
/FIRST_INSTALL
# Ignore system folders, you should have them symlinked. # Ignore system folders, you should have them symlinked.
# If not comment out the following entries. # If not comment out the following entries.
/typo3 /typo3
/typo3_src /typo3_src
/typo3_src-* /typo3_src-*
/Packages
/.htaccess /.htaccess
/index.php /index.php
# Ignore temp directory. # Ignore temp directory.
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
!**/App_Data/[Pp]ackages/* !**/App_Data/[Pp]ackages/*
!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/* !**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/*
# ImageProcessor DiskCache # ImageProcessor DiskCache
**/App_Data/cache/ **/App_Data/cache/
# Ignore the Models Builder models out of date flag # Ignore the Models Builder models out of date flag
......
# Visual Studio 2015 user specific files # Visual Studio 2015 user specific files
.vs/ .vs/
# Visual Studio 2015 database file
*.VC.db
# Compiled Object files # Compiled Object files
*.slo *.slo
*.lo *.lo
......
...@@ -220,7 +220,7 @@ ClientBin/ ...@@ -220,7 +220,7 @@ ClientBin/
*.publishsettings *.publishsettings
orleans.codegen.cs 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) # (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk #*.snk
...@@ -316,7 +316,7 @@ __pycache__/ ...@@ -316,7 +316,7 @@ __pycache__/
# OpenCover UI analysis results # OpenCover UI analysis results
OpenCover/ OpenCover/
# Azure Stream Analytics local run output # Azure Stream Analytics local run output
ASALocalRun/ ASALocalRun/
# MSBuild Binary and Structured Log # MSBuild Binary and Structured Log
...@@ -325,5 +325,5 @@ ASALocalRun/ ...@@ -325,5 +325,5 @@ ASALocalRun/
# NVidia Nsight GPU debugger configuration file # NVidia Nsight GPU debugger configuration file
*.nvuser *.nvuser
# MFractors (Xamarin productivity tool) working folder # MFractors (Xamarin productivity tool) working folder
.mfractor/ .mfractor/
...@@ -105,11 +105,14 @@ code_quality: ...@@ -105,11 +105,14 @@ code_quality:
- code_quality - code_quality
artifacts: artifacts:
paths: [gl-code-quality-report.json] paths: [gl-code-quality-report.json]
only:
- branches
except: except:
variables: variables:
- $CODE_QUALITY_DISABLED - $CODE_QUALITY_DISABLED
license_management: license_management:
stage: test
image: docker:stable image: docker:stable
variables: variables:
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
...@@ -121,6 +124,8 @@ license_management: ...@@ -121,6 +124,8 @@ license_management:
- license_management - license_management
artifacts: artifacts:
paths: [gl-license-management-report.json] paths: [gl-license-management-report.json]
only:
- branches
except: except:
variables: variables:
- $LICENSE_MANAGEMENT_DISABLED - $LICENSE_MANAGEMENT_DISABLED
...@@ -161,6 +166,8 @@ sast: ...@@ -161,6 +166,8 @@ sast:
- sast - sast
artifacts: artifacts:
paths: [gl-sast-report.json] paths: [gl-sast-report.json]
only:
- branches
except: except:
variables: variables:
- $SAST_DISABLED - $SAST_DISABLED
...@@ -178,6 +185,8 @@ dependency_scanning: ...@@ -178,6 +185,8 @@ dependency_scanning:
- dependency_scanning - dependency_scanning
artifacts: artifacts:
paths: [gl-dependency-scanning-report.json] paths: [gl-dependency-scanning-report.json]
only:
- branches
except: except:
variables: variables:
- $DEPENDENCY_SCANNING_DISABLED - $DEPENDENCY_SCANNING_DISABLED
...@@ -195,6 +204,8 @@ container_scanning: ...@@ -195,6 +204,8 @@ container_scanning:
- container_scanning - container_scanning
artifacts: artifacts:
paths: [gl-container-scanning-report.json] paths: [gl-container-scanning-report.json]
only:
- branches
except: except:
variables: variables:
- $CONTAINER_SCANNING_DISABLED - $CONTAINER_SCANNING_DISABLED
...@@ -365,6 +376,7 @@ production_manual: ...@@ -365,6 +376,7 @@ production_manual:
kubernetes: active kubernetes: active
variables: variables:
- $STAGING_ENABLED - $STAGING_ENABLED
- $CANARY_ENABLED
except: except:
variables: variables:
- $INCREMENTAL_ROLLOUT_ENABLED - $INCREMENTAL_ROLLOUT_ENABLED
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
@babel/template,7.0.0-beta.44,MIT @babel/template,7.0.0-beta.44,MIT
@babel/traverse,7.0.0-beta.44,MIT @babel/traverse,7.0.0-beta.44,MIT
@babel/types,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 @sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT @types/jquery,2.0.48,MIT
@vue/component-compiler-utils,1.2.1,MIT @vue/component-compiler-utils,1.2.1,MIT
...@@ -35,7 +35,7 @@ abbrev,1.1.1,ISC ...@@ -35,7 +35,7 @@ abbrev,1.1.1,ISC
accepts,1.3.4,MIT accepts,1.3.4,MIT
ace-rails-ap,4.1.2,MIT ace-rails-ap,4.1.2,MIT
acorn,3.3.0,MIT acorn,3.3.0,MIT
acorn,5.5.3,MIT acorn,5.6.2,MIT
acorn-dynamic-import,3.0.0,MIT acorn-dynamic-import,3.0.0,MIT
acorn-jsx,3.0.1,MIT acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT actionmailer,4.2.10,MIT
...@@ -51,14 +51,12 @@ addressparser,1.0.1,MIT ...@@ -51,14 +51,12 @@ addressparser,1.0.1,MIT
aes_key_wrap,1.0.1,MIT aes_key_wrap,1.0.1,MIT
after,0.8.2,MIT after,0.8.2,MIT
agent-base,2.1.1,MIT agent-base,2.1.1,MIT
ajv,4.11.8,MIT
ajv,5.5.2,MIT ajv,5.5.2,MIT
ajv,6.1.1,MIT ajv,6.1.1,MIT
ajv-keywords,2.1.1,MIT ajv-keywords,2.1.1,MIT
ajv-keywords,3.1.0,MIT ajv-keywords,3.1.0,MIT
akismet,2.0.0,MIT akismet,2.0.0,MIT
align-text,0.1.4,MIT align-text,0.1.4,MIT
allocations,1.0.5,MIT
alphanum-sort,1.0.2,MIT alphanum-sort,1.0.2,MIT
amdefine,1.0.1,BSD-3-Clause OR MIT amdefine,1.0.1,BSD-3-Clause OR MIT
amqplib,0.5.2,MIT amqplib,0.5.2,MIT
...@@ -208,7 +206,7 @@ base64-js,1.2.3,MIT ...@@ -208,7 +206,7 @@ base64-js,1.2.3,MIT
base64id,1.0.0,MIT base64id,1.0.0,MIT
batch,0.6.1,MIT batch,0.6.1,MIT
batch-loader,1.2.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.1,New BSD
bcrypt_pbkdf,1.0.0,MIT bcrypt_pbkdf,1.0.0,MIT
better-assert,1.0.2,MIT better-assert,1.0.2,MIT
...@@ -220,7 +218,6 @@ bitsyntax,0.0.4,Unknown ...@@ -220,7 +218,6 @@ bitsyntax,0.0.4,Unknown
bl,1.1.2,MIT bl,1.1.2,MIT
blackst0ne-mermaid,7.1.0-fixed,MIT blackst0ne-mermaid,7.1.0-fixed,MIT
blob,0.0.4,MIT* blob,0.0.4,MIT*
block-stream,0.0.9,ISC
bluebird,3.5.1,MIT bluebird,3.5.1,MIT
bn.js,4.11.8,MIT bn.js,4.11.8,MIT
body-parser,1.18.2,MIT body-parser,1.18.2,MIT
...@@ -269,7 +266,7 @@ camelcase-keys,2.1.0,MIT ...@@ -269,7 +266,7 @@ camelcase-keys,2.1.0,MIT
caniuse-api,1.6.1,MIT caniuse-api,1.6.1,MIT
caniuse-db,1.0.30000649,CC-BY-4.0 caniuse-db,1.0.30000649,CC-BY-4.0
capture-stack-trace,1.0.0,MIT 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.11.0,Apache 2.0
caseless,0.12.0,Apache 2.0 caseless,0.12.0,Apache 2.0
cause,0.1,MIT cause,0.1,MIT
...@@ -399,6 +396,7 @@ dashdash,1.14.1,MIT ...@@ -399,6 +396,7 @@ dashdash,1.14.1,MIT
data-uri-to-buffer,1.2.0,MIT data-uri-to-buffer,1.2.0,MIT
date-format,1.2.0,MIT date-format,1.2.0,MIT
date-now,0.1.4,MIT date-now,0.1.4,MIT
dateformat,3.0.3,MIT
de-indent,1.0.2,MIT de-indent,1.0.2,MIT
debug,2.2.0,MIT debug,2.2.0,MIT
debug,2.6.8,MIT debug,2.6.8,MIT
...@@ -457,7 +455,7 @@ domelementtype,1.3.0,Simplified BSD ...@@ -457,7 +455,7 @@ domelementtype,1.3.0,Simplified BSD
domhandler,2.4.1,Simplified BSD domhandler,2.4.1,Simplified BSD
domutils,1.6.2,Simplified BSD domutils,1.6.2,Simplified BSD
doorkeeper,4.3.2,MIT 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 dot-prop,4.2.0,MIT
double-ended-queue,2.1.0-0,MIT double-ended-queue,2.1.0-0,MIT
dropzone,4.2.0,MIT dropzone,4.2.0,MIT
...@@ -507,7 +505,7 @@ eslint-plugin-html,4.0.3,ISC ...@@ -507,7 +505,7 @@ eslint-plugin-html,4.0.3,ISC
eslint-plugin-import,2.12.0,MIT eslint-plugin-import,2.12.0,MIT
eslint-plugin-jasmine,2.2.0,MIT eslint-plugin-jasmine,2.2.0,MIT
eslint-plugin-promise,3.8.0,ISC 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-restricted-globals,0.1.1,MIT
eslint-scope,3.7.1,Simplified BSD eslint-scope,3.7.1,Simplified BSD
eslint-visitor-keys,1.0.0,Apache 2.0 eslint-visitor-keys,1.0.0,Apache 2.0
...@@ -515,10 +513,9 @@ espree,3.5.4,Simplified BSD ...@@ -515,10 +513,9 @@ espree,3.5.4,Simplified BSD
esprima,2.7.3,Simplified BSD esprima,2.7.3,Simplified BSD
esprima,3.1.3,Simplified BSD esprima,3.1.3,Simplified BSD
esprima,4.0.0,Simplified BSD esprima,4.0.0,Simplified BSD
esquery,1.0.0,New BSD esquery,1.0.1,New BSD
esrecurse,4.1.0,Simplified BSD esrecurse,4.2.1,Simplified BSD
estraverse,1.9.3,Simplified BSD estraverse,1.9.3,Simplified BSD
estraverse,4.1.1,Simplified BSD
estraverse,4.2.0,Simplified BSD estraverse,4.2.0,Simplified BSD
esutils,2.0.2,Simplified BSD esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT et-orbi,1.0.3,MIT
...@@ -607,11 +604,10 @@ fresh,0.5.2,MIT ...@@ -607,11 +604,10 @@ fresh,0.5.2,MIT
from,0.1.7,MIT from,0.1.7,MIT
from2,2.3.0,MIT from2,2.3.0,MIT
fs-access,1.0.1,MIT fs-access,1.0.1,MIT
fs-minipass,1.2.5,ISC
fs-write-stream-atomic,1.0.10,ISC fs-write-stream-atomic,1.0.10,ISC
fs.realpath,1.0.0,ISC fs.realpath,1.0.0,ISC
fsevents,1.1.3,MIT fsevents,1.2.4,MIT
fstream,1.0.11,ISC
fstream-ignore,1.0.5,ISC
ftp,0.3.10,MIT ftp,0.3.10,MIT
function-bind,1.1.1,MIT function-bind,1.1.1,MIT
functional-red-black-tree,1.0.1,MIT functional-red-black-tree,1.0.1,MIT
...@@ -630,14 +626,14 @@ get_process_mem,0.2.0,MIT ...@@ -630,14 +626,14 @@ get_process_mem,0.2.0,MIT
getpass,0.1.7,MIT getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.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-linguist,5.3.3,MIT
github-markup,1.7.0,MIT github-markup,1.7.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT gitlab-flowdock-git-hook,1.0.1,MIT
gitlab-gollum-lib,4.2.7.2,MIT gitlab-gollum-lib,4.2.7.5,MIT
gitlab-gollum-rugged_adapter,0.4.4,MIT gitlab-gollum-rugged_adapter,0.4.4.1,MIT
gitlab-grit,2.8.2,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 gitlab_omniauth-ldap,2.0.4,MIT
glob,5.0.15,ISC glob,5.0.15,ISC
glob,7.1.2,ISC glob,7.1.2,ISC
...@@ -664,7 +660,7 @@ gpgme,2.0.13,LGPL-2.1+ ...@@ -664,7 +660,7 @@ gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC graceful-fs,4.1.11,ISC
grape,1.0.3,MIT grape,1.0.3,MIT
grape-entity,0.7.1,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 grape_logging,1.7.0,MIT
graphiql-rails,1.4.10,MIT graphiql-rails,1.4.10,MIT
graphlib,2.1.1,MIT graphlib,2.1.1,MIT
...@@ -674,10 +670,8 @@ gzip-size,4.1.0,MIT ...@@ -674,10 +670,8 @@ gzip-size,4.1.0,MIT
hamlit,2.6.1,MIT hamlit,2.6.1,MIT
handle-thing,1.2.5,MIT handle-thing,1.2.5,MIT
handlebars,4.0.6,MIT handlebars,4.0.6,MIT
har-schema,1.0.5,ISC
har-schema,2.0.0,ISC har-schema,2.0.0,ISC
har-validator,2.0.6,ISC har-validator,2.0.6,ISC
har-validator,4.2.1,ISC
har-validator,5.0.3,ISC har-validator,5.0.3,ISC
has,1.0.1,MIT has,1.0.1,MIT
has-ansi,2.0.0,MIT has-ansi,2.0.0,MIT
...@@ -712,7 +706,7 @@ hosted-git-info,2.2.0,ISC ...@@ -712,7 +706,7 @@ hosted-git-info,2.2.0,ISC
hpack.js,2.1.6,MIT hpack.js,2.1.6,MIT
html-comment-regex,1.1.1,MIT html-comment-regex,1.1.1,MIT
html-entities,1.2.0,MIT html-entities,1.2.0,MIT
html-pipeline,2.7.1,MIT html-pipeline,2.8.3,MIT
html2text,0.2.0,MIT html2text,0.2.0,MIT
htmlentities,4.3.4,MIT htmlentities,4.3.4,MIT
htmlparser2,3.9.2,MIT htmlparser2,3.9.2,MIT
...@@ -739,13 +733,14 @@ icalendar,2.4.1,ruby ...@@ -739,13 +733,14 @@ icalendar,2.4.1,ruby
ice_nine,0.11.2,MIT ice_nine,0.11.2,MIT
iconv-lite,0.4.15,MIT iconv-lite,0.4.15,MIT
iconv-lite,0.4.19,MIT iconv-lite,0.4.19,MIT
iconv-lite,0.4.23,MIT
icss-replace-symbols,1.1.0,ISC icss-replace-symbols,1.1.0,ISC
icss-utils,2.1.0,ISC icss-utils,2.1.0,ISC
ieee754,1.1.11,New BSD ieee754,1.1.11,New BSD
ieee754,1.1.8,New BSD
iferr,0.1.5,MIT iferr,0.1.5,MIT
ignore,3.3.8,MIT ignore,3.3.8,MIT
ignore-by-default,1.0.1,ISC ignore-by-default,1.0.1,ISC
ignore-walk,3.0.1,ISC
immediate,3.0.6,MIT immediate,3.0.6,MIT
import-lazy,2.1.0,MIT import-lazy,2.1.0,MIT
import-local,1.0.0,MIT import-local,1.0.0,MIT
...@@ -865,16 +860,14 @@ jsesc,1.3.0,MIT ...@@ -865,16 +860,14 @@ jsesc,1.3.0,MIT
jsesc,2.5.1,MIT jsesc,2.5.1,MIT
json,1.8.6,ruby json,1.8.6,ruby
json-buffer,3.0.0,MIT 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-parse-better-errors,1.0.2,MIT
json-schema,0.2.3,BSD json-schema,0.2.3,BSD
json-schema-traverse,0.3.1,MIT json-schema-traverse,0.3.1,MIT
json-stable-stringify,1.0.1,MIT
json-stable-stringify-without-jsonify,1.0.1,MIT json-stable-stringify-without-jsonify,1.0.1,MIT
json-stringify-safe,5.0.1,ISC json-stringify-safe,5.0.1,ISC
json3,3.3.2,MIT json3,3.3.2,MIT
json5,0.5.1,MIT json5,0.5.1,MIT
jsonify,0.0.0,Public Domain
jsonpointer,4.0.1,MIT jsonpointer,4.0.1,MIT
jsprim,1.4.1,MIT jsprim,1.4.1,MIT
jszip,3.1.3,(MIT OR GPL-3.0) jszip,3.1.3,(MIT OR GPL-3.0)
...@@ -992,12 +985,14 @@ minimatch,3.0.4,ISC ...@@ -992,12 +985,14 @@ minimatch,3.0.4,ISC
minimist,0.0.10,MIT minimist,0.0.10,MIT
minimist,0.0.8,MIT minimist,0.0.8,MIT
minimist,1.2.0,MIT minimist,1.2.0,MIT
minipass,2.3.3,ISC
minizlib,1.1.0,MIT
mississippi,2.0.0,Simplified BSD mississippi,2.0.0,Simplified BSD
mixin-deep,1.3.1,MIT mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT mkdirp,0.5.1,MIT
moment,2.19.2,MIT moment,2.19.2,MIT
monaco-editor,0.13.1,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,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache" mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC move-concurrently,1.0.1,ISC
...@@ -1012,9 +1007,10 @@ mustermann,1.0.2,MIT ...@@ -1012,9 +1007,10 @@ mustermann,1.0.2,MIT
mustermann-grape,1.0.0,MIT mustermann-grape,1.0.0,MIT
mute-stream,0.0.7,ISC mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT mysql2,0.4.10,MIT
nan,2.8.0,MIT nan,2.10.0,MIT
nanomatch,1.2.9,MIT nanomatch,1.2.9,MIT
natural-compare,1.4.0,MIT natural-compare,1.4.0,MIT
needle,2.2.1,MIT
negotiator,0.6.1,MIT negotiator,0.6.1,MIT
neo-async,2.5.0,MIT neo-async,2.5.0,MIT
net-ldap,0.16.0,MIT net-ldap,0.16.0,MIT
...@@ -1024,7 +1020,7 @@ netrc,0.11.0,MIT ...@@ -1024,7 +1020,7 @@ netrc,0.11.0,MIT
nice-try,1.0.4,MIT nice-try,1.0.4,MIT
node-forge,0.6.33,New BSD node-forge,0.6.33,New BSD
node-libs-browser,2.1.0,MIT 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 node-uuid,1.4.8,MIT
nodemailer,2.7.2,MIT nodemailer,2.7.2,MIT
nodemailer-direct-transport,3.3.2,MIT nodemailer-direct-transport,3.3.2,MIT
...@@ -1034,7 +1030,8 @@ nodemailer-smtp-pool,2.8.2,MIT ...@@ -1034,7 +1030,8 @@ nodemailer-smtp-pool,2.8.2,MIT
nodemailer-smtp-transport,2.7.2,MIT nodemailer-smtp-transport,2.7.2,MIT
nodemailer-wellknown,0.1.10,MIT nodemailer-wellknown,0.1.10,MIT
nodemon,1.17.3,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,1.0.10,MIT
nopt,3.0.6,ISC nopt,3.0.6,ISC
nopt,4.0.1,ISC nopt,4.0.1,ISC
...@@ -1043,6 +1040,8 @@ normalize-path,2.1.1,MIT ...@@ -1043,6 +1040,8 @@ normalize-path,2.1.1,MIT
normalize-range,0.1.2,MIT normalize-range,0.1.2,MIT
normalize-url,1.9.1,MIT normalize-url,1.9.1,MIT
normalize-url,2.0.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 npm-run-path,2.0.2,MIT
npmlog,4.1.2,ISC npmlog,4.1.2,ISC
null-check,1.0.0,MIT null-check,1.0.0,MIT
...@@ -1076,7 +1075,7 @@ omniauth-oauth,1.1.0,MIT ...@@ -1076,7 +1075,7 @@ omniauth-oauth,1.1.0,MIT
omniauth-oauth2,1.5.0,MIT omniauth-oauth2,1.5.0,MIT
omniauth-oauth2-generic,0.2.2,MIT omniauth-oauth2-generic,0.2.2,MIT
omniauth-saml,1.10.0,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-twitter,1.4.0,MIT
omniauth_crowd,2.2.3,MIT omniauth_crowd,2.2.3,MIT
on-finished,2.3.0,MIT on-finished,2.3.0,MIT
...@@ -1137,7 +1136,6 @@ peek-pg,1.3.0,MIT ...@@ -1137,7 +1136,6 @@ peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT peek-redis,1.2.0,MIT
peek-sidekiq,1.0.3,MIT peek-sidekiq,1.0.3,MIT
performance-now,0.2.0,MIT
performance-now,2.1.0,MIT performance-now,2.1.0,MIT
pg,0.18.4,"BSD,ruby,GPL" pg,0.18.4,"BSD,ruby,GPL"
pify,2.3.0,MIT pify,2.3.0,MIT
...@@ -1194,7 +1192,6 @@ premailer-rails,1.9.7,MIT ...@@ -1194,7 +1192,6 @@ premailer-rails,1.9.7,MIT
prepend-http,1.0.4,MIT prepend-http,1.0.4,MIT
prepend-http,2.0.0,MIT prepend-http,2.0.0,MIT
preserve,0.2.0,MIT preserve,0.2.0,MIT
prettier,1.11.1,MIT
prettier,1.12.1,MIT prettier,1.12.1,MIT
prismjs,1.6.0,MIT prismjs,1.6.0,MIT
private,0.1.8,MIT private,0.1.8,MIT
...@@ -1222,7 +1219,6 @@ q,1.4.1,MIT ...@@ -1222,7 +1219,6 @@ q,1.4.1,MIT
q,1.5.0,MIT q,1.5.0,MIT
qjobs,1.2.0,MIT qjobs,1.2.0,MIT
qs,6.2.3,New BSD qs,6.2.3,New BSD
qs,6.4.0,New BSD
qs,6.5.1,New BSD qs,6.5.1,New BSD
query-string,4.3.2,MIT query-string,4.3.2,MIT
query-string,5.1.1,MIT query-string,5.1.1,MIT
...@@ -1303,11 +1299,9 @@ repeat-string,1.6.1,MIT ...@@ -1303,11 +1299,9 @@ repeat-string,1.6.1,MIT
repeating,2.0.1,MIT repeating,2.0.1,MIT
representable,3.0.4,MIT representable,3.0.4,MIT
request,2.75.0,Apache 2.0 request,2.75.0,Apache 2.0
request,2.81.0,Apache 2.0
request,2.83.0,Apache 2.0 request,2.83.0,Apache 2.0
request_store,1.3.1,MIT request_store,1.3.1,MIT
requestretry,1.13.0,MIT requestretry,1.13.0,MIT
require-all,2.2.0,MIT
require-directory,2.1.1,MIT require-directory,2.1.1,MIT
require-main-filename,1.0.1,ISC require-main-filename,1.0.1,ISC
require-uncached,1.0.3,MIT require-uncached,1.0.3,MIT
...@@ -1341,24 +1335,26 @@ ruby_parser,3.9.0,MIT ...@@ -1341,24 +1335,26 @@ ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT rufus-scheduler,3.4.0,MIT
rugged,0.27.1,MIT rugged,0.27.2,MIT
run-async,2.3.0,MIT run-async,2.3.0,MIT
run-queue,1.0.3,ISC run-queue,1.0.3,ISC
rx-lite,4.0.8,Apache 2.0 rx-lite,4.0.8,Apache 2.0
rx-lite-aggregates,4.0.8,Apache 2.0 rx-lite-aggregates,4.0.8,Apache 2.0
rxjs,5.5.10,Apache 2.0 rxjs,5.5.10,Apache 2.0
safe-buffer,5.1.1,MIT safe-buffer,5.1.1,MIT
safe-buffer,5.1.2,MIT
safe-regex,1.1.0,MIT safe-regex,1.1.0,MIT
safe_yaml,1.0.4,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 sanitize-html,1.16.3,MIT
sass,3.5.5,MIT sass,3.5.5,MIT
sass-listen,4.0.0,MIT sass-listen,4.0.0,MIT
sass-rails,5.0.6,MIT sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT sawyer,0.8.1,MIT
sax,1.2.2,ISC sax,1.2.2,ISC
sax,1.2.4,ISC
schema-utils,0.4.5,MIT schema-utils,0.4.5,MIT
securecompare,1.0.0,MIT
seed-fu,2.3.7,MIT seed-fu,2.3.7,MIT
select,1.1.2,MIT select,1.1.2,MIT
select-hose,2.0.0,MIT select-hose,2.0.0,MIT
...@@ -1433,7 +1429,7 @@ spdy-transport,2.0.20,MIT ...@@ -1433,7 +1429,7 @@ spdy-transport,2.0.20,MIT
split,0.3.3,MIT split,0.3.3,MIT
split-string,3.1.0,MIT split-string,3.1.0,MIT
sprintf-js,1.0.3,New BSD sprintf-js,1.0.3,New BSD
sprockets,3.7.1,MIT sprockets,3.7.2,MIT
sprockets-rails,3.2.1,MIT sprockets-rails,3.2.1,MIT
sql.js,0.4.0,MIT sql.js,0.4.0,MIT
srcset,1.0.0,MIT srcset,1.0.0,MIT
...@@ -1479,8 +1475,7 @@ sys-filesystem,1.1.6,Artistic 2.0 ...@@ -1479,8 +1475,7 @@ sys-filesystem,1.1.6,Artistic 2.0
table,4.0.2,New BSD table,4.0.2,New BSD
tapable,0.1.10,MIT tapable,0.1.10,MIT
tapable,1.0.0,MIT tapable,1.0.0,MIT
tar,2.2.1,ISC tar,4.4.4,ISC
tar-pack,3.4.1,Simplified BSD
temple,0.7.7,MIT temple,0.7.7,MIT
term-size,1.2.0,MIT term-size,1.2.0,MIT
test-exclude,4.2.1,ISC test-exclude,4.2.1,ISC
...@@ -1535,7 +1530,6 @@ uglify-es,3.3.9,Simplified BSD ...@@ -1535,7 +1530,6 @@ uglify-es,3.3.9,Simplified BSD
uglify-js,2.8.29,Simplified BSD uglify-js,2.8.29,Simplified BSD
uglify-to-browserify,1.0.2,MIT uglify-to-browserify,1.0.2,MIT
uglifyjs-webpack-plugin,1.2.5,MIT uglifyjs-webpack-plugin,1.2.5,MIT
uid-number,0.0.6,ISC
ultron,1.1.1,MIT ultron,1.1.1,MIT
undefsafe,2.0.2,MIT undefsafe,2.0.2,MIT
underscore,1.7.0,MIT underscore,1.7.0,MIT
...@@ -1566,7 +1560,6 @@ url-parse,1.1.9,MIT ...@@ -1566,7 +1560,6 @@ url-parse,1.1.9,MIT
url-parse-lax,1.0.0,MIT url-parse-lax,1.0.0,MIT
url-parse-lax,3.0.0,MIT url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT url-to-options,1.0.1,MIT
url_safe_base64,0.2.2,MIT
use,2.0.2,MIT use,2.0.2,MIT
useragent,2.2.1,MIT useragent,2.2.1,MIT
util,0.10.3,MIT util,0.10.3,MIT
...@@ -1640,6 +1633,7 @@ xtend,4.0.1,MIT ...@@ -1640,6 +1633,7 @@ xtend,4.0.1,MIT
y18n,3.2.1,ISC y18n,3.2.1,ISC
y18n,4.0.0,ISC y18n,4.0.0,ISC
yallist,2.1.2,ISC yallist,2.1.2,ISC
yallist,3.0.2,ISC
yargs,11.0.0,MIT yargs,11.0.0,MIT
yargs,11.1.0,MIT yargs,11.1.0,MIT
yargs,3.10.0,MIT yargs,3.10.0,MIT
......
...@@ -5272,9 +5272,9 @@ moment@2.x, moment@^2.18.1: ...@@ -5272,9 +5272,9 @@ moment@2.x, moment@^2.18.1:
version "2.19.2" version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
monaco-editor-webpack-plugin@^1.2.1: monaco-editor-webpack-plugin@^1.4.0:
version "1.2.1" version "1.4.0"
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.2.1.tgz#577ed091420f422bb8f0ff3a8899dd82344da56d" resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f"
monaco-editor@0.13.1: monaco-editor@0.13.1:
version "0.13.1" version "0.13.1"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment