diff options
256 files changed, 2213 insertions, 616 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5e241e83d..9712b32232e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.16.2 (2017-01-25) + +- allow issue filter bar to be operated with mouse only. !8681 +- Fix CI requests concurrency for newer runners that prevents from picking pending builds (from 1.9.0-rc5). !8760 +- Add some basic fixes for IE11/Edge. +- Remove blue border from comment box hover. +- Fixed bug where links in merge dropdown wouldn't work. + ## 8.16.1 (2017-01-23) - Ensure export files are removed after a namespace is deleted. @@ -403,6 +411,14 @@ entry. - Whitelist next project names: help, ci, admin, search. !8227 - Adds back CSS for progress-bars. !8237 +## 8.14.8 (2017-01-25) + +- Accept environment variables from the `pre-receive` script. !7967 +- Milestoneish SQL performance partially improved and memoized. !8146 +- Fix N+1 queries on milestone show pages. !8185 +- Speed up group milestone index by passing group_id to IssuesFinder. !8363 +- Ensure issuable state changes only fire webhooks once. + ## 8.14.6 (2017-01-10) - Update the gitlab-markup gem to the version 1.5.1. !8509 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b68c4a67826..d404f1b91df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,11 +80,9 @@ the remaining issues on the GitHub issue tracker. ## I want to contribute! If you want to contribute to GitLab, but are not sure where to start, -look for [issues with the label `up-for-grabs`][up-for-grabs]. These issues -will be of reasonable size and challenge, for anyone to start contributing to -GitLab. - -This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. +look for [issues with the label `Accepting Merge Requests` and weight < 5][accepting-mrs-weight]. +These issues will be of reasonable size and challenge, for anyone to start +contributing to GitLab. ## Implement design & UI elements @@ -214,16 +212,19 @@ associated with in the description of the issue. ## Merge requests We welcome merge requests with fixes and improvements to GitLab code, tests, -and/or documentation. The features we would really like a merge request for are -listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce] -and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note -that if an issue is marked for the current milestone either before or while you -are working on it, a team member may take over the merge request in order to -ensure the work is finished before the release date. +and/or documentation. The issues that are specifically suitable for +community contributions are listed with the label +[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce] +and [EE][accepting-mrs-ee], but you are free to contribute to any other issue +you want. + +Please note that if an issue is marked for the current milestone either before +or while you are working on it, a team member may take over the merge request +in order to ensure the work is finished before the release date. If you want to add a new feature that is not labeled it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it -to be marked as `Accepting merge requests`. Please include screenshots or +to be marked as `Accepting Merge Requests`. Please include screenshots or wireframes if the feature will also change the UI. Merge requests should be opened at [GitLab.com][gitlab-mr-tracker]. @@ -285,14 +286,6 @@ request is as follows: 1. For tests that use Capybara or PhantomJS, see this [article on how to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara). -The **official merge window** is in the beginning of the month from the 1st to -the 7th day of the month. This is the best time to submit an MR and get -feedback fast. Before this time the GitLab Inc. team is still dealing with work -that is created by the monthly release such as regressions requiring patch -releases. After the 7th it is already getting closer to the release date of the -next version. This means there is less time to fix the issues created by -merging large new features. - Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split the functionality? Can you only submit the backend/API code? Can @@ -450,8 +443,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [core team]: https://about.gitlab.com/core-team/ [getting-help]: https://about.gitlab.com/getting-help/ [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq -[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs -[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455 +[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc [ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues [ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues [google-group]: https://groups.google.com/forum/#!forum/gitlabhq @@ -322,7 +322,7 @@ group :test do gem 'email_spec', '~> 1.6.0' gem 'json-schema', '~> 2.6.2' gem 'webmock', '~> 1.21.0' - gem 'test_after_commit', '~> 0.4.2' + gem 'test_after_commit', '~> 1.1' gem 'sham_rack', '~> 1.3.6' gem 'timecop', '~> 0.8.0' end diff --git a/Gemfile.lock b/Gemfile.lock index c9115982838..133e47e1ea4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -760,7 +760,7 @@ GEM teaspoon-jasmine (2.2.0) teaspoon (>= 1.0.0) temple (0.7.7) - test_after_commit (0.4.2) + test_after_commit (1.1.0) activerecord (>= 3.2) thin (1.7.0) daemons (~> 1.0, >= 1.0.9) @@ -997,7 +997,7 @@ DEPENDENCIES sys-filesystem (~> 1.1.6) teaspoon (~> 1.1.0) teaspoon-jasmine (~> 2.2.0) - test_after_commit (~> 0.4.2) + test_after_commit (~> 1.1) thin (~> 1.7.0) timecop (~> 0.8.0) truncato (~> 0.7.8) diff --git a/app/assets/javascripts/copy_as_gfm.js.es6 b/app/assets/javascripts/copy_as_gfm.js.es6 new file mode 100644 index 00000000000..b94125a4210 --- /dev/null +++ b/app/assets/javascripts/copy_as_gfm.js.es6 @@ -0,0 +1,355 @@ +/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */ +/* jshint esversion: 6 */ + +/*= require lib/utils/common_utils */ + +(() => { + const gfmRules = { + // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert + // GitLab Flavored Markdown (GFM) to HTML. + // These handlers consequently convert that same HTML to GFM to be copied to the clipboard. + // Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML + // from GFM should have a handler here, in reverse order. + // The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. + InlineDiffFilter: { + 'span.idiff.addition'(el, text) { + return `{+${text}+}`; + }, + 'span.idiff.deletion'(el, text) { + return `{-${text}-}`; + }, + }, + TaskListFilter: { + 'input[type=checkbox].task-list-item-checkbox'(el, text) { + return `[${el.checked ? 'x' : ' '}]`; + }, + }, + ReferenceFilter: { + 'a.gfm:not([data-link=true])'(el, text) { + return el.dataset.original || text; + }, + }, + AutolinkFilter: { + 'a'(el, text) { + // Fallback on the regular MarkdownFilter's `a` handler. + if (text !== el.getAttribute('href')) return false; + + return text; + }, + }, + TableOfContentsFilter: { + 'ul.section-nav'(el, text) { + return '[[_TOC_]]'; + }, + }, + EmojiFilter: { + 'img.emoji'(el, text) { + return el.getAttribute('alt'); + }, + }, + ImageLinkFilter: { + 'a.no-attachment-icon'(el, text) { + return text; + }, + }, + VideoLinkFilter: { + '.video-container'(el, text) { + const videoEl = el.querySelector('video'); + if (!videoEl) return false; + + return CopyAsGFM.nodeToGFM(videoEl); + }, + 'video'(el, text) { + return `![${el.dataset.title}](${el.getAttribute('src')})`; + }, + }, + MathFilter: { + 'pre.code.math[data-math-style=display]'(el, text) { + return `\`\`\`math\n${text.trim()}\n\`\`\``; + }, + 'code.code.math[data-math-style=inline]'(el, text) { + return `$\`${text}\`$`; + }, + 'span.katex-display span.katex-mathml'(el, text) { + const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); + if (!mathAnnotation) return false; + + return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``; + }, + 'span.katex-mathml'(el, text) { + const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); + if (!mathAnnotation) return false; + + return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`; + }, + 'span.katex-html'(el, text) { + // We don't want to include the content of this element in the copied text. + return ''; + }, + 'annotation[encoding="application/x-tex"]'(el, text) { + return text.trim(); + }, + }, + SanitizationFilter: { + 'dl'(el, text) { + let lines = text.trim().split('\n'); + // Add two spaces to the front of subsequent list items lines, + // or leave the line entirely blank. + lines = lines.map((l) => { + const line = l.trim(); + if (line.length === 0) return ''; + + return ` ${line}`; + }); + + return `<dl>\n${lines.join('\n')}\n</dl>`; + }, + 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) { + const tag = el.nodeName.toLowerCase(); + return `<${tag}>${text}</${tag}>`; + }, + }, + SyntaxHighlightFilter: { + 'pre.code.highlight'(el, t) { + const text = t.trim(); + + let lang = el.getAttribute('lang'); + if (lang === 'plaintext') { + lang = ''; + } + + // Prefixes lines with 4 spaces if the code contains triple backticks + if (lang === '' && text.match(/^```/gm)) { + return text.split('\n').map((l) => { + const line = l.trim(); + if (line.length === 0) return ''; + + return ` ${line}`; + }).join('\n'); + } + + return `\`\`\`${lang}\n${text}\n\`\`\``; + }, + 'pre > code'(el, text) { + // Don't wrap code blocks in `` + return text; + }, + }, + MarkdownFilter: { + 'br'(el, text) { + // Two spaces at the end of a line are turned into a BR + return ' '; + }, + 'code'(el, text) { + let backtickCount = 1; + const backtickMatch = text.match(/`+/); + if (backtickMatch) { + backtickCount = backtickMatch[0].length + 1; + } + + const backticks = Array(backtickCount + 1).join('`'); + const spaceOrNoSpace = backtickCount > 1 ? ' ' : ''; + + return backticks + spaceOrNoSpace + text + spaceOrNoSpace + backticks; + }, + 'blockquote'(el, text) { + return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n'); + }, + 'img'(el, text) { + return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`; + }, + 'a.anchor'(el, text) { + // Don't render a Markdown link for the anchor link inside a heading + return text; + }, + 'a'(el, text) { + return `[${text}](${el.getAttribute('href')})`; + }, + 'li'(el, text) { + const lines = text.trim().split('\n'); + const firstLine = `- ${lines.shift()}`; + // Add four spaces to the front of subsequent list items lines, + // or leave the line entirely blank. + const nextLines = lines.map((s) => { + if (s.trim().length === 0) return ''; + + return ` ${s}`; + }); + + return `${firstLine}\n${nextLines.join('\n')}`; + }, + 'ul'(el, text) { + return text; + }, + 'ol'(el, text) { + // LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists. + return text.replace(/^- /mg, '1. '); + }, + 'h1'(el, text) { + return `# ${text.trim()}`; + }, + 'h2'(el, text) { + return `## ${text.trim()}`; + }, + 'h3'(el, text) { + return `### ${text.trim()}`; + }, + 'h4'(el, text) { + return `#### ${text.trim()}`; + }, + 'h5'(el, text) { + return `##### ${text.trim()}`; + }, + 'h6'(el, text) { + return `###### ${text.trim()}`; + }, + 'strong'(el, text) { + return `**${text}**`; + }, + 'em'(el, text) { + return `_${text}_`; + }, + 'del'(el, text) { + return `~~${text}~~`; + }, + 'sup'(el, text) { + return `^${text}`; + }, + 'hr'(el, text) { + return '-----'; + }, + 'table'(el, text) { + const theadEl = el.querySelector('thead'); + const tbodyEl = el.querySelector('tbody'); + if (!theadEl || !tbodyEl) return false; + + const theadText = CopyAsGFM.nodeToGFM(theadEl); + const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl); + + return theadText + tbodyText; + }, + 'thead'(el, text) { + const cells = _.map(el.querySelectorAll('th'), (cell) => { + let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2; + + let before = ''; + let after = ''; + switch (cell.style.textAlign) { + case 'center': + before = ':'; + after = ':'; + chars -= 2; + break; + case 'right': + after = ':'; + chars -= 1; + break; + default: + break; + } + + chars = Math.max(chars, 3); + + const middle = Array(chars + 1).join('-'); + + return before + middle + after; + }); + + return `${text}|${cells.join('|')}|`; + }, + 'tr'(el, text) { + const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim()); + return `| ${cells.join(' | ')} |`; + }, + }, + }; + + class CopyAsGFM { + constructor() { + $(document).on('copy', '.md, .wiki', this.handleCopy); + $(document).on('paste', '.js-gfm-input', this.handlePaste); + } + + handleCopy(e) { + const clipboardData = e.originalEvent.clipboardData; + if (!clipboardData) return; + + const documentFragment = window.gl.utils.getSelectedFragment(); + if (!documentFragment) return; + + // If the documentFragment contains more than just Markdown, don't copy as GFM. + if (documentFragment.querySelector('.md, .wiki')) return; + + e.preventDefault(); + clipboardData.setData('text/plain', documentFragment.textContent); + + const gfm = CopyAsGFM.nodeToGFM(documentFragment); + clipboardData.setData('text/x-gfm', gfm); + } + + handlePaste(e) { + const clipboardData = e.originalEvent.clipboardData; + if (!clipboardData) return; + + const gfm = clipboardData.getData('text/x-gfm'); + if (!gfm) return; + + e.preventDefault(); + + window.gl.utils.insertText(e.target, gfm); + } + + static nodeToGFM(node) { + if (node.nodeType === Node.TEXT_NODE) { + return node.textContent; + } + + const text = this.innerGFM(node); + + if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + return text; + } + + for (const filter in gfmRules) { + const rules = gfmRules[filter]; + + for (const selector in rules) { + const func = rules[selector]; + + if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue; + + const result = func(node, text); + if (result === false) continue; + + return result; + } + } + + return text; + } + + static innerGFM(parentNode) { + const nodes = parentNode.childNodes; + + const clonedParentNode = parentNode.cloneNode(true); + const clonedNodes = Array.prototype.slice.call(clonedParentNode.childNodes, 0); + + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + const clonedNode = clonedNodes[i]; + + const text = this.nodeToGFM(node); + + // `clonedNode.replaceWith(text)` is not yet widely supported + clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode); + } + + return clonedParentNode.innerText || clonedParentNode.textContent; + } + } + + window.gl = window.gl || {}; + window.gl.CopyAsGFM = CopyAsGFM; + + new CopyAsGFM(); +})(); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index fd1e229e30a..70dc0d06b7b 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -336,7 +336,11 @@ .removeClass('is-active'); } - if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { + if ($dropdown.hasClass('js-issuable-form-dropdown')) { + return; + } + + if ($dropdown.hasClass('js-filter-bulk-update')) { _this.enableBulkLabelDropdown(); _this.setDropdownData($dropdown, isMarking, this.id(label)); return; diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6 index 7452879d9a3..51993bb3420 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js.es6 +++ b/app/assets/javascripts/lib/utils/common_utils.js.es6 @@ -160,6 +160,62 @@ return decodeURIComponent(results[2].replace(/\+/g, ' ')); }; + w.gl.utils.getSelectedFragment = () => { + const selection = window.getSelection(); + const documentFragment = selection.getRangeAt(0).cloneContents(); + if (documentFragment.textContent.length === 0) return null; + + return documentFragment; + }; + + w.gl.utils.insertText = (target, text) => { + // Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas + + const selectionStart = target.selectionStart; + const selectionEnd = target.selectionEnd; + const value = target.value; + + const textBefore = value.substring(0, selectionStart); + const textAfter = value.substring(selectionEnd, value.length); + const newText = textBefore + text + textAfter; + + target.value = newText; + target.selectionStart = target.selectionEnd = selectionStart + text.length; + + // Trigger autosave + $(target).trigger('input'); + + // Trigger autosize + var event = document.createEvent('Event'); + event.initEvent('autosize:update', true, false); + target.dispatchEvent(event); + }; + + w.gl.utils.nodeMatchesSelector = (node, selector) => { + const matches = Element.prototype.matches || + Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector; + + if (matches) { + return matches.call(node, selector); + } + + // IE11 doesn't support `node.matches(selector)` + + let parentNode = node.parentNode; + if (!parentNode) { + parentNode = document.createElement('div'); + node = node.cloneNode(true); + parentNode.appendChild(node); + } + + const matchingNodes = parentNode.querySelectorAll(selector); + return Array.prototype.indexOf.call(matchingNodes, node) !== -1; + }; + /** this will take in the headers from an API response and normalize them this way we don't run into production issues when nginx gives us lowercased header keys diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 6f919de3da7..4ef516af8c8 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -39,29 +39,39 @@ } ShortcutsIssuable.prototype.replyWithSelectedText = function() { - var quote, replyField, selected, separator; - if (window.getSelection) { - selected = window.getSelection().toString(); - replyField = $('.js-main-target-form #note_note'); - if (selected.trim() === "") { - return; - } - // Put a '>' character before each non-empty line in the selection - quote = _.map(selected.split("\n"), function(val) { - if (val.trim() !== '') { - return "> " + val + "\n"; - } - }); - // If replyField already has some content, add a newline before our quote - separator = replyField.val().trim() !== "" && "\n" || ''; - replyField.val(function(_, current) { - return current + separator + quote.join('') + "\n"; - }); - // Trigger autosave for the added text - replyField.trigger('input'); - // Focus the input field - return replyField.focus(); + var quote, replyField, documentFragment, selected, separator; + + documentFragment = window.gl.utils.getSelectedFragment(); + if (!documentFragment) return; + + // If the documentFragment contains more than just Markdown, don't copy as GFM. + if (documentFragment.querySelector('.md, .wiki')) return; + + selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment); + + replyField = $('.js-main-target-form #note_note'); + if (selected.trim() === "") { + return; } + quote = _.map(selected.split("\n"), function(val) { + return ("> " + val).trim() + "\n"; + }); + // If replyField already has some content, add a newline before our quote + separator = replyField.val().trim() !== "" && "\n\n" || ''; + replyField.val(function(_, current) { + return current + separator + quote.join('') + "\n"; + }); + + // Trigger autosave + replyField.trigger('input'); + + // Trigger autosize + var event = document.createEvent('Event'); + event.initEvent('autosize:update', true, false); + replyField.get(0).dispatchEvent(event); + + // Focus the input field + return replyField.focus(); }; ShortcutsIssuable.prototype.editIssue = function() { diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 8392b98f0a7..1d59700543c 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -37,6 +37,8 @@ display: inline-block; margin-left: 4px; margin-bottom: 2px; + flex-shrink: 0; + -webkit-flex-shrink: 0; &.s16 { margin-right: 4px; } &.s24 { margin-right: 4px; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 755eddefa42..6bfb9a6d1cb 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -125,7 +125,8 @@ top: 100%; left: 0; z-index: 9; - width: 240px; + max-width: 280px; + min-width: 240px; margin-top: 2px; margin-bottom: 0; font-size: 14px; diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 4b05ec691a8..e3da467a27c 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -132,6 +132,11 @@ display: flex; -webkit-flex-direction: column; flex-direction: column; + + &> span { + white-space: normal; + word-break: break-all; + } } } @@ -141,10 +146,6 @@ } } -.hint-dropdown { - width: 250px; -} - .filter-dropdown-loading { padding: 8px 16px; } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 1c6698ad0c6..426596027de 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -155,7 +155,8 @@ ul.content-list { } > .btn, - > .btn-group { + > .btn-group, + > .dropdown.inline { margin-right: $gl-padding-top; display: inline-block; margin-top: 3px; diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index cb923166b25..6f2e746d4b0 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -13,6 +13,8 @@ $dark-main-bg: #1d1f21; $dark-main-color: #1d1f21; $dark-line-color: #c5c8c6; $dark-line-num-color: rgba(255, 255, 255, 0.3); +$dark-line-num-color-new: #627165; +$dark-line-num-color-old: #806565; $dark-diff-not-empty-bg: #557; $dark-highlight-bg: #ffe792; $dark-highlight-color: $black; @@ -89,7 +91,6 @@ $dark-il: #de935f; .diff-line-num, .diff-line-num a { - color: $dark-main-color; color: $dark-line-num-color; } @@ -121,11 +122,21 @@ $dark-il: #de935f; .diff-line-num.new, .line_content.new { @include diff_background($dark-new-bg, $dark-new-idiff, $dark-border); + + &::before, + a { + color: $dark-line-num-color-new; + } } .diff-line-num.old, .line_content.old { @include diff_background($dark-old-bg, $dark-old-idiff, $dark-border); + + &::before, + a { + color: $dark-line-num-color-old; + } } .line_content.match { diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index d8510baad8a..2144a5f7466 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -7,6 +7,8 @@ $monokai-bg: #272822; $monokai-border: #555; $monokai-text-color: #f8f8f2; $monokai-line-num-color: rgba(255, 255, 255, 0.3); +$monokai-line-num-color-new: #707565; +$monokai-line-num-color-old: #7e736f; $monokai-line-empty-bg: #49483e; $monokai-line-empty-border: darken($monokai-line-empty-bg, 15%); $monokai-diff-border: #808080; @@ -120,11 +122,21 @@ $monokai-gi: #a6e22e; .diff-line-num.new, .line_content.new { @include diff_background($monokai-new-bg, $monokai-new-idiff, $monokai-diff-border); + + &::before, + a { + color: $monokai-line-num-color-new; + } } .diff-line-num.old, .line_content.old { @include diff_background($monokai-old-bg, $monokai-old-idiff, $monokai-diff-border); + + &::before, + a { + color: $monokai-line-num-color-old; + } } .line_content.match { diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index 874aecb5e16..2cb1d18f12f 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -13,6 +13,8 @@ $solarized-dark-pre-color: #93a1a1; $solarized-dark-pre-border: #113b46; $solarized-dark-line-bg: #002b36; $solarized-dark-line-color: rgba(255, 255, 255, 0.3); +$solarized-dark-line-color-new: #5a766c; +$solarized-dark-line-color-old: #7a6c71; $solarized-dark-highlight: #094554; $solarized-dark-hll-bg: #174652; $solarized-dark-c: #586e75; @@ -124,11 +126,21 @@ $solarized-dark-il: #2aa198; .diff-line-num.new, .line_content.new { @include diff_background($solarized-dark-new-bg, $solarized-dark-new-idiff, $solarized-dark-border); + + &::before, + a { + color: $solarized-dark-line-color-new; + } } .diff-line-num.old, .line_content.old { @include diff_background($solarized-dark-old-bg, $solarized-dark-old-idiff, $solarized-dark-border); + + &::before, + a { + color: $solarized-dark-line-color-old; + } } .line_content.match { diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 499a1c108b8..b72c4326730 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -13,6 +13,9 @@ $solarized-light-pre-bg: #002b36; $solarized-light-pre-bg: #fdf6e3; $solarized-light-pre-color: #586e75; $solarized-light-line-bg: #fdf6e3; +$solarized-light-line-color: rgba(0, 0, 0, 0.3); +$solarized-light-line-color-new: #a1a080; +$solarized-light-line-color-old: #ad9186; $solarized-light-highlight: #eee8d5; $solarized-light-hll-bg: #ddd8c5; $solarized-light-c: #93a1a1; @@ -98,7 +101,7 @@ $solarized-light-il: #2aa198; .diff-line-num, .diff-line-num a { - color: $black-transparent; + color: $solarized-light-line-color; } // Code itself @@ -130,11 +133,21 @@ $solarized-light-il: #2aa198; .line_content.new { @include diff_background($solarized-light-new-bg, $solarized-light-new-idiff, $solarized-light-border); + + &::before, + a { + color: $solarized-light-line-color-new; + } } .diff-line-num.old, .line_content.old { @include diff_background($solarized-light-old-bg, $solarized-light-old-idiff, $solarized-light-border); + + &::before, + a { + color: $solarized-light-line-color-old; + } } .line_content.match { diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index b425c78e0d5..398fbfd3b18 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -108,11 +108,19 @@ $white-gc-bg: #eaf2f5; &.old { background-color: $line-number-old; border-color: $line-removed-dark; + + a { + color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + } } &.new { background-color: $line-number-new; border-color: $line-added-dark; + + a { + color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + } } &.hll:not(.empty-cell) { @@ -125,6 +133,10 @@ $white-gc-bg: #eaf2f5; &.old { background-color: $line-removed; + &::before { + color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + } + span.idiff { background-color: $line-removed-dark; } @@ -133,6 +145,10 @@ $white-gc-bg: #eaf2f5; &.new { background-color: $line-added; + &::before { + color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + } + span.idiff { background-color: $line-added-dark; } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 4cce1c363eb..948921efc0b 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -32,6 +32,10 @@ .last-commit { @include str-truncated(506px); + .fa-angle-right { + margin-left: 5px; + } + @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { @include str-truncated(450px); } diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb index 8714349e27f..70845617d3c 100644 --- a/app/controllers/projects/git_http_client_controller.rb +++ b/app/controllers/projects/git_http_client_controller.rb @@ -109,12 +109,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController end def repository + wiki? ? project.wiki.repository : project.repository + end + + def wiki? + return @wiki if defined?(@wiki) + _, suffix = project_id_with_suffix - if suffix == '.wiki.git' - project.wiki.repository - else - project.repository - end + @wiki = suffix == '.wiki.git' end def render_not_found diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb index 9184dcccac5..278098fcc58 100644 --- a/app/controllers/projects/git_http_controller.rb +++ b/app/controllers/projects/git_http_controller.rb @@ -84,7 +84,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController end def access - @access ||= Gitlab::GitAccess.new(user, project, 'http', authentication_abilities: authentication_abilities) + @access ||= access_klass.new(user, project, 'http', authentication_abilities: authentication_abilities) end def access_check @@ -102,4 +102,8 @@ class Projects::GitHttpController < Projects::GitHttpClientController access_check.allowed? end + + def access_klass + @access_klass ||= wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess + end end diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 68385dc47eb..25e2d8ea24e 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -11,10 +11,10 @@ module Taskable INCOMPLETE = 'incomplete'.freeze ITEM_PATTERN = / ^ - \s*(?:[-+*]|(?:\d+\.))? # optional list prefix - \s* # optional whitespace prefix - (\[\s\]|\[[xX]\]) # checkbox - (\s.+) # followed by whitespace and some text. + \s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list + \s+ # whitespace prefix has to be always presented for a list item + (\[\s\]|\[[xX]\]) # checkbox + (\s.+) # followed by whitespace and some text. /x def self.get_tasks(content) diff --git a/app/models/member.rb b/app/models/member.rb index c585e0b450e..26a6054e00d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -68,9 +68,9 @@ class Member < ActiveRecord::Base after_create :send_request, if: :request?, unless: :importing? after_create :create_notification_setting, unless: [:pending?, :importing?] after_create :post_create_hook, unless: [:pending?, :importing?] - after_create :refresh_member_authorized_projects, if: :importing? after_update :post_update_hook, unless: [:pending?, :importing?] after_destroy :post_destroy_hook, unless: :pending? + after_commit :refresh_member_authorized_projects delegate :name, :username, :email, to: :user, prefix: true @@ -147,8 +147,6 @@ class Member < ActiveRecord::Base member.save end - UserProjectAccessChangedService.new(user.id).execute if user.is_a?(User) - member end @@ -275,23 +273,27 @@ class Member < ActiveRecord::Base end def post_create_hook - UserProjectAccessChangedService.new(user.id).execute system_hook_service.execute_hooks_for(self, :create) end def post_update_hook - UserProjectAccessChangedService.new(user.id).execute if access_level_changed? + # override in sub class end def post_destroy_hook - refresh_member_authorized_projects system_hook_service.execute_hooks_for(self, :destroy) end + # Refreshes authorizations of the current member. + # + # This method schedules a job using Sidekiq and as such **must not** be called + # in a transaction. Doing so can lead to the job running before the + # transaction has been committed, resulting in the job either throwing an + # error or not doing any meaningful work. def refresh_member_authorized_projects - # If user/source is being destroyed, project access are gonna be destroyed eventually - # because of DB foreign keys, so we shouldn't bother with refreshing after each - # member is destroyed through association + # If user/source is being destroyed, project access are going to be + # destroyed eventually because of DB foreign keys, so we shouldn't bother + # with refreshing after each member is destroyed through association return if destroyed_by_association.present? UserProjectAccessChangedService.new(user_id).execute diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index 6149c35cc61..5cb6b0c527d 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -16,8 +16,7 @@ class ProjectGroupLink < ActiveRecord::Base validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true validate :different_group - after_create :refresh_group_members_authorized_projects - after_destroy :refresh_group_members_authorized_projects + after_commit :refresh_group_members_authorized_projects def self.access_options Gitlab::Access.options diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 842e23eb6b6..55d9cb13ae4 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -22,6 +22,8 @@ module Projects if project.update_attributes(params.except(:default_branch)) if project.previous_changes.include?('path') project.rename_repo + else + system_hook_service.execute_hooks_for(project, :update) end success diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb index 2469b4f0d7c..d7a6804ee88 100644 --- a/app/services/user_project_access_changed_service.rb +++ b/app/services/user_project_access_changed_service.rb @@ -4,6 +4,6 @@ class UserProjectAccessChangedService end def execute - AuthorizedProjectsWorker.bulk_perform_async(@user_ids.map { |id| [id] }) + AuthorizedProjectsWorker.bulk_perform_and_wait(@user_ids.map { |id| [id] }) end end diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml index afe2fd7fd7b..1a1327fb53c 100644 --- a/app/views/projects/_merge_request_merge_settings.html.haml +++ b/app/views/projects/_merge_request_merge_settings.html.haml @@ -8,7 +8,7 @@ %br %span.descr Builds need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_build_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') + = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds') .checkbox = form.label :only_allow_merge_if_all_discussions_are_resolved do = form.check_box :only_allow_merge_if_all_discussions_are_resolved diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 04efc2e996c..19ffe73a08d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -18,7 +18,7 @@ - if @project.protected_branch? branch.name %span.label.label-success protected - .controls.hidden-xs + .controls.hidden-xs< - if merge_project && create_mr_button?(@repository.root_ref, branch.name) = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do Merge Request diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 5f8f56150f9..bd1f2d96f56 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -12,7 +12,7 @@ = form_tag(filter_branches_path, method: :get) do = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } - .dropdown.inline + .dropdown.inline> %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.light = projects_sort_options_hash[@sort] diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 324a7f8cd3f..762ff34a9ec 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,5 +1,5 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) - .project-action-button.dropdown.inline + .project-action-button.dropdown.inline> %button.btn{ 'data-toggle' => 'dropdown' } = icon('download') = icon("caret-down") diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index dfdaeb04869..818a70f38f1 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -78,7 +78,7 @@ .btn-group.inline - if actions.any? .btn-group - %button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual build', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label': 'Manual build' } + %button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual build', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual build' } = custom_icon('icon_play') = icon('caret-down', 'aria-hidden' => 'true') %ul.dropdown-menu.dropdown-menu-align-right @@ -89,7 +89,7 @@ %span= build.name - if artifacts.present? .btn-group - %button.dropdown-toggle.btn.btn-default.build-artifacts.has-tooltip.js-pipeline-dropdown-download{ type: 'button', title: 'Artifacts', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label': 'Artifacts' } + %button.dropdown-toggle.btn.btn-default.build-artifacts.has-tooltip.js-pipeline-dropdown-download{ type: 'button', title: 'Artifacts', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Artifacts' } = icon("download") = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right @@ -102,8 +102,8 @@ - if can?(current_user, :update_pipeline, pipeline.project) .cancel-retry-btns.inline - if pipeline.retryable? - = link_to retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn has-tooltip', title: 'Retry', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label': 'Retry' , method: :post do + = link_to retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn has-tooltip', title: 'Retry', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Retry' , method: :post do = icon("repeat") - if pipeline.cancelable? - = link_to cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-remove has-tooltip', title: 'Cancel', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label': 'Cancel' , method: :post do + = link_to cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-remove has-tooltip', title: 'Cancel', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Cancel' , method: :post do = icon("remove") diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 08eb0c57f66..6dba42d5226 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,8 +1,7 @@ .page-content-header .header-main-content - %strong + %strong Commit #{@commit.short_id} = clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard") - = @commit.short_id %span.hidden-xs authored #{time_ago_with_tooltip(@commit.authored_date)} %span by diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index b087485aa17..f361204ecac 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -14,7 +14,7 @@ - left_line_code = diff_file.line_code(left) - left_position = diff_file.position(left) %td.old_line.diff-line-num{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } } - %a{ href: "##{left_line_code}" }= raw(left.old_pos) + %a{ href: "##{left_line_code}", data: { linenumber: left.old_pos } } %td.line_content.parallel.noteable_line{ class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old') }= diff_line_content(left.text) - else %td.old_line.diff-line-num.empty-cell @@ -27,7 +27,7 @@ - right_line_code = diff_file.line_code(right) - right_position = diff_file.position(right) %td.new_line.diff-line-num{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } } - %a{ href: "##{right_line_code}" }= raw(right.new_pos) + %a{ href: "##{right_line_code}", data: { linenumber: right.new_pos } } %td.line_content.parallel.noteable_line{ class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new') }= diff_line_content(right.text) - else %td.old_line.diff-line-num.empty-cell diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 1d7fdf68cb3..114865935d6 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -7,10 +7,17 @@ .project-edit-errors = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| %fieldset.append-bottom-0 - .form-group - = f.label :name, class: 'label-light' do - Project name - = f.text_field :name, class: "form-control", id: "project_name_edit" + .row + .form-group.col-md-9 + = f.label :name, class: 'label-light' do + Project name + = f.text_field :name, class: "form-control", id: "project_name_edit" + + .form-group.col-md-3 + = f.label :id, class: 'label-light' do + Project ID + = f.text_field :id, class: 'form-control', readonly: true + .form-group = f.label :description, class: 'label-light' do Project description diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index 2c08221565b..6855c463c6d 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -7,10 +7,12 @@ %th.hidden-xs .pull-left Last commit .last-commit.hidden-sm.pull-left + %i.fa.fa-angle-right %small.light - = clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard") = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" + = clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard") = time_ago_with_tooltip(@commit.committed_date) + \- = @commit.full_title %small.commit-history-link-spacer | = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'commit-history-link' diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index e25d6a48573..fb0efd85dcd 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -17,6 +17,13 @@ %pre.dark :preserve gem install gollum + %p + It is recommended to install + %code github-markdown + so that GFM features render locally: + %pre.dark + :preserve + gem install github-markdown %h3 Clone your wiki %pre.dark diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb index 2badd0680fb..6abbb5a5250 100644 --- a/app/workers/authorized_projects_worker.rb +++ b/app/workers/authorized_projects_worker.rb @@ -2,6 +2,13 @@ class AuthorizedProjectsWorker include Sidekiq::Worker include DedicatedSidekiqQueue + # Schedules multiple jobs and waits for them to be completed. + def self.bulk_perform_and_wait(args_list) + job_ids = bulk_perform_async(args_list) + + Gitlab::JobWaiter.new(job_ids).wait + end + def self.bulk_perform_async(args_list) Sidekiq::Client.push_bulk('class' => self, 'args' => args_list) end diff --git a/changelogs/unreleased/26068_tasklist_issue.yml b/changelogs/unreleased/26068_tasklist_issue.yml new file mode 100644 index 00000000000..c938351b8a7 --- /dev/null +++ b/changelogs/unreleased/26068_tasklist_issue.yml @@ -0,0 +1,4 @@ +--- +title: Don’t count tasks that are not defined as list items correctly +merge_request: 8526 +author: diff --git a/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml b/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml new file mode 100644 index 00000000000..565672917b2 --- /dev/null +++ b/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml @@ -0,0 +1,4 @@ +--- +title: Color + and - signs in diffs to increase code legibility +merge_request: +author: diff --git a/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml b/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml deleted file mode 100644 index 76e9b19b828..00000000000 --- a/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add some basic fixes for IE11/Edge -merge_request: -author: diff --git a/changelogs/unreleased/27013-regression-in-commit-title-bar.yml b/changelogs/unreleased/27013-regression-in-commit-title-bar.yml new file mode 100644 index 00000000000..7cb5e4b273d --- /dev/null +++ b/changelogs/unreleased/27013-regression-in-commit-title-bar.yml @@ -0,0 +1,4 @@ +--- +title: Fix commit title bar and repository view copy clipboard button order on last commit in repository view +merge_request: +author: diff --git a/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml b/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml new file mode 100644 index 00000000000..b5584749098 --- /dev/null +++ b/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml @@ -0,0 +1,4 @@ +--- +title: Prevent copying of line numbers in parallel diff view +merge_request: 8706 +author: diff --git a/changelogs/unreleased/27066-textarea-border.yml b/changelogs/unreleased/27066-textarea-border.yml deleted file mode 100644 index e45cb3aced5..00000000000 --- a/changelogs/unreleased/27066-textarea-border.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove blue border from comment box hover -merge_request: -author: diff --git a/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml b/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml new file mode 100644 index 00000000000..52406bba464 --- /dev/null +++ b/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml @@ -0,0 +1,4 @@ +--- +title: Updated builds info link on the project settings page +merge_request: +author: Ryan Harris diff --git a/changelogs/unreleased/add_project_update_hook.yml b/changelogs/unreleased/add_project_update_hook.yml new file mode 100644 index 00000000000..915c9538843 --- /dev/null +++ b/changelogs/unreleased/add_project_update_hook.yml @@ -0,0 +1,4 @@ +--- +title: Add system hook for when a project is updated (other than rename/transfer) +merge_request: 5711 +author: Tommy Beadle diff --git a/changelogs/unreleased/copy-as-md.yml b/changelogs/unreleased/copy-as-md.yml new file mode 100644 index 00000000000..637e9dc36e2 --- /dev/null +++ b/changelogs/unreleased/copy-as-md.yml @@ -0,0 +1,4 @@ +--- +title: Copying a rendered issue/comment will paste into GFM textareas as actual GFM +merge_request: +author: diff --git a/changelogs/unreleased/display-project-id.yml b/changelogs/unreleased/display-project-id.yml new file mode 100644 index 00000000000..8705ed28400 --- /dev/null +++ b/changelogs/unreleased/display-project-id.yml @@ -0,0 +1,4 @@ +--- +title: Display project ID in project settings +merge_request: 8572 +author: winniehell diff --git a/changelogs/unreleased/fix-26518.yml b/changelogs/unreleased/fix-26518.yml new file mode 100644 index 00000000000..961ac2642fb --- /dev/null +++ b/changelogs/unreleased/fix-26518.yml @@ -0,0 +1,4 @@ +--- +title: Fix access to the wiki code via HTTP when repository feature disabled +merge_request: 8758 +author: diff --git a/changelogs/unreleased/fix-ci-requests-concurrency-for-newer-runners.yml b/changelogs/unreleased/fix-ci-requests-concurrency-for-newer-runners.yml deleted file mode 100644 index 075d74b9cb8..00000000000 --- a/changelogs/unreleased/fix-ci-requests-concurrency-for-newer-runners.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: 'Fix CI requests concurrency for newer runners that prevents from picking pending builds (from 1.9.0-rc5)' -merge_request: 8760 diff --git a/changelogs/unreleased/issue-filter-click-to-search.yml b/changelogs/unreleased/issue-filter-click-to-search.yml deleted file mode 100644 index c024ea48dc7..00000000000 --- a/changelogs/unreleased/issue-filter-click-to-search.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: allow issue filter bar to be operated with mouse only -merge_request: 8681 -author: diff --git a/changelogs/unreleased/label-select-toggle.yml b/changelogs/unreleased/label-select-toggle.yml new file mode 100644 index 00000000000..af5b4246521 --- /dev/null +++ b/changelogs/unreleased/label-select-toggle.yml @@ -0,0 +1,4 @@ +--- +title: Fixed label dropdown toggle text not correctly updating +merge_request: +author: diff --git a/changelogs/unreleased/merge-dropdown-this-context.yml b/changelogs/unreleased/merge-dropdown-this-context.yml deleted file mode 100644 index 5c4890fcaa2..00000000000 --- a/changelogs/unreleased/merge-dropdown-this-context.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed bug where links in merge dropdown wouldn't work -merge_request: -author: diff --git a/changelogs/unreleased/refresh-authorizations-fork-join.yml b/changelogs/unreleased/refresh-authorizations-fork-join.yml new file mode 100644 index 00000000000..b1349b9447d --- /dev/null +++ b/changelogs/unreleased/refresh-authorizations-fork-join.yml @@ -0,0 +1,4 @@ +--- +title: Fix race conditions for AuthorizedProjectsWorker +merge_request: +author: diff --git a/config/database.yml.mysql b/config/database.yml.mysql index d9702870249..a33e40e8eb3 100644 --- a/config/database.yml.mysql +++ b/config/database.yml.mysql @@ -3,8 +3,8 @@ # production: adapter: mysql2 - encoding: utf8mb4 - collation: utf8mb4_general_ci + encoding: utf8 + collation: utf8_general_ci reconnect: false database: gitlabhq_production pool: 10 @@ -18,8 +18,8 @@ production: # development: adapter: mysql2 - encoding: utf8mb4 - collation: utf8mb4_general_ci + encoding: utf8 + collation: utf8_general_ci reconnect: false database: gitlabhq_development pool: 5 diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 5a7365bb0f6..fa318384405 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -12,6 +12,11 @@ Sidekiq.configure_server do |config| chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0' + chain.add Gitlab::SidekiqStatus::ServerMiddleware + end + + config.client_middleware do |chain| + chain.add Gitlab::SidekiqStatus::ClientMiddleware end # Sidekiq-cron: load recurring jobs from gitlab.yml @@ -46,6 +51,10 @@ end Sidekiq.configure_client do |config| config.redis = redis_config_hash + + config.client_middleware do |chain| + chain.add Gitlab::SidekiqStatus::ClientMiddleware + end end # The Sidekiq client API always adds the queue to the Sidekiq queue diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb index bba2fc4b186..6f241f6fa4a 100644 --- a/db/fixtures/development/01_admin.rb +++ b/db/fixtures/development/01_admin.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do User.seed do |s| s.id = 1 diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index a984eda5ab5..c2b8f7ba819 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -1,4 +1,4 @@ -require 'sidekiq/testing' +require './spec/support/sidekiq' Sidekiq::Testing.inline! do Gitlab::Seeder.quiet do diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index 03da29c4c68..101ff3a1209 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do 20.times do |i| begin diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb index 5c2a03fec3f..86e0a38aae1 100644 --- a/db/fixtures/development/06_teams.rb +++ b/db/fixtures/development/06_teams.rb @@ -1,4 +1,4 @@ -require 'sidekiq/testing' +require './spec/support/sidekiq' Sidekiq::Testing.inline! do Gitlab::Seeder.quiet do diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb index 540e4e68259..271bfbc97e0 100644 --- a/db/fixtures/development/07_milestones.rb +++ b/db/fixtures/development/07_milestones.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do Project.all.each do |project| 5.times do |i| diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index 4fa572fca9b..d93d133d157 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do Project.all.each do |project| 10.times do diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index 87fb8e3300d..c04afe97277 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do # Limit the number of merge requests per project to avoid long seeds MAX_NUM_MERGE_REQUESTS = 10 diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb index 8b4bee384e1..51e22137d6f 100644 --- a/db/fixtures/development/11_keys.rb +++ b/db/fixtures/development/11_keys.rb @@ -1,12 +1,18 @@ -Gitlab::Seeder.quiet do - User.first(10).each do |user| - key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{user.id + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" +require './spec/support/sidekiq' - user.keys.create( - title: "Sample key #{user.id}", - key: key - ) +# Creating keys runs a gitlab-shell worker. Since we may not have the right +# gitlab-shell path set (yet) we need to disable this for these fixtures. +Sidekiq::Testing.disable! do + Gitlab::Seeder.quiet do + User.first(10).each do |user| + key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{user.id + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" - print '.' + user.keys.create( + title: "Sample key #{user.id}", + key: key + ) + + print '.' + end end end diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb index 74898544a69..4f3bdba043d 100644 --- a/db/fixtures/development/12_snippets.rb +++ b/db/fixtures/development/12_snippets.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do content =<<eos class Member < ActiveRecord::Base diff --git a/db/fixtures/development/13_comments.rb b/db/fixtures/development/13_comments.rb index 566c0705638..29b8081055d 100644 --- a/db/fixtures/development/13_comments.rb +++ b/db/fixtures/development/13_comments.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do Issue.all.each do |issue| project = issue.project diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb index be95d788850..534847a7107 100644 --- a/db/fixtures/development/14_pipelines.rb +++ b/db/fixtures/development/14_pipelines.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + class Gitlab::Seeder::Pipelines STAGES = %w[build test deploy notify] BUILDS = [ diff --git a/db/fixtures/development/15_award_emoji.rb b/db/fixtures/development/15_award_emoji.rb index baac32f2d10..ea343c26b69 100644 --- a/db/fixtures/development/15_award_emoji.rb +++ b/db/fixtures/development/15_award_emoji.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do emoji = Gitlab::AwardEmoji.emojis.keys diff --git a/db/fixtures/development/16_protected_branches.rb b/db/fixtures/development/16_protected_branches.rb index 103c7f9445c..39d466fb43f 100644 --- a/db/fixtures/development/16_protected_branches.rb +++ b/db/fixtures/development/16_protected_branches.rb @@ -1,3 +1,5 @@ +require './spec/support/sidekiq' + Gitlab::Seeder.quiet do admin_user = User.find(1) diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index 916ee8dbac8..747901dd634 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -1,4 +1,4 @@ -require 'sidekiq/testing' +require './spec/support/sidekiq' require './spec/support/test_env' class Gitlab::Seeder::CycleAnalytics diff --git a/doc/README.md b/doc/README.md index 993b30ccdb5..909740211a6 100644 --- a/doc/README.md +++ b/doc/README.md @@ -44,7 +44,7 @@ - [Operations](administration/operations.md) Keeping GitLab up and running. - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects. - [Repository checks](administration/repository_checks.md) Periodic Git repository checks. -- [Repository storages](administration/repository_storages.md) Manage the paths used to store repositories. +- [Repository storage paths](administration/repository_storage_paths.md) Manage the paths used to store repositories. - [Security](security/README.md) Learn what you can do to further secure your GitLab instance. - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [Update](update/README.md) Update guides to upgrade your installation. @@ -61,7 +61,6 @@ - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs. - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability. - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab. -- [Multiple mountpoints for the repositories storage](administration/repository_storages.md) Define multiple repository storage paths to distribute the storage load. ## Contributor documentation diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index 04723365277..f6027b2f99e 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -65,11 +65,15 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Example: 'Paris' or 'Acme, Ltd.' label: 'LDAP' - + + # Example: 'ldap.mydomain.com' host: '_your_ldap_server' + # This port is an example, it is sometimes different but it is always an integer and not a string port: 389 uid: 'sAMAccountName' method: 'plain' # "tls" or "ssl" or "plain" + + # Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' @@ -101,7 +105,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # Base where we can search for users # - # Ex. ou=People,dc=gitlab,dc=example + # Ex. 'ou=People,dc=gitlab,dc=example' or 'DC=mydomain,DC=com' # base: '' @@ -112,6 +116,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Note: GitLab does not support omniauth-ldap's custom filter syntax. # + # Below an example for get only specific users + # Example: '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))' + # user_filter: '' # LDAP attributes that GitLab will use to create an account for the LDAP user. diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index bc2b1f20ed3..ee37ea49874 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -13,12 +13,12 @@ checks failed you can see their output on the admin log page under ## Periodic checks -GitLab periodically runs a repository check on all project repositories and -wiki repositories in order to detect data corruption problems. A -project will be checked no more than once per week. If any projects +When enabled, GitLab periodically runs a repository check on all project +repositories and wiki repositories in order to detect data corruption problems. +A project will be checked no more than once per month. If any projects fail their repository checks all GitLab administrators will receive an email -notification of the situation. This notification is sent out no more -than once a day. +notification of the situation. This notification is sent out once a week on +Sunday, by default. ## Disabling periodic checks diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md new file mode 100644 index 00000000000..d6aa6101026 --- /dev/null +++ b/doc/administration/repository_storage_paths.md @@ -0,0 +1,102 @@ +# Repository storage paths + +> [Introduced][ce-4578] in GitLab 8.10. + +GitLab allows you to define multiple repository storage paths to distribute the +storage load between several mount points. + +>**Notes:** +> +- You must have at least one storage path called `default`. +- The paths are defined in key-value pairs. The key is an arbitrary name you + can pick to name the file path. +- The target directories and any of its subpaths must not be a symlink. + +## Configure GitLab + +>**Warning:** +In order for [backups] to work correctly, the storage path must **not** be a +mount point and the GitLab user should have correct permissions for the parent +directory of the path. In Omnibus GitLab this is taken care of automatically, +but for source installations you should be extra careful. +> +The thing is that for compatibility reasons `gitlab.yml` has a different +structure than Omnibus. In `gitlab.yml` you indicate the path for the +repositories, for example `/home/git/repositories`, while in Omnibus you +indicate `git_data_dirs`, which for the example above would be `/home/git`. +Then, Omnibus will create a `repositories` directory under that path to use with +`gitlab.yml`. +> +This little detail matters because while restoring a backup, the current +contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`, +so if `/home/git/repositories` is the mount point, then `mv` would be moving +things between mount points, and bad things could happen. Ideally, +`/home/git` would be the mount point, so then things would be moving within the +same mount point. This is guaranteed with Omnibus installations (because they +don't specify the full repository path but the parent path), but not for source +installations. + +--- + +Now that you've read that big fat warning above, let's edit the configuration +files and add the full paths of the alternative repository storage paths. In +the example below, we add two more mountpoints that are named `nfs` and `cephfs` +respectively. + +**For installations from source** + +1. Edit `gitlab.yml` and add the storage paths: + + ```yaml + repositories: + # Paths where repositories can be stored. Give the canonicalized absolute pathname. + # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!! + storages: # You must have at least a 'default' storage path. + default: /home/git/repositories + nfs: /mnt/nfs/repositories + cephfs: /mnt/cephfs/repositories + ``` + +1. [Restart GitLab] for the changes to take effect. + +>**Note:** +The [`gitlab_shell: repos_path` entry][repospath] in `gitlab.yml` will be +deprecated and replaced by `repositories: storages` in the future, so if you +are upgrading from a version prior to 8.10, make sure to add the configuration +as described in the step above. After you make the changes and confirm they are +working, you can remove the `repos_path` line. + +--- + +**For Omnibus installations** + +1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the + default one: + + ```ruby + git_data_dirs({ + "default" => "/var/opt/gitlab/git-data", + "nfs" => "/mnt/nfs/git-data", + "cephfs" => "/mnt/cephfs/git-data" + }) + ``` + + Note that Omnibus stores the repositories in a `repositories` subdirectory + of the `git-data` directory. + +## Choose where new project repositories will be stored + +Once you set the multiple storage paths, you can choose where new projects will +be stored via the **Application Settings** in the Admin area. + +![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png) + +Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be +randomly placed on one of the selected paths. + +[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578 +[restart gitlab]: restart_gitlab.md#installations-from-source +[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure +[backups]: ../raketasks/backup_restore.md +[raketask]: https://gitlab.com/gitlab-org/gitlab-ce/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56 +[repospath]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/config/gitlab.yml.example#L457 diff --git a/doc/administration/repository_storages.md b/doc/administration/repository_storages.md index ab70557b69a..9d41ba77f34 100644 --- a/doc/administration/repository_storages.md +++ b/doc/administration/repository_storages.md @@ -1,102 +1,3 @@ # Repository storages -> [Introduced][ce-4578] in GitLab 8.10. - -GitLab allows you to define multiple repository storage paths to distribute the -storage load between several mount points. - ->**Notes:** -> -- You must have at least one storage path called `default`. -- The paths are defined in key-value pairs. The key is an arbitrary name you - can pick to name the file path. -- The target directories and any of its subpaths must not be a symlink. - -## Configure GitLab - ->**Warning:** -In order for [backups] to work correctly, the storage path must **not** be a -mount point and the GitLab user should have correct permissions for the parent -directory of the path. In Omnibus GitLab this is taken care of automatically, -but for source installations you should be extra careful. -> -The thing is that for compatibility reasons `gitlab.yml` has a different -structure than Omnibus. In `gitlab.yml` you indicate the path for the -repositories, for example `/home/git/repositories`, while in Omnibus you -indicate `git_data_dirs`, which for the example above would be `/home/git`. -Then, Omnibus will create a `repositories` directory under that path to use with -`gitlab.yml`. -> -This little detail matters because while restoring a backup, the current -contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`, -so if `/home/git/repositories` is the mount point, then `mv` would be moving -things between mount points, and bad things could happen. Ideally, -`/home/git` would be the mount point, so then things would be moving within the -same mount point. This is guaranteed with Omnibus installations (because they -don't specify the full repository path but the parent path), but not for source -installations. - ---- - -Now that you've read that big fat warning above, let's edit the configuration -files and add the full paths of the alternative repository storage paths. In -the example below, we add two more mountpoints that are named `nfs` and `cephfs` -respectively. - -**For installations from source** - -1. Edit `gitlab.yml` and add the storage paths: - - ```yaml - repositories: - # Paths where repositories can be stored. Give the canonicalized absolute pathname. - # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!! - storages: # You must have at least a 'default' storage path. - default: /home/git/repositories - nfs: /mnt/nfs/repositories - cephfs: /mnt/cephfs/repositories - ``` - -1. [Restart GitLab] for the changes to take effect. - ->**Note:** -The [`gitlab_shell: repos_path` entry][repospath] in `gitlab.yml` will be -deprecated and replaced by `repositories: storages` in the future, so if you -are upgrading from a version prior to 8.10, make sure to add the configuration -as described in the step above. After you make the changes and confirm they are -working, you can remove the `repos_path` line. - ---- - -**For Omnibus installations** - -1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the - default one: - - ```ruby - git_data_dirs({ - "default" => "/var/opt/gitlab/git-data", - "nfs" => "/mnt/nfs/git-data", - "cephfs" => "/mnt/cephfs/git-data" - }) - ``` - - Note that Omnibus stores the repositories in a `repositories` subdirectory - of the `git-data` directory. - -## Choose where new project repositories will be stored - -Once you set the multiple storage paths, you can choose where new projects will -be stored via the **Application Settings** in the Admin area. - -![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png) - -Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be -randomly placed on one of the selected paths. - -[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578 -[restart gitlab]: restart_gitlab.md#installations-from-source -[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure -[backups]: ../raketasks/backup_restore.md -[raketask]: https://gitlab.com/gitlab-org/gitlab-ce/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56 -[repospath]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/config/gitlab.yml.example#L457 +This document was moved to a [new location](repository_storage_paths.md). diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 75a0897eb15..f11257be5c3 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -86,7 +86,7 @@ used for time of the build. The configuration of this feature is covered in ### before_script `before_script` is used to define the command that should be run before all -builds, including deploy builds. This can be an array or a multi-line string. +builds, including deploy builds, but after the restoration of artifacts. This can be an array or a multi-line string. ### after_script diff --git a/doc/gitlab-basics/add-image.md b/doc/gitlab-basics/add-image.md index 476b48a217c..1a44123aa81 100644 --- a/doc/gitlab-basics/add-image.md +++ b/doc/gitlab-basics/add-image.md @@ -1,62 +1,56 @@ # How to add an image -The following are the steps to add images to your repository in -GitLab: +Using your standard tool for copying files (e.g. Finder in Mac OS, or Explorer +in Windows, or...), put the image file into the GitLab project. You can find the +project as a regular folder in your files. -Find the image that you’d like to add. +Go to your [shell](command-line-commands.md), and move into the folder of your +Gitlab project. This usually means running the following command until you get +to the desired destination: -In your computer files, find the GitLab project to which you'd like to add the image -(you'll find it as a regular file). Click on every file until you find exactly where you'd -like to add the image. There, paste the image. - -Go to your [shell](command-line-commands.md), and add the following commands: - -Add this command for every directory that you'd like to open: ``` -cd NAME-OF-FILE-YOU'D-LIKE-TO-OPEN +cd NAME-OF-FOLDER-YOU'D-LIKE-TO-OPEN ``` -Create a new branch: -``` -git checkout -b NAME-OF-BRANCH -``` +Check if your image is actually present in the directory (if you are in Windows, +use `dir` instead): -Check if your image was correctly added to the directory: ``` ls ``` You should see the name of the image in the list shown. -Move up the hierarchy through directories: -``` -cd ../ -``` +Check the status: -Check the status and you should see your image’s name in red: ``` git status ``` -Add your changes: +Your image's name should appear in red, so `git` took notice of it! Now add it +to the repository: + ``` git add NAME-OF-YOUR-IMAGE ``` -Check the status and you should see your image’s name in green: +Check the status again, your image's name should have turned green: + ``` git status ``` -Add the commit: +Commit: + ``` -git commit -m “DESCRIBE COMMIT IN A FEW WORDS” +git commit -m "DESCRIBE COMMIT IN A FEW WORDS" ``` -Now you can push (send) your changes (in the branch NAME-OF-BRANCH) to GitLab (the git remote named 'origin'): +Now you can push (send) your changes (in the branch NAME-OF-BRANCH) to GitLab +(the git remote named 'origin'): + ``` git push origin NAME-OF-BRANCH ``` -Your image will be added to your branch in your repository in GitLab. Create a [Merge Request](add-merge-request.md) -to integrate your changes to your project. +Your image will be added to your branch in your repository in GitLab. diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 322680f0cf4..65bfb0f7d6e 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -4,7 +4,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). -## MySQL +## Initial database setup # Install the database packages sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev @@ -32,8 +32,11 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" mysql> SET storage_engine=INNODB; + # If you have MySQL < 5.7.7 and want to enable utf8mb4 character set support with your GitLab install, you must set the following NOW: + mysql> SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda, innodb_large_prefix=1; + # Create the GitLab production database - mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; + mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_general_ci`; # Grant the GitLab user necessary permissions on the database mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES, REFERENCES ON `gitlabhq_production`.* TO 'git'@'localhost'; @@ -51,7 +54,203 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se # Quit the database session mysql> \q - # You are done installing the database and can go back to the rest of the installation. + # You are done installing the database for now and can go back to the rest of the installation. + +Please proceed to the rest of the installation before running through the utf8mb4 support section. + + +### MySQL utf8mb4 support + +After installation or upgrade, remember to [convert any new tables](#convert) to `utf8mb4`/`utf8mb4_general_ci`. + +--- + +GitLab 8.14 has introduced [a feature](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7420) requiring `utf8mb4` encoding to be supported in your GitLab MySQL Database, which is not the case if you have setup your database before GitLab 8.16. + +Follow the below instructions to ensure you use the most up to date requirements for your GitLab MySQL Database. + +**We are about to do the following:** +- Ensure you can enable `utf8mb4` encoding and `utf8mb4_general_ci` collation for your GitLab DB, tables and data. +- Convert your GitLab tables and data from `utf8`/`utf8_general_ci` to `utf8mb4`/`utf8mb4_general_ci` + +### Check for utf8mb4 support + +#### Check for InnoDB File-Per-Table Tablespaces + +We need to check, enable and maybe convert your existing GitLab DB tables to the [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) as a prerequise for supporting **utfb8mb4 with long indexes** required by recent GitLab databases. + + # Login to MySQL + mysql -u root -p + + # Type the MySQL root password + mysql > use gitlabhq_production; + + # Check your MySQL version is >= 5.5.3 (GitLab requires 5.5.14+) + mysql > SHOW VARIABLES LIKE 'version'; + +---------------+-----------------+ + | Variable_name | Value | + +---------------+-----------------+ + | version | 5.5.53-0+deb8u1 | + +---------------+-----------------+ + + # Note where is your MySQL data dir for later: + mysql > select @@datadir; + +----------------+ + | @@datadir | + +----------------+ + | /var/lib/mysql | + +----------------+ + + # Note whether your MySQL server runs with innodb_file_per_table ON or OFF: + mysql> SELECT @@innodb_file_per_table; + +-------------------------+ + | @@innodb_file_per_table | + +-------------------------+ + | 1 | + +-------------------------+ + + # You can now quit the database session + mysql> \q + +> You need **MySQL 5.5.3 or later** to perform this update. + +Whatever the results of your checks above, we now need to check if your GitLab database has been created using [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) (i.e. `innodb_file_per_table` was set to **1** at initial setup time). + +> Note: This setting is [enabled by default](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_file_per_table) since MySQL 5.6.6. + + # Run this command with root privileges, replace the data dir if different: + sudo ls -lh /var/lib/mysql/gitlabhq_production/*.ibd | wc -l + + # Run this command with root privileges, replace the data dir if different: + sudo ls -lh /var/lib/mysql/gitlabhq_production/*.frm | wc -l + + +- **Case 1: a result > 0 for both commands** + +Congrats, your GitLab database uses the right InnoDB tablespace format. + +However, you must still ensure that any **future tables** created by GitLab will still use the right format: + +- If `SELECT @@innodb_file_per_table` returned **1** previously, your server is running correctly. +> It's however a requirement to check *now* that this setting is indeed persisted in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file! + +- If `SELECT @@innodb_file_per_table` returned **0** previously, your server is not running correctly. +> [Enable innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) by running in a MySQL session as root the command `SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;` and persist the two settings in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file + +Now, if you have a **different result** returned by the 2 commands above, it means you have a **mix of tables format** uses in your GitLab database. This can happen if your MySQL server had different values for `innodb_file_per_table` in its life and you updated GitLab at different moments with those inconsistent values. So keep reading. + +- **Case 2: a result equals to "0" OR not the same result for both commands** + +Unfortunately, none or only some of your GitLab database tables use the GitLab requirement of [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html). + +Let's enable what we need on the running server: + + # Login to MySQL + mysql -u root -p + + # Type the MySQL root password + + # Enable innodb_file_per_table and set innodb_file_format on the running server: + mysql > SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda; + + # You can now quit the database session + mysql> \q + +> Now, **persist** [innodb_file_per_table](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format-enabling.html) in your `my.cnf` file. + +Ensure at this stage that your GitLab instance is indeed **stopped**. + +Now, let's convert all the GitLab database tables to the new tablespace format: + + # Login to MySQL + mysql -u root -p + + # Type the MySQL root password + mysql > use gitlabhq_production; + + # Safety check: you should still have those values set as follow: + mysql> SELECT @@innodb_file_per_table, @@innodb_file_format; + +-------------------------+----------------------+ + | @@innodb_file_per_table | @@innodb_file_format | + +-------------------------+----------------------+ + | 1 | Barracuda | + +-------------------------+----------------------+ + + mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE"; + + # If previous query returned results, copy & run all shown SQL statements + + # You can now quit the database session + mysql> \q + +--- + +#### Check for proper InnoDB File Format, Row Format, Large Prefix and tables conversion + +We need to check, enable and probably convert your existing GitLab DB tables to use the [Barracuda InnoDB file format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format.html), the [DYNAMIC row format](https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_dynamic_row_format) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) as a second prerequisite for supporting **utfb8mb4 with long indexes** used by recent GitLab databases. + + # Login to MySQL + mysql -u root -p + + # Type the MySQL root password + mysql > use gitlabhq_production; + + # Set innodb_file_format and innodb_large_prefix on the running server: + # Note: These are the default settings only for MySQL 5.7.7 and later. + + mysql > SET GLOBAL innodb_file_format=Barracuda, innodb_large_prefix=1; + + # Your DB must be (still) using utf8/utf8_general_ci as default encoding and collation. + # We will NOT change the default encoding and collation on the DB in order to support future GitLab migrations creating tables + # that require "long indexes support" on installations using MySQL <= 5.7.9. + # However, when such migrations occur, you will have to follow this guide again to convert the newly created tables to the proper encoding/collation. + + # This should return the following: + mysql> SELECT @@character_set_database, @@collation_database; + +--------------------------+----------------------+ + | @@character_set_database | @@collation_database | + +--------------------------+----------------------+ + | utf8 | utf8_general_ci | + +--------------------------+----------------------+ + +> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file. + +#### Tables and data conversion to utf8mb4 +<a name="convert"></a> + +Now that you have a persistent MySQL setup, you can safely upgrade tables after setup or upgrade time: + + # Convert tables not using ROW_FORMAT DYNAMIC: + + mysql> SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ROW_FORMAT=DYNAMIC;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE" AND ROW_FORMAT!="Dynamic"; + + # !! If previous query returned results, copy & run all shown SQL statements + + # Convert tables/columns not using utf8mb4/utf8mb4_general_ci as encoding/collation: + + mysql > SET foreign_key_checks = 0; + + mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_COLLATION != "utf8mb4_general_ci" AND TABLE_TYPE="BASE TABLE"; + + # !! If previous query returned results, copy & run all shown SQL statements + + # Turn foreign key checks back on + mysql > SET foreign_key_checks = 1; + + # You can now quit the database session + mysql> \q + +Ensure your GitLab database configuration file uses a proper connection encoding and collation: + +```sudo -u git -H editor config/database.yml``` + + production: + adapter: mysql2 + encoding: utf8mb4 + collation: utf8mb4_general_ci + +[Restart your GitLab instance](../administration/restart_gitlab.md). + ## MySQL strings limits diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index c44930a4ceb..ec13c2446ef 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -1,6 +1,6 @@ # System hooks -Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. +Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `project_update`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. System hooks can be used, e.g. for logging or changing information in a LDAP server. @@ -88,6 +88,23 @@ X-Gitlab-Event: System Hook } ``` +**Project updated:** + +```json +{ + "created_at": "2012-07-21T07:30:54Z", + "updated_at": "2012-07-21T07:38:22Z", + "event_name": "project_update", + "name": "StoreCloud", + "owner_email": "johnsmith@gmail.com", + "owner_name": "John Smith", + "path": "storecloud", + "path_with_namespace": "jsmith/storecloud", + "project_id": 74, + "project_visibility": "private", +} +``` + **New Team Member:** ```json diff --git a/doc/update/8.15-to-8.16.md b/doc/update/8.15-to-8.16.md index 63f3c3fdda9..2695a16ac0b 100644 --- a/doc/update/8.15-to-8.16.md +++ b/doc/update/8.15-to-8.16.md @@ -97,6 +97,8 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` +**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md). + ### 6. Update gitlab-workhorse Install and compile gitlab-workhorse. This requires diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index 0f959b956a5..cc688a7f99c 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -140,75 +140,77 @@ into the password field. ## Recovery options -If you lose your code generation device (such as your mobile phone) and you need -to disable two-factor authentication on your account, you have several options. +To disable two-factor authentication on your account (for example, if you +have lost your code generation device) you can: +* [Use a saved recovery code](#use-a-saved-recovery-code) +* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-SSH) +* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account) ### Use a saved recovery code -When you enabled two-factor authentication for your account, a series of -recovery codes were generated. If you saved those codes somewhere safe, you -may use one to sign in. +Enabling two-factor authentication for your account generated several recovery +codes. If you saved these codes, you can use one of them to sign in. -First, enter your username/email and password on the GitLab sign in page. When -prompted for a two-factor code, enter one of the recovery codes you saved -previously. +To use a recovery code, enter your username/email and password on the GitLab +sign-in page. When prompted for a two-factor code, enter the recovery code. -> **Note:** Once a particular recovery code has been used, it cannot be used again. - You may still use the other saved recovery codes at a later time. +> **Note:** Once you use a recovery code, you cannot re-use it. You can still + use the other recovery codes you saved. ### Generate new recovery codes using SSH -It's not uncommon for users to forget to save the recovery codes when enabling -two-factor authentication. If you have an SSH key added to your GitLab account, -you can generate a new set of recovery codes using SSH. - -Run `ssh git@gitlab.example.com 2fa_recovery_codes`. You will be prompted to -confirm that you wish to generate new codes. If you choose to continue, any -previously saved codes will be invalidated. - -```bash -$ ssh git@gitlab.example.com 2fa_recovery_codes -Are you sure you want to generate new two-factor recovery codes? -Any existing recovery codes you saved will be invalidated. (yes/no) -yes - -Your two-factor authentication recovery codes are: - -119135e5a3ebce8e -11f6v2a498810dcd -3924c7ab2089c902 -e79a3398bfe4f224 -34bd7b74adbc8861 -f061691d5107df1a -169bf32a18e63e7f -b510e7422e81c947 -20dbed24c5e74663 -df9d3b9403b9c9f0 - -During sign in, use one of the codes above when prompted for -your two-factor code. Then, visit your Profile Settings and add -a new device so you do not lose access to your account again. -``` - -Next, go to the GitLab sign in page and enter your username/email and password. -When prompted for a two-factor code, enter one of the recovery codes obtained -from the command line output. - -> **Note:** After signing in, you should immediately visit your **Profile Settings - -> Account** to set up two-factor authentication with a new device. - -### Ask a GitLab administrator to disable two-factor on your account - -If the above two methods are not possible, you may ask a GitLab global -administrator to disable two-factor authentication for your account. Please -be aware that this will temporarily leave your account in a less secure state. -You should sign in and re-enable two-factor authentication as soon as possible -after the administrator disables it. +Users often forget to save their recovery codes when enabling two-factor +authentication. If an SSH key is added to your GitLab account, you can generate +a new set of recovery codes with SSH. + +1. Run `ssh git@gitlab.example.com 2fa_recovery_codes`. +2. You are prompted to confirm that you want to generate new codes. Continuing this process invalidates previously saved codes. + ``` + bash + $ ssh git@gitlab.example.com 2fa_recovery_codes + Are you sure you want to generate new two-factor recovery codes? + Any existing recovery codes you saved will be invalidated. (yes/no) + + yes + + Your two-factor authentication recovery codes are: + + 119135e5a3ebce8e + 11f6v2a498810dcd + 3924c7ab2089c902 + e79a3398bfe4f224 + 34bd7b74adbc8861 + f061691d5107df1a + 169bf32a18e63e7f + b510e7422e81c947 + 20dbed24c5e74663 + df9d3b9403b9c9f0 + + During sign in, use one of the codes above when prompted for your + two-factor code. Then, visit your Profile Settings and add a new device + so you do not lose access to your account again. + ``` +3. Go to the GitLab sign-in page and enter your username/email and password. When prompted for a two-factor code, enter one of the recovery codes obtained +from the command-line output. + +> **Note:** After signing in, visit your **Profile Settings -> Account** immediately to set up two-factor authentication with a new + device. + +### Ask a GitLab administrator to disable two-factor authentication on your account + +If you cannot use a saved recovery code or generate new recovery codes, ask a +GitLab global administrator to disable two-factor authentication for your +account. This will temporarily leave your account in a less secure state. +Sign in and re-enable two-factor authentication as soon as possible. ## Note to GitLab administrators -You need to take special care to that 2FA keeps working after -[restoring a GitLab backup](../../../raketasks/backup_restore.md). +- You need to take special care to that 2FA keeps working after +[restoring a GitLab backup](../raketasks/backup_restore.md). + +- To ensure 2FA authorizes correctly with TOTP server, you may want to ensure +your GitLab server's time is synchronized via a service like NTP. Otherwise, +you may have cases where authorization always fails because of time differences. [Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en [FreeOTP]: https://fedorahosted.org/freeotp/ diff --git a/features/steps/project/merge_requests/revert.rb b/features/steps/project/merge_requests/revert.rb index 3cc4fe9dafb..31f95b524b3 100644 --- a/features/steps/project/merge_requests/revert.rb +++ b/features/steps/project/merge_requests/revert.rb @@ -30,14 +30,13 @@ class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps end step 'I am signed in as a developer of the project' do + @user = create(:user) { |u| @project.add_developer(u) } login_as(@user) end step 'There is an open Merge Request' do - @user = create(:user) - @project = create(:project, :public, :repository) - @project_member = create(:project_member, :developer, user: @user, project: @project) - @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project) + @merge_request = create(:merge_request, :with_diffs, :simple) + @project = @merge_request.source_project end step 'I should see a revert error' do diff --git a/features/support/env.rb b/features/support/env.rb index 8dbe3624410..f394c30d52f 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -4,7 +4,6 @@ SimpleCovEnv.start! ENV['RAILS_ENV'] = 'test' require './config/environment' require 'rspec/expectations' -require 'sidekiq/testing/inline' require_relative 'capybara' require_relative 'db_cleaner' @@ -15,7 +14,7 @@ if ENV['CI'] Knapsack::Adapters::SpinachAdapter.bind end -%w(select2_helper test_env repo_helpers wait_for_ajax).each do |f| +%w(select2_helper test_env repo_helpers wait_for_ajax sidekiq).each do |f| require Rails.root.join('spec', 'support', f) end diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 6d04f68c8f9..a3d495a5da0 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -153,7 +153,7 @@ module Banzai title = object_link_title(object) klass = reference_class(object_sym) - data = data_attributes_for(link_content || match, project, object) + data = data_attributes_for(link_content || match, project, object, link: !!link_content) if matches.names.include?("url") && matches[:url] url = matches[:url] @@ -172,9 +172,10 @@ module Banzai end end - def data_attributes_for(text, project, object) + def data_attributes_for(text, project, object, link: false) data_attribute( original: text, + link: link, project: project.id, object_sym => object.id ) diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 4d1bc687696..fd6b9704132 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -62,7 +62,7 @@ module Banzai end end - def data_attributes_for(text, project, object) + def data_attributes_for(text, project, object, link: false) if object.is_a?(ExternalIssue) data_attribute( project: project.id, diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb index 026b81ac175..a447e2b8bff 100644 --- a/lib/banzai/filter/syntax_highlight_filter.rb +++ b/lib/banzai/filter/syntax_highlight_filter.rb @@ -20,17 +20,19 @@ module Banzai code = node.text css_classes = "code highlight" lexer = lexer_for(language) + lang = lexer.tag begin code = format(lex(lexer, code)) - css_classes << " js-syntax-highlight #{lexer.tag}" + css_classes << " js-syntax-highlight #{lang}" rescue + lang = nil # Gracefully handle syntax highlighter bugs/errors to ensure # users can still access an issue/comment/etc. end - highlighted = %(<pre class="#{css_classes}" v-pre="true"><code>#{code}</code></pre>) + highlighted = %(<pre class="#{css_classes}" lang="#{lang}" v-pre="true"><code>#{code}</code></pre>) # Extracted to a method to measure it replace_parent_pre_element(node, highlighted) diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb index ac7bbcb0d10..b64a1287d4d 100644 --- a/lib/banzai/filter/video_link_filter.rb +++ b/lib/banzai/filter/video_link_filter.rb @@ -35,7 +35,8 @@ module Banzai src: element['src'], width: '400', controls: true, - 'data-setup' => '{}') + 'data-setup' => '{}', + 'data-title' => element['title'] || element['alt']) link = doc.document.create_element( 'a', diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 5a1f873496c..ac95a79009b 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -1,6 +1,12 @@ module Banzai module Pipeline class GfmPipeline < BasePipeline + # These filters convert GitLab Flavored Markdown (GFM) to HTML. + # The handlers defined in app/assets/javascripts/copy_as_gfm.js.es6 + # consequently convert that same HTML to GFM to be copied to the clipboard. + # Every filter that generates HTML from GFM should have a handler in + # app/assets/javascripts/copy_as_gfm.js.es6, in reverse order. + # The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. def self.filters @filters ||= FilterArray[ Filter::SyntaxHighlightFilter, diff --git a/lib/gitlab/ci/trace_reader.rb b/lib/gitlab/ci/trace_reader.rb index 37e51536e8f..1d7ddeb3e0f 100644 --- a/lib/gitlab/ci/trace_reader.rb +++ b/lib/gitlab/ci/trace_reader.rb @@ -42,6 +42,7 @@ module Gitlab end chunks.join.lines.last(max_lines).join + .force_encoding(Encoding.default_external) end end end diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb new file mode 100644 index 00000000000..8db91d25a4b --- /dev/null +++ b/lib/gitlab/job_waiter.rb @@ -0,0 +1,27 @@ +module Gitlab + # JobWaiter can be used to wait for a number of Sidekiq jobs to complete. + class JobWaiter + # The sleep interval between checking keys, in seconds. + INTERVAL = 0.1 + + # jobs - The job IDs to wait for. + def initialize(jobs) + @jobs = jobs + end + + # Waits for all the jobs to be completed. + # + # timeout - The maximum amount of seconds to block the caller for. This + # ensures we don't indefinitely block a caller in case a job takes + # long to process, or is never processed. + def wait(timeout = 60) + start = Time.current + + while (Time.current - start) <= timeout + break if SidekiqStatus.all_completed?(@jobs) + + sleep(INTERVAL) # to not overload Redis too much. + end + end + end +end diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb new file mode 100644 index 00000000000..aadc401ff8d --- /dev/null +++ b/lib/gitlab/sidekiq_status.rb @@ -0,0 +1,66 @@ +module Gitlab + # The SidekiqStatus module and its child classes can be used for checking if a + # Sidekiq job has been processed or not. + # + # To check if a job has been completed, simply pass the job ID to the + # `completed?` method: + # + # job_id = SomeWorker.perform_async(...) + # + # if Gitlab::SidekiqStatus.completed?(job_id) + # ... + # end + # + # For each job ID registered a separate key is stored in Redis, making lookups + # much faster than using Sidekiq's built-in job finding/status API. These keys + # expire after a certain period of time to prevent storing too many keys in + # Redis. + module SidekiqStatus + STATUS_KEY = 'gitlab-sidekiq-status:%s'.freeze + + # The default time (in seconds) after which a status key is expired + # automatically. The default of 30 minutes should be more than sufficient + # for most jobs. + DEFAULT_EXPIRATION = 30.minutes.to_i + + # Starts tracking of the given job. + # + # jid - The Sidekiq job ID + # expire - The expiration time of the Redis key. + def self.set(jid, expire = DEFAULT_EXPIRATION) + Sidekiq.redis do |redis| + redis.set(key_for(jid), 1, ex: expire) + end + end + + # Stops the tracking of the given job. + # + # jid - The Sidekiq job ID to remove. + def self.unset(jid) + Sidekiq.redis do |redis| + redis.del(key_for(jid)) + end + end + + # Returns true if all the given job have been completed. + # + # jids - The Sidekiq job IDs to check. + # + # Returns true or false. + def self.all_completed?(jids) + keys = jids.map { |jid| key_for(jid) } + + responses = Sidekiq.redis do |redis| + redis.pipelined do + keys.each { |key| redis.exists(key) } + end + end + + responses.all? { |value| !value } + end + + def self.key_for(jid) + STATUS_KEY % jid + end + end +end diff --git a/lib/gitlab/sidekiq_status/client_middleware.rb b/lib/gitlab/sidekiq_status/client_middleware.rb new file mode 100644 index 00000000000..779a9998b22 --- /dev/null +++ b/lib/gitlab/sidekiq_status/client_middleware.rb @@ -0,0 +1,10 @@ +module Gitlab + module SidekiqStatus + class ClientMiddleware + def call(_, job, _, _) + SidekiqStatus.set(job['jid']) + yield + end + end + end +end diff --git a/lib/gitlab/sidekiq_status/server_middleware.rb b/lib/gitlab/sidekiq_status/server_middleware.rb new file mode 100644 index 00000000000..31dfa46ff9d --- /dev/null +++ b/lib/gitlab/sidekiq_status/server_middleware.rb @@ -0,0 +1,13 @@ +module Gitlab + module SidekiqStatus + class ServerMiddleware + def call(worker, job, queue) + ret = yield + + SidekiqStatus.unset(job['jid']) + + ret + end + end + end +end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 602de72d23f..84db26a958a 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Admin::GroupsController do let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } let(:admin) { create(:admin) } before do diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb index 8eaacef2024..2c35d394b74 100644 --- a/spec/controllers/admin/projects_controller_spec.rb +++ b/spec/controllers/admin/projects_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Admin::ProjectsController do - let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:project) { create(:empty_project, :public) } before do sign_in(create(:admin)) diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index ea2fd90a9b0..7d2f6dd9d0a 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe AutocompleteController do - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:user) { create(:user) } context 'GET users' do diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb index 465013231f9..2fcb4a6a528 100644 --- a/spec/controllers/blob_controller_spec.rb +++ b/spec/controllers/blob_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::BlobController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index 5022a3e2c80..86f01f437a2 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Ci::ProjectsController do let(:visibility) { :public } - let!(:project) { create(:project, visibility, ci_id: 1) } + let!(:project) { create(:empty_project, visibility, ci_id: 1) } let(:ci_id) { project.ci_id } describe '#index' do diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb index 19fbc2f7748..79ef3a1adad 100644 --- a/spec/controllers/dashboard/todos_controller_spec.rb +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Dashboard::TodosController do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:todo_service) { TodoService.new } describe 'GET #index' do diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index 8c52f615b8b..6e4b5f78e33 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Groups::MilestonesController do let(:group) { create(:group) } - let(:project) { create(:project, group: group) } + let(:project) { create(:empty_project, group: group) } let(:project2) { create(:empty_project, group: group) } let(:user) { create(:user) } let(:title) { '肯定不是中文的问题' } diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 98dfb3e5216..cad82a34fb0 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe GroupsController do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } let!(:group_member) { create(:group_member, group: group, user: user) } describe 'GET #index' do diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index ce7c0b334ee..fa4cc0ebbe0 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -52,7 +52,7 @@ describe Import::BitbucketController do end it "assigns variables" do - @project = create(:project, import_type: 'bitbucket', creator_id: user.id) + @project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id) allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) get :status @@ -63,7 +63,7 @@ describe Import::BitbucketController do end it "does not show already added project" do - @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') + @project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) get :status diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb index 5f0f6dea821..fffbc805335 100644 --- a/spec/controllers/import/fogbugz_controller_spec.rb +++ b/spec/controllers/import/fogbugz_controller_spec.rb @@ -16,7 +16,7 @@ describe Import::FogbugzController do end it 'assigns variables' do - @project = create(:project, import_type: 'fogbugz', creator_id: user.id) + @project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id) stub_client(repos: [@repo]) get :status @@ -26,7 +26,7 @@ describe Import::FogbugzController do end it 'does not show already added project' do - @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim') + @project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim') stub_client(repos: [@repo]) get :status diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 6f75ebb16c8..3f73ea000ae 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -36,7 +36,7 @@ describe Import::GitlabController do end it "assigns variables" do - @project = create(:project, import_type: 'gitlab', creator_id: user.id) + @project = create(:empty_project, import_type: 'gitlab', creator_id: user.id) stub_client(projects: [@repo]) get :status @@ -46,7 +46,7 @@ describe Import::GitlabController do end it "does not show already added project" do - @project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') + @project = create(:empty_project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') stub_client(projects: [@repo]) get :status diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb index 4241db6e771..c96fb90f70e 100644 --- a/spec/controllers/import/google_code_controller_spec.rb +++ b/spec/controllers/import/google_code_controller_spec.rb @@ -27,7 +27,7 @@ describe Import::GoogleCodeController do end it "assigns variables" do - @project = create(:project, import_type: 'google_code', creator_id: user.id) + @project = create(:empty_project, import_type: 'google_code', creator_id: user.id) stub_client(repos: [@repo], incompatible_repos: []) get :status @@ -38,7 +38,7 @@ describe Import::GoogleCodeController do end it "does not show already added project" do - @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim') + @project = create(:empty_project, import_type: 'google_code', creator_id: user.id, import_source: 'vim') stub_client(repos: [@repo], incompatible_repos: []) get :status diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb index 79b819a1377..9e3a31e1a6b 100644 --- a/spec/controllers/notification_settings_controller_spec.rb +++ b/spec/controllers/notification_settings_controller_spec.rb @@ -93,7 +93,7 @@ describe NotificationSettingsController do end context 'not authorized' do - let(:private_project) { create(:project, :private) } + let(:private_project) { create(:empty_project, :private) } before { sign_in(user) } it 'returns 404' do diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb index f5ea097af8b..8b71d6518bb 100644 --- a/spec/controllers/projects/avatars_controller_spec.rb +++ b/spec/controllers/projects/avatars_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::AvatarsController do - let(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:project) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb index 4402ca43c65..addc5e7ec33 100644 --- a/spec/controllers/projects/blame_controller_spec.rb +++ b/spec/controllers/projects/blame_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::BlameController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index f35c5d992d9..b36d0e69330 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe Projects::BlobController do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index b88586b8678..9de03876755 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::BranchesController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:developer) { create(:user) } diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb index 0fa06a38d2a..a95cfc5c6be 100644 --- a/spec/controllers/projects/commit_controller_spec.rb +++ b/spec/controllers/projects/commit_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::CommitController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:commit) { project.commit("master") } let(:pipeline) { create(:ci_pipeline, project: project, commit: commit) } diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 1ac7e03a2db..54b8d1108a5 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::CommitsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index b03c4b52de6..e811c76fb31 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::CompareController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:ref_from) { "improve%2Fawesome" } let(:ref_to) { "feature" } diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb index a971adf0539..6a6d71a16ee 100644 --- a/spec/controllers/projects/cycle_analytics_controller_spec.rb +++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::CycleAnalyticsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb index ff617fea847..79ab364a6f3 100644 --- a/spec/controllers/projects/discussions_controller_spec.rb +++ b/spec/controllers/projects/discussions_controller_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' describe Projects::DiscussionsController do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:merge_request) { create(:merge_request, source_project: project) } + let(:user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.source_project } let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } let(:discussion) { note.discussion } diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb index 038dfeb8466..a4884256c92 100644 --- a/spec/controllers/projects/find_file_controller_spec.rb +++ b/spec/controllers/projects/find_file_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::FindFileController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index 028ea067a97..a867668d97b 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::ForksController do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:forked_project) { Projects::ForkService.new(project, user).execute } let(:group) { create(:group, owner: forked_project.creator) } diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index 74e6603b0cb..bbe8e4bf6b2 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::GraphsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb index 17dc101b7ee..a976a9c27ab 100644 --- a/spec/controllers/projects/group_links_controller_spec.rb +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::GroupLinksController do let(:group) { create(:group, :private) } let(:group2) { create(:group, :private) } - let(:project) { create(:project, :private, group: group2) } + let(:project) { create(:empty_project, :private, group: group2) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index b5987a83df0..5f27f336f72 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -98,7 +98,7 @@ describe Projects::IssuesController do end it 'fills in an issue for a merge request' do - project_with_repository = create(:project) + project_with_repository = create(:project, :repository) project_with_repository.team << [user, :developer] mr = create(:merge_request_with_diff_notes, source_project: project_with_repository) @@ -124,7 +124,7 @@ describe Projects::IssuesController do describe 'PUT #update' do context 'when moving issue to another private project' do - let(:another_project) { create(:project, :private) } + let(:another_project) { create(:empty_project, :private) } before do sign_in(user) @@ -466,7 +466,7 @@ describe Projects::IssuesController do context "when the user is owner" do let(:owner) { create(:user) } let(:namespace) { create(:namespace, owner: owner) } - let(:project) { create(:project, namespace: namespace) } + let(:project) { create(:empty_project, namespace: namespace) } before { sign_in(owner) } diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 6d30d085056..14207bf6b7a 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::MilestonesController do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:milestone) { create(:milestone, project: project) } let(:issue) { create(:issue, project: project, milestone: milestone) } diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 9f6d4ec6537..dc597202050 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::NotesController do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:note) { create(:note, noteable: issue, project: project) } @@ -16,6 +16,7 @@ describe Projects::NotesController do describe 'POST create' do let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.source_project } let(:request_params) do { note: { note: 'some note', noteable_id: merge_request.id, noteable_type: 'MergeRequest' }, @@ -88,6 +89,7 @@ describe Projects::NotesController do end describe "resolving and unresolving" do + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 442f81187dc..416eaa0037e 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -143,7 +143,7 @@ describe Projects::ProjectMembersController do end context 'and is an owner' do - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } before { project.team << [user, :master] } @@ -234,7 +234,7 @@ describe Projects::ProjectMembersController do end describe 'POST apply_import' do - let(:another_project) { create(:project, :private) } + let(:another_project) { create(:empty_project, :private) } let(:member) { create(:user) } before do diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 04bd9a01f7b..b23d6e257ba 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::RawController do - let(:public_project) { create(:project, :public) } + let(:public_project) { create(:project, :public, :repository) } describe "#show" do context 'regular filename' do diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb index abd45a74e2d..d8fb4667c67 100644 --- a/spec/controllers/projects/refs_controller_spec.rb +++ b/spec/controllers/projects/refs_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::RefsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 9fd5c3b85f6..69fcc26c77e 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::ReleasesController do - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let!(:user) { create(:user) } let!(:release) { create(:release, project: project) } let!(:tag) { release.tag } diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 38e02a46626..04e88879fb8 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" describe Projects::RepositoriesController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } describe "GET archive" do context 'as a guest' do diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index a6e708c01e4..16365642a34 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::ServicesController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { create(:service, project: project) } diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb index e0f9a5b24a6..65f7bb34f4a 100644 --- a/spec/controllers/projects/settings/integrations_controller_spec.rb +++ b/spec/controllers/projects/settings/integrations_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::Settings::IntegrationsController do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index 5e661c2c41d..c36a5fdd66c 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::TagsController do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let!(:release) { create(:release, project: project) } let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') } diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb index 19a152bcb05..99d0bcfa8d1 100644 --- a/spec/controllers/projects/templates_controller_spec.rb +++ b/spec/controllers/projects/templates_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::TemplatesController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:file_path_1) { '.gitlab/issue_templates/bug.md' } diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 1cc050247c6..b81645a3d2d 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::TreeController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 71d0e4be834..f1c8891e87a 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -1,7 +1,7 @@ require('spec_helper') describe Projects::UploadsController do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index d0a63aa9403..9323f723bdb 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1,11 +1,11 @@ require('spec_helper') describe ProjectsController do - let(:project) { create(:project) } - let(:public_project) { create(:project, :public) } - let(:user) { create(:user) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:project) { create(:empty_project) } + let(:public_project) { create(:empty_project, :public) } + let(:user) { create(:user) } + let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } describe 'GET index' do context 'as a user' do @@ -32,7 +32,7 @@ describe ProjectsController do before { sign_in(user) } context "user does not have access to project" do - let(:private_project) { create(:project, :private) } + let(:private_project) { create(:empty_project, :private) } it "does not initialize notification setting" do get :show, namespace_id: private_project.namespace.path, id: private_project.path @@ -146,6 +146,8 @@ describe ProjectsController do end context "rendering default project view" do + let(:public_project) { create(:project, :public, :repository) } + render_views it "renders the activity view" do @@ -190,25 +192,11 @@ describe ProjectsController do expect(assigns(:project)).to eq(public_project) expect(response).to redirect_to("/#{public_project.path_with_namespace}") end - - # MySQL queries are case insensitive by default, so this spec would fail. - if Gitlab::Database.postgresql? - context "when there is also a match with the same casing" do - let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } - - it "loads the exactly matched project" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase - - expect(assigns(:project)).to eq(other_project) - expect(response).to have_http_status(200) - end - end - end end end context "when the url contains .atom" do - let(:public_project_with_dot_atom) { build(:project, :public, name: 'my.atom', path: 'my.atom') } + let(:public_project_with_dot_atom) { build(:empty_project, :public, name: 'my.atom', path: 'my.atom') } it 'expects an error creating the project' do expect(public_project_with_dot_atom).not_to be_valid @@ -217,7 +205,7 @@ describe ProjectsController do context 'when the project is pending deletions' do it 'renders a 404 error' do - project = create(:project, pending_delete: true) + project = create(:empty_project, pending_delete: true) sign_in(user) get :show, namespace_id: project.namespace.path, id: project.path @@ -233,6 +221,7 @@ describe ProjectsController do let(:admin) { create(:admin) } it "sets the repository to the right path after a rename" do + project = create(:project, :repository) new_path = 'renamed_path' project_params = { path: new_path } controller.instance_variable_set(:@project, project) @@ -384,6 +373,8 @@ describe ProjectsController do end describe "GET refs" do + let(:public_project) { create(:project, :public) } + it "gets a list of branches and tags" do get :refs, namespace_id: public_project.namespace.path, id: public_project.path diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 69124ab06bf..570d9fa43f8 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -41,7 +41,7 @@ describe UploadsController do end context "when viewing a project avatar" do - let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:project) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } context "when the project is public" do before do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 19a8b1fe524..bbe9aaf737f 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -73,7 +73,7 @@ describe UsersController do end context 'forked project' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:forked_project) { Projects::ForkService.new(project, user).execute } before do @@ -91,7 +91,7 @@ describe UsersController do end describe 'GET #calendar_activities' do - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:user) { create(:user) } before do diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb index 27cece487bd..75f8982ecd9 100644 --- a/spec/factories/deploy_keys_projects.rb +++ b/spec/factories/deploy_keys_projects.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :deploy_keys_project do deploy_key - project + project factory: :empty_project end end diff --git a/spec/factories/events.rb b/spec/factories/events.rb index 8820d527c61..bfe41f71b57 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :event do - project + project factory: :empty_project author factory: :user factory :closed_issue_event do diff --git a/spec/factories/file_uploader.rb b/spec/factories/file_uploader.rb index 1b36e21f2b0..bc74aeecc3b 100644 --- a/spec/factories/file_uploader.rb +++ b/spec/factories/file_uploader.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :file_uploader do - project + project factory: :empty_project secret nil transient do diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 2b4670be468..7e09f1ba8ea 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -6,7 +6,7 @@ FactoryGirl.define do factory :issue do title author - project + project factory: :empty_project trait :confidential do confidential true diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb index 3e8822faf97..5ba8443c62c 100644 --- a/spec/factories/labels.rb +++ b/spec/factories/labels.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :label, class: ProjectLabel do sequence(:title) { |n| "label#{n}" } color "#990000" - project + project factory: :empty_project transient do priority nil diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 37eb49c94df..22f84150bb3 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :merge_request do title author - source_project factory: :project + association :source_project, :repository, factory: :project target_project { source_project } # $ git log --pretty=oneline feature..master diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb index 84da71ed6dc..841ab3c73b8 100644 --- a/spec/factories/milestones.rb +++ b/spec/factories/milestones.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :milestone do title - project + project factory: :empty_project trait :active do state "active" diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 476f2a37793..a21da7074f9 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -4,7 +4,7 @@ include ActionDispatch::TestProcess FactoryGirl.define do factory :note do - project + project factory: :empty_project note "Note" author on_issue @@ -16,10 +16,16 @@ FactoryGirl.define do factory :note_on_personal_snippet, traits: [:on_personal_snippet] factory :system_note, traits: [:system] - factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: LegacyDiffNote - factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: LegacyDiffNote + factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: LegacyDiffNote do + association :project, :repository + end + + factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: LegacyDiffNote do + association :project, :repository + end factory :diff_note_on_merge_request, traits: [:on_merge_request], class: DiffNote do + association :project, :repository position do Gitlab::Diff::Position.new( old_path: "files/ruby/popen.rb", @@ -37,6 +43,7 @@ FactoryGirl.define do end factory :diff_note_on_commit, traits: [:on_commit], class: DiffNote do + association :project, :repository position do Gitlab::Diff::Position.new( old_path: "files/ruby/popen.rb", @@ -49,6 +56,7 @@ FactoryGirl.define do end trait :on_commit do + association :project, :repository noteable nil noteable_type 'Commit' noteable_id nil diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb index e73cc05f9d7..50341d943f5 100644 --- a/spec/factories/project_group_links.rb +++ b/spec/factories/project_group_links.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :project_group_link do - project + project factory: :empty_project group end end diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb index c21927640d1..d62799a5a47 100644 --- a/spec/factories/project_members.rb +++ b/spec/factories/project_members.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :project_member do user - project + project factory: :empty_project master trait(:guest) { access_level ProjectMember::GUEST } diff --git a/spec/factories/project_snippets.rb b/spec/factories/project_snippets.rb index d681a2c8483..e0fe1b36fd3 100644 --- a/spec/factories/project_snippets.rb +++ b/spec/factories/project_snippets.rb @@ -1,5 +1,5 @@ FactoryGirl.define do factory :project_snippet, parent: :snippet, class: :ProjectSnippet do - project + project factory: :empty_project end end diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 74497dc82c0..6a6d6fa171f 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -2,6 +2,6 @@ FactoryGirl.define do factory :release do tag "v1.1.0" description "Awesome release" - project + project factory: :empty_project end end diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb index 78eb929c6e7..6287c40afe9 100644 --- a/spec/factories/sent_notifications.rb +++ b/spec/factories/sent_notifications.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :sent_notification do - project + project factory: :empty_project recipient factory: :user noteable factory: :issue reply_key "0123456789abcdef" * 2 diff --git a/spec/factories/services.rb b/spec/factories/services.rb index 9de78d68280..a14a46c803e 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -1,5 +1,5 @@ FactoryGirl.define do factory :service do - project + project factory: :empty_project end end diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb index 85a8c263643..91d6f39a5bf 100644 --- a/spec/factories/todos.rb +++ b/spec/factories/todos.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :todo do - project + project factory: :empty_project author user target factory: :issue diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb new file mode 100644 index 00000000000..f3a5b565122 --- /dev/null +++ b/spec/features/copy_as_gfm_spec.rb @@ -0,0 +1,432 @@ +require 'spec_helper' + +describe 'Copy as GFM', feature: true, js: true do + include GitlabMarkdownHelper + include ActionView::Helpers::JavaScriptHelper + + before do + @feat = MarkdownFeature.new + + # `markdown` helper expects a `@project` variable + @project = @feat.project + + visit namespace_project_issue_path(@project.namespace, @project, @feat.issue) + end + + # The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert GitLab Flavored Markdown (GFM) to HTML. + # The handlers defined in app/assets/javascripts/copy_as_gfm.js.es6 consequently convert that same HTML to GFM. + # To make sure these filters and handlers are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle + # by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper. + + # These are all in a single `it` for performance reasons. + it 'works', :aggregate_failures do + verify( + 'nesting', + + '> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**' + ) + + verify( + 'a real world example from the gitlab-ce README', + + <<-GFM.strip_heredoc + # GitLab + + [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) + [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) + [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) + [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) + + ## Canonical source + + The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). + + ## Open source software to collaborate on code + + To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/). + + + - Manage Git repositories with fine grained access controls that keep your code secure + + - Perform code reviews and enhance collaboration with merge requests + + - Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications + + - Each project can also have an issue tracker, issue board, and a wiki + + - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises + + - Completely free and open source (MIT Expat license) + GFM + ) + + verify( + 'InlineDiffFilter', + + '{-Deleted text-}', + '{+Added text+}' + ) + + verify( + 'TaskListFilter', + + '- [ ] Unchecked task', + '- [x] Checked task', + '1. [ ] Unchecked numbered task', + '1. [x] Checked numbered task' + ) + + verify( + 'ReferenceFilter', + + # issue reference + @feat.issue.to_reference, + # full issue reference + @feat.issue.to_reference(full: true), + # issue URL + namespace_project_issue_url(@project.namespace, @project, @feat.issue), + # issue URL with note anchor + namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123'), + # issue link + "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue)})", + # issue link with note anchor + "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123')})", + ) + + verify( + 'AutolinkFilter', + + 'https://example.com' + ) + + verify( + 'TableOfContentsFilter', + + '[[_TOC_]]' + ) + + verify( + 'EmojiFilter', + + ':thumbsup:' + ) + + verify( + 'ImageLinkFilter', + + '![Image](https://example.com/image.png)' + ) + + verify( + 'VideoLinkFilter', + + '![Video](https://example.com/video.mp4)' + ) + + verify( + 'MathFilter: math as converted from GFM to HTML', + + '$`c = \pm\sqrt{a^2 + b^2}`$', + + # math block + <<-GFM.strip_heredoc + ```math + c = \pm\sqrt{a^2 + b^2} + ``` + GFM + ) + + aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do + gfm = '$`c = \pm\sqrt{a^2 + b^2}`$' + + html = <<-HTML.strip_heredoc + <span class="katex"> + <span class="katex-mathml"> + <math> + <semantics> + <mrow> + <mi>c</mi> + <mo>=</mo> + <mo>±</mo> + <msqrt> + <mrow> + <msup> + <mi>a</mi> + <mn>2</mn> + </msup> + <mo>+</mo> + <msup> + <mi>b</mi> + <mn>2</mn> + </msup> + </mrow> + </msqrt> + </mrow> + <annotation encoding="application/x-tex">c = \\pm\\sqrt{a^2 + b^2}</annotation> + </semantics> + </math> + </span> + <span class="katex-html" aria-hidden="true"> + <span class="strut" style="height: 0.913389em;"></span> + <span class="strut bottom" style="height: 1.04em; vertical-align: -0.126611em;"></span> + <span class="base textstyle uncramped"> + <span class="mord mathit">c</span> + <span class="mrel">=</span> + <span class="mord">±</span> + <span class="sqrt mord"><span class="sqrt-sign" style="top: -0.073389em;"> + <span class="style-wrap reset-textstyle textstyle uncramped">√</span> + </span> + <span class="vlist"> + <span class="" style="top: 0em;"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 1em;"></span> + </span> + <span class="mord textstyle cramped"> + <span class="mord"> + <span class="mord mathit">a</span> + <span class="msupsub"> + <span class="vlist"> + <span class="" style="top: -0.289em; margin-right: 0.05em;"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 0em;"></span> + </span> + <span class="reset-textstyle scriptstyle cramped"> + <span class="mord mathrm">2</span> + </span> + </span> + <span class="baseline-fix"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 0em;"></span> + </span> + </span> + </span> + </span> + </span> + <span class="mbin">+</span> + <span class="mord"> + <span class="mord mathit">b</span> + <span class="msupsub"> + <span class="vlist"> + <span class="" style="top: -0.289em; margin-right: 0.05em;"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 0em;"></span> + </span> + <span class="reset-textstyle scriptstyle cramped"> + <span class="mord mathrm">2</span> + </span> + </span> + <span class="baseline-fix"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 0em;"></span> + </span> + </span> + </span> + </span> + </span> + </span> + </span> + <span class="" style="top: -0.833389em;"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 1em;"></span> + </span> + <span class="reset-textstyle textstyle uncramped sqrt-line"></span> + </span> + <span class="baseline-fix"> + <span class="fontsize-ensurer reset-size5 size5"> + <span class="" style="font-size: 1em;"></span> + </span> + </span> + </span> + </span> + </span> + </span> + </span> + HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + + verify( + 'SanitizationFilter', + + <<-GFM.strip_heredoc + <sub>sub</sub> + + <dl> + <dt>dt</dt> + <dd>dd</dd> + </dl> + + <kbd>kbd</kbd> + + <q>q</q> + + <samp>samp</samp> + + <var>var</var> + + <ruby>ruby</ruby> + + <rt>rt</rt> + + <rp>rp</rp> + + <abbr>abbr</abbr> + GFM + ) + + verify( + 'SanitizationFilter', + + <<-GFM.strip_heredoc, + ``` + Plain text + ``` + GFM + + <<-GFM.strip_heredoc, + ```ruby + def foo + bar + end + ``` + GFM + + <<-GFM.strip_heredoc + Foo + + This is an example of GFM + + ```js + Code goes here + ``` + GFM + ) + + verify( + 'MarkdownFilter', + + "Line with two spaces at the end \nto insert a linebreak", + + '`code`', + '`` code with ` ticks ``', + + '> Quote', + + # multiline quote + <<-GFM.strip_heredoc, + > Multiline + > Quote + > + > With multiple paragraphs + GFM + + '![Image](https://example.com/image.png)', + + '# Heading with no anchor link', + + '[Link](https://example.com)', + + '- List item', + + # multiline list item + <<-GFM.strip_heredoc, + - Multiline + List item + GFM + + # nested lists + <<-GFM.strip_heredoc, + - Nested + + + - Lists + GFM + + # list with blockquote + <<-GFM.strip_heredoc, + - List + + > Blockquote + GFM + + '1. Numbered list item', + + # multiline numbered list item + <<-GFM.strip_heredoc, + 1. Multiline + Numbered list item + GFM + + # nested numbered list + <<-GFM.strip_heredoc, + 1. Nested + + + 1. Numbered lists + GFM + + '# Heading', + '## Heading', + '### Heading', + '#### Heading', + '##### Heading', + '###### Heading', + + '**Bold**', + + '_Italics_', + + '~~Strikethrough~~', + + '2^2', + + '-----', + + # table + <<-GFM.strip_heredoc, + | Centered | Right | Left | + |:--------:|------:|------| + | Foo | Bar | **Baz** | + | Foo | Bar | **Baz** | + GFM + + # table with empty heading + <<-GFM.strip_heredoc, + | | x | y | + |---|---|---| + | a | 1 | 0 | + | b | 0 | 1 | + GFM + ) + end + + alias_method :gfm_to_html, :markdown + + def html_to_gfm(html) + js = <<-JS.strip_heredoc + (function(html) { + var node = document.createElement('div'); + node.innerHTML = html; + return window.gl.CopyAsGFM.nodeToGFM(node); + })("#{escape_javascript(html)}") + JS + page.evaluate_script(js) + end + + def verify(label, *gfms) + aggregate_failures(label) do + gfms.each do |gfm| + html = gfm_to_html(gfm) + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + end + end + + # Fake a `current_user` helper + def current_user + @feat.user + end +end diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 8771cc8e157..741ca95f1ca 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -68,6 +68,22 @@ describe 'New/edit issue', feature: true, js: true do end end end + + it 'correctly updates the dropdown toggle when removing a label' do + click_button 'Labels' + + page.within '.dropdown-menu-labels' do + click_link label.title + end + + expect(find('.js-label-select')).to have_content(label.title) + + page.within '.dropdown-menu-labels' do + click_link label.title + end + + expect(find('.js-label-select')).to have_content('Labels') + end end context 'edit issue' do diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index db60c01db0d..91f34973ba5 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe BranchesFinder do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } describe '#execute' do diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb index 65d7f14c721..ad2d456529a 100644 --- a/spec/finders/contributed_projects_finder_spec.rb +++ b/spec/finders/contributed_projects_finder_spec.rb @@ -6,8 +6,8 @@ describe ContributedProjectsFinder do let(:finder) { described_class.new(source_user) } - let!(:public_project) { create(:project, :public) } - let!(:private_project) { create(:project, :private) } + let!(:public_project) { create(:empty_project, :public) } + let!(:private_project) { create(:empty_project, :private) } before do private_project.team << [source_user, Gitlab::Access::MASTER] diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb index 00eec3f3f4c..ef97b061ca7 100644 --- a/spec/finders/group_projects_finder_spec.rb +++ b/spec/finders/group_projects_finder_spec.rb @@ -6,11 +6,11 @@ describe GroupProjectsFinder do let(:finder) { described_class.new(source_user) } - let!(:public_project) { create(:project, :public, group: group, path: '1') } - let!(:private_project) { create(:project, :private, group: group, path: '2') } - let!(:shared_project_1) { create(:project, :public, path: '3') } - let!(:shared_project_2) { create(:project, :private, path: '4') } - let!(:shared_project_3) { create(:project, :internal, path: '5') } + let!(:public_project) { create(:empty_project, :public, group: group, path: '1') } + let!(:private_project) { create(:empty_project, :private, group: group, path: '2') } + let!(:shared_project_1) { create(:empty_project, :public, path: '3') } + let!(:shared_project_2) { create(:empty_project, :private, path: '4') } + let!(:shared_project_3) { create(:empty_project, :internal, path: '5') } before do shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group) diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb index 29a47e005a6..4c389746252 100644 --- a/spec/finders/joined_groups_finder_spec.rb +++ b/spec/finders/joined_groups_finder_spec.rb @@ -42,7 +42,7 @@ describe JoinedGroupsFinder do context 'if profile visitor is in one of the private group projects' do before do - project = create(:project, :private, group: private_group, name: 'B', path: 'B') + project = create(:empty_project, :private, group: private_group, name: 'B', path: 'B') project.add_user(profile_visitor, Gitlab::Access::DEVELOPER) end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index e4ba1d2f1c2..3dcd7781e5b 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -4,9 +4,9 @@ describe MergeRequestsFinder do let(:user) { create :user } let(:user2) { create :user } - let(:project1) { create(:project) } - let(:project2) { create(:project, forked_from_project: project1) } - let(:project3) { create(:project, :archived, forked_from_project: project1) } + let(:project1) { create(:empty_project) } + let(:project2) { create(:empty_project, forked_from_project: project1) } + let(:project3) { create(:empty_project, :archived, forked_from_project: project1) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') } diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index 4d21254323c..bac653ea451 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -28,7 +28,7 @@ describe NotesFinder do end it "excludes notes on commits the author can't download" do - project = create(:project, :private) + project = create(:project, :private, :repository) note = create(:note_on_commit, project: project) params = { target_type: 'commit', target_id: note.noteable.id } @@ -76,7 +76,7 @@ describe NotesFinder do end context 'for target' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:note1) { create :note_on_commit, project: project } let(:note2) { create :note_on_commit, project: project } let(:commit) { note1.noteable } diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index a4681fe59d8..e0e17af681a 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -4,14 +4,14 @@ describe PersonalProjectsFinder do let(:source_user) { create(:user) } let(:current_user) { create(:user) } let(:finder) { described_class.new(source_user) } - let!(:public_project) { create(:project, :public, namespace: source_user.namespace) } + let!(:public_project) { create(:empty_project, :public, namespace: source_user.namespace) } let!(:private_project) do - create(:project, :private, namespace: source_user.namespace, path: 'mepmep') + create(:empty_project, :private, namespace: source_user.namespace, path: 'mepmep') end let!(:internal_project) do - create(:project, :internal, namespace: source_user.namespace, path: 'C') + create(:empty_project, :internal, namespace: source_user.namespace, path: 'C') end before do diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb index b0811d134fa..fdc8215aa47 100644 --- a/spec/finders/pipelines_finder_spec.rb +++ b/spec/finders/pipelines_finder_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe PipelinesFinder do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let!(:tag_pipeline) { create(:ci_pipeline, project: project, ref: 'v1.0.0') } let!(:branch_pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 13bda5f7c5a..e44e7434c80 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -6,19 +6,19 @@ describe ProjectsFinder do let(:group) { create(:group, :public) } let!(:private_project) do - create(:project, :private, name: 'A', path: 'A') + create(:empty_project, :private, name: 'A', path: 'A') end let!(:internal_project) do - create(:project, :internal, group: group, name: 'B', path: 'B') + create(:empty_project, :internal, group: group, name: 'B', path: 'B') end let!(:public_project) do - create(:project, :public, group: group, name: 'C', path: 'C') + create(:empty_project, :public, group: group, name: 'C', path: 'C') end let!(:shared_project) do - create(:project, :private, name: 'D', path: 'D') + create(:empty_project, :private, name: 'D', path: 'D') end let(:finder) { described_class.new } diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb index 98b42e264dc..460e278e2d3 100644 --- a/spec/finders/tags_finder_spec.rb +++ b/spec/finders/tags_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe TagsFinder do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } describe '#execute' do diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 92053e5a7c6..8b201f348f1 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -55,7 +55,7 @@ describe ApplicationHelper do describe 'project_icon' do it 'returns an url for the avatar' do - project = create(:project, avatar: File.open(uploaded_image_temp_path)) + project = create(:empty_project, avatar: File.open(uploaded_image_temp_path)) avatar_url = "http://#{Gitlab.config.gitlab.host}/uploads/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s). @@ -63,7 +63,7 @@ describe ApplicationHelper do end it 'gives uploaded icon when present' do - project = create(:project) + project = create(:empty_project) allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true) diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index a43a7238c70..fa516f9903e 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -70,7 +70,7 @@ describe BlobHelper do describe "#edit_blob_link" do let(:namespace) { create(:namespace, name: 'gitlab' )} - let(:project) { create(:project, namespace: namespace) } + let(:project) { create(:project, :repository, namespace: namespace) } before do allow(self).to receive(:current_user).and_return(double) diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 810311e2b1a..b8ec3521edb 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe GitlabMarkdownHelper do include ApplicationHelper - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:user) { create(:user, username: 'gfm') } let(:commit) { project.commit } @@ -55,18 +55,18 @@ describe GitlabMarkdownHelper do end describe '#link_to_gfm' do - let(:commit_path) { namespace_project_commit_path(project.namespace, project, commit) } - let(:issues) { create_list(:issue, 2, project: project) } + let(:link) { '/commits/0a1b2c3d' } + let(:issues) { create_list(:issue, 2, project: project) } it 'handles references nested in links with all the text' do - actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path) + actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", link) doc = Nokogiri::HTML.parse(actual) # Make sure we didn't create invalid markup expect(doc.errors).to be_empty # Leading commit link - expect(doc.css('a')[0].attr('href')).to eq commit_path + expect(doc.css('a')[0].attr('href')).to eq link expect(doc.css('a')[0].text).to eq 'This should finally fix ' # First issue link @@ -75,7 +75,7 @@ describe GitlabMarkdownHelper do expect(doc.css('a')[1].text).to eq issues[0].to_reference # Internal commit link - expect(doc.css('a')[2].attr('href')).to eq commit_path + expect(doc.css('a')[2].attr('href')).to eq link expect(doc.css('a')[2].text).to eq ' and ' # Second issue link @@ -84,12 +84,12 @@ describe GitlabMarkdownHelper do expect(doc.css('a')[3].text).to eq issues[1].to_reference # Trailing commit link - expect(doc.css('a')[4].attr('href')).to eq commit_path + expect(doc.css('a')[4].attr('href')).to eq link expect(doc.css('a')[4].text).to eq ' for real' end it 'forwards HTML options' do - actual = helper.link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo') + actual = helper.link_to_gfm("Fixed in #{commit.id}", link, class: 'foo') doc = Nokogiri::HTML.parse(actual) expect(doc.css('a')).to satisfy do |v| @@ -100,7 +100,7 @@ describe GitlabMarkdownHelper do it "escapes HTML passed in as the body" do actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}" - expect(helper.link_to_gfm(actual, commit_path)). + expect(helper.link_to_gfm(actual, link)). to match('<h1>test</h1>') end diff --git a/spec/helpers/graph_helper_spec.rb b/spec/helpers/graph_helper_spec.rb index 51c49f0e587..400635abdde 100644 --- a/spec/helpers/graph_helper_spec.rb +++ b/spec/helpers/graph_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe GraphHelper do describe '#get_refs' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit("master") } let(:graph) { Network::Graph.new(project, 'master', commit, '') } diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 9c7e0ee2fe0..13fb9c1f1a7 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" describe IssuesHelper do - let(:project) { create :project } + let(:project) { create(:empty_project) } let(:issue) { create :issue, project: project } let(:ext_project) { create :redmine_project } diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb index 33934cdf8b1..2b455571d52 100644 --- a/spec/helpers/members_helper_spec.rb +++ b/spec/helpers/members_helper_spec.rb @@ -46,7 +46,7 @@ describe MembersHelper do end describe '#leave_confirmation_message' do - let(:project) { build_stubbed(:project) } + let(:project) { build_stubbed(:empty_project) } let(:group) { build_stubbed(:group) } let(:user) { build_stubbed(:user) } diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 1f221487393..550b4a66a6a 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe MergeRequestsHelper do describe 'ci_build_details_path' do - let(:project) { create :project } + let(:project) { create(:empty_project) } let(:merge_request) { MergeRequest.new } let(:ci_service) { CiService.new } let(:last_commit) { Ci::Pipeline.new({}) } @@ -30,7 +30,7 @@ describe MergeRequestsHelper do it { is_expected.to eq('#1, #2, and #3') } context 'for JIRA issues' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:issues) do [ ExternalIssue.new('JIRA-123', project), @@ -52,8 +52,8 @@ describe MergeRequestsHelper do end describe 'within different projects' do - let(:project) { create(:project) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:empty_project) } + let(:fork_project) { create(:empty_project, forked_from_project: project) } let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) } subject { format_mr_branch_names(merge_request) } let(:source_title) { "#{fork_project.path_with_namespace}:#{merge_request.source_branch}" } @@ -64,8 +64,8 @@ describe MergeRequestsHelper do end describe 'mr_widget_refresh_url' do + let(:project) { create(:empty_project) } let(:merge_request) { create(:merge_request, source_project: project) } - let(:project) { create(:project) } it 'returns correct url for MR' do expected_url = "#{project.path_with_namespace}/merge_requests/#{merge_request.iid}/merge_widget_refresh" diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb index ea744dbb629..14a95479339 100644 --- a/spec/helpers/milestones_helper_spec.rb +++ b/spec/helpers/milestones_helper_spec.rb @@ -21,24 +21,22 @@ describe MilestonesHelper do end describe '#milestone_counts' do - let(:project) { FactoryGirl.create(:project) } + let(:project) { create(:empty_project) } let(:counts) { helper.milestone_counts(project.milestones) } context 'when there are milestones' do - let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) } - let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) } - let!(:milestone_3) { FactoryGirl.create(:closed_milestone, project: project) } - it 'returns the correct counts' do + create_list(:active_milestone, 2, project: project) + create(:closed_milestone, project: project) + expect(counts).to eq(opened: 2, closed: 1, all: 3) end end context 'when there are only milestones of one type' do - let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) } - let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) } - it 'returns the correct counts' do + create_list(:active_milestone, 2, project: project) + expect(counts).to eq(opened: 2, closed: 0, all: 2) end end diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 77841e85223..1f02e06e312 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -110,7 +110,7 @@ describe PreferencesHelper do end context 'when repository is not empty' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } it 'returns readme if user has repository access' do allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true) diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 8113742923b..8d1570aa6f3 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -10,7 +10,7 @@ describe ProjectsHelper do end describe "can_change_visibility_level?" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:project_member, :reporter, user: create(:user), project: project).user } let(:fork_project) { Projects::ForkService.new(project, user).execute } @@ -97,7 +97,7 @@ describe ProjectsHelper do end describe '#license_short_name' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } context 'when project.repository has a license_key' do it 'returns the nickname of the license if present' do diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 4b2ca3514f8..e51720f10ed 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -42,7 +42,7 @@ describe SearchHelper do end it "includes the user's projects" do - project = create(:project, namespace: create(:namespace, owner: user)) + project = create(:empty_project, namespace: create(:namespace, owner: user)) expect(search_autocomplete_opts(project.name).size).to eq(1) end @@ -52,7 +52,9 @@ describe SearchHelper do end context "with a current project" do - before { @project = create(:project) } + before do + @project = create(:project, :repository) + end it "includes project-specific sections" do expect(search_autocomplete_opts("Files").size).to eq(1) diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 37ac6a2699d..4da1569e59f 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -116,7 +116,7 @@ describe SubmoduleHelper do context 'submodules with relative links' do let(:group) { create(:group, name: "Master Project", path: "master-project") } - let(:project) { create(:project, group: group) } + let(:project) { create(:empty_project, group: group) } let(:commit_id) { sample_commit[:id] } before do @@ -145,7 +145,7 @@ describe SubmoduleHelper do context 'personal project' do let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } it 'one level down with personal project' do result = relative_self_links('../test.git', commit_id) diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index 8d6537ba4b5..9523d0f4aa6 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe TreeHelper do describe 'flatten_tree' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do @repository = project.repository diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index db3ad1b99e9..8942b00b128 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe VisibilityLevelHelper do - let(:project) { build(:project) } + let(:project) { build(:empty_project) } let(:group) { build(:group) } let(:personal_snippet) { build(:personal_snippet) } let(:project_snippet) { build(:project_snippet) } @@ -60,8 +60,8 @@ describe VisibilityLevelHelper do describe "skip_level?" do describe "forks" do - let(:project) { create(:project, :internal) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:empty_project, :internal) } + let(:fork_project) { create(:empty_project, forked_from_project: project) } it "skips levels" do expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy @@ -71,7 +71,7 @@ describe VisibilityLevelHelper do end describe "non-forked project" do - let(:project) { create(:project, :internal) } + let(:project) { create(:empty_project, :internal) } it "skips levels" do expect(skip_level?(project, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js index 755dba19744..386fc8f514e 100644 --- a/spec/javascripts/shortcuts_issuable_spec.js +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -1,6 +1,7 @@ /* eslint-disable space-before-function-paren, no-return-assign, no-var, quotes */ /* global ShortcutsIssuable */ +/*= require copy_as_gfm */ /*= require shortcuts_issuable */ (function() { @@ -14,10 +15,12 @@ }); return describe('#replyWithSelectedText', function() { var stubSelection; - // Stub window.getSelection to return the provided String. - stubSelection = function(text) { - return window.getSelection = function() { - return text; + // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML. + stubSelection = function(html) { + window.gl.utils.getSelectedFragment = function() { + var node = document.createElement('div'); + node.innerHTML = html; + return node; }; }; beforeEach(function() { @@ -32,13 +35,13 @@ }); describe('with any selection', function() { beforeEach(function() { - return stubSelection('Selected text.'); + return stubSelection('<p>Selected text.</p>'); }); it('leaves existing input intact', function() { $(this.selector).val('This text was already here.'); expect($(this.selector).val()).toBe('This text was already here.'); this.shortcut.replyWithSelectedText(); - return expect($(this.selector).val()).toBe("This text was already here.\n> Selected text.\n\n"); + return expect($(this.selector).val()).toBe("This text was already here.\n\n> Selected text.\n\n"); }); it('triggers `input`', function() { var triggered; @@ -61,16 +64,16 @@ }); describe('with a one-line selection', function() { return it('quotes the selection', function() { - stubSelection('This text has been selected.'); + stubSelection('<p>This text has been selected.</p>'); this.shortcut.replyWithSelectedText(); return expect($(this.selector).val()).toBe("> This text has been selected.\n\n"); }); }); return describe('with a multi-line selection', function() { return it('quotes the selected lines as a group', function() { - stubSelection("Selected line one.\n\nSelected line two.\nSelected line three.\n"); + stubSelection("<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>"); this.shortcut.replyWithSelectedText(); - return expect($(this.selector).val()).toBe("> Selected line one.\n> Selected line two.\n> Selected line three.\n\n"); + return expect($(this.selector).val()).toBe("> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n"); }); }); }); diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb index 9703e2315b8..deadc36524c 100644 --- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do include FilterSpecHelper - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:commit1) { project.commit("HEAD~2") } let(:commit2) { project.commit } @@ -99,7 +99,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do end context 'cross-project / cross-namespace complete reference' do - let(:project2) { create(:project, :public) } + let(:project2) { create(:project, :public, :repository) } let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" } it 'links to a valid reference' do @@ -133,8 +133,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do context 'cross-project / same-namespace complete reference' do let(:namespace) { create(:namespace) } - let(:project) { create(:project, :public, namespace: namespace) } - let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) } + let(:project) { create(:project, :public, :repository, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, path: "same-namespace", namespace: namespace) } let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" } it 'links to a valid reference' do @@ -168,8 +168,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do context 'cross-project shorthand reference' do let(:namespace) { create(:namespace) } - let(:project) { create(:project, :public, namespace: namespace) } - let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) } + let(:project) { create(:project, :public, :repository, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, path: "same-namespace", namespace: namespace) } let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" } it 'links to a valid reference' do @@ -203,7 +203,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do context 'cross-project URL reference' do let(:namespace) { create(:namespace) } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:range) { CommitRange.new("#{commit1.id}...master", project) } let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') } diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index 2e6dcc3a434..a19aac61229 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Banzai::Filter::CommitReferenceFilter, lib: true do include FilterSpecHelper - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:commit) { project.commit } it 'requires project context' do @@ -96,7 +96,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do context 'cross-project / cross-namespace complete reference' do let(:namespace) { create(:namespace) } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } @@ -122,7 +122,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do context 'cross-project / same-namespace complete reference' do let(:namespace) { create(:namespace) } let(:project) { create(:empty_project, namespace: namespace) } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } @@ -148,7 +148,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do context 'cross-project shorthand reference' do let(:namespace) { create(:namespace) } let(:project) { create(:empty_project, namespace: namespace) } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } @@ -173,7 +173,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do context 'cross-project URL reference' do let(:namespace) { create(:namespace) } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb index fe2ce092e6b..082c0d4dd0d 100644 --- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb +++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Banzai::Filter::GollumTagsFilter, lib: true do include FilterSpecHelper - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:user) { double } let(:project_wiki) { ProjectWiki.new(project, user) } diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 275010c1a2c..3d3d36061f4 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -188,7 +188,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:project, :public, namespace: namespace) } + let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 73b5edb99b3..a317c751d32 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Banzai::Filter::MilestoneReferenceFilter, lib: true do include FilterSpecHelper - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:milestone) { create(:milestone, project: project) } let(:reference) { milestone.to_reference } diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index df2dd173b57..1957ba739e2 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -25,7 +25,7 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do %(<a href="#{path}">#{path}</a>) end - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:project_path) { project.path_with_namespace } let(:ref) { 'markdown' } let(:commit) { project.commit(ref) } diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb index d265d29ee86..69e3c52b35a 100644 --- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb +++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb @@ -6,21 +6,21 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do context "when no language is specified" do it "highlights as plaintext" do result = filter('<pre><code>def fun end</code></pre>') - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>def fun end</code></pre>') + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code>def fun end</code></pre>') end end context "when a valid language is specified" do it "highlights as that language" do result = filter('<pre><code class="ruby">def fun end</code></pre>') - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>') + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" lang="ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>') end end context "when an invalid language is specified" do it "highlights as plaintext" do result = filter('<pre><code class="gnuplot">This is a test</code></pre>') - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>This is a test</code></pre>') + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code>This is a test</code></pre>') end end @@ -31,7 +31,7 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do it "highlights as plaintext" do result = filter('<pre><code class="ruby">This is a test</code></pre>') - expect(result.to_html).to eq('<pre class="code highlight" v-pre="true"><code>This is a test</code></pre>') + expect(result.to_html).to eq('<pre class="code highlight" lang="" v-pre="true"><code>This is a test</code></pre>') end end end diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 8b76c1d73c9..639cac6406a 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -29,7 +29,7 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do %(<div><a href="#{path}">#{path}</a></div>) end - let(:project) { create(:project) } + let(:project) { create(:empty_project) } shared_examples :preserve_unchanged do it 'does not modify any relative URL in anchor' do diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb index 6ab1be9ccb7..00494f545a3 100644 --- a/spec/lib/banzai/filter/video_link_filter_spec.rb +++ b/spec/lib/banzai/filter/video_link_filter_spec.rb @@ -13,7 +13,7 @@ describe Banzai::Filter::VideoLinkFilter, lib: true do %(<img src="#{path}" />) end - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } context 'when the element src has a video extension' do UploaderHelper::VIDEO_EXT.each do |ext| diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb index fafc2cec546..31ca9d27b0b 100644 --- a/spec/lib/banzai/reference_parser/user_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb @@ -147,7 +147,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do describe '#nodes_user_can_reference' do context 'when the link has a data-author attribute' do it 'returns the nodes when the user is a member of the project' do - other_project = create(:project) + other_project = create(:empty_project) other_project.team << [user, :developer] link['data-project'] = other_project.id.to_s @@ -164,7 +164,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do end it 'returns an empty Array when the user could not be found' do - other_project = create(:project) + other_project = create(:empty_project) link['data-project'] = other_project.id.to_s link['data-author'] = '' @@ -173,7 +173,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do end it 'returns an empty Array when the user is not a team member' do - other_project = create(:project) + other_project = create(:empty_project) link['data-project'] = other_project.id.to_s link['data-author'] = user.id.to_s diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb index 94266f6653b..a5251e9a8c2 100644 --- a/spec/lib/constraints/project_url_constrainer_spec.rb +++ b/spec/lib/constraints/project_url_constrainer_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe ProjectUrlConstrainer, lib: true do - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:namespace) { project.namespace } describe '#matches?' do diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb index ec2f66b1136..e3066311b7d 100644 --- a/spec/lib/event_filter_spec.rb +++ b/spec/lib/event_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe EventFilter, lib: true do describe '#apply_filter' do let(:source_user) { create(:user) } - let!(:public_project) { create(:project, :public) } + let!(:public_project) { create(:empty_project, :public) } let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) } let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) } diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 0e85e302f29..29c07655ae8 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -24,7 +24,7 @@ describe ExtractsPath, lib: true do let(:params) { { path: sample_commit[:line_code_path], ref: ref } } before do - @project = create(:project) + @project = create(:project, :repository) end it "log tree path has no escape sequences" do diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/build/metadata_spec.rb index d678e522721..9df96ea04eb 100644 --- a/spec/lib/gitlab/badge/build/metadata_spec.rb +++ b/spec/lib/gitlab/badge/build/metadata_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'lib/gitlab/badge/shared/metadata' describe Gitlab::Badge::Build::Metadata do - let(:badge) { double(project: create(:project), ref: 'feature') } + let(:badge) { double(project: create(:empty_project), ref: 'feature') } let(:metadata) { described_class.new(badge) } it_behaves_like 'badge metadata' diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb index 70f03021d36..3c5414701a7 100644 --- a/spec/lib/gitlab/badge/build/status_spec.rb +++ b/spec/lib/gitlab/badge/build/status_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Badge::Build::Status do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:sha) { project.commit.sha } let(:branch) { 'master' } let(:badge) { described_class.new(project, branch) } diff --git a/spec/lib/gitlab/badge/coverage/metadata_spec.rb b/spec/lib/gitlab/badge/coverage/metadata_spec.rb index 74eaf7eaf8b..5e93935ea37 100644 --- a/spec/lib/gitlab/badge/coverage/metadata_spec.rb +++ b/spec/lib/gitlab/badge/coverage/metadata_spec.rb @@ -3,7 +3,7 @@ require 'lib/gitlab/badge/shared/metadata' describe Gitlab::Badge::Coverage::Metadata do let(:badge) do - double(project: create(:project), ref: 'feature', job: 'test') + double(project: create(:empty_project), ref: 'feature', job: 'test') end let(:metadata) { described_class.new(badge) } diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 72b1ba36b58..0a2fe5af2c3 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -52,7 +52,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do let(:project) do create( - :project, + :empty_project, import_source: project_identifier, import_data: ProjectImportData.new(credentials: data) ) diff --git a/spec/lib/gitlab/blame_spec.rb b/spec/lib/gitlab/blame_spec.rb index 89245761b6f..26b1baf75be 100644 --- a/spec/lib/gitlab/blame_spec.rb +++ b/spec/lib/gitlab/blame_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Blame, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:path) { 'files/ruby/popen.rb' } let(:commit) { project.commit('master') } let(:blob) { project.repository.blob_at(commit.id, path) } diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb index a2d84977f58..1e81eaef18c 100644 --- a/spec/lib/gitlab/chat_commands/command_spec.rb +++ b/spec/lib/gitlab/chat_commands/command_spec.rb @@ -11,7 +11,7 @@ describe Gitlab::ChatCommands::Command, service: true do context 'when no command is available' do let(:params) { { text: 'issue show 1' } } - let(:project) { create(:project, has_external_issue_tracker: true) } + let(:project) { create(:empty_project, has_external_issue_tracker: true) } it 'displays 404 messages' do expect(subject[:response_type]).to be(:ephemeral) diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index 98effecdbbc..cadfbadca10 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Checks::ChangeAccess, lib: true do describe '#exec' do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user_access) { Gitlab::UserAccess.new(user, project: project) } let(:changes) do { diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index f6288011494..7a84bbebd02 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Checks::ChangeAccess, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } context "exit code checking" do it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do diff --git a/spec/lib/gitlab/ci/trace_reader_spec.rb b/spec/lib/gitlab/ci/trace_reader_spec.rb index f06d78694d6..ff5551bf703 100644 --- a/spec/lib/gitlab/ci/trace_reader_spec.rb +++ b/spec/lib/gitlab/ci/trace_reader_spec.rb @@ -11,13 +11,25 @@ describe Gitlab::Ci::TraceReader do last_lines = random_lines expected = lines.last(last_lines).join + result = subject.read(last_lines: last_lines) - expect(subject.read(last_lines: last_lines)).to eq(expected) + expect(result).to eq(expected) + expect(result.encoding).to eq(Encoding.default_external) end end it 'returns everything if trying to get too many lines' do - expect(build_subject.read(last_lines: lines.size * 2)).to eq(lines.join) + result = build_subject.read(last_lines: lines.size * 2) + + expect(result).to eq(lines.join) + expect(result.encoding).to eq(Encoding.default_external) + end + + it 'returns all contents if last_lines is not specified' do + result = build_subject.read + + expect(result).to eq(lines.join) + expect(result.encoding).to eq(Encoding.default_external) end it 'raises an error if not passing an integer for last_lines' do diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 1bbaca0739a..97af1c2523d 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' describe Gitlab::ClosingIssueExtractor, lib: true do - let(:project) { create(:project) } - let(:project2) { create(:project) } + let(:project) { create(:empty_project) } + let(:project2) { create(:empty_project) } let(:forked_project) { Projects::ForkService.new(project, project.creator).execute } - let(:issue) { create(:issue, project: project) } - let(:issue2) { create(:issue, project: project2) } + let(:issue) { create(:issue, project: project) } + let(:issue2) { create(:issue, project: project2) } let(:reference) { issue.to_reference } let(:cross_reference) { issue2.to_reference(project) } let(:fork_cross_reference) { issue.to_reference(forked_project) } diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index 648d342ecf8..fbf679c5215 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Conflict::File, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:rugged) { repository.rugged } let(:their_commit) { rugged.branches['conflict-start'].target } diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb index fb6b6c4a8d2..3dd76ba5b8a 100644 --- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::CycleAnalytics::StageSummary, models: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:from) { 1.day.ago } let(:user) { create(:user, :admin) } subject { described_class.new(project, from: Time.now, current_user: user).data } @@ -15,7 +15,7 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do end it "doesn't find issues from other projects" do - Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project)) } + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:empty_project)) } expect(subject.first[:value]).to eq(0) end @@ -30,7 +30,7 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do end it "doesn't find commits from other projects" do - Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project), user, 'master') } + Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project, :repository), user, 'master') } expect(subject.second[:value]).to eq(0) end @@ -51,7 +51,9 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do end it "doesn't find commits from other projects" do - Timecop.freeze(5.days.from_now) { create(:deployment, project: create(:project)) } + Timecop.freeze(5.days.from_now) do + create(:deployment, project: create(:project, :repository)) + end expect(subject.third[:value]).to eq(0) end diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb index 9a4dec91e56..04ec34492e1 100644 --- a/spec/lib/gitlab/data_builder/note_spec.rb +++ b/spec/lib/gitlab/data_builder/note_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::DataBuilder::Note, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:data) { described_class.build(note, user) } let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb index a68f5943a6a..f13041e498c 100644 --- a/spec/lib/gitlab/data_builder/pipeline_spec.rb +++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::DataBuilder::Pipeline do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) do create(:ci_pipeline, diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb index a379f798a16..dbcfb9b7400 100644 --- a/spec/lib/gitlab/data_builder/push_spec.rb +++ b/spec/lib/gitlab/data_builder/push_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::DataBuilder::Push, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } describe '.build_sample' do diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 38475792d93..050689b7c9a 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::File, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit(sample_commit.id) } let(:diff) { commit.raw_diffs.first } let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) } diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb index 1c2ddeed692..1e21270d928 100644 --- a/spec/lib/gitlab/diff/highlight_spec.rb +++ b/spec/lib/gitlab/diff/highlight_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::Highlight, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit(sample_commit.id) } let(:diff) { commit.raw_diffs.first } let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) } diff --git a/spec/lib/gitlab/diff/line_mapper_spec.rb b/spec/lib/gitlab/diff/line_mapper_spec.rb index 4b943fa382d..2c7ecd1907e 100644 --- a/spec/lib/gitlab/diff/line_mapper_spec.rb +++ b/spec/lib/gitlab/diff/line_mapper_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::LineMapper, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } let(:diffs) { commit.raw_diffs } diff --git a/spec/lib/gitlab/diff/parallel_diff_spec.rb b/spec/lib/gitlab/diff/parallel_diff_spec.rb index af18d3c25a6..fe5fa048413 100644 --- a/spec/lib/gitlab/diff/parallel_diff_spec.rb +++ b/spec/lib/gitlab/diff/parallel_diff_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::ParallelDiff, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } let(:diffs) { commit.raw_diffs } diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb index 6e8fff6f516..cdf0af6d7ef 100644 --- a/spec/lib/gitlab/diff/position_spec.rb +++ b/spec/lib/gitlab/diff/position_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::Position, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } describe "position for an added file" do let(:commit) { project.commit("2ea1f3dec713d940208fb5ce4a38765ecb5d3f73") } diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb index c268f84c759..f5822fed37c 100644 --- a/spec/lib/gitlab/diff/position_tracer_spec.rb +++ b/spec/lib/gitlab/diff/position_tracer_spec.rb @@ -51,7 +51,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:current_user) { project.owner } let(:repository) { project.repository } let(:file_name) { "test-file" } diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb index 08897a4c310..4a9c9a7fe34 100644 --- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler, lib: true do let(:email_raw) { fixture_file('emails/valid_new_issue.eml') } let(:namespace) { create(:namespace, path: 'gitlabhq') } - let!(:project) { create(:project, :public, namespace: namespace) } + let!(:project) { create(:project, :public, :repository, namespace: namespace) } let!(:user) do create( :user, diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index cebbeff50cf..17a4ef25210 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -11,7 +11,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do end let(:email_raw) { fixture_file('emails/valid_reply.eml') } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:user) { create(:user) } let(:note) { create(:diff_note_on_merge_request, project: project) } let(:noteable) { note.noteable } diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb index a444257754b..0939e6c4514 100644 --- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::Email::Handler::UnsubscribeHandler, lib: true do end let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "#{mail_key}+unsubscribe") } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:user) { create(:user) } let(:noteable) { create(:issue, project: project) } diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb index 5b966bddb6a..7b3291b8315 100644 --- a/spec/lib/gitlab/email/message/repository_push_spec.rb +++ b/spec/lib/gitlab/email/message/repository_push_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Email::Message::RepositoryPush do include RepoHelpers let!(:group) { create(:group, name: 'my_group') } - let!(:project) { create(:project, name: 'my_project', namespace: group) } + let!(:project) { create(:project, :repository, name: 'my_project', namespace: group) } let!(:author) { create(:author, name: 'Author') } let(:message) do diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index f4703dc704f..5d416c9eec3 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe Gitlab::Gfm::ReferenceRewriter do let(:text) { 'some text' } - let(:old_project) { create(:project, name: 'old') } - let(:new_project) { create(:project, name: 'new') } + let(:old_project) { create(:empty_project, name: 'old-project') } + let(:new_project) { create(:empty_project, name: 'new-project') } let(:user) { create(:user) } before { old_project.team << [user, :reporter] } diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index 6eca33f9fee..c3016f63ebf 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe Gitlab::Gfm::UploadsRewriter do let(:user) { create(:user) } - let(:old_project) { create(:project) } - let(:new_project) { create(:project) } + let(:old_project) { create(:empty_project) } + let(:new_project) { create(:empty_project) } let(:rewriter) { described_class.new(text, old_project, user) } context 'text contains links to uploads' do diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index d1f947b6850..3f279c21865 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -3,7 +3,7 @@ require 'fileutils' describe Gitlab::Git::Hook, lib: true do describe "#trigger" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } def create_hook(name) diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 1f9c987be0b..d48629a296d 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Git::RevList, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } context "validations" do described_class::ALLOWED_VARIABLES.each do |var| diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 44b84afde47..b080be62b34 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::GitAccess, lib: true do let(:access) { Gitlab::GitAccess.new(actor, project, 'web', authentication_abilities: authentication_abilities) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:actor) { user } let(:authentication_abilities) do @@ -88,7 +88,7 @@ describe Gitlab::GitAccess, lib: true do end context 'when project is public' do - let(:public_project) { create(:project, :public) } + let(:public_project) { create(:project, :public, :repository) } let(:guest_access) { Gitlab::GitAccess.new(nil, public_project, 'web', authentication_abilities: []) } subject { guest_access.check('git-upload-pack', '_any') } @@ -124,19 +124,19 @@ describe Gitlab::GitAccess, lib: true do context 'when unauthorized' do context 'from public project' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } it { expect(subject).to be_allowed } end context 'from internal project' do - let(:project) { create(:project, :internal) } + let(:project) { create(:project, :internal, :repository) } it { expect(subject).not_to be_allowed } end context 'from private project' do - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, :repository) } it { expect(subject).not_to be_allowed } end @@ -148,7 +148,7 @@ describe Gitlab::GitAccess, lib: true do let(:authentication_abilities) { build_authentication_abilities } describe 'owner' do - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:project, :repository, namespace: user.namespace) } context 'pull code' do it { expect(subject).to be_allowed } @@ -364,19 +364,19 @@ describe Gitlab::GitAccess, lib: true do context 'when unauthorized' do context 'to public project' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } it { expect(subject).not_to be_allowed } end context 'to internal project' do - let(:project) { create(:project, :internal) } + let(:project) { create(:project, :internal, :repository) } it { expect(subject).not_to be_allowed } end context 'to private project' do - let(:project) { create(:project) } + let(:project) { create(:project, :private, :repository) } it { expect(subject).not_to be_allowed } end diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index a5d172233cc..4a0cdc6887e 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::GitAccessWiki, lib: true do let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:authentication_abilities) do [ diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb index 462caa5b5fe..36e7d739f7e 100644 --- a/spec/lib/gitlab/github_import/branch_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::BranchFormatter, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { create(:commit, project: project) } let(:repo) { double } let(:raw) do diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb index c520a9c53ad..e6e33d3686a 100644 --- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::CommentFormatter, lib: true do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index e31ed9c1fa0..eec1fabab54 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::IssueFormatter, lib: true do - let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } + let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) } let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb index 8098754d735..10449ef5fcb 100644 --- a/spec/lib/gitlab/github_import/label_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::LabelFormatter, lib: true do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:raw) { double(name: 'improvements', color: 'e6e6e6') } subject { described_class.new(project, raw) } diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index 2b3256edcb2..90947ff4707 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::PullRequestFormatter, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:source_sha) { create(:commit, project: project).id } let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } let(:repository) { double(id: 1, fork: false) } diff --git a/spec/lib/gitlab/github_import/release_formatter_spec.rb b/spec/lib/gitlab/github_import/release_formatter_spec.rb index 793128c6ab9..13b15e669ab 100644 --- a/spec/lib/gitlab/github_import/release_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/release_formatter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::GithubImport::ReleaseFormatter, lib: true do - let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } + let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) } let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 097861fd34d..ccaa88a5c79 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do 'user_map' => { 'thilo...' => "@#{mapped_user.username}" } } end - let(:project) { create(:project) } + let(:project) { create(:empty_project) } subject { described_class.new(project) } diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb index f5c064303ad..abb5a26060f 100644 --- a/spec/lib/gitlab/graphs/commits_spec.rb +++ b/spec/lib/gitlab/graphs/commits_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Graphs::Commits, lib: true do - let!(:project) { create(:project, :public, :empty_repo) } + let!(:project) { create(:empty_project, :public) } let!(:commit1) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: Time.now) } let!(:commit1_yesterday) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: 1.day.ago)} diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index fc021416d92..fadfe4d378e 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Highlight, lib: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb index d6409a29550..53f7d244d88 100644 --- a/spec/lib/gitlab/import_export/import_export_spec.rb +++ b/spec/lib/gitlab/import_export/import_export_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport, services: true do describe 'export filename' do - let(:project) { create(:project, :public, path: 'project-path') } + let(:project) { create(:empty_project, :public, path: 'project-path') } it 'contains the project path' do expect(described_class.export_filename(project: project)).to include(project.path) diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb index b069696b5c7..0b7984d6ca9 100644 --- a/spec/lib/gitlab/import_export/members_mapper_spec.rb +++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::MembersMapper, services: true do describe 'map members' do let(:user) { create(:admin, authorized_projects_populated: true) } - let(:project) { create(:project, :public, name: 'searchable_project') } + let(:project) { create(:empty_project, :public, name: 'searchable_project') } let(:user2) { create(:user, authorized_projects_populated: true) } let(:exported_user_id) { 99 } let(:exported_members) do diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index c8bba553558..d480c3821ec 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -151,6 +151,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do project = create(:project, :public, + :repository, issues: [issue], snippets: [snippet], releases: [release], diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_bundler_spec.rb index 135e99bc953..d39ea60ff7f 100644 --- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/repo_bundler_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::RepoSaver, services: true do describe 'bundle a project Git repo' do let(:user) { create(:user) } - let!(:project) { create(:project, :public, name: 'searchable_project') } + let!(:project) { create(:empty_project, :public, name: 'searchable_project') } let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:bundler) { described_class.new(project: project, shared: shared) } diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb index b628da0f3e8..47d5d2fc150 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::WikiRepoSaver, services: true do describe 'bundle a wiki Git repo' do let(:user) { create(:user) } - let!(:project) { create(:project, :public, name: 'searchable_project') } + let!(:project) { create(:empty_project, :public, name: 'searchable_project') } let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:wiki_bundler) { described_class.new(project: project, shared: shared) } diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb new file mode 100644 index 00000000000..780f5b1f8d7 --- /dev/null +++ b/spec/lib/gitlab/job_waiter_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Gitlab::JobWaiter do + describe '#wait' do + let(:waiter) { described_class.new(%w(a)) } + it 'returns when all jobs have been completed' do + expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a)). + and_return(true) + + expect(waiter).not_to receive(:sleep) + + waiter.wait + end + + it 'sleeps between checking the job statuses' do + expect(Gitlab::SidekiqStatus).to receive(:all_completed?). + with(%w(a)). + and_return(false, true) + + expect(waiter).to receive(:sleep).with(described_class::INTERVAL) + + waiter.wait + end + + it 'returns when timing out' do + expect(waiter).not_to receive(:sleep) + waiter.wait(0) + end + end +end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index d94eb52f838..92e3624a8d8 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ProjectSearchResults, lib: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:query) { 'hello world' } describe 'initialize with empty ref' do @@ -22,6 +22,7 @@ describe Gitlab::ProjectSearchResults, lib: true do end describe 'blob search' do + let(:project) { create(:project, :repository) } let(:results) { described_class.new(user, project, 'files').objects('blobs') } it 'finds by name' do @@ -74,6 +75,7 @@ describe Gitlab::ProjectSearchResults, lib: true do end describe 'confidential issues' do + let(:project) { create(:empty_project) } let(:query) { 'issue' } let(:author) { create(:user) } let(:assignee) { create(:user) } @@ -187,7 +189,7 @@ describe Gitlab::ProjectSearchResults, lib: true do # shared_examples 'access restricted commits' do context 'when project is internal' do - let(:project) { create(:project, :internal) } + let(:project) { create(:project, :internal, :repository) } it 'does not search if user is not authenticated' do commits = described_class.new(nil, project, search_phrase).objects('commits') @@ -204,7 +206,7 @@ describe Gitlab::ProjectSearchResults, lib: true do context 'when project is private' do let!(:creator) { create(:user, username: 'private-project-author') } - let!(:private_project) { create(:project, :private, creator: creator, namespace: creator.namespace) } + let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) } let(:team_master) do user = create(:user, username: 'private-project-master') private_project.team << [user, :master] @@ -246,7 +248,7 @@ describe Gitlab::ProjectSearchResults, lib: true do describe 'commit search' do context 'by commit message' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:commit) { project.repository.commit('59e29889be61e6e0e5e223bfa9ac2721d31605b8') } let(:message) { 'Sorry, I did a mistake' } @@ -269,7 +271,7 @@ describe Gitlab::ProjectSearchResults, lib: true do end context 'by commit hash' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:commit) { project.repository.commit('0b4bc9a') } commit_hashes = { short: '0b4bc9a', full: '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index bf0ab9635fd..6b689c41ef6 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' describe Gitlab::ReferenceExtractor, lib: true do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } - before { project.team << [project.creator, :developer] } + before do + project.team << [project.creator, :developer] + end subject { Gitlab::ReferenceExtractor.new(project, project.creator) } @@ -78,22 +80,27 @@ describe Gitlab::ReferenceExtractor, lib: true do end it 'accesses valid commits' do + project = create(:project, :repository) { |p| p.add_developer(p.creator) } commit = project.commit('master') - subject.analyze("this references commits #{commit.sha[0..6]} and 012345") - extracted = subject.commits + extractor = described_class.new(project, project.creator) + extractor.analyze("this references commits #{commit.sha[0..6]} and 012345") + extracted = extractor.commits + expect(extracted.size).to eq(1) expect(extracted[0].sha).to eq(commit.sha) expect(extracted[0].message).to eq(commit.message) end it 'accesses valid commit ranges' do + project = create(:project, :repository) { |p| p.add_developer(p.creator) } commit = project.commit('master') earlier_commit = project.commit('master~2') - subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}") + extractor = described_class.new(project, project.creator) + extractor.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}") + extracted = extractor.commit_ranges - extracted = subject.commit_ranges expect(extracted.size).to eq(1) expect(extracted.first).to be_kind_of(CommitRange) expect(extracted.first.commit_from).to eq earlier_commit @@ -102,7 +109,6 @@ describe Gitlab::ReferenceExtractor, lib: true do context 'with an external issue tracker' do let(:project) { create(:jira_project) } - subject { described_class.new(project, project.creator) } it 'returns JIRA issues for a JIRA-integrated project' do subject.analyze('JIRA-123 and FOOBAR-4567') @@ -112,7 +118,7 @@ describe Gitlab::ReferenceExtractor, lib: true do end context 'with a project with an underscore' do - let(:other_project) { create(:project, path: 'test_project') } + let(:other_project) { create(:empty_project, path: 'test_project') } let(:issue) { create(:issue, project: other_project) } before do diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index 9614aad3e73..847fb977400 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::SearchResults do let(:user) { create(:user) } - let!(:project) { create(:project, name: 'foo') } + let!(:project) { create(:empty_project, name: 'foo') } let!(:issue) { create(:issue, project: project, title: 'foo') } let!(:merge_request) do diff --git a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb new file mode 100644 index 00000000000..287bf62d9bd --- /dev/null +++ b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Gitlab::SidekiqStatus::ClientMiddleware do + describe '#call' do + it 'tracks the job in Redis' do + expect(Gitlab::SidekiqStatus).to receive(:set).with('123') + + described_class.new. + call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil } + end + end +end diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb new file mode 100644 index 00000000000..80728197b8c --- /dev/null +++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe Gitlab::SidekiqStatus::ServerMiddleware do + describe '#call' do + it 'stops tracking of a job upon completion' do + expect(Gitlab::SidekiqStatus).to receive(:unset).with('123') + + ret = described_class.new. + call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 } + + expect(ret).to eq(10) + end + end +end diff --git a/spec/lib/gitlab/sidekiq_status_spec.rb b/spec/lib/gitlab/sidekiq_status_spec.rb new file mode 100644 index 00000000000..0aa36a3416b --- /dev/null +++ b/spec/lib/gitlab/sidekiq_status_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::SidekiqStatus do + describe '.set', :redis do + it 'stores the job ID' do + described_class.set('123') + + key = described_class.key_for('123') + + Sidekiq.redis do |redis| + expect(redis.exists(key)).to eq(true) + expect(redis.ttl(key) > 0).to eq(true) + end + end + end + + describe '.unset', :redis do + it 'removes the job ID' do + described_class.set('123') + described_class.unset('123') + + key = described_class.key_for('123') + + Sidekiq.redis do |redis| + expect(redis.exists(key)).to eq(false) + end + end + end + + describe '.all_completed?', :redis do + it 'returns true if all jobs have been completed' do + expect(described_class.all_completed?(%w(123))).to eq(true) + end + + it 'returns false if a job has not yet been completed' do + described_class.set('123') + + expect(described_class.all_completed?(%w(123 456))).to eq(false) + end + end + + describe '.key_for' do + it 'returns the key for a job ID' do + key = described_class.key_for('123') + + expect(key).to be_an_instance_of(String) + expect(key).to include('123') + end + end +end diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb index d2d334e6413..45cec65a284 100644 --- a/spec/lib/gitlab/template/issue_template_spec.rb +++ b/spec/lib/gitlab/template/issue_template_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Template::IssueTemplate do subject { described_class } let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:file_path_1) { '.gitlab/issue_templates/bug.md' } let(:file_path_2) { '.gitlab/issue_templates/template_test.md' } let(:file_path_3) { '.gitlab/issue_templates/feature_proposal.md' } diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb index ddf68c4cf78..ae51b79be22 100644 --- a/spec/lib/gitlab/template/merge_request_template_spec.rb +++ b/spec/lib/gitlab/template/merge_request_template_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Template::MergeRequestTemplate do subject { described_class } let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:file_path_1) { '.gitlab/merge_request_templates/bug.md' } let(:file_path_2) { '.gitlab/merge_request_templates/template_test.md' } let(:file_path_3) { '.gitlab/merge_request_templates/feature_proposal.md' } diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index a826b24419a..3fe8cf43934 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -99,7 +99,7 @@ describe Gitlab::UrlBuilder, lib: true do context 'on another object' do it 'returns a proper URL' do - project = build_stubbed(:project) + project = build_stubbed(:empty_project) expect { described_class.build(project) }. to raise_error(NotImplementedError, 'No URL builder defined for Project') diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 4b1cd466677..7dd4d76d1a3 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Workhorse, lib: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } def decode_workhorse_header(array) diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb index a826b24419a..3fe8cf43934 100644 --- a/spec/lib/light_url_builder_spec.rb +++ b/spec/lib/light_url_builder_spec.rb @@ -99,7 +99,7 @@ describe Gitlab::UrlBuilder, lib: true do context 'on another object' do it 'returns a proper URL' do - project = build_stubbed(:project) + project = build_stubbed(:empty_project) expect { described_class.build(project) }. to raise_error(NotImplementedError, 'No URL builder defined for Project') diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb index f227926f39c..5892f3481a4 100644 --- a/spec/lib/repository_cache_spec.rb +++ b/spec/lib/repository_cache_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe RepositoryCache, lib: true do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:backend) { double('backend').as_null_object } let(:cache) { RepositoryCache.new('example', project.id, backend) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index bfa36d92ac3..61d72925736 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -217,7 +217,7 @@ describe Issue, models: true do end it_behaves_like 'an editable mentionable' do - subject { create(:issue) } + subject { create(:issue, project: create(:project, :repository)) } let(:backref_text) { "issue #{subject.to_reference}" } let(:set_mentionable_text) { ->(txt){ subject.description = txt } } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 1b8ae356f1f..6f9ae655fed 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -138,7 +138,7 @@ describe Note, models: true do it_behaves_like 'an editable mentionable' do subject { create :note, noteable: issue, project: issue.project } - let(:issue) { create :issue } + let(:issue) { create(:issue, project: create(:project, :repository)) } let(:backref_text) { issue.gfm_reference } let(:set_mentionable_text) { ->(txt) { subject.note = txt } } end diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 5abda28e26f..6a5ad6deb74 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -55,6 +55,28 @@ describe 'Git HTTP requests', lib: true do expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) end end + + context 'but the repo is disabled' do + let(:project) { create(:project, repository_access_level: ProjectFeature::DISABLED, wiki_access_level: ProjectFeature::ENABLED) } + let(:wiki) { ProjectWiki.new(project) } + let(:path) { "/#{wiki.repository.path_with_namespace}.git" } + + before do + project.team << [user, :developer] + end + + it 'allows clones' do + download(path, user: user.username, password: user.password) do |response| + expect(response).to have_http_status(200) + end + end + + it 'allows pushes' do + upload(path, user: user.username, password: user.password) do |response| + expect(response).to have_http_status(200) + end + end + end end context "when the project exists" do diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index fef211ded50..db9f1231682 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -12,6 +12,7 @@ describe SystemHooksService, services: true do it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { expect(event_data(project, :update)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) } it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) } @@ -68,6 +69,7 @@ describe SystemHooksService, services: true do it { expect(event_name(project, :destroy)).to eq "project_destroy" } it { expect(event_name(project, :rename)).to eq "project_rename" } it { expect(event_name(project, :transfer)).to eq "project_transfer" } + it { expect(event_name(project, :update)).to eq "project_update" } it { expect(event_name(project_member, :create)).to eq "user_add_to_team" } it { expect(event_name(project_member, :destroy)).to eq "user_remove_from_team" } it { expect(event_name(key, :create)).to eq 'key_create' } diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb new file mode 100644 index 00000000000..b4efe7de431 --- /dev/null +++ b/spec/services/user_project_access_changed_service_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe UserProjectAccessChangedService do + describe '#execute' do + it 'schedules the user IDs' do + expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait). + with([[1], [2]]) + + described_class.new([1, 2]).execute + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f78899134d5..e160c11111b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,7 +7,6 @@ ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'shoulda/matchers' -require 'sidekiq/testing/inline' require 'rspec/retry' if ENV['CI'] && !ENV['NO_KNAPSACK'] diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb new file mode 100644 index 00000000000..575d3451150 --- /dev/null +++ b/spec/support/sidekiq.rb @@ -0,0 +1,5 @@ +require 'sidekiq/testing/inline' + +Sidekiq::Testing.server_middleware do |chain| + chain.add Gitlab::SidekiqStatus::ServerMiddleware +end diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/taskable_shared_examples.rb index 1b6c33248c9..4056ff06b84 100644 --- a/spec/support/taskable_shared_examples.rb +++ b/spec/support/taskable_shared_examples.rb @@ -72,6 +72,25 @@ shared_examples 'a Taskable' do end end + describe 'with tasks that are not formatted correctly' do + before do + subject.description = <<-EOT.strip_heredoc + [ ] task 1 + [ ] task 2 + + - [ ]task 1 + -[ ] task 2 + EOT + end + + it 'returns the correct task status' do + expect(subject.task_status).to match('0 of') + expect(subject.task_status).to match('0 tasks completed') + expect(subject.task_status_short).to match('0/') + expect(subject.task_status_short).to match('0 task') + end + end + describe 'with a complete task' do before do subject.description = <<-EOT.strip_heredoc diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb index b6591f272f6..97c4bfcd248 100644 --- a/spec/workers/authorized_projects_worker_spec.rb +++ b/spec/workers/authorized_projects_worker_spec.rb @@ -3,6 +3,18 @@ require 'spec_helper' describe AuthorizedProjectsWorker do let(:worker) { described_class.new } + describe '.bulk_perform_and_wait' do + it 'schedules the ids and waits for the jobs to complete' do + project = create(:project) + + project.owner.project_authorizations.delete_all + + described_class.bulk_perform_and_wait([[project.owner.id]]) + + expect(project.owner.project_authorizations.count).to eq(1) + end + end + describe '#perform' do it "refreshes user's authorized projects" do user = create(:user) |