diff options
157 files changed, 2502 insertions, 2416 deletions
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 5d060165f4b..d0db7cde262 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -2,6 +2,7 @@ /* global Pager */ import Cookies from 'js-cookie'; +import { localTimeAgo } from './lib/utils/datetime_utility'; class Activities { constructor() { @@ -15,7 +16,7 @@ class Activities { } updateTooltips() { - gl.utils.localTimeAgo($('.js-timeago', '.content_list')); + localTimeAgo($('.js-timeago', '.content_list')); } reloadActivities() { diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 9b952ea7b60..be58392135c 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -4,6 +4,7 @@ /* global Pager */ import { pluralize } from './lib/utils/text_utility'; +import { localTimeAgo } from './lib/utils/datetime_utility'; export default (function () { const CommitsList = {}; @@ -91,7 +92,7 @@ export default (function () { $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`); } - gl.utils.localTimeAgo($processedData.find('.js-timeago')); + localTimeAgo($processedData.find('.js-timeago')); return processedData; }; diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 0ce467a3bd4..144caf1d278 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,4 +1,5 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */ +import { localTimeAgo } from './lib/utils/datetime_utility'; export default class Compare { constructor(opts) { @@ -81,7 +82,7 @@ export default class Compare { loading.hide(); $target.html(html); var className = '.' + $target[0].className.replace(' ', '.'); - gl.utils.localTimeAgo($('.js-timeago', className)); + localTimeAgo($('.js-timeago', className)); } }); } diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index cd20dde2951..74520675a7c 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -9,7 +9,7 @@ export default class ContextualSidebar { } initDomElements() { - this.$page = $('.page-with-sidebar'); + this.$page = $('.layout-page'); this.$sidebar = $('.nav-sidebar'); this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar); this.$overlay = $('.mobile-overlay'); diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue index b41d464475f..2a05c6f001e 100644 --- a/app/assets/javascripts/deploy_keys/components/key.vue +++ b/app/assets/javascripts/deploy_keys/components/key.vue @@ -1,5 +1,6 @@ <script> import actionBtn from './action_btn.vue'; + import { getTimeago } from '../../lib/utils/datetime_utility'; export default { props: { @@ -21,7 +22,7 @@ }, computed: { timeagoDate() { - return gl.utils.getTimeago().format(this.deployKey.created_at); + return getTimeago().format(this.deployKey.created_at); }, editDeployKeyPath() { return `${this.endpoint}/${this.deployKey.id}/edit`; diff --git a/app/assets/javascripts/diff_notes/models/discussion.js b/app/assets/javascripts/diff_notes/models/discussion.js index dc43e4b2cc7..1b8a9af9390 100644 --- a/app/assets/javascripts/diff_notes/models/discussion.js +++ b/app/assets/javascripts/diff_notes/models/discussion.js @@ -2,6 +2,7 @@ /* global NoteModel */ import Vue from 'vue'; +import { localTimeAgo } from '../../lib/utils/datetime_utility'; class DiscussionModel { constructor (discussionId) { @@ -71,7 +72,7 @@ class DiscussionModel { $(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html); } - gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`)); + localTimeAgo($('.js-timeago', `${discussionSelector}`)); } else { $discussionHeadline.remove(); } diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 299e43a4e90..12402fd645f 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -15,7 +15,7 @@ import GroupLabelSubscription from './group_label_subscription'; import BuildArtifacts from './build_artifacts'; import CILintEditor from './ci_lint_editor'; import groupsSelect from './groups_select'; -/* global Search */ +import Search from './search'; /* global Admin */ import NamespaceSelect from './namespace_select'; import NewCommitForm from './new_commit_form'; @@ -24,7 +24,7 @@ import projectAvatar from './project_avatar'; /* global MergeRequest */ import Compare from './compare'; import initCompareAutocomplete from './compare_autocomplete'; -/* global ProjectFindFile */ +import ProjectFindFile from './project_find_file'; import ProjectNew from './project_new'; import projectImport from './project_import'; import Labels from './labels'; @@ -91,6 +91,7 @@ import DueDateSelectors from './due_date_select'; import Diff from './diff'; import ProjectLabelSubscription from './project_label_subscription'; import ProjectVariables from './project_variables'; +import SearchAutocomplete from './search_autocomplete'; (function() { var Dispatcher; @@ -683,7 +684,7 @@ import ProjectVariables from './project_variables'; Dispatcher.prototype.initSearch = function() { // Only when search form is present if ($('.search').length) { - return new gl.SearchAutocomplete(); + return new SearchAutocomplete(); } }; diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue index 09cb79c1afd..58ba5aff7cf 100644 --- a/app/assets/javascripts/groups/components/item_actions.vue +++ b/app/assets/javascripts/groups/components/item_actions.vue @@ -1,7 +1,7 @@ <script> import { s__ } from '../../locale'; import tooltip from '../../vue_shared/directives/tooltip'; -import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; +import modal from '../../vue_shared/components/modal.vue'; import eventHub from '../event_hub'; import { COMMON_STR } from '../constants'; import Icon from '../../vue_shared/components/icon.vue'; @@ -9,7 +9,7 @@ import Icon from '../../vue_shared/components/icon.vue'; export default { components: { Icon, - PopupDialog, + modal, }, directives: { tooltip, @@ -27,7 +27,7 @@ export default { }, data() { return { - dialogStatus: false, + modalStatus: false, }; }, computed: { @@ -43,10 +43,10 @@ export default { }, methods: { onLeaveGroup() { - this.dialogStatus = true; + this.modalStatus = true; }, leaveGroup(leaveConfirmed) { - this.dialogStatus = false; + this.modalStatus = false; if (leaveConfirmed) { eventHub.$emit('leaveGroup', this.group, this.parentGroup); } @@ -82,8 +82,8 @@ export default { class="fa fa-sign-out" aria-hidden="true"/> </a> - <popup-dialog - v-show="dialogStatus" + <modal + v-show="modalStatus" :primary-button-label="__('Leave')" kind="warning" :title="__('Are you sure?')" diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index ba2b6737988..bf77b93b643 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -21,7 +21,7 @@ export default class IssuableBulkUpdateSidebar { } initDomElements() { - this.$page = $('.page-with-sidebar'); + this.$page = $('.layout-page'); this.$sidebar = $('.right-sidebar'); this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar'); this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide'); diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 91b5ef1c10a..411c820cc43 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -48,7 +48,7 @@ export default class Issue { }) .fail(() => new Flash(issueFailMessage)) .done((data) => { - const isClosedBadge = $('div.status-box-closed'); + const isClosedBadge = $('div.status-box-issue-closed'); const isOpenBadge = $('div.status-box-open'); const projectIssuesCounter = $('.issue_counter'); diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index fd1a50dd533..25ebe5314e0 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -9,7 +9,7 @@ import titleComponent from './title.vue'; import descriptionComponent from './description.vue'; import editedComponent from './edited.vue'; import formComponent from './form.vue'; -import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor'; +import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; export default { props: { @@ -152,7 +152,7 @@ export default { }, mixins: [ - RecaptchaDialogImplementor, + recaptchaModalImplementor, ], methods: { @@ -197,7 +197,7 @@ export default { }); }, - closeRecaptchaDialog() { + closeRecaptchaModal() { this.store.setFormState({ updateLoading: false, }); @@ -273,10 +273,10 @@ export default { :enable-autocomplete="enableAutocomplete" /> - <recaptcha-dialog + <recaptcha-modal v-show="showRecaptcha" :html="recaptchaHTML" - @close="closeRecaptchaDialog" + @close="closeRecaptchaModal" /> </div> <div v-else> diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index feb73481422..c3f2bf130bb 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,12 +1,12 @@ <script> import animateMixin from '../mixins/animate'; import TaskList from '../../task_list'; - import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor'; + import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; export default { mixins: [ animateMixin, - RecaptchaDialogImplementor, + recaptchaModalImplementor, ], props: { @@ -126,7 +126,7 @@ > </textarea> - <recaptcha-dialog + <recaptcha-modal v-show="showRecaptcha" :html="recaptchaHTML" @close="closeRecaptcha" diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index 52fe4ecd08b..4e577546551 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -53,7 +53,7 @@ <textarea id="issue-description" class="note-textarea js-gfm-input js-autosize markdown-area" - data-supports-quick-actionss="false" + data-supports-quick-actions="false" aria-label="Description" v-model="formState.description" ref="textarea" diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js index 06b0e02a870..198a7823381 100644 --- a/app/assets/javascripts/job.js +++ b/app/assets/javascripts/job.js @@ -3,6 +3,7 @@ import { visitUrl } from './lib/utils/url_utility'; import bp from './breakpoints'; import { bytesToKiB } from './lib/utils/number_utils'; import { setCiStatusFavicon } from './lib/utils/common_utils'; +import { timeFor } from './lib/utils/datetime_utility'; export default class Job { constructor(options) { @@ -261,7 +262,7 @@ export default class Job { if ($date.length) { const date = $date.text(); return $date.text( - gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '), + timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3'))), ); } } diff --git a/app/assets/javascripts/lib/utils/cache.js b/app/assets/javascripts/lib/utils/cache.js index 3141f1eeafc..596bd1e388a 100644 --- a/app/assets/javascripts/lib/utils/cache.js +++ b/app/assets/javascripts/lib/utils/cache.js @@ -1,4 +1,4 @@ -class Cache { +export default class Cache { constructor() { this.internalStorage = { }; } @@ -15,5 +15,3 @@ class Cache { delete this.internalStorage[key]; } } - -export default Cache; diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index 7a72509d234..9a61003ef30 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -1,3 +1,2 @@ -/* eslint-disable import/prefer-default-export */ export const BYTES_IN_KIB = 1024; export const HIDDEN_CLASS = 'hidden'; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d0578b230b1..198b5164c92 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,9 +1,6 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */ - import timeago from 'timeago.js'; import dateFormat from 'vendor/date.format'; import { pluralize } from './text_utility'; - import { lang, s__, @@ -12,121 +9,125 @@ import { window.timeago = timeago; window.dateFormat = dateFormat; -(function() { - (function(w) { - var base; - var timeagoInstance; +/** + * Given a date object returns the day of the week in English + * @param {date} date + * @returns {String} + */ +export const getDayName = date => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()]; - if (w.gl == null) { - w.gl = {}; - } - if ((base = w.gl).utils == null) { - base.utils = {}; - } - w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; +/** + * @example + * dateFormat('2017-12-05','mmm d, yyyy h:MMtt Z' ) -> "Dec 5, 2017 12:00am GMT+0000" + * @param {date} datetime + * @returns {String} + */ +export const formatDate = datetime => dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); - w.gl.utils.formatDate = function(datetime) { - return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); +let timeagoInstance; +/** + * Sets a timeago Instance + */ +export function getTimeago() { + if (!timeagoInstance) { + const localeRemaining = function getLocaleRemaining(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], + [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], + [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], + [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], + [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], + [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], + [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], + [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], + [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], + [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], + [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], + [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], + [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')], + ][index]; }; - - w.gl.utils.getDayName = function(date) { - return this.days[date.getDay()]; + const locale = function getLocale(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], + [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], + [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], + [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], + [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], + [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], + [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], + [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], + [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], + [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], + [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], + [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], + [s__('Timeago|%s years ago'), s__('Timeago|in %s years')], + ][index]; }; - w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) { - $timeagoEls.each((i, el) => { - if (setTimeago) { - // Recreate with custom template - $(el).tooltip({ - template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' - }); - } + timeago.register(lang, locale); + timeago.register(`${lang}-remaining`, localeRemaining); + timeagoInstance = timeago(); + } - el.classList.add('js-timeago-render'); - }); + return timeagoInstance; +} - gl.utils.renderTimeago($timeagoEls); - }; +/** + * For the given element, renders a timeago instance. + * @param {jQuery} $els + */ +export const renderTimeago = ($els) => { + const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); - w.gl.utils.getTimeago = function() { - var locale; - - if (!timeagoInstance) { - const localeRemaining = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], - [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], - [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], - [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], - [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], - [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], - [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], - [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], - [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], - [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], - [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], - [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], - [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')] - ][index]; - }; - locale = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], - [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], - [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], - [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], - [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], - [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], - [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], - [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], - [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], - [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], - [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], - [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], - [s__('Timeago|%s years ago'), s__('Timeago|in %s years')] - ][index]; - }; - - timeago.register(lang, locale); - timeago.register(`${lang}-remaining`, localeRemaining); - timeagoInstance = timeago(); - } - - return timeagoInstance; - }; + // timeago.js sets timeouts internally for each timeago value to be updated in real time + getTimeago().render(timeagoEls, lang); +}; - w.gl.utils.timeFor = function(time, suffix, expiredLabel) { - var timefor; - if (!time) { - return ''; - } - if (new Date(time) < new Date()) { - expiredLabel || (expiredLabel = s__('Timeago|Past due')); - timefor = expiredLabel; - } else { - timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim(); - } - return timefor; - }; +/** + * For the given elements, sets a tooltip with a formatted date. + * @param {jQuery} + * @param {Boolean} setTimeago + */ +export const localTimeAgo = ($timeagoEls, setTimeago = true) => { + $timeagoEls.each((i, el) => { + if (setTimeago) { + // Recreate with custom template + $(el).tooltip({ + template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', + }); + } - w.gl.utils.renderTimeago = function($els) { - const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); + el.classList.add('js-timeago-render'); + }); - // timeago.js sets timeouts internally for each timeago value to be updated in real time - gl.utils.getTimeago().render(timeagoEls, lang); - }; + renderTimeago($timeagoEls); +}; - w.gl.utils.getDayDifference = function(a, b) { - var millisecondsPerDay = 1000 * 60 * 60 * 24; - var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); - var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); +/** + * Returns remaining or passed time over the given time. + * @param {*} time + * @param {*} expiredLabel + */ +export const timeFor = (time, expiredLabel) => { + if (!time) { + return ''; + } + if (new Date(time) < new Date()) { + return expiredLabel || s__('Timeago|Past due'); + } + return getTimeago().format(time, `${lang}-remaining`).trim(); +}; - return Math.floor((date2 - date1) / millisecondsPerDay); - }; - })(window); -}).call(window); +export const getDayDifference = (a, b) => { + const millisecondsPerDay = 1000 * 60 * 60 * 24; + const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); + const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); + + return Math.floor((date2 - date1) / millisecondsPerDay); +}; /** * Port of ruby helper time_interval_in_words. @@ -162,3 +163,10 @@ export function dateInWords(date, abbreviated = false) { return `${monthName} ${date.getDate()}, ${year}`; } + +window.gl = window.gl || {}; +window.gl.utils = { + ...(window.gl.utils || {}), + getTimeago, + localTimeAgo, +}; diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 9e4047b6840..f99abeeeb6b 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -28,7 +28,7 @@ import './commit/image_file'; // lib/utils import { handleLocationHash } from './lib/utils/common_utils'; -import './lib/utils/datetime_utility'; +import { localTimeAgo, renderTimeago } from './lib/utils/datetime_utility'; import { getLocationHash, visitUrl } from './lib/utils/url_utility'; // behaviors @@ -60,15 +60,10 @@ import './notifications_dropdown'; import './notifications_form'; import './pager'; import './preview_markdown'; -import './project_find_file'; import './project_import'; import './projects_dropdown'; -import './projects_list'; -import './syntax_highlight'; import './render_gfm'; import './right_sidebar'; -import './search'; -import './search_autocomplete'; import initBreadcrumbs from './breadcrumb'; import './dispatcher'; @@ -125,7 +120,7 @@ $(function () { }); if (bootstrapBreakpoint === 'xs') { - const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar'); + const $rightSidebar = $('aside.right-sidebar, .layout-page'); $rightSidebar .removeClass('right-sidebar-expanded') @@ -185,13 +180,13 @@ $(function () { trigger: 'focus', // set the viewport to the main content, excluding the navigation bar, so // the navigation can't overlap the popover - viewport: '.page-with-sidebar' + viewport: '.layout-page' }); $('.trigger-submit').on('change', function () { return $(this).parents('form').submit(); // Form submitter }); - gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); + localTimeAgo($('abbr.timeago, .js-timeago'), true); // Disable form buttons while a form is submitting $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) { var buttons; @@ -280,7 +275,7 @@ $(function () { loadAwardsHandler(); new Aside(); - gl.utils.renderTimeago(); + renderTimeago(); $(document).trigger('init.scrolling-tabs'); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 17591829b76..94561d6b7c3 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -10,6 +10,7 @@ import './mixins/line_conflict_actions'; import './components/diff_file_editor'; import './components/inline_conflict_lines'; import './components/parallel_conflict_lines'; +import syntaxHighlight from '../syntax_highlight'; $(() => { const INTERACTIVE_RESOLVE_MODE = 'interactive'; @@ -53,7 +54,7 @@ $(() => { mergeConflictsStore.setLoadingState(false); this.$nextTick(() => { - $('.js-syntax-highlight').syntaxHighlight(); + syntaxHighlight($('.js-syntax-highlight')); }); }); }, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 589e65647ac..de84e28f915 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -14,6 +14,8 @@ import { import { getLocationHash } from './lib/utils/url_utility'; import initDiscussionTab from './image_diff/init_discussion_tab'; import Diff from './diff'; +import { localTimeAgo } from './lib/utils/datetime_utility'; +import syntaxHighlight from './syntax_highlight'; /* eslint-disable max-len */ // MergeRequestTabs @@ -247,7 +249,7 @@ import Diff from './diff'; url: `${source}.json`, success: (data) => { document.querySelector('div#commits').innerHTML = data.html; - gl.utils.localTimeAgo($('.js-timeago', 'div#commits')); + localTimeAgo($('.js-timeago', 'div#commits')); this.commitsLoaded = true; this.scrollToElement('#commits'); }, @@ -294,8 +296,8 @@ import Diff from './diff'; gl.diffNotesCompileComponents(); } - gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')); - $('#diffs .js-syntax-highlight').syntaxHighlight(); + localTimeAgo($('.js-timeago', 'div#diffs')); + syntaxHighlight($('#diffs .js-syntax-highlight')); if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) { this.expandViewContainer(); diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 74e5a4f1cea..2e5e818d61d 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -2,6 +2,7 @@ /* global Issuable */ /* global ListMilestone */ import _ from 'underscore'; +import { timeFor } from './lib/utils/datetime_utility'; (function() { this.MilestoneSelect = (function() { @@ -216,7 +217,7 @@ import _ from 'underscore'; $value.css('display', ''); if (data.milestone != null) { data.milestone.full_path = _this.currentProject.full_path; - data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); + data.milestone.remaining = timeFor(data.milestone.due_date); data.milestone.name = data.milestone.title; $value.html(milestoneLinkTemplate(data.milestone)); return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 2a570ac705e..042fe44e1c6 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -25,6 +25,7 @@ import Autosave from './autosave'; import TaskList from './task_list'; import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils'; import imageDiffHelper from './image_diff/helpers/index'; +import { localTimeAgo } from './lib/utils/datetime_utility'; window.autosize = Autosize; @@ -311,7 +312,7 @@ export default class Notes { setupNewNote($note) { // Update datetime format on the recent note - gl.utils.localTimeAgo($note.find('.js-timeago'), false); + localTimeAgo($note.find('.js-timeago'), false); this.collapseLongCommitList(); this.taskList.init(); @@ -463,7 +464,7 @@ export default class Notes { this.renderDiscussionAvatar(diffAvatarContainer, noteEntity); } - gl.utils.localTimeAgo($('.js-timeago'), false); + localTimeAgo($('.js-timeago'), false); Notes.checkMergeRequestStatus(); return this.updateNotesCount(1); } diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue index 0eaac8dd64f..78322f30685 100644 --- a/app/assets/javascripts/pipelines/components/empty_state.vue +++ b/app/assets/javascripts/pipelines/components/empty_state.vue @@ -1,36 +1,41 @@ <script> -export default { - props: { - helpPagePath: { - type: String, - required: true, + export default { + props: { + helpPagePath: { + type: String, + required: true, + }, + emptyStateSvgPath: { + type: String, + required: true, + }, }, - emptyStateSvgPath: { - type: String, - required: true, - }, - }, -}; + }; </script> - <template> <div class="row empty-state js-empty-state"> <div class="col-xs-12"> - <div class="svg-content"> - <img :src="emptyStateSvgPath"/> + <div class="svg-content svg-250"> + <img :src="emptyStateSvgPath" /> </div> </div> - <div class="col-xs-12 text-center"> + <div class="col-xs-12"> <div class="text-content"> - <h4>Build with confidence</h4> + <h4 class="text-center"> + {{ s__("Pipelines|Build with confidence") }} + </h4> <p> - Continous Integration can help catch bugs by running your tests automatically, - while Continuous Deployment can help you deliver code to your product environment. + {{ s__("Pipelines|Continous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment.") }} </p> - <a :href="helpPagePath" class="btn btn-info"> - Get started with Pipelines - </a> + <div class="text-center"> + <a + :href="helpPagePath" + class="btn btn-info" + > + {{ s__("Pipelines|Get started with Pipelines") }} + </a> + </div> </div> </div> </div> diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js index 923d9bfb248..3e4b6eeb5bf 100644 --- a/app/assets/javascripts/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/pipelines/pipelines_bundle.js @@ -1,6 +1,9 @@ import Vue from 'vue'; import PipelinesStore from './stores/pipelines_store'; import pipelinesComponent from './components/pipelines.vue'; +import Translate from '../vue_shared/translate'; + +Vue.use(Translate); document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#pipelines-list-vue', diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue index 6348a2e331d..78be6b6e884 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -1,5 +1,5 @@ <script> - import popupDialog from '../../../vue_shared/components/popup_dialog.vue'; + import modal from '../../../vue_shared/components/modal.vue'; import { __, s__, sprintf } from '../../../locale'; import csrf from '../../../lib/utils/csrf'; @@ -26,7 +26,7 @@ }; }, components: { - popupDialog, + modal, }, computed: { csrfToken() { @@ -89,7 +89,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), <template> <div> - <popup-dialog + <modal v-if="isOpen" :title="s__('Profiles|Delete your account?')" :text="text" @@ -134,7 +134,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), </form> </template> - </popup-dialog> + </modal> <button type="button" diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 19682b20a4a..0da32b4a3cc 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -2,169 +2,163 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; -(function() { - this.ProjectFindFile = (function() { - var highlighter; - - function ProjectFindFile(element1, options) { - this.element = element1; - this.options = options; - this.goToBlob = this.goToBlob.bind(this); - this.goToTree = this.goToTree.bind(this); - this.selectRowDown = this.selectRowDown.bind(this); - this.selectRowUp = this.selectRowUp.bind(this); - this.filePaths = {}; - this.inputElement = this.element.find(".file-finder-input"); - // init event - this.initEvent(); - // focus text input box - this.inputElement.focus(); - // load file list - this.load(this.options.url); +// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) +const highlighter = function(element, text, matches) { + var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; + lastIndex = 0; + highlightText = ""; + matchedChars = []; + for (j = 0, len = matches.length; j < len; j += 1) { + matchIndex = matches[j]; + unmatched = text.substring(lastIndex, matchIndex); + if (unmatched) { + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + matchedChars = []; + element.append(document.createTextNode(unmatched)); } - - ProjectFindFile.prototype.initEvent = function() { - this.inputElement.off("keyup"); - this.inputElement.on("keyup", (function(_this) { - return function(event) { - var oldValue, ref, target, value; - target = $(event.target); - value = target.val(); - oldValue = (ref = target.data("oldValue")) != null ? ref : ""; - if (value !== oldValue) { - target.data("oldValue", value); - _this.findFile(); - return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); - } - }; - })(this)); - }; - - ProjectFindFile.prototype.findFile = function() { - var result, searchText; - searchText = this.inputElement.val(); - result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; - return this.renderList(result, searchText); - // find file - }; + matchedChars.push(text[matchIndex]); + lastIndex = matchIndex + 1; + } + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + return element.append(document.createTextNode(text.substring(lastIndex))); +}; + +export default class ProjectFindFile { + constructor(element1, options) { + this.element = element1; + this.options = options; + this.goToBlob = this.goToBlob.bind(this); + this.goToTree = this.goToTree.bind(this); + this.selectRowDown = this.selectRowDown.bind(this); + this.selectRowUp = this.selectRowUp.bind(this); + this.filePaths = {}; + this.inputElement = this.element.find(".file-finder-input"); + // init event + this.initEvent(); + // focus text input box + this.inputElement.focus(); + // load file list + this.load(this.options.url); + } + + initEvent() { + this.inputElement.off("keyup"); + this.inputElement.on("keyup", (function(_this) { + return function(event) { + var oldValue, ref, target, value; + target = $(event.target); + value = target.val(); + oldValue = (ref = target.data("oldValue")) != null ? ref : ""; + if (value !== oldValue) { + target.data("oldValue", value); + _this.findFile(); + return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); + } + }; + })(this)); + } + + findFile() { + var result, searchText; + searchText = this.inputElement.val(); + result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; + return this.renderList(result, searchText); + // find file + } // files pathes load - ProjectFindFile.prototype.load = function(url) { - return $.ajax({ - url: url, - method: "get", - dataType: "json", - success: (function(_this) { - return function(data) { - _this.element.find(".loading").hide(); - _this.filePaths = data; - _this.findFile(); - return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); - }; - })(this) - }); - }; + load(url) { + return $.ajax({ + url: url, + method: "get", + dataType: "json", + success: (function(_this) { + return function(data) { + _this.element.find(".loading").hide(); + _this.filePaths = data; + _this.findFile(); + return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); + }; + })(this) + }); + } // render result - ProjectFindFile.prototype.renderList = function(filePaths, searchText) { - var blobItemUrl, filePath, html, i, j, len, matches, results; - this.element.find(".tree-table > tbody").empty(); - results = []; - for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { - filePath = filePaths[i]; - if (i === 20) { - break; - } - if (searchText) { - matches = fuzzaldrinPlus.match(filePath, searchText); - } - blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; - html = this.makeHtml(filePath, matches, blobItemUrl); - results.push(this.element.find(".tree-table > tbody").append(html)); - } - return results; - }; - - // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) - highlighter = function(element, text, matches) { - var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; - lastIndex = 0; - highlightText = ""; - matchedChars = []; - for (j = 0, len = matches.length; j < len; j += 1) { - matchIndex = matches[j]; - unmatched = text.substring(lastIndex, matchIndex); - if (unmatched) { - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); - } - matchedChars = []; - element.append(document.createTextNode(unmatched)); - } - matchedChars.push(text[matchIndex]); - lastIndex = matchIndex + 1; - } - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); + renderList(filePaths, searchText) { + var blobItemUrl, filePath, html, i, j, len, matches, results; + this.element.find(".tree-table > tbody").empty(); + results = []; + for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { + filePath = filePaths[i]; + if (i === 20) { + break; } - return element.append(document.createTextNode(text.substring(lastIndex))); - }; - - // make tbody row html - ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) { - var $tr; - $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); - if (matches) { - $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); - } else { - $tr.find("a").attr("href", blobItemUrl); - $tr.find(".str-truncated").text(filePath); + if (searchText) { + matches = fuzzaldrinPlus.match(filePath, searchText); } - return $tr; - }; - - ProjectFindFile.prototype.selectRow = function(type) { - var next, rows, selectedRow; - rows = this.element.find(".files-slider tr.tree-item"); - selectedRow = this.element.find(".files-slider tr.tree-item.selected"); - if (rows && rows.length > 0) { - if (selectedRow && selectedRow.length > 0) { - if (type === "UP") { - next = selectedRow.prev(); - } else if (type === "DOWN") { - next = selectedRow.next(); - } - if (next.length > 0) { - selectedRow.removeClass("selected"); - selectedRow = next; - } - } else { - selectedRow = rows.eq(0); + blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; + html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl); + results.push(this.element.find(".tree-table > tbody").append(html)); + } + return results; + } + + // make tbody row html + static makeHtml(filePath, matches, blobItemUrl) { + var $tr; + $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); + if (matches) { + $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); + } else { + $tr.find("a").attr("href", blobItemUrl); + $tr.find(".str-truncated").text(filePath); + } + return $tr; + } + + selectRow(type) { + var next, rows, selectedRow; + rows = this.element.find(".files-slider tr.tree-item"); + selectedRow = this.element.find(".files-slider tr.tree-item.selected"); + if (rows && rows.length > 0) { + if (selectedRow && selectedRow.length > 0) { + if (type === "UP") { + next = selectedRow.prev(); + } else if (type === "DOWN") { + next = selectedRow.next(); + } + if (next.length > 0) { + selectedRow.removeClass("selected"); + selectedRow = next; } - return selectedRow.addClass("selected").focus(); + } else { + selectedRow = rows.eq(0); } - }; - - ProjectFindFile.prototype.selectRowUp = function() { - return this.selectRow("UP"); - }; + return selectedRow.addClass("selected").focus(); + } + } - ProjectFindFile.prototype.selectRowDown = function() { - return this.selectRow("DOWN"); - }; + selectRowUp() { + return this.selectRow("UP"); + } - ProjectFindFile.prototype.goToTree = function() { - return location.href = this.options.treeUrl; - }; + selectRowDown() { + return this.selectRow("DOWN"); + } - ProjectFindFile.prototype.goToBlob = function() { - var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); + goToTree() { + return location.href = this.options.treeUrl; + } - if ($link.length) { - $link.get(0).click(); - } - }; + goToBlob() { + var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); - return ProjectFindFile; - })(); -}).call(window); + if ($link.length) { + $link.get(0).click(); + } + } +} diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js index c91a0d9ba41..5482c55f8bb 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/render_gfm.js @@ -1,12 +1,12 @@ import renderMath from './render_math'; import renderMermaid from './render_mermaid'; - +import syntaxHighlight from './syntax_highlight'; // Render Gitlab flavoured Markdown // // Delegates to syntax highlight and render math & mermaid diagrams. // $.fn.renderGFM = function renderGFM() { - this.find('.js-syntax-highlight').syntaxHighlight(); + syntaxHighlight(this.find('.js-syntax-highlight')); renderMath(this.find('.js-render-math')); renderMermaid(this.find('.js-render-mermaid')); return this; diff --git a/app/assets/javascripts/repo/components/new_dropdown/modal.vue b/app/assets/javascripts/repo/components/new_dropdown/modal.vue index ac1f613bb71..c191af7dec3 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/repo/components/new_dropdown/modal.vue @@ -1,7 +1,7 @@ <script> import { mapActions } from 'vuex'; import { __ } from '../../../locale'; - import popupDialog from '../../../vue_shared/components/popup_dialog.vue'; + import modal from '../../../vue_shared/components/modal.vue'; export default { props: { @@ -20,7 +20,7 @@ }; }, components: { - popupDialog, + modal, }, methods: { ...mapActions([ @@ -68,7 +68,7 @@ </script> <template> - <popup-dialog + <modal :title="modalTitle" :primary-button-label="buttonLabel" kind="success" @@ -94,5 +94,5 @@ </div> </fieldset> </form> - </popup-dialog> + </modal> </template> diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue index d3344d0c8dc..4e0178072cb 100644 --- a/app/assets/javascripts/repo/components/repo_commit_section.vue +++ b/app/assets/javascripts/repo/components/repo_commit_section.vue @@ -2,12 +2,12 @@ import { mapGetters, mapState, mapActions } from 'vuex'; import tooltip from '../../vue_shared/directives/tooltip'; import icon from '../../vue_shared/components/icon.vue'; -import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; +import modal from '../../vue_shared/components/modal.vue'; import commitFilesList from './commit_sidebar/list.vue'; export default { components: { - PopupDialog, + modal, icon, commitFilesList, }, @@ -16,7 +16,7 @@ export default { }, data() { return { - showNewBranchDialog: false, + showNewBranchModal: false, submitCommitsLoading: false, startNewMR: false, commitMessage: '', @@ -58,7 +58,7 @@ export default { start_branch: createNewBranch ? this.currentBranch : undefined, }; - this.showNewBranchDialog = false; + this.showNewBranchModal = false; this.submitCommitsLoading = true; this.commitChanges({ payload, newMr: this.startNewMR }) @@ -76,7 +76,7 @@ export default { this.checkCommitStatus() .then((branchChanged) => { if (branchChanged) { - this.showNewBranchDialog = true; + this.showNewBranchModal = true; } else { this.makeCommit(); } @@ -99,13 +99,13 @@ export default { 'is-collapsed': collapsed, }" > - <popup-dialog - v-if="showNewBranchDialog" + <modal + v-if="showNewBranchModal" :primary-button-label="__('Create new branch')" kind="primary" :title="__('Branch has changed')" :text="__('This branch has changed since you started editing. Would you like to create a new branch?')" - @toggle="showNewBranchDialog = false" + @toggle="showNewBranchModal = false" @submit="makeCommit(true)" /> <button diff --git a/app/assets/javascripts/repo/components/repo_edit_button.vue b/app/assets/javascripts/repo/components/repo_edit_button.vue index 6c1bb4b8566..37bd9003e96 100644 --- a/app/assets/javascripts/repo/components/repo_edit_button.vue +++ b/app/assets/javascripts/repo/components/repo_edit_button.vue @@ -1,10 +1,10 @@ <script> import { mapGetters, mapActions, mapState } from 'vuex'; -import popupDialog from '../../vue_shared/components/popup_dialog.vue'; +import modal from '../../vue_shared/components/modal.vue'; export default { components: { - popupDialog, + modal, }, computed: { ...mapState([ @@ -43,7 +43,7 @@ export default { {{buttonLabel}} </span> </button> - <popup-dialog + <modal v-if="discardPopupOpen" class="text-left" :primary-button-label="__('Discard changes')" diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/repo/components/repo_preview.vue index 6ce9267f598..425c55fafb5 100644 --- a/app/assets/javascripts/repo/components/repo_preview.vue +++ b/app/assets/javascripts/repo/components/repo_preview.vue @@ -1,6 +1,7 @@ <script> /* global LineHighlighter */ import { mapGetters } from 'vuex'; +import syntaxHighlight from '../../syntax_highlight'; export default { computed: { @@ -13,7 +14,7 @@ export default { }, methods: { highlightFile() { - $(this.$el).find('.file-content').syntaxHighlight(); + syntaxHighlight($(this.$el).find('.file-content')); }, }, mounted() { diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index fa7f6825d7e..ec85b8b6529 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -42,11 +42,11 @@ import Cookies from 'js-cookie'; if ($thisIcon.hasClass('fa-angle-double-right')) { $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); } else { $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); if (gl.lazyLoader) gl.lazyLoader.loadCheck(); } @@ -173,7 +173,7 @@ import Cookies from 'js-cookie'; Sidebar.prototype.setCollapseAfterUpdate = function($block) { $block.addClass('collapse-after-update'); - return $('.page-with-sidebar').addClass('with-overlay'); + return $('.layout-page').addClass('with-overlay'); }; Sidebar.prototype.onSidebarDropdownHidden = function(e) { @@ -187,7 +187,7 @@ import Cookies from 'js-cookie'; Sidebar.prototype.sidebarDropdownHidden = function($block) { if ($block.hasClass('collapse-after-update')) { $block.removeClass('collapse-after-update'); - $('.page-with-sidebar').removeClass('with-overlay'); + $('.layout-page').removeClass('with-overlay'); return this.toggleSidebar('hide'); } }; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 07fee53d814..363322af47a 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -1,118 +1,113 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */ import Flash from './flash'; import Api from './api'; -(function() { - this.Search = (function() { - function Search() { - var $groupDropdown, $projectDropdown; - $groupDropdown = $('.js-search-group-dropdown'); - $projectDropdown = $('.js-search-project-dropdown'); - this.groupId = $groupDropdown.data('group-id'); - this.eventListeners(); - $groupDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'group_id', - search: { - fields: ['full_name'] - }, - data: function(term, callback) { - return Api.groups(term, {}, function(data) { +export default class Search { + constructor() { + const $groupDropdown = $('.js-search-group-dropdown'); + const $projectDropdown = $('.js-search-project-dropdown'); + + this.searchInput = '.js-search-input'; + this.searchClear = '.js-search-clear'; + + this.groupId = $groupDropdown.data('group-id'); + this.eventListeners(); + + $groupDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'group_id', + search: { + fields: ['full_name'], + }, + data(term, callback) { + return Api.groups(term, {}, (data) => { + data.unshift({ + full_name: 'Any', + }); + data.splice(1, 0, 'divider'); + return callback(data); + }); + }, + id(obj) { + return obj.id; + }, + text(obj) { + return obj.full_name; + }, + toggleLabel(obj) { + return `${($groupDropdown.data('default-label'))} ${obj.full_name}`; + }, + clicked: () => Search.submitSearch(), + }); + + $projectDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'project_id', + search: { + fields: ['name'], + }, + data: (term, callback) => { + this.getProjectsData(term) + .then((data) => { data.unshift({ - full_name: 'Any' + name_with_namespace: 'Any', }); data.splice(1, 0, 'divider'); - return callback(data); - }); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.full_name; - }, - toggleLabel: function(obj) { - return ($groupDropdown.data('default-label')) + " " + obj.full_name; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) - }); - $projectDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'project_id', - search: { - fields: ['name'] - }, - data: (term, callback) => { - this.getProjectsData(term) - .then((data) => { - data.unshift({ - name_with_namespace: 'Any' - }); - data.splice(1, 0, 'divider'); - return data; - }) - .then(data => callback(data)) - .catch(() => new Flash('Error fetching projects')); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.name_with_namespace; - }, - toggleLabel: function(obj) { - return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) - }); - } + return data; + }) + .then(data => callback(data)) + .catch(() => new Flash('Error fetching projects')); + }, + id(obj) { + return obj.id; + }, + text(obj) { + return obj.name_with_namespace; + }, + toggleLabel(obj) { + return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`; + }, + clicked: () => Search.submitSearch(), + }); + } - Search.prototype.eventListeners = function() { - $(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp); - return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField); - }; + eventListeners() { + $(document) + .off('keyup', this.searchInput) + .on('keyup', this.searchInput, this.searchKeyUp); + $(document) + .off('click', this.searchClear) + .on('click', this.searchClear, this.clearSearchField.bind(this)); + } - Search.prototype.submitSearch = function() { - return $('.js-search-form').submit(); - }; + static submitSearch() { + return $('.js-search-form').submit(); + } - Search.prototype.searchKeyUp = function() { - var $input; - $input = $(this); - if ($input.val() === '') { - return $('.js-search-clear').addClass('hidden'); - } else { - return $('.js-search-clear').removeClass('hidden'); - } - }; - - Search.prototype.clearSearchField = function() { - return $('.js-search-input').val('').trigger('keyup').focus(); - }; + searchKeyUp() { + const $input = $(this); + if ($input.val() === '') { + $('.js-search-clear').addClass('hidden'); + } else { + $('.js-search-clear').removeClass('hidden'); + } + } - Search.prototype.getProjectsData = function(term) { - return new Promise((resolve) => { - if (this.groupId) { - Api.groupProjects(this.groupId, term, resolve); - } else { - Api.projects(term, { - order_by: 'id', - }, resolve); - } - }); - }; + clearSearchField() { + return $(this.searchInput).val('').trigger('keyup').focus(); + } - return Search; - })(); -}).call(window); + getProjectsData(term) { + return new Promise((resolve) => { + if (this.groupId) { + Api.groupProjects(this.groupId, term, resolve); + } else { + Api.projects(term, { + order_by: 'id', + }, resolve); + } + }); + } +} diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index e40a3596200..98b524f7e3f 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -8,448 +8,445 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '. * When the user clicks `x` button it cleans the input and closes the dropdown. */ -((global) => { - const KEYCODE = { - ESCAPE: 27, - BACKSPACE: 8, - ENTER: 13, - UP: 38, - DOWN: 40, - }; - - class SearchAutocomplete { - constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { - this.bindEventContext(); - this.wrap = wrap || $('.search'); - this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); - this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); - this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); - this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); - this.dropdown = this.wrap.find('.dropdown'); - this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); - this.dropdownContent = this.dropdown.find('.dropdown-content'); - this.locationBadgeEl = this.getElement('.location-badge'); - this.scopeInputEl = this.getElement('#scope'); - this.searchInput = this.getElement('.search-input'); - this.projectInputEl = this.getElement('#search_project_id'); - this.groupInputEl = this.getElement('#group_id'); - this.searchCodeInputEl = this.getElement('#search_code'); - this.repositoryInputEl = this.getElement('#repository_ref'); - this.clearInput = this.getElement('.js-clear-input'); - this.saveOriginalState(); - - // Only when user is logged in - if (gon.current_user_id) { - this.createAutocomplete(); - } +const KEYCODE = { + ESCAPE: 27, + BACKSPACE: 8, + ENTER: 13, + UP: 38, + DOWN: 40, +}; + +function setSearchOptions() { + var $projectOptionsDataEl = $('.js-search-project-options'); + var $groupOptionsDataEl = $('.js-search-group-options'); + var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); + + if ($projectOptionsDataEl.length) { + gl.projectOptions = gl.projectOptions || {}; + + var projectPath = $projectOptionsDataEl.data('project-path'); + + gl.projectOptions[projectPath] = { + name: $projectOptionsDataEl.data('name'), + issuesPath: $projectOptionsDataEl.data('issues-path'), + issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), + mrPath: $projectOptionsDataEl.data('mr-path'), + }; + } - this.searchInput.addClass('disabled'); - this.saveTextLength(); - this.bindEvents(); - this.dropdownToggle.dropdown(); - } + if ($groupOptionsDataEl.length) { + gl.groupOptions = gl.groupOptions || {}; - // Finds an element inside wrapper element - bindEventContext() { - this.onSearchInputBlur = this.onSearchInputBlur.bind(this); - this.onClearInputClick = this.onClearInputClick.bind(this); - this.onSearchInputFocus = this.onSearchInputFocus.bind(this); - this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); - this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); - } - getElement(selector) { - return this.wrap.find(selector); - } + var groupPath = $groupOptionsDataEl.data('group-path'); - saveOriginalState() { - return this.originalState = this.serializeState(); - } + gl.groupOptions[groupPath] = { + name: $groupOptionsDataEl.data('name'), + issuesPath: $groupOptionsDataEl.data('issues-path'), + mrPath: $groupOptionsDataEl.data('mr-path'), + }; + } - saveTextLength() { - return this.lastTextLength = this.searchInput.val().length; + if ($dashboardOptionsDataEl.length) { + gl.dashboardOptions = { + issuesPath: $dashboardOptionsDataEl.data('issues-path'), + mrPath: $dashboardOptionsDataEl.data('mr-path'), + }; + } +} + +export default class SearchAutocomplete { + constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { + setSearchOptions(); + this.bindEventContext(); + this.wrap = wrap || $('.search'); + this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); + this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); + this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); + this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); + this.dropdown = this.wrap.find('.dropdown'); + this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); + this.dropdownContent = this.dropdown.find('.dropdown-content'); + this.locationBadgeEl = this.getElement('.location-badge'); + this.scopeInputEl = this.getElement('#scope'); + this.searchInput = this.getElement('.search-input'); + this.projectInputEl = this.getElement('#search_project_id'); + this.groupInputEl = this.getElement('#group_id'); + this.searchCodeInputEl = this.getElement('#search_code'); + this.repositoryInputEl = this.getElement('#repository_ref'); + this.clearInput = this.getElement('.js-clear-input'); + this.saveOriginalState(); + + // Only when user is logged in + if (gon.current_user_id) { + this.createAutocomplete(); } - createAutocomplete() { - return this.searchInput.glDropdown({ - filterInputBlur: false, - filterable: true, - filterRemote: true, - highlight: true, - enterCallback: false, - filterInput: 'input#search', - search: { - fields: ['text'], - }, - id: this.getSearchText, - data: this.getData.bind(this), - selectable: true, - clicked: this.onClick.bind(this), - }); + this.searchInput.addClass('disabled'); + this.saveTextLength(); + this.bindEvents(); + this.dropdownToggle.dropdown(); + } + + // Finds an element inside wrapper element + bindEventContext() { + this.onSearchInputBlur = this.onSearchInputBlur.bind(this); + this.onClearInputClick = this.onClearInputClick.bind(this); + this.onSearchInputFocus = this.onSearchInputFocus.bind(this); + this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); + this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); + } + getElement(selector) { + return this.wrap.find(selector); + } + + saveOriginalState() { + return this.originalState = this.serializeState(); + } + + saveTextLength() { + return this.lastTextLength = this.searchInput.val().length; + } + + createAutocomplete() { + return this.searchInput.glDropdown({ + filterInputBlur: false, + filterable: true, + filterRemote: true, + highlight: true, + enterCallback: false, + filterInput: 'input#search', + search: { + fields: ['text'], + }, + id: this.getSearchText, + data: this.getData.bind(this), + selectable: true, + clicked: this.onClick.bind(this), + }); + } + + getSearchText(selectedObject, el) { + return selectedObject.id ? selectedObject.text : ''; + } + + getData(term, callback) { + if (!term) { + const contents = this.getCategoryContents(); + if (contents) { + this.searchInput.data('glDropdown').filter.options.callback(contents); + this.enableAutocomplete(); + } + return; } - getSearchText(selectedObject, el) { - return selectedObject.id ? selectedObject.text : ''; + // Prevent multiple ajax calls + if (this.loadingSuggestions) { + return; } - getData(term, callback) { - if (!term) { - const contents = this.getCategoryContents(); - if (contents) { - this.searchInput.data('glDropdown').filter.options.callback(contents); - this.enableAutocomplete(); - } - return; - } + this.loadingSuggestions = true; - // Prevent multiple ajax calls - if (this.loadingSuggestions) { + return $.get(this.autocompletePath, { + project_id: this.projectId, + project_ref: this.projectRef, + term: term, + }, (response) => { + var firstCategory, i, lastCategory, len, suggestion; + // Hide dropdown menu if no suggestions returns + if (!response.length) { + this.disableAutocomplete(); return; } - this.loadingSuggestions = true; - - return $.get(this.autocompletePath, { - project_id: this.projectId, - project_ref: this.projectRef, - term: term, - }, (response) => { - var firstCategory, i, lastCategory, len, suggestion; - // Hide dropdown menu if no suggestions returns - if (!response.length) { - this.disableAutocomplete(); - return; - } - - const data = []; - // List results - firstCategory = true; - for (i = 0, len = response.length; i < len; i += 1) { - suggestion = response[i]; - // Add group header before list each group - if (lastCategory !== suggestion.category) { - if (!firstCategory) { - data.push('separator'); - } - if (firstCategory) { - firstCategory = false; - } - data.push({ - header: suggestion.category, - }); - lastCategory = suggestion.category; + const data = []; + // List results + firstCategory = true; + for (i = 0, len = response.length; i < len; i += 1) { + suggestion = response[i]; + // Add group header before list each group + if (lastCategory !== suggestion.category) { + if (!firstCategory) { + data.push('separator'); + } + if (firstCategory) { + firstCategory = false; } data.push({ - id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, - category: suggestion.category, - text: suggestion.label, - url: suggestion.url, - }); - } - // Add option to proceed with the search - if (data.length) { - data.push('separator'); - data.push({ - text: "Result name contains \"" + term + "\"", - url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()), + header: suggestion.category, }); + lastCategory = suggestion.category; } - return callback(data); - }) - .always(() => { this.loadingSuggestions = false; }); - } - - getCategoryContents() { - const userId = gon.current_user_id; - const userName = gon.current_username; - const { projectOptions, groupOptions, dashboardOptions } = gl; - - // Get options - let options; - if (isInGroupsPage() && groupOptions) { - options = groupOptions[getGroupSlug()]; - } else if (isInProjectPage() && projectOptions) { - options = projectOptions[getProjectSlug()]; - } else if (dashboardOptions) { - options = dashboardOptions; + data.push({ + id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, + category: suggestion.category, + text: suggestion.label, + url: suggestion.url, + }); } - - const { issuesPath, mrPath, name, issuesDisabled } = options; - const baseItems = []; - - if (name) { - baseItems.push({ - header: `${name}`, + // Add option to proceed with the search + if (data.length) { + data.push('separator'); + data.push({ + text: "Result name contains \"" + term + "\"", + url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()), }); } + return callback(data); + }) + .always(() => { this.loadingSuggestions = false; }); + } - const issueItems = [ - { - text: 'Issues assigned to me', - url: `${issuesPath}/?assignee_username=${userName}`, - }, - { - text: "Issues I've created", - url: `${issuesPath}/?author_username=${userName}`, - }, - ]; - const mergeRequestItems = [ - { - text: 'Merge requests assigned to me', - url: `${mrPath}/?assignee_username=${userName}`, - }, - { - text: "Merge requests I've created", - url: `${mrPath}/?author_username=${userName}`, - }, - ]; - - let items; - if (issuesDisabled) { - items = baseItems.concat(mergeRequestItems); - } else { - items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems); - } - return items; + getCategoryContents() { + const userId = gon.current_user_id; + const userName = gon.current_username; + const { projectOptions, groupOptions, dashboardOptions } = gl; + + // Get options + let options; + if (isInGroupsPage() && groupOptions) { + options = groupOptions[getGroupSlug()]; + } else if (isInProjectPage() && projectOptions) { + options = projectOptions[getProjectSlug()]; + } else if (dashboardOptions) { + options = dashboardOptions; } - serializeState() { - return { - // Search Criteria - search_project_id: this.projectInputEl.val(), - group_id: this.groupInputEl.val(), - search_code: this.searchCodeInputEl.val(), - repository_ref: this.repositoryInputEl.val(), - scope: this.scopeInputEl.val(), - // Location badge - _location: this.locationBadgeEl.text(), - }; + const { issuesPath, mrPath, name, issuesDisabled } = options; + const baseItems = []; + + if (name) { + baseItems.push({ + header: `${name}`, + }); } - bindEvents() { - this.searchInput.on('keydown', this.onSearchInputKeyDown); - this.searchInput.on('keyup', this.onSearchInputKeyUp); - this.searchInput.on('focus', this.onSearchInputFocus); - this.searchInput.on('blur', this.onSearchInputBlur); - this.clearInput.on('click', this.onClearInputClick); - this.locationBadgeEl.on('click', () => this.searchInput.focus()); + const issueItems = [ + { + text: 'Issues assigned to me', + url: `${issuesPath}/?assignee_username=${userName}`, + }, + { + text: "Issues I've created", + url: `${issuesPath}/?author_username=${userName}`, + }, + ]; + const mergeRequestItems = [ + { + text: 'Merge requests assigned to me', + url: `${mrPath}/?assignee_username=${userName}`, + }, + { + text: "Merge requests I've created", + url: `${mrPath}/?author_username=${userName}`, + }, + ]; + + let items; + if (issuesDisabled) { + items = baseItems.concat(mergeRequestItems); + } else { + items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems); } + return items; + } - enableAutocomplete() { - // No need to enable anything if user is not logged in - if (!gon.current_user_id) { - return; - } + serializeState() { + return { + // Search Criteria + search_project_id: this.projectInputEl.val(), + group_id: this.groupInputEl.val(), + search_code: this.searchCodeInputEl.val(), + repository_ref: this.repositoryInputEl.val(), + scope: this.scopeInputEl.val(), + // Location badge + _location: this.locationBadgeEl.text(), + }; + } - // If the dropdown is closed, we'll open it - if (!this.dropdown.hasClass('open')) { - this.loadingSuggestions = false; - this.dropdownToggle.dropdown('toggle'); - return this.searchInput.removeClass('disabled'); - } + bindEvents() { + this.searchInput.on('keydown', this.onSearchInputKeyDown); + this.searchInput.on('keyup', this.onSearchInputKeyUp); + this.searchInput.on('focus', this.onSearchInputFocus); + this.searchInput.on('blur', this.onSearchInputBlur); + this.clearInput.on('click', this.onClearInputClick); + this.locationBadgeEl.on('click', () => this.searchInput.focus()); + } + + enableAutocomplete() { + // No need to enable anything if user is not logged in + if (!gon.current_user_id) { + return; } - // Saves last length of the entered text - onSearchInputKeyDown() { - return this.saveTextLength(); + // If the dropdown is closed, we'll open it + if (!this.dropdown.hasClass('open')) { + this.loadingSuggestions = false; + this.dropdownToggle.dropdown('toggle'); + return this.searchInput.removeClass('disabled'); } + } - onSearchInputKeyUp(e) { - switch (e.keyCode) { - case KEYCODE.BACKSPACE: - // when trying to remove the location badge - if (this.lastTextLength === 0 && this.badgePresent()) { - this.removeLocationBadge(); - } - // When removing the last character and no badge is present - if (this.lastTextLength === 1) { - this.disableAutocomplete(); - } - // When removing any character from existin value - if (this.lastTextLength > 1) { - this.enableAutocomplete(); - } - break; - case KEYCODE.ESCAPE: - this.restoreOriginalState(); - break; - case KEYCODE.ENTER: + // Saves last length of the entered text + onSearchInputKeyDown() { + return this.saveTextLength(); + } + + onSearchInputKeyUp(e) { + switch (e.keyCode) { + case KEYCODE.BACKSPACE: + // when trying to remove the location badge + if (this.lastTextLength === 0 && this.badgePresent()) { + this.removeLocationBadge(); + } + // When removing the last character and no badge is present + if (this.lastTextLength === 1) { + this.disableAutocomplete(); + } + // When removing any character from existin value + if (this.lastTextLength > 1) { + this.enableAutocomplete(); + } + break; + case KEYCODE.ESCAPE: + this.restoreOriginalState(); + break; + case KEYCODE.ENTER: + this.disableAutocomplete(); + break; + case KEYCODE.UP: + case KEYCODE.DOWN: + return; + default: + // Handle the case when deleting the input value other than backspace + // e.g. Pressing ctrl + backspace or ctrl + x + if (this.searchInput.val() === '') { this.disableAutocomplete(); - break; - case KEYCODE.UP: - case KEYCODE.DOWN: - return; - default: - // Handle the case when deleting the input value other than backspace - // e.g. Pressing ctrl + backspace or ctrl + x - if (this.searchInput.val() === '') { - this.disableAutocomplete(); - } else { - // We should display the menu only when input is not empty - if (e.keyCode !== KEYCODE.ENTER) { - this.enableAutocomplete(); - } + } else { + // We should display the menu only when input is not empty + if (e.keyCode !== KEYCODE.ENTER) { + this.enableAutocomplete(); } - } - this.wrap.toggleClass('has-value', !!e.target.value); + } } + this.wrap.toggleClass('has-value', !!e.target.value); + } - onSearchInputFocus() { - this.isFocused = true; - this.wrap.addClass('search-active'); - if (this.getValue() === '') { - return this.getData(); - } + onSearchInputFocus() { + this.isFocused = true; + this.wrap.addClass('search-active'); + if (this.getValue() === '') { + return this.getData(); } + } - getValue() { - return this.searchInput.val(); - } + getValue() { + return this.searchInput.val(); + } - onClearInputClick(e) { - e.preventDefault(); - this.wrap.toggleClass('has-value', !!e.target.value); - return this.searchInput.val('').focus(); - } + onClearInputClick(e) { + e.preventDefault(); + this.wrap.toggleClass('has-value', !!e.target.value); + return this.searchInput.val('').focus(); + } - onSearchInputBlur(e) { - this.isFocused = false; - this.wrap.removeClass('search-active'); - // If input is blank then restore state - if (this.searchInput.val() === '') { - return this.restoreOriginalState(); - } + onSearchInputBlur(e) { + this.isFocused = false; + this.wrap.removeClass('search-active'); + // If input is blank then restore state + if (this.searchInput.val() === '') { + return this.restoreOriginalState(); } + } - addLocationBadge(item) { - var badgeText, category, value; - category = item.category != null ? item.category + ": " : ''; - value = item.value != null ? item.value : ''; - badgeText = "" + category + value; - this.locationBadgeEl.text(badgeText).show(); - return this.wrap.addClass('has-location-badge'); - } + addLocationBadge(item) { + var badgeText, category, value; + category = item.category != null ? item.category + ": " : ''; + value = item.value != null ? item.value : ''; + badgeText = "" + category + value; + this.locationBadgeEl.text(badgeText).show(); + return this.wrap.addClass('has-location-badge'); + } - hasLocationBadge() { - return this.wrap.is('.has-location-badge'); - } + hasLocationBadge() { + return this.wrap.is('.has-location-badge'); + } - restoreOriginalState() { - var i, input, inputs, len; - inputs = Object.keys(this.originalState); - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - this.getElement("#" + input).val(this.originalState[input]); - } - if (this.originalState._location === '') { - return this.locationBadgeEl.hide(); - } else { - return this.addLocationBadge({ - value: this.originalState._location, - }); - } + restoreOriginalState() { + var i, input, inputs, len; + inputs = Object.keys(this.originalState); + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + this.getElement("#" + input).val(this.originalState[input]); } - - badgePresent() { - return this.locationBadgeEl.length; + if (this.originalState._location === '') { + return this.locationBadgeEl.hide(); + } else { + return this.addLocationBadge({ + value: this.originalState._location, + }); } + } - resetSearchState() { - var i, input, inputs, len, results; - inputs = Object.keys(this.originalState); - results = []; - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - // _location isnt a input - if (input === '_location') { - break; - } - results.push(this.getElement("#" + input).val('')); + badgePresent() { + return this.locationBadgeEl.length; + } + + resetSearchState() { + var i, input, inputs, len, results; + inputs = Object.keys(this.originalState); + results = []; + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + // _location isnt a input + if (input === '_location') { + break; } - return results; + results.push(this.getElement("#" + input).val('')); } + return results; + } - removeLocationBadge() { - this.locationBadgeEl.hide(); - this.resetSearchState(); - this.wrap.removeClass('has-location-badge'); - return this.disableAutocomplete(); - } + removeLocationBadge() { + this.locationBadgeEl.hide(); + this.resetSearchState(); + this.wrap.removeClass('has-location-badge'); + return this.disableAutocomplete(); + } - disableAutocomplete() { - if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { - this.searchInput.addClass('disabled'); - this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); - this.restoreMenu(); - } + disableAutocomplete() { + if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { + this.searchInput.addClass('disabled'); + this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); + this.restoreMenu(); } + } - restoreMenu() { - var html; - html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>'; - return this.dropdownContent.html(html); - } + restoreMenu() { + var html; + html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>'; + return this.dropdownContent.html(html); + } - onClick(item, $el, e) { - if (location.pathname.indexOf(item.url) !== -1) { - if (!e.metaKey) e.preventDefault(); - if (!this.badgePresent) { - if (item.category === 'Projects') { - this.projectInputEl.val(item.id); - this.addLocationBadge({ - value: 'This project', - }); - } - if (item.category === 'Groups') { - this.groupInputEl.val(item.id); - this.addLocationBadge({ - value: 'This group', - }); - } + onClick(item, $el, e) { + if (location.pathname.indexOf(item.url) !== -1) { + if (!e.metaKey) e.preventDefault(); + if (!this.badgePresent) { + if (item.category === 'Projects') { + this.projectInputEl.val(item.id); + this.addLocationBadge({ + value: 'This project', + }); + } + if (item.category === 'Groups') { + this.groupInputEl.val(item.id); + this.addLocationBadge({ + value: 'This group', + }); } - $el.removeClass('is-active'); - this.disableAutocomplete(); - return this.searchInput.val('').focus(); } + $el.removeClass('is-active'); + this.disableAutocomplete(); + return this.searchInput.val('').focus(); } } - - global.SearchAutocomplete = SearchAutocomplete; - - $(function() { - var $projectOptionsDataEl = $('.js-search-project-options'); - var $groupOptionsDataEl = $('.js-search-group-options'); - var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); - - if ($projectOptionsDataEl.length) { - gl.projectOptions = gl.projectOptions || {}; - - var projectPath = $projectOptionsDataEl.data('project-path'); - - gl.projectOptions[projectPath] = { - name: $projectOptionsDataEl.data('name'), - issuesPath: $projectOptionsDataEl.data('issues-path'), - issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), - mrPath: $projectOptionsDataEl.data('mr-path'), - }; - } - - if ($groupOptionsDataEl.length) { - gl.groupOptions = gl.groupOptions || {}; - - var groupPath = $groupOptionsDataEl.data('group-path'); - - gl.groupOptions[groupPath] = { - name: $groupOptionsDataEl.data('name'), - issuesPath: $groupOptionsDataEl.data('issues-path'), - mrPath: $groupOptionsDataEl.data('mr-path'), - }; - } - - if ($dashboardOptionsDataEl.length) { - gl.dashboardOptions = { - issuesPath: $dashboardOptionsDataEl.data('issues-path'), - mrPath: $dashboardOptionsDataEl.data('mr-path'), - }; - } - }); -})(window.gl || (window.gl = {})); +} diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 3f811c59cb9..95e51bc4e7a 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -2,6 +2,7 @@ import FilesCommentButton from './files_comment_button'; import imageDiffHelper from './image_diff/helpers/index'; +import syntaxHighlight from './syntax_highlight'; const WRAPPER = '<div class="diff-content"></div>'; const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; @@ -64,7 +65,7 @@ export default class SingleFileDiff { _this.loadingContent.hide(); if (data.html) { _this.content = $(data.html); - _this.content.syntaxHighlight(); + syntaxHighlight(_this.content); } else { _this.hasError = true; _this.content = $(ERROR_HTML); diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 662d6b36c16..62bdef76c55 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -10,17 +10,15 @@ // <div class="js-syntax-highlight"></div> // -$.fn.syntaxHighlight = function() { - var $children; - - if ($(this).hasClass('js-syntax-highlight')) { +export default function syntaxHighlight(el) { + if ($(el).hasClass('js-syntax-highlight')) { // Given the element itself, apply highlighting - return $(this).addClass(gon.user_color_scheme); + return $(el).addClass(gon.user_color_scheme); } else { // Given a parent element, recurse to any of its applicable children - $children = $(this).find('.js-syntax-highlight'); + const $children = $(el).find('.js-syntax-highlight'); if ($children.length) { - return $children.syntaxHighlight(); + return syntaxHighlight($children); } } -}; +} diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 5e947769f8a..4fa8c680580 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,5 +1,6 @@ import _ from 'underscore'; import d3 from 'd3'; +import { getDayName, getDayDifference } from '../lib/utils/datetime_utility'; const LOADING_HTML = ` <div class="text-center"> @@ -17,7 +18,7 @@ function getSystemDate(systemUtcOffsetSeconds) { function formatTooltipText({ date, count }) { const dateObject = new Date(date); - const dateDayName = gl.utils.getDayName(dateObject); + const dateDayName = getDayName(dateObject); const dateText = dateObject.format('mmm d, yyyy'); let contribText = 'No contributions'; @@ -51,7 +52,7 @@ export default class ActivityCalendar { const oneYearAgo = new Date(today); oneYearAgo.setFullYear(today.getFullYear() - 1); - const days = gl.utils.getDayDifference(oneYearAgo, today); + const days = getDayDifference(oneYearAgo, today); for (let i = 0; i <= days; i += 1) { const date = new Date(oneYearAgo); diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index 1215b265e28..a7046e2f526 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -1,4 +1,5 @@ import ActivityCalendar from './activity_calendar'; +import { localTimeAgo } from '../lib/utils/datetime_utility'; /** * UserTabs @@ -138,7 +139,7 @@ export default class UserTabs { const tabSelector = `div#${action}`; this.$parentEl.find(tabSelector).html(data.html); this.loaded[action] = true; - gl.utils.localTimeAgo($('.js-timeago', tabSelector)); + localTimeAgo($('.js-timeago', tabSelector)); }, }); } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js index 32028a4a609..ee1a45cc754 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js @@ -1,4 +1,4 @@ -import '~/lib/utils/datetime_utility'; +import { getTimeago } from '~/lib/utils/datetime_utility'; import { visitUrl } from '../../lib/utils/url_utility'; import Flash from '../../flash'; import MemoryUsage from './mr_widget_memory_usage'; @@ -17,7 +17,7 @@ export default { }, methods: { formatDate(date) { - return gl.utils.getTimeago().format(date); + return getTimeago().format(date); }, hasExternalUrls(deployment = {}) { return deployment.external_url && deployment.external_url_formatted; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index c1f7e64f580..707766e08e4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -1,5 +1,6 @@ import Timeago from 'timeago.js'; import { getStateKey } from '../dependencies'; +import { formatDate } from '../../lib/utils/datetime_utility'; export default class MergeRequestStore { @@ -122,7 +123,7 @@ export default class MergeRequestStore { static getEventObject(event) { return { author: MergeRequestStore.getAuthorObject(event), - updatedAt: gl.utils.formatDate(MergeRequestStore.getEventUpdatedAtDate(event)), + updatedAt: formatDate(MergeRequestStore.getEventUpdatedAtDate(event)), formattedUpdatedAt: MergeRequestStore.getEventDate(event), }; } diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.js b/app/assets/javascripts/vue_shared/components/memory_graph.js index 643b77e04c7..f37ef1a5ca3 100644 --- a/app/assets/javascripts/vue_shared/components/memory_graph.js +++ b/app/assets/javascripts/vue_shared/components/memory_graph.js @@ -1,3 +1,5 @@ +import { getTimeago } from '../../lib/utils/datetime_utility'; + export default { name: 'MemoryGraph', props: { @@ -16,7 +18,7 @@ export default { }, computed: { getFormattedMedian() { - const deployedSince = gl.utils.getTimeago().format(this.deploymentTime * 1000); + const deployedSince = getTimeago().format(this.deploymentTime * 1000); return `Deployed ${deployedSince}`; }, }, diff --git a/app/assets/javascripts/vue_shared/components/popup_dialog.vue b/app/assets/javascripts/vue_shared/components/modal.vue index 6d15bbd84ba..55f466b7b41 100644 --- a/app/assets/javascripts/vue_shared/components/popup_dialog.vue +++ b/app/assets/javascripts/vue_shared/components/modal.vue @@ -1,6 +1,6 @@ <script> export default { - name: 'popup-dialog', + name: 'modal', props: { title: { @@ -75,7 +75,7 @@ export default { <template> <div class="modal-open"> <div - class="modal popup-dialog" + class="modal show" role="dialog" tabindex="-1" > diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue index 3ec50f14eb4..8053c65d498 100644 --- a/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue +++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue @@ -1,8 +1,8 @@ <script> -import PopupDialog from './popup_dialog.vue'; +import modal from './modal.vue'; export default { - name: 'recaptcha-dialog', + name: 'recaptcha-modal', props: { html: { @@ -20,7 +20,7 @@ export default { }, components: { - PopupDialog, + modal, }, methods: { @@ -65,9 +65,9 @@ export default { </script> <template> -<popup-dialog +<modal kind="warning" - class="recaptcha-dialog js-recaptcha-dialog" + class="recaptcha-modal js-recaptcha-modal" :hide-footer="true" :title="__('Please solve the reCAPTCHA')" @toggle="close" @@ -81,5 +81,5 @@ export default { v-html="html" ></div> </div> -</popup-dialog> +</modal> </template> diff --git a/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js index ef70f9432e3..ff1f565e79a 100644 --- a/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js +++ b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js @@ -1,4 +1,4 @@ -import RecaptchaDialog from '../components/recaptcha_dialog.vue'; +import recaptchaModal from '../components/recaptcha_modal.vue'; export default { data() { @@ -9,7 +9,7 @@ export default { }, components: { - RecaptchaDialog, + recaptchaModal, }, methods: { diff --git a/app/assets/javascripts/vue_shared/mixins/timeago.js b/app/assets/javascripts/vue_shared/mixins/timeago.js index 20f63ab663c..4e3b9d7b767 100644 --- a/app/assets/javascripts/vue_shared/mixins/timeago.js +++ b/app/assets/javascripts/vue_shared/mixins/timeago.js @@ -1,4 +1,4 @@ -import '../../lib/utils/datetime_utility'; +import { formatDate, getTimeago } from '../../lib/utils/datetime_utility'; /** * Mixin with time ago methods used in some vue components @@ -6,13 +6,13 @@ import '../../lib/utils/datetime_utility'; export default { methods: { timeFormated(time) { - const timeago = gl.utils.getTimeago(); + const timeago = getTimeago(); return timeago.format(time); }, tooltipTitle(time) { - return gl.utils.formatDate(time); + return formatDate(time); }, }, }; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 2ce4de6a2d5..29714e348a0 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -1,7 +1,3 @@ -.content-wrapper.page-with-new-nav { - margin-top: $header-height; -} - .navbar-gitlab { &.navbar-gitlab { padding: 0 16px; diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index 78a8e57ddbb..aa2d30a3cef 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -19,6 +19,13 @@ max-width: 425px; width: 100%; } + + &.svg-250 { + img, + svg { + width: 250px; + } + } } @mixin svg-size($size) { diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 1537b0744cc..1d8bd26cf1a 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -24,10 +24,14 @@ font-size: $gl-font-size; line-height: 25px; - &.status-box-closed { + &.status-box-mr-closed { background-color: $gl-danger; } + &.status-box-issue-closed { + background-color: $gl-primary; + } + &.status-box-merged { background-color: $gl-primary; } diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index cb324ccc440..3f0268541a4 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -24,6 +24,7 @@ body { } .content-wrapper { + margin-top: $header-height; padding-bottom: 100px; } @@ -105,11 +106,11 @@ body { } } -.page-with-sidebar > .content-wrapper { +.layout-page > .content-wrapper { min-height: calc(100vh - #{$header-height}); } -.with-performance-bar .page-with-sidebar { +.with-performance-bar .layout-page { margin-top: $header-height + $performance-bar-height; } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index ce551e6b7ce..1be66d0ab21 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -44,11 +44,18 @@ body.modal-open { } } -.modal.popup-dialog { - display: block; +.modal { + background-color: $black-transparent; + z-index: 2100; + + @media (min-width: $screen-md-min) { + .modal-dialog { + margin: 30px auto; + } + } } -.recaptcha-dialog .recaptcha-form { +.recaptcha-modal .recaptcha-form { display: inline-block; .recaptcha { diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 862ea379cbc..2803144ef1d 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -415,7 +415,7 @@ margin: 5px; } -.page-with-contextual-sidebar.page-with-sidebar .issue-boards-sidebar { +.page-with-contextual-sidebar.layout-page .issue-boards-sidebar { .issuable-sidebar-header { position: relative; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 8f8f11e3857..e19196e0c41 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -610,11 +610,19 @@ } .issuable-status-box { - float: none; - display: inline-block; + align-self: stretch; + display: flex; + justify-content: center; + align-items: center; margin-top: 0; - height: auto; - align-self: center; + padding-left: 9px; + padding-right: 9px; + + @media (min-width: $screen-sm-min) { + display: inline-block; + height: auto; + align-self: center; + } } .issuable-gutter-toggle { diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 402412eae71..6eb92c7baee 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -1,16 +1,3 @@ -.modal.popup-dialog { - display: block; - background-color: $black-transparent; - z-index: 2100; - - @media (min-width: $screen-md-min) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - } -} - .project-refs-form, .project-refs-target-form { display: inline-block; diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb index ed253042701..230bbe4b1aa 100644 --- a/app/controllers/concerns/with_performance_bar.rb +++ b/app/controllers/concerns/with_performance_bar.rb @@ -6,6 +6,7 @@ module WithPerformanceBar end def peek_enabled? + return true if Rails.env.development? return false unless Gitlab::PerformanceBar.enabled?(current_user) if RequestStore.active? diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 212cdbb8157..0f110bd25c5 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -74,7 +74,7 @@ module IssuesHelper elsif item.try(:merged?) 'status-box-merged' elsif item.closed? - 'status-box-closed' + 'status-box-mr-closed' elsif item.try(:upcoming?) 'status-box-upcoming' else diff --git a/app/models/commit.rb b/app/models/commit.rb index 307e4fcedfe..13c31111134 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -52,6 +52,20 @@ class Commit diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) } end + def order_by(collection:, order_by:, sort:) + return collection unless %w[email name commits].include?(order_by) + return collection unless %w[asc desc].include?(sort) + + collection.sort do |a, b| + operands = [a, b].tap { |o| o.reverse! if sort == 'desc' } + + attr1, attr2 = operands.first.public_send(order_by), operands.second.public_send(order_by) # rubocop:disable PublicSend + + # use case insensitive comparison for string values + order_by.in?(%w[email name]) ? attr1.casecmp(attr2) : attr1 <=> attr2 + end + end + # Truncate sha to 8 characters def truncate_sha(sha) sha[0..MIN_SHA_LENGTH] diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 26a3388602a..f627ce260a3 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -85,6 +85,14 @@ class MergeRequest < ActiveRecord::Base transition locked: :opened end + before_transition any => :opened do |merge_request| + merge_request.merge_jid = nil + + merge_request.run_after_commit do + UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id) + end + end + state :opened state :closed state :merged @@ -879,11 +887,11 @@ class MergeRequest < ActiveRecord::Base def state_icon_name if merged? - "check" + "git-merge" elsif closed? - "times" + "close" else - "circle-o" + "issue-open-m" end end diff --git a/app/models/repository.rb b/app/models/repository.rb index c0e31eca8da..28f5fc28b8c 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -697,10 +697,14 @@ class Repository end end - def contributors + # Params: + # + # order_by: name|email|commits + # sort: asc|desc default: 'asc' + def contributors(order_by: nil, sort: 'asc') commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true) - commits.group_by(&:author_email).map do |email, commits| + commits = commits.group_by(&:author_email).map do |email, commits| contributor = Gitlab::Contributor.new contributor.email = email @@ -714,6 +718,7 @@ class Repository contributor end + Commit.order_by(collection: commits, order_by: order_by, sort: sort) end def refs_contains_sha(ref_type, sha) diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 85db2760e23..c8b112132b3 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -81,7 +81,7 @@ module Ci end def related_merge_requests - MergeRequest.where(source_project: pipeline.project, source_branch: pipeline.ref) + MergeRequest.opened.where(source_project: pipeline.project, source_branch: pipeline.ref) end end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 2c51ac13815..e7463e6e25c 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -106,12 +106,14 @@ class IssuableBaseService < BaseService end def merge_quick_actions_into_params!(issuable) + original_description = params.fetch(:description, issuable.description) + description, command_params = QuickActions::InterpretService.new(project, current_user) - .execute(params[:description], issuable) + .execute(original_description, issuable) # Avoid a description already set on an issuable to be overwritten by a nil - params[:description] = description if params.key?(:description) + params[:description] = description if description params.merge!(command_params) end diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index a5686002328..20ca6ec969a 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -83,12 +83,12 @@ You're all done! - elsif current_user.todos.any? .todos-all-done - .svg-content + .svg-content.svg-250 = image_tag 'illustrations/todos_all_done.svg' - if todos_filter_empty? %h4.text-center = Gitlab.config.gitlab.no_todos_messages.sample - %p.text-center + %p Are you looking for things to do? Take a look at = succeed "," do = link_to "the opened issues", issues_dashboard_path @@ -104,7 +104,7 @@ = image_tag 'illustrations/todos_empty.svg' .todos-empty-content %h4 - Todos let you see what you should do next. + Todos let you see what you should do next %p When an issue or merge request is assigned to you, or when you %strong diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 25ed610466a..eba9cd253bb 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,7 +1,7 @@ -.page-with-sidebar{ class: page_with_sidebar_class } +.layout-page{ class: page_with_sidebar_class } - if defined?(nav) && nav = render "layouts/nav/sidebar/#{nav}" - .content-wrapper.page-with-new-nav + .content-wrapper = render 'shared/outdated_browser' .mobile-overlay .alert-wrapper diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml index a7370180bf6..32a24c101fc 100644 --- a/app/views/layouts/nav/projects_dropdown/_show.html.haml +++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml @@ -1,4 +1,4 @@ -- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: @project.web_url, avatar_url: @project.avatar_url } if @project&.persisted? +- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted? .projects-dropdown-container .project-dropdown-sidebar %ul diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml index e629cc58b06..b525f4efc83 100644 --- a/app/views/projects/clusters/_empty_state.html.haml +++ b/app/views/projects/clusters/_empty_state.html.haml @@ -1,12 +1,12 @@ .row.empty-state .col-xs-12 .svg-content= image_tag 'illustrations/clusters_empty.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - %h4= s_('ClusterIntegration|Integrate cluster automation') + %h4.text-center= s_('ClusterIntegration|Integrate cluster automation') - link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer') %p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page} - %p + .text-center = link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success' diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 56cf16c3778..ad94113fffd 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -15,9 +15,9 @@ #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), - "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started'), - "empty-loading-svg-path": image_path('illustrations/monitoring/loading'), - "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect'), + "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'), + "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'), + "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'), "additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json), "project-path": project_path(@project), "tags-path": project_tags_path(@project), diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index d260aaee2d3..eab7879c7bf 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -14,12 +14,12 @@ .detail-page-header .detail-page-header-body - .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) } - = icon('check', class: "hidden-sm hidden-md hidden-lg") + .issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) } + = sprite_icon('mobile-issue-close', size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs Closed .issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) } - = icon('circle-o', class: "hidden-sm hidden-md hidden-lg") + = sprite_icon('issue-open-m', size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs Open .issuable-meta diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml index bc91758110e..22c8b6b513d 100644 --- a/app/views/projects/merge_requests/_mr_title.html.haml +++ b/app/views/projects/merge_requests/_mr_title.html.haml @@ -7,7 +7,7 @@ .detail-page-header .detail-page-header-body .issuable-status-box.status-box{ class: status_box_class(@merge_request) } - = icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg") + = sprite_icon(@merge_request.state_icon_name, size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs = @merge_request.state_human_name diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index be92f953d24..6e105a5521a 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -14,7 +14,7 @@ .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' + = text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10.create-from @@ -28,7 +28,7 @@ .form-group = label_tag :message, nil, class: 'control-label' .col-sm-10 - = text_area_tag :message, @message, required: false, tabindex: 3, class: 'form-control', rows: 5 + = text_area_tag :message, @message, required: false, class: 'form-control', rows: 5 .help-block = s_('TagsPage|Optionally, add a message to the tag.') %hr @@ -41,6 +41,6 @@ .help-block = s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.') .form-actions - = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create', tabindex: 3 + = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create' = link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel' %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml index de26fa8bbf3..e039a73cd3b 100644 --- a/app/views/shared/empty_states/_issues.html.haml +++ b/app/views/shared/empty_states/_issues.html.haml @@ -6,18 +6,21 @@ .col-xs-12 .svg-content = image_tag 'illustrations/issues.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - if has_button && current_user %h4 - The Issue Tracker is the place to add things that need to be improved or solved in a project + = _("The Issue Tracker is the place to add things that need to be improved or solved in a project") %p - Issues can be bugs, tasks or ideas to be discussed. - Also, issues are searchable and filterable. - - if project_select_button - = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues - - else - = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link' + = _("Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.") + .text-center + - if project_select_button + = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues + - else + = link_to 'New issue', button_path, class: 'btn btn-success', title: 'New issue', id: 'new_issue_link' - else + %h4.text-center= _("There are no issues to show") + %p + = _("The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.") .text-center - %h4 There are no issues to show. + = link_to _('Register / Sign In'), new_user_session_path, class: 'btn btn-success' diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml index a65634dce53..04db9de3606 100644 --- a/app/views/shared/empty_states/_labels.html.haml +++ b/app/views/shared/empty_states/_labels.html.haml @@ -2,10 +2,10 @@ .col-xs-12 .svg-content = image_tag 'illustrations/labels.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - %h4 Labels can be applied to issues and merge requests to categorize them. - %p You can also star a label to make it a priority label. + %h4= _("Labels can be applied to issues and merge requests to categorize them.") + %p= _("You can also star a label to make it a priority label.") - if can?(current_user, :admin_label, @project) - = link_to 'New label', new_project_label_path(@project), class: 'btn btn-new', title: 'New label', id: 'new_label_link' - = link_to 'Generate a default set of labels', generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link' + = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link' + = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link' diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml index 67f906903e9..2edf3557df4 100644 --- a/app/views/shared/empty_states/_merge_requests.html.haml +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -6,17 +6,18 @@ .col-xs-12 .svg-content = image_tag 'illustrations/merge_requests.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - if has_button %h4 - Merge requests are a place to propose changes you've made to a project and discuss those changes with others. + = _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others") %p - Interested parties can even contribute by pushing commits if they want to. - - if project_select_button - = render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request', type: :merge_requests - - else - = link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link' + = _("Interested parties can even contribute by pushing commits if they want to.") + .text-center + - if project_select_button + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests + - else + = link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link' - else %h4.text-center - There are no merge requests to show. + = _("There are no merge requests to show") diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb index 36d2a2e6466..16394293c79 100644 --- a/app/workers/stuck_merge_jobs_worker.rb +++ b/app/workers/stuck_merge_jobs_worker.rb @@ -23,7 +23,12 @@ class StuckMergeJobsWorker merge_requests = MergeRequest.where(id: completed_ids) merge_requests.where.not(merge_commit_sha: nil).update_all(state: :merged) - merge_requests.where(merge_commit_sha: nil).update_all(state: :opened, merge_jid: nil) + + merge_requests_to_reopen = merge_requests.where(merge_commit_sha: nil) + + # Do not reopen merge requests using direct queries. + # We rely on state machine callbacks to update head_pipeline_id + merge_requests_to_reopen.each(&:unlock_mr) Rails.logger.info("Updated state of locked merge jobs. JIDs: #{completed_jids.join(', ')}") end diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb index 0a2e9b63578..68c71a2b7a7 100644 --- a/app/workers/update_head_pipeline_for_merge_request_worker.rb +++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb @@ -8,8 +8,19 @@ class UpdateHeadPipelineForMergeRequestWorker pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last return unless pipeline && pipeline.latest? - raise ArgumentError, 'merge request sha does not equal pipeline sha' if merge_request.diff_head_sha != pipeline.sha + + if merge_request.diff_head_sha != pipeline.sha + log_error_message_for(merge_request) + + return + end merge_request.update_attribute(:head_pipeline_id, pipeline.id) end + + def log_error_message_for(merge_request) + Rails.logger.error( + "Outdated head pipeline for active merge request: id=#{merge_request.id}, source_branch=#{merge_request.source_branch}, diff_head_sha=#{merge_request.diff_head_sha}" + ) + end end diff --git a/changelogs/unreleased/13695-order-contributors-in-api.yml b/changelogs/unreleased/13695-order-contributors-in-api.yml new file mode 100644 index 00000000000..26bf8650a4a --- /dev/null +++ b/changelogs/unreleased/13695-order-contributors-in-api.yml @@ -0,0 +1,5 @@ +--- +title: Adds ordering to projects contributors in API +merge_request: 15469 +author: Jacopo Beschi @jacopo-beschi +type: added diff --git a/changelogs/unreleased/33926-update-issuable-icons.yml b/changelogs/unreleased/33926-update-issuable-icons.yml new file mode 100644 index 00000000000..87076dde545 --- /dev/null +++ b/changelogs/unreleased/33926-update-issuable-icons.yml @@ -0,0 +1,5 @@ +--- +title: Update issuable status icons +merge_request: 15898 +author: +type: changed diff --git a/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml b/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml new file mode 100644 index 00000000000..978930a5b8c --- /dev/null +++ b/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml @@ -0,0 +1,5 @@ +--- +title: Fix broken illustration images for monitoring page empty states +merge_request: 15889 +author: +type: fixed diff --git a/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml b/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml new file mode 100644 index 00000000000..485133b46a7 --- /dev/null +++ b/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml @@ -0,0 +1,5 @@ +--- +title: Use relative URL for projects to avoid storing domains +merge_request: 15876 +author: +type: fixed diff --git a/changelogs/unreleased/add-tcp-check-rake-task.yml b/changelogs/unreleased/add-tcp-check-rake-task.yml new file mode 100644 index 00000000000..a7c04bd0d55 --- /dev/null +++ b/changelogs/unreleased/add-tcp-check-rake-task.yml @@ -0,0 +1,5 @@ +--- +title: Add a gitlab:tcp_check rake task +merge_request: 15759 +author: +type: added diff --git a/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml b/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml new file mode 100644 index 00000000000..8668aa18669 --- /dev/null +++ b/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml @@ -0,0 +1,5 @@ +--- +title: Execute quick actions (if present) when creating MR from issue +merge_request: 15810 +author: +type: fixed diff --git a/changelogs/unreleased/issue-description-field-typo.yml b/changelogs/unreleased/issue-description-field-typo.yml new file mode 100644 index 00000000000..9c4c179876d --- /dev/null +++ b/changelogs/unreleased/issue-description-field-typo.yml @@ -0,0 +1,5 @@ +--- +title: Fixed typo for issue description field declaration +merge_request: +author: Marcus Amargi +type: fixed diff --git a/changelogs/unreleased/remove-tabindexes-from-tag-form.yml b/changelogs/unreleased/remove-tabindexes-from-tag-form.yml new file mode 100644 index 00000000000..a15bf2a7a4f --- /dev/null +++ b/changelogs/unreleased/remove-tabindexes-from-tag-form.yml @@ -0,0 +1,5 @@ +--- +title: removed tabindexes from tag form +merge_request: +author: Marcus Amargi +type: changed diff --git a/config/no_todos_messages.yml b/config/no_todos_messages.yml index 264a975b614..da721a9b6e6 100644 --- a/config/no_todos_messages.yml +++ b/config/no_todos_messages.yml @@ -3,9 +3,9 @@ # # If you come up with a fun one, please feel free to contribute it to GitLab! # https://about.gitlab.com/contributing/ ---- -- Good job! Looks like you don't have any todos left. +--- +- Good job! Looks like you don't have any todos left - Isn't an empty todo list beautiful? - Give yourself a pat on the back! - Nothing left to do, high five! -- Henceforth you shall be known as "Todo Destroyer". +- Henceforth you shall be known as "Todo Destroyer" diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md index ee9b9a9466a..373d4239f71 100644 --- a/doc/administration/auth/README.md +++ b/doc/administration/auth/README.md @@ -14,3 +14,4 @@ providers. - [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS - [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider - [Okta](okta.md) Configure GitLab to sign in using Okta +- [Authentiq](authentiq.md): Enable the Authentiq OmniAuth provider for passwordless authentication diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index a88e67bfeb5..ea8077f0623 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -37,6 +37,7 @@ Follow the steps below to configure an active/active setup: 1. [Configure the database](database.md) 1. [Configure Redis](redis.md) + 1. [Configure Redis for GitLab source installations](redis_source.md) 1. [Configure NFS](nfs.md) 1. [Configure the GitLab application servers](gitlab.md) 1. [Configure the load balancers](load_balancer.md) diff --git a/doc/administration/index.md b/doc/administration/index.md index e6986a2b07f..58922b71ae7 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -16,21 +16,27 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Install](../install/README.md): Requirements, directory structures, and installation methods. - [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability. + - [High Availability on AWS](../university/high-availability/aws/README.md): Set up GitLab HA on Amazon AWS. ### Configuring GitLab - [Adjust your instance's timezone](../workflow/timezone.md): Customize the default time zone of GitLab. -- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers. -- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page. - [System hooks](../system_hooks/system_hooks.md): Notifications when users, projects and keys are changed. - [Security](../security/README.md): Learn what you can do to further secure your GitLab instance. - [Usage statistics, version check, and usage ping](../user/admin_area/settings/usage_statistics.md): Enable or disable information about your instance to be sent to GitLab, Inc. - [Polling](polling.md): Configure how often the GitLab UI polls for updates. - [GitLab Pages configuration](pages/index.md): Enable and configure GitLab Pages. -- [GitLab Pages configuration for installations from the source](pages/source.md): Enable and configure GitLab Pages on +- [GitLab Pages configuration for GitLab source installations](pages/source.md): Enable and configure GitLab Pages on [source installations](../install/installation.md#installation-from-source). - [Environment variables](environment_variables.md): Supported environment variables that can be used to override their defaults values in order to configure GitLab. +#### Customizing GitLab's appearance + +- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers. +- [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description. +- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page. +- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project. + ### Maintaining GitLab - [Raketasks](../raketasks/README.md): Perform various tasks for maintenance, backups, automatic webhooks setup, etc. @@ -74,6 +80,7 @@ server with IMAP authentication on Ubuntu, to be used with Reply by email. - [Issue closing pattern](issue_closing_pattern.md): Customize how to close an issue from commit messages. - [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service. - [Default labels](../user/admin_area/labels.html): Create labels that will be automatically added to every new project. +- [Restrict the use of public or internal projects](../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects): Restrict the use of visibility levels for users when they create a project or a snippet. ### Repository settings diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md index 136192191f9..ecf92c379fd 100644 --- a/doc/administration/raketasks/maintenance.md +++ b/doc/administration/raketasks/maintenance.md @@ -221,3 +221,22 @@ sudo gitlab-rake gitlab:shell:create_hooks cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:shell:create_hooks RAILS_ENV=production ``` + +## Check TCP connectivity to a remote site + +Sometimes you need to know if your GitLab installation can connect to a TCP +service on another machine - perhaps a PostgreSQL or HTTPS server. A rake task +is included to help you with this: + +**Omnibus Installation** + +``` +sudo gitlab-rake gitlab:tcp_check[example.com,80] +``` + +**Source Installation** + +``` +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:tcp_check[example.com,80] RAILS_ENV=production +``` diff --git a/doc/api/issues.md b/doc/api/issues.md index ec8ff3cd3f3..d2fefbe68aa 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -514,9 +514,9 @@ PUT /projects/:id/issues/:issue_iid | `title` | string | no | The title of an issue | | `description` | string | no | The description of an issue | | `confidential` | boolean | no | Updates an issue to be confidential | -| `assignee_ids` | Array[integer] | no | The ID of the users to assign the issue to | -| `milestone_id` | integer | no | The ID of a milestone to assign the issue to | -| `labels` | string | no | Comma-separated label names for an issue | +| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. | +| `milestone_id` | integer | no | The ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.| +| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. | | `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it | | `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index b2e4b6d0955..880b0ed2c65 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -524,15 +524,15 @@ PUT /projects/:id/merge_requests/:merge_request_iid | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `merge_request_iid` | integer | yes | The ID of a merge request | | `target_branch` | string | no | The target branch | | `title` | string | no | Title of MR | -| `assignee_id` | integer | no | Assignee user ID | +| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. | +| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.| +| `labels` | string | no | Comma-separated label names for an merge request. Set to an empty string to unassign all labels. | | `description` | string | no | Description of MR | | `state_event` | string | no | New state (close/reopen) | -| `labels` | string | no | Labels for MR as a comma-separated list | -| `milestone_id` | integer | no | The ID of a milestone | | `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | | `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. | diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 594babc74be..03b32577872 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -182,6 +182,8 @@ GET /projects/:id/repository/contributors Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user +- `order_by` (optional) - Return contributors ordered by `name`, `email`, or `commits` fields. If not given contributors are ordered by commit date. +- `sort` (optional) - Return contributors sorted in `asc` or `desc` order. Default is `asc` Response: diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md index e5a2bbd1773..df0e1521150 100644 --- a/doc/ci/ssh_keys/README.md +++ b/doc/ci/ssh_keys/README.md @@ -1,84 +1,106 @@ -# Using SSH keys +--- +last_updated: 2017-12-13 +--- + +# Using SSH keys with GitLab CI/CD GitLab currently doesn't have built-in support for managing SSH keys in a build -environment. +environment (where the GitLab Runner runs). The SSH keys can be useful when: 1. You want to checkout internal submodules -2. You want to download private packages using your package manager (eg. bundler) -3. You want to deploy your application to eg. Heroku or your own server -4. You want to execute SSH commands from the build server to the remote server -5. You want to rsync files from your build server to the remote server +1. You want to download private packages using your package manager (e.g., Bundler) +1. You want to deploy your application to your own server, or, for example, Heroku +1. You want to execute SSH commands from the build environment to a remote server +1. You want to rsync files from the build environment to a remote server If anything of the above rings a bell, then you most likely need an SSH key. -## Inject keys in your build server - The most widely supported method is to inject an SSH key into your build -environment by extending your `.gitlab-ci.yml`. - -This is the universal solution which works with any type of executor -(docker, shell, etc.). - -### How it works - -1. Create a new SSH key pair with [ssh-keygen][] -2. Add the private key as a **Secret Variable** to the project -3. Run the [ssh-agent][] during job to load the private key. +environment by extending your `.gitlab-ci.yml`, and it's a solution which works +with any type of [executor](https://docs.gitlab.com/runner/executors/) +(Docker, shell, etc.). + +## How it works + +1. Create a new SSH key pair locally with [ssh-keygen](http://linux.die.net/man/1/ssh-keygen) +1. Add the private key as a [secret variable](../variables/README.md) to + your project +1. Run the [ssh-agent](http://linux.die.net/man/1/ssh-agent) during job to load + the private key. +1. Copy the public key to the servers you want to have access to (usually in + `~/.ssh/authorized_keys`) or add it as a [deploy key](../../ssh/README.md#deploy-keys) + if you are accessing a private GitLab repository. + +NOTE: **Note:** +The private key will not be displayed in the job trace, unless you enable +[debug tracing](../variables/README.md#debug-tracing). You might also want to +check the [visibility of your pipelines](../../user/project/pipelines/settings.md#visibility-of-pipelines). ## SSH keys when using the Docker executor -You will first need to create an SSH key pair. For more information, follow the -instructions to [generate an SSH key](../../ssh/README.md). Do not add a -passphrase to the SSH key, or the `before_script` will prompt for it. - -Then, create a new **Secret Variable** in your project settings on GitLab -following **Settings > CI/CD** and look for the "Secret Variables" section. -As **Key** add the name `SSH_PRIVATE_KEY` and in the **Value** field paste the -content of your _private_ key that you created earlier. - -It is also good practice to check the server's own public key to make sure you -are not being targeted by a man-in-the-middle attack. To do this, add another -variable named `SSH_SERVER_HOSTKEYS`. To find out the hostkeys of your server, run -the `ssh-keyscan YOUR_SERVER` command from a trusted network (ideally, from the -server itself), and paste its output into the `SSH_SERVER_HOSTKEYS` variable. If -you need to connect to multiple servers, concatenate all the server public keys -that you collected into the **Value** of the variable. There must be one key per -line. - -Next you need to modify your `.gitlab-ci.yml` with a `before_script` action. -Add it to the top: - -``` -before_script: - # Install ssh-agent if not already installed, it is required by Docker. - # (change apt-get to yum if you use a CentOS-based image) - - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - - # Run ssh-agent (inside the build environment) - - eval $(ssh-agent -s) - - # Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store - - ssh-add <(echo "$SSH_PRIVATE_KEY") - - # For Docker builds disable host key checking. Be aware that by adding that - # you are suspectible to man-in-the-middle attacks. - # WARNING: Use this only with the Docker executor, if you use it with shell - # you will overwrite your user's SSH config. - - mkdir -p ~/.ssh - - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - # In order to properly check the server's host key, assuming you created the - # SSH_SERVER_HOSTKEYS variable previously, uncomment the following two lines - # instead. - # - mkdir -p ~/.ssh - # - '[[ -f /.dockerenv ]] && echo "$SSH_SERVER_HOSTKEYS" > ~/.ssh/known_hosts' -``` - -As a final step, add the _public_ key from the one you created earlier to the -services that you want to have an access to from within the build environment. -If you are accessing a private GitLab repository you need to add it as a -[deploy key](../../ssh/README.md#deploy-keys). +When your CI/CD jobs run inside Docker containers (meaning the environment is +contained) and you want to deploy your code in a private server, you need a way +to access it. This is where an SSH key pair comes in handy. + +1. You will first need to create an SSH key pair. For more information, follow + the instructions to [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair). + **Do not** add a passphrase to the SSH key, or the `before_script` will\ + prompt for it. + +1. Create a new [secret variable](../variables/README.md#secret-variables). + As **Key** enter the name `SSH_PRIVATE_KEY` and in the **Value** field paste + the content of your _private_ key that you created earlier. + +1. Modify your `.gitlab-ci.yml` with a `before_script` action. In the following + example, a Debian based image is assumed. Edit to your needs: + + ```yaml + before_script: + ## + ## Install ssh-agent if not already installed, it is required by Docker. + ## (change apt-get to yum if you use an RPM-based image) + ## + - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' + + ## + ## Run ssh-agent (inside the build environment) + ## + - eval $(ssh-agent -s) + + ## + ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store + ## We're using tr to fix line endings which makes ed25519 keys work + ## without extra base64 encoding. + ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 + ## + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null + + ## + ## Create the SSH directory and give it the right permissions + ## + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + + ## + ## Optionally, if you will be using any Git commands, set the user name and + ## and email. + ## + #- git config --global user.email "user@example.com" + #- git config --global user.name "User name" + ``` + + NOTE: **Note:** + The [`before_script`](../yaml/README.md#before-script) can be set globally + or per-job. + +1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys). + +1. As a final step, add the _public_ key from the one you created in the first + step to the services that you want to have an access to from within the build + environment. If you are accessing a private GitLab repository you need to add + it as a [deploy key](../../ssh/README.md#deploy-keys). That's it! You can now have access to private servers or repositories in your build environment. @@ -91,24 +113,93 @@ SSH key. You can generate the SSH key from the machine that GitLab Runner is installed on, and use that key for all projects that are run on this machine. -First, you need to login to the server that runs your jobs. +1. First, you need to login to the server that runs your jobs. + +1. Then from the terminal login as the `gitlab-runner` user: -Then from the terminal login as the `gitlab-runner` user and generate the SSH -key pair as described in the [SSH keys documentation](../../ssh/README.md). + ``` + sudo su - gitlab-runner + ``` -As a final step, add the _public_ key from the one you created earlier to the -services that you want to have an access to from within the build environment. -If you are accessing a private GitLab repository you need to add it as a -[deploy key](../../ssh/README.md#deploy-keys). +1. Generate the SSH key pair as described in the instructions to + [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair). + **Do not** add a passphrase to the SSH key, or the `before_script` will + prompt for it. + +1. As a final step, add the _public_ key from the one you created earlier to the + services that you want to have an access to from within the build environment. + If you are accessing a private GitLab repository you need to add it as a + [deploy key](../../ssh/README.md#deploy-keys). Once done, try to login to the remote server in order to accept the fingerprint: ```bash -ssh <address-of-my-server> +ssh example.com +``` + +For accessing repositories on GitLab.com, you would use `git@gitlab.com`. + +## Verifying the SSH host keys + +It is a good practice to check the private server's own public key to make sure +you are not being targeted by a man-in-the-middle attack. In case anything +suspicious happens, you will notice it since the job would fail (the SSH +connection would fail if the public keys would not match). + +To find out the host keys of your server, run the `ssh-keyscan` command from a +trusted network (ideally, from the private server itself): + +```sh +## Use the domain name +ssh-keyscan example.com + +## Or use an IP +ssh-keyscan 1.2.3.4 ``` -For accessing repositories on GitLab.com, the `<address-of-my-server>` would be -`git@gitlab.com`. +Create a new [secret variable](../variables/README.md#secret-variables) with +`SSH_KNOWN_HOSTS` as "Key", and as a "Value" add the output of `ssh-keyscan`. + +NOTE: **Note:** +If you need to connect to multiple servers, all the server host keys +need to be collected in the **Value** of the variable, one key per line. + +TIP: **Tip:** +By using a secret variable instead of `ssh-keyscan` directly inside +`.gitlab-ci.yml`, it has the benefit that you don't have to change `.gitlab-ci.yml` +if the host domain name changes for some reason. Also, the values are predefined +by you, meaning that if the host keys suddenly change, the CI/CD job will fail, +and you'll know there's something wrong with the server or the network. + +Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the +[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor) +above, here's what more you need to add: + + ```yaml +before_script: + ## + ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the + ## following two lines. + ## + - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts' + - chmod 644 ~/.ssh/known_hosts + + ## + ## Alternatively, use ssh-keyscan to scan the keys of your private server. + ## Replace example.com with your private server's domain name. Repeat that + ## command if you have more than one server to connect to. + ## + #- ssh-keyscan example.com >> ~/.ssh/known_hosts + #- chmod 644 ~/.ssh/known_hosts + + ## + ## You can optionally disable host key checking. Be aware that by adding that + ## you are suspectible to man-in-the-middle attacks. + ## WARNING: Use this only with the Docker executor, if you use it with shell + ## you will overwrite your user's SSH config. + ## + #- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' +``` ## Example project @@ -119,6 +210,4 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the job will begin. -[ssh-keygen]: http://linux.die.net/man/1/ssh-keygen -[ssh-agent]: http://linux.die.net/man/1/ssh-agent [ssh-example-repo]: https://gitlab.com/gitlab-examples/ssh-private-key/ diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index a9e6bda9916..b9d4a2098ed 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -213,14 +213,15 @@ An example project service that defines deployment variables is ## Debug tracing > Introduced in GitLab Runner 1.7. -> -> **WARNING:** Enabling debug tracing can have severe security implications. The - output **will** contain the content of all your secret variables and any other - secrets! The output **will** be uploaded to the GitLab server and made visible - in job traces! + +CAUTION: **Warning:** +Enabling debug tracing can have severe security implications. The +output **will** contain the content of all your secret variables and any other +secrets! The output **will** be uploaded to the GitLab server and made visible +in job traces! By default, GitLab Runner hides most of the details of what it is doing when -processing a job. This behaviour keeps job traces short, and prevents secrets +processing a job. This behavior keeps job traces short, and prevents secrets from being leaked into the trace unless your script writes them to the screen. If a job isn't working as expected, this can make the problem difficult to diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md index 31164ccd465..d14ba6ad522 100644 --- a/doc/customization/issue_closing.md +++ b/doc/customization/issue_closing.md @@ -1,3 +1,7 @@ +--- +comments: false +--- + This document was split into: - [administration/issue_closing_pattern.md](../administration/issue_closing_pattern.md). diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md index a0cb234bea0..0aef0bf5abb 100644 --- a/doc/customization/welcome_message.md +++ b/doc/customization/welcome_message.md @@ -8,5 +8,5 @@ It is possible to add a markdown-formatted welcome message to your GitLab sign-in page. Users of GitLab Enterprise Edition should use the [branded login page feature](branded_login_page.md) instead. -The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI. +The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI. Admin area > Settings diff --git a/doc/development/ux_guide/components.md b/doc/development/ux_guide/components.md index 16a811dbc74..d396964e7c1 100644 --- a/doc/development/ux_guide/components.md +++ b/doc/development/ux_guide/components.md @@ -10,7 +10,7 @@ * [Tables](#tables) * [Blocks](#blocks) * [Panels](#panels) -* [Dialog modals](#dialog-modals) +* [Modals](#modals) * [Alerts](#alerts) * [Forms](#forms) * [Search box](#search-box) @@ -255,18 +255,18 @@ Skeleton loading can replace any existing UI elements for the period in which th --- -## Dialog modals +## Modals -Dialog modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal. +Modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal. ### Usage -* When the action is irreversible, dialog modals provide the details and confirm with the user before they take an advanced action. -* When the action will affect privacy or authorization, dialog modals provide advanced information and confirm with the user. +* When the action is irreversible, modals provide the details and confirm with the user before they take an advanced action. +* When the action will affect privacy or authorization, modals provide advanced information and confirm with the user. ### Style -* Dialog modals contain the header, body, and actions. +* Modals contain the header, body, and actions. * **Header(1):** The header title is a question instead of a descriptive phrase. * **Body(2):** The content in body should never be ambiguous and unclear. It provides specific information. * **Actions(3):** Contains a affirmative action, a dismissive action, and an extra action. The order of actions from left to right: Dismissive action → Extra action → Affirmative action @@ -277,13 +277,13 @@ Dialog modals are only used for having a conversation and confirmation with the ### Placement -* Dialog modals should always be the center of the screen horizontally and be positioned **72px** from the top. +* Modals should always be the center of the screen horizontally and be positioned **72px** from the top. -| Dialog with 2 actions | Dialog with 3 actions | Special confirmation | +| Modal with 2 actions | Modal with 3 actions | Special confirmation | | --------------------- | --------------------- | -------------------- | | ![two-actions](img/modals-general-confimation-dialog.png) | ![three-actions](img/modals-three-buttons.png) | ![spcial-confirmation](img/modals-special-confimation-dialog.png) | -> TODO: Special case for dialog modal. +> TODO: Special case for modal. --- diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md index 12e8d0a31bb..af842da7f62 100644 --- a/doc/development/ux_guide/copy.md +++ b/doc/development/ux_guide/copy.md @@ -46,11 +46,11 @@ Avoid using periods in solitary sentences in these elements: * Labels
* Hover text
* Bulleted lists
-* Dialog body text
+* Modal body text
Periods should be used for:
-* Lists or dialogs with multiple sentences
+* Lists or modals with multiple sentences
* Any sentence followed by a link
| :white_check_mark: **Do** place periods after sentences followed by a link | :no_entry_sign: **Don’t** place periods after a link if it‘s not followed by a sentence |
@@ -80,7 +80,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable | Punctuation mark | Copy and paste | HTML entity | Unicode | Mac shortcut | Windows shortcut | Description |
|---|---|---|---|---|---|---|
-| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and dialog body text.<br><br>Use in lists or dialogs with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
+| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and modal body text.<br><br>Use in lists or modals with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
| Comma | **,** | | | | | Place inside quotation marks.<br><br>Use a [serial comma][serial comma] in lists of three or more terms. |
| Exclamation point | **!** | | | | | Avoid exclamation points as they tend to come across as shouting. Some exceptions include greetings or congratulatory messages. |
| Colon | **:** | `:` | `\u003A` | | | Omit from labels, for example, in the labels for fields in a form. |
@@ -88,7 +88,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable | Quotation marks | **“**<br><br>**”**<br><br>**‘**<br><br>**’** | `“`<br><br>`”`<br><br>`‘`<br><br>`’` | `\u201C`<br><br>`\u201D`<br><br>`\u2018`<br><br>`\u2019` | <kbd>⌥ Option</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>]</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>]</kbd> | <kbd>Alt</kbd>+<kbd>0 1 4 7</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 8</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 5</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 6</kbd> | Use proper quotation marks (also known as smart quotes, curly quotes, or typographer’s quotes) for quotes. Single quotation marks are used for quotes inside of quotes.<br><br>The right single quotation mark symbol is also used for apostrophes.<br><br>Don’t use primes, straight quotes, or free-standing accents for quotation marks. |
| Primes | **′**<br><br>**″** | `′`<br><br>`″` | `\u2032`<br><br>`\u2033` | | <kbd>Alt</kbd>+<kbd>8 2 4 2</kbd><br><br><kbd>Alt</kbd>+<kbd>8 2 4 3</kbd> | Use prime (′) only in abbreviations for feet, arcminutes, and minutes: 3° 15′<br><br>Use double-prime (″) only in abbreviations for inches, arcseconds, and seconds: 3° 15′ 35″<br><br>Don’t use quotation marks, straight quotes, or free-standing accents for primes. |
| Straight quotes and accents | **"**<br><br>**'**<br><br>**`**<br><br>**´** | `"`<br><br>`'`<br><br>```<br><br>`´` | `\u0022`<br><br>`\u0027`<br><br>`\u0060`<br><br>`\u00B4` | | | Don’t use straight quotes or free-standing accents for primes or quotation marks.<br><br>Proper typography never uses straight quotes. They are left over from the age of typewriters and their only modern use is for code. |
-| Ellipsis | **…** | `…` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a dialog or start some other process. |
+| Ellipsis | **…** | `…` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a modal or start some other process. |
| Chevrons | **«**<br><br>**»**<br><br>**‹**<br><br>**›**<br><br>**<**<br><br>**>** | `«`<br><br>`»`<br><br>`‹`<br><br>`›`<br><br>`<`<br><br>`>` | `\u00AB`<br><br>`\u00BB`<br><br>`\u2039`<br><br>`\u203A`<br><br>`\u003C`<br><br>`\u003E`<br><br> | | | Omit from links or buttons that open another page or move to the next or previous step in a process. Also known as angle brackets, angular quote brackets, or guillemets. |
| Em dash | **—** | `—` | `\u2014` | <kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 1</kbd> | Avoid using dashes to separate text. If you must use dashes for this purpose — like this — use an em dash surrounded by spaces. |
| En dash | **–** | `–` | `\u2013` | <kbd>⌥ Option</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 0</kbd> | Use an en dash without spaces instead of a hyphen to indicate a range of values, such as numbers, times, and dates: “3–5 kg”, “8:00 AM–12:30 PM”, “10–17 Jan” |
@@ -175,7 +175,7 @@ A **comment** is a written piece of text that users of GitLab can create. Commen #### Discussion
A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved.
-## Confirmation dialogs
+## Modals
- Destruction buttons should be clear and always say what they are destroying.
E.g., `Delete page` instead of just `Delete`.
@@ -184,6 +184,8 @@ A **discussion** is a group of 1 or more comments. A discussion can include subd - Avoid the word `cancel` or `canceled` in the descriptive copy. It can be
confusing when you then see the `Cancel` button.
+see also: guidelines for [modal components](components.md#modals)
+
---
Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons].
diff --git a/doc/install/installation.md b/doc/install/installation.md index 570b0d5b22f..6c6e5db4cd9 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -299,9 +299,9 @@ sudo usermod -aG redis git ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-2-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-3-stable gitlab -**Note:** You can change `10-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `10-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md index df56f031970..588f4fa369f 100644 --- a/doc/topics/git/index.md +++ b/doc/topics/git/index.md @@ -61,6 +61,10 @@ We've gathered some resources to help you to get the best from Git with GitLab. - [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/) - [Towards a production quality open source Git LFS server](https://about.gitlab.com/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/) +## Troubleshooting + +- Learn a few [Git troubleshooting](troubleshooting_git.md) techniques to help you out. + ## General information - **Articles:** diff --git a/doc/topics/git/troubleshooting_git.md b/doc/topics/git/troubleshooting_git.md new file mode 100644 index 00000000000..8555c5e91ea --- /dev/null +++ b/doc/topics/git/troubleshooting_git.md @@ -0,0 +1,82 @@ +# Troubleshooting Git + +Sometimes things don't work the way they should or as you might expect when +you're using Git. Here are some tips on troubleshooting and resolving issues +with Git. + +## Broken pipe errors on git push + +'Broken pipe' errors can occur when attempting to push to a remote repository. +When pushing you will usually see: + +``` +Write failed: Broken pipe +fatal: The remote end hung up unexpectedly +``` + +To fix this issue, here are some possible solutions. + +### Increase the POST buffer size in Git + +**If pushing over HTTP**, you can try increasing the POST buffer size in Git's +configuration. Open a terminal and enter: + +```sh +git config http.postBuffer 52428800 +``` + +The value is specified in bytes, so in the above case the buffer size has been +set to 50MB. The default is 1MB. + +### Check your SSH configuration + +**If pushing over SSH**, first check your SSH configuration as 'Broken pipe' +errors can sometimes be caused by underlying issues with SSH (such as +authentication). Make sure that SSH is correctly configured by following the +instructions in the [SSH troubleshooting] docs. + +There's another option where you can prevent session timeouts by configuring +SSH 'keep alive' either on the client or on the server (if you are a GitLab +admin and have access to the server). + +NOTE: **Note:** configuring *both* the client and the server is unnecessary. + +**To configure SSH on the client side**: + +- On UNIX, edit `~/.ssh/config` (create the file if it doesn’t exist) and + add or edit: + + ``` + Host your-gitlab-instance-url.com + ServerAliveInterval 60 + ServerAliveCountMax 5 + ``` + +- On Windows, if you are using PuTTY, go to your session properties, then + navigate to "Connection" and under "Sending of null packets to keep + session active", set "Seconds between keepalives (0 to turn off)" to `60`. + +**To configure SSH on the server side**, edit `/etc/ssh/sshd_config` and add: + +``` +ClientAliveInterval 60 +ClientAliveCountMax 5 +``` + +### Running a git repack + +**If 'pack-objects' type errors are also being displayed**, you can try to +run a `git repack` before attempting to push to the remote repository again: + +```sh +git repack +git push +``` + +### Upgrade your Git client + +In case you're running an older version of Git (< 2.9), consider upgrading +to >= 2.9 (see [Broken pipe when pushing to Git repository][Broken-Pipe]). + +[SSH troubleshooting]: ../../ssh/README.md#troubleshooting "SSH Troubleshooting" +[Broken-Pipe]: https://stackoverflow.com/questions/19120120/broken-pipe-when-pushing-to-git-repository/36971469#36971469 "StackOverflow: 'Broken pipe when pushing to Git repository'" diff --git a/doc/update/10.2-to-10.3.md b/doc/update/10.2-to-10.3.md new file mode 100644 index 00000000000..07f9ee965f0 --- /dev/null +++ b/doc/update/10.2-to-10.3.md @@ -0,0 +1,360 @@ +--- +comments: false +--- + +# From 10.2 to 10.3 + +Make sure you view this update guide from the tag (version) of GitLab you would +like to install. In most cases this should be the highest numbered production +tag (without rc in it). You can select the tag in the version dropdown at the +top left corner of GitLab (below the menu bar). + +If the highest number stable branch is unclear please check the +[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation +guide links by version. + +### 1. Stop server + +```bash +sudo service gitlab stop +``` + +### 2. Backup + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Update Ruby + +NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be +sure to upgrade your interpreter if necessary. + +You can check which version you are running with `ruby -v`. + +Download and compile Ruby: + +```bash +mkdir /tmp/ruby && cd /tmp/ruby +curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.5.tar.gz +echo '3247e217d6745c27ef23bdc77b6abdb4b57a118f ruby-2.3.5.tar.gz' | shasum -c - && tar xzf ruby-2.3.5.tar.gz +cd ruby-2.3.5 +./configure --disable-install-rdoc +make +sudo make install +``` + +Install Bundler: + +```bash +sudo gem install bundler --no-ri --no-rdoc +``` + +### 4. Update Node + +GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and +it has a minimum requirement of node v4.3.0. + +You can check which version you are running with `node -v`. If you are running +a version older than `v4.3.0` you will need to update to a newer version. You +can find instructions to install from community maintained packages or compile +from source at the nodejs.org website. + +<https://nodejs.org/en/download/> + + +Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage +JavaScript dependencies. + +```bash +curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list +sudo apt-get update +sudo apt-get install yarn +``` + +More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install). + +### 5. Update Go + +NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go +1.5.x through 1.7.x. Be sure to upgrade your installation if necessary. + +You can check which version you are running with `go version`. + +Download and install Go: + +```bash +# Remove former Go installation folder +sudo rm -rf /usr/local/go + +curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz +echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ + sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ +rm go1.8.3.linux-amd64.tar.gz +``` + +### 6. Get latest code + +```bash +cd /home/git/gitlab + +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +sudo -u git -H git checkout -- locale +``` + +For GitLab Community Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 10-3-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +cd /home/git/gitlab + +sudo -u git -H git checkout 10-3-stable-ee +``` + +### 7. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell + +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION) +sudo -u git -H bin/compile +``` + +### 8. Update gitlab-workhorse + +Install and compile gitlab-workhorse. GitLab-Workhorse uses +[GNU Make](https://www.gnu.org/software/make/). +If you are not using Linux you may have to run `gmake` instead of +`make` below. + +```bash +cd /home/git/gitlab-workhorse + +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION) +sudo -u git -H make +``` + +### 9. Update Gitaly + +#### New Gitaly configuration options required + +In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`. + +```shell +echo ' +[gitaly-ruby] +dir = "/home/git/gitaly/ruby" + +[gitlab-shell] +dir = "/home/git/gitlab-shell" +' | sudo -u git tee -a /home/git/gitaly/config.toml +``` + +#### Check Gitaly configuration + +Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly +configuration file may contain syntax errors. The block name +`[[storages]]`, which may occur more than once in your `config.toml` +file, should be `[[storage]]` instead. + +```shell +sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml +``` + +#### Compile Gitaly + +```shell +cd /home/git/gitaly +sudo -u git -H git fetch --all --tags +sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION) +sudo -u git -H make +``` + +### 10. Update MySQL permissions + +If you are using MySQL you need to grant the GitLab user the necessary +permissions on the database: + +```bash +mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';" +``` + +If you use MySQL with replication, or just have MySQL configured with binary logging, +you will need to also run the following on all of your MySQL servers: + +```bash +mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;" +``` + +You can make this setting permanent by adding it to your `my.cnf`: + +``` +log_bin_trust_function_creators=1 +``` + +### 11. Update configuration files + +#### New configuration options for `gitlab.yml` + +There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +cd /home/git/gitlab + +git diff origin/10-2-stable:config/gitlab.yml.example origin/10-3-stable:config/gitlab.yml.example +``` + +#### Nginx configuration + +Ensure you're still up-to-date with the latest NGINX configuration changes: + +```sh +cd /home/git/gitlab + +# For HTTPS configurations +git diff origin/10-2-stable:lib/support/nginx/gitlab-ssl origin/10-3-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/10-2-stable:lib/support/nginx/gitlab origin/10-3-stable:lib/support/nginx/gitlab +``` + +If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx +configuration as GitLab application no longer handles setting it. + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-workhorse listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/lib/support/init.d/gitlab.default.example#L38 + +#### SMTP configuration + +If you're installing from source and use SMTP to deliver mail, you will need to add the following line +to config/initializers/smtp_settings.rb: + +```ruby +ActionMailer::Base.delivery_method = :smtp +``` + +See [smtp_settings.rb.sample] as an example. + +[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/config/initializers/smtp_settings.rb.sample#L13 + +#### Init script + +There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`: + +```sh +cd /home/git/gitlab + +git diff origin/10-2-stable:lib/support/init.d/gitlab.default.example origin/10-3-stable:lib/support/init.d/gitlab.default.example +``` + +Ensure you're still up-to-date with the latest init script changes: + +```bash +cd /home/git/gitlab + +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +For Ubuntu 16.04.1 LTS: + +```bash +sudo systemctl daemon-reload +``` + +### 12. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment + +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment + +# Optional: clean up old gems +sudo -u git -H bundle clean + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Compile GetText PO files + +sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production + +# Update node dependencies and recompile assets +sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production + +# Clean up cache +sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production +``` + +**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md). + +### 13. Start application + +```bash +sudo service gitlab start +sudo service nginx restart +``` + +### 14. Check application status + +Check if GitLab and its environment are configured correctly: + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +``` + +To make sure you didn't miss anything run a more thorough check: + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +``` + +If all items are green, then congratulations, the upgrade is complete! + +## Things went south? Revert to previous version (10.0) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 9.5 to 10.0](9.5-to-10.0.md), except for the +database migration (the backup is already migrated to the previous version). + +### 2. Restore from the backup + +```bash +cd /home/git/gitlab + +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/config/gitlab.yml.example +[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/lib/support/init.d/gitlab.default.example diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md index daa5463d680..43451844f2d 100644 --- a/doc/user/project/pipelines/settings.md +++ b/doc/user/project/pipelines/settings.md @@ -68,7 +68,7 @@ in the pipelines settings page. Access to pipelines and job details (including output of logs and artifacts) is checked against your current user access level and the **Public pipelines** -project setting. +project setting under your project's **Settings > CI/CD > General pipelines settings**. If **Public pipelines** is enabled (default): diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 7887b886c03..4f36bbd760f 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -110,10 +110,12 @@ module API end params do use :pagination + optional :order_by, type: String, values: %w[email name commits], default: nil, desc: 'Return contributors ordered by `name` or `email` or `commits`' + optional :sort, type: String, values: %w[asc desc], default: nil, desc: 'Sort by asc (ascending) or desc (descending)' end get ':id/repository/contributors' do begin - contributors = ::Kaminari.paginate_array(user_project.repository.contributors) + contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort])) present paginate(contributors), with: Entities::Contributor rescue not_found! diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 65d55576ac2..9112164f22e 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -1,7 +1,11 @@ +# rubocop:disable Style/ClassVars + module Gitlab module Metrics # Class for tracking timing information about method calls class MethodCall + @@measurement_enabled_cache = Concurrent::AtomicBoolean.new(false) + @@measurement_enabled_cache_expires_at = Concurrent::AtomicFixnum.new(Time.now.to_i) MUTEX = Mutex.new BASE_LABELS = { module: nil, method: nil }.freeze attr_reader :real_time, :cpu_time, :call_count, :labels @@ -18,6 +22,10 @@ module Gitlab end end + def self.measurement_enabled_cache_expires_at + @@measurement_enabled_cache_expires_at + end + # name - The full name of the method (including namespace) such as # `User#sign_in`. # @@ -72,7 +80,14 @@ module Gitlab end def call_measurement_enabled? - Feature.get(:prometheus_metrics_method_instrumentation).enabled? + expires_at = @@measurement_enabled_cache_expires_at.value + if expires_at < Time.now.to_i + if @@measurement_enabled_cache_expires_at.compare_and_set(expires_at, 1.minute.from_now.to_i) + @@measurement_enabled_cache.value = Feature.get(:prometheus_metrics_method_instrumentation).enabled? + end + end + + @@measurement_enabled_cache.value end end end diff --git a/lib/gitlab/tcp_checker.rb b/lib/gitlab/tcp_checker.rb new file mode 100644 index 00000000000..6e24e46d0ea --- /dev/null +++ b/lib/gitlab/tcp_checker.rb @@ -0,0 +1,45 @@ +module Gitlab + class TcpChecker + attr_reader :remote_host, :remote_port, :local_host, :local_port, :error + + def initialize(remote_host, remote_port, local_host = nil, local_port = nil) + @remote_host = remote_host + @remote_port = remote_port + @local_host = local_host + @local_port = local_port + end + + def local + join_host_port(local_host, local_port) + end + + def remote + join_host_port(remote_host, remote_port) + end + + def check(timeout: 10) + Socket.tcp( + remote_host, remote_port, + local_host, local_port, + connect_timeout: timeout + ) do |sock| + @local_port, @local_host = Socket.unpack_sockaddr_in(sock.local_address) + @remote_port, @remote_host = Socket.unpack_sockaddr_in(sock.remote_address) + end + + true + rescue => err + @error = err + + false + end + + private + + def join_host_port(host, port) + host = "[#{host}]" if host.include?(':') + + "#{host}:#{port}" + end + end +end diff --git a/lib/tasks/gitlab/tcp_check.rake b/lib/tasks/gitlab/tcp_check.rake new file mode 100644 index 00000000000..1400f57d6b9 --- /dev/null +++ b/lib/tasks/gitlab/tcp_check.rake @@ -0,0 +1,20 @@ +namespace :gitlab do + desc "GitLab | Check TCP connectivity to a specific host and port" + task :tcp_check, [:host, :port] => :environment do |_t, args| + unless args.host && args.port + puts "Please specify a host and port: `rake gitlab:tcp_check[example.com,80]`".color(:red) + exit 1 + end + + checker = Gitlab::TcpChecker.new(args.host, args.port) + + if checker.check + puts "TCP connection from #{checker.local} to #{checker.remote} succeeded".color(:green) + else + puts "TCP connection to #{checker.remote} failed: #{checker.error}".color(:red) + puts + puts 'Check that host and port are correct, and that the traffic is permitted through any firewalls.' + exit 1 + end + end +end diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb index f4f12a095fc..4e2d8e8969e 100644 --- a/spec/factories/commits.rb +++ b/spec/factories/commits.rb @@ -2,15 +2,28 @@ require_relative '../support/repo_helpers' FactoryGirl.define do factory :commit do - git_commit RepoHelpers.sample_commit + transient do + author nil + end + + git_commit do + commit = RepoHelpers.sample_commit + + if author + commit.author_email = author.email + commit.author_name = author.name + end + + commit + end project initialize_with do new(git_commit, project) end - after(:build) do |commit| - allow(commit).to receive(:author).and_return build(:author) + after(:build) do |commit, evaluator| + allow(commit).to receive(:author).and_return(evaluator.author || build(:author)) end trait :without_author do diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index 6f916078b1a..94133c62b5c 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -13,7 +13,7 @@ feature 'Dashboard Todos' do end it 'shows "All done" message' do - expect(page).to have_content 'Todos let you see what you should do next.' + expect(page).to have_content 'Todos let you see what you should do next' end end diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index c60883911f7..0848857ed1e 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -25,7 +25,7 @@ describe 'Profile account page', :js do fill_in 'password', with: '12345678' - page.within '.popup-dialog' do + page.within '.modal' do click_button 'Delete account' end @@ -38,7 +38,7 @@ describe 'Profile account page', :js do fill_in 'password', with: 'testing123' - page.within '.popup-dialog' do + page.within '.modal' do click_button 'Delete account' end diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index 156293289dd..8f06328962e 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -20,7 +20,7 @@ feature 'Multi-file editor new directory', :js do click_link('New directory') - page.within('.popup-dialog') do + page.within('.modal') do find('.form-control').set('foldername') click_button('Create directory') diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index 8fb8476e631..bdebc12ef47 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -20,7 +20,7 @@ feature 'Multi-file editor new file', :js do click_link('New file') - page.within('.popup-dialog') do + page.within('.modal') do find('.form-control').set('filename') click_button('Create file') diff --git a/spec/fixtures/api/schemas/contributor.json b/spec/fixtures/api/schemas/contributor.json new file mode 100644 index 00000000000..e88470a2363 --- /dev/null +++ b/spec/fixtures/api/schemas/contributor.json @@ -0,0 +1,18 @@ +{ + "type": "object", + "required" : [ + "name", + "email", + "commits", + "additions", + "deletions" + ], + "properties" : { + "name": { "type": "string" }, + "email": { "type": "string" }, + "commits": { "type": "integer" }, + "additions": { "type": "integer" }, + "deletions": { "type": "integer" } + }, + "additionalProperties": false +} diff --git a/spec/fixtures/api/schemas/contributors.json b/spec/fixtures/api/schemas/contributors.json new file mode 100644 index 00000000000..a9f1d1ea64f --- /dev/null +++ b/spec/fixtures/api/schemas/contributors.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "items": { "$ref": "contributor.json" } +} diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js index 0f7bf9ec712..2e5b65f5610 100644 --- a/spec/javascripts/datetime_utility_spec.js +++ b/spec/javascripts/datetime_utility_spec.js @@ -1,110 +1,108 @@ import * as datetimeUtility from '~/lib/utils/datetime_utility'; -(() => { - describe('Date time utils', () => { - describe('timeFor', () => { - it('returns `past due` when in past', () => { - const date = new Date(); - date.setFullYear(date.getFullYear() - 1); - - expect( - gl.utils.timeFor(date), - ).toBe('Past due'); - }); - - it('returns remaining time when in the future', () => { - const date = new Date(); - date.setFullYear(date.getFullYear() + 1); - - // Add a day to prevent a transient error. If date is even 1 second - // short of a full year, timeFor will return '11 months remaining' - date.setDate(date.getDate() + 1); - - expect( - gl.utils.timeFor(date), - ).toBe('1 year remaining'); - }); +describe('Date time utils', () => { + describe('timeFor', () => { + it('returns `past due` when in past', () => { + const date = new Date(); + date.setFullYear(date.getFullYear() - 1); + + expect( + datetimeUtility.timeFor(date), + ).toBe('Past due'); }); - describe('get day name', () => { - it('should return Sunday', () => { - const day = gl.utils.getDayName(new Date('07/17/2016')); - expect(day).toBe('Sunday'); - }); - - it('should return Monday', () => { - const day = gl.utils.getDayName(new Date('07/18/2016')); - expect(day).toBe('Monday'); - }); - - it('should return Tuesday', () => { - const day = gl.utils.getDayName(new Date('07/19/2016')); - expect(day).toBe('Tuesday'); - }); - - it('should return Wednesday', () => { - const day = gl.utils.getDayName(new Date('07/20/2016')); - expect(day).toBe('Wednesday'); - }); - - it('should return Thursday', () => { - const day = gl.utils.getDayName(new Date('07/21/2016')); - expect(day).toBe('Thursday'); - }); - - it('should return Friday', () => { - const day = gl.utils.getDayName(new Date('07/22/2016')); - expect(day).toBe('Friday'); - }); - - it('should return Saturday', () => { - const day = gl.utils.getDayName(new Date('07/23/2016')); - expect(day).toBe('Saturday'); - }); - }); + it('returns remaining time when in the future', () => { + const date = new Date(); + date.setFullYear(date.getFullYear() + 1); + + // Add a day to prevent a transient error. If date is even 1 second + // short of a full year, timeFor will return '11 months remaining' + date.setDate(date.getDate() + 1); - describe('get day difference', () => { - it('should return 7', () => { - const firstDay = new Date('07/01/2016'); - const secondDay = new Date('07/08/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(7); - }); - - it('should return 31', () => { - const firstDay = new Date('07/01/2016'); - const secondDay = new Date('08/01/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(31); - }); - - it('should return 365', () => { - const firstDay = new Date('07/02/2015'); - const secondDay = new Date('07/01/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(365); - }); + expect( + datetimeUtility.timeFor(date), + ).toBe('1 year remaining'); }); }); - describe('timeIntervalInWords', () => { - it('should return string with number of minutes and seconds', () => { - expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds'); - expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second'); - expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds'); - expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds'); + describe('get day name', () => { + it('should return Sunday', () => { + const day = datetimeUtility.getDayName(new Date('07/17/2016')); + expect(day).toBe('Sunday'); + }); + + it('should return Monday', () => { + const day = datetimeUtility.getDayName(new Date('07/18/2016')); + expect(day).toBe('Monday'); + }); + + it('should return Tuesday', () => { + const day = datetimeUtility.getDayName(new Date('07/19/2016')); + expect(day).toBe('Tuesday'); + }); + + it('should return Wednesday', () => { + const day = datetimeUtility.getDayName(new Date('07/20/2016')); + expect(day).toBe('Wednesday'); + }); + + it('should return Thursday', () => { + const day = datetimeUtility.getDayName(new Date('07/21/2016')); + expect(day).toBe('Thursday'); + }); + + it('should return Friday', () => { + const day = datetimeUtility.getDayName(new Date('07/22/2016')); + expect(day).toBe('Friday'); + }); + + it('should return Saturday', () => { + const day = datetimeUtility.getDayName(new Date('07/23/2016')); + expect(day).toBe('Saturday'); }); }); - describe('dateInWords', () => { - const date = new Date('07/01/2016'); + describe('get day difference', () => { + it('should return 7', () => { + const firstDay = new Date('07/01/2016'); + const secondDay = new Date('07/08/2016'); + const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(7); + }); - it('should return date in words', () => { - expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016'); + it('should return 31', () => { + const firstDay = new Date('07/01/2016'); + const secondDay = new Date('08/01/2016'); + const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(31); }); - it('should return abbreviated month name', () => { - expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016'); + it('should return 365', () => { + const firstDay = new Date('07/02/2015'); + const secondDay = new Date('07/01/2016'); + const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(365); }); }); -})(); +}); + +describe('timeIntervalInWords', () => { + it('should return string with number of minutes and seconds', () => { + expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds'); + expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second'); + expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds'); + expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds'); + }); +}); + +describe('dateInWords', () => { + const date = new Date('07/01/2016'); + + it('should return date in words', () => { + expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016'); + }); + + it('should return abbreviated month name', () => { + expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016'); + }); +}); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js index 5b64cbb2dfc..2f28c5bbf01 100644 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ b/spec/javascripts/deploy_keys/components/key_spec.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import DeployKeysStore from '~/deploy_keys/store'; import key from '~/deploy_keys/components/key.vue'; +import { getTimeago } from '~/lib/utils/datetime_utility'; describe('Deploy keys key', () => { let vm; @@ -37,7 +38,7 @@ describe('Deploy keys key', () => { it('renders human friendly formatted created date', () => { expect( vm.$el.querySelector('.key-created-at').textContent.trim(), - ).toBe(`created ${gl.utils.getTimeago().format(deployKey.created_at)}`); + ).toBe(`created ${getTimeago().format(deployKey.created_at)}`); }); it('shows edit button', () => { diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js index 2ce1a749a96..7a5c1da4d1d 100644 --- a/spec/javascripts/groups/components/item_actions_spec.js +++ b/spec/javascripts/groups/components/item_actions_spec.js @@ -36,27 +36,27 @@ describe('ItemActionsComponent', () => { describe('methods', () => { describe('onLeaveGroup', () => { - it('should change `dialogStatus` prop to `true` which shows confirmation dialog', () => { - expect(vm.dialogStatus).toBeFalsy(); + it('should change `modalStatus` prop to `true` which shows confirmation dialog', () => { + expect(vm.modalStatus).toBeFalsy(); vm.onLeaveGroup(); - expect(vm.dialogStatus).toBeTruthy(); + expect(vm.modalStatus).toBeTruthy(); }); }); describe('leaveGroup', () => { - it('should change `dialogStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => { + it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => { spyOn(eventHub, '$emit'); - vm.dialogStatus = true; + vm.modalStatus = true; vm.leaveGroup(true); - expect(vm.dialogStatus).toBeFalsy(); + expect(vm.modalStatus).toBeFalsy(); expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup); }); - it('should change `dialogStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => { + it('should change `modalStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => { spyOn(eventHub, '$emit'); - vm.dialogStatus = true; + vm.modalStatus = true; vm.leaveGroup(false); - expect(vm.dialogStatus).toBeFalsy(); + expect(vm.modalStatus).toBeFalsy(); expect(eventHub.$emit).not.toHaveBeenCalled(); }); }); @@ -99,9 +99,9 @@ describe('ItemActionsComponent', () => { newVm.$destroy(); }); - it('should show modal dialog when `dialogStatus` is set to `true`', () => { - vm.dialogStatus = true; - const modalDialogEl = vm.$el.querySelector('.modal.popup-dialog'); + it('should show modal dialog when `modalStatus` is set to `true`', () => { + vm.modalStatus = true; + const modalDialogEl = vm.$el.querySelector('.modal'); expect(modalDialogEl).toBeDefined(); expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index 729c3c29f22..7159148f8fa 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -272,10 +272,10 @@ describe('Issuable output', () => { }); }); - it('opens recaptcha dialog if update rejected as spam', (done) => { + it('opens recaptcha modal if update rejected as spam', (done) => { function mockScriptSrc() { const recaptchaChild = vm.$children - .find(child => child.$options._componentTag === 'recaptcha-dialog'); // eslint-disable-line no-underscore-dangle + .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle recaptchaChild.scriptSrc = '//scriptsrc'; } @@ -302,7 +302,7 @@ describe('Issuable output', () => { .then(promise) .then(() => setTimeoutPromise()) .then(() => { - modal = vm.$el.querySelector('.js-recaptcha-dialog'); + modal = vm.$el.querySelector('.js-recaptcha-modal'); expect(modal.style.display).not.toEqual('none'); expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html'); diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js index 2e000a1063f..0da25bdca9c 100644 --- a/spec/javascripts/issue_show/components/description_spec.js +++ b/spec/javascripts/issue_show/components/description_spec.js @@ -54,7 +54,7 @@ describe('Description component', () => { it('opens recaptcha dialog if update rejected as spam', (done) => { let modal; const recaptchaChild = vm.$children - .find(child => child.$options._componentTag === 'recaptcha-dialog'); // eslint-disable-line no-underscore-dangle + .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle recaptchaChild.scriptSrc = '//scriptsrc'; @@ -64,7 +64,7 @@ describe('Description component', () => { vm.$nextTick() .then(() => { - modal = vm.$el.querySelector('.js-recaptcha-dialog'); + modal = vm.$el.querySelector('.js-recaptcha-modal'); expect(modal.style.display).not.toEqual('none'); expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html'); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 3636aac79a0..2cd2e63b15d 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -55,7 +55,7 @@ describe('Issue', function() { } function findElements(isIssueInitiallyOpen) { - $boxClosed = $('div.status-box-closed'); + $boxClosed = $('div.status-box-issue-closed'); expect($boxClosed).toExist(); expect($boxClosed).toHaveText('Closed'); diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 2612c5fd7bc..e09b8dc7fc5 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -222,7 +222,6 @@ import '~/notes'; notes.note_ids = []; notes.updatedNotesTrackingMap = {}; - spyOn(gl.utils, 'localTimeAgo'); spyOn(Notes, 'isNewNote').and.callThrough(); spyOn(Notes, 'isUpdatedNote').and.callThrough(); spyOn(Notes, 'animateAppendNote').and.callThrough(); @@ -349,7 +348,6 @@ import '~/notes'; ]); notes.note_ids = []; - spyOn(gl.utils, 'localTimeAgo'); spyOn(Notes, 'isNewNote'); spyOn(Notes, 'animateAppendNote'); Notes.isNewNote.and.returnValue(true); diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index 5505f983d71..72790eb215a 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -41,7 +41,7 @@ import '~/right_sidebar'; loadFixtures(fixtureName); this.sidebar = new Sidebar; $aside = $('.right-sidebar'); - $page = $('.page-with-sidebar'); + $page = $('.layout-page'); $icon = $aside.find('i'); $toggle = $aside.find('.js-sidebar-toggle'); return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index 1e4c2c9faad..206f95abc1a 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -1,7 +1,7 @@ /* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */ import '~/gl_dropdown'; -import '~/search_autocomplete'; +import SearchAutocomplete from '~/search_autocomplete'; import '~/lib/utils/common_utils'; import * as urlUtils from '~/lib/utils/url_utility'; @@ -128,7 +128,7 @@ import * as urlUtils from '~/lib/utils/url_utility'; window.gon.current_user_id = userId; window.gon.current_username = userName; - return widget = new gl.SearchAutocomplete; + return widget = new SearchAutocomplete(); }); afterEach(function() { diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 946f98379ce..763a15e710b 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -1,44 +1,42 @@ /* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */ -import '~/syntax_highlight'; +import syntaxHighlight from '~/syntax_highlight'; -(function() { - describe('Syntax Highlighter', function() { - var stubUserColorScheme; - stubUserColorScheme = function(value) { - if (window.gon == null) { - window.gon = {}; - } - return window.gon.user_color_scheme = value; - }; - describe('on a js-syntax-highlight element', function() { - beforeEach(function() { - return setFixtures('<div class="js-syntax-highlight"></div>'); - }); - return it('applies syntax highlighting', function() { - stubUserColorScheme('monokai'); - $('.js-syntax-highlight').syntaxHighlight(); - return expect($('.js-syntax-highlight')).toHaveClass('monokai'); - }); +describe('Syntax Highlighter', function() { + var stubUserColorScheme; + stubUserColorScheme = function(value) { + if (window.gon == null) { + window.gon = {}; + } + return window.gon.user_color_scheme = value; + }; + describe('on a js-syntax-highlight element', function() { + beforeEach(function() { + return setFixtures('<div class="js-syntax-highlight"></div>'); }); - return describe('on a parent element', function() { - beforeEach(function() { - return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); - }); - it('applies highlighting to all applicable children', function() { - stubUserColorScheme('monokai'); - $('.parent').syntaxHighlight(); - expect($('.parent, .foo')).not.toHaveClass('monokai'); - return expect($('.monokai').length).toBe(2); - }); - return it('prevents an infinite loop when no matches exist', function() { - var highlight; - setFixtures('<div></div>'); - highlight = function() { - return $('div').syntaxHighlight(); - }; - return expect(highlight).not.toThrow(); - }); + return it('applies syntax highlighting', function() { + stubUserColorScheme('monokai'); + syntaxHighlight($('.js-syntax-highlight')); + return expect($('.js-syntax-highlight')).toHaveClass('monokai'); }); }); -}).call(window); + return describe('on a parent element', function() { + beforeEach(function() { + return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); + }); + it('applies highlighting to all applicable children', function() { + stubUserColorScheme('monokai'); + syntaxHighlight($('.parent')); + expect($('.parent, .foo')).not.toHaveClass('monokai'); + return expect($('.monokai').length).toBe(2); + }); + return it('prevents an infinite loop when no matches exist', function() { + var highlight; + setFixtures('<div></div>'); + highlight = function() { + return syntaxHighlight($('div')); + }; + return expect(highlight).not.toThrow(); + }); + }); +}); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js index d5b8947c86f..db7d083065b 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import * as urlUtils from '~/lib/utils/url_utility'; import deploymentComponent from '~/vue_merge_request_widget/components/mr_widget_deployment'; import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service'; +import { getTimeago } from '~/lib/utils/datetime_utility'; const deploymentMockData = [ { @@ -49,7 +50,7 @@ describe('MRWidgetDeployment', () => { describe('formatDate', () => { it('should work', () => { - const readable = gl.utils.getTimeago().format(deployment.deployed_at); + const readable = getTimeago().format(deployment.deployed_at); expect(vm.formatDate(deployment.deployed_at)).toEqual(readable); }); }); diff --git a/spec/javascripts/vue_shared/components/popup_dialog_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js index 5c1d2a196f4..721f4044659 100644 --- a/spec/javascripts/vue_shared/components/popup_dialog_spec.js +++ b/spec/javascripts/vue_shared/components/modal_spec.js @@ -1,11 +1,11 @@ import Vue from 'vue'; -import PopupDialog from '~/vue_shared/components/popup_dialog.vue'; +import modal from '~/vue_shared/components/modal.vue'; import mountComponent from '../../helpers/vue_mount_component_helper'; -describe('PopupDialog', () => { +describe('Modal', () => { it('does not render a primary button if no primaryButtonLabel', () => { - const popupDialog = Vue.extend(PopupDialog); - const vm = mountComponent(popupDialog); + const modalComponent = Vue.extend(modal); + const vm = mountComponent(modalComponent); expect(vm.$el.querySelector('.js-primary-button')).toBeNull(); }); diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js index b4c1f70ed1e..b4fb568f1d4 100644 --- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js +++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import timeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import '~/lib/utils/datetime_utility'; +import { formatDate, getTimeago } from '~/lib/utils/datetime_utility'; describe('Time ago with tooltip component', () => { let TimeagoTooltip; @@ -24,10 +24,10 @@ describe('Time ago with tooltip component', () => { expect(vm.$el.tagName).toEqual('TIME'); expect( vm.$el.getAttribute('data-original-title'), - ).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z')); + ).toEqual(formatDate('2017-05-08T14:57:39.781Z')); expect(vm.$el.getAttribute('data-placement')).toEqual('top'); - const timeago = gl.utils.getTimeago(); + const timeago = getTimeago(); expect(vm.$el.textContent.trim()).toEqual(timeago.format('2017-05-08T14:57:39.781Z')); }); diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb index 79d2c071446..e1c4f9cfea7 100644 --- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb +++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb @@ -2,19 +2,20 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migration, schema: 20170929131201 do let(:migration) { described_class.new } + let(:projects) { table(:projects) } - let(:base1) { create(:project) } - let(:base1_fork1) { create(:project) } - let(:base1_fork2) { create(:project) } + let(:base1) { projects.create } + let(:base1_fork1) { projects.create } + let(:base1_fork2) { projects.create } - let(:base2) { create(:project) } - let(:base2_fork1) { create(:project) } - let(:base2_fork2) { create(:project) } + let(:base2) { projects.create } + let(:base2_fork1) { projects.create } + let(:base2_fork2) { projects.create } - let(:fork_of_fork) { create(:project) } - let(:fork_of_fork2) { create(:project) } - let(:second_level_fork) { create(:project) } - let(:third_level_fork) { create(:project) } + let(:fork_of_fork) { projects.create } + let(:fork_of_fork2) { projects.create } + let(:second_level_fork) { projects.create } + let(:third_level_fork) { projects.create } let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) } let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) } @@ -97,7 +98,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat end it 'does not miss members for forks of forks for which the root was deleted' do - forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: create(:project).id) + forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: projects.create.id) base1.destroy expect(migration.missing_members?(7, 10)).to be_falsy @@ -105,8 +106,8 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat context 'with more forks' do before do - forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id) - forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id) + forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id) + forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id) end it 'only processes a single batch of links at a time' do diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb index cb52d971047..3998ca940a4 100644 --- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb @@ -225,7 +225,8 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati let(:user_class) { table(:users) } let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) } let(:namespace) { create(:namespace, owner: author) } - let(:project) { create(:project_empty_repo, namespace: namespace, creator: author) } + let(:projects) { table(:projects) } + let(:project) { projects.create(namespace_id: namespace.id, creator_id: author.id) } # We can not rely on FactoryGirl as the state of Event may change in ways that # the background migration does not expect, hence we use the Event class of diff --git a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb index e52baf8dde7..8582af96199 100644 --- a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb @@ -2,10 +2,11 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, schema: 20170929131201 do let(:migration) { described_class.new } - let(:base1) { create(:project) } + let(:projects) { table(:projects) } + let(:base1) { projects.create } - let(:base2) { create(:project) } - let(:base2_fork1) { create(:project) } + let(:base2) { projects.create } + let(:base2_fork1) { projects.create } let!(:forked_project_links) { table(:forked_project_links) } let!(:fork_networks) { table(:fork_networks) } @@ -18,10 +19,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch # A normal fork link forked_project_links.create(id: 1, forked_from_project_id: base1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 2, forked_from_project_id: base1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 3, forked_from_project_id: base2.id, forked_to_project_id: base2_fork1.id) @@ -29,10 +30,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch # create a fork of a fork forked_project_links.create(id: 4, forked_from_project_id: base2_fork1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 5, - forked_from_project_id: create(:project).id, - forked_to_project_id: create(:project).id) + forked_from_project_id: projects.create.id, + forked_to_project_id: projects.create.id) # Stub out the calls to the other migrations allow(BackgroundMigrationWorker).to receive(:perform_in) @@ -63,7 +64,7 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch end it 'creates a fork network for the fork of which the source was deleted' do - fork = create(:project) + fork = projects.create forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id) migration.perform(5, 8) diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb index b80df6956b0..be45c00dfe6 100644 --- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb @@ -182,13 +182,22 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do end context 'for a pre-Markdown Note attachment file path' do - class Note < ActiveRecord::Base - has_many :uploads, as: :model, dependent: :destroy + let(:model) { create(:note, :with_attachment) } + let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') } + let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) } + + before do + Upload.where(model_type: 'Note', model_id: model.id).delete_all end - let(:model) { create(:note, :with_attachment) } + # Can't use the shared example because Note doesn't have an `uploads` association + it 'creates an Upload record' do + expect do + subject.perform(1, untracked_files_for_uploads.last.id) + end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1) - it_behaves_like 'non_markdown_file' + expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs) + end end context 'for a user avatar file path' do diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 5341addf911..78767d06462 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -20,9 +20,39 @@ describe Gitlab::Metrics::MethodCall do context 'prometheus instrumentation is enabled' do before do + allow(Feature.get(:prometheus_metrics_method_instrumentation)).to receive(:enabled?).and_call_original + described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1 Feature.get(:prometheus_metrics_method_instrumentation).enable end + around do |example| + Timecop.freeze do + example.run + end + end + + it 'caches subsequent invocations of feature check' do + 10.times do + method_call.measure { 'foo' } + end + + expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).once + end + + it 'expires feature check cache after 1 minute' do + method_call.measure { 'foo' } + + Timecop.travel(1.minute.from_now) do + method_call.measure { 'foo' } + end + + Timecop.travel(1.minute.from_now + 1.second) do + method_call.measure { 'foo' } + end + + expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).twice + end + it 'observes the performance of the supplied block' do expect(described_class.call_duration_histogram) .to receive(:observe) @@ -34,6 +64,8 @@ describe Gitlab::Metrics::MethodCall do context 'prometheus instrumentation is disabled' do before do + described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1 + Feature.get(:prometheus_metrics_method_instrumentation).disable end diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb new file mode 100644 index 00000000000..4acf0334496 --- /dev/null +++ b/spec/lib/gitlab/tcp_checker_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Gitlab::TcpChecker do + before do + @server = TCPServer.new('localhost', 0) + _, @port, _, @ip = @server.addr + end + + after do + @server.close + end + + subject(:checker) { described_class.new(@ip, @port) } + + describe '#check' do + subject { checker.check } + + it 'can connect to an open port' do + is_expected.to be_truthy + + expect(checker.error).to be_nil + end + + it 'fails to connect to a closed port' do + @server.close + + is_expected.to be_falsy + + expect(checker.error).to be_a(Errno::ECONNREFUSED) + end + end +end diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb index 05f281fffff..57ee2adaaff 100644 --- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb +++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb') describe MigrateGcpClustersToNewClustersArchitectures, :migration do - let(:project) { create(:project) } + let(:projects) { table(:projects) } + let(:project) { projects.create } let(:user) { create(:user) } - let(:service) { create(:kubernetes_service, project: project) } + let(:service) { create(:kubernetes_service, project_id: project.id) } context 'when cluster is being created' do let(:project_id) { project.id } @@ -56,8 +57,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do expect(cluster.provider_type).to eq('gcp') expect(cluster.platform_type).to eq('kubernetes') - expect(cluster.project).to eq(project) - expect(project.clusters).to include(cluster) + expect(cluster.project_ids).to include(project.id) expect(cluster.provider_gcp.cluster).to eq(cluster) expect(cluster.provider_gcp.status).to eq(status) @@ -133,8 +133,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do expect(cluster.provider_type).to eq('gcp') expect(cluster.platform_type).to eq('kubernetes') - expect(cluster.project).to eq(project) - expect(project.clusters).to include(cluster) + expect(cluster.project_ids).to include(project.id) expect(cluster.provider_gcp.cluster).to eq(cluster) expect(cluster.provider_gcp.status).to eq(status) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6b98d013ded..98a39c33319 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1857,4 +1857,20 @@ describe MergeRequest do it_behaves_like 'throttled touch' do subject { create(:merge_request, updated_at: 1.hour.ago) } end + + context 'state machine transitions' do + describe '#unlock_mr' do + subject { create(:merge_request, state: 'locked', merge_jid: 123) } + + it 'updates merge request head pipeline and sets merge_jid to nil' do + pipeline = create(:ci_empty_pipeline, project: subject.project, ref: subject.source_branch, sha: subject.source_branch_sha) + + subject.unlock_mr + + subject.reload + expect(subject.head_pipeline).to eq(pipeline) + expect(subject.merge_jid).to be_nil + end + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 129fce74f45..2c0d4db3307 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2360,4 +2360,111 @@ describe Repository do end end end + + describe '#contributors' do + let(:author_a) { build(:author, email: 'tiagonbotelho@hotmail.com', name: 'tiagonbotelho') } + let(:author_b) { build(:author, email: 'gitlab@winniehell.de', name: 'Winnie') } + let(:author_c) { build(:author, email: 'douwe@gitlab.com', name: 'Douwe Maan') } + let(:stubbed_commits) do + [build(:commit, author: author_a), + build(:commit, author: author_a), + build(:commit, author: author_b), + build(:commit, author: author_c), + build(:commit, author: author_c), + build(:commit, author: author_c)] + end + let(:order_by) { nil } + let(:sort) { nil } + + before do + allow(repository).to receive(:commits).with(nil, limit: 2000, offset: 0, skip_merges: true).and_return(stubbed_commits) + end + + subject { repository.contributors(order_by: order_by, sort: sort) } + + def expect_contributors(*contributors) + expect(subject.map(&:email)).to eq(contributors.map(&:email)) + end + + it 'returns the array of Gitlab::Contributor for the repository' do + expect_contributors(author_a, author_b, author_c) + end + + context 'order_by email' do + let(:order_by) { 'email' } + + context 'asc' do + let(:sort) { 'asc' } + + it 'returns all the contributors ordered by email asc case insensitive' do + expect_contributors(author_c, author_b, author_a) + end + end + + context 'desc' do + let(:sort) { 'desc' } + + it 'returns all the contributors ordered by email desc case insensitive' do + expect_contributors(author_a, author_b, author_c) + end + end + end + + context 'order_by name' do + let(:order_by) { 'name' } + + context 'asc' do + let(:sort) { 'asc' } + + it 'returns all the contributors ordered by name asc case insensitive' do + expect_contributors(author_c, author_a, author_b) + end + end + + context 'desc' do + let(:sort) { 'desc' } + + it 'returns all the contributors ordered by name desc case insensitive' do + expect_contributors(author_b, author_a, author_c) + end + end + end + + context 'order_by commits' do + let(:order_by) { 'commits' } + + context 'asc' do + let(:sort) { 'asc' } + + it 'returns all the contributors ordered by commits asc' do + expect_contributors(author_b, author_a, author_c) + end + end + + context 'desc' do + let(:sort) { 'desc' } + + it 'returns all the contributors ordered by commits desc' do + expect_contributors(author_c, author_a, author_b) + end + end + end + + context 'invalid ordering' do + let(:order_by) { 'unknown' } + + it 'returns the contributors unsorted' do + expect_contributors(author_a, author_b, author_c) + end + end + + context 'invalid sorting' do + let(:order_by) { 'name' } + let(:sort) { 'unknown' } + + it 'returns the contributors unsorted' do + expect_contributors(author_a, author_b, author_c) + end + end + end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 9f2ff3b5af6..741800ff61d 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -378,6 +378,28 @@ describe API::Repositories do expect(first_contributor['additions']).to eq(0) expect(first_contributor['deletions']).to eq(0) end + + context 'using sorting' do + context 'by commits desc' do + it 'returns the repository contribuors sorted by commits desc' do + get api(route, current_user), { order_by: 'commits', sort: 'desc' } + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('contributors') + expect(json_response.first['commits']).to be > json_response.last['commits'] + end + end + + context 'by name desc' do + it 'returns the repository contribuors sorted by name asc case insensitive' do + get api(route, current_user), { order_by: 'name', sort: 'asc' } + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('contributors') + expect(json_response.first['name'].downcase).to be < json_response.last['name'].downcase + end + end + end end context 'when unauthenticated', 'and project is public' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 41ce81e0651..267258b33a8 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -64,6 +64,18 @@ describe Ci::CreatePipelineService do create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project) end + context 'when related merge request is already merged' do + let!(:merged_merge_request) do + create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project, state: 'merged') + end + + it 'does not schedule update head pipeline job' do + expect(UpdateHeadPipelineForMergeRequestWorker).not_to receive(:perform_async).with(merged_merge_request.id) + + execute_service + end + end + context 'when the head pipeline sha equals merge request sha' do it 'updates head pipeline of each merge request' do merge_request_1 @@ -77,13 +89,13 @@ describe Ci::CreatePipelineService do end context 'when the head pipeline sha does not equal merge request sha' do - it 'raises the ArgumentError error from worker and does not update the head piepeline of MRs' do + it 'does not update the head piepeline of MRs' do merge_request_1 merge_request_2 allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(true) - expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.to raise_error(ArgumentError) + expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.not_to raise_error last_pipeline = Ci::Pipeline.last diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb index a7ab389b357..623b182b205 100644 --- a/spec/services/merge_requests/create_from_issue_service_spec.rb +++ b/spec/services/merge_requests/create_from_issue_service_spec.rb @@ -100,5 +100,17 @@ describe MergeRequests::CreateFromIssueService do expect(result[:merge_request].target_branch).to eq(project.default_branch) end + + it 'executes quick actions if the build service sets them in the description' do + allow(service).to receive(:merge_request).and_wrap_original do |m, *args| + m.call(*args).tap do |merge_request| + merge_request.description = "/assign #{user.to_reference}" + end + end + + result = service.execute + + expect(result[:merge_request].assignee).to eq(user) + end end end diff --git a/spec/workers/stuck_merge_jobs_worker_spec.rb b/spec/workers/stuck_merge_jobs_worker_spec.rb index f8b55e873df..c2c2a5f9121 100644 --- a/spec/workers/stuck_merge_jobs_worker_spec.rb +++ b/spec/workers/stuck_merge_jobs_worker_spec.rb @@ -14,7 +14,6 @@ describe StuckMergeJobsWorker do mr_with_sha.reload mr_without_sha.reload - expect(mr_with_sha).to be_merged expect(mr_without_sha).to be_opened expect(mr_with_sha.merge_jid).to be_present @@ -24,10 +23,13 @@ describe StuckMergeJobsWorker do it 'updates merge request to opened when locked but has not been merged' do allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123)) merge_request = create(:merge_request, :locked, merge_jid: '123', state: :locked) + pipeline = create(:ci_empty_pipeline, project: merge_request.project, ref: merge_request.source_branch, sha: merge_request.source_branch_sha) worker.perform - expect(merge_request.reload).to be_opened + merge_request.reload + expect(merge_request).to be_opened + expect(merge_request.head_pipeline).to eq(pipeline) end it 'logs updated stuck merge job ids' do diff --git a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb index 522e1566271..9adde5fc21a 100644 --- a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb +++ b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb @@ -22,7 +22,7 @@ describe UpdateHeadPipelineForMergeRequestWorker do end it 'does not update head_pipeline_id' do - expect { subject.perform(merge_request.id) }.to raise_error(ArgumentError) + expect { subject.perform(merge_request.id) }.not_to raise_error expect(merge_request.reload.head_pipeline_id).to eq(nil) end diff --git a/vendor/Dockerfile/CONTRIBUTING.md b/vendor/Dockerfile/CONTRIBUTING.md index 0878db6dd9e..3e98f2e7b5b 100644 --- a/vendor/Dockerfile/CONTRIBUTING.md +++ b/vendor/Dockerfile/CONTRIBUTING.md @@ -1,22 +1,15 @@ -The canonical repository for `Dockerfile` templates is -https://gitlab.com/gitlab-org/Dockerfile. +## Developer Certificate of Origin + License -GitLab only mirrors the templates. Please submit your merge requests to -https://gitlab.com/gitlab-org/Dockerfile. +By contributing to GitLab B.V., You accept and agree to the following terms and +conditions for Your present and future Contributions submitted to GitLab B.V. +Except for the license granted herein to GitLab B.V. and recipients of software +distributed by GitLab B.V., You reserve all right, title, and interest in and to +Your Contributions. All Contributions are subject to the following DCO + License +terms. -## Contributing +[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md) -Thank you for your interest in contributing to this GitLab project! We welcome -all contributions. By participating in this project, you agree to abide by the -[code of conduct](#code-of-conduct). - -## Contributor license agreement - -By submitting code as an individual you agree to the [individual contributor -license agreement][individual-agreement]. - -By submitting code as an entity you agree to the [corporate contributor license -agreement][corporate-agreement]. +_This notice should stay as the first item in the CONTRIBUTING.md file._ ## Code of conduct diff --git a/vendor/Dockerfile/LICENSE b/vendor/Dockerfile/LICENSE index d6c93c6fcf7..27a215686e7 100644 --- a/vendor/Dockerfile/LICENSE +++ b/vendor/Dockerfile/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +Copyright (c) 2011-2017 GitLab B.V. -Copyright (c) 2016-2017 GitLab.org +With regard to the GitLab Software: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,17 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +For all third party components incorporated into the GitLab Software, those +components are licensed under the original license provided by the owner of the +applicable component. diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore index cca150a88dd..7996ad5058e 100644 --- a/vendor/gitignore/Global/Matlab.gitignore +++ b/vendor/gitignore/Global/Matlab.gitignore @@ -1,5 +1,5 @@ ##--------------------------------------------------- -## Remove autosaves generated by the Matlab editor +## Remove autosaves generated by the MATLAB editor ## We have git for backups! ##--------------------------------------------------- @@ -14,6 +14,7 @@ # Simulink Code Generation slprj/ +sccprj/ # Session info octave-workspace diff --git a/vendor/gitignore/Go.gitignore b/vendor/gitignore/Go.gitignore index a1338d68517..ea58090bd21 100644 --- a/vendor/gitignore/Go.gitignore +++ b/vendor/gitignore/Go.gitignore @@ -9,6 +9,3 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out - -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ diff --git a/vendor/gitignore/Haskell.gitignore b/vendor/gitignore/Haskell.gitignore index eee88b2f0f7..82f3a88e17b 100644 --- a/vendor/gitignore/Haskell.gitignore +++ b/vendor/gitignore/Haskell.gitignore @@ -17,5 +17,6 @@ cabal.sandbox.config *.eventlog .stack-work/ cabal.project.local +cabal.project.local~ .HTF/ .ghc.environment.* diff --git a/vendor/gitignore/Jekyll.gitignore b/vendor/gitignore/Jekyll.gitignore index 5c91b60c063..2ca868298ce 100644 --- a/vendor/gitignore/Jekyll.gitignore +++ b/vendor/gitignore/Jekyll.gitignore @@ -1,3 +1,4 @@ _site/ .sass-cache/ +.jekyll-cache/ .jekyll-metadata diff --git a/vendor/gitignore/ROS.gitignore b/vendor/gitignore/ROS.gitignore index f8bcd117371..425641f2c3a 100644 --- a/vendor/gitignore/ROS.gitignore +++ b/vendor/gitignore/ROS.gitignore @@ -1,3 +1,5 @@ +devel/ +logs/ build/ bin/ lib/ diff --git a/vendor/gitignore/Symfony.gitignore b/vendor/gitignore/Symfony.gitignore index 85fd714a965..d098259ffb0 100644 --- a/vendor/gitignore/Symfony.gitignore +++ b/vendor/gitignore/Symfony.gitignore @@ -25,6 +25,7 @@ /bin/* !bin/console !bin/symfony_requirements +/vendor/ # Assets and user uploads /web/bundles/ @@ -37,6 +38,9 @@ # 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 b6418e51766..9bb63365618 100644 --- a/vendor/gitignore/TeX.gitignore +++ b/vendor/gitignore/TeX.gitignore @@ -215,7 +215,11 @@ TSWLatexianTemp* *~[0-9]* # auto folder when using emacs and auctex -/auto/* +./auto/* +*.el # expex forward references with \gathertags *-tags.tex + +# standalone packages +*.sta diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore index eb83a8f122d..75e5b1405da 100644 --- a/vendor/gitignore/Unity.gitignore +++ b/vendor/gitignore/Unity.gitignore @@ -1,9 +1,9 @@ -/[Ll]ibrary/ -/[Tt]emp/ -/[Oo]bj/ -/[Bb]uild/ -/[Bb]uilds/ -/Assets/AssetStoreTools* +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +[Bb]uild/ +[Bb]uilds/ +Assets/AssetStoreTools* # Visual Studio 2015 cache directory /.vs/ @@ -25,6 +25,7 @@ ExportedObj/ # Unity3D generated meta files *.pidb.meta +*.pdb.meta # Unity3D Generated File On Crash Reports sysinfo.txt diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore index 6c6e1c327fd..1daca8b50d9 100644 --- a/vendor/gitignore/UnrealEngine.gitignore +++ b/vendor/gitignore/UnrealEngine.gitignore @@ -50,6 +50,7 @@ SourceArt/**/*.tga # Binary Files Binaries/* +Plugins/*/Binaries/* # Builds Build/* @@ -70,6 +71,7 @@ Saved/* # Compiled source files for the engine to use Intermediate/* +Plugins/*/Intermediate/* # Cache files for the editor to use DerivedDataCache/* diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore index 509668db67a..6217e6c48e9 100644 --- a/vendor/gitignore/VisualStudio.gitignore +++ b/vendor/gitignore/VisualStudio.gitignore @@ -24,11 +24,14 @@ bld/ [Oo]bj/ [Ll]og/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -51,6 +54,10 @@ project.fragment.lock.json artifacts/ **/Properties/launchSettings.json +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c *_i.h @@ -247,7 +254,7 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ -# Typescript v1 declaration files +# TypeScript v1 declaration files typings/ # Visual Studio 6 build log @@ -303,3 +310,6 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 88261502d7f..da4d86b9a04 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -83,6 +83,16 @@ codequality: artifacts: paths: [codeclimate.json] +sast: + image: registry.gitlab.com/gitlab-org/gl-sast:latest + variables: + POSTGRES_DB: "false" + allow_failure: true + script: + - /app/bin/run . + artifacts: + paths: [gl-sast-report.json] + review: stage: review script: @@ -218,8 +228,8 @@ production: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume /tmp/cc:/tmp/cc" - docker run ${cc_opts} codeclimate/codeclimate init - docker run ${cc_opts} codeclimate/codeclimate analyze -f json > codeclimate.json + docker run ${cc_opts} codeclimate/codeclimate:0.69.0 init + docker run ${cc_opts} codeclimate/codeclimate:0.69.0 analyze -f json > codeclimate.json } function deploy() { @@ -345,6 +355,13 @@ production: } function build() { + + if [[ -n "$CI_REGISTRY_USER" ]]; then + echo "Logging to GitLab Container Registry with CI credentials..." + docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" + echo "" + fi + if [[ -f Dockerfile ]]; then echo "Building Dockerfile-based application..." docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" . @@ -362,12 +379,6 @@ production: echo "" fi - if [[ -n "$CI_REGISTRY_USER" ]]; then - echo "Logging to GitLab Container Registry with CI credentials..." - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" - echo "" - fi - echo "Pushing to GitLab Container Registry..." docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" echo "" @@ -402,7 +413,9 @@ production: name="$name-$track" fi - helm delete "$name" || true + if [[ -n "$(helm ls -q "^$name$")" ]]; then + helm delete "$name" + fi } before_script: diff --git a/vendor/gitlab-ci-yml/CONTRIBUTING.md b/vendor/gitlab-ci-yml/CONTRIBUTING.md index d4c057bf9dc..d33a1f06f26 100644 --- a/vendor/gitlab-ci-yml/CONTRIBUTING.md +++ b/vendor/gitlab-ci-yml/CONTRIBUTING.md @@ -1,16 +1,15 @@ -## Contributing +## Developer Certificate of Origin + License -Thank you for your interest in contributing to this GitLab project! We welcome -all contributions. By participating in this project, you agree to abide by the -[code of conduct](#code-of-conduct). +By contributing to GitLab B.V., You accept and agree to the following terms and +conditions for Your present and future Contributions submitted to GitLab B.V. +Except for the license granted herein to GitLab B.V. and recipients of software +distributed by GitLab B.V., You reserve all right, title, and interest in and to +Your Contributions. All Contributions are subject to the following DCO + License +terms. -## Contributor license agreement +[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md) -By submitting code as an individual you agree to the [individual contributor -license agreement][individual-agreement]. - -By submitting code as an entity you agree to the [corporate contributor license -agreement][corporate-agreement]. +_This notice should stay as the first item in the CONTRIBUTING.md file._ ## Code of conduct diff --git a/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml b/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml new file mode 100644 index 00000000000..4d5b6484d6e --- /dev/null +++ b/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml @@ -0,0 +1,51 @@ +# This file uses Test Kitchen with the kitchen-dokken driver to +# perform functional testing. Doing so requires that your runner be a +# Docker runner configured for privileged mode. Please see +# https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode +# for help configuring your runner properly, or, if you want to switch +# to a different driver, see http://kitchen.ci/docs/drivers + +image: "chef/chefdk" +services: + - docker:dind + +variables: + DOCKER_HOST: "tcp://docker:2375" + KITCHEN_LOCAL_YAML: ".kitchen.dokken.yml" + +stages: + - lint + - unit + - functional + +foodcritic: + stage: lint + script: + - chef exec foodcritic . + +cookstyle: + stage: lint + script: + - chef exec cookstyle . + +chefspec: + stage: unit + script: + - chef exec rspec spec + +# Set up your test matrix here. Example: +#verify-centos-6: +# stage: functional +# before_script: +# - apt-get update +# - apt-get -y install rsync +# script: +# - kitchen verify default-centos-6 --destroy=always +# +#verify-centos-7: +# stage: functional +# before_script: +# - apt-get update +# - apt-get -y install rsync +# script: +# - kitchen verify default-centos-7 --destroy=always diff --git a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml index 86e4985d8d2..d572d7a1edc 100644 --- a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml @@ -11,8 +11,8 @@ variables: # repository in /go/src/gitlab.com/namespace/project # Thus, making a symbolic link corrects this. before_script: - - mkdir -p $GOPATH/src/$REPO_NAME - - ln -svf $CI_PROJECT_DIR/* $GOPATH/src/$REPO_NAME + - mkdir -p $GOPATH/src/$(dirname $REPO_NAME) + - ln -svf $CI_PROJECT_DIR $GOPATH/src/$REPO_NAME - cd $GOPATH/src/$REPO_NAME stages: diff --git a/vendor/gitlab-ci-yml/LICENSE b/vendor/gitlab-ci-yml/LICENSE index d6c93c6fcf7..27a215686e7 100644 --- a/vendor/gitlab-ci-yml/LICENSE +++ b/vendor/gitlab-ci-yml/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +Copyright (c) 2011-2017 GitLab B.V. -Copyright (c) 2016-2017 GitLab.org +With regard to the GitLab Software: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,17 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +For all third party components incorporated into the GitLab Software, those +components are licensed under the original license provided by the owner of the +applicable component. diff --git a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml index 6573eceaa59..1463161a04b 100644 --- a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml @@ -20,4 +20,4 @@ image: "rust:latest" test:cargo: script: - rustc --version && cargo --version # Print version info for debugging - - cargo test --verbose --jobs 1 --release # Don't paralize to make errors more readable + - cargo test --verbose --jobs 1 --release # Don't parallelise to make errors more readable diff --git a/vendor/licenses.csv b/vendor/licenses.csv index 6f6ca5f8b32..b6a5c2f81a0 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -1,460 +1,78 @@ -"","","MIT,ISC,Apache 2.0,New BSD,Simplified BSD" RedCloth,4.3.2,MIT -abbrev,1.0.9,ISC -abbrev,1.1.0,ISC -accepts,1.3.3,MIT ace-rails-ap,4.1.2,MIT -acorn,3.3.0,MIT -acorn,4.0.13,MIT -acorn,5.1.1,MIT -acorn-dynamic-import,2.0.2,MIT -acorn-jsx,3.0.1,MIT -actionmailer,4.2.8,MIT -actionpack,4.2.8,MIT -actionview,4.2.8,MIT -activejob,4.2.8,MIT -activemodel,4.2.8,MIT -activerecord,4.2.8,MIT -activesupport,4.2.8,MIT +actionmailer,4.2.10,MIT +actionpack,4.2.10,MIT +actionview,4.2.10,MIT +activejob,4.2.10,MIT +activemodel,4.2.10,MIT +activerecord,4.2.10,MIT +activesupport,4.2.10,MIT acts-as-taggable-on,4.0.0,MIT addressable,2.5.2,Apache 2.0 -after,0.8.2,MIT -ajv,4.11.8,MIT -ajv,5.2.2,MIT -ajv-keywords,1.5.1,MIT -ajv-keywords,2.1.0,MIT akismet,2.0.0,MIT -align-text,0.1.4,MIT allocations,1.0.5,MIT -alphanum-sort,1.0.2,MIT -amdefine,1.0.1,BSD-3-Clause OR MIT -ansi-escapes,1.4.0,MIT -ansi-html,0.0.5,"Apache, Version 2.0" -ansi-html,0.0.7,Apache 2.0 -ansi-regex,2.1.1,MIT -ansi-styles,2.2.1,MIT -ansi-styles,3.2.0,MIT -anymatch,1.3.2,ISC -append-transform,0.4.0,MIT -aproba,1.1.1,ISC -are-we-there-yet,1.1.4,ISC arel,6.0.4,MIT -argparse,1.0.9,MIT -arr-diff,2.0.0,MIT -arr-flatten,1.0.1,MIT -array-find,1.0.0,MIT -array-find-index,1.0.2,MIT -array-flatten,1.1.1,MIT -array-flatten,2.1.1,MIT -array-slice,0.2.3,MIT -array-union,1.0.2,MIT -array-uniq,1.0.3,MIT -array-unique,0.2.1,MIT -arraybuffer.slice,0.0.6,MIT -arrify,1.0.1,MIT asana,0.6.0,MIT asciidoctor,1.5.3,MIT asciidoctor-plantuml,0.0.7,MIT -asn1,0.2.3,MIT -asn1.js,4.9.1,MIT -assert,1.4.1,MIT -assert-plus,0.2.0,MIT -assert-plus,1.0.0,MIT -async,0.9.2,MIT -async,1.5.2,MIT -async,2.4.1,MIT -async-each,1.0.1,MIT -asynckit,0.4.0,MIT +asset_sync,2.2.0,MIT atomic,1.1.99,Apache 2.0 attr_encrypted,3.0.3,MIT attr_required,1.0.0,MIT -autoprefixer,6.7.7,MIT autoprefixer-rails,6.2.3,MIT -autosize,4.0.0,MIT -aws-sign2,0.6.0,Apache 2.0 -aws4,1.6.0,MIT axiom-types,0.1.1,MIT -axios,0.16.2,MIT -babel-code-frame,6.22.0,MIT -babel-core,6.23.1,MIT -babel-eslint,7.2.1,MIT -babel-generator,6.23.0,MIT -babel-helper-bindify-decorators,6.22.0,MIT -babel-helper-builder-binary-assignment-operator-visitor,6.22.0,MIT -babel-helper-call-delegate,6.22.0,MIT -babel-helper-define-map,6.23.0,MIT -babel-helper-explode-assignable-expression,6.22.0,MIT -babel-helper-explode-class,6.22.0,MIT -babel-helper-function-name,6.23.0,MIT -babel-helper-get-function-arity,6.22.0,MIT -babel-helper-hoist-variables,6.22.0,MIT -babel-helper-optimise-call-expression,6.23.0,MIT -babel-helper-regex,6.22.0,MIT -babel-helper-remap-async-to-generator,6.22.0,MIT -babel-helper-replace-supers,6.23.0,MIT -babel-helpers,6.23.0,MIT -babel-loader,7.1.1,MIT -babel-messages,6.23.0,MIT -babel-plugin-check-es2015-constants,6.22.0,MIT -babel-plugin-istanbul,4.0.0,New BSD -babel-plugin-syntax-async-functions,6.13.0,MIT -babel-plugin-syntax-async-generators,6.13.0,MIT -babel-plugin-syntax-class-properties,6.13.0,MIT -babel-plugin-syntax-decorators,6.13.0,MIT -babel-plugin-syntax-dynamic-import,6.18.0,MIT -babel-plugin-syntax-exponentiation-operator,6.13.0,MIT -babel-plugin-syntax-object-rest-spread,6.13.0,MIT -babel-plugin-syntax-trailing-function-commas,6.22.0,MIT -babel-plugin-transform-async-generator-functions,6.22.0,MIT -babel-plugin-transform-async-to-generator,6.22.0,MIT -babel-plugin-transform-class-properties,6.23.0,MIT -babel-plugin-transform-decorators,6.22.0,MIT -babel-plugin-transform-define,1.2.0,MIT -babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT -babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT -babel-plugin-transform-es2015-block-scoping,6.23.0,MIT -babel-plugin-transform-es2015-classes,6.23.0,MIT -babel-plugin-transform-es2015-computed-properties,6.22.0,MIT -babel-plugin-transform-es2015-destructuring,6.23.0,MIT -babel-plugin-transform-es2015-duplicate-keys,6.22.0,MIT -babel-plugin-transform-es2015-for-of,6.23.0,MIT -babel-plugin-transform-es2015-function-name,6.22.0,MIT -babel-plugin-transform-es2015-literals,6.22.0,MIT -babel-plugin-transform-es2015-modules-amd,6.24.0,MIT -babel-plugin-transform-es2015-modules-commonjs,6.24.0,MIT -babel-plugin-transform-es2015-modules-systemjs,6.23.0,MIT -babel-plugin-transform-es2015-modules-umd,6.24.0,MIT -babel-plugin-transform-es2015-object-super,6.22.0,MIT -babel-plugin-transform-es2015-parameters,6.23.0,MIT -babel-plugin-transform-es2015-shorthand-properties,6.22.0,MIT -babel-plugin-transform-es2015-spread,6.22.0,MIT -babel-plugin-transform-es2015-sticky-regex,6.22.0,MIT -babel-plugin-transform-es2015-template-literals,6.22.0,MIT -babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT -babel-plugin-transform-es2015-unicode-regex,6.22.0,MIT -babel-plugin-transform-exponentiation-operator,6.22.0,MIT -babel-plugin-transform-object-rest-spread,6.23.0,MIT -babel-plugin-transform-regenerator,6.22.0,MIT -babel-plugin-transform-strict-mode,6.22.0,MIT -babel-preset-es2015,6.24.0,MIT -babel-preset-es2016,6.22.0,MIT -babel-preset-es2017,6.22.0,MIT -babel-preset-latest,6.24.0,MIT -babel-preset-stage-2,6.22.0,MIT -babel-preset-stage-3,6.22.0,MIT -babel-register,6.23.0,MIT -babel-runtime,6.22.0,MIT -babel-template,6.23.0,MIT -babel-traverse,6.23.1,MIT -babel-types,6.23.0,MIT babosa,1.0.2,MIT -babylon,6.16.1,MIT -backo2,1.0.2,MIT -balanced-match,0.4.2,MIT -balanced-match,1.0.0,MIT base32,0.3.2,MIT -base64-arraybuffer,0.1.5,MIT -base64-js,1.2.0,MIT -base64id,1.0.0,MIT -batch,0.6.1,MIT +batch-loader,1.1.1,MIT bcrypt,3.1.11,MIT -bcrypt-pbkdf,1.0.1,New BSD bcrypt_pbkdf,1.0.0,MIT -better-assert,1.0.2,MIT -big.js,3.1.3,MIT -binary-extensions,1.10.0,MIT bindata,2.4.1,ruby -blob,0.0.4,unknown -block-stream,0.0.9,ISC -bluebird,2.11.0,MIT -bluebird,3.5.0,MIT -bn.js,4.11.6,MIT -body-parser,1.17.2,MIT -bonjour,3.5.0,MIT -boom,2.10.1,New BSD bootstrap-sass,3.3.6,MIT bootstrap_form,2.7.0,MIT -brace-expansion,1.1.7,MIT -brace-expansion,1.1.8,MIT -braces,0.1.5,MIT -braces,1.8.5,MIT -brorand,1.0.7,MIT browser,2.2.0,MIT -browserify-aes,1.0.6,MIT -browserify-cipher,1.0.0,MIT -browserify-des,1.0.0,MIT -browserify-rsa,4.0.1,MIT -browserify-sign,4.0.0,ISC -browserify-zlib,0.1.4,MIT -browserslist,1.7.7,MIT -buffer,4.9.1,MIT -buffer-indexof,1.1.0,MIT -buffer-shims,1.0.0,MIT -buffer-xor,1.0.3,MIT builder,3.2.3,MIT -builtin-modules,1.1.1,MIT -builtin-status-codes,3.0.0,MIT -bytes,2.4.0,MIT -bytes,2.5.0,MIT -caller-path,0.1.0,MIT -callsite,1.0.0,unknown -callsites,0.2.0,MIT -camelcase,1.2.1,MIT -camelcase,2.1.1,MIT -camelcase,3.0.0,MIT -camelcase,4.1.0,MIT -camelcase-keys,2.1.0,MIT -caniuse-api,1.6.1,MIT -caniuse-db,1.0.30000649,CC-BY-4.0 carrierwave,1.2.1,MIT -caseless,0.12.0,Apache 2.0 cause,0.1,MIT -center-align,0.1.3,MIT -chalk,1.1.3,MIT -chalk,2.3.0,MIT charlock_holmes,0.7.5,MIT -chokidar,1.7.0,MIT chronic,0.10.2,MIT chronic_duration,0.10.6,MIT chunky_png,1.3.5,MIT -cipher-base,1.0.3,MIT -circular-json,0.3.3,MIT citrus,3.0.2,MIT -clap,1.1.3,MIT -cli-cursor,1.0.2,MIT -cli-width,2.1.0,ISC -clipboard,1.6.1,MIT -cliui,2.1.0,ISC -cliui,3.2.0,ISC -clone,1.0.2,MIT -co,4.6.0,MIT -coa,1.0.1,MIT -code-point-at,1.1.0,MIT coercible,1.0.0,MIT -color,0.11.4,MIT -color-convert,1.9.0,MIT -color-name,1.1.2,MIT -color-string,0.3.0,MIT -colormin,1.1.2,MIT -colors,1.1.2,MIT -combine-lists,1.0.1,MIT -combined-stream,1.0.5,MIT -commander,2.9.0,MIT -commondir,1.0.1,MIT -component-bind,1.0.0,unknown -component-emitter,1.1.2,unknown -component-emitter,1.2.1,MIT -component-inherit,0.0.3,unknown -compressible,2.0.11,MIT -compression,1.7.0,MIT -compression-webpack-plugin,1.0.0,MIT -concat-map,0.0.1,MIT -concat-stream,1.6.0,MIT concurrent-ruby-ext,1.0.5,MIT -config-chain,1.1.11,MIT -configstore,1.4.0,Simplified BSD -connect,3.6.3,MIT -connect-history-api-fallback,1.3.0,MIT connection_pool,2.2.1,MIT -console-browserify,1.1.0,MIT -console-control-strings,1.1.0,ISC -consolidate,0.14.5,MIT -constants-browserify,1.0.0,MIT -contains-path,0.1.0,MIT -content-disposition,0.5.2,MIT -content-type,1.0.2,MIT -convert-source-map,1.3.0,MIT -cookie,0.3.1,MIT -cookie-signature,1.0.6,MIT -copy-webpack-plugin,4.0.1,MIT -core-js,2.3.0,MIT -core-js,2.4.1,MIT -core-util-is,1.0.2,MIT -cosmiconfig,2.1.1,MIT crack,0.4.3,MIT -create-ecdh,4.0.0,MIT -create-hash,1.1.2,MIT -create-hmac,1.1.4,MIT creole,0.5.0,ruby -cropper,2.3.0,MIT -cross-spawn,5.1.0,MIT -cryptiles,2.0.5,New BSD -crypto-browserify,3.11.0,MIT -css-color-names,0.0.4,MIT -css-loader,0.28.0,MIT -css-selector-tokenizer,0.6.0,MIT -css-selector-tokenizer,0.7.0,MIT css_parser,1.5.0,MIT -cssesc,0.1.0,MIT -cssnano,3.10.0,MIT -csso,2.3.2,MIT -currently-unhandled,0.4.1,MIT -custom-event,1.0.1,MIT -d,0.1.1,MIT -d,1.0.0,MIT -d3,3.5.11,New BSD d3_rails,3.5.11,MIT -dashdash,1.14.1,MIT -date-now,0.1.4,MIT -de-indent,1.0.2,MIT -debug,2.2.0,MIT -debug,2.3.3,MIT -debug,2.6.7,MIT -debug,2.6.8,MIT debugger-ruby_core_source,1.3.8,MIT -decamelize,1.2.0,MIT deckar01-task_list,2.0.0,MIT declarative,0.0.10,MIT declarative-option,0.1.0,MIT -decompress-response,3.3.0,MIT -deep-equal,1.0.1,MIT -deep-extend,0.4.2,MIT -deep-is,0.1.3,MIT -default-require-extensions,1.0.0,MIT default_value_for,3.0.2,MIT -defined,1.0.0,MIT -del,2.2.2,MIT -del,3.0.0,MIT -delayed-stream,1.0.0,MIT -delegate,3.1.2,MIT -delegates,1.0.0,MIT -depd,1.1.0,MIT -depd,1.1.1,MIT -des.js,1.0.0,MIT descendants_tracker,0.0.4,MIT -destroy,1.0.4,MIT -detect-indent,4.0.0,MIT -detect-node,2.0.3,ISC devise,4.2.0,MIT devise-two-factor,3.0.0,MIT -di,0.0.1,MIT diff-lcs,1.3,"MIT,Artistic-2.0,GPL-2.0+" -diffie-hellman,5.0.2,MIT diffy,3.1.0,MIT -dns-equal,1.0.0,MIT -dns-packet,1.2.2,MIT -dns-txt,2.0.2,MIT -doctrine,1.5.0,BSD -doctrine,2.0.0,Apache 2.0 -document-register-element,1.3.0,MIT -dom-serialize,2.2.1,MIT -dom-serializer,0.1.0,MIT -domain-browser,1.1.7,MIT domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0" -domelementtype,1.1.3,unknown -domelementtype,1.3.0,unknown -domhandler,2.3.0,unknown -domutils,1.5.1,unknown doorkeeper,4.2.6,MIT doorkeeper-openid_connect,1.2.0,MIT -dropzone,4.2.0,MIT dropzonejs-rails,0.7.2,MIT -duplexer,0.1.1,MIT -duplexer3,0.1.4,New BSD -duplexify,3.5.1,MIT -ecc-jsbn,0.1.1,MIT -editorconfig,0.13.2,MIT -ee-first,1.1.1,MIT -ejs,2.5.6,Apache 2.0 -electron-to-chromium,1.3.3,ISC -elliptic,6.3.3,MIT email_reply_trimmer,0.1.6,MIT -emoji-unicode-version,0.2.1,MIT -emojis-list,2.1.0,MIT -encodeurl,1.0.1,MIT encryptor,3.0.0,MIT -end-of-stream,1.4.0,MIT -engine.io,1.8.3,MIT -engine.io-client,1.8.3,MIT -engine.io-parser,1.3.2,MIT -enhanced-resolve,0.9.1,MIT -enhanced-resolve,3.4.1,MIT -ent,2.2.0,MIT -entities,1.1.1,BSD-like equalizer,0.0.11,MIT -errno,0.1.4,MIT -error-ex,1.3.0,MIT erubis,2.7.0,MIT -es5-ext,0.10.24,MIT -es6-iterator,2.0.1,MIT -es6-map,0.1.5,MIT -es6-promise,3.0.2,MIT -es6-set,0.1.5,MIT -es6-symbol,3.1.1,MIT -es6-weak-map,2.0.1,MIT -escape-html,1.0.3,MIT -escape-string-regexp,1.0.5,MIT escape_utils,1.1.1,MIT -escodegen,1.8.1,Simplified BSD -escope,3.6.0,Simplified BSD -eslint,3.19.0,MIT -eslint-config-airbnb-base,10.0.1,MIT -eslint-import-resolver-node,0.2.3,MIT -eslint-import-resolver-webpack,0.8.3,MIT -eslint-module-utils,2.0.0,MIT -eslint-plugin-filenames,1.1.0,MIT -eslint-plugin-html,2.0.1,ISC -eslint-plugin-import,2.2.0,MIT -eslint-plugin-jasmine,2.2.0,MIT -eslint-plugin-promise,3.5.0,ISC -espree,3.5.0,Simplified BSD -esprima,2.7.3,Simplified BSD -esprima,4.0.0,Simplified BSD -esquery,1.0.0,BSD -esrecurse,4.1.0,Simplified BSD -estraverse,1.9.3,BSD -estraverse,4.1.1,Simplified BSD -estraverse,4.2.0,Simplified BSD -esutils,2.0.2,BSD et-orbi,1.0.3,MIT -etag,1.8.0,MIT -eve-raphael,0.5.0,Apache 2.0 -event-emitter,0.3.5,MIT -event-stream,3.3.4,MIT -eventemitter3,1.2.0,MIT -events,1.1.1,MIT -eventsource,0.1.6,MIT -evp_bytestokey,1.0.0,MIT excon,0.57.1,MIT -execa,0.7.0,MIT execjs,2.6.0,MIT -exit-hook,1.1.1,MIT -expand-braces,0.1.2,MIT -expand-brackets,0.1.5,MIT -expand-range,0.1.1,MIT -expand-range,1.8.2,MIT -exports-loader,0.6.4,MIT -express,4.15.4,MIT expression_parser,0.9.0,MIT -extend,3.0.1,MIT -extglob,0.3.2,MIT -extsprintf,1.0.2,MIT faraday,0.12.2,MIT faraday_middleware,0.11.0.1,MIT faraday_middleware-multi_json,0.0.6,MIT -fast-deep-equal,1.0.0,MIT -fast-levenshtein,2.0.6,MIT fast_gettext,1.4.0,"MIT,ruby" -fastparse,1.1.1,MIT -faye-websocket,0.10.0,MIT -faye-websocket,0.11.1,MIT -faye-websocket,0.7.3,MIT ffi,1.9.18,New BSD -figures,1.7.0,MIT -file-entry-cache,2.0.0,MIT -file-loader,0.11.1,MIT -filename-regex,2.0.0,MIT -fileset,2.0.3,MIT -filesize,3.3.0,New BSD -filesize,3.5.10,New BSD -fill-range,2.2.3,MIT -finalhandler,1.0.4,MIT -find-cache-dir,1.0.0,MIT -find-root,0.1.2,MIT -find-up,1.1.2,MIT -find-up,2.1.0,MIT -flat-cache,1.2.2,MIT -flatten,1.0.2,MIT flipper,0.10.2,MIT flipper-active_record,0.10.2,MIT flowdock,0.7.1,MIT @@ -467,392 +85,93 @@ fog-local,0.3.1,MIT fog-openstack,0.1.21,MIT fog-rackspace,0.1.1,MIT fog-xml,0.1.3,MIT -follow-redirects,1.2.3,MIT font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License" -for-in,0.1.6,MIT -for-own,0.1.4,MIT -forever-agent,0.6.1,Apache 2.0 -form-data,2.1.4,MIT formatador,0.2.5,MIT -forwarded,0.1.0,MIT -fresh,0.5.0,MIT -from,0.1.7,MIT -fs-access,1.0.1,MIT -fs-extra,0.26.7,MIT -fs.realpath,1.0.0,ISC -fsevents,1.1.2,MIT -fstream,1.0.11,ISC -fstream-ignore,1.0.5,ISC -function-bind,1.1.0,MIT -fuzzaldrin-plus,0.5.0,MIT -gauge,2.7.4,ISC gemnasium-gitlab-service,0.2.6,MIT gemojione,3.3.0,MIT -generate-function,2.0.0,MIT -generate-object-property,1.2.0,MIT -get-caller-file,1.0.2,ISC -get-stdin,4.0.1,MIT -get-stream,3.0.0,MIT get_process_mem,0.2.0,MIT -getpass,0.1.7,MIT gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails_js,1.2.0,MIT -gitaly-proto,0.51.0,MIT +gitaly-proto,0.59.0,MIT github-linguist,4.7.6,MIT github-markup,1.6.1,MIT gitlab-flowdock-git-hook,1.0.1,MIT gitlab-grit,2.8.2,MIT gitlab-markup,1.6.3,MIT -gitlab-svgs,1.0.4,unknown gitlab_omniauth-ldap,2.0.4,MIT -glob,5.0.15,ISC -glob,6.0.4,ISC -glob,7.1.1,ISC -glob,7.1.2,ISC -glob-base,0.3.0,MIT -glob-parent,2.0.0,ISC -globalid,0.3.7,MIT -globals,9.18.0,MIT -globby,5.0.0,MIT -globby,6.1.0,MIT +globalid,0.4.1,MIT gollum-grit_adapter,1.0.1,MIT gollum-lib,4.2.7,MIT gollum-rugged_adapter,0.4.4,MIT gon,6.1.0,MIT -good-listener,1.2.2,MIT google-api-client,0.13.6,Apache 2.0 google-protobuf,3.4.1.1,New BSD -googleapis-common-protos-types,1.0.0,Apache 2.0 googleauth,0.5.3,Apache 2.0 -got,3.3.1,MIT -got,7.1.0,MIT gpgme,2.0.13,LGPL-2.1+ -graceful-fs,4.1.11,ISC -graceful-readlink,1.0.1,MIT grape,1.0.0,MIT grape-entity,0.6.0,MIT grape-route-helpers,2.1.0,MIT grape_logging,1.7.0,MIT -grpc,1.6.6,Apache 2.0 -gzip-size,3.0.0,MIT +grpc,1.4.5,New BSD hamlit,2.6.1,MIT -handle-thing,1.2.5,MIT -handlebars,4.0.6,MIT -har-schema,1.0.5,ISC -har-validator,4.2.1,ISC -has,1.0.1,MIT -has-ansi,2.0.0,MIT -has-binary,0.1.7,MIT -has-cors,1.1.0,MIT -has-flag,1.0.0,MIT -has-flag,2.0.0,MIT -has-symbol-support-x,1.3.0,MIT -has-to-string-tag-x,1.3.0,MIT -has-unicode,2.0.1,ISC -hash-sum,1.0.2,MIT -hash.js,1.0.3,MIT hashie,3.5.6,MIT hashie-forbidden_attributes,0.1.1,MIT -hawk,3.1.3,New BSD -he,1.1.1,MIT health_check,2.6.0,MIT hipchat,1.5.2,MIT -hoek,2.16.3,New BSD -home-or-tmp,2.0.0,MIT -hosted-git-info,2.2.0,ISC -hpack.js,2.1.6,MIT -html-comment-regex,1.1.1,MIT -html-entities,1.2.0,MIT html-pipeline,1.11.0,MIT html2text,0.2.0,MIT htmlentities,4.3.4,MIT -htmlparser2,3.9.2,MIT http,0.9.8,MIT http-cookie,1.0.3,MIT -http-deceiver,1.2.7,MIT -http-errors,1.6.1,MIT -http-errors,1.6.2,MIT http-form_data,1.0.1,MIT -http-proxy,1.16.2,MIT -http-proxy-middleware,0.17.4,MIT -http-signature,1.1.1,MIT http_parser.rb,0.6.0,MIT httparty,0.13.7,MIT httpclient,2.8.2,ruby -https-browserify,0.0.1,MIT -i18n,0.8.6,MIT +i18n,0.9.1,MIT ice_nine,0.11.2,MIT -iconv-lite,0.4.15,MIT -icss-replace-symbols,1.0.2,ISC -ieee754,1.1.8,New BSD -ignore,3.3.3,MIT -ignore-by-default,1.0.1,ISC -immediate,3.0.6,MIT -imports-loader,0.7.1,MIT -imurmurhash,0.1.4,MIT -indent-string,2.1.0,MIT -indexes-of,1.0.1,MIT -indexof,0.0.1,unknown -infinity-agent,2.0.3,MIT -inflight,1.0.6,ISC influxdb,0.2.3,MIT -inherits,2.0.1,ISC -inherits,2.0.3,ISC -ini,1.3.4,ISC -inquirer,0.12.0,MIT -internal-ip,1.2.0,MIT -interpret,1.0.1,MIT -invariant,2.2.2,New BSD -invert-kv,1.0.0,MIT -ip,1.1.5,MIT -ipaddr.js,1.4.0,MIT ipaddress,0.8.3,MIT -is-absolute,0.2.6,MIT -is-absolute-url,2.1.0,MIT -is-arrayish,0.2.1,MIT -is-binary-path,1.0.1,MIT -is-buffer,1.1.5,MIT -is-builtin-module,1.0.0,MIT -is-dotfile,1.0.2,MIT -is-equal-shallow,0.1.3,MIT -is-extendable,0.1.1,MIT -is-extglob,1.0.0,MIT -is-extglob,2.1.1,MIT -is-finite,1.0.2,MIT -is-fullwidth-code-point,1.0.0,MIT -is-fullwidth-code-point,2.0.0,MIT -is-glob,2.0.1,MIT -is-glob,3.1.0,MIT -is-my-json-valid,2.16.0,MIT -is-npm,1.0.0,MIT -is-number,0.1.1,MIT -is-number,2.1.0,MIT -is-object,1.0.1,MIT -is-path-cwd,1.0.0,MIT -is-path-in-cwd,1.0.0,MIT -is-path-inside,1.0.0,MIT -is-plain-obj,1.1.0,MIT -is-posix-bracket,0.1.1,MIT -is-primitive,2.0.0,MIT -is-property,1.0.2,MIT -is-redirect,1.0.0,MIT -is-relative,0.2.1,MIT -is-resolvable,1.0.0,MIT -is-retry-allowed,1.1.0,MIT -is-stream,1.1.0,MIT -is-svg,2.1.0,MIT -is-typedarray,1.0.0,MIT -is-unc-path,0.1.2,MIT -is-utf8,0.2.1,MIT -is-windows,0.2.0,MIT -isarray,0.0.1,MIT -isarray,1.0.0,MIT -isbinaryfile,3.0.2,MIT -isexe,1.1.2,ISC -isobject,2.1.0,MIT -isstream,0.1.2,MIT -istanbul,0.4.5,New BSD -istanbul-api,1.1.1,New BSD -istanbul-lib-coverage,1.0.1,New BSD -istanbul-lib-hook,1.0.0,New BSD -istanbul-lib-instrument,1.4.2,New BSD -istanbul-lib-report,1.0.0-alpha.3,New BSD -istanbul-lib-source-maps,1.1.0,New BSD -istanbul-reports,1.0.1,New BSD -isurl,1.0.0,MIT -jasmine-core,2.6.3,MIT -jasmine-jquery,2.1.1,MIT -jed,1.1.1,MIT jira-ruby,1.4.1,MIT -jodid25519,1.0.2,MIT -jquery,2.2.1,MIT jquery-atwho-rails,1.3.2,MIT -jquery-rails,4.1.1,MIT -jquery-ujs,1.2.1,MIT -js-base64,2.1.9,BSD -js-beautify,1.6.12,MIT -js-cookie,2.1.3,MIT -js-tokens,3.0.1,MIT -js-yaml,3.7.0,MIT -js-yaml,3.9.1,MIT -jsbn,0.1.1,MIT -jsesc,0.5.0,MIT -jsesc,1.3.0,MIT +jquery-rails,4.3.1,MIT json,1.8.6,ruby json-jwt,1.7.2,MIT -json-loader,0.5.7,MIT -json-schema,0.2.3,"AFLv2.1,BSD" -json-schema-traverse,0.3.1,MIT -json-stable-stringify,1.0.1,MIT -json-stringify-safe,5.0.1,ISC -json3,3.3.2,MIT -json5,0.5.1,MIT -jsonfile,2.4.0,MIT -jsonify,0.0.0,Public Domain -jsonpointer,4.0.1,MIT -jsprim,1.4.0,MIT -jszip,3.1.3,(MIT OR GPL-3.0) -jszip-utils,0.0.2,MIT or GPLv3 jwt,1.5.6,MIT kaminari,1.0.1,MIT kaminari-actionview,1.0.1,MIT kaminari-activerecord,1.0.1,MIT kaminari-core,1.0.1,MIT -karma,1.7.0,MIT -karma-chrome-launcher,2.1.1,MIT -karma-coverage-istanbul-reporter,0.2.0,MIT -karma-jasmine,1.1.0,MIT -karma-mocha-reporter,2.2.2,MIT -karma-sourcemap-loader,0.3.7,MIT -karma-webpack,2.0.4,MIT kgio,2.10.0,LGPL-2.1+ -kind-of,3.1.0,MIT -klaw,1.3.1,MIT kubeclient,2.2.0,MIT -latest-version,1.0.1,MIT -lazy-cache,1.0.4,MIT -lcid,1.0.0,MIT -levn,0.3.0,MIT licensee,8.7.0,MIT -lie,3.1.1,MIT little-plugger,1.1.4,MIT -load-json-file,1.1.0,MIT -load-json-file,2.0.0,MIT -loader-runner,2.3.0,MIT -loader-utils,0.2.16,MIT -loader-utils,1.1.0,MIT locale,2.1.2,"ruby,LGPLv3+" -locate-path,2.0.0,MIT -lodash,3.10.1,MIT -lodash,4.17.4,MIT -lodash._baseassign,3.2.0,MIT -lodash._basecopy,3.0.1,MIT -lodash._baseget,3.7.2,MIT -lodash._bindcallback,3.0.1,MIT -lodash._createassigner,3.1.1,MIT -lodash._getnative,3.9.1,MIT -lodash._isiterateecall,3.0.9,MIT -lodash._topath,3.8.1,MIT -lodash.assign,3.2.0,MIT -lodash.camelcase,4.1.1,MIT -lodash.camelcase,4.3.0,MIT -lodash.capitalize,4.2.1,MIT -lodash.cond,4.5.2,MIT -lodash.deburr,4.1.0,MIT -lodash.defaults,3.1.2,MIT -lodash.get,3.7.0,MIT -lodash.get,4.4.2,MIT -lodash.isarguments,3.1.0,MIT -lodash.isarray,3.0.4,MIT -lodash.kebabcase,4.0.1,MIT -lodash.keys,3.1.2,MIT -lodash.memoize,4.1.2,MIT -lodash.restparam,3.6.1,MIT -lodash.snakecase,4.0.1,MIT -lodash.uniq,4.5.0,MIT -lodash.words,4.2.0,MIT -log4js,0.6.38,Apache 2.0 logging,2.2.2,MIT -loglevel,1.4.1,MIT lograge,0.5.1,MIT -longest,1.0.1,MIT loofah,2.0.3,MIT -loose-envify,1.3.1,MIT -loud-rejection,1.6.0,MIT -lowercase-keys,1.0.0,MIT -lru-cache,2.2.4,MIT -lru-cache,3.2.0,ISC -lru-cache,4.0.2,ISC -macaddress,0.2.8,MIT -mail,2.6.6,MIT +mail,2.7.0,MIT mail_room,0.9.1,MIT -make-dir,1.0.0,MIT -map-obj,1.0.1,MIT -map-stream,0.1.0,unknown -marked,0.3.6,MIT -math-expression-evaluator,1.2.16,MIT -media-typer,0.3.0,MIT -mem,1.1.0,MIT memoist,0.16.0,MIT -memory-fs,0.2.0,MIT -memory-fs,0.4.1,MIT -meow,3.7.0,MIT -merge-descriptors,1.0.1,MIT method_source,0.8.2,MIT -methods,1.1.2,MIT -micromatch,2.3.11,MIT -miller-rabin,4.0.0,MIT -mime,1.3.4,MIT -mime-db,1.27.0,MIT -mime-db,1.29.0,MIT -mime-types,2.1.15,MIT mime-types,3.1,MIT mime-types-data,3.2016.0521,MIT mimemagic,0.3.0,MIT -mimic-fn,1.1.0,MIT -mimic-response,1.0.0,MIT +mini_mime,0.1.4,MIT mini_portile2,2.3.0,MIT -minimalistic-assert,1.0.0,ISC -minimatch,3.0.3,ISC -minimatch,3.0.4,ISC -minimist,0.0.8,MIT -minimist,1.2.0,MIT -mkdirp,0.5.1,MIT -mmap2,2.2.7,ruby -moment,2.17.1,MIT -monaco-editor,0.10.0,MIT -mousetrap,1.4.6,Apache 2.0 mousetrap-rails,1.4.6,"MIT,Apache" -ms,0.7.1,MIT -ms,0.7.2,MIT -ms,2.0.0,MIT multi_json,1.12.2,MIT multi_xml,0.6.0,MIT -multicast-dns,6.1.1,MIT -multicast-dns-service-types,1.1.0,MIT multipart-post,2.0.0,MIT mustermann,1.0.0,MIT mustermann-grape,1.0.0,MIT -mute-stream,0.0.5,ISC mysql2,0.4.5,MIT -name-all-modules-plugin,1.0.1,MIT -nan,2.6.2,MIT -natural-compare,1.4.0,MIT -negotiator,0.6.1,MIT -nested-error-stacks,1.0.2,MIT net-ldap,0.16.0,MIT net-ssh,4.1.0,MIT netrc,0.11.0,MIT -node-dir,0.1.17,MIT -node-forge,0.6.33,BSD -node-libs-browser,1.1.1,MIT -node-libs-browser,2.0.0,MIT -node-pre-gyp,0.6.36,New BSD -node-pre-gyp,0.6.37,New BSD -nodemon,1.11.0,MIT nokogiri,1.8.1,MIT -nopt,1.0.10,MIT -nopt,3.0.6,ISC -nopt,4.0.1,ISC -normalize-package-data,2.4.0,Simplified BSD -normalize-path,2.1.1,MIT -normalize-range,0.1.2,MIT -normalize-url,1.9.1,MIT -npm-run-path,2.0.2,MIT -npmlog,4.1.0,ISC -null-check,1.0.0,MIT -num2fraction,1.2.2,MIT -number-is-nan,1.0.1,MIT numerizer,0.1.1,MIT oauth,0.5.1,MIT -oauth-sign,0.8.2,Apache 2.0 oauth2,1.4.0,MIT -object-assign,3.0.0,MIT -object-assign,4.1.0,MIT -object-assign,4.1.1,MIT -object-component,0.0.3,unknown -object.omit,2.0.1,MIT -obuf,1.1.1,MIT octokit,4.6.2,MIT oj,2.17.5,MIT omniauth,1.4.2,MIT @@ -873,54 +192,10 @@ omniauth-saml,1.7.0,MIT omniauth-shibboleth,1.2.1,MIT omniauth-twitter,1.2.1,MIT omniauth_crowd,2.2.3,MIT -on-finished,2.3.0,MIT -on-headers,1.0.1,MIT -once,1.4.0,ISC -onetime,1.1.0,MIT -opener,1.4.3,(WTFPL OR MIT) -opn,4.0.2,MIT -optimist,0.6.1,MIT/X11 -optionator,0.8.2,MIT -options,0.0.6,MIT org-ruby,0.9.12,MIT -original,1.0.0,MIT orm_adapter,0.5.0,MIT os,0.9.6,MIT -os-browserify,0.2.1,MIT -os-homedir,1.0.2,MIT -os-locale,1.4.0,MIT -os-locale,2.1.0,MIT -os-tmpdir,1.0.2,MIT -osenv,0.1.4,ISC -p-cancelable,0.3.0,MIT -p-finally,1.0.0,MIT -p-limit,1.1.0,MIT -p-locate,2.0.0,MIT -p-map,1.1.1,MIT -p-timeout,1.2.0,MIT -package-json,1.2.0,MIT -pako,0.2.9,MIT -pako,1.0.5,(MIT AND Zlib) paranoia,2.3.1,MIT -parse-asn1,5.0.0,ISC -parse-glob,3.0.4,MIT -parse-json,2.2.0,MIT -parsejson,0.0.3,MIT -parseqs,0.0.5,MIT -parseuri,0.0.5,MIT -parseurl,1.3.1,MIT -path-browserify,0.0.0,MIT -path-exists,2.1.0,MIT -path-exists,3.0.0,MIT -path-is-absolute,1.0.1,MIT -path-is-inside,1.0.2,(WTFPL OR MIT) -path-key,2.0.1,MIT -path-parse,1.0.5,MIT -path-to-regexp,0.1.7,MIT -path-type,1.1.0,MIT -path-type,2.0.0,MIT -pause-stream,0.0.11,"MIT,Apache2" -pbkdf2,3.0.9,MIT peek,1.0.1,MIT peek-gc,0.0.2,MIT peek-host,1.0.0,MIT @@ -930,86 +205,14 @@ peek-pg,1.3.0,MIT peek-rblineprof,0.2.0,MIT peek-redis,1.2.0,MIT peek-sidekiq,1.0.3,MIT -performance-now,0.2.0,MIT pg,0.18.4,"BSD,ruby,GPL" -pify,2.3.0,MIT -pify,3.0.0,MIT -pikaday,1.6.1,MIT -pinkie,2.0.4,MIT -pinkie-promise,2.0.1,MIT -pkg-dir,1.0.0,MIT -pkg-dir,2.0.0,MIT -pkg-up,1.0.0,MIT -pluralize,1.2.1,MIT po_to_json,1.0.1,MIT -portfinder,1.0.13,MIT posix-spawn,0.3.13,MIT -postcss,5.2.16,MIT -postcss-calc,5.3.1,MIT -postcss-colormin,2.2.2,MIT -postcss-convert-values,2.6.1,MIT -postcss-discard-comments,2.0.4,MIT -postcss-discard-duplicates,2.1.0,MIT -postcss-discard-empty,2.1.0,MIT -postcss-discard-overridden,0.1.1,MIT -postcss-discard-unused,2.2.3,MIT -postcss-filter-plugins,2.0.2,MIT -postcss-load-config,1.2.0,MIT -postcss-load-options,1.2.0,MIT -postcss-load-plugins,2.3.0,MIT -postcss-merge-idents,2.1.7,MIT -postcss-merge-longhand,2.0.2,MIT -postcss-merge-rules,2.1.2,MIT -postcss-message-helpers,2.0.0,MIT -postcss-minify-font-values,1.0.5,MIT -postcss-minify-gradients,1.0.5,MIT -postcss-minify-params,1.2.2,MIT -postcss-minify-selectors,2.1.1,MIT -postcss-modules-extract-imports,1.0.1,ISC -postcss-modules-local-by-default,1.1.1,MIT -postcss-modules-scope,1.0.2,ISC -postcss-modules-values,1.2.2,ISC -postcss-normalize-charset,1.1.1,MIT -postcss-normalize-url,3.0.8,MIT -postcss-ordered-values,2.2.3,MIT -postcss-reduce-idents,2.4.0,MIT -postcss-reduce-initial,1.0.1,MIT -postcss-reduce-transforms,1.0.4,MIT -postcss-selector-parser,2.2.3,MIT -postcss-svgo,2.1.6,MIT -postcss-unique-selectors,2.0.2,MIT -postcss-value-parser,3.3.0,MIT -postcss-zindex,2.2.0,MIT -prelude-ls,1.1.2,MIT premailer,1.10.4,New BSD premailer-rails,1.9.7,MIT -prepend-http,1.0.4,MIT -preserve,0.2.0,MIT -prismjs,1.6.0,MIT -private,0.1.7,MIT -process,0.11.9,MIT -process-nextick-args,1.0.7,MIT -progress,1.1.8,MIT -prometheus-client-mmap,0.7.0.beta18,Apache 2.0 -proto-list,1.2.4,ISC -proxy-addr,1.1.5,MIT -prr,0.0.0,MIT -ps-tree,1.1.0,MIT -pseudomap,1.0.2,ISC -public-encrypt,4.0.0,MIT +prometheus-client-mmap,0.7.0.beta43,Apache 2.0 public_suffix,3.0.0,MIT -punycode,1.3.2,MIT -punycode,1.4.1,MIT pyu-ruby-sasl,0.0.3.3,MIT -q,1.5.0,MIT -qjobs,1.1.5,MIT -qs,6.4.0,New BSD -qs,6.5.0,New BSD -query-string,4.3.2,MIT -querystring,0.2.0,MIT -querystring-es3,0.2.1,MIT -querystringify,0.0.4,MIT -querystringify,1.0.0,MIT rack,1.6.8,MIT rack-accept,0.4.5,MIT rack-attack,4.4.1,MIT @@ -1018,88 +221,35 @@ rack-oauth2,1.2.3,MIT rack-protection,1.5.3,MIT rack-proxy,0.6.0,MIT rack-test,0.6.3,MIT -rails,4.2.8,MIT +rails,4.2.10,MIT rails-deprecated_sanitizer,1.0.3,MIT rails-dom-testing,1.0.8,MIT rails-html-sanitizer,1.0.3,MIT rails-i18n,4.0.9,MIT -railties,4.2.8,MIT +railties,4.2.10,MIT rainbow,2.2.2,MIT raindrops,0.18.0,LGPL-2.1+ -rake,12.1.0,MIT -randomatic,1.1.6,MIT -randombytes,2.0.3,MIT -range-parser,1.2.0,MIT -raphael,2.2.7,MIT -raven-js,3.14.0,Simplified BSD -raw-body,2.2.0,MIT -raw-loader,0.5.1,MIT +rake,12.3.0,MIT rbnacl,4.0.2,MIT rbnacl-libsodium,1.0.11,MIT -rc,1.2.1,(BSD-2-Clause OR MIT OR Apache-2.0) rdoc,4.2.2,ruby re2,1.1.1,New BSD -react-dev-utils,0.5.2,New BSD -read-all-stream,3.1.0,MIT -read-pkg,1.1.0,MIT -read-pkg,2.0.0,MIT -read-pkg-up,1.0.1,MIT -read-pkg-up,2.0.0,MIT -readable-stream,1.0.34,MIT -readable-stream,2.0.6,MIT -readable-stream,2.2.9,MIT -readable-stream,2.3.3,MIT -readdirp,2.1.0,MIT -readline2,1.0.1,MIT recaptcha,3.0.0,MIT -rechoir,0.6.2,MIT recursive-open-struct,1.0.0,MIT -recursive-readdir,2.1.1,MIT redcarpet,3.4.0,MIT -redent,1.0.0,MIT redis,3.3.3,MIT -redis-actionpack,5.0.1,MIT -redis-activesupport,5.0.1,MIT +redis-actionpack,5.0.2,MIT +redis-activesupport,5.0.4,MIT redis-namespace,1.5.2,MIT -redis-rack,1.6.0,MIT -redis-rails,5.0.1,MIT -redis-store,1.2.0,MIT -reduce-css-calc,1.3.0,MIT -reduce-function-call,1.0.2,MIT -regenerate,1.3.2,MIT -regenerator-runtime,0.10.1,MIT -regenerator-transform,0.9.8,BSD -regex-cache,0.4.3,MIT -regexpu-core,1.0.0,MIT -regexpu-core,2.0.0,MIT -registry-url,3.1.0,MIT -regjsgen,0.2.0,MIT -regjsparser,0.1.5,BSD -remove-trailing-separator,1.1.0,ISC -repeat-element,1.1.2,MIT -repeat-string,0.2.2,MIT -repeat-string,1.6.1,MIT -repeating,1.1.3,MIT -repeating,2.0.1,MIT +redis-rack,2.0.3,MIT +redis-rails,5.0.2,MIT +redis-store,1.4.1,MIT representable,3.0.4,MIT -request,2.81.0,Apache 2.0 request_store,1.3.1,MIT -require-directory,2.1.1,MIT -require-from-string,1.2.1,MIT -require-main-filename,1.0.1,ISC -require-uncached,1.0.3,MIT -requires-port,1.0.0,MIT -resolve,1.1.7,MIT -resolve,1.2.0,MIT -resolve-from,1.0.1,MIT responders,2.3.0,MIT rest-client,2.0.0,MIT -restore-cursor,1.0.1,MIT retriable,3.1.1,MIT -right-align,0.1.3,MIT -rimraf,2.6.1,ISC rinku,2.0.0,ISC -ripemd160,1.0.1,New BSD rotp,2.1.2,MIT rouge,2.2.1,MIT rqrcode,0.7.0,MIT @@ -1112,244 +262,51 @@ rubyntlm,0.6.2,MIT rubypants,0.2.0,BSD rufus-scheduler,3.4.0,MIT rugged,0.26.0,MIT -run-async,0.1.0,MIT -rx-lite,3.1.2,Apache 2.0 -safe-buffer,5.0.1,MIT -safe-buffer,5.1.1,MIT safe_yaml,1.0.4,MIT sanitize,2.1.0,MIT sass,3.4.22,MIT sass-rails,5.0.6,MIT sawyer,0.8.1,MIT -sax,1.2.2,ISC securecompare,1.0.0,MIT seed-fu,2.3.6,MIT -select,1.1.2,MIT -select-hose,2.0.0,MIT -select2,3.5.2-browserify,unknown select2-rails,3.5.9.3,MIT -selfsigned,1.10.1,MIT -semver,4.3.6,ISC -semver,5.3.0,ISC -semver-diff,2.1.0,MIT -send,0.15.4,MIT sentry-raven,2.5.3,Apache 2.0 -serve-index,1.9.0,MIT -serve-static,1.12.4,MIT -set-blocking,2.0.0,ISC -set-immediate-shim,1.0.1,MIT -setimmediate,1.0.5,MIT -setprototypeof,1.0.3,ISC settingslogic,2.0.9,MIT sexp_processor,4.9.0,MIT -sha.js,2.4.8,MIT -shebang-command,1.2.0,MIT -shebang-regex,1.0.0,MIT -shelljs,0.7.8,New BSD sidekiq,5.0.4,LGPL sidekiq-cron,0.6.0,MIT sidekiq-limit_fetch,3.4.0,MIT -sigmund,1.0.1,ISC -signal-exit,3.0.2,ISC signet,0.7.3,Apache 2.0 slack-notifier,1.5.1,MIT -slash,1.0.0,MIT -slice-ansi,0.0.4,MIT -slide,1.1.6,ISC -sntp,1.0.9,BSD -socket.io,1.7.3,MIT -socket.io-adapter,0.5.0,MIT -socket.io-client,1.7.3,MIT -socket.io-parser,2.3.1,MIT -sockjs,0.3.18,MIT -sockjs-client,1.0.1,MIT -sockjs-client,1.1.4,MIT -sort-keys,1.1.2,MIT -source-list-map,0.1.8,MIT -source-list-map,2.0.0,MIT -source-map,0.1.43,BSD -source-map,0.2.0,BSD -source-map,0.4.4,New BSD -source-map,0.5.6,New BSD -source-map-support,0.4.11,MIT -spdx-correct,1.0.2,Apache 2.0 -spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0) -spdx-license-ids,1.2.2,Unlicense -spdy,3.4.7,MIT -spdy-transport,2.0.20,MIT -split,0.3.3,MIT -sprintf-js,1.0.3,New BSD sprockets,3.7.1,MIT -sprockets-rails,3.2.0,MIT -sql.js,0.4.0,MIT -sshpk,1.13.0,MIT +sprockets-rails,3.2.1,MIT state_machines,0.4.0,MIT state_machines-activemodel,0.4.0,MIT state_machines-activerecord,0.4.0,MIT -statuses,1.3.1,MIT -stream-browserify,2.0.1,MIT -stream-combiner,0.0.4,MIT -stream-http,2.6.3,MIT -stream-shift,1.0.0,MIT -strict-uri-encode,1.1.0,MIT -string-length,1.0.1,MIT -string-width,1.0.2,MIT -string-width,2.0.0,MIT -string_decoder,0.10.31,MIT -string_decoder,1.0.1,MIT -string_decoder,1.0.3,MIT stringex,2.7.1,MIT -stringstream,0.0.5,MIT -strip-ansi,3.0.1,MIT -strip-bom,2.0.0,MIT -strip-bom,3.0.0,MIT -strip-eof,1.0.0,MIT -strip-indent,1.0.1,MIT -strip-json-comments,2.0.1,MIT -supports-color,2.0.0,MIT -supports-color,3.2.3,MIT -supports-color,4.2.1,MIT -svg4everybody,2.1.9,CC0-1.0 -svgo,0.7.2,MIT sys-filesystem,1.1.6,Artistic 2.0 -table,3.8.3,New BSD -tapable,0.1.10,MIT -tapable,0.2.8,MIT -tar,2.2.1,ISC -tar-pack,3.4.0,Simplified BSD temple,0.7.7,MIT -test-exclude,4.0.0,ISC text,1.3.1,MIT -text-table,0.2.0,MIT thor,0.19.4,MIT thread_safe,0.3.6,Apache 2.0 -three,0.84.0,MIT -three-orbit-controls,82.1.0,MIT -three-stl-loader,1.0.4,MIT -through,2.3.8,MIT -thunky,0.1.0,unknown tilt,2.0.6,MIT -timeago.js,2.0.5,MIT -timed-out,2.0.0,MIT -timed-out,4.0.1,MIT -timers-browserify,1.4.2,MIT -timers-browserify,2.0.4,MIT timfel-krb5-auth,0.8.3,LGPL -tiny-emitter,1.1.0,MIT -tmp,0.0.31,MIT -to-array,0.1.4,MIT -to-arraybuffer,1.0.1,MIT -to-fast-properties,1.0.2,MIT toml-rb,0.3.15,MIT -touch,1.0.0,ISC -tough-cookie,2.3.2,New BSD -traverse,0.6.6,MIT -trim-newlines,1.0.0,MIT -trim-right,1.0.1,MIT truncato,0.7.10,MIT -tryit,1.0.3,MIT -ts-loader,3.1.1,MIT -tty-browserify,0.0.0,MIT -tunnel-agent,0.6.0,Apache 2.0 -tweetnacl,0.14.5,Unlicense -type-check,0.3.2,MIT -type-is,1.6.15,MIT -typedarray,0.0.6,MIT -typescript,2.6.1,Apache 2.0 -tzinfo,1.2.3,MIT +tzinfo,1.2.4,MIT u2f,0.2.1,MIT uber,0.1.0,MIT uglifier,2.7.2,MIT -uglify-js,2.8.29,Simplified BSD -uglify-to-browserify,1.0.2,MIT -uglifyjs-webpack-plugin,0.4.6,MIT -uid-number,0.0.6,ISC -ultron,1.0.2,MIT -ultron,1.1.0,MIT -unc-path-regex,0.1.2,MIT -undefsafe,0.0.3,MIT / http://rem.mit-license.org -underscore,1.8.3,MIT unf,0.1.4,BSD unf_ext,0.0.7.4,MIT unicorn,5.1.0,ruby unicorn-worker-killer,0.4.4,ruby -uniq,1.0.1,MIT -uniqid,4.1.1,MIT -uniqs,2.0.0,MIT -unpipe,1.0.0,MIT -update-notifier,0.5.0,Simplified BSD -url,0.11.0,MIT -url-loader,0.5.8,MIT -url-parse,1.0.5,MIT -url-parse,1.1.7,MIT -url-parse,1.1.9,MIT -url-parse-lax,1.0.0,MIT -url-to-options,1.0.1,MIT url_safe_base64,0.2.2,MIT -user-home,2.0.0,MIT -useragent,2.2.1,MIT -util,0.10.3,MIT -util-deprecate,1.0.2,MIT -utils-merge,1.0.0,MIT -uuid,2.0.3,MIT -uuid,3.0.1,MIT -validate-npm-package-license,3.0.1,Apache 2.0 validates_hostname,1.0.6,MIT -vary,1.1.1,MIT -vendors,1.0.1,MIT -verror,1.3.6,MIT version_sorter,2.1.0,MIT virtus,1.0.5,MIT -visibilityjs,1.2.4,MIT -vm-browserify,0.0.4,MIT vmstat,2.3.0,MIT -void-elements,2.0.1,MIT -vue,2.5.2,MIT -vue-hot-reload-api,2.0.11,MIT -vue-loader,11.3.4,MIT -vue-resource,1.3.4,MIT -vue-style-loader,2.0.5,MIT -vue-template-compiler,2.5.2,MIT -vue-template-es2015-compiler,1.5.1,MIT -vuex,3.0.0,MIT warden,1.2.6,MIT -watchpack,1.4.0,MIT -wbuf,1.7.2,MIT -webpack,3.5.5,MIT -webpack-bundle-analyzer,2.8.2,MIT -webpack-dev-middleware,1.11.0,MIT -webpack-dev-server,2.7.1,MIT webpack-rails,0.9.10,MIT -webpack-sources,1.0.1,MIT -webpack-stats-plugin,0.1.5,MIT -websocket-driver,0.6.5,MIT -websocket-extensions,0.1.1,MIT -whet.extend,0.9.9,MIT -which,1.2.12,ISC -which-module,1.0.0,ISC -which-module,2.0.0,ISC -wide-align,1.1.2,ISC wikicloth,0.8.1,MIT -window-size,0.1.0,MIT -wordwrap,0.0.2,MIT/X11 -wordwrap,0.0.3,MIT -wordwrap,1.0.0,MIT -wrap-ansi,2.1.0,MIT -wrappy,1.0.2,ISC -write,0.2.1,MIT -write-file-atomic,1.3.4,ISC -ws,1.1.2,MIT -ws,2.3.1,MIT -wtf-8,1.0.0,MIT -xdg-basedir,2.0.0,MIT xml-simple,1.1.5,ruby -xmlhttprequest-ssl,1.5.3,MIT -xtend,4.0.1,MIT -y18n,3.2.1,ISC -yallist,2.1.2,ISC -yargs,3.10.0,MIT -yargs,6.6.0,MIT -yargs,8.0.2,MIT -yargs-parser,4.2.1,ISC -yargs-parser,7.0.0,ISC -yeast,0.1.2,MIT |