diff options
172 files changed, 3843 insertions, 1291 deletions
diff --git a/.babelrc.js b/.babelrc.js index bfcc7d96634..1b05a67354e 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -18,6 +18,7 @@ const plugins = [ '@babel/plugin-syntax-import-meta', '@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-json-strings', + '@babel/plugin-proposal-private-methods', ]; // add code coverage tooling if necessary diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d12f4142ba..45de5ce61c6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -315,6 +315,7 @@ cloud-native-image: variables: GIT_DEPTH: "1" cache: {} + when: always script: - gem install gitlab --no-document - CNG_PROJECT_PATH="gitlab-org/build/CNG" BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN ./scripts/trigger-build cng diff --git a/.gitlab/issue_templates/Add style proposal b/.gitlab/issue_templates/Add style proposal new file mode 100644 index 00000000000..1a3be44bea0 --- /dev/null +++ b/.gitlab/issue_templates/Add style proposal @@ -0,0 +1,16 @@ +## Description of the proposal + +<!-- +Please describe the proposal and add a link to the source (for example, http://www.betterspecs.org/). +--> + +- [ ] Mention the proposal in the next backend weekly call and the #backend channel to encourage contribution +- [ ] Proceed with the proposal once 50% of the maintainers have weighed in, and 80% of the votes are :+1: +- [ ] Once approved, mention it again in the next backend weekly call and the #backend channel + + +/label ~"development guidelines" +/label ~"Style decision" +/label ~Documentation + +/cc @gitlab-org/maintainers/rails-backend diff --git a/Gemfile.lock b/Gemfile.lock index d708d0e9740..075aa616215 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,7 +108,7 @@ GEM capybara-screenshot (1.0.22) capybara (>= 1.0, < 4) launchy - carrierwave (1.3.0) + carrierwave (1.3.1) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index 2fa7a219ea8..3d846310008 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -23,7 +23,7 @@ export default function initTodoToggle() { }); } -document.addEventListener('DOMContentLoaded', () => { +function initStatusTriggers() { const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger'); const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper'); @@ -72,4 +72,8 @@ document.addEventListener('DOMContentLoaded', () => { }, }); } +} + +document.addEventListener('DOMContentLoaded', () => { + requestIdleCallback(initStatusTriggers); }); diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 3f6101e58f4..229ef168926 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -73,7 +73,7 @@ router.beforeEach((to, from, next) => { projectId: to.params.project, }) .then(() => { - const basePath = to.params[0] || ''; + const basePath = to.params.pathMatch || ''; const projectId = `${to.params.namespace}/${to.params.project}`; const branchId = to.params.branchid; const mergeRequestId = to.params.mrid; diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue index e0f55518eef..7076a79dd5d 100644 --- a/app/assets/javascripts/jobs/components/commit_block.vue +++ b/app/assets/javascripts/jobs/components/commit_block.vue @@ -45,7 +45,7 @@ export default { /> <span v-if="mergeRequest"> - {{ __('in') }} + in <gl-link :href="mergeRequest.path" class="js-link-commit link-commit" >!{{ mergeRequest.iid }}</gl-link > diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 9850f7ce782..4ba84589705 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -42,22 +42,35 @@ export function mergeUrlParams(params, url) { return `${urlparts[1]}?${query}${urlparts[3]}`; } -export function removeParamQueryString(url, param) { - const decodedUrl = decodeURIComponent(url); - const urlVariables = decodedUrl.split('&'); - - return urlVariables.filter(variable => variable.indexOf(param) === -1).join('&'); -} - -export function removeParams(params, source = window.location.href) { - const url = document.createElement('a'); - url.href = source; +/** + * Removes specified query params from the url by returning a new url string that no longer + * includes the param/value pair. If no url is provided, `window.location.href` is used as + * the default value. + * + * @param {string[]} params - the query param names to remove + * @param {string} [url=windowLocation().href] - url from which the query param will be removed + * @returns {string} A copy of the original url but without the query param + */ +export function removeParams(params, url = window.location.href) { + const [rootAndQuery, fragment] = url.split('#'); + const [root, query] = rootAndQuery.split('?'); + + if (!query) { + return url; + } - params.forEach(param => { - url.search = removeParamQueryString(url.search, param); - }); + const encodedParams = params.map(param => encodeURIComponent(param)); + const updatedQuery = query + .split('&') + .filter(paramPair => { + const [foundParam] = paramPair.split('='); + return encodedParams.indexOf(foundParam) < 0; + }) + .join('&'); - return url.href; + const writableQuery = updatedQuery.length > 0 ? `?${updatedQuery}` : ''; + const writableFragment = fragment ? `#${fragment}` : ''; + return `${root}${writableQuery}${writableFragment}`; } export function getLocationHash(url = window.location.href) { @@ -66,6 +79,20 @@ export function getLocationHash(url = window.location.href) { return hashIndex === -1 ? null : url.substring(hashIndex + 1); } +/** + * Apply the fragment to the given url by returning a new url string that includes + * the fragment. If the given url already contains a fragment, the original fragment + * will be removed. + * + * @param {string} url - url to which the fragment will be applied + * @param {string} fragment - fragment to append + */ +export const setUrlFragment = (url, fragment) => { + const [rootUrl] = url.split('#'); + const encodedFragment = encodeURIComponent(fragment.replace(/^#/, '')); + return `${rootUrl}#${encodedFragment}`; +}; + export function visitUrl(url, external = false) { if (external) { // Simulate `target="blank" rel="noopener noreferrer"` diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index c866e8d180a..4ba3543f9b2 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -66,15 +66,11 @@ gl.lazyLoader = new LazyLoader({ observerNode: '#content-body', }); -document.addEventListener('DOMContentLoaded', () => { +// Put all initialisations here that can also wait after everything is rendered and ready +function deferredInitialisation() { const $body = $('body'); - const $document = $(document); - const $window = $(window); - const $sidebarGutterToggle = $('.js-sidebar-toggle'); - let bootstrapBreakpoint = bp.getBreakpointSize(); initBreadcrumbs(); - initLayoutNav(); initImporterStatus(); initTodoToggle(); initLogoAnimation(); @@ -84,34 +80,6 @@ document.addEventListener('DOMContentLoaded', () => { if (document.querySelector('.search')) initSearchAutocomplete(); if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' }); - // Set the default path for all cookies to GitLab's root directory - Cookies.defaults.path = gon.relative_url_root || '/'; - - // `hashchange` is not triggered when link target is already in window.location - $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() { - const href = this.getAttribute('href'); - if (href.substr(1) === getLocationHash()) { - setTimeout(handleLocationHash, 1); - } - }); - - if (bootstrapBreakpoint === 'xs') { - const $rightSidebar = $('aside.right-sidebar, .layout-page'); - - $rightSidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - } - - // prevent default action for disabled buttons - $('.btn').click(function clickDisabledButtonCallback(e) { - if ($(this).hasClass('disabled')) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - - return true; - }); - addSelectOnFocusBehaviour('.js-select-on-focus'); $('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() { @@ -164,6 +132,48 @@ document.addEventListener('DOMContentLoaded', () => { viewport: '.layout-page', }); + loadAwardsHandler(); +} + +document.addEventListener('DOMContentLoaded', () => { + const $body = $('body'); + const $document = $(document); + const $window = $(window); + const $sidebarGutterToggle = $('.js-sidebar-toggle'); + let bootstrapBreakpoint = bp.getBreakpointSize(); + + initLayoutNav(); + + // Set the default path for all cookies to GitLab's root directory + Cookies.defaults.path = gon.relative_url_root || '/'; + + // `hashchange` is not triggered when link target is already in window.location + $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() { + const href = this.getAttribute('href'); + if (href.substr(1) === getLocationHash()) { + setTimeout(handleLocationHash, 1); + } + }); + + if (bootstrapBreakpoint === 'xs') { + const $rightSidebar = $('aside.right-sidebar, .layout-page'); + + $rightSidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + } + + // prevent default action for disabled buttons + $('.btn').click(function clickDisabledButtonCallback(e) { + if ($(this).hasClass('disabled')) { + e.preventDefault(); + e.stopImmediatePropagation(); + return false; + } + + return true; + }); + + localTimeAgo($('abbr.timeago, .js-timeago'), true); + // Form submitter $('.trigger-submit').on('change', function triggerSubmitCallback() { $(this) @@ -171,8 +181,6 @@ document.addEventListener('DOMContentLoaded', () => { .submit(); }); - localTimeAgo($('abbr.timeago, .js-timeago'), true); - // Disable form buttons while a form is submitting $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function ajaxCompleteCallback(e) { const $buttons = $('[type="submit"], .js-disable-on-submit', this); @@ -195,6 +203,10 @@ document.addEventListener('DOMContentLoaded', () => { } }); + $('.navbar-toggler').on('click', () => { + $('.header-content').toggleClass('menu-expanded'); + }); + // Commit show suppressed diff $document.on('click', '.diff-content .js-show-suppressed-diff', function showDiffCallback() { const $container = $(this).parent(); @@ -202,10 +214,6 @@ document.addEventListener('DOMContentLoaded', () => { $container.remove(); }); - $('.navbar-toggler').on('click', () => { - $('.header-content').toggleClass('menu-expanded'); - }); - // Show/hide comments on diff $body.on('click', '.js-toggle-diff-comments', function toggleDiffCommentsCallback(e) { const $this = $(this); @@ -250,8 +258,6 @@ document.addEventListener('DOMContentLoaded', () => { $window.on('resize.app', fitSidebarForSize); - loadAwardsHandler(); - $('form.filter-form').on('submit', function filterFormSubmitCallback(event) { const link = document.createElement('a'); link.href = this.action; @@ -274,4 +280,6 @@ document.addEventListener('DOMContentLoaded', () => { // initialize field errors $('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form)); + + requestIdleCallback(deferredInitialisation); }); diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 8bf02327cd2..7c17147dd01 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -370,7 +370,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" > <button :disabled="isSubmitButtonDisabled" - class="btn btn-create comment-btn js-comment-button js-comment-submit-button + class="btn btn-success js-comment-button js-comment-submit-button qa-comment-button" type="submit" @click.prevent="handleSave();" @@ -381,7 +381,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" :disabled="isSubmitButtonDisabled" name="button" type="button" - class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown" + class="btn btn-success note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown" data-display="static" data-toggle="dropdown" aria-label="Open comment type dropdown" diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index f5c410211b6..2d7c04ea614 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -1,6 +1,7 @@ <script> import $ from 'jquery'; import { mapGetters, mapActions } from 'vuex'; +import { getLocationHash } from '../../lib/utils/url_utility'; import Icon from '~/vue_shared/components/icon.vue'; import { DISCUSSION_FILTERS_DEFAULT_VALUE, @@ -44,29 +45,47 @@ export default { eventHub.$on('MergeRequestTabChange', this.toggleFilters); this.toggleFilters(currentTab); } + + window.addEventListener('hashchange', this.handleLocationHash); + this.handleLocationHash(); }, mounted() { this.toggleCommentsForm(); }, + destroyed() { + window.removeEventListener('hashchange', this.handleLocationHash); + }, methods: { - ...mapActions(['filterDiscussion', 'setCommentsDisabled']), + ...mapActions(['filterDiscussion', 'setCommentsDisabled', 'setTargetNoteHash']), selectFilter(value) { const filter = parseInt(value, 10); // close dropdown - $(this.$refs.dropdownToggle).dropdown('toggle'); + this.toggleDropdown(); if (filter === this.currentValue) return; this.currentValue = filter; this.filterDiscussion({ path: this.getNotesDataByProp('discussionsPath'), filter }); this.toggleCommentsForm(); }, + toggleDropdown() { + $(this.$refs.dropdownToggle).dropdown('toggle'); + }, toggleCommentsForm() { this.setCommentsDisabled(this.currentValue === HISTORY_ONLY_FILTER_VALUE); }, toggleFilters(tab) { this.displayFilters = tab === DISCUSSION_TAB_LABEL; }, + handleLocationHash() { + const hash = getLocationHash(); + + if (/^note_/.test(hash) && this.currentValue !== DISCUSSION_FILTERS_DEFAULT_VALUE) { + this.selectFilter(this.defaultValue); + this.toggleDropdown(); // close dropdown + this.setTargetNoteHash(hash); + } + }, }, }; </script> diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index 3d60eb02db8..bde00ea87ff 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -1,6 +1,6 @@ <script> import { mapActions, mapGetters } from 'vuex'; -import { GlTooltipDirective } from '@gitlab/ui'; +import tooltip from '~/vue_shared/directives/tooltip'; import Icon from '~/vue_shared/components/icon.vue'; import Flash from '../../flash'; import { glEmojiTag } from '../../emoji'; @@ -10,7 +10,7 @@ export default { Icon, }, directives: { - GlTooltip: GlTooltipDirective, + tooltip, }, props: { awards: { @@ -167,9 +167,11 @@ export default { <button v-for="(awardList, awardName, index) in groupedAwards" :key="index" - v-gl-tooltip.bottom="{ boundary: 'viewport' }" + v-tooltip :class="getAwardClassBindings(awardList)" :title="awardTitle(awardList)" + data-boundary="viewport" + data-placement="bottom" class="btn award-control" type="button" @click="handleAward(awardName);" @@ -179,7 +181,7 @@ export default { </button> <div v-if="canAwardEmoji" class="award-menu-holder"> <button - v-gl-tooltip + v-tooltip :class="{ 'js-user-authored': isAuthoredByMe }" class="award-control btn js-add-award" title="Add reaction" diff --git a/app/assets/javascripts/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js index 07f32210d93..d54bff88f70 100644 --- a/app/assets/javascripts/pages/sessions/new/index.js +++ b/app/assets/javascripts/pages/sessions/new/index.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import UsernameValidator from './username_validator'; import SigninTabsMemoizer from './signin_tabs_memoizer'; import OAuthRememberMe from './oauth_remember_me'; +import preserveUrlFragment from './preserve_url_fragment'; document.addEventListener('DOMContentLoaded', () => { new UsernameValidator(); // eslint-disable-line no-new @@ -10,4 +11,8 @@ document.addEventListener('DOMContentLoaded', () => { new OAuthRememberMe({ container: $('.omniauth-container'), }).bindEvents(); + + // Save the URL fragment from the current window location. This will be present if the user was + // redirected to sign-in after attempting to access a protected URL that included a fragment. + preserveUrlFragment(window.location.hash); }); diff --git a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js index 761618109a4..191221a48cd 100644 --- a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js +++ b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility'; /** * OAuth-based login buttons have a separate "remember me" checkbox. @@ -24,9 +25,9 @@ export default class OAuthRememberMe { const href = $(element).attr('href'); if (rememberMe) { - $(element).attr('href', `${href}?remember_me=1`); + $(element).attr('href', mergeUrlParams({ remember_me: 1 }, href)); } else { - $(element).attr('href', href.replace('?remember_me=1', '')); + $(element).attr('href', removeParams(['remember_me'], href)); } }); } diff --git a/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js new file mode 100644 index 00000000000..e617fecaa0f --- /dev/null +++ b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js @@ -0,0 +1,32 @@ +import { mergeUrlParams, setUrlFragment } from '~/lib/utils/url_utility'; + +/** + * Ensure the given URL fragment is preserved by appending it to sign-in/sign-up form actions and + * OAuth/SAML login links. + * + * @param fragment {string} - url fragment to be preserved + */ +export default function preserveUrlFragment(fragment = '') { + if (fragment) { + const normalFragment = fragment.replace(/^#/, ''); + + // Append the fragment to all sign-in/sign-up form actions so it is preserved when the user is + // eventually redirected back to the originally requested URL. + const forms = document.querySelectorAll('#signin-container form'); + Array.prototype.forEach.call(forms, form => { + const actionWithFragment = setUrlFragment(form.getAttribute('action'), `#${normalFragment}`); + form.setAttribute('action', actionWithFragment); + }); + + // Append a redirect_fragment query param to all oauth provider links. The redirect_fragment + // query param will be available in the omniauth callback upon successful authentication + const anchors = document.querySelectorAll('#signin-container a.oauth-login'); + Array.prototype.forEach.call(anchors, anchor => { + const newHref = mergeUrlParams( + { redirect_fragment: normalFragment }, + anchor.getAttribute('href'), + ); + anchor.setAttribute('href', newHref); + }); + } +} diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js index e9ecec717d6..a7c3c9d104d 100644 --- a/app/assets/javascripts/pages/users/user_overview_block.js +++ b/app/assets/javascripts/pages/users/user_overview_block.js @@ -15,7 +15,8 @@ export default class UserOverviewBlock { } loadData() { - const loadingEl = document.querySelector(`${this.container} .loading`); + const containerEl = document.querySelector(this.container); + const loadingEl = containerEl.querySelector(`.loading`); loadingEl.classList.remove('hide'); @@ -42,7 +43,7 @@ export default class UserOverviewBlock { const nothingHereBlock = containerEl.querySelector('.nothing-here-block'); if (nothingHereBlock) { - nothingHereBlock.classList.add('text-left', 'p-0'); + nothingHereBlock.classList.add('p-5'); } } diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 78c7671856a..2c19973a114 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -70,7 +70,7 @@ export default { </thead> <tbody> <tr v-for="item in repo.list" :key="item.tag"> - <td> + <td class="monospace"> {{ item.tag }} <clipboard-button v-if="item.location" @@ -80,7 +80,9 @@ export default { /> </td> <td> - <span v-gl-tooltip.bottom :title="item.revision">{{ item.shortRevision }}</span> + <span v-gl-tooltip.bottom class="monospace" :title="item.revision">{{ + item.shortRevision + }}</span> </td> <td> {{ formatSize(item.size) }} diff --git a/app/assets/javascripts/releases/components/app.vue b/app/assets/javascripts/releases/components/app.vue index 0ad5ee2915c..5a06c4fec58 100644 --- a/app/assets/javascripts/releases/components/app.vue +++ b/app/assets/javascripts/releases/components/app.vue @@ -1,12 +1,12 @@ <script> import { mapState, mapActions } from 'vuex'; -import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui'; +import { GlSkeletonLoading, GlEmptyState } from '@gitlab/ui'; import ReleaseBlock from './release_block.vue'; export default { name: 'ReleasesApp', components: { - GlLoadingIcon, + GlSkeletonLoading, GlEmptyState, ReleaseBlock, }, @@ -43,7 +43,7 @@ export default { </script> <template> <div class="prepend-top-default"> - <gl-loading-icon v-if="isLoading" :size="2" class="js-loading prepend-top-20" /> + <gl-skeleton-loading v-if="isLoading" class="js-loading" /> <gl-empty-state v-else-if="shouldRenderEmptyState" diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js index 066a3b833d7..0cc4fd59f5e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js @@ -13,6 +13,8 @@ export default function deviseState(data) { return stateKey.conflicts; } else if (data.work_in_progress) { return stateKey.workInProgress; + } else if (this.shouldBeRebased) { + return stateKey.rebase; } else if (this.onlyAllowMergeIfPipelineSucceeds && this.isPipelineFailed) { return stateKey.pipelineFailed; } else if (this.hasMergeableDiscussionsState) { @@ -25,8 +27,6 @@ export default function deviseState(data) { return this.mergeError ? stateKey.autoMergeFailed : stateKey.mergeWhenPipelineSucceeds; } else if (!this.canMerge) { return stateKey.notAllowedToMerge; - } else if (this.shouldBeRebased) { - return stateKey.rebase; } else if (this.canBeMerged) { return stateKey.readyToMerge; } diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue index f98560f7336..b9f884074d0 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue @@ -50,7 +50,7 @@ export default { :help-page-path="helpPagePath" @apply="applySuggestion" /> - <table class="mb-3 md-suggestion-diff"> + <table class="mb-3 md-suggestion-diff js-syntax-highlight code"> <tbody> <!-- Old Line --> <tr class="line_holder old"> diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 18c62cb4f1e..b78f11aadf1 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -9,7 +9,7 @@ @media (min-width: map-get($grid-breakpoints, md)) { position: -webkit-sticky; position: sticky; - top: 92px; + top: $header-height + 51px; margin-left: -1px; border-left: 1px solid $border-color; z-index: 102; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 5b30295adf9..86f571dd90d 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -1,10 +1,6 @@ /** * Note Form */ -.comment-btn { - @extend .btn-success; -} - .diff-file .diff-content { tr.line_holder:hover > td .line_note_link { opacity: 1; @@ -386,7 +382,7 @@ table { } .comment-type-dropdown { - .comment-btn { + .btn-success { width: auto; } @@ -417,7 +413,7 @@ table { width: 100%; margin-bottom: 10px; - .comment-btn { + .btn-success { flex-grow: 1; flex-shrink: 0; width: auto; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index a5b1eff3e1d..69b7b80dbf4 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -153,12 +153,12 @@ $note-form-margin-left: 72px; position: relative; .timeline-discussion-body { - margin-top: -8px; + margin-top: -$gl-padding-8; overflow-x: auto; overflow-y: hidden; - .discussion-resolved-text { - margin-bottom: 8px; + .note-body { + margin-top: $gl-padding-8; } } diff --git a/app/controllers/admin/requests_profiles_controller.rb b/app/controllers/admin/requests_profiles_controller.rb index 57f7d3e3951..89d4c4f18d9 100644 --- a/app/controllers/admin/requests_profiles_controller.rb +++ b/app/controllers/admin/requests_profiles_controller.rb @@ -11,7 +11,7 @@ class Admin::RequestsProfilesController < Admin::ApplicationController profile = Gitlab::RequestProfiler::Profile.find(clean_name) if profile - render html: profile.content + render html: profile.content.html_safe else redirect_to admin_requests_profiles_path, alert: 'Profile not found' end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 30be50d4595..f8e482937d5 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -75,6 +75,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private def omniauth_flow(auth_module, identity_linker: nil) + if fragment = request.env.dig('omniauth.params', 'redirect_fragment').presence + store_redirect_fragment(fragment) + end + if current_user log_audit_event(current_user, with: oauth['provider']) @@ -189,4 +193,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController request_params = request.env['omniauth.params'] (request_params['remember_me'] == '1') if request_params.present? end + + def store_redirect_fragment(redirect_fragment) + key = stored_location_key_for(:user) + location = session[key] + if uri = parse_uri(location) + uri.fragment = redirect_fragment + store_location_for(:user, uri.to_s) + end + end end diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 1a91e07b97f..9a0997e92ee 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -85,20 +85,15 @@ class Projects::ArtifactsController < Projects::ApplicationController end end - # rubocop: disable CodeReuse/ActiveRecord def build_from_id - project.builds.find_by(id: params[:job_id]) if params[:job_id] + project.get_build(params[:job_id]) if params[:job_id] end - # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord def build_from_ref return unless @ref_name - builds = project.latest_successful_builds_for(@ref_name) - builds.find_by(name: params[:job]) + project.latest_successful_build_for(params[:job], @ref_name) end - # rubocop: enable CodeReuse/ActiveRecord def artifacts_file @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive) diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb index 7d4d566499c..d3d5ba5c75d 100644 --- a/app/controllers/projects/build_artifacts_controller.rb +++ b/app/controllers/projects/build_artifacts_controller.rb @@ -44,18 +44,13 @@ class Projects::BuildArtifactsController < Projects::ApplicationController @job ||= job_from_id || job_from_ref end - # rubocop: disable CodeReuse/ActiveRecord def job_from_id - project.builds.find_by(id: params[:build_id]) if params[:build_id] + project.get_build(params[:build_id]) if params[:build_id] end - # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord def job_from_ref return unless @ref_name - jobs = project.latest_successful_builds_for(@ref_name) - jobs.find_by(name: params[:job]) + project.latest_successful_build_for(params[:job], @ref_name) end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb new file mode 100644 index 00000000000..4596b6c91f2 --- /dev/null +++ b/app/controllers/projects/error_tracking_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class Projects::ErrorTrackingController < Projects::ApplicationController + before_action :check_feature_flag! + before_action :authorize_read_sentry_issue! + before_action :push_feature_flag_to_frontend + + POLLING_INTERVAL = 10_000 + + def index + respond_to do |format| + format.html + format.json do + set_polling_interval + render_index_json + end + end + end + + private + + def render_index_json + service = ErrorTracking::ListIssuesService.new(project, current_user) + result = service.execute + + unless result[:status] == :success + return render json: { message: result[:message] }, + status: result[:http_status] || :bad_request + end + + render json: { + errors: serialize_errors(result[:issues]), + external_url: service.external_url + } + end + + def set_polling_interval + Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) + end + + def serialize_errors(errors) + ErrorTracking::ErrorSerializer + .new(project: project, user: current_user) + .represent(errors) + end + + def check_feature_flag! + render_404 unless Feature.enabled?(:error_tracking, project) + end + + def push_feature_flag_to_frontend + push_frontend_feature_flag(:error_tracking, current_user) + end +end diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb index 632c64c2f1c..7f4947ba27a 100644 --- a/app/models/error_tracking/project_error_tracking_setting.rb +++ b/app/models/error_tracking/project_error_tracking_setting.rb @@ -2,13 +2,58 @@ module ErrorTracking class ProjectErrorTrackingSetting < ActiveRecord::Base + include ReactiveCaching + + self.reactive_cache_key = ->(setting) { [setting.class.model_name.singular, setting.project_id] } + belongs_to :project validates :api_url, length: { maximum: 255 }, public_url: true, url: { enforce_sanitization: true } + validate :validate_api_url_path + attr_encrypted :token, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, algorithm: 'aes-256-gcm' + + after_save :clear_reactive_cache! + + def sentry_client + Sentry::Client.new(api_url, token) + end + + def sentry_external_url + self.class.extract_sentry_external_url(api_url) + end + + def list_sentry_issues(opts = {}) + with_reactive_cache('list_issues', opts.stringify_keys) do |result| + { issues: result } + end + end + + def calculate_reactive_cache(request, opts) + case request + when 'list_issues' + sentry_client.list_issues(**opts.symbolize_keys) + end + end + + # http://HOST/api/0/projects/ORG/PROJECT + # -> + # http://HOST/ORG/PROJECT + def self.extract_sentry_external_url(url) + url.sub('api/0/projects/', '') + end + + private + + def validate_api_url_path + unless URI(api_url).path.starts_with?('/api/0/projects') + errors.add(:api_url, 'path needs to start with /api/0/projects') + end + rescue URI::InvalidURIError + end end end diff --git a/app/models/project.rb b/app/models/project.rb index cab173503ce..a66ed6736ca 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -647,19 +647,19 @@ class Project < ActiveRecord::Base end # ref can't be HEAD, can only be branch/tag name or SHA - def latest_successful_builds_for(ref = default_branch) + def latest_successful_build_for(job_name, ref = default_branch) latest_pipeline = ci_pipelines.latest_successful_for(ref) + return unless latest_pipeline - if latest_pipeline - latest_pipeline.builds.latest.with_artifacts_archive - else - builds.none - end + latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name) end - def latest_successful_build_for(job_name, ref = default_branch) - builds = latest_successful_builds_for(ref) - builds.find_by!(name: job_name) + def latest_successful_build_for!(job_name, ref = default_branch) + latest_successful_build_for(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}")) + end + + def get_build(id) + builds.find_by(id: id) end def merge_base_commit(first_commit_id, second_commit_id) diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index b8e17087db5..3245cd22e73 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -39,9 +39,7 @@ class TeamcityService < CiService end def help - 'The build configuration in Teamcity must use the build format '\ - 'number %build.vcs.number% '\ - 'you will also want to configure monitoring of all branches so merge '\ + 'You will want to configure monitoring of all branches so merge '\ 'requests build, that setting is in the vsc root advanced settings.' end @@ -70,7 +68,7 @@ class TeamcityService < CiService end def calculate_reactive_cache(sha, ref) - response = get_path("httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}") + response = get_path("httpAuth/app/rest/builds/branch:unspecified:any,revision:#{sha}") { build_page: read_build_page(response), commit_status: read_commit_status(response) } end diff --git a/app/models/releases/link.rb b/app/models/releases/link.rb index 766cb2efff2..6f639e5a7b2 100644 --- a/app/models/releases/link.rb +++ b/app/models/releases/link.rb @@ -6,7 +6,7 @@ module Releases belongs_to :release - validates :url, presence: true, url: true + validates :url, presence: true, url: true, uniqueness: { scope: :release } validates :name, presence: true, uniqueness: { scope: :release } scope :sorted, -> { order(created_at: :desc) } diff --git a/app/models/repository.rb b/app/models/repository.rb index b19ae2e0e6a..b47238b52f1 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1072,19 +1072,11 @@ class Repository end def cache - @cache ||= if is_wiki - Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki') - else - Gitlab::RepositoryCache.new(self) - end + @cache ||= Gitlab::RepositoryCache.new(self) end def request_store_cache - @request_store_cache ||= if is_wiki - Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki', backend: Gitlab::SafeRequestStore) - else - Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore) - end + @request_store_cache ||= Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore) end def tags_sorted_by_committed_date diff --git a/app/models/ssh_host_key.rb b/app/models/ssh_host_key.rb index b6844dbe870..99a0c54a26a 100644 --- a/app/models/ssh_host_key.rb +++ b/app/models/ssh_host_key.rb @@ -52,6 +52,11 @@ class SshHostKey @compare_host_keys = compare_host_keys end + # Needed for reactive caching + def self.primary_key + 'id' + end + def id [project.id, url].join(':') end diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb index 0d0f1c28bad..72de04203a6 100644 --- a/app/policies/base_policy.rb +++ b/app/policies/base_policy.rb @@ -7,6 +7,10 @@ class BasePolicy < DeclarativePolicy::Base with_options scope: :user, score: 0 condition(:admin) { @user&.admin? } + desc "User has access to all private groups & projects" + with_options scope: :user, score: 0 + condition(:full_private_access) { @user&.full_private_access? } + with_options scope: :user, score: 0 condition(:external_user) { @user.nil? || @user.external? } diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index d70417e710e..12f9f29dcc1 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -200,6 +200,7 @@ class ProjectPolicy < BasePolicy enable :read_environment enable :read_deployment enable :read_merge_request + enable :read_sentry_issue end # We define `:public_user_access` separately because there are cases in gitlab-ee diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb index 288bf070cfc..7dafa33bb99 100644 --- a/app/policies/project_snippet_policy.rb +++ b/app/policies/project_snippet_policy.rb @@ -5,13 +5,12 @@ class ProjectSnippetPolicy < BasePolicy desc "Snippet is public" condition(:public_snippet, scope: :subject) { @subject.public? } + condition(:internal_snippet, scope: :subject) { @subject.internal? } condition(:private_snippet, scope: :subject) { @subject.private? } condition(:public_project, scope: :subject) { @subject.project.public? } condition(:is_author) { @user && @subject.author == @user } - condition(:internal, scope: :subject) { @subject.internal? } - # We have to check both project feature visibility and a snippet visibility and take the stricter one # This will be simplified - check https://gitlab.com/gitlab-org/gitlab-ce/issues/27573 rule { ~can?(:read_project) }.policy do @@ -26,13 +25,13 @@ class ProjectSnippetPolicy < BasePolicy # is used to hide/show various snippet-related controls, so we can't just move # all of the handling here. rule do - all?(private_snippet | (internal & external_user), + all?(private_snippet | (internal_snippet & external_user), ~project.guest, - ~admin, - ~is_author) + ~is_author, + ~full_private_access) end.prevent :read_project_snippet - rule { internal & ~is_author & ~admin }.policy do + rule { internal_snippet & ~is_author & ~admin }.policy do prevent :update_project_snippet prevent :admin_project_snippet end diff --git a/app/serializers/error_tracking/error_entity.rb b/app/serializers/error_tracking/error_entity.rb new file mode 100644 index 00000000000..91388e7c3ad --- /dev/null +++ b/app/serializers/error_tracking/error_entity.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ErrorTracking + class ErrorEntity < Grape::Entity + expose :id, :title, :type, :user_count, :count, + :first_seen, :last_seen, :message, :culprit, + :external_url, :project_id, :project_name, :project_slug, + :short_id, :status, :frequency + end +end diff --git a/app/serializers/error_tracking/error_serializer.rb b/app/serializers/error_tracking/error_serializer.rb new file mode 100644 index 00000000000..ff9a645eb16 --- /dev/null +++ b/app/serializers/error_tracking/error_serializer.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ErrorTracking + class ErrorSerializer < BaseSerializer + entity ErrorEntity + end +end diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb index 13f892aabb8..5c4a34043c1 100644 --- a/app/services/ci/destroy_pipeline_service.rb +++ b/app/services/ci/destroy_pipeline_service.rb @@ -5,8 +5,6 @@ module Ci def execute(pipeline) raise Gitlab::Access::AccessDeniedError unless can?(current_user, :destroy_pipeline, pipeline) - AuditEventService.new(current_user, pipeline).security_event - pipeline.destroy! end end diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb new file mode 100644 index 00000000000..4cc35cfa4a8 --- /dev/null +++ b/app/services/error_tracking/list_issues_service.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module ErrorTracking + class ListIssuesService < ::BaseService + DEFAULT_ISSUE_STATUS = 'unresolved' + DEFAULT_LIMIT = 20 + + def execute + return error('not enabled') unless enabled? + return error('access denied') unless can_read? + + result = project_error_tracking_setting + .list_sentry_issues(issue_status: issue_status, limit: limit) + + # our results are not yet ready + unless result + return error('not ready', :no_content) + end + + success(issues: result[:issues]) + end + + def external_url + project_error_tracking_setting&.sentry_external_url + end + + private + + def project_error_tracking_setting + project.error_tracking_setting + end + + def issue_status + params[:issue_status] || DEFAULT_ISSUE_STATUS + end + + def limit + params[:limit] || DEFAULT_LIMIT + end + + def enabled? + project_error_tracking_setting&.enabled? + end + + def can_read? + can?(current_user, :read_sentry_issue, project) + end + end +end diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml index fdaad1cf181..c99d7e9b8e9 100644 --- a/app/views/admin/application_settings/_ci_cd.html.haml +++ b/app/views/admin/application_settings/_ci_cd.html.haml @@ -13,7 +13,7 @@ = s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.') = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank' .form-group - = f.label :auto_devops_domain, class: 'label-bold' + = f.label :auto_devops_domain, s_('AdminSettings|Auto DevOps domain'), class: 'label-bold' = f.text_field :auto_devops_domain, class: 'form-control', placeholder: 'domain.com' .form-text.text-muted = s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.") diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 34d4293bd45..30ed7ed6b29 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,6 +1,6 @@ - page_title "Sign in" -%div +#signin-container - if form_based_providers.any? = render 'devise/shared/tabs_ldap' - else diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml index bcfb6d99716..806a24e2bbc 100644 --- a/app/views/groups/runners/_group_runners.html.haml +++ b/app/views/groups/runners/_group_runners.html.haml @@ -21,6 +21,6 @@ - else %h4.underlined-title - = _('Available group Runners : %{runners}.').html_safe % { runners: @group.runners.count } + = _('Available group Runners: %{runners}').html_safe % { runners: @group.runners.count } %ul.bordered-list = render partial: 'groups/runners/runner', collection: @group.runners, as: :runner diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml index 2eef599cf84..2cc3d921abc 100644 --- a/app/views/projects/diffs/_warning.html.haml +++ b/app/views/projects/diffs/_warning.html.haml @@ -9,4 +9,4 @@ = link_to _("Plain diff"), merge_request_path(@merge_request, format: :diff), class: "btn btn-sm" = link_to _("Email patch"), merge_request_path(@merge_request, format: :patch), class: "btn btn-sm" %p - = _("To preserve performance only <strong>%{display_size} of ${real_size}</strong> files are displayed.").html_safe % { display_size: diff_files.size, real_size: diff_files.real_size } + = _("To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed.").html_safe % { display_size: diff_files.size, real_size: diff_files.real_size } diff --git a/app/views/projects/error_tracking/index.html.haml b/app/views/projects/error_tracking/index.html.haml new file mode 100644 index 00000000000..a3e0dc75f6f --- /dev/null +++ b/app/views/projects/error_tracking/index.html.haml @@ -0,0 +1 @@ +- page_title _('Errors') diff --git a/app/views/projects/runners/_group_runners.html.haml b/app/views/projects/runners/_group_runners.html.haml index a6c16c70313..a24ada53bac 100644 --- a/app/views/projects/runners/_group_runners.html.haml +++ b/app/views/projects/runners/_group_runners.html.haml @@ -32,6 +32,6 @@ - else %h4.underlined-title - = _('Available group Runners : %{runners}').html_safe % { runners: @group_runners.count } + = _('Available group Runners: %{runners}').html_safe % { runners: @group_runners.count } %ul.bordered-list = render partial: 'projects/runners/runner', collection: @group_runners, as: :runner diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml index 0434860dec4..2691ec4cd46 100644 --- a/app/views/shared/empty_states/_issues.html.haml +++ b/app/views/shared/empty_states/_issues.html.haml @@ -2,6 +2,10 @@ - project_select_button = local_assigns.fetch(:project_select_button, false) - show_import_button = local_assigns.fetch(:show_import_button, false) && Feature.enabled?(:issues_import_csv) && can?(current_user, :import_issues, @project) - has_button = button_path || project_select_button +- closed_issues_count = issuables_count_for_state(:issues, :closed) +- opened_issues_count = issuables_count_for_state(:issues, :opened) +- is_opened_state = params[:state] == 'opened' +- is_closed_state = params[:state] == 'closed' .row.empty-state .col-12 @@ -14,6 +18,20 @@ = _("Sorry, your filter produced no results") %p.text-center = _("To widen your search, change or remove filters above") + - if show_new_issue_link?(@project) + .text-center + = link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", title: _("New issue"), id: "new_issue_body_link" + - elsif is_opened_state && opened_issues_count == 0 && closed_issues_count > 0 + %h4.text-center + = _("There are no open issues") + %p.text-center + = _("To keep this project going, create a new issue") + - if show_new_issue_link?(@project) + .text-center + = link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", title: _("New issue"), id: "new_issue_body_link" + - elsif is_closed_state && opened_issues_count > 0 && closed_issues_count == 0 + %h4.text-center + = _("There are no closed issues") - elsif current_user %h4 = _("The Issue Tracker is the place to add things that need to be improved or solved in a project") diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml index 06ceb9738bc..be5b1c6b6ce 100644 --- a/app/views/shared/empty_states/_merge_requests.html.haml +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -1,6 +1,11 @@ - button_path = local_assigns.fetch(:button_path, false) - project_select_button = local_assigns.fetch(:project_select_button, false) - has_button = button_path || project_select_button +- closed_merged_count = issuables_count_for_state(:merged, :closed) +- opened_merged_count = issuables_count_for_state(:merged, :opened) +- is_opened_state = params[:state] == 'opened' +- is_closed_state = params[:state] == 'closed' +- can_create_merge_request = merge_request_source_project_for_project(@project) .row.empty-state.merge-requests .col-12 @@ -13,6 +18,20 @@ = _("Sorry, your filter produced no results") %p.text-center = _("To widen your search, change or remove filters above") + .text-center + - if can_create_merge_request + = link_to _("New merge request"), project_new_merge_request_path(@project), class: "btn btn-success", title: _("New merge request"), id: "new_merge_request_body_link" + - elsif is_opened_state && opened_merged_count == 0 && closed_merged_count > 0 + %h4.text-center + = _("There are no open merge requests") + %p.text-center + = _("To keep this project going, create a new merge request") + .text-center + - if can_create_merge_request + = link_to _("New merge request"), project_new_merge_request_path(@project), class: "btn btn-success", title: _("New merge request"), id: "new_merge_request_body_link" + - elsif is_closed_state && opened_merged_count > 0 && closed_merged_count == 0 + %h4.text-center + = _("There are no closed merge requests") - else %h4 = _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others") diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml index f487c0bf0d5..c3f5eeb0da6 100644 --- a/app/views/shared/notes/_comment_button.html.haml +++ b/app/views/shared/notes/_comment_button.html.haml @@ -1,10 +1,10 @@ - noteable_name = @note.noteable.human_class_name .float-left.btn-group.append-right-10.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown - %input.btn.btn-nr.btn-success.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment') } + %input.btn.btn-nr.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment') } - if @note.can_be_discussion_note? - = button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do + = button_tag type: 'button', class: 'btn btn-nr dropdown-toggle btn-success js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do = icon('caret-down', class: 'toggle-icon') %ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } } diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 15c29e14cc0..7d90d9ca4a5 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -33,4 +33,9 @@ %span you have no access to. = paginate_collection(projects, remote: remote) unless skip_pagination - else - .nothing-here-block No projects found + .nothing-here-block + .svg-content.svg-130 + = image_tag 'illustrations/profile-page/personal-project.svg' + %div + %span + = s_('UserProfile|This user doesn\'t have any personal projects') diff --git a/app/workers/reactive_caching_worker.rb b/app/workers/reactive_caching_worker.rb index 96ff8cd6222..7c66ac046ea 100644 --- a/app/workers/reactive_caching_worker.rb +++ b/app/workers/reactive_caching_worker.rb @@ -12,7 +12,7 @@ class ReactiveCachingWorker end return unless klass - klass.find_by(id: id).try(:exclusively_update_reactive_cache!, *args) + klass.find_by(klass.primary_key => id).try(:exclusively_update_reactive_cache!, *args) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/changelogs/unreleased/25043-empty-states.yml b/changelogs/unreleased/25043-empty-states.yml new file mode 100644 index 00000000000..529a8b3206f --- /dev/null +++ b/changelogs/unreleased/25043-empty-states.yml @@ -0,0 +1,5 @@ +--- +title: Make issuable empty states actionable +merge_request: 24077 +author: +type: changed diff --git a/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml b/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml new file mode 100644 index 00000000000..4d543db567d --- /dev/null +++ b/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml @@ -0,0 +1,5 @@ +--- +title: Ensured links to a comment or system note anchor resolves to the right note if a user has a discussion filter. +merge_request: 24228 +author: +type: changed diff --git a/changelogs/unreleased/55242-skeleton-loading-releases.yml b/changelogs/unreleased/55242-skeleton-loading-releases.yml new file mode 100644 index 00000000000..43cda64ce04 --- /dev/null +++ b/changelogs/unreleased/55242-skeleton-loading-releases.yml @@ -0,0 +1,5 @@ +--- +title: Adds skeleton loading to releases page +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml b/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml new file mode 100644 index 00000000000..724de733b7c --- /dev/null +++ b/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml @@ -0,0 +1,5 @@ +--- +title: Build number does not need to be tweaked anymore for the TeamCity integration to work properly. +merge_request: 23898 +author: +type: changed diff --git a/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml b/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml new file mode 100644 index 00000000000..071036cd568 --- /dev/null +++ b/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml @@ -0,0 +1,5 @@ +--- +title: Handle regular job dependencies next to parallelized job dependencies. +merge_request: 24273 +author: +type: fixed diff --git a/changelogs/unreleased/55945-suggested-change-highlight.yml b/changelogs/unreleased/55945-suggested-change-highlight.yml new file mode 100644 index 00000000000..611854d36ab --- /dev/null +++ b/changelogs/unreleased/55945-suggested-change-highlight.yml @@ -0,0 +1,5 @@ +--- +title: Add syntax highlighting to suggestion diff +merge_request: 24156 +author: +type: fixed diff --git a/changelogs/unreleased/55958-inconsistent-spacing-between-note-and-user-avatar-in-discussions.yml b/changelogs/unreleased/55958-inconsistent-spacing-between-note-and-user-avatar-in-discussions.yml new file mode 100644 index 00000000000..765398cda84 --- /dev/null +++ b/changelogs/unreleased/55958-inconsistent-spacing-between-note-and-user-avatar-in-discussions.yml @@ -0,0 +1,5 @@ +--- +title: Fix spacing on discussions +merge_request: !24197 +author: +type: fixed diff --git a/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml b/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml new file mode 100644 index 00000000000..ff9d4f2c175 --- /dev/null +++ b/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml @@ -0,0 +1,4 @@ +title: Remove multilingual translation from the word "in" in the job details sidebar. +merge_request: 24192 +author: Nathan Friend +type: changed diff --git a/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml b/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml new file mode 100644 index 00000000000..7d767e220f7 --- /dev/null +++ b/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml @@ -0,0 +1,5 @@ +--- +title: Add uniqueness validation to url column in Releases::Link model +merge_request: 24223 +author: +type: other diff --git a/changelogs/unreleased/backup_aws_sse-c.yml b/changelogs/unreleased/backup_aws_sse-c.yml new file mode 100644 index 00000000000..78b57d7efc3 --- /dev/null +++ b/changelogs/unreleased/backup_aws_sse-c.yml @@ -0,0 +1,5 @@ +title: Add support for customer provided encryption keys for Amazon S3 remote backups +merge_request: 23797 +author: Pepijn Van Eeckhoudt +type: added + diff --git a/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml b/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml new file mode 100644 index 00000000000..bb0b193a846 --- /dev/null +++ b/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml @@ -0,0 +1,5 @@ +--- +title: Fixes Auto DevOps title on CI/CD admin settings +merge_request: 24249 +author: +type: other diff --git a/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml b/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml new file mode 100644 index 00000000000..f64b29644b0 --- /dev/null +++ b/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml @@ -0,0 +1,5 @@ +--- +title: Fix files/blob api endpoints content disposition +merge_request: 24267 +author: +type: fixed diff --git a/changelogs/unreleased/gt-remove-unused-button-class.yml b/changelogs/unreleased/gt-remove-unused-button-class.yml new file mode 100644 index 00000000000..f7889e1d6f6 --- /dev/null +++ b/changelogs/unreleased/gt-remove-unused-button-class.yml @@ -0,0 +1,5 @@ +--- +title: Remove unused button classes `btn-create` and `comment-btn` +merge_request: 23232 +author: George Tsiolis +type: performance diff --git a/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml b/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml new file mode 100644 index 00000000000..fa06a78adae --- /dev/null +++ b/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml @@ -0,0 +1,5 @@ +--- +title: Update string structure for available group runners +merge_request: 24187 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml b/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml new file mode 100644 index 00000000000..8025cd472bd --- /dev/null +++ b/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml @@ -0,0 +1,6 @@ +--- +title: Fix lost line number when navigating to a specific line in a protected file + before authenticating. +merge_request: 19165 +author: Scott Escue +type: fixed diff --git a/changelogs/unreleased/monospace-registry-tags.yml b/changelogs/unreleased/monospace-registry-tags.yml new file mode 100644 index 00000000000..b5992707d8c --- /dev/null +++ b/changelogs/unreleased/monospace-registry-tags.yml @@ -0,0 +1,5 @@ +--- +title: Use monospace font for registry table tag id and tag name +merge_request: 24205 +author: +type: other diff --git a/changelogs/unreleased/mr-rebase-failing-tests.yml b/changelogs/unreleased/mr-rebase-failing-tests.yml new file mode 100644 index 00000000000..07ae05766b1 --- /dev/null +++ b/changelogs/unreleased/mr-rebase-failing-tests.yml @@ -0,0 +1,5 @@ +--- +title: Fixed rebase button not showing in merge request widget +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml new file mode 100644 index 00000000000..23338a60c2a --- /dev/null +++ b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml @@ -0,0 +1,5 @@ +--- +title: Fixed double tooltips on note awards buttons +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/pl-reactive-caching-primary_key.yml b/changelogs/unreleased/pl-reactive-caching-primary_key.yml new file mode 100644 index 00000000000..a72933c19b1 --- /dev/null +++ b/changelogs/unreleased/pl-reactive-caching-primary_key.yml @@ -0,0 +1,5 @@ +--- +title: Enable caching for records which primary key is not `id` +merge_request: 24245 +author: +type: fixed diff --git a/changelogs/unreleased/profile-project-empty-state.yml b/changelogs/unreleased/profile-project-empty-state.yml new file mode 100644 index 00000000000..484306d5b98 --- /dev/null +++ b/changelogs/unreleased/profile-project-empty-state.yml @@ -0,0 +1,5 @@ +--- +title: Added empty project illustration and updated text to user profile overview +merge_request: 23973 +author: Fernando Arias +type: changed diff --git a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml new file mode 100644 index 00000000000..ce8e1829b48 --- /dev/null +++ b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml @@ -0,0 +1,5 @@ +--- +title: Remove extra space between MR tab bar and sticky file headers +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-real-size-warnings.yml b/changelogs/unreleased/sh-fix-real-size-warnings.yml new file mode 100644 index 00000000000..5062ffd677c --- /dev/null +++ b/changelogs/unreleased/sh-fix-real-size-warnings.yml @@ -0,0 +1,5 @@ +--- +title: Fix broken templated "Too many changes to show" text +merge_request: 24282 +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-request-profiles-html.yml b/changelogs/unreleased/sh-fix-request-profiles-html.yml new file mode 100644 index 00000000000..74e4115db8e --- /dev/null +++ b/changelogs/unreleased/sh-fix-request-profiles-html.yml @@ -0,0 +1,5 @@ +--- +title: Fix requests profiler in admin page not rendering HTML properly +merge_request: 24291 +author: +type: fixed diff --git a/changelogs/unreleased/tc-remove-20181218192239-migration.yml b/changelogs/unreleased/tc-remove-20181218192239-migration.yml new file mode 100644 index 00000000000..81e06a99c1f --- /dev/null +++ b/changelogs/unreleased/tc-remove-20181218192239-migration.yml @@ -0,0 +1,5 @@ +--- +title: Remove migration to backfill project_repositories for legacy storage projects +merge_request: 24299 +author: +type: removed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 7fe85f0e0d7..6fc33e8971e 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -635,6 +635,10 @@ production: &base # multipart_chunk_size: 104857600 # # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional # # encryption: 'AES256' + # # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional + # # This should be set to the 256-bit, base64-encoded encryption key for Amazon S3 to use to encrypt or decrypt your data. + # # 'encryption' must also be set in order for this to have any effect. + # # encryption_key: '<base64 key>' # # Specifies Amazon S3 storage class to use for backups, this is optional # # storage_class: 'STANDARD' diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index db35fa96ea2..1aed41e02ab 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -392,6 +392,7 @@ Settings.backup['archive_permissions'] ||= 0600 Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 Settings.backup['upload']['encryption'] ||= nil +Settings.backup['upload']['encryption_key'] ||= ENV['GITLAB_BACKUP_ENCRYPTION_KEY'] Settings.backup['upload']['storage_class'] ||= nil # diff --git a/config/routes/project.rb b/config/routes/project.rb index a2c383e4648..797bf6de37b 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -443,6 +443,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + resources :error_tracking, only: [:index], controller: :error_tracking + # Since both wiki and repository routing contains wildcard characters # its preferable to keep it below all other project routes draw :wiki diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index aa8686ac7d8..9a5f7cf8175 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -1,94 +1,136 @@ require './spec/support/sidekiq' +# rubocop:disable Rails/Output + Sidekiq::Testing.inline! do Gitlab::Seeder.quiet do - project_urls = [ - 'https://gitlab.com/gitlab-org/gitlab-test.git', - 'https://gitlab.com/gitlab-org/gitlab-shell.git', - 'https://gitlab.com/gnuwget/wget2.git', - 'https://gitlab.com/Commit451/LabCoat.git', - 'https://github.com/jashkenas/underscore.git', - 'https://github.com/flightjs/flight.git', - 'https://github.com/twitter/typeahead.js.git', - 'https://github.com/h5bp/html5-boilerplate.git', - 'https://github.com/google/material-design-lite.git', - 'https://github.com/jlevy/the-art-of-command-line.git', - 'https://github.com/FreeCodeCamp/freecodecamp.git', - 'https://github.com/google/deepdream.git', - 'https://github.com/jtleek/datasharing.git', - 'https://github.com/WebAssembly/design.git', - 'https://github.com/airbnb/javascript.git', - 'https://github.com/tessalt/echo-chamber-js.git', - 'https://github.com/atom/atom.git', - 'https://github.com/mattermost/mattermost-server.git', - 'https://github.com/purifycss/purifycss.git', - 'https://github.com/facebook/nuclide.git', - 'https://github.com/wbkd/awesome-d3.git', - 'https://github.com/kilimchoi/engineering-blogs.git', - 'https://github.com/gilbarbara/logos.git', - 'https://github.com/reduxjs/redux.git', - 'https://github.com/awslabs/s2n.git', - 'https://github.com/arkency/reactjs_koans.git', - 'https://github.com/twbs/bootstrap.git', - 'https://github.com/chjj/ttystudio.git', - 'https://github.com/MostlyAdequate/mostly-adequate-guide.git', - 'https://github.com/octocat/Spoon-Knife.git', - 'https://github.com/opencontainers/runc.git', - 'https://github.com/googlesamples/android-topeka.git' - ] - - # You can specify how many projects you need during seed execution - size = ENV['SIZE'].present? ? ENV['SIZE'].to_i : 8 - - project_urls.first(size).each_with_index do |url, i| - group_path, project_path = url.split('/')[-2..-1] - - group = Group.find_by(path: group_path) - - unless group - group = Group.new( - name: group_path.titleize, - path: group_path - ) - group.description = FFaker::Lorem.sentence - group.save - - group.add_owner(User.first) - end + Gitlab::Seeder.without_gitaly_timeout do + project_urls = %w[ + https://gitlab.com/gitlab-org/gitlab-test.git + https://gitlab.com/gitlab-org/gitlab-shell.git + https://gitlab.com/gnuwget/wget2.git + https://gitlab.com/Commit451/LabCoat.git + https://github.com/jashkenas/underscore.git + https://github.com/flightjs/flight.git + https://github.com/twitter/typeahead.js.git + https://github.com/h5bp/html5-boilerplate.git + https://github.com/google/material-design-lite.git + https://github.com/jlevy/the-art-of-command-line.git + https://github.com/FreeCodeCamp/freecodecamp.git + https://github.com/google/deepdream.git + https://github.com/jtleek/datasharing.git + https://github.com/WebAssembly/design.git + https://github.com/airbnb/javascript.git + https://github.com/tessalt/echo-chamber-js.git + https://github.com/atom/atom.git + https://github.com/mattermost/mattermost-server.git + https://github.com/purifycss/purifycss.git + https://github.com/facebook/nuclide.git + https://github.com/wbkd/awesome-d3.git + https://github.com/kilimchoi/engineering-blogs.git + https://github.com/gilbarbara/logos.git + https://github.com/reduxjs/redux.git + https://github.com/awslabs/s2n.git + https://github.com/arkency/reactjs_koans.git + https://github.com/twbs/bootstrap.git + https://github.com/chjj/ttystudio.git + https://github.com/MostlyAdequate/mostly-adequate-guide.git + https://github.com/octocat/Spoon-Knife.git + https://github.com/opencontainers/runc.git + https://github.com/googlesamples/android-topeka.git + ] - project_path.gsub!(".git", "") + large_project_urls = %w[ + https://github.com/torvalds/linux.git + https://gitlab.gnome.org/GNOME/gimp.git + https://gitlab.gnome.org/GNOME/gnome-mud.git + https://gitlab.com/fdroid/fdroidclient.git + https://gitlab.com/inkscape/inkscape.git + https://github.com/gnachman/iTerm2.git + ] - params = { - import_url: url, - namespace_id: group.id, - name: project_path.titleize, - description: FFaker::Lorem.sentence, - visibility_level: Gitlab::VisibilityLevel.values.sample, - skip_disk_validation: true - } + def create_project(url, force_latest_storage: false) + group_path, project_path = url.split('/')[-2..-1] - if i % 2 == 0 - params[:storage_version] = Project::LATEST_STORAGE_VERSION - end + group = Group.find_by(path: group_path) + + unless group + group = Group.new( + name: group_path.titleize, + path: group_path + ) + group.description = FFaker::Lorem.sentence + group.save + + group.add_owner(User.first) + end + + project_path.gsub!(".git", "") + + params = { + import_url: url, + namespace_id: group.id, + name: project_path.titleize, + description: FFaker::Lorem.sentence, + visibility_level: Gitlab::VisibilityLevel.values.sample, + skip_disk_validation: true + } + + if force_latest_storage + params[:storage_version] = Project::LATEST_STORAGE_VERSION + end + + project = nil - project = nil + Sidekiq::Worker.skipping_transaction_check do + project = Projects::CreateService.new(User.first, params).execute - Sidekiq::Worker.skipping_transaction_check do - project = Projects::CreateService.new(User.first, params).execute + # Seed-Fu runs this entire fixture in a transaction, so the `after_commit` + # hook won't run until after the fixture is loaded. That is too late + # since the Sidekiq::Testing block has already exited. Force clearing + # the `after_commit` queue to ensure the job is run now. + project.send(:_run_after_commit_queue) + project.import_state.send(:_run_after_commit_queue) + end - # Seed-Fu runs this entire fixture in a transaction, so the `after_commit` - # hook won't run until after the fixture is loaded. That is too late - # since the Sidekiq::Testing block has already exited. Force clearing - # the `after_commit` queue to ensure the job is run now. - project.send(:_run_after_commit_queue) - project.import_state.send(:_run_after_commit_queue) + if project.valid? && project.valid_repo? + print '.' + else + puts project.errors.full_messages + print 'F' + end end - if project.valid? && project.valid_repo? - print '.' - else - puts project.errors.full_messages - print 'F' + # You can specify how many projects you need during seed execution + size = ENV['SIZE'].present? ? ENV['SIZE'].to_i : 8 + + project_urls.first(size).each_with_index do |url, i| + create_project(url, force_latest_storage: i.even?) + end + + if ENV['LARGE_PROJECTS'].present? + large_project_urls.each(&method(:create_project)) + + if ENV['FORK'].present? + puts "\nGenerating forks" + + project_name = ENV['FORK'] == 'true' ? 'torvalds/linux' : ENV['FORK'] + + project = Project.find_by_full_path(project_name) + + User.offset(1).first(5).each do |user| + new_project = Projects::ForkService.new(project, user).execute + + if new_project.valid? && (new_project.valid_repo? || new_project.import_state.scheduled?) + print '.' + else + new_project.errors.full_messages.each do |error| + puts "#{new_project.full_path}: #{error}" + end + print 'F' + end + end + end end end end diff --git a/db/post_migrate/20181218192239_backfill_project_repositories_for_legacy_storage_projects.rb b/db/post_migrate/20181218192239_backfill_project_repositories_for_legacy_storage_projects.rb deleted file mode 100644 index 42f96750789..00000000000 --- a/db/post_migrate/20181218192239_backfill_project_repositories_for_legacy_storage_projects.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -class BackfillProjectRepositoriesForLegacyStorageProjects < ActiveRecord::Migration[5.0] - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - BATCH_SIZE = 1_000 - DELAY_INTERVAL = 5.minutes - MIGRATION = 'BackfillLegacyProjectRepositories' - - disable_ddl_transaction! - - class Project < ActiveRecord::Base - include EachBatch - - self.table_name = 'projects' - end - - def up - queue_background_migration_jobs_by_range_at_intervals(Project, MIGRATION, DELAY_INTERVAL) - end - - def down - # no-op: since there could have been existing rows before the migration do not remove anything - end -end diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md index bad61151bda..0e2079cb093 100644 --- a/doc/administration/operations/unicorn.md +++ b/doc/administration/operations/unicorn.md @@ -60,7 +60,17 @@ Unicorn master then automatically replaces the worker process. This is a robust way to handle memory leaks: Unicorn is designed to handle workers that 'crash' so no user requests will be dropped. The unicorn-worker-killer gem is designed to only terminate a worker process _in -between requests_, so no user requests are affected. +between requests_, so no user requests are affected. You can set the minimum and +maximum memory threshold (in bytes) for the Unicorn worker killer by +setting the following values `/etc/gitlab/gitlab.rb`: + +```ruby +unicorn['worker_memory_limit_min'] = "400 * 1 << 20" +unicorn['worker_memory_limit_max'] = "650 * 1 << 20" +``` + +Otherwise, you can set the `GITLAB_UNICORN_MEMORY_MIN` and `GITLAB_UNICORN_MEMORY_MIN` +[environment variables](../environment_variables.md). This is what a Unicorn worker memory restart looks like in unicorn_stderr.log. You see that worker 4 (PID 125918) is inspecting itself and decides to exit. diff --git a/doc/api/README.md b/doc/api/README.md index d481d0699e7..3ed1a3799c8 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -49,6 +49,7 @@ The following API resources are available: - [Projects](projects.md) including setting Webhooks - [Project access requests](access_requests.md) - [Project badges](project_badges.md) + - [Project clusters](project_clusters.md) - [Project-level variables](project_level_variables.md) - [Project import/export](project_import_export.md) - [Project members](members.md) diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md new file mode 100644 index 00000000000..c51a3564211 --- /dev/null +++ b/doc/api/project_clusters.md @@ -0,0 +1,346 @@ +# Project clusters API + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23922) +in GitLab 11.7. + +NOTE: **Note:** +User will need at least maintainer access to use these endpoints. + +## List project clusters + +Returns a list of project clusters. + +``` +GET /projects/:id/clusters +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project owned by the authenticated user | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/26/clusters +``` + +Example response: + +```json +[ + { + "id":18, + "name":"cluster-1", + "created_at":"2019-01-02T20:18:12.563Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"project_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://104.197.68.152", + "namespace":"cluster-1-namespace", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + } + }, + { + "id":19, + "name":"cluster-2", + ... + } +] +``` + +## Get a single project cluster + +Gets a single project cluster. + +```bash +GET /projects/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project owned by the authenticated user | +| `cluster_id` | integer | yes | The ID of the cluster | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/26/clusters/18 +``` + +Example response: + +```json +{ + "id":18, + "name":"cluster-1", + "created_at":"2019-01-02T20:18:12.563Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"project_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://104.197.68.152", + "namespace":"cluster-1-namespace", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "project": + { + "id":26, + "description":"", + "name":"project-with-clusters-api", + "name_with_namespace":"Administrator / project-with-clusters-api", + "path":"project-with-clusters-api", + "path_with_namespace":"root/project-with-clusters-api", + "created_at":"2019-01-02T20:13:32.600Z", + "default_branch":null, + "tag_list":[], + "ssh_url_to_repo":"ssh://gitlab.example.com/root/project-with-clusters-api.git", + "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", + "web_url":"https://gitlab.example.com/root/project-with-clusters-api", + "readme_url":null, + "avatar_url":null, + "star_count":0, + "forks_count":0, + "last_activity_at":"2019-01-02T20:13:32.600Z", + "namespace": + { + "id":1, + "name":"root", + "path":"root", + "kind":"user", + "full_path":"root", + "parent_id":null + } + } +} +``` + +## Add existing cluster to project + +Adds an existing Kubernetes cluster to the project. + +```bash +POST /projects/:id/clusters/user +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project owned by the authenticated user | +| `name` | String | yes | The name of the cluster | +| `enabled` | Boolean | no | Determines if cluster is active or not, defaults to true | +| `platform_kubernetes_attributes[api_url]` | String | yes | The URL to access the Kubernetes API | +| `platform_kubernetes_attributes[token]` | String | yes | The token to authenticate against Kubernetes | +| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | +| `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project | +| `platform_kubernetes_attributes[authorization_type]` | String | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/26/clusters/user \ +-H "Accept: application/json" \ +-H "Content-Type:application/json" \ +-X POST --data '{"name":"cluster-5", "platform_kubernetes_attributes":{"api_url":"https://35.111.51.20","token":"12345","namespace":"cluster-5-namespace","ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"}}' +``` + +Example response: + +```json +{ + "id":24, + "name":"cluster-5", + "created_at":"2019-01-03T21:53:40.610Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"project_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://35.111.51.20", + "namespace":"cluster-5-namespace", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "project": + { + "id":26, + "description":"", + "name":"project-with-clusters-api", + "name_with_namespace":"Administrator / project-with-clusters-api", + "path":"project-with-clusters-api", + "path_with_namespace":"root/project-with-clusters-api", + "created_at":"2019-01-02T20:13:32.600Z", + "default_branch":null, + "tag_list":[], + "ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git", + "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", + "web_url":"https://gitlab.example.com/root/project-with-clusters-api", + "readme_url":null, + "avatar_url":null, + "star_count":0, + "forks_count":0, + "last_activity_at":"2019-01-02T20:13:32.600Z", + "namespace": + { + "id":1, + "name":"root", + "path":"root", + "kind":"user", + "full_path":"root", + "parent_id":null + } + } +} +``` + +## Edit project cluster + +Updates an existing project cluster. + +```bash +PUT /projects/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project owned by the authenticated user | +| `name` | String | no | The name of the cluster | +| `platform_kubernetes_attributes[api_url]` | String | no | The URL to access the Kubernetes API | +| `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes | +| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | +| `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project | + +NOTE: **Note:** +`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added +through the ["Add an existing Kubernetes Cluster"](../user/project/clusters/index.md#adding-an-existing-kubernetes-cluster) option or +through the ["Add existing cluster to project"](#add-existing-cluster-to-project) endpoint. + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/26/clusters/24 \ +-H "Content-Type:application/json" \ +-X PUT --data '{"name":"new-cluster-name","api_url":"https://new-api-url.com"}' +``` + +Example response: + +```json +{ + "id":24, + "name":"new-cluster-name", + "created_at":"2019-01-03T21:53:40.610Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"project_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://new-api-url.com", + "namespace":"cluster-5-namespace", + "authorization_type":"rbac", + "ca_cert":null + }, + "project": + { + "id":26, + "description":"", + "name":"project-with-clusters-api", + "name_with_namespace":"Administrator / project-with-clusters-api", + "path":"project-with-clusters-api", + "path_with_namespace":"root/project-with-clusters-api", + "created_at":"2019-01-02T20:13:32.600Z", + "default_branch":null, + "tag_list":[], + "ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git", + "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", + "web_url":"https://gitlab.example.com/root/project-with-clusters-api", + "readme_url":null, + "avatar_url":null, + "star_count":0, + "forks_count":0, + "last_activity_at":"2019-01-02T20:13:32.600Z", + "namespace": + { + "id":1, + "name":"root", + "path":"root", + "kind":"user", + "full_path":"root", + "parent_id":null + } + } +} + +``` + +## Delete project cluster + +Deletes an existing project cluster. + +``` +DELETE /projects/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project owned by the authenticated user | +| `cluster_id` | integer | yes | The ID of the cluster | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/26/clusters/23' +``` diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 315d0c5e7ef..010c579b83e 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -419,7 +419,7 @@ and/or `production`) you can see this information in the merge request itself. > Introduced in GitLab 8.17. In GitLab 11.5 the file links are surfaced to the merge request widget. -You can specify a Route Map to get GitLab to show "View on <environment URL>" +You can specify a Route Map to get GitLab to show **View on ...** buttons to go directly from a file to that file's representation on the [deployed website via Review Apps](review_apps/index.md). diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index 9c9ea651678..c742dc61368 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -135,6 +135,14 @@ Consider that if you don't lock your specific Runner to a specific project, any user with Maintainer role in you project can assign your Runner to another arbitrary project without requiring your authorization, so use it with caution. +CAUTION: **Caution:** +Never add a private Runner that you're using in your private projects to a +project that you share with other people. + +CAUTION: **Caution:** +Never use a Runner from a project which has multiple maintainers in your +private project. + An admin can enable/disable a specific Runner for projects: 1. Navigate to **Admin > Runners** diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index a18c21d921a..721e26f2de9 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -15,7 +15,7 @@ For programmatic help adhering to the guidelines, see [linting](index.md#linting ## Files - [Directory structure](index.md#location-and-naming-documents): place the docs -in the correct location. + in the correct location. - [Documentation files](index.md#documentation-files): name the files accordingly. DANGER: **Attention:** @@ -29,12 +29,12 @@ a test that will fail if it spots a new `README.md` file. ### Markdown The [documentation website](https://docs.gitlab.com) had its markdown engine migrated from [Redcarpet to GitLab Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108) -in October, 2018. +in October 2018. The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown) gem will support all [GFM markup](../../user/markdown.md) in the future. For now, use regular markdown markup, following the rules on this style guide. For a complete -Kramdown reference, check the [GiLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/). +Kramdown reference, check the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/). Use Kramdown markup wisely: do not overuse its specific markup (e.g., `{:.class}`) as it will not render properly in [`/help`](#gitlab-help). @@ -45,38 +45,38 @@ yield a useful result, and ensuring content is helpful and easy to consume. - What to include: - Any and all helpful information, processes, and tips for implementing, -using, and troubleshooting GitLab features. [The documentation is the single source of truth](https://about.gitlab.com/handbook/documentation/#documentation-as-single-source-of-truth-ssot) -for this information. + using, and troubleshooting GitLab features. [The documentation is the single source of truth](https://about.gitlab.com/handbook/documentation/#documentation-as-single-source-of-truth-ssot) + for this information. - 'Risky' or niche problem-solving steps. There is no reason to withhold these or -store them elsewhere; simply include them along with the rest of the docs including all necessary -detail, such as specific warnings and caveats about potential ramifications. + store them elsewhere; simply include them along with the rest of the docs including all necessary + detail, such as specific warnings and caveats about potential ramifications. - Any content types/sources, if relevant to users or admins. You can freely -include presentations, videos, etc.; no matter who it was originally written for, -if it is helpful to any of our audiences, we can include it. If an outside source -that's under copyright, rephrase, or summarize and link out; do not copy and paste. + include presentations, videos, etc.; no matter who it was originally written for, + if it is helpful to any of our audiences, we can include it. If an outside source + that's under copyright, rephrase, or summarize and link out; do not copy and paste. - All applicable subsections as described on the [structure and template](structure.md) page, -with files organized in the [correct directory](index.md#documentation-directory-structure). + with files organized in the [correct directory](index.md#documentation-directory-structure). - To ensure discoverability, link to each doc from its higher-level index page and other related pages. - When referencing other GitLab products and features, link to their respective docs; when referencing third-party products or technologies, link out to their external sites, documentation, and resources. - Do not duplicate information. - Structure content in alphabetical order in tables, lists, etc., unless there is -a logical reason not to (for example, when mirroring the UI or an ordered sequence). + a logical reason not to (for example, when mirroring the UI or an ordered sequence). ## Language - Use inclusive language and avoid jargon, as well as uncommon -words. The docs should be clear and easy to understand. + words. The docs should be clear and easy to understand. - Write in the 3rd person (use "we", "you", "us", "one", instead of "I" or "me"). - Be clear, concise, and stick to the goal of the doc. - Write in US English. - Capitalize "G" and "L" in GitLab. - Use title case when referring to [features](https://about.gitlab.com/features/) or -[products](https://about.gitlab.com/pricing/) (e.g., GitLab Runner, Geo, -Issue Boards, GitLab Core, Git, Prometheus, Kubernetes, etc), and methods or methodologies -(e.g., Continuous Integration, Continuous Deployment, Scrum, Agile, etc). Note that -some features are also objects (e.g. "GitLab's Merge Requests support X." and "Create a new merge request for Z."). + [products](https://about.gitlab.com/pricing/) (e.g., GitLab Runner, Geo, + Issue Boards, GitLab Core, Git, Prometheus, Kubernetes, etc), and methods or methodologies + (e.g., Continuous Integration, Continuous Deployment, Scrum, Agile, etc). Note that + some features are also objects (e.g. "GitLab's Merge Requests support X." and "Create a new merge request for Z."). ## Text @@ -127,9 +127,9 @@ Check specific punctuation rules for [list items](#list-items) below. **Markup:** -- Use dashes (`- `) for unordered lists instead of asterisks (`* `). +- Use dashes (`-`) for unordered lists instead of asterisks (`*`). - Use the number one (`1`) for each item in an ordered list. -When rendered, the list items will appear with sequential numbering. + When rendered, the list items will appear with sequential numbering. **Punctuation:** @@ -226,12 +226,11 @@ For other punctuation rules, please refer to the To indicate the steps of navigation through the UI: - - Use the exact word as shown in the UI, including any capital letters as-is. - Use bold text for navigation items and the char "greater than" (`>`) as separator -(e.g., `Navigate to your project's **Settings > CI/CD**` ). + (e.g., `Navigate to your project's **Settings > CI/CD**` ). - If there are any expandable menus, make sure to mention that the user -needs to expand the tab to find the settings you're referring to (e.g., `Navigate to your project's **Settings > CI/CD** and expand **General pipelines**`). + needs to expand the tab to find the settings you're referring to (e.g., `Navigate to your project's **Settings > CI/CD** and expand **General pipelines**`). ## Images @@ -246,7 +245,7 @@ needs to expand the tab to find the settings you're referring to (e.g., `Navigat - Compress all images with <https://tinypng.com/> or similar tool. - Compress gifs with <https://ezgif.com/optimize> or similar tool. - Images should be used (only when necessary) to _illustrate_ the description -of a process, not to _replace_ it. + of a process, not to _replace_ it. - Max image size: 100KB (gifs included). - The GitLab docs do not support videos yet. @@ -266,12 +265,12 @@ Inside the document: ## Code blocks - Always wrap code added to a sentence in inline code blocks (``` ` ```). -E.g., `.gitlab-ci.yml`, `git add .`, `CODEOWNERS`, `only: master`. -File names, commands, entries, and anything that refers to code should be added to code blocks. -To make things easier for the user, always add a full code block for things that can be -useful to copy and paste, as they can easily do it with the button on code blocks. + E.g., `.gitlab-ci.yml`, `git add .`, `CODEOWNERS`, `only: master`. + File names, commands, entries, and anything that refers to code should be added to code blocks. + To make things easier for the user, always add a full code block for things that can be + useful to copy and paste, as they can easily do it with the button on code blocks. - For regular code blocks, always use a highlighting class corresponding to the -language for better readability. Examples: + language for better readability. Examples: ```md ```ruby @@ -592,12 +591,12 @@ on this document. Further explanation is given below. The following can be used as a template to get started: -```md +````md ## Descriptive title One or two sentence description of what endpoint does. -``` +```text METHOD /endpoint ``` @@ -609,7 +608,7 @@ METHOD /endpoint Example request: ```sh -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/endpoint?parameters' +curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/endpoint?parameters' ``` Example response: @@ -620,8 +619,7 @@ Example response: } ] ``` - -``` +```` ### Fake tokens diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 2ad748d4802..ae9bf863419 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -38,6 +38,14 @@ Note that since you can't see the questions from stdout, you might just want to `echo 'yes'` to keep it running. It would still print the errors on stderr so no worries about missing errors. +### Extra Project seed options + +There are a few environment flags you can pass to change how projects are seeded + +- `SIZE`: defaults to `8`, max: `32`. Amount of projects to create. +- `LARGE_PROJECTS`: defaults to false. If set will clone 6 large projects to help with testing. +- `FORK`: defaults to false. If set to `true` will fork `torvalds/linux` five times. Can also be set to an existing project full_path and it will fork that instead. + ### Notes for MySQL Since the seeds would contain various UTF-8 characters, such as emojis or so, diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 57bc71d2903..bb28ca35a26 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -311,6 +311,11 @@ For installations from source: remote_directory: 'my.s3.bucket' # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional # encryption: 'AES256' + # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional + # This should be set to the base64-encoded encryption key for Amazon S3 to use to encrypt or decrypt your data. + # 'encryption' must also be set in order for this to have any effect. + # To avoid storing the key on disk, the key can also be specified via the `GITLAB_BACKUP_ENCRYPTION_KEY` environment variable. + # encryption_key: '<base64 key>' # Specifies Amazon S3 storage class to use for backups, this is optional # storage_class: 'STANDARD' ``` diff --git a/doc/user/project/img/issue_boards_multiple.png b/doc/user/project/img/issue_boards_multiple.png Binary files differindex 7bb088aad0b..242460925f7 100644 --- a/doc/user/project/img/issue_boards_multiple.png +++ b/doc/user/project/img/issue_boards_multiple.png diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 9e2434c02ec..7962eeada5c 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -175,6 +175,7 @@ products. Clicking on the current board name in the upper left corner will reveal a menu from where you can create another Issue Board and rename or delete the existing one. +Using the search box at the top of the menu, you can filter the listed boards. When you're revisiting an issue board in a project or group with multiple boards, GitLab will automatically load the last board you visited. diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md new file mode 100644 index 00000000000..2b5abc7233f --- /dev/null +++ b/doc/user/project/operations/error_tracking.md @@ -0,0 +1,30 @@ +# Error Tracking + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/169) in GitLab 11.7. + +Error tracking allows developers to easily discover and view the errors that their application may be generating. By surfacing error information where the code is being developed, efficiency and awareness can be increased. + +## Sentry error tracking + +[Sentry](https://sentry.io/) is an open source error tracking system. GitLab allows administrators to connect Sentry to GitLab, to allow users to view a list of Sentry errors in GitLab itself. + +### Deploying Sentry + +You may sign up to the cloud hosted https://sentry.io or deploy your own [on-premise instance](https://docs.sentry.io/server/installation/). + +### Enabling Sentry + +GitLab provides an easy way to connect Sentry to your project: + +1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance. +1. [Find or generate](https://docs.sentry.io/api/auth/) a Sentry auth token for your Sentry project. +1. Navigate to your project’s **Settings > Operations** and provide the Sentry API URL and auth token. +1. Ensure that the 'Active' checkbox is set. +1. Click **Save changes** for the changes to take effect. +1. You can now visit **Operations > Error Tracking** in your project's sidebar to [view a list](#error-tracking-list) of Sentry errors. + +## Error Tracking List + +The Error Tracking list may be found at **Operations > Error Tracking** in your project's sidebar. + +![Error Tracking list](img/error_tracking_list.png) diff --git a/doc/user/project/operations/img/error_tracking_list.png b/doc/user/project/operations/img/error_tracking_list.png Binary files differnew file mode 100644 index 00000000000..aa0f9867fdb --- /dev/null +++ b/doc/user/project/operations/img/error_tracking_list.png diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md index c6239c8e41c..c7e20f01a75 100644 --- a/doc/user/project/repository/gpg_signed_commits/index.md +++ b/doc/user/project/repository/gpg_signed_commits/index.md @@ -4,7 +4,7 @@ NOTE: **Note:** The term GPG is used for all OpenPGP/PGP/GPG related material and implementations. -> - [Introduced][ce-9546] in GitLab 9.5. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9546) in GitLab 9.5. > - Subkeys support was added in GitLab 10.1. GitLab can show whether a commit is verified or not when signed with a GPG key. @@ -36,30 +36,22 @@ to be met: ## Generating a GPG key -> **Notes:** -> - If your Operating System has `gpg2` installed, replace `gpg` with `gpg2` in -> the following commands. -> - If Git is using `gpg` and you get errors like `secret key not available` or -> `gpg: signing failed: secret key not available`, run the following command to -> change to `gpg2`: -> -> ``` -> git config --global gpg.program gpg2 -> ``` - If you don't already have a GPG key, the following steps will help you get started: -1. [Install GPG](https://www.gnupg.org/download/index.html) for your operating system -1. Generate the private/public key pair with the following command: +1. [Install GPG](https://www.gnupg.org/download/index.html) for your operating system. + If your Operating System has `gpg2` installed, replace `gpg` with `gpg2` in + the following commands. +1. Generate the private/public key pair with the following command, which will + spawn a series of questions: ```sh gpg --full-gen-key ``` - - _NOTE: In some cases like Gpg4win on Windows and other Mac OS versions the command here may be ` gpg --gen-key`_ - This will spawn a series of questions. + NOTE: **Note:** + In some cases like Gpg4win on Windows and other macOS versions, the command + here may be `gpg --gen-key`. 1. The first question is which algorithm can be used. Select the kind you want or press <kbd>Enter</kbd> to choose the default (RSA and RSA): @@ -109,10 +101,10 @@ started: GnuPG needs to construct a user ID to identify your key. Real name: Mr. Robot - Email address: mr@robot.sh + Email address: <your_email> Comment: You selected this USER-ID: - "Mr. Robot <mr@robot.sh>" + "Mr. Robot <your_email>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O ``` @@ -121,10 +113,10 @@ started: 1. Use the following command to list the private GPG key you just created: ``` - gpg --list-secret-keys --keyid-format LONG mr@robot.sh + gpg --list-secret-keys --keyid-format LONG <your_email> ``` - Replace `mr@robot.sh` with the email address you entered above. + Replace `<your_email>` with the email address you entered above. 1. Copy the GPG key ID that starts with `sec`. In the following example, that's `30F2B65B9246B6CA`: @@ -132,7 +124,7 @@ started: ``` sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC] D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA - uid [ultimate] Mr. Robot <mr@robot.sh> + uid [ultimate] Mr. Robot <your_email> ssb rsa4096/B7ABC0813E4028C0 2017-08-18 [E] ``` @@ -146,7 +138,7 @@ started: ## Adding a GPG key to your account ->**Note:** +NOTE: **Note:** Once you add a key, you cannot edit it, only remove it. In case the paste didn't work, you'll have to remove the offending key and re-add it. @@ -174,11 +166,11 @@ key to use. 1. Use the following command to list the private GPG key you just created: - ``` - gpg --list-secret-keys --keyid-format LONG mr@robot.sh + ```sh + gpg --list-secret-keys --keyid-format LONG <your_email> ``` - Replace `mr@robot.sh` with the email address you entered above. + Replace `<your_email>` with the email address you entered above. 1. Copy the GPG key ID that starts with `sec`. In the following example, that's `30F2B65B9246B6CA`: @@ -186,18 +178,27 @@ key to use. ``` sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC] D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA - uid [ultimate] Mr. Robot <mr@robot.sh> + uid [ultimate] Mr. Robot <your_email> ssb rsa4096/B7ABC0813E4028C0 2017-08-18 [E] ``` 1. Tell Git to use that key to sign the commits: - ``` + ```sh git config --global user.signingkey 30F2B65B9246B6CA ``` Replace `30F2B65B9246B6CA` with your GPG key ID. + +1. (Optional) If Git is using `gpg` and you get errors like `secret key not available` + or `gpg: signing failed: secret key not available`, run the following command to + change to `gpg2`: + + ```sh + git config --global gpg.program gpg2 + ``` + ## Signing commits After you have [created your GPG key](#generating-a-gpg-key) and [added it to @@ -261,4 +262,7 @@ To remove a GPG key from your account: 1. Navigate to the **GPG keys** tab. 1. Click on the trash icon besides the GPG key you want to delete. -[ce-9546]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9546 +## Rejecting commits that are not signed **[PREMIUM]** + +You can configure your project to reject commits that aren't GPG-signed +via [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html). diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index d6754372816..221fa4b3d9f 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -122,3 +122,9 @@ GitLab administrators can use the admin interface to move any project to any namespace if needed. [permissions]: ../../permissions.md##project-members-permissions + +## Operations settings + +### Error Tracking + +Configure Error Tracking to discover and view [Sentry errors within GitLab](../operations/error_tracking.md). diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 6c1a730935a..74927b4db81 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -496,7 +496,11 @@ module API def send_git_blob(repository, blob) env['api.format'] = :txt content_type 'text/plain' - header['Content-Disposition'] = content_disposition('attachment', blob.name) + header['Content-Disposition'] = content_disposition('inline', blob.name) + + # Let Workhorse examine the content and determine the better content disposition + header[Gitlab::Workhorse::DETECT_HEADER] = "true" + header(*Gitlab::Workhorse.send_git_blob(repository, blob)) end @@ -512,7 +516,7 @@ module API # `request`. We workaround this by defining methods that returns the right # values. def define_params_for_grape_middleware - self.define_singleton_method(:request) { Rack::Request.new(env) } + self.define_singleton_method(:request) { ActionDispatch::Request.new(env) } self.define_singleton_method(:params) { request.params.symbolize_keys } end diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb index a4068a200b3..933bd067e26 100644 --- a/lib/api/job_artifacts.rb +++ b/lib/api/job_artifacts.rb @@ -23,17 +23,14 @@ module API requires :job, type: String, desc: 'The name for the job' end route_setting :authentication, job_token_allowed: true - # rubocop: disable CodeReuse/ActiveRecord get ':id/jobs/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do authorize_download_artifacts! - builds = user_project.latest_successful_builds_for(params[:ref_name]) - latest_build = builds.find_by!(name: params[:job]) + latest_build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) present_carrierwave_file!(latest_build.artifacts_file) end - # rubocop: enable CodeReuse/ActiveRecord desc 'Download a specific file from artifacts archive from a ref' do detail 'This feature was introduced in GitLab 11.5' @@ -48,7 +45,7 @@ module API requirements: { ref_name: /.+/ } do authorize_download_artifacts! - build = user_project.latest_successful_build_for(params[:job], params[:ref_name]) + build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) path = Gitlab::Ci::Build::Artifacts::Path .new(params[:artifact_path]) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 0add2b3f875..06b0338b1ed 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -50,6 +50,7 @@ module Backup if directory.files.create(key: remote_target, body: File.open(tar_file), public: false, multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, encryption: Gitlab.config.backup.upload.encryption, + encryption_key: Gitlab.config.backup.upload.encryption_key, storage_class: Gitlab.config.backup.upload.storage_class) progress.puts "done".color(:green) else diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb index b7743bd2090..191f5d09645 100644 --- a/lib/gitlab/ci/config/normalizer.rb +++ b/lib/gitlab/ci/config/normalizer.rb @@ -46,7 +46,8 @@ module Gitlab parallelized_job_names = @parallelized_jobs.keys.map(&:to_s) parallelized_config.each_with_object({}) do |(job_name, config), hash| if config[:dependencies] && (intersection = config[:dependencies] & parallelized_job_names).any? - deps = intersection.map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }.flatten + parallelized_deps = intersection.map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }.flatten + deps = config[:dependencies] - intersection + parallelized_deps hash[job_name] = config.merge(dependencies: deps) else hash[job_name] = config diff --git a/lib/gitlab/error_tracking/error.rb b/lib/gitlab/error_tracking/error.rb new file mode 100644 index 00000000000..4af5192aa6a --- /dev/null +++ b/lib/gitlab/error_tracking/error.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Gitlab + module ErrorTracking + class Error + include ActiveModel::Model + + attr_accessor :id, :title, :type, :user_count, :count, + :first_seen, :last_seen, :message, :culprit, + :external_url, :project_id, :project_name, :project_slug, + :short_id, :status, :frequency + end + end +end diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb index 0341f930b9c..a11d6b66409 100644 --- a/lib/gitlab/etag_caching/middleware.rb +++ b/lib/gitlab/etag_caching/middleware.rb @@ -8,7 +8,7 @@ module Gitlab end def call(env) - request = Rack::Request.new(env) + request = ActionDispatch::Request.new(env) route = Gitlab::EtagCaching::Router.match(request.path_info) return @app.call(env) unless route diff --git a/lib/gitlab/middleware/basic_health_check.rb b/lib/gitlab/middleware/basic_health_check.rb index f2a03217098..acf8c301b8f 100644 --- a/lib/gitlab/middleware/basic_health_check.rb +++ b/lib/gitlab/middleware/basic_health_check.rb @@ -24,7 +24,7 @@ module Gitlab def call(env) return @app.call(env) unless env['PATH_INFO'] == HEALTH_PATH - request = Rack::Request.new(env) + request = ActionDispatch::Request.new(env) return OK_RESPONSE if client_ip_whitelisted?(request) diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb index 89941a9efa0..f142f9da43d 100644 --- a/lib/gitlab/middleware/read_only/controller.rb +++ b/lib/gitlab/middleware/read_only/controller.rb @@ -60,7 +60,7 @@ module Gitlab end def request - @env['rack.request'] ||= Rack::Request.new(@env) + @env['actionpack.request'] ||= ActionDispatch::Request.new(@env) end def last_visited_url diff --git a/lib/gitlab/repository_cache.rb b/lib/gitlab/repository_cache.rb index 6b0808f5304..56007574b1b 100644 --- a/lib/gitlab/repository_cache.rb +++ b/lib/gitlab/repository_cache.rb @@ -7,13 +7,13 @@ module Gitlab def initialize(repository, extra_namespace: nil, backend: Rails.cache) @repository = repository - @namespace = "project:#{repository.project.id}" + @namespace = "#{repository.full_path}:#{repository.project.id}" @namespace = "#{@namespace}:#{extra_namespace}" if extra_namespace @backend = backend end def cache_key(type) - "#{namespace}:#{type}" + "#{type}:#{namespace}" end def expire(key) diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb index f8f8ec789ce..d9811e036d3 100644 --- a/lib/gitlab/request_context.rb +++ b/lib/gitlab/request_context.rb @@ -13,7 +13,7 @@ module Gitlab end def call(env) - req = Rack::Request.new(env) + req = ActionDispatch::Request.new(env) Gitlab::SafeRequestStore[:client_ip] = req.ip diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index 84a51773276..8e2f16271eb 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -26,6 +26,19 @@ module Gitlab puts "\nOK".color(:green) end + def self.without_gitaly_timeout + # Remove Gitaly timeout + old_timeout = Gitlab::CurrentSettings.current_application_settings.gitaly_timeout_default + Gitlab::CurrentSettings.current_application_settings.update_columns(gitaly_timeout_default: 0) + # Otherwise we still see the default value when running seed_fu + ApplicationSetting.expire + + yield + ensure + Gitlab::CurrentSettings.current_application_settings.update_columns(gitaly_timeout_default: old_timeout) + ApplicationSetting.expire + end + def self.mute_notifications NotificationService.prepend(MuteNotifications) end diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb new file mode 100644 index 00000000000..343f2c49a7f --- /dev/null +++ b/lib/sentry/client.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +module Sentry + class Client + Error = Class.new(StandardError) + + attr_accessor :url, :token + + def initialize(api_url, token) + @url = api_url + @token = token + end + + def list_issues(issue_status:, limit:) + issues = get_issues(issue_status: issue_status, limit: limit) + map_to_errors(issues) + end + + private + + def request_params + { + headers: { + 'Authorization' => "Bearer #{@token}" + }, + follow_redirects: false + } + end + + def get_issues(issue_status:, limit:) + resp = Gitlab::HTTP.get( + issues_api_url, + **request_params.merge(query: { + query: "is:#{issue_status}", + limit: limit + }) + ) + + handle_response(resp) + end + + def handle_response(response) + unless response.code == 200 + raise Client::Error, "Sentry response error: #{response.code}" + end + + response.as_json + end + + def issues_api_url + issues_url = URI(@url + '/issues/') + issues_url.path.squeeze!('/') + + issues_url + end + + def map_to_errors(issues) + issues.map do |issue| + map_to_error(issue) + end + end + + def issue_url(id) + issues_url = @url + "/issues/#{id}" + issues_url = ErrorTracking::ProjectErrorTrackingSetting.extract_sentry_external_url(issues_url) + + uri = URI(issues_url) + uri.path.squeeze!('/') + + uri.to_s + end + + def map_to_error(issue) + id = issue.fetch('id') + project = issue.fetch('project') + + count = issue.fetch('count', nil) + + frequency = issue.dig('stats', '24h') + message = issue.dig('metadata', 'value') + + external_url = issue_url(id) + + Gitlab::ErrorTracking::Error.new( + id: id, + first_seen: issue.fetch('firstSeen', nil), + last_seen: issue.fetch('lastSeen', nil), + title: issue.fetch('title', nil), + type: issue.fetch('type', nil), + user_count: issue.fetch('userCount', nil), + count: count, + message: message, + culprit: issue.fetch('culprit', nil), + external_url: external_url, + short_id: issue.fetch('shortId', nil), + status: issue.fetch('status', nil), + frequency: frequency, + project_id: project.fetch('id'), + project_name: project.fetch('name', nil), + project_slug: project.fetch('slug', nil) + ) + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 81de85063e4..d3f751c7796 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -450,6 +450,9 @@ msgstr "" msgid "AdminProjects|Delete project" msgstr "" +msgid "AdminSettings|Auto DevOps domain" +msgstr "" + msgid "AdminSettings|Environment variables are protected by default" msgstr "" @@ -873,10 +876,7 @@ msgstr "" msgid "Available" msgstr "" -msgid "Available group Runners : %{runners}" -msgstr "" - -msgid "Available group Runners : %{runners}." +msgid "Available group Runners: %{runners}" msgstr "" msgid "Available shared Runners:" @@ -2956,6 +2956,9 @@ msgstr "" msgid "Error while loading the merge request. Please try again." msgstr "" +msgid "Errors" +msgstr "" + msgid "Estimated" msgstr "" @@ -6779,12 +6782,24 @@ msgstr "" msgid "There are no archived projects yet" msgstr "" +msgid "There are no closed issues" +msgstr "" + +msgid "There are no closed merge requests" +msgstr "" + msgid "There are no issues to show" msgstr "" msgid "There are no labels yet" msgstr "" +msgid "There are no open issues" +msgstr "" + +msgid "There are no open merge requests" +msgstr "" + msgid "There are no projects shared with this group yet" msgstr "" @@ -7209,13 +7224,19 @@ msgstr "" msgid "To import an SVN repository, check out %{svn_link}." msgstr "" +msgid "To keep this project going, create a new issue" +msgstr "" + +msgid "To keep this project going, create a new merge request" +msgstr "" + msgid "To link Sentry to GitLab, enter your Sentry URL and Auth Token." msgstr "" msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here." msgstr "" -msgid "To preserve performance only <strong>%{display_size} of ${real_size}</strong> files are displayed." +msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed." msgstr "" msgid "To start serving your jobs you can add Runners to your group" @@ -7491,6 +7512,9 @@ msgstr "" msgid "UserProfile|Subscribe" msgstr "" +msgid "UserProfile|This user doesn't have any personal projects" +msgstr "" + msgid "UserProfile|This user has a private profile" msgstr "" @@ -8062,9 +8086,6 @@ msgstr "" msgid "importing" msgstr "" -msgid "in" -msgstr "" - msgid "issue boards" msgstr "" diff --git a/package.json b/package.json index 5c4abacae99..8c6fdcbad06 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,13 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { - "@babel/core": "^7.1.2", - "@babel/plugin-proposal-class-properties": "^7.1.0", - "@babel/plugin-proposal-json-strings": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-import-meta": "^7.0.0", - "@babel/preset-env": "^7.1.0", + "@babel/core": "^7.2.2", + "@babel/plugin-proposal-class-properties": "^7.2.3", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-private-methods": "^7.2.3", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/plugin-syntax-import-meta": "^7.2.0", + "@babel/preset-env": "^7.2.3", "@gitlab/csslab": "^1.8.0", "@gitlab/svgs": "^1.47.0", "@gitlab/ui": "^1.20.0", @@ -32,10 +33,10 @@ "apollo-client": "^2.4.5", "autosize": "^4.0.0", "axios": "^0.17.1", - "babel-loader": "^8.0.4", + "babel-loader": "^8.0.5", "bootstrap": "4.1.3", "brace-expansion": "^1.1.8", - "cache-loader": "^1.2.2", + "cache-loader": "^2.0.1", "chart.js": "1.0.2", "classlist-polyfill": "^1.2.0", "clipboard": "^1.7.1", @@ -61,7 +62,7 @@ "echarts": "^4.2.0-rc.2", "emoji-unicode-version": "^0.2.1", "exports-loader": "^0.7.0", - "file-loader": "^2.0.0", + "file-loader": "^3.0.1", "formdata-polyfill": "^3.0.11", "fuzzaldrin-plus": "^0.5.0", "glob": "^7.1.2", @@ -85,7 +86,7 @@ "prismjs": "^1.6.0", "raphael": "^2.2.7", "raven-js": "^3.22.1", - "raw-loader": "^0.5.1", + "raw-loader": "^1.0.0", "sanitize-html": "^1.16.1", "select2": "3.5.2-browserify", "sha1": "^1.1.1", @@ -93,26 +94,26 @@ "sortablejs": "^1.7.0", "sql.js": "^0.4.0", "stickyfilljs": "^2.0.5", - "style-loader": "^0.23.0", + "style-loader": "^0.23.1", "svg4everybody": "2.1.9", "three": "^0.84.0", "three-orbit-controls": "^82.1.0", "three-stl-loader": "^1.0.4", "timeago.js": "^3.0.2", "underscore": "^1.9.0", - "url-loader": "^1.1.1", + "url-loader": "^1.1.2", "visibilityjs": "^1.2.4", - "vue": "^2.5.17", + "vue": "^2.5.21", "vue-apollo": "^3.0.0-beta.25", "vue-loader": "^15.4.2", - "vue-resource": "^1.5.0", - "vue-router": "^3.0.1", - "vue-template-compiler": "^2.5.17", + "vue-resource": "^1.5.1", + "vue-router": "^3.0.2", + "vue-template-compiler": "^2.5.21", "vue-virtual-scroll-list": "^1.2.5", "vuex": "^3.0.1", - "webpack": "^4.19.1", - "webpack-bundle-analyzer": "^3.0.2", - "webpack-cli": "^3.1.0", + "webpack": "^4.28.1", + "webpack-bundle-analyzer": "^3.0.3", + "webpack-cli": "^3.2.1", "webpack-stats-plugin": "^0.2.1", "worker-loader": "^2.0.0", "xterm": "^3.5.0" @@ -157,8 +158,8 @@ "karma-webpack": "^4.0.0-beta.0", "nodemon": "^1.18.4", "prettier": "1.15.2", - "vue-jest": "^3.0.1", - "webpack-dev-server": "^3.1.10", + "vue-jest": "^3.0.2", + "webpack-dev-server": "^3.1.14", "yarn-deduplicate": "^1.0.5" }, "engines": { diff --git a/qa/README.md b/qa/README.md index 08ba59e117d..5e32496ea9f 100644 --- a/qa/README.md +++ b/qa/README.md @@ -86,7 +86,7 @@ The environment variable `QA_COOKIES` can be set to send additional cookies on every request. This is necessary on gitlab.com to direct traffic to the canary fleet. To do this set `QA_COOKIES="gitlab_canary=true"`. -To set multiple cookies, separate them with the `;` character, for example: `QA_COOKIES="cookie1=value;cookie2=value2"` +To set multiple cookies, separate them with the `;` character, for example: `QA_COOKIES="cookie1=value;cookie2=value2"` ### Building a Docker image to test @@ -100,3 +100,24 @@ docker build -t gitlab/gitlab-ce-qa:nightly . ``` [GDK]: https://gitlab.com/gitlab-org/gitlab-development-kit/ + +### Quarantined tests + +Tests can be put in quarantine by assigning `:quarantine` metadata. This means +they will be skipped unless run with `--tag quarantine`. This can be used for +tests that are expected to fail while a fix is in progress (similar to how +[`skip` or `pending`](https://relishapp.com/rspec/rspec-core/v/3-8/docs/pending-and-skipped-examples) + can be used). + +``` +bin/qa Test::Instance::All http://localhost --tag quarantine +``` + +If `quarantine` is used with other tags, tests will only be run if they have at +least one of the tags other than `quarantine`. This is different from how RSpec +tags usually work, where all tags are inclusive. + +For example, suppose one test has `:smoke` and `:quarantine` metadata, and +another test has `:ldap` and `:quarantine` metadata. If the tests are run with +`--tag smoke --tag quarantine`, only the first test will run. The test with +`:ldap` will not run even though it also has `:quarantine`. diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb index 67d7f114786..f5add6bc9b5 100644 --- a/qa/qa/page/component/note.rb +++ b/qa/qa/page/component/note.rb @@ -32,9 +32,13 @@ module QA click_element :comment_button end - def reply_to_discussion(reply_text) + def type_reply_to_discussion(reply_text) all_elements(:discussion_reply).last.click fill_element :reply_input, reply_text + end + + def reply_to_discussion(reply_text) + type_reply_to_discussion(reply_text) click_element :reply_comment_button end diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb index dcea144ab74..f325162d1c0 100644 --- a/qa/qa/resource/base.rb +++ b/qa/qa/resource/base.rb @@ -126,10 +126,6 @@ module QA mod end - def self.attributes_names - dynamic_attributes.instance_methods(false).sort.grep_v(/=$/) - end - class DSL def initialize(base) @base = base diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb index c26f0c84a1f..b9580d81171 100644 --- a/qa/qa/resource/user.rb +++ b/qa/qa/resource/user.rb @@ -6,9 +6,12 @@ module QA module Resource class User < Base attr_reader :unique_id - attr_writer :username, :password, :name, :email + attr_writer :username, :password attr_accessor :provider, :extern_uid + attribute :name + attribute :email + def initialize @unique_id = SecureRandom.hex(8) end @@ -22,11 +25,11 @@ module QA end def name - @name ||= username + @name ||= api_resource&.dig(:name) || username end def email - @email ||= "#{username}@example.com" + @email ||= api_resource&.dig(:email) || "#{username}@example.com" end def credentials_given? diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index b706d6565d2..0bcf5e693f0 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -40,34 +40,38 @@ module QA return if Capybara.drivers.include?(:chrome) - Capybara.register_driver :chrome do |app| - capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( - # This enables access to logs with `page.driver.manage.get_log(:browser)` - loggingPrefs: { - browser: "ALL", - client: "ALL", - driver: "ALL", - server: "ALL" - } - ) + Capybara.register_driver QA::Runtime::Env.browser do |app| + capabilities = Selenium::WebDriver::Remote::Capabilities.send(QA::Runtime::Env.browser, + # This enables access to logs with `page.driver.manage.get_log(:browser)` + loggingPrefs: { + browser: "ALL", + client: "ALL", + driver: "ALL", + server: "ALL" + }) if QA::Runtime::Env.accept_insecure_certs? capabilities['acceptInsecureCerts'] = true end - options = Selenium::WebDriver::Chrome::Options.new - options.add_argument("window-size=1240,1680") + # QA::Runtime::Env.browser.capitalize will work for every driver type except PhantomJS. + # We will have no use to use PhantomJS so this shouldn't be a problem. + options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize)::Options.new + + if QA::Runtime::Env.browser == :chrome + options.add_argument("window-size=1240,1680") - # Chrome won't work properly in a Docker container in sandbox mode - options.add_argument("no-sandbox") + # Chrome won't work properly in a Docker container in sandbox mode + options.add_argument("no-sandbox") - # Run headless by default unless CHROME_HEADLESS is false - if QA::Runtime::Env.chrome_headless? - options.add_argument("headless") + # Run headless by default unless CHROME_HEADLESS is false + if QA::Runtime::Env.chrome_headless? + options.add_argument("headless") - # Chrome documentation says this flag is needed for now - # https://developers.google.com/web/updates/2017/04/headless-chrome#cli - options.add_argument("disable-gpu") + # Chrome documentation says this flag is needed for now + # https://developers.google.com/web/updates/2017/04/headless-chrome#cli + options.add_argument("disable-gpu") + end end # Use the same profile on QA runs if CHROME_REUSE_PROFILE is true. @@ -80,12 +84,18 @@ module QA # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252 options.add_argument("disable-dev-shm-usage") if QA::Runtime::Env.running_in_ci? - Capybara::Selenium::Driver.new( - app, - browser: :chrome, + selenium_options = { + browser: QA::Runtime::Env.browser, clear_local_storage: true, desired_capabilities: capabilities, options: options + } + + selenium_options[:url] = QA::Runtime::Env.remote_grid if QA::Runtime::Env.remote_grid + + Capybara::Selenium::Driver.new( + app, + selenium_options ) end @@ -93,7 +103,7 @@ module QA Capybara::Screenshot.prune_strategy = :keep_last_run # From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326 - Capybara::Screenshot.register_driver(:chrome) do |driver, path| + Capybara::Screenshot.register_driver(QA::Runtime::Env.browser) do |driver, path| driver.browser.save_screenshot(path) end @@ -102,8 +112,8 @@ module QA end Capybara.configure do |config| - config.default_driver = :chrome - config.javascript_driver = :chrome + config.default_driver = QA::Runtime::Env.browser + config.javascript_driver = QA::Runtime::Env.browser config.default_max_wait_time = 10 # https://github.com/mattheworiordan/capybara-screenshot/issues/164 config.save_path = ::File.expand_path('../../tmp', __dir__) diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index dae5aa3f794..79b40223d84 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -56,6 +56,34 @@ module QA @personal_access_token ||= ENV['PERSONAL_ACCESS_TOKEN'] end + def remote_grid + # if username specified, password/auth token is required + # can be + # - "http://user:pass@somehost.com/wd/hub" + # - "https://user:pass@somehost.com:443/wd/hub" + # - "http://localhost:4444/wd/hub" + + return unless ENV['QA_REMOTE_GRID'] + + "#{remote_grid_protocol}://#{remote_grid_credentials}#{ENV['QA_REMOTE_GRID']}/wd/hub" + end + + def remote_grid_username + ENV['QA_REMOTE_GRID_USERNAME'] + end + + def remote_grid_access_key + ENV['QA_REMOTE_GRID_ACCESS_KEY'] + end + + def remote_grid_protocol + ENV['QA_REMOTE_GRID_PROTOCOL'] || 'http' + end + + def browser + ENV['QA_BROWSER'].nil? ? :chrome : ENV['QA_BROWSER'].to_sym + end + def user_username ENV['GITLAB_USERNAME'] end @@ -158,6 +186,16 @@ module QA private + def remote_grid_credentials + if remote_grid_username + raise ArgumentError, %Q(Please provide an access key for user "#{remote_grid_username}") unless remote_grid_access_key + + return "#{remote_grid_username}:#{remote_grid_access_key}@" + end + + '' + end + def enabled?(value, default: true) return default if value.nil? diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb index 203338ddf77..3a5d89e6b83 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb @@ -39,11 +39,15 @@ module QA end it 'user views raw email patch' do + user = Resource::User.fabricate_via_api! do |user| + user.username = Runtime::User.username + end + view_commit Page::Project::Commit::Show.perform(&:select_email_patches) - expect(page).to have_content('From: Administrator <admin@example.com>') + expect(page).to have_content("From: #{user.name} <#{user.email}>") expect(page).to have_content('Subject: [PATCH] Add second file') expect(page).to have_content('diff --git a/second b/second') end diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb index 210271705d9..a7d0998d42c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb @@ -17,7 +17,8 @@ module QA login end - it 'user creates, edits, clones, and pushes to the wiki' do + # Failure reported: https://gitlab.com/gitlab-org/quality/nightly/issues/24 + it 'user creates, edits, clones, and pushes to the wiki', :quarantine do wiki = Resource::Wiki.fabricate! do |resource| resource.title = 'Home' resource.content = '# My First Wiki Content' diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb index 5ee8df03d50..5147b17d7ab 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -98,6 +98,17 @@ module QA resource.value = 'You can see this application secret' end + # Our current Auto DevOps implementation won't update the production + # app if we only update a CI variable with no code change. + # + # Workaround: push new code and use the resultant pipeline. + Resource::Repository::ProjectPush.fabricate! do |push| + push.project = @project + push.commit_message = 'Force a Deployment change by pushing new code' + push.file_name = 'new_file.txt' + push.file_content = 'new file contents' + end + @project.visit! Page::Project::Menu.act { click_ci_cd_pipelines } Page::Project::Pipeline::Index.act { go_to_latest_pipeline } diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb index dc9e16792d3..b8c406ae72a 100644 --- a/qa/spec/resource/base_spec.rb +++ b/qa/spec/resource/base_spec.rb @@ -138,10 +138,6 @@ describe QA::Resource::Base do describe '.attribute' do include_context 'simple resource' - it 'appends new attribute' do - expect(subject.attributes_names).to eq([:no_block, :test, :web_url]) - end - context 'when the attribute is populated via a block' do it 'returns value from the block' do result = subject.fabricate!(resource: resource) diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index ded51d5bb7c..bc0ec08d66d 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -207,4 +207,63 @@ describe QA::Runtime::Env do expect { described_class.can_test? :foo }.to raise_error(ArgumentError, 'Unknown feature "foo"') end end + + describe 'remote grid credentials' do + it 'is blank if username is empty' do + stub_env('QA_REMOTE_GRID_USERNAME', nil) + + expect(described_class.send(:remote_grid_credentials)).to eq('') + end + + it 'throws ArgumentError if GRID_ACCESS_KEY is not specified with USERNAME' do + stub_env('QA_REMOTE_GRID_USERNAME', 'foo') + + expect { described_class.send(:remote_grid_credentials) }.to raise_error(ArgumentError, 'Please provide an access key for user "foo"') + end + + it 'returns a user:key@ combination when all args are satiated' do + stub_env('QA_REMOTE_GRID_USERNAME', 'foo') + stub_env('QA_REMOTE_GRID_ACCESS_KEY', 'bar') + + expect(described_class.send(:remote_grid_credentials)).to eq('foo:bar@') + end + end + + describe '.remote_grid_protocol' do + it 'defaults protocol to http' do + stub_env('QA_REMOTE_GRID_PROTOCOL', nil) + expect(described_class.remote_grid_protocol).to eq('http') + end + end + + describe '.remote_grid' do + it 'is falsey if QA_REMOTE_GRID is not set' do + expect(described_class.remote_grid).to be_falsey + end + + it 'accepts https protocol' do + stub_env('QA_REMOTE_GRID', 'localhost:4444') + stub_env('QA_REMOTE_GRID_PROTOCOL', 'https') + + expect(described_class.remote_grid).to eq('https://localhost:4444/wd/hub') + end + + context 'with credentials' do + it 'has a grid of http://user:key@grid/wd/hub' do + stub_env('QA_REMOTE_GRID_USERNAME', 'foo') + stub_env('QA_REMOTE_GRID_ACCESS_KEY', 'bar') + stub_env('QA_REMOTE_GRID', 'localhost:4444') + + expect(described_class.remote_grid).to eq('http://foo:bar@localhost:4444/wd/hub') + end + end + + context 'without credentials' do + it 'has a grid of http://grid/wd/hub' do + stub_env('QA_REMOTE_GRID', 'localhost:4444') + + expect(described_class.remote_grid).to eq('http://localhost:4444/wd/hub') + end + end + end end diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb index 8e01da01340..3537ba7c235 100644 --- a/qa/spec/spec_helper.rb +++ b/qa/spec/spec_helper.rb @@ -5,6 +5,24 @@ Dir[::File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f } RSpec.configure do |config| config.before do |example| QA::Runtime::Logger.debug("Starting test: #{example.full_description}") if QA::Runtime::Env.debug? + + # If quarantine is tagged, skip tests that have other metadata unless + # they're also tagged. This lets us run quarantined tests in a particular + # category without running tests in other categories. + # E.g., if a test is tagged 'smoke' and 'quarantine', and another is tagged + # 'ldap' and 'quarantine', if we wanted to run just quarantined smoke tests + # using `--tag quarantine --tag smoke`, without this check we'd end up + # running that ldap test as well. + if config.inclusion_filter[:quarantine] + skip("Running tests tagged with all of #{config.inclusion_filter.rules.keys}") unless quarantine_and_optional_other_tag?(example, config) + end + end + + config.before(:each, :quarantine) do |example| + # Skip tests in quarantine unless we explicitly focus on them + # We could use an exclusion filter, but this way the test report will list + # the quarantined tests when they're not run so that we're aware of them + skip('In quarantine') unless config.inclusion_filter[:quarantine] end config.expect_with :rspec do |expectations| @@ -22,3 +40,19 @@ RSpec.configure do |config| config.order = :random Kernel.srand config.seed end + +# Checks if a test has the 'quarantine' tag and other tags in the inclusion filter. +# +# Returns true if +# - the example metadata includes the quarantine tag +# - and the metadata and inclusion filter both have any other tag +# - or no other tags are in the inclusion filter +def quarantine_and_optional_other_tag?(example, config) + return false unless example.metadata.keys.include? :quarantine + + filters_other_than_quarantine = config.inclusion_filter.rules.keys.reject { |key| key == :quarantine } + + return true if filters_other_than_quarantine.empty? + + filters_other_than_quarantine.any? { |key| example.metadata.keys.include? key } +end diff --git a/qa/spec/spec_helper_spec.rb b/qa/spec/spec_helper_spec.rb new file mode 100644 index 00000000000..f001200fb52 --- /dev/null +++ b/qa/spec/spec_helper_spec.rb @@ -0,0 +1,264 @@ +# frozen_string_literal: true + +describe 'rspec config tests' do + let(:group) do + RSpec.describe do + shared_examples 'passing tests' do + example 'not in quarantine' do + end + example 'in quarantine', :quarantine do + end + end + + context 'foo', :foo do + it_behaves_like 'passing tests' + end + + context 'default' do + it_behaves_like 'passing tests' + end + end + end + + context 'default config' do + it 'tests are skipped if in quarantine' do + group.run + + foo_context = group.children.find { |c| c.description == "foo" } + foo_examples = foo_context.descendant_filtered_examples + expect(foo_examples.count).to eq(2) + + ex = foo_examples.find { |e| e.description == "not in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + + ex = foo_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('In quarantine') + + default_context = group.children.find { |c| c.description == "default" } + default_examples = default_context.descendant_filtered_examples + expect(default_examples.count).to eq(2) + + ex = default_examples.find { |e| e.description == "not in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + + ex = default_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('In quarantine') + end + end + + context "with 'quarantine' tagged" do + before do + RSpec.configure do |config| + config.inclusion_filter = :quarantine + end + end + after do + RSpec.configure do |config| + config.inclusion_filter.clear + end + end + + it "only quarantined tests are run" do + group.run + + foo_context = group.children.find { |c| c.description == "foo" } + foo_examples = foo_context.descendant_filtered_examples + expect(foo_examples.count).to be(1) + + ex = foo_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + + default_context = group.children.find { |c| c.description == "default" } + default_examples = default_context.descendant_filtered_examples + expect(default_examples.count).to be(1) + + ex = default_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + end + end + + context "with 'foo' tagged" do + before do + RSpec.configure do |config| + config.inclusion_filter = :foo + end + + group.run + end + after do + RSpec.configure do |config| + config.inclusion_filter.clear + end + end + + it "tests are not run if not tagged 'foo'" do + default_context = group.children.find { |c| c.description == "default" } + expect(default_context.descendant_filtered_examples.count).to eq(0) + end + + it "tests are skipped if in quarantine" do + foo_context = group.children.find { |c| c.description == "foo" } + foo_examples = foo_context.descendant_filtered_examples + expect(foo_examples.count).to eq(2) + + ex = foo_examples.find { |e| e.description == "not in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + + ex = foo_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('In quarantine') + end + end + + context "with 'quarantine' and 'foo' tagged" do + before do + RSpec.configure do |config| + config.inclusion_filter = { quarantine: true, foo: true } + end + end + after do + RSpec.configure do |config| + config.inclusion_filter.clear + end + end + + it 'of tests tagged foo, only tests in quarantine run' do + group.run + + foo_context = group.children.find { |c| c.description == "foo" } + foo_examples = foo_context.descendant_filtered_examples + expect(foo_examples.count).to eq(2) + + ex = foo_examples.find { |e| e.description == "not in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('Running tests tagged with all of [:quarantine, :foo]') + + ex = foo_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:passed) + end + + it 'if tests are not tagged they are skipped, even if they are in quarantine' do + group.run + default_context = group.children.find { |c| c.description == "default" } + default_examples = default_context.descendant_filtered_examples + expect(default_examples.count).to eq(1) + + ex = default_examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('Running tests tagged with all of [:quarantine, :foo]') + end + end + + context "with 'foo' and 'bar' tagged" do + before do + RSpec.configure do |config| + config.inclusion_filter = { bar: true, foo: true } + end + end + after do + RSpec.configure do |config| + config.inclusion_filter.clear + end + end + + it "runs tests tagged either 'foo' or 'bar'" do + group = RSpec.describe do + example 'foo', :foo do + end + example 'bar', :bar do + end + example 'foo and bar', :foo, :bar do + end + end + + group.run + expect(group.examples.count).to eq(3) + + ex = group.examples.find { |e| e.description == "foo" } + expect(ex.execution_result.status).to eq(:passed) + + ex = group.examples.find { |e| e.description == "bar" } + expect(ex.execution_result.status).to eq(:passed) + + ex = group.examples.find { |e| e.description == "foo and bar" } + expect(ex.execution_result.status).to eq(:passed) + end + + it "skips quarantined tests tagged 'foo' and/or 'bar'" do + group = RSpec.describe do + example 'foo in quarantine', :foo, :quarantine do + end + example 'foo and bar in quarantine', :foo, :bar, :quarantine do + end + end + + group.run + expect(group.examples.count).to eq(2) + + ex = group.examples.find { |e| e.description == "foo in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('In quarantine') + + ex = group.examples.find { |e| e.description == "foo and bar in quarantine" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('In quarantine') + end + + it "ignores quarantined tests not tagged either 'foo' or 'bar'" do + group = RSpec.describe do + example 'in quarantine', :quarantine do + end + end + + group.run + + ex = group.examples.find { |e| e.description == "in quarantine" } + expect(ex.execution_result.status).to be_nil + end + end + + context "with 'foo' and 'bar' and 'quarantined' tagged" do + before do + RSpec.configure do |config| + config.inclusion_filter = { bar: true, foo: true, quarantine: true } + end + end + after do + RSpec.configure do |config| + config.inclusion_filter.clear + end + end + + it "runs tests tagged 'quarantine' and 'foo' or 'bar'" do + group = RSpec.describe do + example 'foo', :foo do + end + example 'bar and quarantine', :bar, :quarantine do + end + example 'foo and bar', :foo, :bar do + end + example 'foo, bar, and quarantine', :foo, :bar, :quarantine do + end + end + + group.run + expect(group.examples.count).to eq(4) + + ex = group.examples.find { |e| e.description == "foo" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('Running tests tagged with all of [:bar, :foo, :quarantine]') + + ex = group.examples.find { |e| e.description == "bar and quarantine" } + expect(ex.execution_result.status).to eq(:passed) + + ex = group.examples.find { |e| e.description == "foo and bar" } + expect(ex.execution_result.status).to eq(:pending) + expect(ex.execution_result.pending_message).to eq('Running tests tagged with all of [:bar, :foo, :quarantine]') + + ex = group.examples.find { |e| e.description == "foo, bar, and quarantine" } + expect(ex.execution_result.status).to eq(:passed) + end + end +end diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 118a7c7f638..4e1dbff7b80 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -31,7 +31,9 @@ function ensure_namespace() { function install_tiller() { echo "Checking Tiller..." - helm init --upgrade + helm init \ + --upgrade \ + --replicas 2 kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy" if ! helm version --debug; then echo "Failed to init Tiller." diff --git a/spec/controllers/admin/requests_profiles_controller_spec.rb b/spec/controllers/admin/requests_profiles_controller_spec.rb new file mode 100644 index 00000000000..10850cb4603 --- /dev/null +++ b/spec/controllers/admin/requests_profiles_controller_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Admin::RequestsProfilesController do + set(:admin) { create(:admin) } + + before do + sign_in(admin) + end + + describe '#show' do + let(:basename) { "profile_#{Time.now.to_i}.html" } + let(:tmpdir) { Dir.mktmpdir('profiler-test') } + let(:test_file) { File.join(tmpdir, basename) } + let(:profile) { Gitlab::RequestProfiler::Profile.new(basename) } + let(:sample_data) do + <<~HTML + <!DOCTYPE html> + <html> + <body> + <h1>My First Heading</h1> + <p>My first paragraph.</p> + </body> + </html> + HTML + end + + before do + stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir) + output = File.open(test_file, 'w') + output.write(sample_data) + output.close + end + + after do + File.unlink(test_file) + end + + it 'loads an HTML profile' do + get :show, params: { name: basename } + + expect(response).to have_gitlab_http_status(200) + expect(response.body).to eq(sample_data) + end + end +end diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index d377d69457f..21936491ffc 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -45,6 +45,40 @@ describe OmniauthCallbacksController, type: :controller do end end + context 'when a redirect fragment is provided' do + let(:provider) { :jwt } + let(:extern_uid) { 'my-uid' } + + before do + request.env['omniauth.params'] = { 'redirect_fragment' => 'L101' } + end + + context 'when a redirect url is stored' do + it 'redirects with fragment' do + post provider, nil, { user_return_to: '/fake/url' } + + expect(response).to redirect_to('/fake/url#L101') + end + end + + context 'when a redirect url with a fragment is stored' do + it 'redirects with the new fragment' do + post provider, nil, { user_return_to: '/fake/url#replaceme' } + + expect(response).to redirect_to('/fake/url#L101') + end + end + + context 'when no redirect url is stored' do + it 'does not redirect with the fragment' do + post provider + + expect(response.redirect?).to be true + expect(response.location).not_to include('#L101') + end + end + end + context 'strategies' do context 'github' do let(:extern_uid) { 'my-uid' } diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb new file mode 100644 index 00000000000..729e71b87a6 --- /dev/null +++ b/spec/controllers/projects/error_tracking_controller_spec.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Projects::ErrorTrackingController do + set(:project) { create(:project) } + set(:user) { create(:user) } + + before do + sign_in(user) + project.add_maintainer(user) + end + + describe 'GET #index' do + describe 'html' do + it 'renders index with 200 status code' do + get :index, params: project_params + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:index) + end + + context 'with feature flag disabled' do + before do + stub_feature_flags(error_tracking: false) + end + + it 'returns 404' do + get :index, params: project_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'with insufficient permissions' do + before do + project.add_guest(user) + end + + it 'returns 404' do + get :index, params: project_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'with an anonymous user' do + before do + sign_out(user) + end + + it 'redirects to sign-in page' do + get :index, params: project_params + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + describe 'format json' do + shared_examples 'no data' do + it 'returns no data' do + get :index, params: project_params(format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('error_tracking/index') + expect(json_response['external_url']).to be_nil + expect(json_response['errors']).to eq([]) + end + end + + let(:list_issues_service) { spy(:list_issues_service) } + let(:external_url) { 'http://example.com' } + + before do + expect(ErrorTracking::ListIssuesService) + .to receive(:new).with(project, user) + .and_return(list_issues_service) + end + + context 'service result is successful' do + before do + expect(list_issues_service).to receive(:execute) + .and_return(status: :success, issues: [error]) + expect(list_issues_service).to receive(:external_url) + .and_return(external_url) + end + + let(:error) { build(:error_tracking_error) } + + it 'returns a list of errors' do + get :index, params: project_params(format: :json) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('error_tracking/index') + expect(json_response['external_url']).to eq(external_url) + expect(json_response['errors']).to eq([error].as_json) + end + end + + context 'service result is erroneous' do + let(:error_message) { 'error message' } + + context 'without http_status' do + before do + expect(list_issues_service).to receive(:execute) + .and_return(status: :error, message: error_message) + end + + it 'returns 400 with message' do + get :index, params: project_params(format: :json) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq(error_message) + end + end + + context 'with explicit http_status' do + let(:http_status) { :no_content } + + before do + expect(list_issues_service).to receive(:execute) + .and_return(status: :error, message: error_message, http_status: http_status) + end + + it 'returns http_status with message' do + get :index, params: project_params(format: :json) + + expect(response).to have_gitlab_http_status(http_status) + expect(json_response['message']).to eq(error_message) + end + end + end + end + end + + private + + def project_params(opts = {}) + opts.reverse_merge(namespace_id: project.namespace, project_id: project) + end +end diff --git a/spec/factories/error_tracking/error.rb b/spec/factories/error_tracking/error.rb new file mode 100644 index 00000000000..ff883a3d22c --- /dev/null +++ b/spec/factories/error_tracking/error.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :error_tracking_error, class: Gitlab::ErrorTracking::Error do + id 'id' + title 'title' + type 'error' + user_count 1 + count 2 + first_seen { Time.now } + last_seen { Time.now } + message 'message' + culprit 'culprit' + external_url 'http://example.com/id' + project_id 'project1' + project_name 'project name' + project_slug 'project_name' + short_id 'ID' + status 'unresolved' + frequency [] + + skip_create + end +end diff --git a/spec/factories/project_error_tracking_settings.rb b/spec/factories/project_error_tracking_settings.rb index f044cbe8755..fbd8dfd395c 100644 --- a/spec/factories/project_error_tracking_settings.rb +++ b/spec/factories/project_error_tracking_settings.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :project_error_tracking_setting, class: ErrorTracking::ProjectErrorTrackingSetting do project - api_url 'https://gitlab.com' + api_url 'https://gitlab.com/api/0/projects/sentry-org/sentry-project' enabled true token 'access_token_123' end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 0a69a26eb3e..04f39b807d7 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -238,7 +238,7 @@ describe 'Admin updates settings' do page.within('.as-ci-cd') do check 'Default to Auto DevOps pipeline for all projects' - fill_in 'Auto devops domain', with: 'domain.com' + fill_in 'application_setting_auto_devops_domain', with: 'domain.com' click_button 'Save changes' end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index 31a1dcf826d..cbddf117462 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -49,7 +49,7 @@ describe 'Dashboard shortcuts', :js do find('body').send_keys([:shift, 'P']) find('.nothing-here-block') - expect(page).to have_content('No projects found') + expect(page).to have_content('This user doesn\'t have any personal projects') end end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index 3746d37b9cd..cc86114e436 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -38,7 +38,7 @@ describe 'Dashboard > User filters projects' do it 'returns message when starred projects fitler returns no results' do fill_in 'project-filter-form-field', with: 'Beta\n' - expect(page).to have_content('No projects found') + expect(page).to have_content('This user doesn\'t have any personal projects') expect(page).not_to have_content('You don\'t have starred projects yet') end end diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb index 8f5ca781b2c..e4eb0d355d1 100644 --- a/spec/features/groups/empty_states_spec.rb +++ b/spec/features/groups/empty_states_spec.rb @@ -23,14 +23,52 @@ describe 'Group empty states' do end context "the project has #{issuable_name}s" do - before do + it 'does not display an empty state' do create(issuable, project_relation => project) visit path + expect(page).not_to have_selector('.empty-state') end - it 'does not display an empty state' do - expect(page).not_to have_selector('.empty-state') + it "displays link to create new #{issuable} when no open #{issuable} is found" do + create("closed_#{issuable}", project_relation => project) + issuable_link_fn = "project_#{issuable}s_path" + + visit public_send(issuable_link_fn, project) + + page.within(find('.empty-state')) do + expect(page).to have_content(/There are no open #{issuable.to_s.humanize.downcase}/) + expect(page).to have_selector("#new_#{issuable}_body_link") + end + end + + it 'displays link to create new issue when the current search gave no results' do + create(issuable, project_relation => project) + + issuable_link_fn = "project_#{issuable}s_path" + + visit public_send(issuable_link_fn, project, author_username: 'foo', scope: 'all', state: 'opened') + + page.within(find('.empty-state')) do + expect(page).to have_content(/Sorry, your filter produced no results/) + new_issuable_path = issuable == :issue ? 'new_project_issue_path' : 'project_new_merge_request_path' + + path = public_send(new_issuable_path, project) + + expect(page).to have_selector("#new_#{issuable}_body_link[href='#{path}']") + end + end + + it "displays conditional text when no closed #{issuable} is found" do + create(issuable, project_relation => project) + + issuable_link_fn = "project_#{issuable}s_path" + + visit public_send(issuable_link_fn, project, state: 'closed') + + page.within(find('.empty-state')) do + expect(page).to have_content(/There are no closed #{issuable.to_s.humanize.downcase}/) + end end end diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb index 69600884909..5f7cf68987e 100644 --- a/spec/features/projects/compare_spec.rb +++ b/spec/features/projects/compare_spec.rb @@ -87,6 +87,21 @@ describe "Compare", :js do expect(find(".js-compare-from-dropdown .dropdown-content")).to have_selector("li", count: 3) end + + context 'when commit has overflow', :js do + it 'displays warning' do + visit project_compare_index_path(project, from: "feature", to: "master") + + allow(Commit).to receive(:max_diff_options).and_return(max_files: 3) + allow_any_instance_of(DiffHelper).to receive(:render_overflow_warning?).and_return(true) + + click_button('Compare') + + page.within('.alert') do + expect(page).to have_text("Too many changes to show. To preserve performance only 3 of 3+ files are displayed.") + end + end + end end describe "tags" do diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 09de983f669..6eae0be4b9f 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -236,7 +236,7 @@ describe 'Runners' do it 'group runners are available' do visit project_runners_path(project) - expect(page).to have_content 'Available group Runners : 1' + expect(page).to have_content 'Available group Runners: 1' expect(page).to have_content 'group-runner' end @@ -279,7 +279,7 @@ describe 'Runners' do visit group_settings_ci_cd_path(group) expect(page).not_to have_content 'This group does not provide any group Runners yet' - expect(page).to have_content 'Available group Runners : 1' + expect(page).to have_content 'Available group Runners: 1' expect(page).to have_content 'group-runner' end diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb index 8748230fa0c..3708f0ee477 100644 --- a/spec/features/users/overview_spec.rb +++ b/spec/features/users/overview_spec.rb @@ -96,7 +96,7 @@ describe 'Overview tab on a user profile', :js do it 'it shows an empty project list with an info message' do page.within('.projects-block') do expect(page).to have_selector('.loading', visible: false) - expect(page).to have_content('No projects found') + expect(page).to have_content('This user doesn\'t have any personal projects') expect(page).not_to have_selector('.project-row') end end diff --git a/spec/fixtures/api/schemas/error_tracking/error.json b/spec/fixtures/api/schemas/error_tracking/error.json new file mode 100644 index 00000000000..df2c02d7d5d --- /dev/null +++ b/spec/fixtures/api/schemas/error_tracking/error.json @@ -0,0 +1,21 @@ +{ + "type": "object", + "required" : [ + "external_url", + "last_seen", + "message", + "type" + ], + "properties" : { + "id": { "type": "string"}, + "first_seen": { "type": "string", "format": "date-time" }, + "last_seen": { "type": "string", "format": "date-time" }, + "type": { "type": "string" }, + "message": { "type": "string" }, + "culprit": { "type": "string" }, + "count": { "type": "integer"}, + "external_url": { "type": "string" }, + "user_count": { "type": "integer"} + }, + "additionalProperties": true +} diff --git a/spec/fixtures/api/schemas/error_tracking/index.json b/spec/fixtures/api/schemas/error_tracking/index.json new file mode 100644 index 00000000000..d3abc29ffa7 --- /dev/null +++ b/spec/fixtures/api/schemas/error_tracking/index.json @@ -0,0 +1,15 @@ +{ + "type": "object", + "required": [ + "external_url", + "errors" + ], + "properties": { + "external_url": { "type": ["string", "null"] }, + "errors": { + "type": "array", + "items": { "$ref": "error.json" } + } + }, + "additionalProperties": false +} diff --git a/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json b/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json index 9840382df6f..6f89d20d4bf 100644 --- a/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json +++ b/spec/fixtures/security-reports/feature-branch/gl-container-scanning-report.json @@ -1,18 +1,16 @@ { - "image": "registry.gitlab.com/bikebilly/auto-devops-10-6/feature-branch:e7315ba964febb11bac8f5cd6ec433db8a3a1583", - "unapproved": [ - "CVE-2017-15650" - ], - "vulnerabilities": [ - { - "featurename": "musl", - "featureversion": "1.1.14-r15", - "vulnerability": "CVE-2017-15650", - "namespace": "alpine:v3.4", - "description": "", - "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650", - "severity": "Medium", - "fixedby": "1.1.14-r16" - } - ] + "image": "registry.gitlab.com/bikebilly/auto-devops-10-6/feature-branch:e7315ba964febb11bac8f5cd6ec433db8a3a1583", + "unapproved": ["CVE-2017-15650"], + "vulnerabilities": [ + { + "featurename": "musl", + "featureversion": "1.1.14-r15", + "vulnerability": "CVE-2017-15650", + "namespace": "alpine:v3.4", + "description": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15650", + "severity": "Medium", + "fixedby": "1.1.14-r16" + } + ] } diff --git a/spec/fixtures/security-reports/master/gl-container-scanning-report.json b/spec/fixtures/security-reports/master/gl-container-scanning-report.json index c087352a122..68c6099836b 100644 --- a/spec/fixtures/security-reports/master/gl-container-scanning-report.json +++ b/spec/fixtures/security-reports/master/gl-container-scanning-report.json @@ -1,92 +1,92 @@ { - "image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff", - "unapproved": [ - "CVE-2017-18018", - "CVE-2016-2781", - "CVE-2017-12424", - "CVE-2007-5686", - "CVE-2013-4235" - ], - "vulnerabilities": [ - { - "featurename": "glibc", - "featureversion": "2.24-11+deb9u3", - "vulnerability": "CVE-2017-18269", - "namespace": "debian:9", - "description": "SSE2-optimized memmove implementation problem.", - "link": "https://security-tracker.debian.org/tracker/CVE-2017-18269", - "severity": "Defcon1", - "fixedby": "2.24-11+deb9u4" - }, - { - "featurename": "glibc", - "featureversion": "2.24-11+deb9u3", - "vulnerability": "CVE-2017-16997", - "namespace": "debian:9", - "description": "elf/dl-load.c in the GNU C Library (aka glibc or libc6) 2.19 through 2.26 mishandles RPATH and RUNPATH containing $ORIGIN for a privileged (setuid or AT_SECURE) program, which allows local users to gain privileges via a Trojan horse library in the current working directory, related to the fillin_rpath and decompose_rpath functions. This is associated with misinterpretion of an empty RPATH/RUNPATH token as the \"./\" directory. NOTE: this configuration of RPATH/RUNPATH for a privileged program is apparently very uncommon; most likely, no such program is shipped with any common Linux distribution.", - "link": "https://security-tracker.debian.org/tracker/CVE-2017-16997", - "severity": "Critical", - "fixedby": "" - }, - { - "featurename": "glibc", - "featureversion": "2.24-11+deb9u3", - "vulnerability": "CVE-2018-1000001", - "namespace": "debian:9", - "description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.", - "link": "https://security-tracker.debian.org/tracker/CVE-2018-1000001", - "severity": "High", - "fixedby": "" - }, - { - "featurename": "glibc", - "featureversion": "2.24-11+deb9u3", - "vulnerability": "CVE-2016-10228", - "namespace": "debian:9", - "description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.25 and earlier, when invoked with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", - "link": "https://security-tracker.debian.org/tracker/CVE-2016-10228", - "severity": "Medium", - "fixedby": "" - }, - { - "featurename": "elfutils", - "featureversion": "0.168-1", - "vulnerability": "CVE-2018-18520", - "namespace": "debian:9", - "description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.", - "link": "https://security-tracker.debian.org/tracker/CVE-2018-18520", - "severity": "Low", - "fixedby": "" - }, - { - "featurename": "glibc", - "featureversion": "2.24-11+deb9u3", - "vulnerability": "CVE-2010-4052", - "namespace": "debian:9", - "description": "Stack consumption vulnerability in the regcomp implementation in the GNU C Library (aka glibc or libc6) through 2.11.3, and 2.12.x through 2.12.2, allows context-dependent attackers to cause a denial of service (resource exhaustion) via a regular expression containing adjacent repetition operators, as demonstrated by a {10,}{10,}{10,}{10,} sequence in the proftpd.gnu.c exploit for ProFTPD.", - "link": "https://security-tracker.debian.org/tracker/CVE-2010-4052", - "severity": "Negligible", - "fixedby": "" - }, - { - "featurename": "nettle", - "featureversion": "3.3-1", - "vulnerability": "CVE-2018-16869", - "namespace": "debian:9", - "description": "A Bleichenbacher type side-channel based padding oracle attack was found in the way nettle handles endian conversion of RSA decrypted PKCS#1 v1.5 data. An attacker who is able to run a process on the same physical core as the victim process, could use this flaw extract plaintext or in some cases downgrade any TLS connections to a vulnerable server.", - "link": "https://security-tracker.debian.org/tracker/CVE-2018-16869", - "severity": "Unknown", - "fixedby": "" - }, - { - "featurename": "perl", - "featureversion": "5.24.1-3+deb9u4", - "vulnerability": "CVE-2018-18311", - "namespace": "debian:9", - "description": "Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.", - "link": "https://security-tracker.debian.org/tracker/CVE-2018-18311", - "severity": "Unknown", - "fixedby": "5.24.1-3+deb9u5" - } - ] -}
\ No newline at end of file + "image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff", + "unapproved": [ + "CVE-2017-18018", + "CVE-2016-2781", + "CVE-2017-12424", + "CVE-2007-5686", + "CVE-2013-4235" + ], + "vulnerabilities": [ + { + "featurename": "glibc", + "featureversion": "2.24-11+deb9u3", + "vulnerability": "CVE-2017-18269", + "namespace": "debian:9", + "description": "SSE2-optimized memmove implementation problem.", + "link": "https://security-tracker.debian.org/tracker/CVE-2017-18269", + "severity": "Defcon1", + "fixedby": "2.24-11+deb9u4" + }, + { + "featurename": "glibc", + "featureversion": "2.24-11+deb9u3", + "vulnerability": "CVE-2017-16997", + "namespace": "debian:9", + "description": "elf/dl-load.c in the GNU C Library (aka glibc or libc6) 2.19 through 2.26 mishandles RPATH and RUNPATH containing $ORIGIN for a privileged (setuid or AT_SECURE) program, which allows local users to gain privileges via a Trojan horse library in the current working directory, related to the fillin_rpath and decompose_rpath functions. This is associated with misinterpretion of an empty RPATH/RUNPATH token as the \"./\" directory. NOTE: this configuration of RPATH/RUNPATH for a privileged program is apparently very uncommon; most likely, no such program is shipped with any common Linux distribution.", + "link": "https://security-tracker.debian.org/tracker/CVE-2017-16997", + "severity": "Critical", + "fixedby": "" + }, + { + "featurename": "glibc", + "featureversion": "2.24-11+deb9u3", + "vulnerability": "CVE-2018-1000001", + "namespace": "debian:9", + "description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.", + "link": "https://security-tracker.debian.org/tracker/CVE-2018-1000001", + "severity": "High", + "fixedby": "" + }, + { + "featurename": "glibc", + "featureversion": "2.24-11+deb9u3", + "vulnerability": "CVE-2016-10228", + "namespace": "debian:9", + "description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.25 and earlier, when invoked with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", + "link": "https://security-tracker.debian.org/tracker/CVE-2016-10228", + "severity": "Medium", + "fixedby": "" + }, + { + "featurename": "elfutils", + "featureversion": "0.168-1", + "vulnerability": "CVE-2018-18520", + "namespace": "debian:9", + "description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.", + "link": "https://security-tracker.debian.org/tracker/CVE-2018-18520", + "severity": "Low", + "fixedby": "" + }, + { + "featurename": "glibc", + "featureversion": "2.24-11+deb9u3", + "vulnerability": "CVE-2010-4052", + "namespace": "debian:9", + "description": "Stack consumption vulnerability in the regcomp implementation in the GNU C Library (aka glibc or libc6) through 2.11.3, and 2.12.x through 2.12.2, allows context-dependent attackers to cause a denial of service (resource exhaustion) via a regular expression containing adjacent repetition operators, as demonstrated by a {10,}{10,}{10,}{10,} sequence in the proftpd.gnu.c exploit for ProFTPD.", + "link": "https://security-tracker.debian.org/tracker/CVE-2010-4052", + "severity": "Negligible", + "fixedby": "" + }, + { + "featurename": "nettle", + "featureversion": "3.3-1", + "vulnerability": "CVE-2018-16869", + "namespace": "debian:9", + "description": "A Bleichenbacher type side-channel based padding oracle attack was found in the way nettle handles endian conversion of RSA decrypted PKCS#1 v1.5 data. An attacker who is able to run a process on the same physical core as the victim process, could use this flaw extract plaintext or in some cases downgrade any TLS connections to a vulnerable server.", + "link": "https://security-tracker.debian.org/tracker/CVE-2018-16869", + "severity": "Unknown", + "fixedby": "" + }, + { + "featurename": "perl", + "featureversion": "5.24.1-3+deb9u4", + "vulnerability": "CVE-2018-18311", + "namespace": "debian:9", + "description": "Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.", + "link": "https://security-tracker.debian.org/tracker/CVE-2018-18311", + "severity": "Unknown", + "fixedby": "5.24.1-3+deb9u5" + } + ] +} diff --git a/spec/fixtures/sentry/issues_sample_response.json b/spec/fixtures/sentry/issues_sample_response.json new file mode 100644 index 00000000000..ed22499cfa1 --- /dev/null +++ b/spec/fixtures/sentry/issues_sample_response.json @@ -0,0 +1,42 @@ +[{ + "lastSeen": "2018-12-31T12:00:11Z", + "numComments": 0, + "userCount": 0, + "stats": { + "24h": [ + [ + 1546437600, + 0 + ] + ] + }, + "culprit": "sentry.tasks.reports.deliver_organization_user_report", + "title": "gaierror: [Errno -2] Name or service not known", + "id": "11", + "assignedTo": null, + "logger": null, + "type": "error", + "annotations": [], + "metadata": { + "type": "gaierror", + "value": "[Errno -2] Name or service not known" + }, + "status": "unresolved", + "subscriptionDetails": null, + "isPublic": false, + "hasSeen": false, + "shortId": "INTERNAL-4", + "shareId": null, + "firstSeen": "2018-12-17T12:00:14Z", + "count": "21", + "permalink": "35.228.54.90/sentry/internal/issues/11/", + "level": "error", + "isSubscribed": true, + "isBookmarked": false, + "project": { + "slug": "internal", + "id": "1", + "name": "Internal" + }, + "statusDetails": {} + }] diff --git a/spec/javascripts/fixtures/oauth_remember_me.html.haml b/spec/javascripts/fixtures/oauth_remember_me.html.haml index 7886e995e57..a5d7c4e816a 100644 --- a/spec/javascripts/fixtures/oauth_remember_me.html.haml +++ b/spec/javascripts/fixtures/oauth_remember_me.html.haml @@ -3,3 +3,4 @@ %a.oauth-login.twitter{ href: "http://example.com/" } %a.oauth-login.github{ href: "http://example.com/" } + %a.oauth-login.facebook{ href: "http://example.com/?redirect_fragment=L1" } diff --git a/spec/javascripts/fixtures/sessions.rb b/spec/javascripts/fixtures/sessions.rb new file mode 100644 index 00000000000..e90a58e8c54 --- /dev/null +++ b/spec/javascripts/fixtures/sessions.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'Sessions (JavaScript fixtures)' do + include JavaScriptFixturesHelpers + + before(:all) do + clean_frontend_fixtures('sessions/') + end + + describe SessionsController, '(JavaScript fixtures)', type: :controller do + include DeviseHelpers + + render_views + + before do + set_devise_mapping(context: @request) + end + + it 'sessions/new.html.raw' do |example| + get :new + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end + end +end diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js index e4df8441793..381c7b2d0a6 100644 --- a/spec/javascripts/lib/utils/url_utility_spec.js +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -1,4 +1,4 @@ -import { webIDEUrl, mergeUrlParams } from '~/lib/utils/url_utility'; +import * as urlUtils from '~/lib/utils/url_utility'; describe('URL utility', () => { describe('webIDEUrl', () => { @@ -8,7 +8,7 @@ describe('URL utility', () => { describe('without relative_url_root', () => { it('returns IDE path with route', () => { - expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + expect(urlUtils.webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( '/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', ); }); @@ -20,7 +20,7 @@ describe('URL utility', () => { }); it('returns IDE path with route', () => { - expect(webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + expect(urlUtils.webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe( '/gitlab/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', ); }); @@ -29,23 +29,82 @@ describe('URL utility', () => { describe('mergeUrlParams', () => { it('adds w', () => { - expect(mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag'); - expect(mergeUrlParams({ w: 1 }, '/path#frag')).toBe('/path?w=1#frag'); - expect(mergeUrlParams({ w: 1 }, 'https://host/path')).toBe('https://host/path?w=1'); - expect(mergeUrlParams({ w: 1 }, 'https://host/path#frag')).toBe('https://host/path?w=1#frag'); - expect(mergeUrlParams({ w: 1 }, 'https://h/p?k1=v1#frag')).toBe('https://h/p?k1=v1&w=1#frag'); + expect(urlUtils.mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag'); + expect(urlUtils.mergeUrlParams({ w: 1 }, '/path#frag')).toBe('/path?w=1#frag'); + expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://host/path')).toBe('https://host/path?w=1'); + expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://host/path#frag')).toBe( + 'https://host/path?w=1#frag', + ); + + expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://h/p?k1=v1#frag')).toBe( + 'https://h/p?k1=v1&w=1#frag', + ); }); it('updates w', () => { - expect(mergeUrlParams({ w: 1 }, '?k1=v1&w=0#frag')).toBe('?k1=v1&w=1#frag'); + expect(urlUtils.mergeUrlParams({ w: 1 }, '?k1=v1&w=0#frag')).toBe('?k1=v1&w=1#frag'); }); it('adds multiple params', () => { - expect(mergeUrlParams({ a: 1, b: 2, c: 3 }, '#frag')).toBe('?a=1&b=2&c=3#frag'); + expect(urlUtils.mergeUrlParams({ a: 1, b: 2, c: 3 }, '#frag')).toBe('?a=1&b=2&c=3#frag'); }); it('adds and updates encoded params', () => { - expect(mergeUrlParams({ a: '&', q: '?' }, '?a=%23#frag')).toBe('?a=%26&q=%3F#frag'); + expect(urlUtils.mergeUrlParams({ a: '&', q: '?' }, '?a=%23#frag')).toBe('?a=%26&q=%3F#frag'); + }); + }); + + describe('removeParams', () => { + describe('when url is passed', () => { + it('removes query param with encoded ampersand', () => { + const url = urlUtils.removeParams(['filter'], '/mail?filter=n%3Djoe%26l%3Dhome'); + + expect(url).toBe('/mail'); + }); + + it('should remove param when url has no other params', () => { + const url = urlUtils.removeParams(['size'], '/feature/home?size=5'); + + expect(url).toBe('/feature/home'); + }); + + it('should remove param when url has other params', () => { + const url = urlUtils.removeParams(['size'], '/feature/home?q=1&size=5&f=html'); + + expect(url).toBe('/feature/home?q=1&f=html'); + }); + + it('should remove param and preserve fragment', () => { + const url = urlUtils.removeParams(['size'], '/feature/home?size=5#H2'); + + expect(url).toBe('/feature/home#H2'); + }); + + it('should remove multiple params', () => { + const url = urlUtils.removeParams(['z', 'a'], '/home?z=11111&l=en_US&a=true#H2'); + + expect(url).toBe('/home?l=en_US#H2'); + }); + }); + }); + + describe('setUrlFragment', () => { + it('should set fragment when url has no fragment', () => { + const url = urlUtils.setUrlFragment('/home/feature', 'usage'); + + expect(url).toBe('/home/feature#usage'); + }); + + it('should set fragment when url has existing fragment', () => { + const url = urlUtils.setUrlFragment('/home/feature#overview', 'usage'); + + expect(url).toBe('/home/feature#usage'); + }); + + it('should set fragment when given fragment includes #', () => { + const url = urlUtils.setUrlFragment('/home/feature#overview', '#install'); + + expect(url).toBe('/home/feature#install'); }); }); }); diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js index 5efcab436e4..91dab58ba7f 100644 --- a/spec/javascripts/notes/components/discussion_filter_spec.js +++ b/spec/javascripts/notes/components/discussion_filter_spec.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import createStore from '~/notes/stores'; import DiscussionFilter from '~/notes/components/discussion_filter.vue'; +import { DISCUSSION_FILTERS_DEFAULT_VALUE } from '~/notes/constants'; import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper'; import { discussionFiltersMock, discussionMock } from '../mock_data'; @@ -20,16 +21,14 @@ describe('DiscussionFilter component', () => { }, ]; const Component = Vue.extend(DiscussionFilter); - const selectedValue = discussionFiltersMock[0].value; + const selectedValue = DISCUSSION_FILTERS_DEFAULT_VALUE; + const props = { filters: discussionFiltersMock, selectedValue }; store.state.discussions = discussions; return mountComponentWithStore(Component, { el: null, store, - props: { - filters: discussionFiltersMock, - selectedValue, - }, + props, }); }; @@ -115,4 +114,41 @@ describe('DiscussionFilter component', () => { }); }); }); + + describe('URL with Links to notes', () => { + afterEach(() => { + window.location.hash = ''; + }); + + it('updates the filter when the URL links to a note', done => { + window.location.hash = `note_${discussionMock.notes[0].id}`; + vm.currentValue = discussionFiltersMock[2].value; + vm.handleLocationHash(); + + vm.$nextTick(() => { + expect(vm.currentValue).toEqual(DISCUSSION_FILTERS_DEFAULT_VALUE); + done(); + }); + }); + + it('does not update the filter when the current filter is "Show all activity"', done => { + window.location.hash = `note_${discussionMock.notes[0].id}`; + vm.handleLocationHash(); + + vm.$nextTick(() => { + expect(vm.currentValue).toEqual(DISCUSSION_FILTERS_DEFAULT_VALUE); + done(); + }); + }); + + it('only updates filter when the URL links to a note', done => { + window.location.hash = `testing123`; + vm.handleLocationHash(); + + vm.$nextTick(() => { + expect(vm.currentValue).toEqual(DISCUSSION_FILTERS_DEFAULT_VALUE); + done(); + }); + }); + }); }); diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js index 2caa266b85f..4125706a407 100644 --- a/spec/javascripts/oauth_remember_me_spec.js +++ b/spec/javascripts/oauth_remember_me_spec.js @@ -20,6 +20,10 @@ describe('OAuthRememberMe', () => { expect($('#oauth-container .oauth-login.github').attr('href')).toBe( 'http://example.com/?remember_me=1', ); + + expect($('#oauth-container .oauth-login.facebook').attr('href')).toBe( + 'http://example.com/?redirect_fragment=L1&remember_me=1', + ); }); it('removes the "remember_me" query parameter from all OAuth login buttons', () => { @@ -28,5 +32,8 @@ describe('OAuthRememberMe', () => { expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/'); expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/'); + expect($('#oauth-container .oauth-login.facebook').attr('href')).toBe( + 'http://example.com/?redirect_fragment=L1', + ); }); }); diff --git a/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js b/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js new file mode 100644 index 00000000000..7a8227479d4 --- /dev/null +++ b/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js @@ -0,0 +1,61 @@ +import $ from 'jquery'; +import preserveUrlFragment from '~/pages/sessions/new/preserve_url_fragment'; + +describe('preserve_url_fragment', () => { + preloadFixtures('sessions/new.html.raw'); + + beforeEach(() => { + loadFixtures('sessions/new.html.raw'); + }); + + it('adds the url fragment to all login and sign up form actions', () => { + preserveUrlFragment('#L65'); + + expect($('#new_user').attr('action')).toBe('http://test.host/users/sign_in#L65'); + expect($('#new_new_user').attr('action')).toBe('http://test.host/users#L65'); + }); + + it('does not add an empty url fragment to login and sign up form actions', () => { + preserveUrlFragment(); + + expect($('#new_user').attr('action')).toBe('http://test.host/users/sign_in'); + expect($('#new_new_user').attr('action')).toBe('http://test.host/users'); + }); + + it('does not add an empty query parameter to OmniAuth login buttons', () => { + preserveUrlFragment(); + + expect($('#oauth-login-cas3').attr('href')).toBe('http://test.host/users/auth/cas3'); + + expect($('.omniauth-container #oauth-login-auth0').attr('href')).toBe( + 'http://test.host/users/auth/auth0', + ); + }); + + describe('adds "redirect_fragment" query parameter to OmniAuth login buttons', () => { + it('when "remember_me" is not present', () => { + preserveUrlFragment('#L65'); + + expect($('#oauth-login-cas3').attr('href')).toBe( + 'http://test.host/users/auth/cas3?redirect_fragment=L65', + ); + + expect($('.omniauth-container #oauth-login-auth0').attr('href')).toBe( + 'http://test.host/users/auth/auth0?redirect_fragment=L65', + ); + }); + + it('when "remember-me" is present', () => { + $('a.omniauth-btn').attr('href', (i, href) => `${href}?remember_me=1`); + preserveUrlFragment('#L65'); + + expect($('#oauth-login-cas3').attr('href')).toBe( + 'http://test.host/users/auth/cas3?remember_me=1&redirect_fragment=L65', + ); + + expect($('#oauth-login-auth0').attr('href')).toBe( + 'http://test.host/users/auth/auth0?remember_me=1&redirect_fragment=L65', + ); + }); + }); +}); diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js index 61ef26cd080..b356ea85cad 100644 --- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js +++ b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js @@ -76,4 +76,28 @@ describe('getStateKey', () => { expect(bound()).toEqual('archived'); }); + + it('returns rebased state key', () => { + const context = { + mergeStatus: 'checked', + mergeWhenPipelineSucceeds: false, + canMerge: true, + onlyAllowMergeIfPipelineSucceeds: true, + isPipelineFailed: true, + hasMergeableDiscussionsState: false, + isPipelineBlocked: false, + canBeMerged: false, + shouldBeRebased: true, + }; + const data = { + project_archived: false, + branch_missing: false, + commits_count: 2, + has_conflicts: false, + work_in_progress: false, + }; + const bound = getStateKey.bind(context, data); + + expect(bound()).toEqual('rebase'); + }); }); diff --git a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js index d4ed8f2f7a4..f87c2a92f47 100644 --- a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js @@ -34,8 +34,8 @@ describe('Suggestion Diff component', () => { expect(vm.$el.querySelector('.qa-suggestion-diff-header')).not.toBeNull(); }); - it('renders a diff table', () => { - expect(vm.$el.querySelector('table.md-suggestion-diff')).not.toBeNull(); + it('renders a diff table with syntax highlighting', () => { + expect(vm.$el.querySelector('.md-suggestion-diff.js-syntax-highlight.code')).not.toBeNull(); }); it('renders the oldLineNumber', () => { diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb index 1c73a936e17..e1aea82653d 100644 --- a/spec/lib/api/helpers_spec.rb +++ b/spec/lib/api/helpers_spec.rb @@ -150,32 +150,36 @@ describe API::Helpers do end describe '#send_git_blob' do - context 'content disposition' do - let(:repository) { double } - let(:blob) { double(name: 'foobar') } + let(:repository) { double } + let(:blob) { double(name: 'foobar') } - let(:send_git_blob) do - subject.send(:send_git_blob, repository, blob) - end + let(:send_git_blob) do + subject.send(:send_git_blob, repository, blob) + end - before do - allow(subject).to receive(:env).and_return({}) - allow(subject).to receive(:content_type) - allow(subject).to receive(:header).and_return({}) - allow(Gitlab::Workhorse).to receive(:send_git_blob) - end + before do + allow(subject).to receive(:env).and_return({}) + allow(subject).to receive(:content_type) + allow(subject).to receive(:header).and_return({}) + allow(Gitlab::Workhorse).to receive(:send_git_blob) + end + + it 'sets Gitlab::Workhorse::DETECT_HEADER header' do + expect(send_git_blob[Gitlab::Workhorse::DETECT_HEADER]).to eq "true" + end + context 'content disposition' do context 'when blob name is null' do let(:blob) { double(name: nil) } it 'returns only the disposition' do - expect(send_git_blob['Content-Disposition']).to eq 'attachment' + expect(send_git_blob['Content-Disposition']).to eq 'inline' end end context 'when blob name is not null' do it 'returns disposition with the blob name' do - expect(send_git_blob['Content-Disposition']).to eq 'attachment; filename="foobar"' + expect(send_git_blob['Content-Disposition']).to eq 'inline; filename="foobar"' end end end diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb index 9633caac788..ae1c881e1f6 100644 --- a/spec/lib/backup/manager_spec.rb +++ b/spec/lib/backup/manager_spec.rb @@ -266,6 +266,7 @@ describe Backup::Manager do remote_directory: 'directory', multipart_chunk_size: 104857600, encryption: nil, + encryption_key: nil, storage_class: nil } ) diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb index 4e4c8b215c2..1e2aebdc84b 100644 --- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Auth::UserAuthFinders do 'rack.input' => '' } end - let(:request) { Rack::Request.new(env) } + let(:request) { ActionDispatch::Request.new(env) } def set_param(key, value) request.update_param(key, value) diff --git a/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb index ae4b53d62e6..947c99b860f 100644 --- a/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb +++ b/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::BackfillLegacyProjectRepositories, :migration, schema: 20181218192239 do +describe Gitlab::BackgroundMigration::BackfillLegacyProjectRepositories, :migration, schema: 20181212171634 do it_behaves_like 'backfill migration for project repositories', :legacy end diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb index 97926695b6e..cd880177170 100644 --- a/spec/lib/gitlab/ci/config/normalizer_spec.rb +++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb @@ -62,5 +62,25 @@ describe Gitlab::Ci::Config::Normalizer do expect(subject[:other_job][:dependencies]).not_to include(job_name) end end + + context 'when there are dependencies which are both parallelized and not' do + let(:config) do + { + job_name => job_config, + other_job: { script: 'echo 1' }, + final_job: { script: 'echo 1', dependencies: [job_name.to_s, "other_job"] } + } + end + + it 'parallelizes dependencies' do + job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"] + + expect(subject[:final_job][:dependencies]).to include(*job_names) + end + + it 'includes the regular job in dependencies' do + expect(subject[:final_job][:dependencies]).to include('other_job') + end + end end end diff --git a/spec/lib/gitlab/repository_cache_spec.rb b/spec/lib/gitlab/repository_cache_spec.rb index 1b9a8b4ab0d..741ee12633f 100644 --- a/spec/lib/gitlab/repository_cache_spec.rb +++ b/spec/lib/gitlab/repository_cache_spec.rb @@ -4,14 +4,14 @@ describe Gitlab::RepositoryCache do let(:backend) { double('backend').as_null_object } let(:project) { create(:project) } let(:repository) { project.repository } - let(:namespace) { "project:#{project.id}" } + let(:namespace) { "#{repository.full_path}:#{project.id}" } let(:cache) { described_class.new(repository, backend: backend) } describe '#cache_key' do subject { cache.cache_key(:foo) } it 'includes the namespace' do - expect(subject).to eq "#{namespace}:foo" + expect(subject).to eq "foo:#{namespace}" end context 'with a given namespace' do @@ -22,7 +22,7 @@ describe Gitlab::RepositoryCache do end it 'includes the full namespace' do - expect(subject).to eq "#{namespace}:#{extra_namespace}:foo" + expect(subject).to eq "foo:#{namespace}:#{extra_namespace}" end end end @@ -30,21 +30,21 @@ describe Gitlab::RepositoryCache do describe '#expire' do it 'expires the given key from the cache' do cache.expire(:foo) - expect(backend).to have_received(:delete).with("#{namespace}:foo") + expect(backend).to have_received(:delete).with("foo:#{namespace}") end end describe '#fetch' do it 'fetches the given key from the cache' do cache.fetch(:bar) - expect(backend).to have_received(:fetch).with("#{namespace}:bar") + expect(backend).to have_received(:fetch).with("bar:#{namespace}") end it 'accepts a block' do p = -> {} cache.fetch(:baz, &p) - expect(backend).to have_received(:fetch).with("#{namespace}:baz", &p) + expect(backend).to have_received(:fetch).with("baz:#{namespace}", &p) end end @@ -67,7 +67,7 @@ describe Gitlab::RepositoryCache do end it 'caches the value' do - expect(backend).to receive(:write).with("#{namespace}:#{key}", true) + expect(backend).to receive(:write).with("#{key}:#{namespace}", true) cache.fetch_without_caching_false(key) { true } end @@ -83,7 +83,7 @@ describe Gitlab::RepositoryCache do end it 'does not cache the value' do - expect(backend).not_to receive(:write).with("#{namespace}:#{key}", true) + expect(backend).not_to receive(:write).with("#{key}:#{namespace}", true) cache.fetch_without_caching_false(key, &p) end @@ -92,7 +92,7 @@ describe Gitlab::RepositoryCache do context 'when the cached value is truthy' do before do - backend.write("#{namespace}:#{key}", true) + backend.write("#{key}:#{namespace}", true) end it 'returns the cached value' do @@ -116,7 +116,7 @@ describe Gitlab::RepositoryCache do context 'when the cached value is falsey' do before do - backend.write("#{namespace}:#{key}", false) + backend.write("#{key}:#{namespace}", false) end it 'returns the result of the block' do @@ -126,7 +126,7 @@ describe Gitlab::RepositoryCache do end it 'writes the truthy value to the cache' do - expect(backend).to receive(:write).with("#{namespace}:#{key}", 'block result') + expect(backend).to receive(:write).with("#{key}:#{namespace}", 'block result') cache.fetch_without_caching_false(key) { 'block result' } end diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb index 8a28ad0e597..fd443cc1f71 100644 --- a/spec/lib/gitlab/request_context_spec.rb +++ b/spec/lib/gitlab/request_context_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::RequestContext do let(:ip) { '192.168.1.11' } before do - allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip) + allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return(ip) described_class.new(app).call(env) end diff --git a/spec/lib/omni_auth/strategies/jwt_spec.rb b/spec/lib/omni_auth/strategies/jwt_spec.rb index c2e2db27362..c1eaf0bb0bf 100644 --- a/spec/lib/omni_auth/strategies/jwt_spec.rb +++ b/spec/lib/omni_auth/strategies/jwt_spec.rb @@ -25,6 +25,8 @@ describe OmniAuth::Strategies::Jwt do subject.options[:secret] = secret subject.options[:algorithm] = algorithm + # We use Rack::Request instead of ActionDispatch::Request because + # Rack::Test::Methods enables testing of this module. expect_next_instance_of(Rack::Request) do |rack_request| expect(rack_request).to receive(:params).and_return('jwt' => payload) end diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb new file mode 100644 index 00000000000..b36be0fd9c1 --- /dev/null +++ b/spec/lib/sentry/client_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Sentry::Client do + let(:issue_status) { 'unresolved' } + let(:limit) { 20 } + let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' } + let(:token) { 'test-token' } + + let(:sample_response) do + Gitlab::Utils.deep_indifferent_access( + JSON.parse(File.read(Rails.root.join('spec/fixtures/sentry/issues_sample_response.json'))) + ) + end + + subject(:client) { described_class.new(sentry_url, token) } + + describe '#list_issues' do + subject { client.list_issues(issue_status: issue_status, limit: limit) } + + before do + stub_sentry_request(sentry_url + '/issues/?limit=20&query=is:unresolved', body: sample_response) + end + + it 'returns objects of type ErrorTracking::Error' do + expect(subject.length).to eq(1) + expect(subject[0]).to be_a(Gitlab::ErrorTracking::Error) + end + + context 'error object created from sentry response' do + using RSpec::Parameterized::TableSyntax + + where(:error_object, :sentry_response) do + :id | :id + :first_seen | :firstSeen + :last_seen | :lastSeen + :title | :title + :type | :type + :user_count | :userCount + :count | :count + :message | [:metadata, :value] + :culprit | :culprit + :short_id | :shortId + :status | :status + :frequency | [:stats, '24h'] + :project_id | [:project, :id] + :project_name | [:project, :name] + :project_slug | [:project, :slug] + end + + with_them do + it { expect(subject[0].public_send(error_object)).to eq(sample_response[0].dig(*sentry_response)) } + end + + context 'external_url' do + it 'is constructed correctly' do + expect(subject[0].external_url).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/11') + end + end + end + + context 'redirects' do + let(:redirect_to) { 'https://redirected.example.com' } + let(:other_url) { 'https://other.example.org' } + + let!(:redirected_req_stub) { stub_sentry_request(other_url) } + + let!(:redirect_req_stub) do + stub_sentry_request( + sentry_url + '/issues/?limit=20&query=is:unresolved', + status: 302, + headers: { location: redirect_to } + ) + end + + it 'does not follow redirects' do + expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response error: 302') + expect(redirect_req_stub).to have_been_requested + expect(redirected_req_stub).not_to have_been_requested + end + end + + # Sentry API returns 404 if there are extra slashes in the URL! + context 'extra slashes in URL' do + let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects//sentry-org/sentry-project/' } + let(:client) { described_class.new(sentry_url, token) } + + let!(:valid_req_stub) do + stub_sentry_request( + 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \ + 'issues/?limit=20&query=is:unresolved' + ) + end + + it 'removes extra slashes in api url' do + expect(Gitlab::HTTP).to receive(:get).with( + URI('https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/issues/'), + anything + ).and_call_original + + client.list_issues(issue_status: issue_status, limit: limit) + + expect(valid_req_stub).to have_been_requested + end + end + end + + private + + def stub_sentry_request(url, body: {}, status: 200, headers: {}) + WebMock.stub_request(:get, url) + .to_return( + status: status, + headers: { 'Content-Type' => 'application/json' }.merge(headers), + body: body.to_json + ) + end +end diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb index 83f29718eda..2f8ab21d4b2 100644 --- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb +++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb @@ -3,33 +3,106 @@ require 'spec_helper' describe ErrorTracking::ProjectErrorTrackingSetting do + include ReactiveCachingHelpers + set(:project) { create(:project) } + subject { create(:project_error_tracking_setting, project: project) } + describe 'Associations' do it { is_expected.to belong_to(:project) } end describe 'Validations' do - subject { create(:project_error_tracking_setting, project: project) } - context 'when api_url is over 255 chars' do - before do + it 'fails validation' do subject.api_url = 'https://' + 'a' * 250 - end - it 'fails validation' do expect(subject).not_to be_valid expect(subject.errors.messages[:api_url]).to include('is too long (maximum is 255 characters)') end end context 'With unsafe url' do - let(:project_error_tracking_setting) { create(:project_error_tracking_setting, project: project) } - it 'fails validation' do - project_error_tracking_setting.api_url = "https://replaceme.com/'><script>alert(document.cookie)</script>" + subject.api_url = "https://replaceme.com/'><script>alert(document.cookie)</script>" + + expect(subject).not_to be_valid + end + end + + context 'URL path' do + it 'fails validation with wrong path' do + subject.api_url = 'http://gitlab.com/project1/something' + + expect(subject).not_to be_valid + expect(subject.errors.messages[:api_url]).to include('path needs to start with /api/0/projects') + end + + it 'passes validation with correct path' do + subject.api_url = 'http://gitlab.com/api/0/projects/project1/something' + + expect(subject).to be_valid + end + end + end + + describe '#sentry_external_url' do + let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' } + + before do + subject.api_url = sentry_url + end + + it 'returns the correct url' do + expect(subject.class).to receive(:extract_sentry_external_url).with(sentry_url).and_call_original + + result = subject.sentry_external_url + + expect(result).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project') + end + end + + describe '#sentry_client' do + it 'returns sentry client' do + expect(subject.sentry_client).to be_a(Sentry::Client) + end + end + + describe '#list_sentry_issues' do + let(:issues) { [:list, :of, :issues] } + + let(:opts) do + { issue_status: 'unresolved', limit: 10 } + end + + let(:result) do + subject.list_sentry_issues(**opts) + end + + context 'when cached' do + let(:sentry_client) { spy(:sentry_client) } + + before do + stub_reactive_cache(subject, issues, opts) + synchronous_reactive_cache(subject) + + expect(subject).to receive(:sentry_client).and_return(sentry_client) + end + + it 'returns cached issues' do + expect(sentry_client).to receive(:list_issues).with(opts) + .and_return(issues) + + expect(result).to eq(issues: issues) + end + end + + context 'when not cached' do + it 'returns nil' do + expect(subject).not_to receive(:sentry_client) - expect(project_error_tracking_setting).not_to be_valid + expect(result).to be_nil end end end diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 43a0ed99296..64b4efca43a 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -205,7 +205,7 @@ describe TeamcityService, :use_clean_rails_memory_store_caching do end def stub_request(status: 200, body: nil, build_status: 'success') - teamcity_full_url = 'http://gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,number:123' + teamcity_full_url = 'http://gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,revision:123' auth = %w(mic password) body ||= %Q({"build":{"status":"#{build_status}","id":"666"}}) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d1ab0bdba29..3044150bca8 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1105,13 +1105,13 @@ describe Project do describe '#pipeline_for' do let(:project) { create(:project, :repository) } - let!(:pipeline) { create_pipeline } + let!(:pipeline) { create_pipeline(project) } shared_examples 'giving the correct pipeline' do it { is_expected.to eq(pipeline) } context 'return latest' do - let!(:pipeline2) { create_pipeline } + let!(:pipeline2) { create_pipeline(project) } it { is_expected.to eq(pipeline2) } end @@ -1128,13 +1128,6 @@ describe Project do it_behaves_like 'giving the correct pipeline' end - - def create_pipeline - create(:ci_pipeline, - project: project, - ref: 'master', - sha: project.commit('master').sha) - end end describe '#builds_enabled' do @@ -1922,38 +1915,21 @@ describe Project do end end - describe '#latest_successful_builds_for and #latest_successful_build_for' do - def create_pipeline(status = 'success') - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: project.default_branch, - status: status) - end - - def create_build(new_pipeline = pipeline, name = 'test') - create(:ci_build, :success, :artifacts, - pipeline: new_pipeline, - status: new_pipeline.status, - name: name) - end - + describe '#latest_successful_build_for' do let(:project) { create(:project, :repository) } - let(:pipeline) { create_pipeline } + let(:pipeline) { create_pipeline(project) } context 'with many builds' do it 'gives the latest builds from latest pipeline' do - pipeline1 = create_pipeline - pipeline2 = create_pipeline + pipeline1 = create_pipeline(project) + pipeline2 = create_pipeline(project) create_build(pipeline1, 'test') create_build(pipeline1, 'test2') build1_p2 = create_build(pipeline2, 'test') - build2_p2 = create_build(pipeline2, 'test2') + create_build(pipeline2, 'test2') - latest_builds = project.latest_successful_builds_for - single_build = project.latest_successful_build_for(build1_p2.name) - - expect(latest_builds).to contain_exactly(build2_p2, build1_p2) - expect(single_build).to eq(build1_p2) + expect(project.latest_successful_build_for(build1_p2.name)) + .to eq(build1_p2) end end @@ -1962,53 +1938,115 @@ describe Project do context 'standalone pipeline' do it 'returns builds for ref for default_branch' do - builds = project.latest_successful_builds_for - single_build = project.latest_successful_build_for(build.name) + expect(project.latest_successful_build_for(build.name)) + .to eq(build) + end - expect(builds).to contain_exactly(build) - expect(single_build).to eq(build) + it 'returns empty relation if the build cannot be found' do + expect(project.latest_successful_build_for('TAIL')) + .to be_nil end + end - it 'returns empty relation if the build cannot be found for #latest_successful_builds_for' do - builds = project.latest_successful_builds_for('TAIL') + context 'with some pending pipeline' do + before do + create_build(create_pipeline(project, 'pending')) + end - expect(builds).to be_kind_of(ActiveRecord::Relation) - expect(builds).to be_empty + it 'gives the latest build from latest pipeline' do + expect(project.latest_successful_build_for(build.name)) + .to eq(build) end + end + end - it 'returns exception if the build cannot be found for #latest_successful_build_for' do - expect { project.latest_successful_build_for(build.name, 'TAIL') }.to raise_error(ActiveRecord::RecordNotFound) + context 'with pending pipeline' do + it 'returns empty relation' do + pipeline.update(status: 'pending') + pending_build = create_build(pipeline) + + expect(project.latest_successful_build_for(pending_build.name)).to be_nil + end + end + end + + describe '#latest_successful_build_for!' do + let(:project) { create(:project, :repository) } + let(:pipeline) { create_pipeline(project) } + + context 'with many builds' do + it 'gives the latest builds from latest pipeline' do + pipeline1 = create_pipeline(project) + pipeline2 = create_pipeline(project) + create_build(pipeline1, 'test') + create_build(pipeline1, 'test2') + build1_p2 = create_build(pipeline2, 'test') + create_build(pipeline2, 'test2') + + expect(project.latest_successful_build_for(build1_p2.name)) + .to eq(build1_p2) + end + end + + context 'with succeeded pipeline' do + let!(:build) { create_build } + + context 'standalone pipeline' do + it 'returns builds for ref for default_branch' do + expect(project.latest_successful_build_for!(build.name)) + .to eq(build) + end + + it 'returns exception if the build cannot be found' do + expect { project.latest_successful_build_for!(build.name, 'TAIL') } + .to raise_error(ActiveRecord::RecordNotFound) end end context 'with some pending pipeline' do before do - create_build(create_pipeline('pending')) + create_build(create_pipeline(project, 'pending')) end it 'gives the latest build from latest pipeline' do - latest_builds = project.latest_successful_builds_for - last_single_build = project.latest_successful_build_for(build.name) - - expect(latest_builds).to contain_exactly(build) - expect(last_single_build).to eq(build) + expect(project.latest_successful_build_for!(build.name)) + .to eq(build) end end end context 'with pending pipeline' do - before do + it 'returns empty relation' do pipeline.update(status: 'pending') - create_build(pipeline) + pending_build = create_build(pipeline) + + expect { project.latest_successful_build_for!(pending_build.name) } + .to raise_error(ActiveRecord::RecordNotFound) end + end + end - it 'returns empty relation' do - builds = project.latest_successful_builds_for + describe '#get_build' do + let(:project) { create(:project, :repository) } + let(:ci_pipeline) { create(:ci_pipeline, project: project) } + + context 'when build exists' do + context 'build is associated with project' do + let(:build) { create(:ci_build, :success, pipeline: ci_pipeline) } + + it { expect(project.get_build(build.id)).to eq(build) } + end - expect(builds).to be_kind_of(ActiveRecord::Relation) - expect(builds).to be_empty + context 'build is not associated with project' do + let(:build) { create(:ci_build, :success) } + + it { expect(project.get_build(build.id)).to be_nil } end end + + context 'build does not exists' do + it { expect(project.get_build(rand 100)).to be_nil } + end end describe '#import_status' do @@ -4390,4 +4428,18 @@ describe Project do def rugged_config rugged_repo(project.repository).config end + + def create_pipeline(project, status = 'success') + create(:ci_pipeline, project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) + end + + def create_build(new_pipeline = pipeline, name = 'test') + create(:ci_build, :success, :artifacts, + pipeline: new_pipeline, + status: new_pipeline.status, + name: name) + end end diff --git a/spec/models/releases/link_spec.rb b/spec/models/releases/link_spec.rb index e88c186cbb8..06ed1438688 100644 --- a/spec/models/releases/link_spec.rb +++ b/spec/models/releases/link_spec.rb @@ -31,6 +31,16 @@ describe Releases::Link do end.to raise_error(ActiveRecord::RecordInvalid) end end + + context 'when duplicate url is added to a release' do + let!(:link) { create(:release_link, url: 'http://gitlab.com', release: release) } + + it 'raises an error' do + expect do + create(:release_link, url: 'http://gitlab.com', release: release) + end.to raise_error(ActiveRecord::RecordInvalid) + end + end end describe '.sorted' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 2063b4bbe75..ac5874fd0f7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2400,22 +2400,4 @@ describe Repository do repository.merge_base('master', 'fix') end end - - describe '#cache' do - subject(:cache) { repository.send(:cache) } - - it 'returns a RepositoryCache' do - expect(subject).to be_kind_of Gitlab::RepositoryCache - end - - it 'when is_wiki it includes wiki as part of key' do - allow(repository).to receive(:is_wiki) { true } - - expect(subject.namespace).to include('wiki') - end - - it 'when is_wiki is false extra_namespace is nil' do - expect(subject.namespace).not_to include('wiki') - end - end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 9cb20854f6e..2a4030de998 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -24,7 +24,7 @@ describe ProjectPolicy do download_code fork_project create_project_snippet update_issue admin_issue admin_label admin_list read_commit_status read_build read_container_image read_pipeline read_environment read_deployment - read_merge_request download_wiki_code + read_merge_request download_wiki_code read_sentry_issue ] end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index a0aee937185..9b32dc78274 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -183,14 +183,15 @@ describe API::Files do get api(url, current_user), params: params expect(response).to have_gitlab_http_status(200) + expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true" end - it 'forces attachment content disposition' do + it 'sets inline content disposition by default' do url = route(file_path) + "/raw" get api(url, current_user), params: params - expect(headers['Content-Disposition']).to eq('attachment; filename="popen.rb"') + expect(headers['Content-Disposition']).to eq('inline; filename="popen.rb"') end context 'when mandatory params are not given' do diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index eb002de62a2..52599db9a9e 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -456,8 +456,8 @@ describe API::Pipelines do expect(json_response['message']).to eq '404 Not found' end - it 'logs an audit event' do - expect { delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner) }.to change { SecurityEvent.count }.by(1) + it 'does not log an audit event' do + expect { delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner) }.not_to change { SecurityEvent.count } end context 'when the pipeline has jobs' do diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index b6b57803a6a..0adc95cfbeb 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -166,12 +166,13 @@ describe API::Repositories do get api(route, current_user) expect(response).to have_gitlab_http_status(200) + expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true" end - it 'forces attachment content disposition' do + it 'sets inline content disposition by default' do get api(route, current_user) - expect(headers['Content-Disposition']).to eq 'attachment' + expect(headers['Content-Disposition']).to eq 'inline' end context 'when sha does not exist' do diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 939e870ec53..5b625fd47be 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -387,7 +387,7 @@ describe 'Git HTTP requests' do it "responds with status 401" do expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true) - allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4') + allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return('1.2.3.4') clone_get(path, env) @@ -548,7 +548,7 @@ describe 'Git HTTP requests' do maxretry = options[:maxretry] - 1 ip = '1.2.3.4' - allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip) + allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return(ip) Rack::Attack::Allow2Ban.reset(ip, options) maxretry.times.each do diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb index 097daf67feb..d896f990470 100644 --- a/spec/services/ci/destroy_pipeline_service_spec.rb +++ b/spec/services/ci/destroy_pipeline_service_spec.rb @@ -17,8 +17,8 @@ describe ::Ci::DestroyPipelineService do expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'logs an audit event' do - expect { subject }.to change { SecurityEvent.count }.by(1) + it 'does not log an audit event' do + expect { subject }.not_to change { SecurityEvent.count } end context 'when the pipeline has jobs' do diff --git a/spec/services/error_tracking/list_issues_service_spec.rb b/spec/services/error_tracking/list_issues_service_spec.rb new file mode 100644 index 00000000000..d9dab1d705c --- /dev/null +++ b/spec/services/error_tracking/list_issues_service_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ErrorTracking::ListIssuesService do + set(:user) { create(:user) } + set(:project) { create(:project) } + + let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' } + let(:token) { 'test-token' } + let(:result) { subject.execute } + + let(:error_tracking_setting) do + create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project) + end + + subject { described_class.new(project, user) } + + before do + expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting) + + project.add_reporter(user) + end + + describe '#execute' do + context 'with authorized user' do + context 'when list_sentry_issues returns issues' do + let(:issues) { [:list, :of, :issues] } + + before do + expect(error_tracking_setting) + .to receive(:list_sentry_issues).and_return(issues: issues) + end + + it 'returns the issues' do + expect(result).to eq(status: :success, issues: issues) + end + end + + context 'when list_sentry_issues returns nil' do + before do + expect(error_tracking_setting) + .to receive(:list_sentry_issues).and_return(nil) + end + + it 'result is not ready' do + expect(result).to eq( + status: :error, http_status: :no_content, message: 'not ready') + end + end + end + + context 'with unauthorized user' do + let(:unauthorized_user) { create(:user) } + + subject { described_class.new(project, unauthorized_user) } + + it 'returns error' do + result = subject.execute + + expect(result).to include(status: :error, message: 'access denied') + end + end + + context 'with error tracking disabled' do + before do + error_tracking_setting.enabled = false + end + + it 'raises error' do + result = subject.execute + + expect(result).to include(status: :error, message: 'not enabled') + end + end + end + + describe '#sentry_external_url' do + let(:external_url) { 'https://sentrytest.gitlab.com/sentry-org/sentry-project' } + + it 'calls ErrorTracking::ProjectErrorTrackingSetting' do + expect(error_tracking_setting).to receive(:sentry_external_url).and_call_original + + subject.external_url + end + end +end diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb index 731be907453..6afae3da80c 100644 --- a/spec/services/projects/operations/update_service_spec.rb +++ b/spec/services/projects/operations/update_service_spec.rb @@ -17,7 +17,7 @@ describe Projects::Operations::UpdateService do { error_tracking_setting_attributes: { enabled: false, - api_url: 'http://url', + api_url: 'http://gitlab.com/api/0/projects/org/project', token: 'token' } } @@ -32,7 +32,7 @@ describe Projects::Operations::UpdateService do project.reload expect(project.error_tracking_setting).not_to be_enabled - expect(project.error_tracking_setting.api_url).to eq('http://url') + expect(project.error_tracking_setting.api_url).to eq('http://gitlab.com/api/0/projects/org/project') expect(project.error_tracking_setting.token).to eq('token') end end @@ -42,7 +42,7 @@ describe Projects::Operations::UpdateService do { error_tracking_setting_attributes: { enabled: true, - api_url: 'http://url', + api_url: 'http://gitlab.com/api/0/projects/org/project', token: 'token' } } @@ -52,7 +52,7 @@ describe Projects::Operations::UpdateService do expect(result[:status]).to eq(:success) expect(project.error_tracking_setting).to be_enabled - expect(project.error_tracking_setting.api_url).to eq('http://url') + expect(project.error_tracking_setting.api_url).to eq('http://gitlab.com/api/0/projects/org/project') expect(project.error_tracking_setting.token).to eq('token') end end diff --git a/spec/simplecov_env.rb b/spec/simplecov_env.rb index 25ddf932d42..82236bb4201 100644 --- a/spec/simplecov_env.rb +++ b/spec/simplecov_env.rb @@ -1,5 +1,6 @@ require 'simplecov' require 'active_support/core_ext/numeric/time' +require_relative '../lib/gitlab/utils' module SimpleCovEnv extend self @@ -16,8 +17,9 @@ module SimpleCovEnv def configure_job SimpleCov.configure do if ENV['CI_JOB_NAME'] - coverage_dir "coverage/#{ENV['CI_JOB_NAME']}" - command_name ENV['CI_JOB_NAME'] + job_name = Gitlab::Utils.slugify(ENV['CI_JOB_NAME']) + coverage_dir "coverage/#{job_name}" + command_name job_name end if ENV['CI'] diff --git a/yarn.lock b/yarn.lock index 7bf59f9de94..40a8594ee6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,27 +9,27 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.1.2.tgz#f8d2a9ceb6832887329a7b60f9d035791400ba4e" - integrity sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw== +"@babel/core@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687" + integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.1.2" - "@babel/helpers" "^7.1.2" - "@babel/parser" "^7.1.2" - "@babel/template" "^7.1.2" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.1.2" + "@babel/generator" "^7.2.2" + "@babel/helpers" "^7.2.0" + "@babel/parser" "^7.2.2" + "@babel/template" "^7.2.2" + "@babel/traverse" "^7.2.2" + "@babel/types" "^7.2.2" convert-source-map "^1.1.0" - debug "^3.1.0" - json5 "^0.5.0" + debug "^4.1.0" + json5 "^2.1.0" lodash "^4.17.10" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.0.0", "@babel/generator@^7.1.2": +"@babel/generator@^7.0.0": version "7.1.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.2.tgz#fde75c072575ce7abbd97322e8fef5bae67e4630" integrity sha512-70A9HWLS/1RHk3Ck8tNHKxOoKQuSKocYgwDN85Pyl/RBduss6AKxUR7RIZ/lzduQMSYfWEM4DDBu6A+XGbkFig== @@ -40,6 +40,17 @@ source-map "^0.5.0" trim-right "^1.0.1" +"@babel/generator@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.2.tgz#18c816c70962640eab42fe8cae5f3947a5c65ccc" + integrity sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg== + dependencies: + "@babel/types" "^7.2.2" + jsesc "^2.5.1" + lodash "^4.17.10" + source-map "^0.5.0" + trim-right "^1.0.1" + "@babel/helper-annotate-as-pure@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" @@ -64,6 +75,17 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-create-class-features-plugin@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.2.3.tgz#f6e719abb90cb7f4a69591e35fd5eb89047c4a7c" + integrity sha512-xO/3Gn+2C7/eOUeb0VRnSP1+yvWHNxlpAot1eMhtoKDCN7POsyQP5excuT5UsV5daHxMWBeIIOeI5cmB8vMRgQ== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.2.3" + "@babel/helper-define-map@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c" @@ -170,6 +192,16 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-replace-supers@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz#19970020cf22677d62b3a689561dbd9644d8c5e5" + integrity sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/traverse" "^7.2.3" + "@babel/types" "^7.0.0" + "@babel/helper-simple-access@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" @@ -195,14 +227,14 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helpers@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.1.2.tgz#ab752e8c35ef7d39987df4e8586c63b8846234b5" - integrity sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA== +"@babel/helpers@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.2.0.tgz#8335f3140f3144270dc63c4732a4f8b0a50b7a21" + integrity sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A== dependencies: "@babel/template" "^7.1.2" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.1.2" + "@babel/traverse" "^7.1.5" + "@babel/types" "^7.2.0" "@babel/highlight@^7.0.0": version "7.0.0" @@ -218,144 +250,146 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409" integrity sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ== -"@babel/plugin-proposal-async-generator-functions@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz#41c1a702e10081456e23a7b74d891922dd1bb6ce" - integrity sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew== +"@babel/parser@^7.2.2", "@babel/parser@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" + integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" - "@babel/plugin-syntax-async-generators" "^7.0.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" -"@babel/plugin-proposal-class-properties@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.1.0.tgz#9af01856b1241db60ec8838d84691aa0bd1e8df4" - integrity sha512-/PCJWN+CKt5v1xcGn4vnuu13QDoV+P7NcICP44BoonAJoPSGwVkgrXihFIQGiEjjPlUDBIw1cM7wYFLARS2/hw== +"@babel/plugin-proposal-class-properties@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.2.3.tgz#c9e1294363b346cff333007a92080f3203698461" + integrity sha512-FVuQngLoN2iDrpW7LmhPZ2sO4DJxf35FOcwidwB9Ru9tMvI5URthnkVHuG14IStV+TzkMTyLMoOUlSTtrdVwqw== dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-member-expression-to-functions" "^7.0.0" - "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-create-class-features-plugin" "^7.2.3" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.1.0" - "@babel/plugin-syntax-class-properties" "^7.0.0" -"@babel/plugin-proposal-json-strings@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz#3b4d7b5cf51e1f2e70f52351d28d44fc2970d01e" - integrity sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q== +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" + integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz#9a17b547f64d0676b6c9cecd4edf74a82ab85e7e" - integrity sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw== +"@babel/plugin-proposal-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz#88f5fec3e7ad019014c97f7ee3c992f0adbf7fb8" + integrity sha512-1L5mWLSvR76XYUQJXkd/EEQgjq8HHRP6lQuZTTg0VA4tTGPpGemmCdAfQIz1rzEuWAm+ecP8PyyEm30jC1eQCg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" -"@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz#b610d928fe551ff7117d42c8bb410eec312a6425" - integrity sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw== +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" -"@babel/plugin-proposal-unicode-property-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz#498b39cd72536cd7c4b26177d030226eba08cd33" - integrity sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ== +"@babel/plugin-proposal-private-methods@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.2.3.tgz#aff0f5436df2c4365938c0309d551984e42c290c" + integrity sha512-jehrt1/TuLdLeBAVEv1VmTCNJcvSj+5Ozp7l21DN19Ylo0ATxpZ5bDk8i4WS9Ngvdgk/YTcqJCTp3uY2lwQoxw== dependencies: + "@babel/helper-create-class-features-plugin" "^7.2.3" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" - regexpu-core "^4.2.0" -"@babel/plugin-syntax-async-generators@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz#bf0891dcdbf59558359d0c626fdc9490e20bc13c" - integrity sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA== +"@babel/plugin-proposal-unicode-property-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz#abe7281fe46c95ddc143a65e5358647792039520" + integrity sha512-LvRVYb7kikuOtIoUeWTkOxQEV1kYvL5B6U3iWEGCzPNRus1MzJweFqORTj+0jkxozkTSYNJozPOddxmqdqsRpw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + regexpu-core "^4.2.0" -"@babel/plugin-syntax-class-properties@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz#e051af5d300cbfbcec4a7476e37a803489881634" - integrity sha512-cR12g0Qzn4sgkjrbrzWy2GE7m9vMl/sFkqZ3gIpAQdrvPDnLM8180i+ANDFIXfjHo9aqp0ccJlQ0QNZcFUbf9w== +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-dynamic-import@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0.tgz#6dfb7d8b6c3be14ce952962f658f3b7eb54c33ee" - integrity sha512-Gt9xNyRrCHCiyX/ZxDGOcBnlJl0I3IWicpZRC4CdC0P5a/I07Ya2OAMEBU+J7GmRFVmIetqEYRko6QYRuKOESw== +"@babel/plugin-syntax-dynamic-import@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" + integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-import-meta@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.0.0.tgz#ca946b73216c29c39a55ef2d739097fee8a85d69" - integrity sha512-FEoGvhXVAiWzpDjyZIlBGzKyNk/lnRPy7aPke3PjVkiAY0QFsvFfkjUg5diRwVfowBA8SJqvFt0ZoXNSjl70hQ== +"@babel/plugin-syntax-import-meta@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.2.0.tgz#2333ef4b875553a3bcd1e93f8ebc09f5b9213a40" + integrity sha512-Hq6kFSZD7+PHkmBN8bCpHR6J8QEoCuEV/B38AIQscYjgMZkGlXB7cHNFzP5jR4RCh5545yP1ujHdmO7hAgKtBA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-json-strings@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz#0d259a68090e15b383ce3710e01d5b23f3770cbd" - integrity sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA== +"@babel/plugin-syntax-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" + integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz#37d8fbcaf216bd658ea1aebbeb8b75e88ebc549b" - integrity sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw== +"@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz#886f72008b3a8b185977f7cb70713b45e51ee475" - integrity sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw== +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz#a6c14875848c68a3b4b3163a486535ef25c7e749" - integrity sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w== +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" + integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-async-to-generator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz#109e036496c51dd65857e16acab3bafdf3c57811" - integrity sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g== +"@babel/plugin-transform-async-to-generator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz#68b8a438663e88519e65b776f8938f3445b1a2ff" + integrity sha512-CEHzg4g5UraReozI9D4fblBYABs7IM6UerAVG7EJVrTLC5keh00aEuLUT+O40+mJCEzaXkYfTCUKIyeDfMOFFQ== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" -"@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz#482b3f75103927e37288b3b67b65f848e2aa0d07" - integrity sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ== +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" + integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoping@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0.tgz#1745075edffd7cdaf69fab2fb6f9694424b7e9bc" - integrity sha512-GWEMCrmHQcYWISilUrk9GDqH4enf3UmhOEbNbNrlNAX1ssH3MsS1xLOS6rdjRVPgA7XXVPn87tRkdTEoA/dxEg== +"@babel/plugin-transform-block-scoping@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz#f17c49d91eedbcdf5dd50597d16f5f2f770132d4" + integrity sha512-vDTgf19ZEV6mx35yiPJe4fS02mPQUUcBNwWQSZFXSzTSbsJFQvHt7DqyS3LK8oOWALFOsJ+8bbqBgkirZteD5Q== dependencies: "@babel/helper-plugin-utils" "^7.0.0" lodash "^4.17.10" -"@babel/plugin-transform-classes@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz#ab3f8a564361800cbc8ab1ca6f21108038432249" - integrity sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg== +"@babel/plugin-transform-classes@^7.2.0": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.2.tgz#6c90542f210ee975aa2aa8c8b5af7fa73a126953" + integrity sha512-gEZvgTy1VtcDOaQty1l10T3jQmJKlNVxLDCs+3rCVPr6nMkODLELxViq5X9l+rfxbie3XrfrMCYYY6eX3aOcOQ== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-define-map" "^7.1.0" @@ -366,95 +400,95 @@ "@babel/helper-split-export-declaration" "^7.0.0" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz#2fbb8900cd3e8258f2a2ede909b90e7556185e31" - integrity sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA== +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" + integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-destructuring@^7.0.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.2.tgz#5fa77d473f5a0a3f5266ad7ce2e8c995a164d60a" - integrity sha512-cvToXvp/OsYxtEn57XJu9BvsGSEYjAh9UeUuXpoi7x6QHB7YdWyQ4lRU/q0Fu1IJNT0o0u4FQ1DMQBzJ8/8vZg== +"@babel/plugin-transform-destructuring@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.2.0.tgz#e75269b4b7889ec3a332cd0d0c8cff8fed0dc6f3" + integrity sha512-coVO2Ayv7g0qdDbrNiadE4bU7lvCd9H539m2gMknyVjjMdwF/iCOM7R+E8PkntoqLkltO0rk+3axhpp/0v68VQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-dotall-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz#73a24da69bc3c370251f43a3d048198546115e58" - integrity sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig== +"@babel/plugin-transform-dotall-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz#f0aabb93d120a8ac61e925ea0ba440812dbe0e49" + integrity sha512-sKxnyHfizweTgKZf7XsXu/CNupKhzijptfTM+bozonIuyVrLWVUvYjE2bhuSBML8VQeMxq4Mm63Q9qvcvUcciQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" regexpu-core "^4.1.3" -"@babel/plugin-transform-duplicate-keys@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz#a0601e580991e7cace080e4cf919cfd58da74e86" - integrity sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ== +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3" + integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-exponentiation-operator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz#9c34c2ee7fd77e02779cfa37e403a2e1003ccc73" - integrity sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ== +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-for-of@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz#f2ba4eadb83bd17dc3c7e9b30f4707365e1c3e39" - integrity sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA== +"@babel/plugin-transform-for-of@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz#ab7468befa80f764bb03d3cb5eef8cc998e1cad9" + integrity sha512-Kz7Mt0SsV2tQk6jG5bBv5phVbkd0gd27SgYD4hH1aLMJRchM0dzHaXvrWhVZ+WxAlDoAKZ7Uy3jVTW2mKXQ1WQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz#29c5550d5c46208e7f730516d41eeddd4affadbb" - integrity sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg== +"@babel/plugin-transform-function-name@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz#f7930362829ff99a3174c39f0afcc024ef59731a" + integrity sha512-kWgksow9lHdvBC2Z4mxTsvc7YdY7w/V6B2vy9cTIPtLEE9NhwoWivaxdNM/S37elu5bqlLP/qOY906LukO9lkQ== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-literals@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz#2aec1d29cdd24c407359c930cdd89e914ee8ff86" - integrity sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA== +"@babel/plugin-transform-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" + integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-amd@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz#f9e0a7072c12e296079b5a59f408ff5b97bf86a8" - integrity sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A== +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6" + integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz#0a9d86451cbbfb29bd15186306897c67f6f9a05c" - integrity sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA== +"@babel/plugin-transform-modules-commonjs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz#c4f1933f5991d5145e9cfad1dfd848ea1727f404" + integrity sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" -"@babel/plugin-transform-modules-systemjs@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0.tgz#8873d876d4fee23209decc4d1feab8f198cf2df4" - integrity sha512-8EDKMAsitLkiF/D4Zhe9CHEE2XLh4bfLbb9/Zf3FgXYQOZyZYyg7EAel/aT2A7bHv62jwHf09q2KU/oEexr83g== +"@babel/plugin-transform-modules-systemjs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz#912bfe9e5ff982924c81d0937c92d24994bb9068" + integrity sha512-aYJwpAhoK9a+1+O625WIjvMY11wkB/ok0WClVwmeo3mCjcNRjt+/8gHWrB5i+00mUju0gWsBkQnPpdvQ7PImmQ== dependencies: "@babel/helper-hoist-variables" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-umd@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz#a29a7d85d6f28c3561c33964442257cc6a21f2a8" - integrity sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig== +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" + integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -466,18 +500,18 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-object-super@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz#b1ae194a054b826d8d4ba7ca91486d4ada0f91bb" - integrity sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw== +"@babel/plugin-transform-object-super@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" + integrity sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-replace-supers" "^7.1.0" -"@babel/plugin-transform-parameters@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz#44f492f9d618c9124026e62301c296bf606a7aed" - integrity sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw== +"@babel/plugin-transform-parameters@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz#0d5ad15dc805e2ea866df4dd6682bfe76d1408c2" + integrity sha512-kB9+hhUidIgUoBQ0MsxMewhzr8i60nMa2KgeJKQWYrqQpqcBYtnpR+JgkadZVZoaEZ/eKu9mclFaVwhRpLNSzA== dependencies: "@babel/helper-call-delegate" "^7.1.0" "@babel/helper-get-function-arity" "^7.0.0" @@ -490,95 +524,95 @@ dependencies: regenerator-transform "^0.13.3" -"@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz#85f8af592dcc07647541a0350e8c95c7bf419d15" - integrity sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw== +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" + integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz#93583ce48dd8c85e53f3a46056c856e4af30b49b" - integrity sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ== +"@babel/plugin-transform-spread@^7.2.0": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" + integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz#30a9d64ac2ab46eec087b8530535becd90e73366" - integrity sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw== +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" + integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" -"@babel/plugin-transform-template-literals@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz#084f1952efe5b153ddae69eb8945f882c7a97c65" - integrity sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg== +"@babel/plugin-transform-template-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz#d87ed01b8eaac7a92473f608c97c089de2ba1e5b" + integrity sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-typeof-symbol@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz#4dcf1e52e943e5267b7313bff347fdbe0f81cec9" - integrity sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg== +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" + integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz#c6780e5b1863a76fe792d90eded9fcd5b51d68fc" - integrity sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw== +"@babel/plugin-transform-unicode-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b" + integrity sha512-m48Y0lMhrbXEJnVUaYly29jRXbQ3ksxPrS1Tg8t+MHqzXhtBYAvI51euOBaoAlZLPHsieY9XPVMf80a5x0cPcA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" regexpu-core "^4.1.3" -"@babel/preset-env@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11" - integrity sha512-ZLVSynfAoDHB/34A17/JCZbyrzbQj59QC1Anyueb4Bwjh373nVPq5/HMph0z+tCmcDjXDe+DlKQq9ywQuvWrQg== +"@babel/preset-env@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.2.3.tgz#948c8df4d4609c99c7e0130169f052ea6a7a8933" + integrity sha512-AuHzW7a9rbv5WXmvGaPX7wADxFkZIqKlbBh1dmZUQp4iwiPpkE/Qnrji6SC4UQCQzvWY/cpHET29eUhXS9cLPw== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.1.0" - "@babel/plugin-proposal-json-strings" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.0.0" - "@babel/plugin-syntax-async-generators" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.1.0" - "@babel/plugin-transform-block-scoped-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.1.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-dotall-regex" "^7.0.0" - "@babel/plugin-transform-duplicate-keys" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.1.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.1.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-amd" "^7.1.0" - "@babel/plugin-transform-modules-commonjs" "^7.1.0" - "@babel/plugin-transform-modules-systemjs" "^7.0.0" - "@babel/plugin-transform-modules-umd" "^7.1.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.2.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.2.0" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.2.0" + "@babel/plugin-transform-classes" "^7.2.0" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.2.0" + "@babel/plugin-transform-dotall-regex" "^7.2.0" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.2.0" + "@babel/plugin-transform-function-name" "^7.2.0" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.2.0" + "@babel/plugin-transform-modules-systemjs" "^7.2.0" + "@babel/plugin-transform-modules-umd" "^7.2.0" "@babel/plugin-transform-new-target" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.1.0" - "@babel/plugin-transform-parameters" "^7.1.0" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.2.0" "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typeof-symbol" "^7.0.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - browserslist "^4.1.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.2.0" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.2.0" + browserslist "^4.3.4" invariant "^2.2.2" js-levenshtein "^1.1.3" semver "^5.3.0" @@ -592,6 +626,15 @@ "@babel/parser" "^7.1.2" "@babel/types" "^7.1.2" +"@babel/template@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" + integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.2.2" + "@babel/types" "^7.2.2" + "@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.0.tgz#503ec6669387efd182c3888c4eec07bcc45d91b2" @@ -607,6 +650,21 @@ globals "^11.1.0" lodash "^4.17.10" +"@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" + integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.2.2" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.0.0" + "@babel/parser" "^7.2.3" + "@babel/types" "^7.2.2" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.10" + "@babel/types@^7.0.0", "@babel/types@^7.1.2": version "7.1.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.2.tgz#183e7952cf6691628afdc2e2b90d03240bac80c0" @@ -616,6 +674,15 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" +"@babel/types@^7.2.0", "@babel/types@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e" + integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== + dependencies: + esutils "^2.0.2" + lodash "^4.17.10" + to-fast-properties "^2.0.0" + "@gitlab/csslab@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@gitlab/csslab/-/csslab-1.8.0.tgz#54a2457fdc80f006665f0e578a5532780954ccfa" @@ -743,151 +810,147 @@ dependencies: lodash "^4.17.4" -"@webassemblyjs/ast@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.6.tgz#3ef8c45b3e5e943a153a05281317474fef63e21e" - integrity sha512-8nkZS48EVsMUU0v6F1LCIOw4RYWLm2plMtbhFTjNgeXmsTNLuU3xTRtnljt9BFQB+iPbLRobkNrCWftWnNC7wQ== - dependencies: - "@webassemblyjs/helper-module-context" "1.7.6" - "@webassemblyjs/helper-wasm-bytecode" "1.7.6" - "@webassemblyjs/wast-parser" "1.7.6" - mamacro "^0.0.3" - -"@webassemblyjs/floating-point-hex-parser@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.6.tgz#7cb37d51a05c3fe09b464ae7e711d1ab3837801f" - integrity sha512-VBOZvaOyBSkPZdIt5VBMg3vPWxouuM13dPXGWI1cBh3oFLNcFJ8s9YA7S9l4mPI7+Q950QqOmqj06oa83hNWBA== - -"@webassemblyjs/helper-api-error@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.6.tgz#99b7e30e66f550a2638299a109dda84a622070ef" - integrity sha512-SCzhcQWHXfrfMSKcj8zHg1/kL9kb3aa5TN4plc/EREOs5Xop0ci5bdVBApbk2yfVi8aL+Ly4Qpp3/TRAUInjrg== - -"@webassemblyjs/helper-buffer@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.6.tgz#ba0648be12bbe560c25c997e175c2018df39ca3e" - integrity sha512-1/gW5NaGsEOZ02fjnFiU8/OEEXU1uVbv2um0pQ9YVL3IHSkyk6xOwokzyqqO1qDZQUAllb+V8irtClPWntbVqw== - -"@webassemblyjs/helper-code-frame@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.6.tgz#5a94d21b0057b69a7403fca0c253c3aaca95b1a5" - integrity sha512-+suMJOkSn9+vEvDvgyWyrJo5vJsWSDXZmJAjtoUq4zS4eqHyXImpktvHOZwXp1XQjO5H+YQwsBgqTQEc0J/5zg== - dependencies: - "@webassemblyjs/wast-printer" "1.7.6" - -"@webassemblyjs/helper-fsm@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.6.tgz#ae1741c6f6121213c7a0b587fb964fac492d3e49" - integrity sha512-HCS6KN3wgxUihGBW7WFzEC/o8Eyvk0d56uazusnxXthDPnkWiMv+kGi9xXswL2cvfYfeK5yiM17z2K5BVlwypw== - -"@webassemblyjs/helper-module-context@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.6.tgz#116d19a51a6cebc8900ad53ca34ff8269c668c23" - integrity sha512-e8/6GbY7OjLM+6OsN7f2krC2qYVNaSr0B0oe4lWdmq5sL++8dYDD1TFbD1TdAdWMRTYNr/Qq7ovXWzia2EbSjw== - dependencies: - mamacro "^0.0.3" - -"@webassemblyjs/helper-wasm-bytecode@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.6.tgz#98e515eaee611aa6834eb5f6a7f8f5b29fefb6f1" - integrity sha512-PzYFCb7RjjSdAOljyvLWVqd6adAOabJW+8yRT+NWhXuf1nNZWH+igFZCUK9k7Cx7CsBbzIfXjJc7u56zZgFj9Q== - -"@webassemblyjs/helper-wasm-section@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.6.tgz#783835867bdd686df7a95377ab64f51a275e8333" - integrity sha512-3GS628ppDPSuwcYlQ7cDCGr4W2n9c4hLzvnRKeuz+lGsJSmc/ADVoYpm1ts2vlB1tGHkjtQMni+yu8mHoMlKlA== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-buffer" "1.7.6" - "@webassemblyjs/helper-wasm-bytecode" "1.7.6" - "@webassemblyjs/wasm-gen" "1.7.6" - -"@webassemblyjs/ieee754@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.6.tgz#c34fc058f2f831fae0632a8bb9803cf2d3462eb1" - integrity sha512-V4cIp0ruyw+hawUHwQLn6o2mFEw4t50tk530oKsYXQhEzKR+xNGDxs/SFFuyTO7X3NzEu4usA3w5jzhl2RYyzQ== +"@webassemblyjs/ast@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" + integrity sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA== + dependencies: + "@webassemblyjs/helper-module-context" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/wast-parser" "1.7.11" + +"@webassemblyjs/floating-point-hex-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313" + integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg== + +"@webassemblyjs/helper-api-error@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a" + integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg== + +"@webassemblyjs/helper-buffer@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b" + integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w== + +"@webassemblyjs/helper-code-frame@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b" + integrity sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw== + dependencies: + "@webassemblyjs/wast-printer" "1.7.11" + +"@webassemblyjs/helper-fsm@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181" + integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A== + +"@webassemblyjs/helper-module-context@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209" + integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg== + +"@webassemblyjs/helper-wasm-bytecode@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06" + integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ== + +"@webassemblyjs/helper-wasm-section@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a" + integrity sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + +"@webassemblyjs/ieee754@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b" + integrity sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.6.tgz#197f75376a29f6ed6ace15898a310d871d92f03b" - integrity sha512-ojdlG8WpM394lBow4ncTGJoIVZ4aAtNOWHhfAM7m7zprmkVcKK+2kK5YJ9Bmj6/ketTtOn7wGSHCtMt+LzqgYQ== +"@webassemblyjs/leb128@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63" + integrity sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw== dependencies: "@xtuc/long" "4.2.1" -"@webassemblyjs/utf8@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.6.tgz#eb62c66f906af2be70de0302e29055d25188797d" - integrity sha512-oId+tLxQ+AeDC34ELRYNSqJRaScB0TClUU6KQfpB8rNT6oelYlz8axsPhf6yPTg7PBJ/Z5WcXmUYiHEWgbbHJw== - -"@webassemblyjs/wasm-edit@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.6.tgz#fa41929160cd7d676d4c28ecef420eed5b3733c5" - integrity sha512-pTNjLO3o41v/Vz9VFLl+I3YLImpCSpodFW77pNoH4agn5I6GgSxXHXtvWDTvYJFty0jSeXZWLEmbaSIRUDlekg== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-buffer" "1.7.6" - "@webassemblyjs/helper-wasm-bytecode" "1.7.6" - "@webassemblyjs/helper-wasm-section" "1.7.6" - "@webassemblyjs/wasm-gen" "1.7.6" - "@webassemblyjs/wasm-opt" "1.7.6" - "@webassemblyjs/wasm-parser" "1.7.6" - "@webassemblyjs/wast-printer" "1.7.6" - -"@webassemblyjs/wasm-gen@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.6.tgz#695ac38861ab3d72bf763c8c75e5f087ffabc322" - integrity sha512-mQvFJVumtmRKEUXMohwn8nSrtjJJl6oXwF3FotC5t6e2hlKMh8sIaW03Sck2MDzw9xPogZD7tdP5kjPlbH9EcQ== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-wasm-bytecode" "1.7.6" - "@webassemblyjs/ieee754" "1.7.6" - "@webassemblyjs/leb128" "1.7.6" - "@webassemblyjs/utf8" "1.7.6" - -"@webassemblyjs/wasm-opt@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.6.tgz#fbafa78e27e1a75ab759a4b658ff3d50b4636c21" - integrity sha512-go44K90fSIsDwRgtHhX14VtbdDPdK2sZQtZqUcMRvTojdozj5tLI0VVJAzLCfz51NOkFXezPeVTAYFqrZ6rI8Q== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-buffer" "1.7.6" - "@webassemblyjs/wasm-gen" "1.7.6" - "@webassemblyjs/wasm-parser" "1.7.6" - -"@webassemblyjs/wasm-parser@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.6.tgz#84eafeeff405ad6f4c4b5777d6a28ae54eed51fe" - integrity sha512-t1T6TfwNY85pDA/HWPA8kB9xA4sp9ajlRg5W7EKikqrynTyFo+/qDzIpvdkOkOGjlS6d4n4SX59SPuIayR22Yg== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-api-error" "1.7.6" - "@webassemblyjs/helper-wasm-bytecode" "1.7.6" - "@webassemblyjs/ieee754" "1.7.6" - "@webassemblyjs/leb128" "1.7.6" - "@webassemblyjs/utf8" "1.7.6" - -"@webassemblyjs/wast-parser@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.6.tgz#ca4d20b1516e017c91981773bd7e819d6bd9c6a7" - integrity sha512-1MaWTErN0ziOsNUlLdvwS+NS1QWuI/kgJaAGAMHX8+fMJFgOJDmN/xsG4h/A1Gtf/tz5VyXQciaqHZqp2q0vfg== - dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/floating-point-hex-parser" "1.7.6" - "@webassemblyjs/helper-api-error" "1.7.6" - "@webassemblyjs/helper-code-frame" "1.7.6" - "@webassemblyjs/helper-fsm" "1.7.6" +"@webassemblyjs/utf8@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82" + integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA== + +"@webassemblyjs/wasm-edit@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005" + integrity sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/helper-wasm-section" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + "@webassemblyjs/wasm-opt" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" + "@webassemblyjs/wast-printer" "1.7.11" + +"@webassemblyjs/wasm-gen@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8" + integrity sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/ieee754" "1.7.11" + "@webassemblyjs/leb128" "1.7.11" + "@webassemblyjs/utf8" "1.7.11" + +"@webassemblyjs/wasm-opt@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7" + integrity sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" + +"@webassemblyjs/wasm-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a" + integrity sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-api-error" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/ieee754" "1.7.11" + "@webassemblyjs/leb128" "1.7.11" + "@webassemblyjs/utf8" "1.7.11" + +"@webassemblyjs/wast-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c" + integrity sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ== + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/floating-point-hex-parser" "1.7.11" + "@webassemblyjs/helper-api-error" "1.7.11" + "@webassemblyjs/helper-code-frame" "1.7.11" + "@webassemblyjs/helper-fsm" "1.7.11" "@xtuc/long" "4.2.1" - mamacro "^0.0.3" -"@webassemblyjs/wast-printer@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.6.tgz#a6002c526ac5fa230fe2c6d2f1bdbf4aead43a5e" - integrity sha512-vHdHSK1tOetvDcl1IV1OdDeGNe/NDDQ+KzuZHMtqTVP1xO/tZ/IKNpj5BaGk1OYFdsDWQqb31PIwdEyPntOWRQ== +"@webassemblyjs/wast-printer@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813" + integrity sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg== dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/wast-parser" "1.7.6" + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/wast-parser" "1.7.11" "@xtuc/long" "4.2.1" "@xtuc/ieee754@^1.2.0": @@ -1436,12 +1499,12 @@ babel-jest@^23.6.0: babel-plugin-istanbul "^4.1.6" babel-preset-jest "^23.2.0" -babel-loader@^8.0.4: - version "8.0.4" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.4.tgz#7bbf20cbe4560629e2e41534147692d3fecbdce6" - integrity sha512-fhBhNkUToJcW9nV46v8w87AJOwAJDz84c1CL57n3Stj73FANM/b9TbCUK4YhdOwEyZ+OxhYpdeZDNzSI29Firw== +babel-loader@^8.0.5: + version "8.0.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.5.tgz#225322d7509c2157655840bba52e46b6c2f2fe33" + integrity sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw== dependencies: - find-cache-dir "^1.0.0" + find-cache-dir "^2.0.0" loader-utils "^1.0.2" mkdirp "^0.5.1" util.promisify "^1.0.0" @@ -1683,10 +1746,10 @@ blob@0.0.4: resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" integrity sha1-vPEwUspURj8w+fx+lbmkdjCpSSE= -bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== +bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" @@ -1868,14 +1931,14 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.1.1.tgz#328eb4ff1215b12df6589e9ab82f8adaa4fc8cd6" - integrity sha512-VBorw+tgpOtZ1BYhrVSVTzTt/3+vSE3eFUh0N2GCFK1HffceOaf32YS/bs6WiFhjDAblAFrx85jMy3BG9fBK2Q== +browserslist@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.3.7.tgz#f1de479a6466ea47a0a26dcc725e7504817e624a" + integrity sha512-pWQv51Ynb0MNk9JGMCZ8VkM785/4MQNXiFYtPqI7EEP0TJO+/d/NqRVn1uiAN0DNbnlUSpL2sh16Kspasv3pUQ== dependencies: - caniuse-lite "^1.0.30000884" - electron-to-chromium "^1.3.62" - node-releases "^1.0.0-alpha.11" + caniuse-lite "^1.0.30000925" + electron-to-chromium "^1.3.96" + node-releases "^1.1.3" bser@^2.0.0: version "2.0.0" @@ -1923,43 +1986,24 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -cacache@^10.0.4: - version "10.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" - integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== - dependencies: - bluebird "^3.5.1" - chownr "^1.0.1" - glob "^7.1.2" - graceful-fs "^4.1.11" - lru-cache "^4.1.1" - mississippi "^2.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.2" - ssri "^5.2.4" - unique-filename "^1.1.0" - y18n "^4.0.0" - -cacache@^11.2.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.2.0.tgz#617bdc0b02844af56310e411c0878941d5739965" - integrity sha512-IFWl6lfK6wSeYCHUXh+N1lY72UDrpyrYQJNIVQf48paDuWbv5RbAtJYf/4gUQFObTCHZwdZ5sI8Iw7nqwP6nlQ== +cacache@^11.0.2, cacache@^11.2.0: + version "11.3.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" + integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg== dependencies: - bluebird "^3.5.1" - chownr "^1.0.1" - figgy-pudding "^3.1.0" - glob "^7.1.2" - graceful-fs "^4.1.11" - lru-cache "^4.1.3" + bluebird "^3.5.3" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.3" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" mississippi "^3.0.0" mkdirp "^0.5.1" move-concurrently "^1.0.1" promise-inflight "^1.0.1" rimraf "^2.6.2" - ssri "^6.0.0" - unique-filename "^1.1.0" + ssri "^6.0.1" + unique-filename "^1.1.1" y18n "^4.0.0" cache-base@^1.0.1: @@ -1977,15 +2021,16 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cache-loader@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-1.2.2.tgz#6d5c38ded959a09cc5d58190ab5af6f73bd353f5" - integrity sha512-rsGh4SIYyB9glU+d0OcHwiXHXBoUgDhHZaQ1KAbiXqfz1CDPxtTboh1gPbJ0q2qdO8a9lfcjgC5CJ2Ms32y5bw== +cache-loader@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-2.0.1.tgz#5758f41a62d7c23941e3c3c7016e6faeb03acb07" + integrity sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ== dependencies: loader-utils "^1.1.0" mkdirp "^0.5.1" - neo-async "^2.5.0" - schema-utils "^0.4.2" + neo-async "^2.6.0" + normalize-path "^3.0.0" + schema-utils "^1.0.0" cacheable-request@^2.1.1: version "2.1.4" @@ -2027,10 +2072,15 @@ camelcase@^4.0.0, camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -caniuse-lite@^1.0.30000884: - version "1.0.30000888" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000888.tgz#22edb50d91dd70612b5898e3b36f460600c6492f" - integrity sha512-vftg+5p/lPsQGpnhSo/yBuYL36ai/cyjLvU3dOPJY1kkKrekLWIy8SLm+wzjX0hpCUdFTasC4/ZT7uqw4rKOnQ== +camelcase@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + +caniuse-lite@^1.0.30000925: + version "1.0.30000927" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000927.tgz#114a9de4ff1e01f5790fe578ecd93421c7524665" + integrity sha512-ogq4NbUWf1uG/j66k0AmiO3GjqJAlQyF8n4w8a954cbCyFKmYGvRtgz6qkq2fWuduTXHibX7GyYL5Pg58Aks2g== capture-exit@^1.2.0: version "1.2.0" @@ -2114,10 +2164,10 @@ chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3: optionalDependencies: fsevents "^1.2.2" -chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE= +chownr@^1.0.1, chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== chrome-trace-event@^1.0.0: version "1.0.0" @@ -2284,11 +2334,6 @@ commander@2, commander@^2.10.0, commander@^2.18.0, commander@^2.19.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== -commander@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" - integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== - commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -2588,15 +2633,15 @@ crypto-random-string@^1.0.0: integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= css-loader@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56" - integrity sha512-tMXlTYf3mIMt3b0dDCOQFJiVvxbocJ5Ho577WiGPYPZcqVEO218L2iU22pDXzkTZCLDE+9AmGSUkWxeh/nZReA== + version "1.0.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe" + integrity sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw== dependencies: babel-code-frame "^6.26.0" css-selector-tokenizer "^0.7.0" icss-utils "^2.1.0" loader-utils "^1.0.2" - lodash.camelcase "^4.3.0" + lodash "^4.17.11" postcss "^6.0.23" postcss-modules-extract-imports "^1.2.0" postcss-modules-local-by-default "^1.2.0" @@ -2977,7 +3022,7 @@ debug@~3.1.0: dependencies: ms "2.0.0" -decamelize@^1.1.1: +decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -3135,6 +3180,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= + detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -3152,10 +3202,10 @@ detect-newline@^2.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= -detect-node@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" - integrity sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc= +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== di@^0.0.1: version "0.0.1" @@ -3345,10 +3395,10 @@ ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== -electron-to-chromium@^1.3.62: - version "1.3.73" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz#aa67787067d58cc3920089368b3b8d6fe0fc12f6" - integrity sha512-6PIg7v9zRoVGh6EheRF8h6Plti+3Yo/qtHobS4/Htyt53DNHmKKGFqSae1AIk0k1S4gCQvt7I2WgpbuZNcDY+g== +electron-to-chromium@^1.3.96: + version "1.3.100" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.100.tgz#899fb088def210aee6b838a47655bbb299190e13" + integrity sha512-cEUzis2g/RatrVf8x26L8lK5VEls1AGnLHk6msluBUg/NTB4wcXzExTsGscFq+Vs4WBBU2zbLLySvD4C0C3hwg== elliptic@^6.0.0: version "6.4.0" @@ -3432,7 +3482,7 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: +enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -3908,6 +3958,13 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + expect@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98" @@ -4076,7 +4133,7 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -figgy-pudding@^3.1.0, figgy-pudding@^3.5.1: +figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== @@ -4096,10 +4153,10 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde" - integrity sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ== +file-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== dependencies: loader-utils "^1.0.2" schema-utils "^1.0.0" @@ -4177,15 +4234,6 @@ find-babel-config@^1.1.0: json5 "^0.5.1" path-exists "^3.0.0" -find-cache-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" - integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= - dependencies: - commondir "^1.0.1" - make-dir "^1.0.0" - pkg-dir "^2.0.0" - find-cache-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" @@ -4222,6 +4270,16 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + flat-cache@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" @@ -4476,10 +4534,30 @@ global-dirs@^0.1.0: dependencies: ini "^1.3.4" -global-modules-path@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb" - integrity sha512-3DrmGj2TP+96cABk9TfMp6f3knH/Y46dqvWznTU3Tf6/bDGLDAn15tFluQ7BcloykOcdY16U0WGq0BQblYOxJQ== +global-modules-path@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.1.tgz#e541f4c800a1a8514a990477b267ac67525b9931" + integrity sha512-y+shkf4InI7mPRHSo2b/k6ix6+NLDtyccYv86whhxrSGX9wjPX1VMITmrDbE1eh7zkzhiWtW2sHklJYoQ62Cxg== + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" globals@^11.1.0, globals@^11.7.0: version "11.7.0" @@ -4561,10 +4639,10 @@ got@^8.0.3: url-parse-lax "^3.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== graphlibrary@^2.2.0: version "2.2.0" @@ -4605,10 +4683,10 @@ gzip-size@^5.0.0: duplexer "^0.1.1" pify "^3.0.0" -handle-thing@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" - integrity sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ= +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.3: version "4.0.12" @@ -4778,6 +4856,13 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +homedir-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + integrity sha1-TCu8inWJmP7r9e1oWA921GdotLw= + dependencies: + parse-passwd "^1.0.0" + hoopy@^0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -5021,7 +5106,7 @@ inquirer@3.0.6: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^6.0.0, inquirer@^6.1.0: +inquirer@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== @@ -5408,7 +5493,7 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-windows@^1.0.2: +is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -6135,6 +6220,13 @@ json5@^0.5.0, json5@^0.5.1: resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= +json5@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -6351,6 +6443,11 @@ lie@~3.1.0: dependencies: immediate "~3.0.5" +lightercollective@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lightercollective/-/lightercollective-0.1.0.tgz#70df102c530dcb8d0ccabfe6175a8d00d5f61300" + integrity sha512-J9tg5uraYoQKaWbmrzDDexbG6hHnMcWS1qLYgJSWE+mpA3U5OCSeMUhb+K55otgZJ34oFdR0ECvdIb3xuO5JOQ== + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -6412,7 +6509,7 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash.camelcase@4.3.0, lodash.camelcase@^4.3.0: +lodash.camelcase@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= @@ -6517,7 +6614,7 @@ lru-cache@2.2.x: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" integrity sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0= -lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.3: +lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" integrity sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA== @@ -6525,6 +6622,13 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.3: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" @@ -6544,11 +6648,6 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - map-age-cleaner@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74" @@ -6692,7 +6791,7 @@ micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.4, micromatch@^3.1.6, micromatch@^3.1.8, micromatch@^3.1.9: +micromatch@^3.0.4, micromatch@^3.1.4, micromatch@^3.1.6, micromatch@^3.1.8, micromatch@^3.1.9: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -6793,22 +6892,6 @@ minizlib@^1.1.0: dependencies: minipass "^2.2.1" -mississippi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" - integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^2.0.1" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -6942,10 +7025,10 @@ negotiator@0.6.1: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= -neo-async@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" - integrity sha512-nJmSswG4As/MkRq7QZFuH/sf/yuv8ODdMZrY4Bedjp77a5MK4A6s7YbBB64c9u79EBUOfXUXBvArmvzTD0X+6g== +neo-async@^2.5.0, neo-async@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" + integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== nice-try@^1.0.4: version "1.0.4" @@ -7033,10 +7116,10 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-releases@^1.0.0-alpha.11: - version "1.0.0-alpha.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.12.tgz#32e461b879ea76ac674e511d9832cf29da345268" - integrity sha512-VPB4rTPqpVyWKBHbSa4YPFme3+8WHsOSpvbp0Mfj0bWsC8TEjt4HQrLl1hsBDELlp1nB4lflSgSuGTYiuyaP7Q== +node-releases@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.3.tgz#aad9ce0dcb98129c753f772c0aa01360fb90fbd2" + integrity sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ== dependencies: semver "^5.3.0" @@ -7095,6 +7178,11 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + normalize-url@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" @@ -7228,10 +7316,10 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -obuf@^1.0.0, obuf@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" - integrity sha1-EEEktsYCxnlogaBCVB0220OlJk4= +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== on-finished@~2.3.0: version "2.3.0" @@ -7496,6 +7584,11 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -7708,9 +7801,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postcss-modules-extract-imports@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" - integrity sha1-ZhQOzs447wa/DT41XWm/WdFB6oU= + version "1.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" + integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw== dependencies: postcss "^6.0.1" @@ -7897,7 +7990,7 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^2.0.0, pump@^2.0.1: +pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== @@ -8027,10 +8120,13 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -raw-loader@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" - integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= +raw-loader@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-1.0.0.tgz#3f9889e73dadbda9a424bce79809b4133ad46405" + integrity sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA== + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" @@ -8093,7 +8189,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -8352,6 +8448,14 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" @@ -8511,7 +8615,7 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.4, schema-utils@^0.4.5: +schema-utils@^0.4.0, schema-utils@^0.4.4: version "0.4.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" integrity sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA== @@ -8877,7 +8981,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.6: +source-map-support@^0.5.6, source-map-support@~0.5.6: version "0.5.9" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== @@ -8929,30 +9033,28 @@ spdx-license-ids@^1.0.2: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc= -spdy-transport@^2.0.18: - version "2.0.20" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d" - integrity sha1-c15yBUxIayNU/onnAiVgBKOazk0= +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== dependencies: - debug "^2.6.8" - detect-node "^2.0.3" + debug "^4.1.0" + detect-node "^2.0.4" hpack.js "^2.1.6" - obuf "^1.1.1" - readable-stream "^2.2.9" - safe-buffer "^5.0.1" - wbuf "^1.7.2" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" -spdy@^3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" - integrity sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw= +spdy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52" + integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q== dependencies: - debug "^2.6.8" - handle-thing "^1.2.5" + debug "^4.1.0" + handle-thing "^2.0.0" http-deceiver "^1.2.7" - safe-buffer "^5.0.1" select-hose "^2.0.0" - spdy-transport "^2.0.18" + spdy-transport "^3.0.0" split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -9001,14 +9103,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52" - integrity sha512-UnEAgMZa15973iH7cUi0AHjJn1ACDIkaMyZILoqwN6yzt+4P81I8tBc5Hl+qwi5auMplZtPQsHrPBR5vJLcQtQ== - dependencies: - safe-buffer "^5.1.1" - -ssri@^6.0.0: +ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== @@ -9182,13 +9277,13 @@ strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0 resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.0.tgz#8377fefab68416a2e05f1cabd8c3a3acfcce74f1" - integrity sha512-uCcN7XWHkqwGVt7skpInW6IGO1tG6ReyFQ1Cseh0VcN6VdcFQi62aG/2F3Y9ueA8x4IVlfaSUxpmQXQD9QrEuQ== +style-loader@^0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" + integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== dependencies: loader-utils "^1.1.0" - schema-utils "^0.4.5" + schema-utils "^1.0.0" supports-color@^2.0.0: version "2.0.0" @@ -9202,7 +9297,7 @@ supports-color@^3.1.0, supports-color@^3.1.2: dependencies: has-flag "^1.0.0" -supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -9264,6 +9359,29 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +terser-webpack-plugin@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz#7545da9ae5f4f9ae6a0ac961eb46f5e7c845cc26" + integrity sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw== + dependencies: + cacache "^11.0.2" + find-cache-dir "^2.0.0" + schema-utils "^1.0.0" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + terser "^3.8.1" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +terser@^3.8.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.14.1.tgz#cc4764014af570bc79c79742358bd46926018a32" + integrity sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw== + dependencies: + commander "~2.17.1" + source-map "~0.6.1" + source-map-support "~0.5.6" + test-exclude@^4.2.1: version "4.2.3" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20" @@ -9513,14 +9631,6 @@ typescript@^2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== -uglify-es@^3.3.4: - version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" - integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== - dependencies: - commander "~2.13.0" - source-map "~0.6.1" - uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" @@ -9529,20 +9639,6 @@ uglify-js@^3.1.4: commander "~2.17.1" source-map "~0.6.1" -uglifyjs-webpack-plugin@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz#2ef8387c8f1a903ec5e44fa36f9f3cbdcea67641" - integrity sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw== - dependencies: - cacache "^10.0.4" - find-cache-dir "^1.0.0" - schema-utils "^0.4.5" - serialize-javascript "^1.4.0" - source-map "^0.6.1" - uglify-es "^3.3.4" - webpack-sources "^1.1.0" - worker-farm "^1.5.2" - ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" @@ -9598,10 +9694,10 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= -unique-filename@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" - integrity sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM= +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" @@ -9669,10 +9765,10 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.1.tgz#4d1f3b4f90dde89f02c008e662d604d7511167c1" - integrity sha512-vugEeXjyYFBCUOpX+ZuaunbK3QXMKaQ3zUnRfIpRBlGkY7QizCnzyyn2ASfcxsvyU3ef+CJppVywnl3Kgf13Gg== +url-loader@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" + integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== dependencies: loader-utils "^1.1.0" mime "^2.0.3" @@ -9765,10 +9861,10 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -v8-compile-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" - integrity sha512-qNdTUMaCjPs4eEnM3W9H94R3sU70YCuT+/ST7nUf+id1bVOrdjrpUaeZLqPBPRph3hsgn4a4BvwpxhHZx+oSDg== +v8-compile-cache@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" + integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== validate-npm-package-license@^3.0.1: version "3.0.1" @@ -9839,10 +9935,10 @@ vue-hot-reload-api@^2.3.0: resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926" integrity sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA== -vue-jest@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.1.tgz#127cded1a57cdfcf01fa8a10ce29579e2cb3a04d" - integrity sha512-otS+n341cTsp0pF7tuTu2x43b23x/+K0LZdAXV+ewKYIMZRqhuQaJTECWEt/cN/YZw2JC6hUM6xybdnOB4ZQ+g== +vue-jest@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.2.tgz#c64bf5da9abd0d3ee16071217037696d14b0689c" + integrity sha512-5XIQ1xQFW0ZnWxHWM7adVA2IqbDsdw1vhgZfGFX4oWd75J38KIS3YT41PtiE7lpMLmNM6+VJ0uprT2mhHjUgkA== dependencies: babel-plugin-transform-es2015-modules-commonjs "^6.26.0" chalk "^2.1.0" @@ -9866,17 +9962,17 @@ vue-loader@^15.4.2: vue-hot-reload-api "^2.3.0" vue-style-loader "^4.1.0" -vue-resource@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.5.0.tgz#ba0c6ef7af2eeace03cf24a91f529471be974c72" - integrity sha512-em+Ihe+duUWQv4uKO8aFTGK+e/lvNtk5EBEmWaBYcfQzBmHhKR4jJAeVIHcG6otugmsme/DmYrOEPfbss+2XfQ== +vue-resource@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.5.1.tgz#0f3d685e3254d21800bebd966edcf56c34b3b6e4" + integrity sha512-o6V4wNgeqP+9v9b2bPXrr20CGNQPEXjpbUWdZWq9GJhqVeAGcYoeTtn/D4q059ZiyN0DIrDv/ADrQUmlUQcsmg== dependencies: got "^8.0.3" -vue-router@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9" - integrity sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w== +vue-router@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be" + integrity sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg== vue-style-loader@^4.1.0: version "4.1.0" @@ -9886,7 +9982,7 @@ vue-style-loader@^4.1.0: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-template-compiler@^2.5.0, vue-template-compiler@^2.5.17: +vue-template-compiler@^2.5.0, vue-template-compiler@^2.5.21: version "2.5.21" resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.21.tgz#a57ceb903177e8f643560a8d639a0f8db647054a" integrity sha512-Vmk5Cv7UcmI99B9nXJEkaK262IQNnHp5rJYo+EwYpe2epTAXqcVyExhV6pk8jTkxQK2vRc8v8KmZBAwdmUZvvw== @@ -9904,7 +10000,7 @@ vue-virtual-scroll-list@^1.2.5: resolved "https://registry.yarnpkg.com/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.2.5.tgz#bcbd010f7cdb035eba8958ebf807c6214d9a167a" integrity sha1-vL0BD3zbA166iVjr+AfGIU2aFno= -vue@^2.5.17, vue@^2.5.21: +vue@^2.5.21: version "2.5.21" resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.21.tgz#3d33dcd03bb813912ce894a8303ab553699c4a85" integrity sha512-Aejvyyfhn0zjVeLvXd70h4hrE4zZDx1wfZqia6ekkobLmUZ+vNFQer53B4fu0EjWBSiqApxPejzkO1Znt3joxQ== @@ -9945,10 +10041,10 @@ watchpack@^1.5.0: graceful-fs "^4.1.2" neo-async "^2.5.0" -wbuf@^1.1.0, wbuf@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" - integrity sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4= +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== dependencies: minimalistic-assert "^1.0.0" @@ -9957,10 +10053,10 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-bundle-analyzer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.2.tgz#22f19ea6d1b5a15fd7a90baae0bc0f39bd1e4d48" - integrity sha512-cZG4wSQtKrSpk5RJ33dxiaAyo8bP0V+JvycAyIDFEiDIhw4LHhhVKhn40YT1w6TR9E4scHA00LnIoBtTA13Mow== +webpack-bundle-analyzer@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz#dbc7fff8f52058b6714a20fddf309d0790e3e0a0" + integrity sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw== dependencies: acorn "^5.7.3" bfj "^6.1.1" @@ -9975,22 +10071,24 @@ webpack-bundle-analyzer@^3.0.2: opener "^1.5.1" ws "^6.0.0" -webpack-cli@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.1.0.tgz#d71a83687dcfeb758fdceeb0fe042f96bcf62994" - integrity sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ== +webpack-cli@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.2.1.tgz#779c696c82482491f0803907508db2e276ed3b61" + integrity sha512-jeJveHwz/vwpJ3B8bxEL5a/rVKIpRNJDsKggfKnxuYeohNDW4Y/wB9N/XHJA093qZyS0r6mYL+/crLsIol4WKA== dependencies: chalk "^2.4.1" cross-spawn "^6.0.5" - enhanced-resolve "^4.0.0" - global-modules-path "^2.1.0" - import-local "^1.0.0" - inquirer "^6.0.0" + enhanced-resolve "^4.1.0" + findup-sync "^2.0.0" + global-modules "^1.0.0" + global-modules-path "^2.3.0" + import-local "^2.0.0" interpret "^1.1.0" + lightercollective "^0.1.0" loader-utils "^1.1.0" - supports-color "^5.4.0" - v8-compile-cache "^2.0.0" - yargs "^12.0.1" + supports-color "^5.5.0" + v8-compile-cache "^2.0.2" + yargs "^12.0.4" webpack-dev-middleware@3.4.0, webpack-dev-middleware@^3.2.0: version "3.4.0" @@ -10002,10 +10100,10 @@ webpack-dev-middleware@3.4.0, webpack-dev-middleware@^3.2.0: range-parser "^1.0.3" webpack-log "^2.0.0" -webpack-dev-server@^3.1.10: - version "3.1.10" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz#507411bee727ee8d2fdffdc621b66a64ab3dea2b" - integrity sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww== +webpack-dev-server@^3.1.14: + version "3.1.14" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz#60fb229b997fc5a0a1fc6237421030180959d469" + integrity sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -10026,12 +10124,14 @@ webpack-dev-server@^3.1.10: portfinder "^1.0.9" schema-utils "^1.0.0" selfsigned "^1.9.1" + semver "^5.6.0" serve-index "^1.7.2" sockjs "0.3.19" sockjs-client "1.3.0" - spdy "^3.4.1" + spdy "^4.0.0" strip-ansi "^3.0.0" supports-color "^5.1.0" + url "^0.11.0" webpack-dev-middleware "3.4.0" webpack-log "^2.0.0" yargs "12.0.2" @@ -10044,7 +10144,7 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.2.0: +webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== @@ -10057,15 +10157,15 @@ webpack-stats-plugin@^0.2.1: resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595" integrity sha512-OYMZLpZrK/qLA79NE4kC4DCt85h/5ipvWJcsefKe9MMw0qU4/ck/IJg+4OmWA+5EfrZZpHXDq92IptfYDWVfkw== -webpack@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.19.1.tgz#096674bc3b573f8756c762754366e5b333d6576f" - integrity sha512-j7Q/5QqZRqIFXJvC0E59ipLV5Hf6lAnS3ezC3I4HMUybwEDikQBVad5d+IpPtmaQPQArvgUZLXIN6lWijHBn4g== +webpack@^4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.28.1.tgz#d0e2856e75d1224b170bf16c30b6ca9b75f0d958" + integrity sha512-qAS7BFyS5iuOZzGJxyDXmEI289h7tVNtJ5XMxf6Tz55J2riOyH42uaEsWF0F32TRaI+54SmI6qRgHM3GzsZ+sQ== dependencies: - "@webassemblyjs/ast" "1.7.6" - "@webassemblyjs/helper-module-context" "1.7.6" - "@webassemblyjs/wasm-edit" "1.7.6" - "@webassemblyjs/wasm-parser" "1.7.6" + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-module-context" "1.7.11" + "@webassemblyjs/wasm-edit" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" acorn "^5.6.2" acorn-dynamic-import "^3.0.0" ajv "^6.1.0" @@ -10083,9 +10183,9 @@ webpack@^4.19.1: node-libs-browser "^2.0.0" schema-utils "^0.4.4" tapable "^1.1.0" - uglifyjs-webpack-plugin "^1.2.4" + terser-webpack-plugin "^1.1.0" watchpack "^1.5.0" - webpack-sources "^1.2.0" + webpack-sources "^1.3.0" websocket-driver@>=0.5.1: version "0.6.5" @@ -10134,7 +10234,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.9, which@^1.3.0: +which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -10305,6 +10405,14 @@ yargs-parser@^10.1.0: dependencies: camelcase "^4.1.0" +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" @@ -10312,7 +10420,7 @@ yargs-parser@^9.0.2: dependencies: camelcase "^4.1.0" -yargs@12.0.2, yargs@^12.0.1: +yargs@12.0.2: version "12.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc" integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ== @@ -10348,6 +10456,24 @@ yargs@^11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" +yargs@^12.0.4: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + yarn-deduplicate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-1.0.5.tgz#e56016f1c29e77e323f401ea838f5e8c7cdbfd42" |