diff options
87 files changed, 1357 insertions, 681 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3cbc826e6db..57d94dad672 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -299,10 +299,13 @@ request is as follows: 1. [Generate a changelog entry with `bin/changelog`][changelog] 1. If you are writing documentation, make sure to follow the [documentation styleguide][doc-styleguide] -1. If you have multiple commits please combine them into one commit by - [squashing them][git-squash] +1. If you have multiple commits please combine them into a few logically + organized commits by [squashing them][git-squash] 1. Push the commit(s) to your fork 1. Submit a merge request (MR) to the `master` branch +1. Leave the approvals settings as they are: + 1. Your merge request needs at least 1 approval + 1. You don't have to select any approvers 1. The MR title should describe the change you want to make 1. The MR description should give a motive for your change and the method you used to achieve it. @@ -345,13 +348,31 @@ The ['How to get faster PR reviews' document of Kubernetes](https://github.com/k For examples of feedback on merge requests please look at already [closed merge requests][closed-merge-requests]. If you would like quick feedback -on your merge request feel free to mention one of the Merge Marshalls in the -[core team] or one of the [Merge request coaches](https://about.gitlab.com/team/). +on your merge request feel free to mention someone from the [core team] or one +of the [Merge request coaches][team]. Please ensure that your merge request meets the contribution acceptance criteria. When having your code reviewed and when reviewing merge requests please take the [code review guidelines](doc/development/code_review.md) into account. +### Getting your merge request reviewed, approved, and merged + +There are a few rules to get your merge request accepted: + +1. Your merge request should only be **merged by a [maintainer][team]**. + 1. If your merge request includes only backend changes [^1], it must be + **approved by a [backend maintainer][team]**. + 1. If your merge request includes only frontend changes [^1], it must be + **approved by a [frontend maintainer][team]**. + 1. If your merge request includes frontend and backend changes [^1], it must + be approved by a frontend **and** a backend maintainer. +1. To lower the amount of merge requests maintainers need to review, you can + ask or assign any [reviewers][team] for a first review. + 1. If you need some guidance (e.g. it's your first merge request), feel free + to ask one of the [Merge request coaches][team]. + 1. The reviewer will assign the merge request to a maintainer once the + reviewer is satisfied with the state of the merge request. + ### Contribution acceptance criteria 1. The change is as small as possible @@ -489,6 +510,7 @@ This Code of Conduct is adapted from the [Contributor Covenant][contributor-cove available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). [core team]: https://about.gitlab.com/core-team/ +[team]: https://about.gitlab.com/team/ [getting-help]: https://about.gitlab.com/getting-help/ [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc @@ -513,3 +535,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide" [UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/ [license-finder-doc]: doc/development/licensing.md + +[^1]: Specs other than JavaScript specs are considered backend code. Haml + changes are considered backend code if they include Ruby code other than just + pure HTML. diff --git a/Gemfile.lock b/Gemfile.lock index 62388628eaa..c60c045a4c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ GEM remote: https://rubygems.org/ specs: RedCloth (4.3.2) - ace-rails-ap (4.1.0) + ace-rails-ap (4.1.2) actionmailer (4.2.8) actionpack (= 4.2.8) actionview (= 4.2.8) diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 4667980a960..54836efdf29 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,10 +1,8 @@ /* global Cookies */ -const emojiMap = require('emoji-map'); -const emojiAliases = require('emoji-aliases'); -const glEmoji = require('./behaviors/gl_emoji'); - -const glEmojiTag = glEmoji.glEmojiTag; +import emojiMap from 'emojis/digests.json'; +import emojiAliases from 'emojis/aliases.json'; +import { glEmojiTag } from './behaviors/gl_emoji'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const requestAnimationFrame = window.requestAnimationFrame || @@ -515,4 +513,4 @@ AwardsHandler.prototype.destroy = function destroy() { $('.emoji-menu').remove(); }; -module.exports = AwardsHandler; +export default AwardsHandler; diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js index d1d98c3919f..59741cc9b1a 100644 --- a/app/assets/javascripts/behaviors/gl_emoji.js +++ b/app/assets/javascripts/behaviors/gl_emoji.js @@ -1,11 +1,13 @@ -const installCustomElements = require('document-register-element'); -const emojiMap = require('emoji-map'); -const emojiAliases = require('emoji-aliases'); -const generatedUnicodeSupportMap = require('./gl_emoji/unicode_support_map'); -const spreadString = require('./gl_emoji/spread_string'); +import installCustomElements from 'document-register-element'; +import emojiMap from 'emojis/digests.json'; +import emojiAliases from 'emojis/aliases.json'; +import { getUnicodeSupportMap } from './gl_emoji/unicode_support_map'; +import { isEmojiUnicodeSupported } from './gl_emoji/is_emoji_unicode_supported'; installCustomElements(window); +const generatedUnicodeSupportMap = getUnicodeSupportMap(); + function emojiImageTag(name, src) { return `<img class="emoji" title=":${name}:" alt=":${name}:" src="${src}" width="20" height="20" align="absmiddle" />`; } @@ -55,163 +57,49 @@ function glEmojiTag(inputName, options) { `; } -// On Windows, flags render as two-letter country codes, see http://emojipedia.org/flags/ -const flagACodePoint = 127462; // parseInt('1F1E6', 16) -const flagZCodePoint = 127487; // parseInt('1F1FF', 16) -function isFlagEmoji(emojiUnicode) { - const cp = emojiUnicode.codePointAt(0); - // Length 4 because flags are made of 2 characters which are surrogate pairs - return emojiUnicode.length === 4 && cp >= flagACodePoint && cp <= flagZCodePoint; -} - -// Chrome <57 renders keycaps oddly -// See https://bugs.chromium.org/p/chromium/issues/detail?id=632294 -// Same issue on Windows also fixed in Chrome 57, http://i.imgur.com/rQF7woO.png -function isKeycapEmoji(emojiUnicode) { - return emojiUnicode.length === 3 && emojiUnicode[2] === '\u20E3'; -} - -// Check for a skin tone variation emoji which aren't always supported -const tone1 = 127995;// parseInt('1F3FB', 16) -const tone5 = 127999;// parseInt('1F3FF', 16) -function isSkinToneComboEmoji(emojiUnicode) { - return emojiUnicode.length > 2 && spreadString(emojiUnicode).some((char) => { - const cp = char.codePointAt(0); - return cp >= tone1 && cp <= tone5; - }); -} - -// macOS supports most skin tone emoji's but -// doesn't support the skin tone versions of horse racing -const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16) -function isHorceRacingSkinToneComboEmoji(emojiUnicode) { - return spreadString(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint && - isSkinToneComboEmoji(emojiUnicode); -} - -// Check for `family_*`, `kiss_*`, `couple_*` -// For ex. Windows 8.1 Firefox 51.0.1, doesn't support these -const zwj = 8205; // parseInt('200D', 16) -const personStartCodePoint = 128102; // parseInt('1F466', 16) -const personEndCodePoint = 128105; // parseInt('1F469', 16) -function isPersonZwjEmoji(emojiUnicode) { - let hasPersonEmoji = false; - let hasZwj = false; - spreadString(emojiUnicode).forEach((character) => { - const cp = character.codePointAt(0); - if (cp === zwj) { - hasZwj = true; - } else if (cp >= personStartCodePoint && cp <= personEndCodePoint) { - hasPersonEmoji = true; +function installGlEmojiElement() { + const GlEmojiElementProto = Object.create(HTMLElement.prototype); + GlEmojiElementProto.createdCallback = function createdCallback() { + const emojiUnicode = this.textContent.trim(); + const { + name, + unicodeVersion, + fallbackSrc, + fallbackSpriteClass, + } = this.dataset; + + const isEmojiUnicode = this.childNodes && Array.prototype.every.call( + this.childNodes, + childNode => childNode.nodeType === 3, + ); + const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; + const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; + + if ( + isEmojiUnicode && + !isEmojiUnicodeSupported(generatedUnicodeSupportMap, emojiUnicode, unicodeVersion) + ) { + // CSS sprite fallback takes precedence over image fallback + if (hasCssSpriteFalback) { + // IE 11 doesn't like adding multiple at once :( + this.classList.add('emoji-icon'); + this.classList.add(fallbackSpriteClass); + } else if (hasImageFallback) { + this.innerHTML = emojiImageTag(name, fallbackSrc); + } else { + const src = assembleFallbackImageSrc(name); + this.innerHTML = emojiImageTag(name, src); + } } - }); - - return hasPersonEmoji && hasZwj; -} - -// Helper so we don't have to run `isFlagEmoji` twice -// in `isEmojiUnicodeSupported` logic -function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) { - const isFlagResult = isFlagEmoji(emojiUnicode); - return ( - (unicodeSupportMap.flag && isFlagResult) || - !isFlagResult - ); -} - -// Helper so we don't have to run `isSkinToneComboEmoji` twice -// in `isEmojiUnicodeSupported` logic -function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) { - const isSkinToneResult = isSkinToneComboEmoji(emojiUnicode); - return ( - (unicodeSupportMap.skinToneModifier && isSkinToneResult) || - !isSkinToneResult - ); -} - -// Helper func so we don't have to run `isHorceRacingSkinToneComboEmoji` twice -// in `isEmojiUnicodeSupported` logic -function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) { - const isHorseRacingSkinToneResult = isHorceRacingSkinToneComboEmoji(emojiUnicode); - return ( - (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || - !isHorseRacingSkinToneResult - ); -} - -// Helper so we don't have to run `isPersonZwjEmoji` twice -// in `isEmojiUnicodeSupported` logic -function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) { - const isPersonZwjResult = isPersonZwjEmoji(emojiUnicode); - return ( - (unicodeSupportMap.personZwj && isPersonZwjResult) || - !isPersonZwjResult - ); -} - -// Takes in a support map and determines whether -// the given unicode emoji is supported on the platform. -// -// Combines all the edge case tests into a one-stop shop method -function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVersion) { - const isOlderThanChrome57 = unicodeSupportMap.meta && unicodeSupportMap.meta.isChrome && - unicodeSupportMap.meta.chromeVersion < 57; + }; - // For comments about each scenario, see the comments above each individual respective function - return unicodeSupportMap[unicodeVersion] && - !(isOlderThanChrome57 && isKeycapEmoji(emojiUnicode)) && - checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) && - checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) && - checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) && - checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode); + document.registerElement('gl-emoji', { + prototype: GlEmojiElementProto, + }); } -const GlEmojiElementProto = Object.create(HTMLElement.prototype); -GlEmojiElementProto.createdCallback = function createdCallback() { - const emojiUnicode = this.textContent.trim(); - const { - name, - unicodeVersion, - fallbackSrc, - fallbackSpriteClass, - } = this.dataset; - - const isEmojiUnicode = this.childNodes && Array.prototype.every.call( - this.childNodes, - childNode => childNode.nodeType === 3, - ); - const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; - const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; - - if ( - isEmojiUnicode && - !isEmojiUnicodeSupported(generatedUnicodeSupportMap, emojiUnicode, unicodeVersion) - ) { - // CSS sprite fallback takes precedence over image fallback - if (hasCssSpriteFalback) { - // IE 11 doesn't like adding multiple at once :( - this.classList.add('emoji-icon'); - this.classList.add(fallbackSpriteClass); - } else if (hasImageFallback) { - this.innerHTML = emojiImageTag(name, fallbackSrc); - } else { - const src = assembleFallbackImageSrc(name); - this.innerHTML = emojiImageTag(name, src); - } - } -}; - -document.registerElement('gl-emoji', { - prototype: GlEmojiElementProto, -}); - -module.exports = { - emojiImageTag, +export { + installGlEmojiElement, glEmojiTag, - isEmojiUnicodeSupported, - isFlagEmoji, - isKeycapEmoji, - isSkinToneComboEmoji, - isHorceRacingSkinToneComboEmoji, - isPersonZwjEmoji, + emojiImageTag, }; diff --git a/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js b/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js new file mode 100644 index 00000000000..5e3c45f7e92 --- /dev/null +++ b/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js @@ -0,0 +1,121 @@ +import spreadString from './spread_string'; + +// On Windows, flags render as two-letter country codes, see http://emojipedia.org/flags/ +const flagACodePoint = 127462; // parseInt('1F1E6', 16) +const flagZCodePoint = 127487; // parseInt('1F1FF', 16) +function isFlagEmoji(emojiUnicode) { + const cp = emojiUnicode.codePointAt(0); + // Length 4 because flags are made of 2 characters which are surrogate pairs + return emojiUnicode.length === 4 && cp >= flagACodePoint && cp <= flagZCodePoint; +} + +// Chrome <57 renders keycaps oddly +// See https://bugs.chromium.org/p/chromium/issues/detail?id=632294 +// Same issue on Windows also fixed in Chrome 57, http://i.imgur.com/rQF7woO.png +function isKeycapEmoji(emojiUnicode) { + return emojiUnicode.length === 3 && emojiUnicode[2] === '\u20E3'; +} + +// Check for a skin tone variation emoji which aren't always supported +const tone1 = 127995;// parseInt('1F3FB', 16) +const tone5 = 127999;// parseInt('1F3FF', 16) +function isSkinToneComboEmoji(emojiUnicode) { + return emojiUnicode.length > 2 && spreadString(emojiUnicode).some((char) => { + const cp = char.codePointAt(0); + return cp >= tone1 && cp <= tone5; + }); +} + +// macOS supports most skin tone emoji's but +// doesn't support the skin tone versions of horse racing +const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16) +function isHorceRacingSkinToneComboEmoji(emojiUnicode) { + return spreadString(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint && + isSkinToneComboEmoji(emojiUnicode); +} + +// Check for `family_*`, `kiss_*`, `couple_*` +// For ex. Windows 8.1 Firefox 51.0.1, doesn't support these +const zwj = 8205; // parseInt('200D', 16) +const personStartCodePoint = 128102; // parseInt('1F466', 16) +const personEndCodePoint = 128105; // parseInt('1F469', 16) +function isPersonZwjEmoji(emojiUnicode) { + let hasPersonEmoji = false; + let hasZwj = false; + spreadString(emojiUnicode).forEach((character) => { + const cp = character.codePointAt(0); + if (cp === zwj) { + hasZwj = true; + } else if (cp >= personStartCodePoint && cp <= personEndCodePoint) { + hasPersonEmoji = true; + } + }); + + return hasPersonEmoji && hasZwj; +} + +// Helper so we don't have to run `isFlagEmoji` twice +// in `isEmojiUnicodeSupported` logic +function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) { + const isFlagResult = isFlagEmoji(emojiUnicode); + return ( + (unicodeSupportMap.flag && isFlagResult) || + !isFlagResult + ); +} + +// Helper so we don't have to run `isSkinToneComboEmoji` twice +// in `isEmojiUnicodeSupported` logic +function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) { + const isSkinToneResult = isSkinToneComboEmoji(emojiUnicode); + return ( + (unicodeSupportMap.skinToneModifier && isSkinToneResult) || + !isSkinToneResult + ); +} + +// Helper func so we don't have to run `isHorceRacingSkinToneComboEmoji` twice +// in `isEmojiUnicodeSupported` logic +function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) { + const isHorseRacingSkinToneResult = isHorceRacingSkinToneComboEmoji(emojiUnicode); + return ( + (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || + !isHorseRacingSkinToneResult + ); +} + +// Helper so we don't have to run `isPersonZwjEmoji` twice +// in `isEmojiUnicodeSupported` logic +function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) { + const isPersonZwjResult = isPersonZwjEmoji(emojiUnicode); + return ( + (unicodeSupportMap.personZwj && isPersonZwjResult) || + !isPersonZwjResult + ); +} + +// Takes in a support map and determines whether +// the given unicode emoji is supported on the platform. +// +// Combines all the edge case tests into a one-stop shop method +function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVersion) { + const isOlderThanChrome57 = unicodeSupportMap.meta && unicodeSupportMap.meta.isChrome && + unicodeSupportMap.meta.chromeVersion < 57; + + // For comments about each scenario, see the comments above each individual respective function + return unicodeSupportMap[unicodeVersion] && + !(isOlderThanChrome57 && isKeycapEmoji(emojiUnicode)) && + checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) && + checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) && + checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) && + checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode); +} + +export { + isEmojiUnicodeSupported, + isFlagEmoji, + isKeycapEmoji, + isSkinToneComboEmoji, + isHorceRacingSkinToneComboEmoji, + isPersonZwjEmoji, +}; diff --git a/app/assets/javascripts/behaviors/gl_emoji/spread_string.js b/app/assets/javascripts/behaviors/gl_emoji/spread_string.js index 2380349c4fa..327764ec6e9 100644 --- a/app/assets/javascripts/behaviors/gl_emoji/spread_string.js +++ b/app/assets/javascripts/behaviors/gl_emoji/spread_string.js @@ -47,4 +47,4 @@ function spreadString(str) { return arr; } -module.exports = spreadString; +export default spreadString; diff --git a/app/assets/javascripts/behaviors/gl_emoji/unicode_support_map.js b/app/assets/javascripts/behaviors/gl_emoji/unicode_support_map.js index f31716d4c07..aa522e20c36 100644 --- a/app/assets/javascripts/behaviors/gl_emoji/unicode_support_map.js +++ b/app/assets/javascripts/behaviors/gl_emoji/unicode_support_map.js @@ -68,7 +68,7 @@ const chromeVersion = chromeMatches && chromeMatches[1] && parseInt(chromeMatche // See 32px, https://i.imgur.com/htY6Zym.png // See 16px, https://i.imgur.com/FPPsIF8.png const fontSize = 16; -function testUnicodeSupportMap(testMap) { +function generateUnicodeSupportMap(testMap) { const testMapKeys = Object.keys(testMap); const numTestEntries = testMapKeys .reduce((list, testKey) => list.concat(testMap[testKey]), []).length; @@ -138,17 +138,24 @@ function testUnicodeSupportMap(testMap) { return resultMap; } -let unicodeSupportMap; -const userAgentFromCache = window.localStorage.getItem('gl-emoji-user-agent'); -try { - unicodeSupportMap = JSON.parse(window.localStorage.getItem('gl-emoji-unicode-support-map')); -} catch (err) { - // swallow -} -if (!unicodeSupportMap || userAgentFromCache !== navigator.userAgent) { - unicodeSupportMap = testUnicodeSupportMap(unicodeSupportTestMap); - window.localStorage.setItem('gl-emoji-user-agent', navigator.userAgent); - window.localStorage.setItem('gl-emoji-unicode-support-map', JSON.stringify(unicodeSupportMap)); +function getUnicodeSupportMap() { + let unicodeSupportMap; + const userAgentFromCache = window.localStorage.getItem('gl-emoji-user-agent'); + try { + unicodeSupportMap = JSON.parse(window.localStorage.getItem('gl-emoji-unicode-support-map')); + } catch (err) { + // swallow + } + if (!unicodeSupportMap || userAgentFromCache !== navigator.userAgent) { + unicodeSupportMap = generateUnicodeSupportMap(unicodeSupportTestMap); + window.localStorage.setItem('gl-emoji-user-agent', navigator.userAgent); + window.localStorage.setItem('gl-emoji-unicode-support-map', JSON.stringify(unicodeSupportMap)); + } + + return unicodeSupportMap; } -module.exports = unicodeSupportMap; +export { + getUnicodeSupportMap, + generateUnicodeSupportMap, +}; diff --git a/app/assets/javascripts/extensions/string.js b/app/assets/javascripts/extensions/string.js index fe23be0bbc1..ae9662444b0 100644 --- a/app/assets/javascripts/extensions/string.js +++ b/app/assets/javascripts/extensions/string.js @@ -1,2 +1,2 @@ -require('string.prototype.codepointat'); -require('string.fromcodepoint'); +import 'string.prototype.codepointat'; +import 'string.fromcodepoint'; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 1bc04a5ad96..4f7ce1fa197 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -1,10 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, no-template-curly-in-string, comma-dangle, object-shorthand, quotes, dot-notation, no-else-return, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-param-reassign, no-useless-escape, prefer-template, consistent-return, wrap-iife, prefer-arrow-callback, camelcase, no-unused-vars, no-useless-return, vars-on-top, max-len */ -const emojiMap = require('emoji-map'); -const emojiAliases = require('emoji-aliases'); -const glEmoji = require('./behaviors/gl_emoji'); - -const glEmojiTag = glEmoji.glEmojiTag; +import emojiMap from 'emojis/digests.json'; +import emojiAliases from 'emojis/aliases.json'; +import { glEmojiTag } from '~/behaviors/gl_emoji'; // Creates the variables for setting up GFM auto-completion (function() { diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 1bc81d2e4a4..09c4261b318 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -66,6 +66,13 @@ return results; })()).join('&'); }; + w.gl.utils.removeParams = (params) => { + const url = new URL(window.location.href); + params.forEach((param) => { + url.search = w.gl.utils.removeParamQueryString(url.search, param); + }); + return url.href; + }; w.gl.utils.getLocationHash = function(url) { var hashIndex; if (typeof url === 'undefined') { diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 79164edff0e..689a6c3a93a 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import */ +/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */ /* global bp */ /* global Cookies */ /* global Flash */ @@ -13,19 +13,20 @@ import Dropzone from 'dropzone'; import Sortable from 'vendor/Sortable'; // libraries with import side-effects -require('mousetrap'); -require('mousetrap/plugins/pause/mousetrap-pause'); -require('vendor/fuzzaldrin-plus'); -require('es6-promise').polyfill(); +import 'mousetrap'; +import 'mousetrap/plugins/pause/mousetrap-pause'; +import 'vendor/fuzzaldrin-plus'; +import promisePolyfill from 'es6-promise'; // extensions -require('./extensions/string'); -require('./extensions/array'); -require('./extensions/custom_event'); -require('./extensions/element'); -require('./extensions/jquery'); -require('./extensions/object'); -require('es6-promise').polyfill(); +import './extensions/string'; +import './extensions/array'; +import './extensions/custom_event'; +import './extensions/element'; +import './extensions/jquery'; +import './extensions/object'; + +promisePolyfill.polyfill(); // expose common libraries as globals (TODO: remove these) window.jQuery = jQuery; @@ -37,174 +38,171 @@ window.Dropzone = Dropzone; window.Sortable = Sortable; // shortcuts -require('./shortcuts'); -require('./shortcuts_navigation'); -require('./shortcuts_dashboard_navigation'); -require('./shortcuts_issuable'); -require('./shortcuts_network'); +import './shortcuts'; +import './shortcuts_blob'; +import './shortcuts_dashboard_navigation'; +import './shortcuts_navigation'; +import './shortcuts_find_file'; +import './shortcuts_issuable'; +import './shortcuts_network'; // behaviors -require('./behaviors/autosize'); -require('./behaviors/details_behavior'); -require('./behaviors/quick_submit'); -require('./behaviors/requires_input'); -require('./behaviors/toggler_behavior'); -require('./behaviors/bind_in_out'); +import './behaviors/autosize'; +import './behaviors/details_behavior'; +import './behaviors/quick_submit'; +import './behaviors/requires_input'; +import './behaviors/toggler_behavior'; +import './behaviors/bind_in_out'; +import { installGlEmojiElement } from './behaviors/gl_emoji'; +installGlEmojiElement(); // blob -require('./blob/blob_ci_yaml'); -require('./blob/blob_dockerfile_selector'); -require('./blob/blob_dockerfile_selectors'); -require('./blob/blob_file_dropzone'); -require('./blob/blob_gitignore_selector'); -require('./blob/blob_gitignore_selectors'); -require('./blob/blob_license_selector'); -require('./blob/blob_license_selectors'); -require('./blob/template_selector'); +import './blob/blob_ci_yaml'; +import './blob/blob_dockerfile_selector'; +import './blob/blob_dockerfile_selectors'; +import './blob/blob_file_dropzone'; +import './blob/blob_gitignore_selector'; +import './blob/blob_gitignore_selectors'; +import './blob/blob_license_selector'; +import './blob/blob_license_selectors'; +import './blob/template_selector'; // templates -require('./templates/issuable_template_selector'); -require('./templates/issuable_template_selectors'); +import './templates/issuable_template_selector'; +import './templates/issuable_template_selectors'; // commit -require('./commit/file.js'); -require('./commit/image_file.js'); +import './commit/file'; +import './commit/image_file'; // lib/utils -require('./lib/utils/animate'); -require('./lib/utils/bootstrap_linked_tabs'); -require('./lib/utils/common_utils'); -require('./lib/utils/datetime_utility'); -require('./lib/utils/notify'); -require('./lib/utils/pretty_time'); -require('./lib/utils/text_utility'); -require('./lib/utils/type_utility'); -require('./lib/utils/url_utility'); +import './lib/utils/animate'; +import './lib/utils/bootstrap_linked_tabs'; +import './lib/utils/common_utils'; +import './lib/utils/datetime_utility'; +import './lib/utils/notify'; +import './lib/utils/pretty_time'; +import './lib/utils/text_utility'; +import './lib/utils/type_utility'; +import './lib/utils/url_utility'; // u2f -require('./u2f/authenticate'); -require('./u2f/error'); -require('./u2f/register'); -require('./u2f/util'); +import './u2f/authenticate'; +import './u2f/error'; +import './u2f/register'; +import './u2f/util'; // droplab -require('./droplab/droplab'); -require('./droplab/droplab_ajax'); -require('./droplab/droplab_ajax_filter'); -require('./droplab/droplab_filter'); +import './droplab/droplab'; +import './droplab/droplab_ajax'; +import './droplab/droplab_ajax_filter'; +import './droplab/droplab_filter'; // everything else -require('./abuse_reports'); -require('./activities'); -require('./admin'); -require('./ajax_loading_spinner'); -require('./api'); -require('./aside'); -require('./autosave'); -const AwardsHandler = require('./awards_handler'); -require('./breakpoints'); -require('./broadcast_message'); -require('./build'); -require('./build_artifacts'); -require('./build_variables'); -require('./ci_lint_editor'); -require('./commit'); -require('./commits'); -require('./compare'); -require('./compare_autocomplete'); -require('./confirm_danger_modal'); -require('./copy_as_gfm'); -require('./copy_to_clipboard'); -require('./create_label'); -require('./diff'); -require('./dispatcher'); -require('./dropzone_input'); -require('./due_date_select'); -require('./files_comment_button'); -require('./flash'); -require('./gfm_auto_complete'); -require('./gl_dropdown'); -require('./gl_field_error'); -require('./gl_field_errors'); -require('./gl_form'); -require('./group_avatar'); -require('./group_label_subscription'); -require('./groups_select'); -require('./header'); -require('./importer_status'); -require('./issuable'); -require('./issuable_context'); -require('./issuable_form'); -require('./issue'); -require('./issue_status_select'); -require('./issues_bulk_assignment'); -require('./label_manager'); -require('./labels'); -require('./labels_select'); -require('./layout_nav'); -require('./line_highlighter'); -require('./logo'); -require('./member_expiration_date'); -require('./members'); -require('./merge_request'); -require('./merge_request_tabs'); -require('./merge_request_widget'); -require('./merged_buttons'); -require('./milestone'); -require('./milestone_select'); -require('./mini_pipeline_graph_dropdown'); -require('./namespace_select'); -require('./new_branch_form'); -require('./new_commit_form'); -require('./notes'); -require('./notifications_dropdown'); -require('./notifications_form'); -require('./pager'); -require('./pipelines'); -require('./preview_markdown'); -require('./project'); -require('./project_avatar'); -require('./project_find_file'); -require('./project_fork'); -require('./project_import'); -require('./project_label_subscription'); -require('./project_new'); -require('./project_select'); -require('./project_show'); -require('./project_variables'); -require('./projects_list'); -require('./render_gfm'); -require('./render_math'); -require('./right_sidebar'); -require('./search'); -require('./search_autocomplete'); -require('./shortcuts'); -require('./shortcuts_blob'); -require('./shortcuts_dashboard_navigation'); -require('./shortcuts_find_file'); -require('./shortcuts_issuable'); -require('./shortcuts_navigation'); -require('./shortcuts_network'); -require('./signin_tabs_memoizer'); -require('./single_file_diff'); -require('./smart_interval'); -require('./snippets_list'); -require('./star'); -require('./subbable_resource'); -require('./subscription'); -require('./subscription_select'); -require('./syntax_highlight'); -require('./task_list'); -require('./todos'); -require('./tree'); -require('./user'); -require('./user_tabs'); -require('./username_validator'); -require('./users_select'); -require('./version_check_image'); -require('./visibility_select'); -require('./wikis'); -require('./zen_mode'); +import './abuse_reports'; +import './activities'; +import './admin'; +import './ajax_loading_spinner'; +import './api'; +import './aside'; +import './autosave'; +import AwardsHandler from './awards_handler'; +import './breakpoints'; +import './broadcast_message'; +import './build'; +import './build_artifacts'; +import './build_variables'; +import './ci_lint_editor'; +import './commit'; +import './commits'; +import './compare'; +import './compare_autocomplete'; +import './confirm_danger_modal'; +import './copy_as_gfm'; +import './copy_to_clipboard'; +import './create_label'; +import './diff'; +import './dispatcher'; +import './dropzone_input'; +import './due_date_select'; +import './files_comment_button'; +import './flash'; +import './gfm_auto_complete'; +import './gl_dropdown'; +import './gl_field_error'; +import './gl_field_errors'; +import './gl_form'; +import './group_avatar'; +import './group_label_subscription'; +import './groups_select'; +import './header'; +import './importer_status'; +import './issuable'; +import './issuable_context'; +import './issuable_form'; +import './issue'; +import './issue_status_select'; +import './issues_bulk_assignment'; +import './label_manager'; +import './labels'; +import './labels_select'; +import './layout_nav'; +import './line_highlighter'; +import './logo'; +import './member_expiration_date'; +import './members'; +import './merge_request'; +import './merge_request_tabs'; +import './merge_request_widget'; +import './merged_buttons'; +import './milestone'; +import './milestone_select'; +import './mini_pipeline_graph_dropdown'; +import './namespace_select'; +import './new_branch_form'; +import './new_commit_form'; +import './notes'; +import './notifications_dropdown'; +import './notifications_form'; +import './pager'; +import './pipelines'; +import './preview_markdown'; +import './project'; +import './project_avatar'; +import './project_find_file'; +import './project_fork'; +import './project_import'; +import './project_label_subscription'; +import './project_new'; +import './project_select'; +import './project_show'; +import './project_variables'; +import './projects_list'; +import './render_gfm'; +import './render_math'; +import './right_sidebar'; +import './search'; +import './search_autocomplete'; +import './signin_tabs_memoizer'; +import './single_file_diff'; +import './smart_interval'; +import './snippets_list'; +import './star'; +import './subbable_resource'; +import './subscription'; +import './subscription_select'; +import './syntax_highlight'; +import './task_list'; +import './todos'; +import './tree'; +import './user'; +import './user_tabs'; +import './username_validator'; +import './users_select'; +import './version_check_image'; +import './visibility_select'; +import './wikis'; +import './zen_mode'; (function () { document.addEventListener('beforeunload', function () { diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index eeab69da941..47cc34e7a20 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1,12 +1,14 @@ /* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */ /* global Flash */ /* global Autosave */ +/* global Cookies */ /* global ResolveService */ /* global mrRefreshWidgetUrl */ require('./autosave'); window.autosize = require('vendor/autosize'); window.Dropzone = require('dropzone'); +window.Cookies = require('js-cookie'); require('./dropzone_input'); require('./gfm_auto_complete'); require('vendor/jquery.caret'); // required by jquery.atwho @@ -42,7 +44,6 @@ require('./task_list'); this.notes_url = notes_url; this.note_ids = note_ids; this.last_fetched_at = last_fetched_at; - this.view = view; this.noteable_url = document.URL; this.notesCountBadge || (this.notesCountBadge = $(".issuable-details").find(".notes-tab .badge")); this.basePollingInterval = 15000; @@ -57,6 +58,7 @@ require('./task_list'); selector: '.notes' }); this.collapseLongCommitList(); + this.setViewType(view); // We are in the Merge Requests page so we need another edit form for Changes tab if (gl.utils.getPagePath(1) === 'merge_requests') { @@ -65,6 +67,10 @@ require('./task_list'); } } + Notes.prototype.setViewType = function(view) { + this.view = Cookies.get('diff_view') || view; + }; + Notes.prototype.addBinding = function() { // add note to UI after creation $(document).on("ajax:success", ".js-main-target-form", this.addNote); @@ -302,7 +308,7 @@ require('./task_list'); }; Notes.prototype.isParallelView = function() { - return this.view === 'parallel'; + return Cookies.get('diff_view') === 'parallel'; }; /* diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index e35cf6d295e..5f6bc902cf8 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,11 +1,15 @@ +require('~/lib/utils/common_utils'); +require('~/lib/utils/url_utility'); + (() => { const ENDLESS_SCROLL_BOTTOM_PX = 400; const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; const Pager = { init(limit = 0, preload = false, disable = false, callback = $.noop) { + this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']); this.limit = limit; - this.offset = this.limit; + this.offset = parseInt(gl.utils.getParameterByName('offset'), 10) || this.limit; this.disable = disable; this.callback = callback; this.loading = $('.loading').first(); @@ -20,7 +24,7 @@ this.loading.show(); $.ajax({ type: 'GET', - url: $('.content_list').data('href') || window.location.href, + url: this.url, data: `limit=${this.limit}&offset=${this.offset}`, dataType: 'json', error: () => this.loading.hide(), diff --git a/app/controllers/projects/settings/members_controller.rb b/app/controllers/projects/settings/members_controller.rb index 5735e281f66..cbfa2afa959 100644 --- a/app/controllers/projects/settings/members_controller.rb +++ b/app/controllers/projects/settings/members_controller.rb @@ -7,47 +7,18 @@ module Projects @sort = params[:sort].presence || sort_value_name @group_links = @project.project_group_links - @project_members = @project.project_members - @project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project) - - group = @project.group - - # group links - @group_links = @project.project_group_links.all - @skip_groups = @group_links.pluck(:group_id) @skip_groups << @project.namespace_id unless @project.personal? - if group - # We need `.where.not(user_id: nil)` here otherwise when a group has an - # invitee, it would make the following query return 0 rows since a NULL - # user_id would be present in the subquery - # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values - group_members = MembersFinder.new(@project_members, group).execute(current_user) - end + @project_members = MembersFinder.new(@project, current_user).execute if params[:search].present? - user_ids = @project.users.search(params[:search]).select(:id) - @project_members = @project_members.where(user_id: user_ids) - - if group_members - user_ids = group.users.search(params[:search]).select(:id) - group_members = group_members.where(user_id: user_ids) - end - - @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) + @project_members = @project_members.joins(:user).merge(User.search(params[:search])) + @group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - wheres = ["members.id IN (#{@project_members.select(:id).to_sql})"] - wheres << "members.id IN (#{group_members.select(:id).to_sql})" if group_members - - @project_members = Member. - where(wheres.join(' OR ')). - sort(@sort). - page(params[:page]) - + @project_members = @project_members.sort(@sort).page(params[:page]) @requesters = AccessRequestsFinder.new(@project).execute(current_user) - @project_member = @project.project_members.new end end diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index 9f2206346ce..fce3775f40e 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -1,4 +1,4 @@ -class GroupMembersFinder < Projects::ApplicationController +class GroupMembersFinder def initialize(group) @group = group end diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 702944404f5..af24045886e 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -1,13 +1,35 @@ -class MembersFinder < Projects::ApplicationController - def initialize(project_members, project_group) - @project_members = project_members - @project_group = project_group +class MembersFinder + attr_reader :project, :current_user, :group + + def initialize(project, current_user) + @project = project + @current_user = current_user + @group = project.group + end + + def execute + project_members = project.project_members + project_members = project_members.non_invite unless can?(current_user, :admin_project, project) + wheres = ["members.id IN (#{project_members.select(:id).to_sql})"] + + if group + # We need `.where.not(user_id: nil)` here otherwise when a group has an + # invitee, it would make the following query return 0 rows since a NULL + # user_id would be present in the subquery + # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values + non_null_user_ids = project_members.where.not(user_id: nil).select(:user_id) + + group_members = GroupMembersFinder.new(group).execute + group_members = group_members.where.not(user_id: non_null_user_ids) + group_members = group_members.non_invite unless can?(current_user, :admin_group, group) + + wheres << "members.id IN (#{group_members.select(:id).to_sql})" + end + + Member.where(wheres.join(' OR ')) end - def execute(current_user) - non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id) - group_members = @project_group.group_members.where.not(user_id: non_null_user_ids) - group_members = group_members.non_invite unless can?(current_user, :admin_group, @project_group) - group_members + def can?(*args) + Ability.allowed?(*args) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 2c2c408b035..a7cdca9ba2e 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -15,6 +15,8 @@ module CiStatusHelper 'passed' when 'success_with_warnings' 'passed with warnings' + when 'manual' + 'waiting for manual action' else status end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 710218082f2..243ef39ef61 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -36,7 +36,7 @@ module PreferencesHelper def project_view_choices [ ['Files and Readme (default)', :files], - ['Activity view', :activity] + ['Activity', :activity] ] end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 37c727b5d9f..3cf4c67d7e7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -63,6 +63,7 @@ module Issuable scope :authored, ->(user) { where(author_id: user) } scope :assigned_to, ->(u) { where(assignee_id: u.id)} scope :recent, -> { reorder(id: :desc) } + scope :order_position_asc, -> { reorder(position: :asc) } scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } @@ -144,6 +145,7 @@ module Issuable when 'downvotes_desc' then order_downvotes_desc when 'upvotes_desc' then order_upvotes_desc when 'priority' then order_labels_priority(excluded_labels: excluded_labels) + when 'position_asc' then order_position_asc else order_by(method) end diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 70705923d42..162ae153b1c 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -6,7 +6,7 @@ - if @last_push = render "events/event_last_push", event: @last_push -- if @projects.any? +- if @projects.any? || params[:filter_projects] = render 'projects' - else %h3 You don't have starred projects yet diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 951f03083bf..a039756c7e2 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -1,6 +1,6 @@ - if inject_u2f_api? - content_for :page_specific_javascripts do - = page_specific_javascript_tag('u2f.js') + = page_specific_javascript_bundle_tag('u2f') %div = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication' diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 558a1d56151..7ade5f00d47 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -4,7 +4,7 @@ - if inject_u2f_api? - content_for :page_specific_javascripts do - = page_specific_javascript_tag('u2f.js') + = page_specific_javascript_bundle_tag('u2f') .row.prepend-top-default .col-lg-3 @@ -96,4 +96,3 @@ :javascript var button = "<a class='btn btn-xs btn-warning pull-right' data-method='patch' href='#{skip_profile_two_factor_auth_path}'>Configure it later</a>"; $(".flash-alert").append(button); - diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index ed279cfe168..62135d3ae32 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -14,13 +14,13 @@ %td.new_line.diff-line-num %td.line_content.match= line.text - else - %td.old_line.diff-line-num.js-avatar-container{ class: type, data: { linenumber: line.old_pos } } + %td.old_line.diff-line-num{ class: [type, ("js-avatar-container" if !plain)], data: { linenumber: line.old_pos } } - link_text = type == "new" ? " " : line.old_pos - if plain = link_text - else %a{ href: "##{line_code}", data: { linenumber: link_text } } - - if discussion && !plain + - if discussion && discussion.resolvable? && !plain %diff-note-avatars{ "discussion-id" => discussion.id } %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } - link_text = type == "old" ? " " : line.new_pos diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index 6448748113b..e7758c8bdfa 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -20,7 +20,7 @@ - left_position = diff_file.position(left) %td.old_line.diff-line-num.js-avatar-container{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } } %a{ href: "##{left_line_code}", data: { linenumber: left.old_pos } } - - if discussion_left + - if discussion_left && discussion_left.resolvable? %diff-note-avatars{ "discussion-id" => discussion_left.id } %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 @@ -39,7 +39,7 @@ - right_position = diff_file.position(right) %td.new_line.diff-line-num.js-avatar-container{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } } %a{ href: "##{right_line_code}", data: { linenumber: right.new_pos } } - - if discussion_right + - if discussion_right && discussion_right.resolvable? %diff-note-avatars{ "discussion-id" => discussion_right.id } %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 diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index cb92b2e97a7..70470c83c51 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -62,24 +62,25 @@ - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) .row-content-block{ class: (is_footer ? "footer-block" : "middle-block") } - - if issuable.new_record? - = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - - else - = form.submit 'Save changes', class: 'btn btn-save' + .pull-right + - if issuable.new_record? + = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class]), class: 'btn btn-cancel' + - else + - if can?(current_user, :"destroy_#{issuable.to_ability_name}", @project) + = link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.human_class_name} will be removed! Are you sure?" }, method: :delete, class: 'btn btn-danger btn-grouped' + = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' + + %span.append-right-10 + - if issuable.new_record? + = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' + - else + = form.submit 'Save changes', class: 'btn btn-save' - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) - .inline.prepend-left-10 + .inline.prepend-top-10 Please review the %strong= link_to('contribution guidelines', guide_url) for this project. - - if issuable.new_record? - = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class]), class: 'btn btn-cancel' - - else - .pull-right - - if can?(current_user, :"destroy_#{issuable.to_ability_name}", @project) - = link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.human_class_name} will be removed! Are you sure?" }, - method: :delete, class: 'btn btn-danger btn-grouped' - = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' = form.hidden_field :lock_version diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 32128f3b3dc..f8123846596 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -14,18 +14,18 @@ .scroll-container %ul.tokens-container.list-unstyled %li.input-token - %input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id, 'data-username-params' => @users.to_json(only: [:id, :username]), 'data-base-endpoint' => namespace_project_path(@project.namespace, @project) } + %input.form-control.filtered-search{ placeholder: 'Search or filter results...', data: { id: 'filtered-search', 'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username]), 'base-endpoint' => namespace_project_path(@project.namespace, @project) } } = icon('filter') %button.clear-search.hidden{ type: 'button' } = icon('times') #js-dropdown-hint.dropdown-menu.hint-dropdown - %ul{ 'data-dropdown' => true } - %li.filter-dropdown-item{ 'data-action' => 'submit' } + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { action: 'submit' } } %button.btn.btn-link = icon('search') %span Keep typing and press Enter - %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item %button.btn.btn-link -# Encapsulate static class name `{{icon}}` inside #{} to bypass @@ -36,50 +36,50 @@ %span.js-filter-tag.dropdown-light-content {{tag}} #js-dropdown-author.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } } - %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item %button.btn.btn-link.dropdown-user - %img.avatar.avatar-inline{ 'data-src' => '{{avatar_url}}', alt: '{{name}}\'s avatar', width: '30' } + %img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } } .dropdown-user-details %span {{name}} %span.dropdown-light-content @{{username}} #js-dropdown-assignee.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } } - %ul{ 'data-dropdown' => true } - %li.filter-dropdown-item{ 'data-value' => 'none' } + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'none' } } %button.btn.btn-link No Assignee %li.divider - %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item %button.btn.btn-link.dropdown-user - %img.avatar.avatar-inline{ 'data-src' => '{{avatar_url}}', alt: '{{name}}\'s avatar', width: '30' } + %img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } } .dropdown-user-details %span {{name}} %span.dropdown-light-content @{{username}} #js-dropdown-milestone.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } } - %ul{ 'data-dropdown' => true } - %li.filter-dropdown-item{ 'data-value' => 'none' } + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'none' } } %button.btn.btn-link No Milestone - %li.filter-dropdown-item{ 'data-value' => 'upcoming' } + %li.filter-dropdown-item{ data: { value: 'upcoming' } } %button.btn.btn-link Upcoming %li.divider - %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item %button.btn.btn-link.js-data-value {{title}} #js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label' } } - %ul{ 'data-dropdown' => true } - %li.filter-dropdown-item{ 'data-value' => 'none' } + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'none' } } %button.btn.btn-link No Label %li.divider - %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item %button.btn.btn-link %span.dropdown-label-box{ style: 'background: {{color}}' } diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml index a93cbd1041f..8af3bd597c5 100644 --- a/app/views/shared/milestones/_issuables.html.haml +++ b/app/views/shared/milestones/_issuables.html.haml @@ -13,6 +13,6 @@ - class_prefix = dom_class(issuables).pluralize %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } = render partial: 'shared/milestones/issuable', - collection: issuables.sort_by(&:position), + collection: issuables.order_position_asc, as: :issuable, locals: { show_project_name: show_project_name, show_full_project_name: show_full_project_name } diff --git a/changelogs/unreleased/28030-infinite-offset.yml b/changelogs/unreleased/28030-infinite-offset.yml new file mode 100644 index 00000000000..6f4082d7684 --- /dev/null +++ b/changelogs/unreleased/28030-infinite-offset.yml @@ -0,0 +1,4 @@ +--- +title: allow offset query parameter for infinite list pages +merge_request: +author: diff --git a/changelogs/unreleased/28402-fix-starred-projects-filter-wrong-message-on-no-results.yml b/changelogs/unreleased/28402-fix-starred-projects-filter-wrong-message-on-no-results.yml new file mode 100644 index 00000000000..dd94b3fe663 --- /dev/null +++ b/changelogs/unreleased/28402-fix-starred-projects-filter-wrong-message-on-no-results.yml @@ -0,0 +1,4 @@ +--- +title: Fix wrong message on starred projects filtering +merge_request: +author: George Andrinopoulos diff --git a/changelogs/unreleased/28874-fix-milestone-issues-position-order-in-api.yml b/changelogs/unreleased/28874-fix-milestone-issues-position-order-in-api.yml new file mode 100644 index 00000000000..0177394aa0f --- /dev/null +++ b/changelogs/unreleased/28874-fix-milestone-issues-position-order-in-api.yml @@ -0,0 +1,4 @@ +--- +title: Order milestone issues by position ascending in api +merge_request: 9635 +author: George Andrinopoulos diff --git a/changelogs/unreleased/29014-create-issue-form-buttons-misaligned-on-mobile.yml b/changelogs/unreleased/29014-create-issue-form-buttons-misaligned-on-mobile.yml new file mode 100644 index 00000000000..f869249c22b --- /dev/null +++ b/changelogs/unreleased/29014-create-issue-form-buttons-misaligned-on-mobile.yml @@ -0,0 +1,4 @@ +--- +title: Fix create issue form buttons are misaligned on mobile +merge_request: 9706 +author: TM Lee diff --git a/changelogs/unreleased/dz-nested-groups-members.yml b/changelogs/unreleased/dz-nested-groups-members.yml new file mode 100644 index 00000000000..bab0c8465c2 --- /dev/null +++ b/changelogs/unreleased/dz-nested-groups-members.yml @@ -0,0 +1,4 @@ +--- +title: Show members of parent groups on project members page +merge_request: +author: diff --git a/changelogs/unreleased/update-ace.yml b/changelogs/unreleased/update-ace.yml new file mode 100644 index 00000000000..dbe476e3ae0 --- /dev/null +++ b/changelogs/unreleased/update-ace.yml @@ -0,0 +1,4 @@ +--- +title: Update code editor (ACE) to 1.2.6, to fix input problems with compose key +merge_request: +author: diff --git a/config/application.rb b/config/application.rb index cdb93e50e66..1cc092c4da1 100644 --- a/config/application.rb +++ b/config/application.rb @@ -100,7 +100,6 @@ module Gitlab config.assets.precompile << "katex.js" config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "lib/ace.js" - config.assets.precompile << "u2f.js" config.assets.precompile << "vendor/assets/fonts/*" # Version of your assets, change this if you want to expire all your assets diff --git a/config/initializers/6_validations.rb b/config/initializers/6_validations.rb index abe570f430c..9e24f42d284 100644 --- a/config/initializers/6_validations.rb +++ b/config/initializers/6_validations.rb @@ -13,24 +13,27 @@ def storage_validation_error(message) raise "#{message}. Please fix this in your gitlab.yml before starting GitLab." end -def validate_storages +def validate_storages_config storage_validation_error('No repository storage path defined') if Gitlab.config.repositories.storages.empty? Gitlab.config.repositories.storages.each do |name, repository_storage| storage_validation_error("\"#{name}\" is not a valid storage name") unless storage_name_valid?(name) if repository_storage.is_a?(String) - error = "#{name} is not a valid storage, because it has no `path` key. " \ + raise "#{name} is not a valid storage, because it has no `path` key. " \ "It may be configured as:\n\n#{name}:\n path: #{repository_storage}\n\n" \ - "Refer to gitlab.yml.example for an updated example" - - storage_validation_error(error) + "For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\n" \ + "If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n" end if !repository_storage.is_a?(Hash) || repository_storage['path'].nil? storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example") end + end +end +def validate_storages_paths + Gitlab.config.repositories.storages.each do |name, repository_storage| parent_name, _parent_path = find_parent_path(name, repository_storage['path']) if parent_name storage_validation_error("#{name} is a nested path of #{parent_name}. Nested paths are not supported for repository storages") @@ -38,4 +41,5 @@ def validate_storages end end -validate_storages unless Rails.env.test? || ENV['SKIP_STORAGE_VALIDATION'] == 'true' +validate_storages_config +validate_storages_paths unless Rails.env.test? || ENV['SKIP_STORAGE_VALIDATION'] == 'true' diff --git a/config/webpack.config.js b/config/webpack.config.js index 7298e7109c6..8e2b11a4145 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -40,6 +40,7 @@ var config = { protected_branches: './protected_branches/protected_branches_bundle.js', snippet: './snippet/snippet_bundle.js', terminal: './terminal/terminal_bundle.js', + u2f: ['vendor/u2f'], users: './users/users_bundle.js', vue_pipelines: './vue_pipelines_index/index.js', }, @@ -132,8 +133,7 @@ var config = { extensions: ['.js', '.es6', '.js.es6'], alias: { '~': path.join(ROOT_PATH, 'app/assets/javascripts'), - 'emoji-map$': path.join(ROOT_PATH, 'fixtures/emojis/digests.json'), - 'emoji-aliases$': path.join(ROOT_PATH, 'fixtures/emojis/aliases.json'), + 'emojis': path.join(ROOT_PATH, 'fixtures/emojis'), 'empty_states': path.join(ROOT_PATH, 'app/views/shared/empty_states'), 'icons': path.join(ROOT_PATH, 'app/views/shared/icons'), 'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'), diff --git a/doc/ci/README.md b/doc/ci/README.md index cbab7c9f18d..d8fba5d7a77 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -27,6 +27,8 @@ ## Breaking changes +- [CI variables renaming](variables/README.md#9-0-renaming) Read about the + deprecated CI variables and what you should use for GitLab 9.0+. - [New CI job permissions model](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your jobs. There's a new way to access your Git submodules and LFS objects in jobs. diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 2a5401ac13a..76e86f3e3c3 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -6,7 +6,7 @@ projects. GitLab offers a [continuous integration][ci] service. If you [add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository, -and configure your GitLab project to use a [Runner], then each merge request or +and configure your GitLab project to use a [Runner], then each commit or push, triggers your CI [pipeline]. The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs @@ -14,8 +14,8 @@ a pipeline with three [stages]: `build`, `test`, and `deploy`. You don't need to use all three stages; stages with no jobs are simply ignored. If everything runs OK (no non-zero return values), you'll get a nice green -checkmark associated with the pushed commit or merge request. This makes it -easy to see whether a merge request caused any of the tests to fail before +checkmark associated with the commit. This makes it +easy to see whether a commit caused any of the tests to fail before you even look at the code. Most projects use GitLab's CI service to run the test suite so that diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 4c3e7c4e86e..03e6b5303c5 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -27,93 +27,53 @@ Some of the predefined environment variables are available only if a minimum version of [GitLab Runner][runner] is used. Consult the table below to find the version of Runner required. -| Variable | GitLab | Runner | Description | -|-------------------------|--------|--------|-------------| -| **CI** | all | 0.4 | Mark that job is executed in CI environment | -| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | -| **CI_SERVER** | all | all | Mark that job is executed in CI environment | -| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | -| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | -| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | -| **CI_BUILD_ID** | all | all | The unique id of the current job that GitLab CI uses internally. Deprecated, use CI_JOB_ID | -| **CI_JOB_ID** | 9.0 | all | The unique id of the current job that GitLab CI uses internally | -| **CI_BUILD_REF** | all | all | The commit revision for which project is built. Deprecated, use CI_COMMIT_REF | -| **CI_COMMIT_SHA** | 9.0 | all | The commit revision for which project is built | -| **CI_BUILD_TAG** | all | 0.5 | The commit tag name. Present only when building tags. Deprecated, use CI_COMMIT_TAG | -| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. | -| **CI_BUILD_NAME** | all | 0.5 | The name of the job as defined in `.gitlab-ci.yml`. Deprecated, use CI_JOB_NAME | -| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` | -| **CI_BUILD_STAGE** | all | 0.5 | The name of the stage as defined in `.gitlab-ci.yml`. Deprecated, use CI_JOB_STAGE | -| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | -| **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built. Deprecated, use CI_COMMIT_REF_NAME | -| **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built | -| **CI_BUILD_REF_SLUG** | 8.15 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. Deprecated, use CI_COMMIT_REF_SLUG | -| **CI_COMMIT_REF_SLUG** | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. | -| **CI_BUILD_REPO** | all | all | The URL to clone the Git repository. Deprecated, use CI_REPOSITORY | -| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository | -| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that job was [triggered]. Deprecated, use CI_PIPELINE_TRIGGERED | -| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | -| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that job was manually started. Deprecated, use CI_JOB_MANUAL | -| **CI_JOB_MANUAL** | 8.12 | all | The flag to indicate that job was manually started | -| **CI_BUILD_TOKEN** | all | 1.2 | Token used for authenticating with the GitLab Container Registry. Deprecated, use CI_JOB_TOKEN | -| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry | -| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | -| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | -| **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built | -| **CI_PROJECT_NAMESPACE**| 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built | -| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name | -| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project | -| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | -| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | -| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | -| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry | -| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project | -| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used | -| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | -| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | -| **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | -| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | -| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job | -| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job | -| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job | -| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job | -| **CI_REGISTRY_USER** | 9.0 | all | The username to use to push containers to the GitLab Container Registry | -| **CI_REGISTRY_PASSWORD** | 9.0 | all | The password to use to push containers to the GitLab Container Registry | - -Example values: - -```bash -export CI_JOB_ID="50" -export CI_COMMIT_SHA="1ecfd275763eff1d6b4844ea3168962458c9f27a" -export CI_COMMIT_REF_NAME="master" -export CI_REPOSITORY="https://gitab-ci-token:abcde-1234ABCD5678ef@example.com/gitlab-org/gitlab-ce.git" -export CI_COMMIT_TAG="1.0.0" -export CI_JOB_NAME="spec:other" -export CI_JOB_STAGE="test" -export CI_JOB_MANUAL="true" -export CI_JOB_TRIGGERED="true" -export CI_JOB_TOKEN="abcde-1234ABCD5678ef" -export CI_PIPELINE_ID="1000" -export CI_PROJECT_ID="34" -export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce" -export CI_PROJECT_NAME="gitlab-ce" -export CI_PROJECT_NAMESPACE="gitlab-org" -export CI_PROJECT_PATH="gitlab-org/gitlab-ce" -export CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-ce" -export CI_REGISTRY="registry.example.com" -export CI_REGISTRY_IMAGE="registry.example.com/gitlab-org/gitlab-ce" -export CI_RUNNER_ID="10" -export CI_RUNNER_DESCRIPTION="my runner" -export CI_RUNNER_TAGS="docker, linux" -export CI_SERVER="yes" -export CI_SERVER_NAME="GitLab" -export CI_SERVER_REVISION="70606bf" -export CI_SERVER_VERSION="8.9.0" -export GITLAB_USER_ID="42" -export GITLAB_USER_EMAIL="user@example.com" -export CI_REGISTRY_USER="gitlab-ci-token" -export CI_REGISTRY_PASSWORD="longalfanumstring" -``` +>**Note:** +Starting with GitLab 9.0, we have deprecated some variables. Read the +[9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are +strongly advised to use the new variables as we will remove the old ones in +future GitLab releases.** + +| Variable | GitLab | Runner | Description | +|-------------------------------- |--------|--------|-------------| +| **CI** | all | 0.4 | Mark that job is executed in CI environment | +| **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built | +| **CI_COMMIT_REF_SLUG** | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. | +| **CI_COMMIT_SHA** | 9.0 | all | The commit revision for which project is built | +| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. | +| **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | +| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | +| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | +| **CI_JOB_ID** | 9.0 | all | The unique id of the current job that GitLab CI uses internally | +| **CI_JOB_MANUAL** | 8.12 | all | The flag to indicate that job was manually started | +| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` | +| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | +| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry | +| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository | +| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | +| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used | +| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | +| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | +| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | +| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | +| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | +| **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built | +| **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built | +| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name | +| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project | +| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry | +| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project | +| **CI_REGISTRY_PASSWORD** | 9.0 | all | The password to use to push containers to the GitLab Container Registry | +| **CI_REGISTRY_USER** | 9.0 | all | The username to use to push containers to the GitLab Container Registry | +| **CI_SERVER** | all | all | Mark that job is executed in CI environment | +| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | +| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | +| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | +| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job | +| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | +| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | +| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job | +| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job | +| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job | ## 9.0 Renaming @@ -121,19 +81,19 @@ To follow conventions of naming across GitLab, and to futher move away from the `build` term and toward `job` CI variables have been renamed for the 9.0 release. -| 8.X name | 9.0 name | -|----------|----------| -| CI_BUILD_ID | CI_JOB_ID | -| CI_BUILD_REF | CI_COMMIT_SHA | -| CI_BUILD_TAG | CI_COMMIT_TAG | -| CI_BUILD_REF_NAME | CI_COMMIT_REF_NAME | -| CI_BUILD_REF_SLUG | CI_COMMIT_REF_SLUG | -| CI_BUILD_NAME | CI_JOB_NAME | -| CI_BUILD_STAGE | CI_JOB_STAGE | -| CI_BUILD_REPO | CI_REPOSITORY | -| CI_BUILD_TRIGGERED | CI_PIPELINE_TRIGGERED | -| CI_BUILD_MANUAL | CI_JOB_MANUAL | -| CI_BUILD_TOKEN | CI_JOB_TOKEN | +| 8.x name | 9.0+ name | +| --------------------- |------------------------ | +| `CI_BUILD_ID` | `CI_JOB_ID` | +| `CI_BUILD_REF` | `CI_COMMIT_SHA` | +| `CI_BUILD_TAG` | `CI_COMMIT_TAG` | +| `CI_BUILD_REF_NAME` | `CI_COMMIT_REF_NAME` | +| `CI_BUILD_REF_SLUG` | `CI_COMMIT_REF_SLUG` | +| `CI_BUILD_NAME` | `CI_JOB_NAME` | +| `CI_BUILD_STAGE` | `CI_JOB_STAGE` | +| `CI_BUILD_REPO` | `CI_REPOSITORY_URL` | +| `CI_BUILD_TRIGGERED` | `CI_PIPELINE_TRIGGERED` | +| `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` | +| `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` | ## `.gitlab-ci.yaml` defined variables @@ -386,6 +346,41 @@ job_name: - export ``` +Example values: + +```bash +export CI_JOB_ID="50" +export CI_COMMIT_SHA="1ecfd275763eff1d6b4844ea3168962458c9f27a" +export CI_COMMIT_REF_NAME="master" +export CI_REPOSITORY="https://gitab-ci-token:abcde-1234ABCD5678ef@example.com/gitlab-org/gitlab-ce.git" +export CI_COMMIT_TAG="1.0.0" +export CI_JOB_NAME="spec:other" +export CI_JOB_STAGE="test" +export CI_JOB_MANUAL="true" +export CI_JOB_TRIGGERED="true" +export CI_JOB_TOKEN="abcde-1234ABCD5678ef" +export CI_PIPELINE_ID="1000" +export CI_PROJECT_ID="34" +export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce" +export CI_PROJECT_NAME="gitlab-ce" +export CI_PROJECT_NAMESPACE="gitlab-org" +export CI_PROJECT_PATH="gitlab-org/gitlab-ce" +export CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-ce" +export CI_REGISTRY="registry.example.com" +export CI_REGISTRY_IMAGE="registry.example.com/gitlab-org/gitlab-ce" +export CI_RUNNER_ID="10" +export CI_RUNNER_DESCRIPTION="my runner" +export CI_RUNNER_TAGS="docker, linux" +export CI_SERVER="yes" +export CI_SERVER_NAME="GitLab" +export CI_SERVER_REVISION="70606bf" +export CI_SERVER_VERSION="8.9.0" +export GITLAB_USER_ID="42" +export GITLAB_USER_EMAIL="user@example.com" +export CI_REGISTRY_USER="gitlab-ci-token" +export CI_REGISTRY_PASSWORD="longalfanumstring" +``` + [ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 [runner]: https://docs.gitlab.com/runner/ [triggered]: ../triggers/README.md diff --git a/doc/install/google-protobuf.md b/doc/install/google-protobuf.md new file mode 100644 index 00000000000..a531b4519b3 --- /dev/null +++ b/doc/install/google-protobuf.md @@ -0,0 +1,26 @@ +# Installing a locally compiled google-protobuf gem + +First we must find the exact version of google-protobuf that your +GitLab installation requires. + + cd /home/git/gitlab + + # Only one of the following two commands will print something. It + # will look like: * google-protobuf (3.2.0) + bundle list | grep google-protobuf + bundle check | grep google-protobuf + +Below we use `3.2.0` as an example. Replace it with the version number +you found above. + + cd /home/git/gitlab + sudo -u git -H gem install google-protobuf --version 3.2.0 --platform ruby + +Finally, you can test whether google-protobuf loads correctly. The +following should print 'OK'. + + sudo -u git -H bundle exec ruby -rgoogle/protobuf -e 'puts :OK' + +If the `gem install` command fails you may need to install developer +tools. On Debian: `apt-get install build-essential libgmp-dev`, on +Centos/RedHat `yum groupinstall 'Development Tools'`. diff --git a/doc/install/installation.md b/doc/install/installation.md index b6fb7a14a3a..177e1a9378b 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -658,6 +658,12 @@ misconfigured gitlab-workhorse instance. Double-check that you've [installed Go](#3-go), [installed gitlab-workhorse](#install-gitlab-workhorse), and correctly [configured Nginx](#site-configuration). +### google-protobuf "LoadError: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.14' not found" + +This can happen on some platforms for some versions of the +google-protobuf gem. The workaround is to [install a source-only +version of this gem](google-protobuf.md). + [RVM]: https://rvm.io/ "RVM Homepage" [rbenv]: https://github.com/sstephenson/rbenv "rbenv on GitHub" [chruby]: https://github.com/postmodern/chruby "chruby on GitHub" diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index e7f7edd95c7..abd263c1dfc 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -116,7 +116,8 @@ module API finder_params = { project_id: user_project.id, - milestone_title: milestone.title + milestone_title: milestone.title, + sort: 'position_asc' } issues = IssuesFinder.new(current_user, finder_params).execute @@ -138,7 +139,8 @@ module API finder_params = { project_id: user_project.id, - milestone_id: milestone.id + milestone_id: milestone.id, + sort: 'position_asc' } merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute diff --git a/lib/gitlab/ci/status/pipeline/blocked.rb b/lib/gitlab/ci/status/pipeline/blocked.rb new file mode 100644 index 00000000000..a250c3fcb41 --- /dev/null +++ b/lib/gitlab/ci/status/pipeline/blocked.rb @@ -0,0 +1,23 @@ +module Gitlab + module Ci + module Status + module Pipeline + class Blocked < SimpleDelegator + include Status::Extended + + def text + 'blocked' + end + + def label + 'waiting for manual action' + end + + def self.matches?(pipeline, user) + pipeline.blocked? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb index 13c8343b12a..17f9a75f436 100644 --- a/lib/gitlab/ci/status/pipeline/factory.rb +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -4,7 +4,8 @@ module Gitlab module Pipeline class Factory < Status::Factory def self.extended_statuses - [Status::SuccessWarning] + [[Status::SuccessWarning, + Status::Pipeline::Blocked]] end def self.common_helpers diff --git a/lib/gitlab/import_export/project_tree_saver.rb b/lib/gitlab/import_export/project_tree_saver.rb index b79be62245b..3473b466936 100644 --- a/lib/gitlab/import_export/project_tree_saver.rb +++ b/lib/gitlab/import_export/project_tree_saver.rb @@ -47,7 +47,13 @@ module Gitlab def group_members return [] unless @current_user.can?(:admin_group, @project.group) - MembersFinder.new(@project.project_members, @project.group).execute(@current_user) + # We need `.where.not(user_id: nil)` here otherwise when a group has an + # invitee, it would make the following query return 0 rows since a NULL + # user_id would be present in the subquery + # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values + non_null_user_ids = @project.project_members.where.not(user_id: nil).select(:user_id) + + GroupMembersFinder.new(@project.group).execute.where.not(user_id: non_null_user_ids) end end end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index ecf6b6e068b..5476438b8fa 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -62,7 +62,7 @@ namespace :gitlab do ref = Shellwords.escape(args[:ref]) - migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines + migrations = `git diff #{ref}.. --diff-filter=A --name-only -- db/migrate`.lines .map { |file| Rails.root.join(file.strip).to_s } .select { |file| File.file?(file) } diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb index 77404f46c92..b67c96bc00d 100644 --- a/spec/factories/ci/pipelines.rb +++ b/spec/factories/ci/pipelines.rb @@ -40,6 +40,14 @@ FactoryGirl.define do trait :invalid do config(rspec: nil) end + + trait :blocked do + status :manual + end + + trait :success do + status :success + end end end end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index c2e0612aef8..34d6257f5fd 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -1,26 +1,45 @@ require 'spec_helper' -describe "Dashboard > User filters projects", feature: true do +describe 'Dashboard > User filters projects', :feature do + let(:user) { create(:user) } + let(:project) { create(:project, name: 'Victorialand', namespace: user.namespace) } + let(:user2) { create(:user) } + let(:project2) { create(:project, name: 'Treasure', namespace: user2.namespace) } + + before do + project.team << [user, :master] + + login_as(user) + end + describe 'filtering personal projects' do before do - user = create(:user) - project = create(:project, name: "Victorialand", namespace: user.namespace) - project.team << [user, :master] - - user2 = create(:user) - project2 = create(:project, name: "Treasure", namespace: user2.namespace) project2.team << [user, :developer] - login_as(user) visit dashboard_projects_path end it 'filters by projects "Owned by me"' do - click_link "Owned by me" + click_link 'Owned by me' expect(page).to have_css('.is-active', text: 'Owned by me') expect(page).to have_content('Victorialand') expect(page).not_to have_content('Treasure') end end + + describe 'filtering starred projects', :js do + before do + user.toggle_star(project) + + visit dashboard_projects_path + end + + it 'returns message when starred projects fitler returns no results' do + fill_in 'project-filter-form-field', with: 'Beta\n' + + expect(page).to have_content('No projects found') + expect(page).not_to have_content('You don\'t have starred projects yet') + end + end end diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb index 7df102067d6..a6c72b0b3ac 100644 --- a/spec/features/merge_requests/diff_notes_avatars_spec.rb +++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb @@ -23,6 +23,56 @@ feature 'Diff note avatars', feature: true, js: true do login_as user end + context 'discussion tab' do + before do + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + end + + it 'does not show avatars on discussion tab' do + expect(page).not_to have_selector('.js-avatar-container') + expect(page).not_to have_selector('.diff-comment-avatar-holders') + end + + it 'does not render avatars after commening on discussion tab' do + click_button 'Reply...' + + page.within('.js-discussion-note-form') do + find('.note-textarea').native.send_keys('Test comment') + + click_button 'Comment' + end + + expect(page).to have_content('Test comment') + expect(page).not_to have_selector('.js-avatar-container') + expect(page).not_to have_selector('.diff-comment-avatar-holders') + end + end + + context 'commit view' do + before do + visit namespace_project_commit_path(project.namespace, project, merge_request.commits.first.id) + end + + it 'does not render avatar after commenting' do + first('.diff-line-num').trigger('mouseover') + find('.js-add-diff-note-button').click + + page.within('.js-discussion-note-form') do + find('.note-textarea').native.send_keys('test comment') + + click_button 'Comment' + + wait_for_ajax + end + + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + + expect(page).to have_content('test comment') + expect(page).not_to have_selector('.js-avatar-container') + expect(page).not_to have_selector('.diff-comment-avatar-holders') + end + end + %w(inline parallel).each do |view| context "#{view} view" do before do diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb new file mode 100644 index 00000000000..cf691cf684b --- /dev/null +++ b/spec/finders/members_finder_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe MembersFinder, '#execute' do + let(:group) { create(:group) } + let(:nested_group) { create(:group, :access_requestable, parent: group) } + let(:project) { create(:project, namespace: nested_group) } + let(:user1) { create(:user) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + let(:user4) { create(:user) } + + it 'returns members for project and parent groups' do + nested_group.request_access(user1) + member1 = group.add_master(user2) + member2 = nested_group.add_master(user3) + member3 = project.add_master(user4) + + result = described_class.new(project, user2).execute + + expect(result.to_a).to eq([member3, member2, member1]) + end +end diff --git a/spec/initializers/6_validations_spec.rb b/spec/initializers/6_validations_spec.rb index cf182e6d221..374517fec37 100644 --- a/spec/initializers/6_validations_spec.rb +++ b/spec/initializers/6_validations_spec.rb @@ -12,63 +12,77 @@ describe '6_validations', lib: true do FileUtils.rm_rf('tmp/tests/paths') end - context 'with correct settings' do - before do - mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/d' }) - end + describe 'validate_storages_config' do + context 'with correct settings' do + before do + mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/d' }) + end - it 'passes through' do - expect { validate_storages }.not_to raise_error + it 'passes through' do + expect { validate_storages_config }.not_to raise_error + end end - end - context 'with invalid storage names' do - before do - mock_storages('name with spaces' => { 'path' => 'tmp/tests/paths/a/b/c' }) - end + context 'with invalid storage names' do + before do + mock_storages('name with spaces' => { 'path' => 'tmp/tests/paths/a/b/c' }) + end - it 'throws an error' do - expect { validate_storages }.to raise_error('"name with spaces" is not a valid storage name. Please fix this in your gitlab.yml before starting GitLab.') + it 'throws an error' do + expect { validate_storages_config }.to raise_error('"name with spaces" is not a valid storage name. Please fix this in your gitlab.yml before starting GitLab.') + end end - end - context 'with nested storage paths' do - before do - mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/c/d' }) - end + context 'with incomplete settings' do + before do + mock_storages('foo' => {}) + end - it 'throws an error' do - expect { validate_storages }.to raise_error('bar is a nested path of foo. Nested paths are not supported for repository storages. Please fix this in your gitlab.yml before starting GitLab.') + it 'throws an error suggesting the user to update its settings' do + expect { validate_storages_config }.to raise_error('foo is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example. Please fix this in your gitlab.yml before starting GitLab.') + end end - end - context 'with similar but un-nested storage paths' do - before do - mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/c2' }) - end + context 'with deprecated settings structure' do + before do + mock_storages('foo' => 'tmp/tests/paths/a/b/c') + end - it 'passes through' do - expect { validate_storages }.not_to raise_error + it 'throws an error suggesting the user to update its settings' do + expect { validate_storages_config }.to raise_error("foo is not a valid storage, because it has no `path` key. It may be configured as:\n\nfoo:\n path: tmp/tests/paths/a/b/c\n\nFor source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\nIf you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n") + end end end - context 'with incomplete settings' do - before do - mock_storages('foo' => {}) - end + describe 'validate_storages_paths' do + context 'with correct settings' do + before do + mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/d' }) + end - it 'throws an error suggesting the user to update its settings' do - expect { validate_storages }.to raise_error('foo is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example. Please fix this in your gitlab.yml before starting GitLab.') + it 'passes through' do + expect { validate_storages_paths }.not_to raise_error + end end - end - context 'with deprecated settings structure' do - before do - mock_storages('foo' => 'tmp/tests/paths/a/b/c') + context 'with nested storage paths' do + before do + mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/c/d' }) + end + + it 'throws an error' do + expect { validate_storages_paths }.to raise_error('bar is a nested path of foo. Nested paths are not supported for repository storages. Please fix this in your gitlab.yml before starting GitLab.') + end end - it 'throws an error suggesting the user to update its settings' do - expect { validate_storages }.to raise_error("foo is not a valid storage, because it has no `path` key. It may be configured as:\n\nfoo:\n path: tmp/tests/paths/a/b/c\n\nRefer to gitlab.yml.example for an updated example. Please fix this in your gitlab.yml before starting GitLab.") + context 'with similar but un-nested storage paths' do + before do + mock_storages('foo' => { 'path' => 'tmp/tests/paths/a/b/c' }, 'bar' => { 'path' => 'tmp/tests/paths/a/b/c2' }) + end + + it 'passes through' do + expect { validate_storages_paths }.not_to raise_error + end end end diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index f4b1d777203..dc0a62ade50 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,8 +1,9 @@ /* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */ -require('es6-promise').polyfill(); +import promisePolyfill from 'es6-promise'; +import AwardsHandler from '~/awards_handler'; -const AwardsHandler = require('~/awards_handler'); +promisePolyfill.polyfill(); (function() { var awardsHandler, lazyAssert, urlRoot, openAndWaitForEmojiMenu; @@ -46,9 +47,6 @@ const AwardsHandler = require('~/awards_handler'); isEmojiMenuBuilt = true; resolve(); }); - - // Fail after 1 second - setTimeout(reject, 1000); } }); }; diff --git a/spec/javascripts/gl_emoji_spec.js b/spec/javascripts/gl_emoji_spec.js index e94e220b19f..7ab0b37f2ec 100644 --- a/spec/javascripts/gl_emoji_spec.js +++ b/spec/javascripts/gl_emoji_spec.js @@ -1,16 +1,15 @@ +import '~/extensions/string'; +import '~/extensions/array'; -require('~/extensions/string'); -require('~/extensions/array'); - -const glEmoji = require('~/behaviors/gl_emoji'); - -const glEmojiTag = glEmoji.glEmojiTag; -const isEmojiUnicodeSupported = glEmoji.isEmojiUnicodeSupported; -const isFlagEmoji = glEmoji.isFlagEmoji; -const isKeycapEmoji = glEmoji.isKeycapEmoji; -const isSkinToneComboEmoji = glEmoji.isSkinToneComboEmoji; -const isHorceRacingSkinToneComboEmoji = glEmoji.isHorceRacingSkinToneComboEmoji; -const isPersonZwjEmoji = glEmoji.isPersonZwjEmoji; +import { glEmojiTag } from '~/behaviors/gl_emoji'; +import { + isEmojiUnicodeSupported, + isFlagEmoji, + isKeycapEmoji, + isSkinToneComboEmoji, + isHorceRacingSkinToneComboEmoji, + isPersonZwjEmoji, +} from '~/behaviors/gl_emoji/is_emoji_unicode_supported'; const emptySupportMap = { personZwj: false, diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js new file mode 100644 index 00000000000..d966226909b --- /dev/null +++ b/spec/javascripts/pager_spec.js @@ -0,0 +1,90 @@ +/* global fixture */ + +require('~/pager'); + +describe('pager', () => { + const Pager = window.Pager; + + it('is defined on window', () => { + expect(window.Pager).toBeDefined(); + }); + + describe('init', () => { + const originalHref = window.location.href; + + beforeEach(() => { + setFixtures('<div class="content_list"></div><div class="loading"></div>'); + spyOn($, 'ajax'); + }); + + afterEach(() => { + window.history.replaceState({}, null, originalHref); + }); + + it('should use data-href attribute from list element', () => { + const href = `${gl.TEST_HOST}/some_list.json`; + setFixtures(`<div class="content_list" data-href="${href}"></div>`); + Pager.init(); + expect(Pager.url).toBe(href); + }); + + it('should use current url if data-href attribute not provided', () => { + const href = `${gl.TEST_HOST}/some_list`; + spyOn(gl.utils, 'removeParams').and.returnValue(href); + Pager.init(); + expect(Pager.url).toBe(href); + }); + + it('should get initial offset from query parameter', () => { + window.history.replaceState({}, null, '?offset=100'); + Pager.init(); + expect(Pager.offset).toBe(100); + }); + + it('keeps extra query parameters from url', () => { + window.history.replaceState({}, null, '?filter=test&offset=100'); + const href = `${gl.TEST_HOST}/some_list?filter=test`; + spyOn(gl.utils, 'removeParams').and.returnValue(href); + Pager.init(); + expect(gl.utils.removeParams).toHaveBeenCalledWith(['limit', 'offset']); + expect(Pager.url).toEqual(href); + }); + }); + + describe('getOld', () => { + beforeEach(() => { + setFixtures('<div class="content_list" data-href="/some_list"></div><div class="loading"></div>'); + Pager.init(); + }); + + it('shows loader while loading next page', () => { + spyOn(Pager.loading, 'show'); + Pager.getOld(); + expect(Pager.loading.show).toHaveBeenCalled(); + }); + + it('hides loader on success', () => { + spyOn($, 'ajax').and.callFake(options => options.success({})); + spyOn(Pager.loading, 'hide'); + Pager.getOld(); + expect(Pager.loading.hide).toHaveBeenCalled(); + }); + + it('hides loader on error', () => { + spyOn($, 'ajax').and.callFake(options => options.error()); + spyOn(Pager.loading, 'hide'); + Pager.getOld(); + expect(Pager.loading.hide).toHaveBeenCalled(); + }); + + it('sends request to url with offset and limit params', () => { + spyOn($, 'ajax'); + Pager.offset = 100; + Pager.limit = 20; + Pager.getOld(); + const [{ data, url }] = $.ajax.calls.argsFor(0); + expect(data).toBe('limit=20&offset=100'); + expect(url).toBe('/some_list'); + }); + }); +}); diff --git a/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb new file mode 100644 index 00000000000..1a2b952d374 --- /dev/null +++ b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Pipeline::Blocked do + let(:pipeline) { double('pipeline') } + + subject do + described_class.new(pipeline) + end + + describe '#text' do + it 'overrides status text' do + expect(subject.text).to eq 'blocked' + end + end + + describe '#label' do + it 'overrides status label' do + expect(subject.label).to eq 'waiting for manual action' + end + end + + describe '.matches?' do + let(:user) { double('user') } + subject { described_class.matches?(pipeline, user) } + + context 'when pipeline is blocked' do + let(:pipeline) { create(:ci_pipeline, :blocked) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when pipeline is not blocked' do + let(:pipeline) { create(:ci_pipeline, :success) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index b10a447c27a..dd754b849b2 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Ci::Status::Pipeline::Factory do end context 'when pipeline has a core status' do - HasStatus::AVAILABLE_STATUSES.each do |simple_status| + (HasStatus::AVAILABLE_STATUSES - [HasStatus::BLOCKED_STATUS]) + .each do |simple_status| context "when core status is #{simple_status}" do let(:pipeline) { create(:ci_pipeline, status: simple_status) } @@ -23,7 +24,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do expect(factory.core_status).to be_a expected_status end - it 'does not matche extended statuses' do + it 'does not match extended statuses' do expect(factory.extended_statuses).to be_empty end @@ -39,6 +40,27 @@ describe Gitlab::Ci::Status::Pipeline::Factory do end end end + + context "when core status is manual" do + let(:pipeline) { create(:ci_pipeline, status: :manual) } + + it "matches manual core status" do + expect(factory.core_status) + .to be_a Gitlab::Ci::Status::Manual + end + + it 'matches a correct extended statuses' do + expect(factory.extended_statuses) + .to eq [Gitlab::Ci::Status::Pipeline::Blocked] + end + + it 'extends core status with common pipeline methods' do + expect(status).to have_details + expect(status).not_to have_action + expect(status.details_path) + .to include "pipelines/#{pipeline.id}" + end + end end context 'when pipeline has warnings' do diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index dd5f7098d06..3ea62df62f2 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -647,7 +647,7 @@ describe Ci::Pipeline, models: true do let(:pipeline) { create(:ci_pipeline, status: :manual) } it 'returns detailed status for blocked pipeline' do - expect(subject.text).to eq 'manual' + expect(subject.text).to eq 'blocked' end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 3bb8b6fdbeb..7fb728fed6f 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -243,8 +243,8 @@ describe API::Milestones, api: true do describe 'confidential issues' do let(:public_project) { create(:empty_project, :public) } let(:milestone) { create(:milestone, project: public_project) } - let(:issue) { create(:issue, project: public_project) } - let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } + let(:issue) { create(:issue, project: public_project, position: 2) } + let(:confidential_issue) { create(:issue, confidential: true, project: public_project, position: 1) } before do public_project.team << [user, :developer] @@ -283,11 +283,24 @@ describe API::Milestones, api: true do expect(json_response.size).to eq(1) expect(json_response.map { |issue| issue['id'] }).to include(issue.id) end + + it 'returns issues ordered by position asc' do + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + expect(json_response.first['id']).to eq(confidential_issue.id) + expect(json_response.second['id']).to eq(issue.id) + end end end describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do - let(:merge_request) { create(:merge_request, source_project: project) } + let(:merge_request) { create(:merge_request, source_project: project, position: 2) } + let(:another_merge_request) { create(:merge_request, :simple, source_project: project, position: 1) } + before do milestone.merge_requests << merge_request end @@ -320,5 +333,18 @@ describe API::Milestones, api: true do expect(response).to have_http_status(401) end + + it 'returns merge_requests ordered by position asc' do + milestone.merge_requests << another_merge_request + + get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + expect(json_response.first['id']).to eq(another_merge_request.id) + expect(json_response.second['id']).to eq(merge_request.id) + end end end diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore index a1a65c2d72e..520a86352f7 100644 --- a/vendor/gitignore/Android.gitignore +++ b/vendor/gitignore/Android.gitignore @@ -37,6 +37,7 @@ captures/ .idea/workspace.xml .idea/tasks.xml .idea/gradle.xml +.idea/dictionaries .idea/libraries # Keystore files @@ -48,7 +49,7 @@ captures/ # Google Services (e.g. APIs or Firebase) google-services.json -#Freeline +# Freeline freeline.py freeline/ freeline_project_description.json diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore index 31c9fb31167..4f88399d2d8 100644 --- a/vendor/gitignore/Global/Eclipse.gitignore +++ b/vendor/gitignore/Global/Eclipse.gitignore @@ -49,3 +49,8 @@ local.properties # Code Recommenders .recommenders/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore index 401fee15748..ec7e95c6ab5 100644 --- a/vendor/gitignore/Global/JetBrains.gitignore +++ b/vendor/gitignore/Global/JetBrains.gitignore @@ -4,6 +4,7 @@ # User-specific stuff: .idea/**/workspace.xml .idea/**/tasks.xml +.idea/dictionaries # Sensitive or high-churn files: .idea/**/dataSources/ diff --git a/vendor/gitignore/Global/SBT.gitignore b/vendor/gitignore/Global/SBT.gitignore index 970d897c75c..5ed6acb6576 100644 --- a/vendor/gitignore/Global/SBT.gitignore +++ b/vendor/gitignore/Global/SBT.gitignore @@ -1,9 +1,12 @@ # Simple Build Tool # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control +dist/* target/ lib_managed/ src_managed/ project/boot/ +project/plugins/project/ .history .cache +.lib/ diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore index dbb4a2dfa1a..6143e53f9e3 100644 --- a/vendor/gitignore/Java.gitignore +++ b/vendor/gitignore/Java.gitignore @@ -1,3 +1,4 @@ +# Compiled class file *.class # Log file diff --git a/vendor/gitignore/Maven.gitignore b/vendor/gitignore/Maven.gitignore index 9af45b175ae..5f2dbe11df9 100644 --- a/vendor/gitignore/Maven.gitignore +++ b/vendor/gitignore/Maven.gitignore @@ -8,5 +8,5 @@ dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties -# Exclude maven wrapper +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) !/.mvn/wrapper/maven-wrapper.jar diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore index 38ac77e405e..00cbbdf53f6 100644 --- a/vendor/gitignore/Node.gitignore +++ b/vendor/gitignore/Node.gitignore @@ -2,6 +2,8 @@ logs *.log npm-debug.log* +yarn-debug.log* +yarn-error.log* # Runtime data pids diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore index af90c007a3f..09dfede4814 100644 --- a/vendor/gitignore/Objective-C.gitignore +++ b/vendor/gitignore/Objective-C.gitignore @@ -45,10 +45,10 @@ Carthage/Build # fastlane # -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md +# https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html diff --git a/vendor/gitignore/PlayFramework.gitignore b/vendor/gitignore/PlayFramework.gitignore index 6d67f119175..ae5ec9fe1d9 100644 --- a/vendor/gitignore/PlayFramework.gitignore +++ b/vendor/gitignore/PlayFramework.gitignore @@ -5,6 +5,7 @@ bin/ /lib/ /logs/ /modules +/project/project /project/target /target tmp/ diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore index cf3102d6b00..62c1e736924 100644 --- a/vendor/gitignore/Python.gitignore +++ b/vendor/gitignore/Python.gitignore @@ -76,6 +76,9 @@ target/ # celery beat schedule file celerybeat-schedule +# SageMath parsed files +*.sage.py + # dotenv .env diff --git a/vendor/gitignore/Scala.gitignore b/vendor/gitignore/Scala.gitignore index 006a7b247fe..9c07d4ae988 100644 --- a/vendor/gitignore/Scala.gitignore +++ b/vendor/gitignore/Scala.gitignore @@ -1,23 +1,2 @@ *.class *.log - -# sbt specific -.cache -.history -.lib/ -dist/* -target/ -lib_managed/ -src_managed/ -project/boot/ -project/plugins/project/ - -# Scala-IDE specific -.ensime -.ensime_cache/ -.scala_dependencies -.worksheet - -# ENSIME specific -.ensime_cache/ -.ensime diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore index 099d22ae2f4..d5340449396 100644 --- a/vendor/gitignore/Swift.gitignore +++ b/vendor/gitignore/Swift.gitignore @@ -59,7 +59,7 @@ Carthage/Build # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md +# https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html diff --git a/vendor/gitignore/Symfony.gitignore b/vendor/gitignore/Symfony.gitignore index ed4d3c6c28d..6c224e024e9 100644 --- a/vendor/gitignore/Symfony.gitignore +++ b/vendor/gitignore/Symfony.gitignore @@ -25,7 +25,6 @@ /bin/* !bin/console !bin/symfony_requirements -/vendor/ # Assets and user uploads /web/bundles/ @@ -38,8 +37,5 @@ # Build data /build/ -# Composer PHAR -/composer.phar - # Backup entities generated with doctrine:generate:entities command **/Entity/*~ diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore index 69bfb1eec3e..57ed9f5d972 100644 --- a/vendor/gitignore/TeX.gitignore +++ b/vendor/gitignore/TeX.gitignore @@ -28,7 +28,6 @@ *.blg *-blx.aux *-blx.bib -*.brf *.run.xml ## Build tool auxiliary files: @@ -77,8 +76,6 @@ acs-*.bib *.t[1-9] *.t[1-9][0-9] *.tfm -*.[1-9] -*.[1-9][0-9] #(r)(e)ledmac/(r)(e)ledpar *.end @@ -134,6 +131,9 @@ acs-*.bib *.mlf *.mlt *.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* # minted _minted* @@ -142,9 +142,6 @@ _minted* # morewrites *.mw -# mylatexformat -*.fmt - # nomencl *.nlo diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore index 8054980d742..a752eacca7d 100644 --- a/vendor/gitignore/VisualStudio.gitignore +++ b/vendor/gitignore/VisualStudio.gitignore @@ -166,7 +166,7 @@ PublishScripts/ !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files +# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -276,3 +276,12 @@ __pycache__/ # Cake - Uncomment if you are using it # tools/** # !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs
\ No newline at end of file diff --git a/vendor/gitlab-ci-yml/Android.gitlab-ci.yml b/vendor/gitlab-ci-yml/Android.gitlab-ci.yml new file mode 100644 index 00000000000..5f9d54ff574 --- /dev/null +++ b/vendor/gitlab-ci-yml/Android.gitlab-ci.yml @@ -0,0 +1,51 @@ +# Read more about this script on this blog post https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/, by Greyson Parrelli +image: openjdk:8-jdk + +variables: + ANDROID_COMPILE_SDK: "25" + ANDROID_BUILD_TOOLS: "24.0.0" + ANDROID_SDK_TOOLS: "24.4.1" + +before_script: + - apt-get --quiet update --yes + - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1 + - wget --quiet --output-document=android-sdk.tgz https://dl.google.com/android/android-sdk_r${ANDROID_SDK_TOOLS}-linux.tgz + - tar --extract --gzip --file=android-sdk.tgz + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter android-${ANDROID_COMPILE_SDK} + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter platform-tools + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter build-tools-${ANDROID_BUILD_TOOLS} + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-android-m2repository + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-google_play_services + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-m2repository + - export ANDROID_HOME=$PWD/android-sdk-linux + - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/ + - chmod +x ./gradlew + +stages: + - build + - test + +build: + stage: build + script: + - ./gradlew assembleDebug + artifacts: + paths: + - app/build/outputs/ + +unitTests: + stage: test + script: + - ./gradlew test + +functionalTests: + stage: test + script: + - wget --quiet --output-document=android-wait-for-emulator https://raw.githubusercontent.com/travis-ci/travis-cookbooks/0f497eb71291b52a703143c5cd63a217c8766dc9/community-cookbooks/android-sdk/files/default/android-wait-for-emulator + - chmod +x android-wait-for-emulator + - echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter sys-img-x86-google_apis-${ANDROID_COMPILE_SDK} + - echo no | android-sdk-linux/tools/android create avd -n test -t android-${ANDROID_COMPILE_SDK} --abi google_apis/x86 + - android-sdk-linux/tools/emulator64-x86 -avd test -no-window -no-audio & + - ./android-wait-for-emulator + - adb shell input keyevent 82 + - ./gradlew cAT diff --git a/vendor/gitlab-ci-yml/Bash.gitlab-ci.yml b/vendor/gitlab-ci-yml/Bash.gitlab-ci.yml new file mode 100644 index 00000000000..27537689b80 --- /dev/null +++ b/vendor/gitlab-ci-yml/Bash.gitlab-ci.yml @@ -0,0 +1,35 @@ +# see https://docs.gitlab.com/ce/ci/yaml/README.html for all available options + +# you can delete this line if you're not using Docker +image: busybox:latest + +before_script: + - echo "Before script section" + - echo "For example you might run an update here or install a build dependency" + - echo "Or perhaps you might print out some debugging details" + +after_script: + - echo "After script section" + - echo "For example you might do some cleanup here" + +build1: + stage: build + script: + - echo "Do your build here" + +test1: + stage: test + script: + - echo "Do a test here" + - echo "For example run a test suite" + +test2: + stage: test + script: + - echo "Do another parallel test here" + - echo "For example run a lint test" + +deploy1: + stage: deploy + script: + - echo "Do your deploy here"
\ No newline at end of file diff --git a/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml b/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml index e8da49a935e..37e44735f7c 100644 --- a/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml @@ -1,4 +1,3 @@ -# This file is a template, and might need editing before it works on your project. # Official language image. Look for the different tagged releases at: # https://hub.docker.com/r/crystallang/crystal/ image: "crystallang/crystal:latest" diff --git a/vendor/gitlab-ci-yml/Django.gitlab-ci.yml b/vendor/gitlab-ci-yml/Django.gitlab-ci.yml new file mode 100644 index 00000000000..b3106863cca --- /dev/null +++ b/vendor/gitlab-ci-yml/Django.gitlab-ci.yml @@ -0,0 +1,34 @@ +# Official framework image. Look for the different tagged releases at: +# https://hub.docker.com/r/library/python +image: python:latest + +# Pick zero or more services to be used on all builds. +# Only needed when using a docker container to run your tests in. +# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service +services: + - mysql:latest + - postgres:latest + +variables: + POSTGRES_DB: database_name + +# This folder is cached between builds +# http://docs.gitlab.com/ce/ci/yaml/README.html#cache +cache: + paths: + - ~/.cache/pip/ + +# This is a basic example for a gem or script which doesn't use +# services such as redis or postgres +before_script: + - python -V # Print out python version for debugging + # Uncomment next line if your Django app needs a JS runtime: + # - apt-get update -q && apt-get install nodejs -yqq + - pip install -r requirements.txt + +test: + variables: + DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" + script: + - python manage.py migrate + - python manage.py test diff --git a/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml b/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml index 98d3039ad06..a65e48a3389 100644 --- a/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Gradle.gitlab-ci.yml @@ -6,6 +6,13 @@ # https://github.com/gradle/gradle image: java:8 +# Disable the Gradle daemon for Continuous Integration servers as correctness +# is usually a priority over speed in CI environments. Using a fresh +# runtime for each build is more reliable since the runtime is completely +# isolated from any previous builds. +variables: + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + # Make the gradle wrapper executable. This essentially downloads a copy of # Gradle to build the project with. # https://docs.gradle.org/current/userguide/gradle_wrapper.html diff --git a/vendor/gitlab-ci-yml/LICENSE b/vendor/gitlab-ci-yml/LICENSE index 80f7b87b6c0..d6c93c6fcf7 100644 --- a/vendor/gitlab-ci-yml/LICENSE +++ b/vendor/gitlab-ci-yml/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 GitLab.org +Copyright (c) 2016-2017 GitLab.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml b/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml new file mode 100644 index 00000000000..0d6a6eddc97 --- /dev/null +++ b/vendor/gitlab-ci-yml/Laravel.gitlab-ci.yml @@ -0,0 +1,78 @@ +# Official framework image. Look for the different tagged releases at: +# https://hub.docker.com/r/library/php +image: php:latest + +# Pick zero or more services to be used on all builds. +# Only needed when using a docker container to run your tests in. +# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service +services: + - mysql:latest + +variables: + MYSQL_DATABASE: project_name + MYSQL_ROOT_PASSWORD: secret + +# This folder is cached between builds +# http://docs.gitlab.com/ce/ci/yaml/README.html#cache +cache: + paths: + - vendor/ + - node_modules/ + +# This is a basic example for a gem or script which doesn't use +# services such as redis or postgres +before_script: + # Update packages + - apt-get update -yqq + + # Upgrade to Node 7 + - curl -sL https://deb.nodesource.com/setup_7.x | bash - + + # Install dependencies + - apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq + + # Install php extensions + - docker-php-ext-install mbstring mcrypt pdo_mysql curl json intl gd xml zip bz2 opcache + + # Install Composer and project dependencies. + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + + # Install Node dependencies. + # comment this out if you don't have a node dependency + - npm install + + # Copy over testing configuration. + # Don't forget to set the database config in .env.testing correctly + # DB_HOST=mysql + # DB_DATABASE=project_name + # DB_USERNAME=root + # DB_PASSWORD=secret + - cp .env.testing .env + + # Run npm build + # comment this out if you don't have a frontend build + # you can change this to to your frontend building script like + # npm run build + - npm run dev + + # Generate an application key. Re-cache. + - php artisan key:generate + - php artisan config:cache + + # Run database migrations. + - php artisan migrate + + # Run database seed + - php artisan db:seed + +test: + script: + # run laravel tests + - php vendor/bin/phpunit --coverage-text --colors=never + + # run frontend tests + # if you have any task for testing frontend + # set it in your package.json script + # comment this out if you don't have a frontend test + - npm test diff --git a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml index 1678a47f9ac..b75f0665bee 100644 --- a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml @@ -17,16 +17,17 @@ variables: # This will supress any download for dependencies and plugins or upload messages which would clutter the console log. # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. - MAVEN_OPTS: "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true" + MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true" # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used # when running from the command line. # `installAtEnd` and `deployAtEnd`are only effective with recent version of the corresponding plugins. MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true" # Cache downloaded dependencies and plugins between builds. +# To keep cache across branches add 'key: "$CI_BUILD_REF_NAME"' cache: paths: - - /root/.m2/repository/ + - .m2/repository # This will only validate and compile stuff and run e.g. maven-enforcer-plugin. # Because some enforcer rules might check dependency convergence and class duplications diff --git a/vendor/gitlab-ci-yml/Openshift.gitlab-ci.yml b/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml index 2ba5cad9682..6b6c405a507 100644 --- a/vendor/gitlab-ci-yml/Openshift.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml @@ -1,4 +1,3 @@ -# This file is a template, and might need editing before it works on your project. image: ayufan/openshift-cli stages: @@ -6,6 +5,7 @@ stages: - review - staging - production + - cleanup variables: OPENSHIFT_SERVER: openshift.default.svc.cluster.local @@ -28,7 +28,7 @@ test2: .deploy: &deploy before_script: - oc login "$OPENSHIFT_SERVER" --token="$OPENSHIFT_TOKEN" --insecure-skip-tls-verify - - oc project "$CI_PROJECT_NAME" 2> /dev/null || oc new-project "$CI_PROJECT_NAME" + - oc project "$CI_PROJECT_NAME-$CI_PROJECT_ID" 2> /dev/null || oc new-project "$CI_PROJECT_NAME-$CI_PROJECT_ID" script: - "oc get services $APP 2> /dev/null || oc new-app . --name=$APP --strategy=docker" - "oc start-build $APP --from-dir=. --follow || sleep 3s || oc start-build $APP --from-dir=. --follow" @@ -51,7 +51,7 @@ review: stop-review: <<: *deploy - stage: review + stage: cleanup script: - oc delete all -l "app=$APP" when: manual diff --git a/vendor/gitlab-ci-yml/PHP.gitlab-ci.yml b/vendor/gitlab-ci-yml/PHP.gitlab-ci.yml new file mode 100644 index 00000000000..bb8caa49d6b --- /dev/null +++ b/vendor/gitlab-ci-yml/PHP.gitlab-ci.yml @@ -0,0 +1,33 @@ +# Select image from https://hub.docker.com/_/php/ +image: php:7.1.1 + +# Select what we should cache between builds +cache: + paths: + - vendor/ + +before_script: +- apt-get update -yqq +- apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev +# Install PHP extensions +- docker-php-ext-install mbstring mcrypt pdo_pgsql curl json intl gd xml zip bz2 opcache +# Install and run Composer +- curl -sS https://getcomposer.org/installer | php +- php composer.phar install + +# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service +# See http://docs.gitlab.com/ce/ci/services/README.html for examples. +services: + - mysql:5.7 + +# Set any variables we need +variables: + # Configure mysql environment variables (https://hub.docker.com/r/_/mysql/) + MYSQL_DATABASE: mysql_database + MYSQL_ROOT_PASSWORD: mysql_strong_password + +# Run our tests +# If Xdebug was installed you can generate a coverage report and see code coverage metrics. +test: + script: + - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
\ No newline at end of file diff --git a/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml b/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml index 45df6975259..a72b8281401 100644 --- a/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Pages/Hugo.gitlab-ci.yml @@ -9,3 +9,9 @@ pages: - public only: - master + +test: + script: + - hugo + except: + - master diff --git a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml b/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml index 36918fc005a..d98cf94d635 100644 --- a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml @@ -1,11 +1,15 @@ -# Full project: https://gitlab.com/pages/jekyll +# Template project: https://gitlab.com/pages/jekyll +# Docs: https://docs.gitlab.com/ce/pages/ +# Jekyll version: 3.4.0 image: ruby:2.3 +before_script: +- bundle install + test: stage: test script: - - gem install jekyll - - jekyll build -d test + - bundle exec jekyll build -d test artifacts: paths: - test @@ -15,10 +19,10 @@ test: pages: stage: deploy script: - - gem install jekyll - - jekyll build -d public + - bundle exec jekyll build -d public artifacts: paths: - public only: - master +
\ No newline at end of file diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml index 7298ea73bab..574f9365f14 100644 --- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml @@ -12,6 +12,7 @@ stages: - review - staging - production + - cleanup build: stage: build @@ -61,7 +62,7 @@ review: - master stop_review: - stage: review + stage: cleanup variables: GIT_STRATEGY: none script: diff --git a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml index 249adbc9f4a..4d6f4e00ebb 100644 --- a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml @@ -12,6 +12,7 @@ stages: - review - staging - production + - cleanup build: stage: build @@ -61,7 +62,7 @@ review: - master stop_review: - stage: review + stage: cleanup variables: GIT_STRATEGY: none script: |