diff options
199 files changed, 1384 insertions, 850 deletions
diff --git a/.gitignore b/.gitignore index 65f61e1fade..0696dd217af 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ eslint-report.html package-lock.json /junit_*.xml /coverage-frontend/ +jsdoc/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 18e2c998ee8..d736e0fa5c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -499,6 +499,22 @@ rspec-mysql: <<: *rspec-metadata-mysql parallel: 50 +.rspec-quarantine: &rspec-quarantine + script: + - export CACHE_CLASSES=true + - scripts/gitaly-test-spawn + - bin/rspec --color --format documentation --tag quarantine spec/ + +rspec-pg-quarantine: + <<: *rspec-metadata-pg + <<: *rspec-quarantine + allow_failure: true + +rspec-mysql-quarantine: + <<: *rspec-metadata-mysql + <<: *rspec-quarantine + allow_failure: true + static-analysis: <<: *dedicated-no-docs-no-db-pull-cache-job dependencies: @@ -850,6 +866,21 @@ lint:javascript:report: paths: - eslint-report.html +jsdoc: + <<: *dedicated-no-docs-pull-cache-job + stage: post-test + dependencies: + - compile-assets + before_script: [] + script: + - date + - yarn run jsdoc || true # ignore exit code + artifacts: + name: jsdoc + expire_in: 31d + paths: + - jsdoc/ + pages: <<: *dedicated-no-docs-no-db-pull-cache-job before_script: [] @@ -859,6 +890,7 @@ pages: - karma - gitlab:assets:compile - lint:javascript:report + - jsdoc script: - mv public/ .public/ - mkdir public/ @@ -868,6 +900,7 @@ pages: - mv webpack-report/ public/webpack-report/ || true - cp .public/assets/application-*.css public/application.css || true - cp .public/assets/application-*.css.gz public/application.css.gz || true + - mv jsdoc/ public/jsdoc/ || true artifacts: paths: - public diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue index 9051be1e102..cad5611c8c5 100644 --- a/app/assets/javascripts/badges/components/badge_list_row.vue +++ b/app/assets/javascripts/badges/components/badge_list_row.vue @@ -55,7 +55,7 @@ export default { :disabled="badge.isDeleting" class="btn btn-default append-right-8" type="button" - @click="editBadge(badge);" + @click="editBadge(badge)" > <icon :size="16" :aria-label="__('Edit')" name="pencil" /> </button> @@ -65,7 +65,7 @@ export default { type="button" data-toggle="modal" data-target="#delete-badge-modal" - @click="updateBadgeInModal(badge);" + @click="updateBadgeInModal(badge)" > <icon :size="16" :aria-label="__('Delete')" name="remove" /> </button> diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 30fbdb9e97f..f569322ab70 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -86,7 +86,7 @@ export default { class="board-card" @mousedown="mouseDown" @mousemove="mouseMove" - @mouseup="showIssue($event);" + @mouseup="showIssue($event)" > <issue-card-inner :list="list" diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index 93bcb4e129e..28d96dab605 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -96,7 +96,7 @@ export default { <template> <div class="board-new-issue-form"> <div class="board-card"> - <form @submit="submit($event);"> + <form @submit="submit($event)"> <div v-if="error" class="flash-container"> <div class="flash-alert">An error occurred. Please try again.</div> </div> diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 0f581c3d37d..90ab3a76342 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -184,7 +184,7 @@ export default { :title="label.description" class="badge color-label append-right-4 prepend-top-4" type="button" - @click="filterByLabel(label);" + @click="filterByLabel(label)" > {{ label.title }} </button> diff --git a/app/assets/javascripts/boards/components/modal/empty_state.vue b/app/assets/javascripts/boards/components/modal/empty_state.vue index defd857b92c..2a0008467c4 100644 --- a/app/assets/javascripts/boards/components/modal/empty_state.vue +++ b/app/assets/javascripts/boards/components/modal/empty_state.vue @@ -58,7 +58,7 @@ export default { v-if="activeTab === 'selected'" class="btn btn-default" type="button" - @click="changeTab('all');" + @click="changeTab('all')" > Open issues </button> diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue index b1bc7d87086..d4afd9d59da 100644 --- a/app/assets/javascripts/boards/components/modal/footer.vue +++ b/app/assets/javascripts/boards/components/modal/footer.vue @@ -71,7 +71,7 @@ export default { <span class="inline add-issues-footer-to-list"> to list </span> <lists-dropdown /> </div> - <button class="btn btn-default float-right" type="button" @click="toggleModal(false);"> + <button class="btn btn-default float-right" type="button" @click="toggleModal(false)"> Cancel </button> </footer> diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue index d0e285a149e..1f0961e02d8 100644 --- a/app/assets/javascripts/boards/components/modal/header.vue +++ b/app/assets/javascripts/boards/components/modal/header.vue @@ -58,7 +58,7 @@ export default { class="close" data-dismiss="modal" aria-label="Close" - @click="toggleModal(false);" + @click="toggleModal(false)" > <span aria-hidden="true">×</span> </button> diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue index 878bb002c6c..e9ed2de859d 100644 --- a/app/assets/javascripts/boards/components/modal/list.vue +++ b/app/assets/javascripts/boards/components/modal/list.vue @@ -130,7 +130,7 @@ export default { <div :class="{ 'is-active': issue.selected }" class="board-card" - @click="toggleIssue($event, issue);" + @click="toggleIssue($event, issue)" > <issue-card-inner :issue="issue" :issue-link-base="issueLinkBase" :root-path="rootPath" /> <icon diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue index 820d0679df5..3fbe8fe1be7 100644 --- a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue +++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue @@ -38,7 +38,7 @@ export default { :class="{ 'is-active': list.id == selected.id }" href="#" role="button" - @click.prevent="modal.selectedList = list;" + @click.prevent="modal.selectedList = list" > <span :style="{ backgroundColor: list.label.color }" class="dropdown-label-box"> </span> {{ list.title }} diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue index 7b800a6ab97..2d2920e312e 100644 --- a/app/assets/javascripts/boards/components/modal/tabs.vue +++ b/app/assets/javascripts/boards/components/modal/tabs.vue @@ -21,12 +21,12 @@ export default { <div class="top-area prepend-top-10 append-bottom-10"> <ul class="nav-links issues-state-filters"> <li :class="{ active: activeTab == 'all' }"> - <a href="#" role="button" @click.prevent="changeTab('all');"> + <a href="#" role="button" @click.prevent="changeTab('all')"> Open issues <span class="badge badge-pill"> {{ issuesCount }} </span> </a> </li> <li :class="{ active: activeTab == 'selected' }"> - <a href="#" role="button" @click.prevent="changeTab('selected');"> + <a href="#" role="button" @click.prevent="changeTab('selected')"> Selected issues <span class="badge badge-pill"> {{ selectedCount }} </span> </a> </li> diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index 10f02739ec8..42c3542b50b 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -13,6 +13,9 @@ export default class ContextualSidebar { initDomElements() { this.$page = $('.layout-page'); this.$sidebar = $('.nav-sidebar'); + + if (!this.$sidebar.length) return; + this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar); this.$overlay = $('.mobile-overlay'); this.$openSidebar = $('.toggle-mobile-nav'); @@ -21,12 +24,14 @@ export default class ContextualSidebar { } bindEvents() { + if (!this.$sidebar.length) return; + document.addEventListener('click', e => { if ( !e.target.closest('.nav-sidebar') && (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md') ) { - this.toggleCollapsedSidebar(true); + this.toggleCollapsedSidebar(true, true); } }); this.$openSidebar.on('click', () => this.toggleSidebarNav(true)); @@ -34,7 +39,7 @@ export default class ContextualSidebar { this.$overlay.on('click', () => this.toggleSidebarNav(false)); this.$sidebarToggle.on('click', () => { const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop'); - this.toggleCollapsedSidebar(value); + this.toggleCollapsedSidebar(value, true); }); $(window).on('resize', () => _.debounce(this.render(), 100)); @@ -53,16 +58,19 @@ export default class ContextualSidebar { this.$sidebar.removeClass('sidebar-collapsed-desktop'); } - toggleCollapsedSidebar(collapsed) { + toggleCollapsedSidebar(collapsed, saveCookie) { const breakpoint = bp.getBreakpointSize(); if (this.$sidebar.length) { this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed); this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed); } - ContextualSidebar.setCollapsedCookie(collapsed); - this.toggleSidebarOverflow(); + if (saveCookie) { + ContextualSidebar.setCollapsedCookie(collapsed); + } + + requestIdleCallback(this.toggleSidebarOverflow); } toggleSidebarOverflow() { @@ -74,13 +82,15 @@ export default class ContextualSidebar { } render() { + if (!this.$sidebar.length) return; + const breakpoint = bp.getBreakpointSize(); if (breakpoint === 'sm' || breakpoint === 'md') { - this.toggleCollapsedSidebar(true); + this.toggleCollapsedSidebar(true, false); } else if (breakpoint === 'lg') { const collapse = parseBoolean(Cookies.get('sidebar_collapsed')); - this.toggleCollapsedSidebar(collapse); + this.toggleCollapsedSidebar(collapse, false); } } } diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index ba6dcd63880..6dc2f5d3f68 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -127,7 +127,7 @@ export default { :save-button-title="__('Comment')" class="diff-comment-form new-note discussion-form discussion-form-container" @handleFormUpdate="handleSaveNote" - @cancelForm="closeDiffFileCommentForm(diffFile.file_hash);" + @cancelForm="closeDiffFileCommentForm(diffFile.file_hash)" /> </div> </diff-viewer> diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue index b2021cd6061..4c73eea4049 100644 --- a/app/assets/javascripts/diffs/components/diff_discussions.vue +++ b/app/assets/javascripts/diffs/components/diff_discussions.vue @@ -68,7 +68,7 @@ export default { }" type="button" class="js-diff-notes-toggle" - @click="toggleDiscussion({ discussionId: discussion.id });" + @click="toggleDiscussion({ discussionId: discussion.id })" > <icon v-if="discussion.expanded" name="collapse" class="collapse-icon" /> <template v-else> diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index f75a01b023b..b58f704bebb 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -145,7 +145,7 @@ export default { <div ref="header" class="js-file-title file-title file-title-flex-parent" - @click="handleToggleFile($event, true);" + @click="handleToggleFile($event, true)" > <div class="file-header-content"> <icon diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index c0613d80d37..6709df48637 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -179,7 +179,7 @@ export default { v-if="lineNumber" :data-linenumber="lineNumber" :href="lineHref" - @click="setHighlightedRow(lineCode);" + @click="setHighlightedRow(lineCode)" > </a> <diff-gutter-avatars v-if="shouldShowAvatarsOnGutter" :discussions="line.discussions" /> diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue index e7569ba7b84..18edbe286ba 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -28,6 +28,11 @@ export default { type: Object, required: true, }, + helpPagePath: { + type: String, + required: false, + default: '', + }, }, computed: { ...mapState({ @@ -95,6 +100,7 @@ export default { :is-editing="true" :line-code="line.line_code" :line="line" + :help-page-path="helpPagePath" save-button-title="Comment" class="diff-comment-form" @cancelForm="handleCancelCommentForm" diff --git a/app/assets/javascripts/diffs/components/image_diff_overlay.vue b/app/assets/javascripts/diffs/components/image_diff_overlay.vue index d30e64312aa..4a83c5a72a5 100644 --- a/app/assets/javascripts/diffs/components/image_diff_overlay.vue +++ b/app/assets/javascripts/diffs/components/image_diff_overlay.vue @@ -97,7 +97,7 @@ export default { v-if="canComment" type="button" class="btn-transparent position-absolute image-diff-overlay-add-comment w-100 h-100 js-add-image-diff-note-button" - @click="clickedImage($event.offsetX, $event.offsetY);" + @click="clickedImage($event.offsetX, $event.offsetY)" > <span class="sr-only"> {{ __('Add image comment') }} </span> </button> @@ -109,7 +109,7 @@ export default { :disabled="!shouldToggleDiscussion" class="js-image-badge" type="button" - @click="toggleDiscussion({ discussionId: discussion.id });" + @click="toggleDiscussion({ discussionId: discussion.id })" > <icon v-if="showCommentIcon" name="image-comment-dark" /> <template v-else> diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue index 814ee0b7c02..69146f1f6fd 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue @@ -54,6 +54,7 @@ export default { :diff-file-hash="diffFileHash" :line="line" :note-target-line="line" + :help-page-path="helpPagePath" /> </div> </td> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue index a65cf025cde..370cb6e339a 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue @@ -101,6 +101,7 @@ export default { :diff-file-hash="diffFileHash" :line="line.left" :note-target-line="line.left" + :help-page-path="helpPagePath" line-position="left" /> </td> diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index eb8f274aff3..097587c5ac1 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -81,7 +81,7 @@ export default { :placeholder="s__('MergeRequest|Filter files')" type="search" class="form-control" - @focus="toggleFocusSearch(true);" + @focus="toggleFocusSearch(true)" @blur="blurSearch" /> <button @@ -104,7 +104,7 @@ export default { }" class="btn btn-default pt-0 pb-0 d-flex align-items-center" type="button" - @click="toggleRenderTreeList(false);" + @click="toggleRenderTreeList(false)" > <icon name="hamburger" /> </button> @@ -117,7 +117,7 @@ export default { }" class="btn btn-default pt-0 pb-0 d-flex align-items-center" type="button" - @click="toggleRenderTreeList(true);" + @click="toggleRenderTreeList(true)" > <icon name="file-tree" /> </button> diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index 1f7dab9fbd2..208bd19f6b0 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -92,7 +92,7 @@ export default { :disabled="isActionDisabled(action)" type="button" class="js-manual-action-link no-btn btn d-flex align-items-center" - @click="onClickAction(action);" + @click="onClickAction(action)" > <span class="flex-fill"> {{ action.name }} </span> <span v-if="action.scheduledAt" class="text-secondary"> diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index ae9459a2482..87c1d44dd40 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -96,9 +96,9 @@ export default { <tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" /> <div v-if="canCreateEnvironment && !isLoading" class="nav-controls"> - <a :href="newEnvironmentPath" class="btn btn-success"> - {{ s__('Environments|New environment') }} - </a> + <a :href="newEnvironmentPath" class="btn btn-success">{{ + s__('Environments|New environment') + }}</a> </div> </div> diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue index 533e90e2222..75bdf87137f 100644 --- a/app/assets/javascripts/environments/components/environments_table.vue +++ b/app/assets/javascripts/environments/components/environments_table.vue @@ -85,9 +85,9 @@ export default { <div :key="`sub-div-${i}`"> <div class="text-center prepend-top-10"> - <a :href="folderUrl(model)" class="btn btn-default"> - {{ s__('Environments|Show all') }} - </a> + <a :href="folderUrl(model)" class="btn btn-default">{{ + s__('Environments|Show all') + }}</a> </div> </div> </template> diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js index 3cf6e4ad14d..982e550e73c 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js @@ -15,11 +15,11 @@ export default () => const environmentsData = document.querySelector(this.$options.el).dataset; return { - endpoint: environmentsData.endpoint, - folderName: environmentsData.folderName, + endpoint: environmentsData.environmentsDataEndpoint, + folderName: environmentsData.environmentsDataFolderName, cssContainerClass: environmentsData.cssClass, - canCreateDeployment: parseBoolean(environmentsData.canCreateDeployment), - canReadEnvironment: parseBoolean(environmentsData.canReadEnvironment), + canCreateDeployment: parseBoolean(environmentsData.environmentsDataCanCreateDeployment), + canReadEnvironment: parseBoolean(environmentsData.environmentsDataCanReadEnvironment), }; }, render(createElement) { diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue index 6b1a934d3fe..19bc3313373 100644 --- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue +++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue @@ -66,7 +66,7 @@ export default { <button type="button" class="filtered-search-history-dropdown-item" - @click="onItemActivated(item.text);" + @click="onItemActivated(item.text)" > <span> <span @@ -88,7 +88,7 @@ export default { <button type="button" class="filtered-search-history-clear-button" - @click="onRequestClearRecentSearches($event);" + @click="onRequestClearRecentSearches($event)" > Clear recent searches </button> diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 3ac00c51df4..2b6af9060d1 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -24,6 +24,9 @@ export const slope = (a, b) => (b.y - a.y) / (b.x - a.x); let headerHeight = 50; export const getHeaderHeight = () => headerHeight; +const setHeaderHeight = () => { + headerHeight = sidebar.offsetTop; +}; export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop'); @@ -186,7 +189,7 @@ export default () => { }); } - headerHeight = document.querySelector('.nav-sidebar').offsetTop; + requestIdleCallback(setHeaderHeight); items.forEach(el => { const subItems = el.querySelector('.sidebar-sub-level-items'); diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue index a1f66ff764d..7c769ab7fa0 100644 --- a/app/assets/javascripts/ide/components/activity_bar.vue +++ b/app/assets/javascripts/ide/components/activity_bar.vue @@ -45,7 +45,7 @@ export default { data-placement="right" type="button" class="ide-sidebar-link js-ide-edit-mode" - @click.prevent="changedActivityView($event, $options.activityBarViews.edit);" + @click.prevent="changedActivityView($event, $options.activityBarViews.edit)" > <icon name="code" /> </button> @@ -62,7 +62,7 @@ export default { data-placement="right" type="button" class="ide-sidebar-link js-ide-review-mode" - @click.prevent="changedActivityView($event, $options.activityBarViews.review);" + @click.prevent="changedActivityView($event, $options.activityBarViews.review)" > <icon name="file-modified" /> </button> @@ -79,7 +79,7 @@ export default { data-placement="right" type="button" class="ide-sidebar-link js-ide-commit-mode" - @click.prevent="changedActivityView($event, $options.activityBarViews.commit);" + @click.prevent="changedActivityView($event, $options.activityBarViews.commit)" > <icon name="commit" /> </button> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue index 6f1ded91753..00b2d236da3 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue @@ -111,8 +111,8 @@ export default { name="commit-message" @scroll="handleScroll" @input="onInput" - @focus="updateIsFocused(true);" - @blur="updateIsFocused(false);" + @focus="updateIsFocused(true)" + @blur="updateIsFocused(false)" @keydown.ctrl.enter="onCtrlEnter" @keydown.meta.enter="onCtrlEnter" > diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue index 3525084b1cb..2b44438f849 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -65,7 +65,7 @@ export default { :disabled="disabled" type="radio" name="commit-action" - @change="updateCommitAction($event.target.value);" + @change="updateCommitAction($event.target.value)" /> <span class="prepend-left-10"> <span v-if="label" class="ide-radio-label"> {{ label }} </span> <slot v-else></slot> @@ -76,7 +76,7 @@ export default { :placeholder="newBranchName" type="text" class="form-control monospace" - @input="updateBranchName($event.target.value);" + @input="updateBranchName($event.target.value)" /> </div> </fieldset> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue index 02c2004d495..e054be86c1e 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue @@ -48,7 +48,7 @@ export default { data-container="body" data-boundary="viewport" data-placement="bottom" - @click.stop.prevent="stageChange(path);" + @click.stop.prevent="stageChange(path)" > <icon :size="16" name="mobile-issue-close" class="ml-auto mr-auto" /> </button> @@ -70,7 +70,7 @@ export default { :header-title-text="modalTitle" :footer-primary-button-text="__('Discard changes')" footer-primary-button-variant="danger" - @submit="discardFileChanges(path);" + @submit="discardFileChanges(path)" > {{ __("You will loose all changes you've made to this file. This action cannot be undone.") }} </gl-modal> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue index ce41fcdb087..0567ef54ff3 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue @@ -33,7 +33,7 @@ export default { data-container="body" data-boundary="viewport" data-placement="bottom" - @click.stop.prevent="unstageChange(path);" + @click.stop.prevent="unstageChange(path)" > <icon :size="16" name="redo" class="ml-auto mr-auto" /> </button> diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue index 5f99261ec39..732fa0786b0 100644 --- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue +++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue @@ -40,7 +40,7 @@ export default { 'is-active': viewer === $options.viewerTypes.mr, }" href="#" - @click.prevent="changeMode($options.viewerTypes.mr);" + @click.prevent="changeMode($options.viewerTypes.mr)" > <strong class="dropdown-menu-inner-title"> {{ mergeReviewLine }} </strong> <span class="dropdown-menu-inner-content"> @@ -54,7 +54,7 @@ export default { 'is-active': viewer === $options.viewerTypes.diff, }" href="#" - @click.prevent="changeMode($options.viewerTypes.diff);" + @click.prevent="changeMode($options.viewerTypes.diff)" > <strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong> <span class="dropdown-menu-inner-content"> diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue index bb391912572..0b0cd7b75eb 100644 --- a/app/assets/javascripts/ide/components/file_finder/index.vue +++ b/app/assets/javascripts/ide/components/file_finder/index.vue @@ -164,7 +164,7 @@ export default { </script> <template> - <div class="ide-file-finder-overlay" @mousedown.self="toggleFileFinder(false);"> + <div class="ide-file-finder-overlay" @mousedown.self="toggleFileFinder(false)"> <div class="dropdown-menu diff-file-changes ide-file-finder show"> <div class="dropdown-input"> <input @@ -174,8 +174,8 @@ export default { type="search" class="dropdown-input-field" autocomplete="off" - @keydown="onKeydown($event);" - @keyup="onKeyup($event);" + @keydown="onKeydown($event)" + @keyup="onKeyup($event)" /> <i :class="{ diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue index 414ea9c7d4d..343e0cca672 100644 --- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue +++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue @@ -91,7 +91,7 @@ export default { <gl-loading-icon v-if="showLoading" :size="2" /> <ul v-else> <li v-for="(item, index) in outputData" :key="index"> - <button type="button" @click="clickItem(item);">{{ item.name }}</button> + <button type="button" @click="clickItem(item)">{{ item.name }}</button> </li> </ul> </div> diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue index e2e0acc22b1..f1d40586903 100644 --- a/app/assets/javascripts/ide/components/ide_status_bar.vue +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -84,7 +84,7 @@ export default { <button type="button" class="p-0 border-0 h-50" - @click="openRightPane($options.rightSidebarViews.pipelines);" + @click="openRightPane($options.rightSidebarViews.pipelines)" > <ci-icon v-tooltip diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 9fc21adae7c..f93496132a4 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -43,7 +43,7 @@ export default { :show-label="false" class="d-flex border-0 p-0 mr-3 qa-new-file" icon="doc-new" - @click="openNewEntryModal({ type: 'blob' });" + @click="openNewEntryModal({ type: 'blob' })" /> <upload :show-label="false" @@ -56,7 +56,7 @@ export default { :show-label="false" class="d-flex border-0 p-0" icon="folder-new" - @click="openNewEntryModal({ type: 'tree' });" + @click="openNewEntryModal({ type: 'tree' })" /> </div> </template> diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue index e8fe5fc696d..7710bfb49ec 100644 --- a/app/assets/javascripts/ide/components/jobs/detail.vue +++ b/app/assets/javascripts/ide/components/jobs/detail.vue @@ -75,7 +75,7 @@ export default { <template> <div class="ide-pipeline build-page d-flex flex-column flex-fill"> <header class="ide-job-header d-flex align-items-center"> - <button class="btn btn-default btn-sm d-flex" @click="setDetailJob(null);"> + <button class="btn btn-default btn-sm d-flex" @click="setDetailJob(null)"> <icon name="chevron-left" /> {{ __('View jobs') }} </button> </header> diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue index ac2b0eddfb4..2d55ffb3c65 100644 --- a/app/assets/javascripts/ide/components/merge_requests/list.vue +++ b/app/assets/javascripts/ide/components/merge_requests/list.vue @@ -84,7 +84,7 @@ export default { :placeholder="__('Search merge requests')" @focus="onSearchFocus" @input="searchMergeRequests" - @removeToken="setSearchType(null);" + @removeToken="setSearchType(null)" /> <icon :size="18" name="search" class="input-icon" /> </div> @@ -102,7 +102,7 @@ export default { <button type="button" class="btn-link d-flex align-items-center" - @click.stop="setSearchType(searchType);" + @click.stop="setSearchType(searchType)" > <span class="d-flex append-right-default ide-search-list-current-icon"> <icon :size="18" name="search" /> diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index a50d729036f..d7a7b1b4d78 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -73,7 +73,7 @@ export default { :aria-label="__('Create new file or directory')" type="button" class="rounded border-0 d-flex ide-entry-dropdown-toggle" - @click.stop="openDropdown();" + @click.stop="openDropdown()" > <icon name="ellipsis_v" /> <icon name="arrow-down" /> </button> @@ -85,7 +85,7 @@ export default { class="d-flex" icon="doc-new" icon-classes="mr-2" - @click="createNewItem('blob');" + @click="createNewItem('blob')" /> </li> <li><upload :path="path" @create="createTempEntry" /></li> @@ -95,7 +95,7 @@ export default { class="d-flex" icon="folder-new" icon-classes="mr-2" - @click="createNewItem($options.modalTypes.tree);" + @click="createNewItem($options.modalTypes.tree)" /> </li> <li class="divider"></li> @@ -106,7 +106,7 @@ export default { class="d-flex" icon="pencil" icon-classes="mr-2" - @click="createNewItem($options.modalTypes.rename);" + @click="createNewItem($options.modalTypes.rename)" /> </li> <li> @@ -115,7 +115,7 @@ export default { class="d-flex" icon="remove" icon-classes="mr-2" - @click="deleteEntry(path);" + @click="deleteEntry(path)" /> </li> </ul> diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 63cbf41b89b..04ecd4ba4e7 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -114,7 +114,7 @@ export default { <button type="button" class="btn btn-missing p-1 pr-2 pl-2" - @click="createFromTemplate(template);" + @click="createFromTemplate(template)" > {{ template.name }} </button> diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue index 7a57ccf2dd3..2e6bd85feec 100644 --- a/app/assets/javascripts/ide/components/panes/right.vue +++ b/app/assets/javascripts/ide/components/panes/right.vue @@ -122,7 +122,7 @@ export default { data-placement="left" class="ide-sidebar-link is-right" type="button" - @click="clickTab($event, tab);" + @click="clickTab($event, tab)" > <icon :size="16" :name="tab.icon" /> </button> diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index c13d3ec094b..94a9e87369c 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -219,7 +219,7 @@ export default { <a href="javascript:void(0);" role="button" - @click.prevent="setFileViewMode({ file, viewMode: 'editor' });" + @click.prevent="setFileViewMode({ file, viewMode: 'editor' })" > <template v-if="viewer === $options.viewerTypes.edit"> {{ __('Edit') }} @@ -233,7 +233,7 @@ export default { <a href="javascript:void(0);" role="button" - @click.prevent="setFileViewMode({ file, viewMode: 'preview' });" + @click.prevent="setFileViewMode({ file, viewMode: 'preview' })" > {{ file.previewMode.previewTitle }} </a> diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index 4b87b83db8a..f6aa2295844 100644 --- a/app/assets/javascripts/ide/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -74,7 +74,7 @@ export default { active: tab.active, disabled: tab.pending, }" - @click="clickFile(tab);" + @click="clickFile(tab)" @mouseover="mouseOverTab" @mouseout="mouseOutTab" > @@ -88,7 +88,7 @@ export default { :disabled="tab.pending" type="button" class="multi-file-tab-close" - @click.stop.prevent="closeFile(tab);" + @click.stop.prevent="closeFile(tab)" > <icon v-if="!showChangedIcon" :size="12" name="close" /> <changed-file-icon v-else :file="tab" /> diff --git a/app/assets/javascripts/ide/components/resizable_panel.vue b/app/assets/javascripts/ide/components/resizable_panel.vue index a89de56ab5c..7277fcb7617 100644 --- a/app/assets/javascripts/ide/components/resizable_panel.vue +++ b/app/assets/javascripts/ide/components/resizable_panel.vue @@ -78,8 +78,8 @@ export default { :min-size="minSize" :max-size="$options.maxSize" :side="side === 'right' ? 'left' : 'right'" - @resize-start="setResizingStatus(true);" - @resize-end="setResizingStatus(false);" + @resize-start="setResizingStatus(true)" + @resize-end="setResizingStatus(false)" /> </div> </template> diff --git a/app/assets/javascripts/ide/components/shared/tokened_input.vue b/app/assets/javascripts/ide/components/shared/tokened_input.vue index f58e08c2cc9..de3e71dad92 100644 --- a/app/assets/javascripts/ide/components/shared/tokened_input.vue +++ b/app/assets/javascripts/ide/components/shared/tokened_input.vue @@ -76,8 +76,8 @@ export default { <button class="selectable btn-blank" type="button" - @click.stop="removeToken(token);" - @keyup.delete="removeToken(token);" + @click.stop="removeToken(token)" + @keyup.delete="removeToken(token)" > <div class="value-container rounded"> <div class="value">{{ token.label }}</div> diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index 6351948f750..5a2b680c2f7 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -10,13 +10,20 @@ import { parseBoolean } from '../lib/utils/common_utils'; Vue.use(Translate); /** + * Function that receives the default store and returns an extended one. + * @callback extendStoreCallback + * @param {Vuex.Store} store + * @param {Element} el + */ + +/** * Initialize the IDE on the given element. * * @param {Element} el - The element that will contain the IDE. * @param {Object} options - Extra options for the IDE (Used by EE). * @param {Component} options.rootComponent - * Component that overrides the root component. - * @param {(store:Vuex.Store, el:Element) => Vuex.Store} options.extendStore - + * @param {extendStoreCallback} options.extendStore - * Function that receives the default store and returns an extended one. */ export function initIde(el, options = {}) { diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue index 7f79e92067f..91332c21b52 100644 --- a/app/assets/javascripts/jobs/components/stages_dropdown.vue +++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue @@ -55,7 +55,7 @@ export default { <ul class="dropdown-menu"> <li v-for="stage in stages" :key="stage.name"> - <button type="button" class="js-stage-item stage-item" @click="onStageClick(stage);"> + <button type="button" class="js-stage-item stage-item" @click="onStageClick(stage)"> {{ stage.name }} </button> </li> diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index b8c3c237eb3..4314e5e1afb 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -11,48 +11,53 @@ function hideEndFade($scrollingTabs) { }); } +function initDeferred() { + $(document).trigger('init.scrolling-tabs'); +} + export default function initLayoutNav() { const contextualSidebar = new ContextualSidebar(); contextualSidebar.bindEvents(); initFlyOutNav(); - $(document) - .on('init.scrolling-tabs', () => { - const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized'); - $scrollingTabs.addClass('is-initialized'); + // We need to init it on DomContentLoaded as others could also call it + $(document).on('init.scrolling-tabs', () => { + const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized'); + $scrollingTabs.addClass('is-initialized'); - $(window) - .on('resize.nav', () => { - hideEndFade($scrollingTabs); - }) - .trigger('resize.nav'); + $(window) + .on('resize.nav', () => { + hideEndFade($scrollingTabs); + }) + .trigger('resize.nav'); - $scrollingTabs.on('scroll', function tabsScrollEvent() { - const $this = $(this); - const currentPosition = $this.scrollLeft(); - const maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); + $scrollingTabs.on('scroll', function tabsScrollEvent() { + const $this = $(this); + const currentPosition = $this.scrollLeft(); + const maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); - $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); - $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); - }); + $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); + $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); + }); - $scrollingTabs.each(function scrollTabsEachLoop() { - const $this = $(this); - const scrollingTabWidth = $this.width(); - const $active = $this.find('.active'); - const activeWidth = $active.width(); + $scrollingTabs.each(function scrollTabsEachLoop() { + const $this = $(this); + const scrollingTabWidth = $this.width(); + const $active = $this.find('.active'); + const activeWidth = $active.width(); - if ($active.length) { - const offset = $active.offset().left + activeWidth; + if ($active.length) { + const offset = $active.offset().left + activeWidth; - if (offset > scrollingTabWidth - 30) { - const scrollLeft = offset - scrollingTabWidth / 2 - activeWidth / 2; + if (offset > scrollingTabWidth - 30) { + const scrollLeft = offset - scrollingTabWidth / 2 - activeWidth / 2; - $this.scrollLeft(scrollLeft); - } + $this.scrollLeft(scrollLeft); } - }); - }) - .trigger('init.scrolling-tabs'); + } + }); + }); + + requestIdleCallback(initDeferred); } diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index fc34d243dd7..3b6a57dad44 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,3 +1,7 @@ +/** + * @module common-utils + */ + import $ from 'jquery'; import axios from './axios_utils'; import { getLocationHash } from './url_utility'; @@ -426,13 +430,14 @@ export const historyPushState = newUrl => { }; /** - * Returns true for a String "true" and false otherwise. - * This is the opposite of Boolean(...).toString() + * Returns true for a String value of "true" and false otherwise. + * This is the opposite of Boolean(...).toString(). + * `parseBoolean` is idempotent. * * @param {String} value * @returns {Boolean} */ -export const parseBoolean = value => value === 'true'; +export const parseBoolean = value => (value && value.toString()) === 'true'; /** * Converts permission provided as strings to booleans. @@ -450,10 +455,16 @@ export const convertPermissionToBoolean = permission => { }; /** + * @callback backOffCallback + * @param {Function} next + * @param {Function} stop + */ + +/** * Back Off exponential algorithm * backOff :: (Function<next, stop>, Number) -> Promise<Any, Error> * - * @param {Function<next, stop>} fn function to be called + * @param {backOffCallback} fn function to be called * @param {Number} timeout * @return {Promise<Any, Error>} * @example diff --git a/app/assets/javascripts/locale/sprintf.js b/app/assets/javascripts/locale/sprintf.js index 5246c49842e..68b64a3a16a 100644 --- a/app/assets/javascripts/locale/sprintf.js +++ b/app/assets/javascripts/locale/sprintf.js @@ -4,8 +4,8 @@ import _ from 'underscore'; Very limited implementation of sprintf supporting only named parameters. @param input (translated) text with parameters (e.g. '%{num_users} users use us') - @param parameters object mapping parameter names to values (e.g. { num_users: 5 }) - @param escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape) + @param {Object} parameters object mapping parameter names to values (e.g. { num_users: 5 }) + @param {Boolean} escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape) @returns {String} the text with parameters replaces (e.g. '5 users use us') @see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index 64a1df80a8e..309b73f5a4d 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -257,8 +257,8 @@ export default { <template> <div class="prometheus-graph" - @mouseover="showFlagContent = true;" - @mouseleave="showFlagContent = false;" + @mouseover="showFlagContent = true" + @mouseleave="showFlagContent = false" > <div class="prometheus-graph-header"> <h5 class="prometheus-graph-title">{{ graphData.title }}</h5> @@ -300,7 +300,7 @@ export default { :height="graphHeight - 100" class="prometheus-graph-overlay" transform="translate(-5, 20)" - @mousemove="handleMouseOverGraph($event);" + @mousemove="handleMouseOverGraph($event)" /> </svg> <svg v-else :viewBox="innerViewBox" class="js-no-data-to-display"> diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 7c17147dd01..d669ba5a8fa 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -357,9 +357,9 @@ js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input" data-supports-quick-actions="true" aria-label="Description" placeholder="Write a comment or drag your files here…" - @keydown.up="editCurrentUserLastNote();" - @keydown.meta.enter="handleSave();" - @keydown.ctrl.enter="handleSave();" + @keydown.up="editCurrentUserLastNote()" + @keydown.meta.enter="handleSave()" + @keydown.ctrl.enter="handleSave()" > </textarea> </markdown-field> @@ -373,7 +373,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" class="btn btn-success js-comment-button js-comment-submit-button qa-comment-button" type="submit" - @click.prevent="handleSave();" + @click.prevent="handleSave()" > {{ __(commentButtonTitle) }} </button> @@ -394,7 +394,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" <button type="button" class="btn btn-transparent" - @click.prevent="setNoteType('comment');" + @click.prevent="setNoteType('comment')" > <i aria-hidden="true" class="fa fa-check icon"> </i> <div class="description"> @@ -408,7 +408,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" <button type="button" class="btn btn-transparent qa-discussion-option" - @click.prevent="setNoteType('discussion');" + @click.prevent="setNoteType('discussion')" > <i aria-hidden="true" class="fa fa-check icon"> </i> <div class="description"> @@ -429,7 +429,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" ]" :disabled="isToggleStateButtonLoading || isSubmitting" :label="issueActionButtonTitle" - @click="handleSave(true);" + @click="handleSave(true)" /> </div> </form> diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 2d7c04ea614..e03d6e9cd02 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -112,7 +112,7 @@ export default { :class="{ 'is-active': filter.value === currentValue }" class="qa-filter-options" type="button" - @click="selectFilter(filter.value);" + @click="selectFilter(filter.value)" > {{ filter.title }} </button> diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index bde00ea87ff..3efdd1c5c17 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -174,7 +174,7 @@ export default { data-placement="bottom" class="btn award-control" type="button" - @click="handleAward(awardName);" + @click="handleAward(awardName)" > <span v-html="getAwardHTML(awardName)"></span> <span class="award-control-text js-counter">{{ awardList.length }}</span> diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index db62ddb3ecd..6dbb858e93d 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -219,10 +219,10 @@ export default { class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input" aria-label="Description" placeholder="Write a comment or drag your files here…" - @keydown.meta.enter="handleKeySubmit();" - @keydown.ctrl.enter="handleKeySubmit();" - @keydown.up="editMyLastNote();" - @keydown.esc="cancelHandler(true);" + @keydown.meta.enter="handleKeySubmit()" + @keydown.ctrl.enter="handleKeySubmit()" + @keydown.up="editMyLastNote()" + @keydown.esc="cancelHandler(true)" ></textarea> </markdown-field> <div class="note-form-actions clearfix"> @@ -230,21 +230,21 @@ export default { :disabled="isDisabled" type="button" class="js-vue-issue-save btn btn-success js-comment-button qa-reply-comment-button" - @click="handleUpdate();" + @click="handleUpdate()" > {{ saveButtonTitle }} </button> <button v-if="discussion.resolvable" class="btn btn-nr btn-default append-right-10 js-comment-resolve-button" - @click.prevent="handleUpdate(true);" + @click.prevent="handleUpdate(true)" > {{ resolveButtonTitle }} </button> <button class="btn btn-cancel note-edit-cancel js-close-discussion-note-form" type="button" - @click="cancelHandler();" + @click="cancelHandler()" > Cancel </button> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 7c3f5d00308..4480ec74182 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -440,7 +440,7 @@ Please check your network connection and try again.`; <button type="button" class="btn btn-default ml-sm-2" - @click="resolveHandler();" + @click="resolveHandler()" > <i v-if="isResolving" aria-hidden="true" class="fa fa-spinner fa-spin"></i> {{ resolveButtonTitle }} diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index db2a4041ec0..bd4309e47ad 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -70,7 +70,7 @@ export default { :checked="isEditable" class="label-bold" type="radio" - @click="toggleCustomInput(true);" + @click="toggleCustomInput(true)" /> <label for="custom"> {{ s__('PipelineSheduleIntervalPattern|Custom') }} </label> @@ -88,7 +88,7 @@ export default { :value="cronIntervalPresets.everyDay" class="label-bold" type="radio" - @click="toggleCustomInput(false);" + @click="toggleCustomInput(false)" /> <label class="label-bold" for="every-day"> {{ __('Every day (at 4:00am)') }} </label> @@ -102,7 +102,7 @@ export default { :value="cronIntervalPresets.everyWeek" class="label-bold" type="radio" - @click="toggleCustomInput(false);" + @click="toggleCustomInput(false)" /> <label class="label-bold" for="every-week"> @@ -118,7 +118,7 @@ export default { :value="cronIntervalPresets.everyMonth" class="label-bold" type="radio" - @click="toggleCustomInput(false);" + @click="toggleCustomInput(false)" /> <label class="label-bold" for="every-month"> diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index 59cebaba717..a49dc311bd0 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -1,59 +1,20 @@ <script> -import _ from 'underscore'; import { GlLoadingIcon } from '@gitlab/ui'; import StageColumnComponent from './stage_column_component.vue'; +import GraphMixin from '../../mixins/graph_component_mixin'; export default { components: { StageColumnComponent, GlLoadingIcon, }, - props: { - isLoading: { - type: Boolean, - required: true, - }, - pipeline: { - type: Object, - required: true, - }, - }, - computed: { - graph() { - return this.pipeline.details && this.pipeline.details.stages; - }, - }, - methods: { - capitalizeStageName(name) { - const escapedName = _.escape(name); - return escapedName.charAt(0).toUpperCase() + escapedName.slice(1); - }, - isFirstColumn(index) { - return index === 0; - }, - stageConnectorClass(index, stage) { - let className; - - // If it's the first stage column and only has one job - if (index === 0 && stage.groups.length === 1) { - className = 'no-margin'; - } else if (index > 0) { - // If it is not the first column - className = 'left-margin'; - } - - return className; - }, - refreshPipelineGraph() { - this.$emit('refreshPipelineGraph'); - }, - }, + mixins: [GraphMixin], }; </script> <template> <div class="build-content middle-block js-pipeline-graph"> <div class="pipeline-visualization pipeline-graph pipeline-tab-content"> - <div class="text-center"><gl-loading-icon v-if="isLoading" :size="3" /></div> + <div v-if="isLoading" class="m-auto"><gl-loading-icon :size="3" /></div> <ul v-if="!isLoading" class="stage-column-list"> <stage-column-component diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue index 2e9f2519fcb..0152e2fbe04 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -77,7 +77,7 @@ export default { :class="{ disabled: isActionDisabled(action) }" :disabled="isActionDisabled(action)" class="js-pipeline-action-link no-btn btn" - @click="onClickAction(action);" + @click="onClickAction(action)" > {{ action.name }} <span v-if="action.scheduled_at" class="pull-right"> diff --git a/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js new file mode 100644 index 00000000000..66e9476dadf --- /dev/null +++ b/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js @@ -0,0 +1,44 @@ +import _ from 'underscore'; + +export default { + props: { + isLoading: { + type: Boolean, + required: true, + }, + pipeline: { + type: Object, + required: true, + }, + }, + computed: { + graph() { + return this.pipeline.details && this.pipeline.details.stages; + }, + }, + methods: { + capitalizeStageName(name) { + const escapedName = _.escape(name); + return escapedName.charAt(0).toUpperCase() + escapedName.slice(1); + }, + isFirstColumn(index) { + return index === 0; + }, + stageConnectorClass(index, stage) { + let className; + + // If it's the first stage column and only has one job + if (index === 0 && stage.groups.length === 1) { + className = 'no-margin'; + } else if (index > 0) { + // If it is not the first column + className = 'left-margin'; + } + + return className; + }, + refreshPipelineGraph() { + this.$emit('refreshPipelineGraph'); + }, + }, +}; diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue index 21095fcba16..83811ab489a 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue @@ -108,9 +108,7 @@ export default { </span> </li> <li v-for="result in results" :key="result.id"> - <button type="button" @click.prevent="setItem(result.name);"> - {{ result.name }} - </button> + <button type="button" @click.prevent="setItem(result.name)">{{ result.name }}</button> </li> </ul> </div> diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue index 056584c8865..a2eb79af4f9 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue @@ -169,7 +169,7 @@ export default { </span> </li> <li v-for="result in results" :key="result.project_number"> - <button type="button" @click.prevent="setItem(result);">{{ result.name }}</button> + <button type="button" @click.prevent="setItem(result)">{{ result.name }}</button> </li> </ul> </div> diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue index 728616a441f..5f8a4946f4a 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue @@ -82,9 +82,7 @@ export default { </span> </li> <li v-for="result in results" :key="result.id"> - <button type="button" @click.prevent="setItem(result.name);"> - {{ result.name }} - </button> + <button type="button" @click.prevent="setItem(result.name)">{{ result.name }}</button> </li> </ul> </div> diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 2c19973a114..81fe0a48c06 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -106,7 +106,7 @@ export default { :aria-label="s__('ContainerRegistry|Remove tag')" variant="danger" class="js-delete-registry d-none d-sm-block float-right" - @click="handleDeleteRegistry(item);" + @click="handleDeleteRegistry(item)" > <icon name="remove" /> </gl-button> diff --git a/app/assets/javascripts/reports/components/modal_open_name.vue b/app/assets/javascripts/reports/components/modal_open_name.vue index 118e4b02c46..4f81cee2a38 100644 --- a/app/assets/javascripts/reports/components/modal_open_name.vue +++ b/app/assets/javascripts/reports/components/modal_open_name.vue @@ -26,7 +26,7 @@ export default { <button type="button" class="btn-link btn-blank text-left break-link vulnerability-name-button" - @click="handleIssueClick();" + @click="handleIssueClick()" > {{ issue.title }} </button> diff --git a/app/assets/javascripts/reports/components/test_issue_body.vue b/app/assets/javascripts/reports/components/test_issue_body.vue index 938e83de546..7700f49bf7d 100644 --- a/app/assets/javascripts/reports/components/test_issue_body.vue +++ b/app/assets/javascripts/reports/components/test_issue_body.vue @@ -30,7 +30,7 @@ export default { <button type="button" class="btn-link btn-blank text-left break-link vulnerability-name-button" - @click="openModal({ issue });" + @click="openModal({ issue })" > <div v-if="isNew" class="badge badge-danger append-right-5">{{ s__('New') }}</div> {{ issue.name }} diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index f04f7606976..7f86741ed29 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -219,7 +219,7 @@ export default { name="button" type="button" class="js-clear-user-status-button clear-user-status btn" - @click="clearStatusInputs();" + @click="clearStatusInputs()" > <icon name="close" /> </button> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue index d3a4f9c81e0..c03b2a68c78 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue @@ -102,13 +102,13 @@ export default { /> <div class="title hide-collapsed"> {{ __('Time tracking') }} - <div v-if="!showHelpState" class="help-button float-right" @click="toggleHelpState(true);"> + <div v-if="!showHelpState" class="help-button float-right" @click="toggleHelpState(true)"> <i class="fa fa-question-circle" aria-hidden="true"> </i> </div> <div v-if="showHelpState" class="close-help-button float-right" - @click="toggleHelpState(false);" + @click="toggleHelpState(false)" > <i class="fa fa-close" aria-hidden="true"> </i> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index 84c8a3464a5..0cafa73362e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -12,7 +12,7 @@ export default { name: 'ReadyToMerge', components: { statusIcon, - 'squash-before-merge': SquashBeforeMerge, + SquashBeforeMerge, }, props: { mr: { type: Object, required: true }, @@ -28,6 +28,7 @@ export default { isMakingRequest: false, isMergingImmediately: false, commitMessage: this.mr.commitMessage, + squashBeforeMerge: this.mr.squash, successSvg, warningSvg, }; @@ -110,12 +111,6 @@ export default { return enableSquashBeforeMerge && commitsCount > 1; }, }, - created() { - eventHub.$on('MRWidgetUpdateSquash', this.handleUpdateSquash); - }, - beforeDestroy() { - eventHub.$off('MRWidgetUpdateSquash', this.handleUpdateSquash); - }, methods: { shouldShowMergeControls() { return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText; @@ -143,7 +138,7 @@ export default { commit_message: this.commitMessage, merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds, should_remove_source_branch: this.removeSourceBranch === true, - squash: this.mr.squash, + squash: this.squashBeforeMerge, }; this.isMakingRequest = true; @@ -166,9 +161,6 @@ export default { new Flash('Something went wrong. Please try again.'); // eslint-disable-line }); }, - handleUpdateSquash(val) { - this.mr.squash = val; - }, initiateMergePolling() { simplePoll((continuePolling, stopPolling) => { this.handleMergePolling(continuePolling, stopPolling); @@ -249,7 +241,7 @@ export default { :class="mergeButtonClass" type="button" class="qa-merge-button" - @click="handleMergeButtonClick();" + @click="handleMergeButtonClick()" > <i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"></i> {{ mergeButtonText }} @@ -273,7 +265,7 @@ export default { <a class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option" href="#" - @click.prevent="handleMergeButtonClick(true);" + @click.prevent="handleMergeButtonClick(true)" > <span class="media"> <span class="merge-opt-icon" aria-hidden="true" v-html="successSvg"></span> @@ -285,7 +277,7 @@ export default { <a class="accept-merge-request qa-merge-immediately-option" href="#" - @click.prevent="handleMergeButtonClick(false, true);" + @click.prevent="handleMergeButtonClick(false, true)" > <span class="media"> <span class="merge-opt-icon" aria-hidden="true" v-html="warningSvg"></span> @@ -311,8 +303,9 @@ export default { <!-- Placeholder for EE extension of this component --> <squash-before-merge v-if="shouldShowSquashBeforeMerge" - :mr="mr" - :is-merge-button-disabled="isMergeButtonDisabled" + v-model="squashBeforeMerge" + :help-path="mr.squashBeforeMergeHelpPath" + :is-disabled="isMergeButtonDisabled" /> <span v-if="mr.ffOnlyEnabled" class="js-fast-forward-message"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue index e71acf0d7dd..b1f5655a15a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue @@ -1,6 +1,5 @@ <script> import Icon from '~/vue_shared/components/icon.vue'; -import eventHub from '~/vue_merge_request_widget/event_hub'; import tooltip from '~/vue_shared/directives/tooltip'; export default { @@ -11,23 +10,19 @@ export default { tooltip, }, props: { - mr: { - type: Object, - required: true, - }, - isMergeButtonDisabled: { + value: { type: Boolean, required: true, }, - }, - data() { - return { - squashBeforeMerge: this.mr.squash, - }; - }, - methods: { - updateSquashModel() { - eventHub.$emit('MRWidgetUpdateSquash', this.squashBeforeMerge); + helpPath: { + type: String, + required: false, + default: '', + }, + isDisabled: { + type: Boolean, + required: false, + default: false, }, }, }; @@ -37,18 +32,19 @@ export default { <div class="accept-control inline"> <label class="merge-param-checkbox"> <input - v-model="squashBeforeMerge" - :disabled="isMergeButtonDisabled" + :checked="value" + :disabled="isDisabled" type="checkbox" name="squash" class="qa-squash-checkbox" - @change="updateSquashModel" + @change="$emit('input', $event.target.checked)" /> {{ __('Squash commits') }} </label> <a + v-if="helpPath" v-tooltip - :href="mr.squashBeforeMergeHelpPath" + :href="helpPath" data-title="About this feature" data-placement="bottom" target="_blank" diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index b7f12076958..5a9d86594b1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -32,7 +32,6 @@ import MRWidgetStore from './stores/ee_switch_mr_widget_store'; import MRWidgetService from './services/ee_switch_mr_widget_service'; import eventHub from './event_hub'; import stateMaps from './stores/ee_switch_state_maps'; -import SquashBeforeMerge from './components/states/squash_before_merge.vue'; import notify from '~/lib/utils/notify'; import SourceBranchRemovalStatus from './components/source_branch_removal_status.vue'; import GroupedTestReportsApp from '../reports/components/grouped_test_reports_app.vue'; @@ -59,7 +58,6 @@ export default { 'mr-widget-missing-branch': MissingBranchState, 'mr-widget-ready-to-merge': ReadyToMergeState, 'sha-mismatch': ShaMismatchState, - 'mr-widget-squash-before-merge': SquashBeforeMerge, 'mr-widget-checking': CheckingState, 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState, 'mr-widget-pipeline-blocked': PipelineBlockedState, diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue index 4abf795f7bd..eabf5d4bf60 100644 --- a/app/assets/javascripts/vue_shared/components/bar_chart.vue +++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue @@ -293,8 +293,8 @@ export default { :title="setTooltipTitle(data)" class="bar-rect" data-placement="top" - @mouseover="barHoveredIn(index);" - @mouseout="barHoveredOut(index);" + @mouseover="barHoveredIn(index)" + @mouseout="barHoveredOut(index)" /> </template> </g> diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue index 2129f90d497..36b3ee05456 100644 --- a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue +++ b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue @@ -95,7 +95,7 @@ export default { class="close float-right" data-dismiss="modal" aria-label="Close" - @click="emitCancel($event);" + @click="emitCancel($event)" > <span aria-hidden="true">×</span> </button> @@ -112,7 +112,7 @@ export default { type="button" class="btn" data-dismiss="modal" - @click="emitCancel($event);" + @click="emitCancel($event)" > {{ closeButtonLabel }} </button> @@ -130,7 +130,7 @@ export default { type="button" class="btn js-primary-button" data-dismiss="modal" - @click="emitSubmit($event);" + @click="emitSubmit($event)" > {{ primaryButtonLabel }} </button> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue index d5fda7e4ed3..cab92297ca7 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue @@ -75,7 +75,7 @@ export default { :class="{ active: mode === $options.imageViewMode.twoup, }" - @click="changeMode($options.imageViewMode.twoup);" + @click="changeMode($options.imageViewMode.twoup)" > {{ s__('ImageDiffViewer|2-up') }} </li> @@ -83,7 +83,7 @@ export default { :class="{ active: mode === $options.imageViewMode.swipe, }" - @click="changeMode($options.imageViewMode.swipe);" + @click="changeMode($options.imageViewMode.swipe)" > {{ s__('ImageDiffViewer|Swipe') }} </li> @@ -91,7 +91,7 @@ export default { :class="{ active: mode === $options.imageViewMode.onion, }" - @click="changeMode($options.imageViewMode.onion);" + @click="changeMode($options.imageViewMode.onion)" > {{ s__('ImageDiffViewer|Onion skin') }} </li> diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue index 4c884c55a30..f54033efc54 100644 --- a/app/assets/javascripts/vue_shared/components/file_row.vue +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -139,8 +139,8 @@ export default { class="file-row" role="button" @click="clickFile" - @mouseover="toggleHover(true);" - @mouseout="toggleHover(false);" + @mouseover="toggleHover(true)" + @mouseout="toggleHover(false)" > <div class="file-row-name-container"> <span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated"> diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue index faf4181bbaf..438851e5ac7 100644 --- a/app/assets/javascripts/vue_shared/components/gl_modal.vue +++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue @@ -81,7 +81,7 @@ export default { type="button" class="close js-modal-close-action" data-dismiss="modal" - @click="emitCancel($event);" + @click="emitCancel($event)" > <span aria-hidden="true">×</span> </button> @@ -96,7 +96,7 @@ export default { type="button" class="btn js-modal-cancel-action qa-modal-cancel-button" data-dismiss="modal" - @click="emitCancel($event);" + @click="emitCancel($event)" > {{ s__('Modal|Cancel') }} </button> @@ -105,7 +105,7 @@ export default { type="button" class="btn js-modal-primary-action qa-modal-primary-button" data-dismiss="modal" - @click="emitSubmit($event);" + @click="emitSubmit($event)" > {{ footerPrimaryButtonText }} </button> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index c830f5b49b6..3f45dc7853b 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -148,7 +148,7 @@ export default { :class="action.cssClass" container-class="d-inline" :label="action.label" - @click="onClickAction(action);" + @click="onClickAction(action)" /> </template> </section> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 937a2847a58..cc07ef46064 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -182,9 +182,9 @@ export default { this.hasSuggestion = data.references.suggestions && data.references.suggestions.length; } - this.$nextTick(() => { - $(this.$refs['markdown-preview']).renderGFM(); - }); + this.$nextTick() + .then(() => $(this.$refs['markdown-preview']).renderGFM()) + .catch(() => new Flash(__('Error rendering markdown preview'))); }, versionedPreviewPath() { diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index bf4d42670ee..dbfa32cd0ce 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -78,12 +78,7 @@ export default { <div class="md-header"> <ul class="nav-links clearfix"> <li :class="{ active: !previewMarkdown }" class="md-header-tab"> - <button - class="js-write-link" - tabindex="-1" - type="button" - @click="writeMarkdownTab($event);" - > + <button class="js-write-link" tabindex="-1" type="button" @click="writeMarkdownTab($event)"> Write </button> </li> @@ -92,7 +87,7 @@ export default { class="js-preview-link js-md-preview-button" tabindex="-1" type="button" - @click="previewMarkdownTab($event);" + @click="previewMarkdownTab($event)" > Preview </button> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue index 563e2f94fcc..c5a2aa1f2af 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue @@ -42,7 +42,7 @@ export default { <div class="md-suggestion-header border-bottom-0 mt-2"> <div class="qa-suggestion-diff-header font-weight-bold"> {{ __('Suggested change') }} - <a v-if="helpPagePath" :href="helpPagePath" :aria-label="__('Help')"> + <a v-if="helpPagePath" :href="helpPagePath" :aria-label="__('Help')" class="js-help-btn"> <icon name="question-o" css-classes="link-highlight" /> </a> </div> diff --git a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue index 09a64502819..f8983a3d29a 100644 --- a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue +++ b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue @@ -58,7 +58,7 @@ export default { active: tab.isActive, }" > - <a :class="`js-${scope}-tab-${tab.scope}`" role="button" @click="onTabClick(tab);"> + <a :class="`js-${scope}-tab-${tab.scope}`" role="button" @click="onTabClick(tab)"> {{ tab.name }} <span v-if="shouldRenderBadge(tab.count)" class="badge badge-pill"> {{ tab.count }} </span> diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index 31df26f7b05..b0af8399955 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -97,7 +97,7 @@ export default { v-html="note.note_html" ></div> <div v-if="hasMoreCommits" class="flex-list"> - <div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded;"> + <div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded"> <icon :name="toggleIcon" :size="8" class="append-right-5" /> <span>Toggle commit list</span> </div> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue index 82067129c57..6c0c7f15943 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue @@ -134,7 +134,7 @@ export default { <button type="button" class="btn-blank btn-link btn-secondary-hover-link" - @click="newDateSelected(null);" + @click="newDateSelected(null)" > remove </button> diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 01e655d27e5..2a34b4630f2 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -149,7 +149,7 @@ export default { }" class="page-item" > - <a class="page-link" @click.prevent="changePage(item.title, item.disabled);"> + <a class="page-link" @click.prevent="changePage(item.title, item.disabled)"> {{ item.title }} </a> </li> diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index 587127bb059..c8357f7751c 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -225,7 +225,7 @@ h3.popover-header { } .info-well { - background: $theme-gray-50; + background: $gray-50; color: $gl-text-color; border: 1px solid $border-color; border-radius: 4px; diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 43d4044033f..4fb787887a1 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -204,7 +204,7 @@ a { [class^="skeleton-line-"] { position: relative; - background-color: $theme-gray-100; + background-color: $gray-100; height: 10px; overflow: hidden; @@ -220,10 +220,10 @@ a { background-size: cover; background-image: linear-gradient( to right, - $theme-gray-100 0%, - $theme-gray-50 20%, - $theme-gray-100 40%, - $theme-gray-100 100% + $gray-100 0%, + $gray-50 20%, + $gray-100 40%, + $gray-100 100% ); height: 10px; } diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss index 7a95db5976d..ad650d45314 100644 --- a/app/assets/stylesheets/framework/awards.scss +++ b/app/assets/stylesheets/framework/awards.scss @@ -176,7 +176,7 @@ &.user-authored { cursor: default; background-color: $gray-light; - border-color: $theme-gray-200; + border-color: $gray-200; color: $gl-text-color-disabled; gl-emoji { diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index a4a9276c580..5d2cbdde8dc 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -483,7 +483,7 @@ // All disabled buttons, regardless of color, type, etc %disabled { background-color: $gray-light !important; - border-color: $theme-gray-200 !important; + border-color: $gray-200 !important; color: $gl-text-color-disabled !important; opacity: 1 !important; cursor: default !important; diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 0a0ef2071e9..cbf9ee24ec5 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -261,7 +261,7 @@ label { right: 0.8em; top: 50%; transform: translateY(-50%); - color: $theme-gray-600; + color: $gray-600; } } diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss index 0ef50e139f2..418eafa153c 100644 --- a/app/assets/stylesheets/framework/gitlab_theme.scss +++ b/app/assets/stylesheets/framework/gitlab_theme.scss @@ -282,31 +282,31 @@ body { &.ui-dark { @include gitlab-theme( - $theme-gray-200, - $theme-gray-500, - $theme-gray-700, - $theme-gray-800, - $theme-gray-900, + $gray-200, + $gray-500, + $gray-700, + $gray-800, + $gray-900, $white-light ); } &.ui-light { @include gitlab-theme( - $theme-gray-700, - $theme-gray-800, - $theme-gray-700, - $theme-gray-700, - $theme-gray-100, - $theme-gray-700 + $gray-700, + $gray-800, + $gray-700, + $gray-700, + $gray-100, + $gray-700 ); .navbar-gitlab { - background-color: $theme-gray-100; + background-color: $gray-100; box-shadow: 0 1px 0 0 $border-color; .logo-text svg { - fill: $theme-gray-900; + fill: $gray-900; } .navbar-sub-nav, @@ -315,7 +315,7 @@ body { > a:hover, > a:focus, > button:hover { - color: $theme-gray-900; + color: $gray-900; } &.active > a, @@ -329,8 +329,8 @@ body { .container-fluid { .navbar-toggler, .navbar-toggler:hover { - color: $theme-gray-700; - border-left: 1px solid $theme-gray-200; + color: $gray-700; + border-left: 1px solid $gray-200; } } } @@ -348,7 +348,7 @@ body { .search-input-wrap { .search-icon { - fill: $theme-gray-200; + fill: $gray-200; } .search-input { @@ -359,16 +359,16 @@ body { .nav-sidebar li.active { > a { - color: $theme-gray-900; + color: $gray-900; } svg { - fill: $theme-gray-900; + fill: $gray-900; } } .sidebar-top-level-items > li.active .badge.badge-pill { - color: $theme-gray-900; + color: $gray-900; } } } diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 8db7d63266e..49b9b7014ae 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -87,8 +87,8 @@ display: flex; align-items: center; justify-content: center; - border: $border-size solid $theme-gray-400; + border: $border-size solid $gray-400; border-radius: 50%; padding: $gl-padding-8 - $border-size; - color: $theme-gray-700; + color: $gray-700; } diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index ce46d760d7b..679148ddf7b 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -158,7 +158,7 @@ max-height: calc(100vh - 100px); } - table { + table:not(.js-syntax-highlight) { @include markdown-table; } } diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss index 29a2d5881f7..2255d3be75b 100644 --- a/app/assets/stylesheets/framework/stacked_progress_bar.scss +++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss @@ -3,7 +3,7 @@ height: 16px; border-radius: 10px; overflow: hidden; - background-color: $theme-gray-100; + background-color: $gray-100; .status-unavailable, .status-green, @@ -24,7 +24,7 @@ .status-unavailable { padding: 0 10px; - color: $theme-gray-700; + color: $gray-700; } .status-green { @@ -36,11 +36,11 @@ } .status-neutral { - background-color: $theme-gray-200; + background-color: $gray-200; color: $gl-gray-dark; &:hover { - background-color: $theme-gray-300; + background-color: $gray-300; } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 0c81dc2e156..45dab036d35 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -143,7 +143,7 @@ margin: 0 0 16px; } - table { + table:not(.js-syntax-highlight) { @extend .table; @extend .table-bordered; margin: 16px 0; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 242977e8543..c1666c728f3 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -97,6 +97,18 @@ $red-800: #8b2615; $red-900: #711e11; $red-950: #4b140b; +$gray-50: #fafafa; +$gray-100: #f2f2f2; +$gray-200: #dfdfdf; +$gray-300: #cccccc; +$gray-400: #bababa; +$gray-500: #a7a7a7; +$gray-600: #919191; +$gray-700: #707070; +$gray-800: #4f4f4f; +$gray-900: #2e2e2e; +$gray-950: #1f1f1f; + // GitLab themes $indigo-50: #f7f7ff; @@ -111,18 +123,6 @@ $indigo-800: #393982; $indigo-900: #292961; $indigo-950: #1a1a40; -$theme-gray-50: #fafafa; -$theme-gray-100: #f2f2f2; -$theme-gray-200: #dfdfdf; -$theme-gray-300: #cccccc; -$theme-gray-400: #bababa; -$theme-gray-500: #a7a7a7; -$theme-gray-600: #919191; -$theme-gray-700: #707070; -$theme-gray-800: #4f4f4f; -$theme-gray-900: #2e2e2e; -$theme-gray-950: #1f1f1f; - $theme-blue-50: #f4f8fc; $theme-blue-100: #e6edf5; $theme-blue-200: #c8d7e6; diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss index 069f45bff49..d5f8e3fb4ee 100644 --- a/app/assets/stylesheets/framework/variables_overrides.scss +++ b/app/assets/stylesheets/framework/variables_overrides.scss @@ -5,7 +5,7 @@ $secondary: $gray-light; $input-disabled-bg: $gray-light; -$input-border-color: $theme-gray-200; +$input-border-color: $gray-200; $input-color: $gl-text-color; $font-family-sans-serif: $regular-font; $font-family-monospace: $monospace-font; @@ -20,7 +20,7 @@ $warning: $orange-500; $danger: $red-500; $zindex-modal-backdrop: 1040; $nav-divider-margin-y: ($grid-size / 2); -$dropdown-divider-bg: $theme-gray-200; +$dropdown-divider-bg: $gray-200; $dropdown-item-padding-y: 8px; $dropdown-item-padding-x: 12px; $popover-max-width: 300px; diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss index 98d0a2d43ea..553cc44fe83 100644 --- a/app/assets/stylesheets/page_bundles/ide.scss +++ b/app/assets/stylesheets/page_bundles/ide.scss @@ -130,7 +130,7 @@ $ide-commit-header-height: 48px; background: none; border: 0; border-radius: $border-radius-default; - color: $theme-gray-900; + color: $gray-900; svg { position: relative; @@ -145,7 +145,7 @@ $ide-commit-header-height: 48px; } &:not([disabled]):hover { - background-color: $theme-gray-200; + background-color: $gray-200; } &:not([disabled]):focus { @@ -265,7 +265,7 @@ $ide-commit-header-height: 48px; .margin { background-color: $white-light; - border-right: 1px solid $theme-gray-100; + border-right: 1px solid $gray-100; .line-insert { border-right: 1px solid $line-added-dark; @@ -292,7 +292,7 @@ $ide-commit-header-height: 48px; .monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input { - background-color: $theme-gray-50; + background-color: $gray-50; } } } @@ -527,7 +527,7 @@ $ide-commit-header-height: 48px; display: block; margin-left: auto; margin-right: auto; - color: $theme-gray-700; + color: $gray-700; } .file-status-icon { @@ -551,7 +551,7 @@ $ide-commit-header-height: 48px; &:hover, &:focus { - background: $theme-gray-100; + background: $gray-100; outline: 0; @@ -563,7 +563,7 @@ $ide-commit-header-height: 48px; } &:active { - background: $theme-gray-200; + background: $gray-200; } &.is-active { @@ -767,12 +767,12 @@ $ide-commit-header-height: 48px; &:hover { color: $gl-text-color; - background-color: $theme-gray-100; + background-color: $gray-100; } &:focus { color: $gl-text-color; - background-color: $theme-gray-200; + background-color: $gray-200; } &.active { @@ -1273,10 +1273,10 @@ $ide-commit-header-height: 48px; .ide-entry-dropdown-toggle { padding: $gl-padding-4; color: $gl-text-color; - background-color: $theme-gray-100; + background-color: $gray-100; &:hover { - background-color: $theme-gray-200; + background-color: $gray-200; } &:active, @@ -1331,7 +1331,7 @@ $ide-commit-header-height: 48px; &:focus { outline: 0; box-shadow: none; - border-color: $theme-gray-200; + border-color: $gray-200; } } @@ -1358,7 +1358,7 @@ $ide-commit-header-height: 48px; .ide-commit-editor-header { height: 65px; padding: 8px 16px; - background-color: $theme-gray-50; + background-color: $gray-50; box-shadow: inset 0 -1px $white-dark; } @@ -1370,7 +1370,7 @@ $ide-commit-header-height: 48px; .ide-file-icon-holder { display: flex; align-items: center; - color: $theme-gray-700; + color: $gray-700; } .file-row:hover, diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index c5a0eaaf704..e1d1e598da8 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -277,7 +277,7 @@ padding: $gl-padding; background: $white-light; border-radius: $border-radius-default; - border: 1px solid $theme-gray-200; + border: 1px solid $gray-200; box-shadow: 0 1px 2px $issue-boards-card-shadow; list-style: none; line-height: $gl-padding; @@ -669,7 +669,7 @@ } .board-card-info-icon { - color: $theme-gray-600; + color: $gray-600; margin-right: $gl-padding-4; } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index bfdc2366239..65f46e3852a 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -261,7 +261,7 @@ .trigger-variables-table-cell { font-size: $gl-font-size-small; line-height: $gl-line-height; - border: 1px solid $theme-gray-200; + border: 1px solid $gray-200; padding: $gl-padding-4 6px; width: 50%; vertical-align: top; diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 75166ffcada..b6abb792709 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -267,7 +267,7 @@ .prometheus-graph-cursor { position: absolute; - background: $theme-gray-600; + background: $gray-600; width: 1px; } @@ -309,7 +309,7 @@ > .arrow::after { border-top: 6px solid transparent; border-bottom: 6px solid transparent; - border-left: 4px solid $theme-gray-50; + border-left: 4px solid $gray-50; } .arrow-shadow { @@ -331,7 +331,7 @@ > .arrow::after { border-top: 6px solid transparent; border-bottom: 6px solid transparent; - border-right: 4px solid $theme-gray-50; + border-right: 4px solid $gray-50; } .arrow-shadow { @@ -364,7 +364,7 @@ } > .popover-title { - background-color: $theme-gray-50; + background-color: $gray-50; border-radius: $border-radius-default $border-radius-default 0 0; } } @@ -439,7 +439,7 @@ } > text { - fill: $theme-gray-600; + fill: $gray-600; font-size: 10px; } } @@ -482,5 +482,5 @@ } .prometheus-table-row-highlight { - background-color: $theme-gray-100; + background-color: $gray-100; } diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss index 4fb1a956fab..83b1680512d 100644 --- a/app/assets/stylesheets/pages/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss @@ -44,7 +44,7 @@ } .x-axis-text { - fill: $theme-gray-900; + fill: $gray-900; } .bar-rect { @@ -64,7 +64,7 @@ text { font-weight: bold; font-size: 12px; - fill: $theme-gray-800; + fill: $gray-800; } } } @@ -87,5 +87,5 @@ .animate-flicker { animation: flickerAnimation 1.5s infinite; - fill: $theme-gray-500; + fill: $gray-500; } diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index f0cb81e0bc3..ebbb5beed81 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -78,7 +78,7 @@ &:hover { background-color: $gray-darker; - color: $theme-gray-900; + color: $gray-900; } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index a1069aa9783..e0bdc1341b1 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -934,7 +934,7 @@ .sidebar-collapsed-divider { line-height: 5px; font-size: 12px; - color: $theme-gray-700; + color: $gray-700; + .sidebar-collapsed-icon { padding-top: 0; diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index d2b9470be69..2372640277e 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -82,7 +82,7 @@ justify-content: space-between; padding: $gl-padding; border-radius: $border-radius-default; - border: 1px solid $theme-gray-100; + border: 1px solid $gray-100; &:last-child { margin-bottom: 0; @@ -257,7 +257,7 @@ } .label-badge { - color: $theme-gray-900; + color: $gray-900; font-weight: $gl-font-weight-normal; padding: $gl-padding-4 $gl-padding-8; border-radius: $border-radius-default; @@ -269,7 +269,7 @@ } .label-badge-gray { - background-color: $theme-gray-100; + background-color: $gray-100; } .label-links { @@ -326,11 +326,11 @@ } .label-action { - color: $theme-gray-800; + color: $gray-800; cursor: pointer; svg { - fill: $theme-gray-800; + fill: $gray-800; } &:hover { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 221b4e934ff..1e4b8d8b7e4 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -64,7 +64,7 @@ &::before { content: ''; - border-left: 1px solid $theme-gray-200; + border-left: 1px solid $gray-200; position: absolute; left: 32px; top: -17px; @@ -907,7 +907,7 @@ } .btn svg { - fill: $theme-gray-700; + fill: $gray-700; } .dropdown-menu { @@ -951,7 +951,7 @@ .coverage { font-size: 12px; - color: $theme-gray-700; + color: $gray-700; line-height: initial; } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 94bf32945fc..15f3a2ef4a8 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -8,7 +8,7 @@ $status-box-line-height: 26px; padding: $gl-padding-8; margin-top: $gl-padding-8; border-radius: $border-radius-default; - background-color: $theme-gray-100; + background-color: $gray-100; .milestone { border: 0; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 86f571dd90d..51f755c67af 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -147,7 +147,7 @@ } .sidebar-item-value & { - fill: $theme-gray-700; + fill: $gray-700; } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 69b7b80dbf4..23b9e4f9416 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -5,7 +5,7 @@ $note-form-margin-left: 72px; @mixin vertical-line($left) { &::before { content: ''; - border-left: 2px solid $theme-gray-100; + border-left: 2px solid $gray-100; position: absolute; top: 0; bottom: 0; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 7a47e0a2836..058b0ffef5f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -433,6 +433,7 @@ } .pipeline-tab-content { + display: flex; width: 100%; background-color: $gray-light; padding: $gl-padding; diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index a353f301d07..45e62913f37 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -60,11 +60,11 @@ } &.ui-dark { - background-color: $theme-gray-900; + background-color: $gray-900; } &.ui-light { - background-color: $theme-gray-200; + background-color: $gray-200; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 2d28333689f..505f6e036e3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -459,7 +459,7 @@ margin-right: $gl-padding-4; margin-bottom: $gl-padding-4; color: $gl-text-color-secondary; - background-color: $theme-gray-100; + background-color: $gray-100; line-height: $gl-btn-line-height; &:hover { @@ -914,7 +914,7 @@ } .repository-language-bar-tooltip-share { - color: $theme-gray-400; + color: $gray-400; } pre.light-well { @@ -1025,8 +1025,10 @@ pre.light-well { margin: 0; } - @include media-breakpoint-up(md) { - .description { + .description { + line-height: 1.5; + + @include media-breakpoint-up(md) { color: $gl-text-color; } } diff --git a/app/assets/stylesheets/pages/reports.scss b/app/assets/stylesheets/pages/reports.scss index ecd51aa06a4..f7619ccbd20 100644 --- a/app/assets/stylesheets/pages/reports.scss +++ b/app/assets/stylesheets/pages/reports.scss @@ -96,7 +96,7 @@ } &.neutral svg { - color: $theme-gray-700; + color: $gray-700; } .ci-status-icon { diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index ccfa4e00a5b..c5b9d1f6885 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -297,7 +297,7 @@ .btn-clipboard { background-color: $white-light; - border: 1px solid $theme-gray-200; + border: 1px solid $gray-200; } .deploy-token-help-block { diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index bfbbcba883f..d5ce790e2d9 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -4,10 +4,10 @@ class Projects::JobsController < Projects::ApplicationController include SendFileUpload include ContinueParams - before_action :build, except: [:index, :cancel_all] + before_action :build, except: [:index] before_action :authorize_read_build! before_action :authorize_update_build!, - except: [:index, :show, :status, :raw, :trace, :cancel_all, :erase] + except: [:index, :show, :status, :raw, :trace, :erase] before_action :authorize_erase_build!, only: [:erase] before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize] before_action :verify_api_request!, only: :terminal_websocket_authorize @@ -39,16 +39,6 @@ class Projects::JobsController < Projects::ApplicationController end # rubocop: enable CodeReuse/ActiveRecord - def cancel_all - return access_denied! unless can?(current_user, :update_build, project) - - @project.builds.running_or_pending.each do |build| - build.cancel if can?(current_user, :update_build, build) - end - - redirect_to project_jobs_path(project) - end - # rubocop: disable CodeReuse/ActiveRecord def show @pipeline = @build.pipeline diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 7b22bc8f98f..b3935ae350d 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -7,6 +7,15 @@ module EnvironmentsHelper } end + def environments_folder_list_view_data + { + "endpoint" => folder_project_environments_path(@project, @folder, format: :json), + "folder-name" => @folder, + "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, + "can-read-environment" => can?(current_user, :read_environment, @project).to_s + } + end + def metrics_data(project, environment) { "settings-path" => edit_project_service_path(project, 'prometheus'), diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 525725034a5..aa0c121fe99 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -30,4 +30,8 @@ class ProjectImportData < ActiveRecord::Base def merge_credentials(hash) self.credentials = credentials.to_h.merge(hash) unless hash.empty? end + + def clear_credentials + self.credentials = {} + end end diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index a3fa67c72bf..5eba7ddd75c 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -61,7 +61,10 @@ class RemoteMirror < ActiveRecord::Base timestamp = Time.now remote_mirror.update!( - last_update_at: timestamp, last_successful_update_at: timestamp, last_error: nil + last_update_at: timestamp, + last_successful_update_at: timestamp, + last_error: nil, + error_notification_sent: false ) end @@ -179,6 +182,10 @@ class RemoteMirror < ActiveRecord::Base project.repository.add_remote(remote_name, remote_url) end + def after_sent_notification + update_column(:error_notification_sent, true) + end + private def store_credentials @@ -221,7 +228,8 @@ class RemoteMirror < ActiveRecord::Base last_error: nil, last_update_at: nil, last_successful_update_at: nil, - update_status: 'finished' + update_status: 'finished', + error_notification_sent: false ) end diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb index 0efca895a50..9a243e07936 100644 --- a/app/uploaders/records_uploads.rb +++ b/app/uploaders/records_uploads.rb @@ -23,13 +23,23 @@ module RecordsUploads return unless model return unless file && file.exists? - Upload.transaction do - uploads.where(path: upload_path).delete_all - upload.delete if upload - - self.upload = build_upload.tap(&:save!) + # MySQL InnoDB may encounter a deadlock if a deletion and an + # insert is in the same transaction due to its next-key locking + # algorithm, so we need to skip the transaction. + # https://gitlab.com/gitlab-org/gitlab-ce/issues/55161#note_131556351 + if Gitlab::Database.mysql? + readd_upload + else + Upload.transaction { readd_upload } end end + + def readd_upload + uploads.where(path: upload_path).delete_all + upload.delete if upload + + self.upload = build_upload.tap(&:save!) + end # rubocop: enable CodeReuse/ActiveRecord def upload_path diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index e167e094240..ee2c5a13b8a 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,37 +1,37 @@ -- page_title "Account" +- page_title _('Account') - @content_class = "limit-container-width" unless fluid_layout - if current_user.ldap_user? .alert.alert-info - Some options are unavailable for LDAP accounts + = s_('Profiles|Some options are unavailable for LDAP accounts') .row.prepend-top-default .col-lg-4.profile-settings-sidebar %h4.prepend-top-0 - Two-Factor Authentication + = s_('Profiles|Two-Factor Authentication') %p - Increase your account's security by enabling Two-Factor Authentication (2FA). + = s_("Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)") .col-lg-8 %p - Status: #{current_user.two_factor_enabled? ? 'Enabled' : 'Disabled'} + #{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')} - if current_user.two_factor_enabled? - = link_to 'Manage two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-info' + = link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-info' - else .append-bottom-10 - = link_to 'Enable two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-success' + = link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-success' %hr - if display_providers_on_profile? .row.prepend-top-default .col-lg-4.profile-settings-sidebar %h4.prepend-top-0 - Social sign-in + = s_('Profiles|Social sign-in') %p - Activate signin with one of the following services + = s_('Profiles|Activate signin with one of the following services') .col-lg-8 %label.label-bold - Connected Accounts - %p Click on icon to activate signin with one of the following services + = s_('Profiles|Connected Accounts') + %p= s_('Profiles|Click on icon to activate signin with one of the following services') - button_based_providers.each do |provider| .provider-btn-group .provider-btn-image @@ -39,24 +39,24 @@ - if auth_active?(provider) - if unlink_allowed?(provider) = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do - Disconnect + = s_('Profiles|Disconnect') - else %a.provider-btn - Active + = s_('Profiles|Active') - else = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do - Connect + = s_('Profiles|Connect') = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: local_assigns[:group_saml_identities] %hr - if current_user.can_change_username? .row.prepend-top-default .col-lg-4.profile-settings-sidebar %h4.prepend-top-0.warning-title - Change username + = s_('Profiles|Change username') %p - Changing your username can have unintended side effects. + = s_('Profiles|Changing your username can have unintended side effects.') = succeed '.' do - = link_to 'Learn more', help_page_path('user/profile/index', anchor: 'changing-your-username'), target: '_blank' + = link_to s_('Profiles|Learn more'), help_page_path('user/profile/index', anchor: 'changing-your-username'), target: '_blank' .col-lg-8 - data = { initial_username: current_user.username, root_url: root_url, action_url: update_username_profile_path(format: :json) } #update-username{ data: data } diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index b7e1cf85cb7..aebd176af9b 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -1,8 +1,5 @@ - @no_container = true - page_title _("Environments") -#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), - "folder-name" => @folder, - "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, - "can-read-environment" => can?(current_user, :read_environment, @project).to_s, +#environments-folder-list-view{ data: { environments_data: environments_folder_list_view_data, "css-class" => container_class } } diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index 59592abcf6a..afea5268006 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -8,10 +8,6 @@ .nav-controls - if can?(current_user, :update_build, @project) - - if @all_builds.running_or_pending.limit(1).any? # rubocop: disable CodeReuse/ActiveRecord - = link_to 'Cancel running', cancel_all_project_jobs_path(@project), - data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post - - unless @repository.gitlab_ci_yml = link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info' diff --git a/app/views/projects/project_members/_groups.html.haml b/app/views/projects/project_members/_groups.html.haml index 3f05e06b0c6..b5d397e3065 100644 --- a/app/views/projects/project_members/_groups.html.haml +++ b/app/views/projects/project_members/_groups.html.haml @@ -1,7 +1,6 @@ .card.project-members-groups .card-header - Groups with access to - %strong= @project.name + = _("Groups with access to <strong>%{project_name}</strong>").html_safe % { project_name: sanitize_project_name(@project.name) } %span.badge.badge-pill= group_links.size %ul.content-list.members-list = render partial: 'shared/members/group', collection: group_links, as: :group_link diff --git a/app/views/projects/project_members/_new_project_group.html.haml b/app/views/projects/project_members/_new_project_group.html.haml index 88e68f89024..079811e4e79 100644 --- a/app/views/projects/project_members/_new_project_group.html.haml +++ b/app/views/projects/project_members/_new_project_group.html.haml @@ -10,8 +10,9 @@ = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control" = icon('chevron-down') .form-text.text-muted.append-bottom-10 - = link_to _("Read more"), help_page_path("user/permissions") - about role permissions + - permissions_docs_path = help_page_path('user/permissions') + - link_start = %q{<a href="%{url}">}.html_safe % { url: permissions_docs_path } + = _("%{link_start}Read more%{link_end} about role permissions").html_safe % { link_start: link_start, link_end: '</a>'.html_safe } .form-group = label_tag :expires_at, _('Access expiration date'), class: 'label-bold' .clearable-input diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index 1de7d9c6957..0590578c3fe 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -2,20 +2,21 @@ .col-sm-12 = form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f| .form-group - = label_tag :user_ids, "Select members to invite", class: "label-bold" + = label_tag :user_ids, _("Select members to invite"), class: "label-bold" = users_select_tag(:user_ids, multiple: true, class: "input-clamp qa-member-select-input", scope: :all, email_user: true, placeholder: "Search for members to update or invite") .form-group - = label_tag :access_level, "Choose a role permission", class: "label-bold" + = label_tag :access_level, _("Choose a role permission"), class: "label-bold" .select-wrapper = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control" = icon('chevron-down') .form-text.text-muted.append-bottom-10 - = link_to "Read more", help_page_path("user/permissions") - about role permissions + - permissions_docs_path = help_page_path('user/permissions') + - link_start = %q{<a href="%{url}">}.html_safe % { url: permissions_docs_path } + = _("%{link_start}Read more%{link_end} about role permissions").html_safe % { link_start: link_start, link_end: '</a>'.html_safe } .form-group .clearable-input - = label_tag :expires_at, 'Access expiration date', class: 'label-bold' + = label_tag :expires_at, _('Access expiration date'), class: 'label-bold' = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' %i.clear-icon.js-clear-input - = f.submit "Add to project", class: "btn btn-success qa-add-member-button" - = link_to "Import", import_project_project_members_path(@project), class: "btn btn-default", title: "Import members from another project" + = f.submit _("Add to project"), class: "btn btn-success qa-add-member-button" + = link_to _("Import"), import_project_project_members_path(@project), class: "btn btn-default", title: _("Import members from another project") diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 9682f8ac922..b92ecf4412f 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -4,14 +4,13 @@ .card .card-header.flex-project-members-panel %span.flex-project-title - Members of - %strong= project.name + = _("Members of <strong>%{project_name}</strong>").html_safe % { project_name: sanitize_project_name(project.name) } %span.badge.badge-pill= members.total_count = form_tag project_project_members_path(project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do .form-group .position-relative - = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } - %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } + = search_field_tag :search, params[:search], { placeholder: _('Find existing members by name'), class: 'form-control', spellcheck: false } + %button.member-search-btn{ type: "submit", "aria-label" => _("Submit search") } = icon("search") = render 'shared/members/sort_dropdown' %ul.content-list.members-list.qa-members-list diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml index 8b93e81cd31..bcca943de6a 100644 --- a/app/views/projects/project_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -1,15 +1,15 @@ -- page_title "Import members" +- page_title _("Import members") %h3.page-title - Import members from another project + = _("Import members from another project") %p.light - Only project members will be imported. Group members will be skipped. + = _("Only project members will be imported. Group members will be skipped.") %hr = form_tag apply_import_project_project_members_path(@project), method: 'post' do .form-group.row - = label_tag :source_project_id, "Project", class: 'col-form-label col-sm-2' + = label_tag :source_project_id, _("Project"), class: 'col-form-label col-sm-2' .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(@projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions - = button_tag 'Import project members', class: "btn btn-success" - = link_to "Cancel", project_project_members_path(@project), class: "btn btn-cancel" + = button_tag _('Import project members'), class: "btn btn-success" + = link_to _("Cancel"), project_project_members_path(@project), class: "btn btn-cancel" diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 14ed3345765..242ff91f539 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,39 +1,34 @@ -- page_title "Members" +- page_title _("Members") .row.prepend-top-default .col-lg-12 %h4 - Project members + = _("Project members") - if can?(current_user, :admin_project_member, @project) %p - You can invite a new member to - %strong= @project.name - or invite another group. + = _("You can invite a new member to <strong>%{project_name}</strong> or invite another group.").html_safe % { project_name: sanitize_project_name(@project.name) } - else %p - Members can be added by project - %i Maintainers - or - %i Owners + = _("Members can be added by project <i>Maintainers</i> or <i>Owners</i>").html_safe .light - if can?(current_user, :admin_project_member, @project) %ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' } %li.nav-tab{ role: 'presentation' } - %a.nav-link.active{ href: '#invite-member-pane', id: 'invite-member-tab', data: { toggle: 'tab' }, role: 'tab' } Invite member + %a.nav-link.active{ href: '#invite-member-pane', id: 'invite-member-tab', data: { toggle: 'tab' }, role: 'tab' }= _("Invite member") - if @project.allowed_to_share_with_group? %li.nav-tab{ role: 'presentation' } - %a.nav-link{ href: '#invite-group-pane', id: 'invite-group-tab', data: { toggle: 'tab' }, role: 'tab' } Invite group + %a.nav-link{ href: '#invite-group-pane', id: 'invite-group-tab', data: { toggle: 'tab' }, role: 'tab' }= _("Invite group") .tab-content.gitlab-tab-content .tab-pane.active{ id: 'invite-member-pane', role: 'tabpanel' } - = render 'projects/project_members/new_project_member', tab_title: 'Invite member' + = render 'projects/project_members/new_project_member', tab_title: _('Invite member') .tab-pane{ id: 'invite-group-pane', role: 'tabpanel' } - = render 'projects/project_members/new_project_group', tab_title: 'Invite group' + = render 'projects/project_members/new_project_group', tab_title: _('Invite group') = render 'shared/members/requests', membership_source: @project, requesters: @requesters .clearfix %h5.member.existing-title - Existing members and groups + = _("Existing members and groups") - if @group_links.any? = render 'projects/project_members/groups', group_links: @group_links diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index ff9a7b53a86..aaf9b973cda 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -6,75 +6,75 @@ - if project_search_tabs?(:blobs) %li{ class: active_when(@scope == 'blobs') } = link_to search_filter_path(scope: 'blobs') do - Code + = _("Code") %span.badge.badge-pill = @search_results.blobs_count - if project_search_tabs?(:issues) %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do - Issues + = _("Issues") %span.badge.badge-pill = limited_count(@search_results.limited_issues_count) - if project_search_tabs?(:merge_requests) %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do - Merge requests + = _("Merge requests") %span.badge.badge-pill = limited_count(@search_results.limited_merge_requests_count) - if project_search_tabs?(:milestones) %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do - Milestones + = _("Milestones") %span.badge.badge-pill = limited_count(@search_results.limited_milestones_count) - if project_search_tabs?(:notes) %li{ class: active_when(@scope == 'notes') } = link_to search_filter_path(scope: 'notes') do - Comments + = _("Comments") %span.badge.badge-pill = limited_count(@search_results.limited_notes_count) - if project_search_tabs?(:wiki) %li{ class: active_when(@scope == 'wiki_blobs') } = link_to search_filter_path(scope: 'wiki_blobs') do - Wiki + = _("Wiki") %span.badge.badge-pill = @search_results.wiki_blobs_count - if project_search_tabs?(:commits) %li{ class: active_when(@scope == 'commits') } = link_to search_filter_path(scope: 'commits') do - Commits + = _("Commits") %span.badge.badge-pill = @search_results.commits_count - elsif @show_snippets %li{ class: active_when(@scope == 'snippet_blobs') } = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do - Snippet Contents + = _("Snippet Contents") %span.badge.badge-pill = @search_results.snippet_blobs_count %li{ class: active_when(@scope == 'snippet_titles') } = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do - Titles and Filenames + = _("Titles and Filenames") %span.badge.badge-pill = @search_results.snippet_titles_count - else %li{ class: active_when(@scope == 'projects') } = link_to search_filter_path(scope: 'projects') do - Projects + = _("Projects") %span.badge.badge-pill = limited_count(@search_results.limited_projects_count) %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do - Issues + = _("Issues") %span.badge.badge-pill = limited_count(@search_results.limited_issues_count) %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do - Merge requests + = _("Merge requests") %span.badge.badge-pill = limited_count(@search_results.limited_merge_requests_count) %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do - Milestones + = _("Milestones") %span.badge.badge-pill = limited_count(@search_results.limited_milestones_count) diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index 6837f64f132..c8b6a3258ab 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -3,31 +3,31 @@ - if params[:project_id].present? = hidden_field_tag :project_id, params[:project_id] .dropdown - %button.dropdown-menu-toggle.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Group:", group_id: params[:group_id] } } + %button.dropdown-menu-toggle.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: _('Group:'), group_id: params[:group_id] } } %span.dropdown-toggle-text - Group: + = _("Group:") - if @group.present? = @group.name - else - Any + = _("Any") = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right - = dropdown_title("Filter results by group") - = dropdown_filter("Search groups") + = dropdown_title(_("Filter results by group")) + = dropdown_filter(_("Search groups")) = dropdown_content = dropdown_loading .dropdown.project-filter - %button.dropdown-menu-toggle.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Project:" } } + %button.dropdown-menu-toggle.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: _('Project:') } } %span.dropdown-toggle-text - Project: + = _("Project:") - if @project.present? = @project.full_name - else - Any + = _("Any") = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right - = dropdown_title("Filter results by project") - = dropdown_filter("Search projects") + = dropdown_title(_("Filter results by project")) + = dropdown_filter(_("Search projects")) = dropdown_content = dropdown_loading diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml index a4a5cec1314..4af0c6bf84a 100644 --- a/app/views/search/_form.html.haml +++ b/app/views/search/_form.html.haml @@ -4,12 +4,12 @@ .search-holder .search-field-holder - = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false + = search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false = icon("search", class: "search-icon") %button.search-clear.js-search-clear{ class: ("hidden" if !params[:search].present?), type: "button", tabindex: "-1" } = icon("times-circle") %span.sr-only - Clear search + = _("Clear search") - unless params[:snippets].eql? 'true' = render 'filter' - = button_tag "Search", class: "btn btn-success btn-search" + = button_tag _("Search"), class: "btn btn-success btn-search" diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index c4d52431d6e..be7a2436d16 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -6,9 +6,11 @@ = search_entries_info(@search_objects, @scope, @search_term) - unless @show_snippets - if @project - in project #{link_to @project.full_name, [@project.namespace.becomes(Namespace), @project]} + - link_to_project = link_to(@project.full_name, [@project.namespace.becomes(Namespace), @project]) + = _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project } - elsif @group - in group #{link_to @group.name, @group} + - link_to_group = link_to(@group.name, @group) + = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group } .results.prepend-top-10 - if @scope == 'commits' diff --git a/app/views/search/results/_empty.html.haml b/app/views/search/results/_empty.html.haml index 821a39d61f5..9d15995bb51 100644 --- a/app/views/search/results/_empty.html.haml +++ b/app/views/search/results/_empty.html.haml @@ -2,5 +2,5 @@ .search_glyph %h4 = icon('search') - We couldn't find any results matching + = _("We couldn't find any results matching") %code= @search_term diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index c413c3d4abb..796782035f2 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -4,7 +4,7 @@ = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do %span.term.str-truncated= issue.title - if issue.closed? - %span.badge.badge-danger.prepend-left-5 Closed + %span.badge.badge-danger.prepend-left-5= _("Closed") .float-right ##{issue.iid} - if issue.description.present? .description.term diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 519176af108..f0e0af11f27 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -3,9 +3,9 @@ = link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do %span.term.str-truncated= merge_request.title - if merge_request.merged? - %span.badge.badge-primary.prepend-left-5 Merged + %span.badge.badge-primary.prepend-left-5= _("Merged") - elsif merge_request.closed? - %span.badge.badge-danger.prepend-left-5 Closed + %span.badge.badge-danger.prepend-left-5= _("Closed") .float-right= merge_request.to_reference - if merge_request.description.present? .description.term diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index e4ab7b0541f..6717939d034 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -6,14 +6,14 @@ %h5.note-search-caption.str-truncated %i.fa.fa-comment = link_to_member(project, note.author, avatar: false) - commented on - = link_to project.full_name, project + - link_to_project = link_to(project.full_name, project) + = _("commented on %{link_to_project}").html_safe % { link_to_project: link_to_project } · - if note.for_commit? - = link_to_if(noteable_identifier, "Commit #{truncate_sha(note.commit_id)}", note_url) do + = link_to_if(noteable_identifier, _("Commit %{commit_id}") % { commit_id: truncate_sha(note.commit_id) }, note_url) do = truncate_sha(note.commit_id) - %span.light Commit deleted + %span.light= _("Commit deleted") - else %span #{note.noteable_type.titleize} ##{noteable_identifier} diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index b4ecd7bbae9..e0130f9a4b5 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -24,7 +24,7 @@ = markup(snippet.file_name, chunk[:data], legacy_render_context(params)) - else .file-content.code - .nothing-here-block Empty file + .nothing-here-block= _("Empty file") - else .file-content.code.js-syntax-highlight .line-numbers @@ -42,4 +42,4 @@ = highlight(snippet.file_name, chunk[:data]) - else .file-content.code - .nothing-here-block Empty file + .nothing-here-block= _("Empty file") diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml index 9c8afb2165b..1e01088d9e6 100644 --- a/app/views/search/results/_snippet_title.html.haml +++ b/app/views/search/results/_snippet_title.html.haml @@ -5,7 +5,7 @@ - if snippet_title.private? %span.badge.badge-gray %i.fa.fa-lock - private + = _("private") %span.cgray.monospace.tiny.float-right.term = snippet_title.file_name diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index 499697f2777..3260d05f509 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -1,5 +1,5 @@ - @hide_top_links = true -- breadcrumb_title "Search" +- breadcrumb_title _("Search") - page_title @search_term .prepend-top-default diff --git a/app/workers/remote_mirror_notification_worker.rb b/app/workers/remote_mirror_notification_worker.rb index 70c2e857d09..5bafe8e2046 100644 --- a/app/workers/remote_mirror_notification_worker.rb +++ b/app/workers/remote_mirror_notification_worker.rb @@ -9,7 +9,10 @@ class RemoteMirrorNotificationWorker # We check again if there's an error because a newer run since this job was # fired could've completed successfully. return unless remote_mirror && remote_mirror.last_error.present? + return if remote_mirror.error_notification_sent? NotificationService.new.remote_mirror_update_failed(remote_mirror) + + remote_mirror.after_sent_notification end end diff --git a/changelogs/unreleased/52278-squash-checkbox-fix.yml b/changelogs/unreleased/52278-squash-checkbox-fix.yml new file mode 100644 index 00000000000..c81748ae419 --- /dev/null +++ b/changelogs/unreleased/52278-squash-checkbox-fix.yml @@ -0,0 +1,5 @@ +--- +title: Resolve When merging an MR, the squash checkbox isnt always supported +merge_request: 24296 +author: +type: fixed diff --git a/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml b/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml new file mode 100644 index 00000000000..b609fc2d60b --- /dev/null +++ b/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml @@ -0,0 +1,5 @@ +--- +title: 'API: Fix default_branch_protection admin setting' +merge_request: 24398 +author: Robert Schilling +type: fixed diff --git a/changelogs/unreleased/55945-suggested-change-preview-highlight.yml b/changelogs/unreleased/55945-suggested-change-preview-highlight.yml new file mode 100644 index 00000000000..997290a5d50 --- /dev/null +++ b/changelogs/unreleased/55945-suggested-change-preview-highlight.yml @@ -0,0 +1,5 @@ +--- +title: Fix syntax highlighting for suggested changes preview +merge_request: 24358 +author: +type: fixed diff --git a/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml b/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml new file mode 100644 index 00000000000..cd7b56a1e05 --- /dev/null +++ b/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml @@ -0,0 +1,5 @@ +--- +title: Creates mixin to reduce code duplication between CE and EE in graph component +merge_request: +author: +type: other diff --git a/changelogs/unreleased/api-wiki-dot-slug.yml b/changelogs/unreleased/api-wiki-dot-slug.yml new file mode 100644 index 00000000000..82c76fa7450 --- /dev/null +++ b/changelogs/unreleased/api-wiki-dot-slug.yml @@ -0,0 +1,5 @@ +--- +title: 'API: Support dots in wiki slugs' +merge_request: 24383 +author: Robert Schilling +type: fixed diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml b/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml new file mode 100644 index 00000000000..1acea10fcaa --- /dev/null +++ b/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml @@ -0,0 +1,5 @@ +--- +title: Externalize strings from `/app/views/projects/project_members` +merge_request: 23227 +author: Tao Wang +type: other diff --git a/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml b/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml new file mode 100644 index 00000000000..b612bb3ee39 --- /dev/null +++ b/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml @@ -0,0 +1,5 @@ +--- +title: Remove all `$theme-gray-{weight}` variables in favor of `$gray-{weight}` +merge_request: 24333 +author: George Tsiolis +type: other diff --git a/changelogs/unreleased/homepage-proj-descr-cutoff.yml b/changelogs/unreleased/homepage-proj-descr-cutoff.yml new file mode 100644 index 00000000000..837c01f6722 --- /dev/null +++ b/changelogs/unreleased/homepage-proj-descr-cutoff.yml @@ -0,0 +1,5 @@ +--- +title: Increase line height of project summaries +merge_request: +author: gfyoung +type: fixed diff --git a/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml b/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml new file mode 100644 index 00000000000..06546bc5a8e --- /dev/null +++ b/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml @@ -0,0 +1,5 @@ +--- +title: Remove Cancel all jobs button in general jobs list view +merge_request: +author: Jordi Llull +type: removed diff --git a/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml b/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml new file mode 100644 index 00000000000..d1d4412eb50 --- /dev/null +++ b/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml @@ -0,0 +1,5 @@ +--- +title: Fix duplicate project disk path in BackfillLegacyProjectRepositories +merge_request: 24213 +author: +type: changed diff --git a/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml b/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml new file mode 100644 index 00000000000..1af1fe09f33 --- /dev/null +++ b/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml @@ -0,0 +1,5 @@ +--- +title: 'i18n: externalize strings from ''app/views/search''' +merge_request: 24297 +author: Tao Wang +type: other diff --git a/config/jsdocs.config.js b/config/jsdocs.config.js new file mode 100644 index 00000000000..52635b1ce13 --- /dev/null +++ b/config/jsdocs.config.js @@ -0,0 +1,14 @@ +module.exports = { + source: { + include: ['app/assets/javascripts/'], + }, + opts: { + template: 'node_modules/docdash', + destination: 'jsdoc/', + recurse: true, + }, + docdash: { + search: true, + static: true, + }, +}; diff --git a/config/routes/project.rb b/config/routes/project.rb index 797bf6de37b..d9afb4e7bc8 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -256,8 +256,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resources :jobs, only: [:index, :show], constraints: { id: /\d+/ } do collection do - post :cancel_all - resources :artifacts, only: [] do collection do get :latest_succeeded, diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index 8bdc7c6556c..2051bcff8f0 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -27,6 +27,9 @@ Gitlab::Seeder.quiet do Sidekiq::Worker.skipping_transaction_check do MergeRequests::CreateService.new(project, developer, params).execute + rescue Repository::AmbiguousRefError + # Ignore pipelines creation errors for now, we can doing that after + # https://gitlab.com/gitlab-org/gitlab-ce/issues/55966. will be resolved. end print '.' end diff --git a/db/migrate/20190115054216_add_error_notification_sent_to_remote_mirrors.rb b/db/migrate/20190115054216_add_error_notification_sent_to_remote_mirrors.rb new file mode 100644 index 00000000000..d8f979a1848 --- /dev/null +++ b/db/migrate/20190115054216_add_error_notification_sent_to_remote_mirrors.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddErrorNotificationSentToRemoteMirrors < ActiveRecord::Migration[5.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :remote_mirrors, :error_notification_sent, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 87826881d58..c4902116a3a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20190103140724) do +ActiveRecord::Schema.define(version: 20190115054216) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1849,6 +1849,7 @@ ActiveRecord::Schema.define(version: 20190103140724) do t.string "encrypted_credentials_salt" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "error_notification_sent" t.index ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree t.index ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree end diff --git a/doc/api/avatar.md b/doc/api/avatar.md index aa6f7c185ae..e55fffba4b2 100644 --- a/doc/api/avatar.md +++ b/doc/api/avatar.md @@ -1,33 +1,41 @@ # Avatar API -> [Introduced][ce-19121] in GitLab 11.0 +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19121) in GitLab 11.0. ## Get a single avatar URL -Get a single avatar URL for a given email address. If user with matching public -email address is not found, results from external avatar services are returned. -This endpoint can be accessed without authentication. In case public visibility -is restricted, response will be `403 Forbidden` when unauthenticated. +Get a single [avatar](../user/profile/index.md#profile-settings) URL for a user with the given email address. -``` +If: + +- No user with the given public email address is found, results from external avatar services are + returned. +- Public visibility is restricted, response will be `403 Forbidden` when unauthenticated. + +NOTE: **Note:** +This endpoint can be accessed without authentication. + +```text GET /avatar?email=admin@example.com ``` -| Attribute | Type | Required | Description | -| --------- | ------- | -------- | --------------------- | -| `email` | string | yes | Public email address of the user | -| `size` | integer | no | Single pixel dimension (since images are squares). Only used for avatar lookups at `Gravatar` or at the configured `Libravatar` server | +Parameters: -```bash -curl https://gitlab.example.com/api/v4/avatar?email=admin@example.com +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------| +| `email` | string | yes | Public email address of the user. | +| `size` | integer | no | Single pixel dimension (since images are squares). Only used for avatar lookups at `Gravatar` or at the configured `Libravatar` server. | + +Example request: + +```sh +curl https://gitlab.example.com/api/v4/avatar?email=admin@example.com&size=32 ``` Example response: ```json { - "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon" + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=64&d=identicon" } ``` - -[ce-19121]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19121 diff --git a/doc/api/branches.md b/doc/api/branches.md index 3b55154887d..8d5f333ba77 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -1,21 +1,31 @@ # Branches API +This API operates on [repository branches](../user/project/repository/branches/index.md). + +TIP: **Tip:** +See also [Protected branches API](protected_branches.md). + ## List repository branches Get a list of repository branches from a project, sorted by name alphabetically. -This endpoint can be accessed without authentication if the repository is -publicly accessible. -``` +NOTE: **Note:** +This endpoint can be accessed without authentication if the repository is publicly accessible. + +```text GET /projects/:id/repository/branches ``` -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `search` | string | no | Return list of branches matching the search criteria. | +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:---------------|:---------|:-------------------------------------------------------------------------------------------------------------| +| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | +| `search` | string | no | Return list of branches matching the search criteria. | + +Example request: -```bash +```sh curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches ``` @@ -53,19 +63,25 @@ Example response: ## Get single repository branch -Get a single project repository branch. This endpoint can be accessed without -authentication if the repository is publicly accessible. +Get a single project repository branch. -``` +NOTE: **Note:** +This endpoint can be accessed without authentication if the repository is publicly accessible. + +```text GET /projects/:id/repository/branches/:branch ``` -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `branch` | string | yes | The name of the branch | +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:---------------|:---------|:-------------------------------------------------------------------------------------------------------------| +| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | +| `branch` | string | yes | Name of the branch. | -```bash +Example request: + +```sh curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches/master ``` @@ -100,120 +116,34 @@ Example response: ## Protect repository branch ->**Note:** This API endpoint is deprecated in favor of `POST /projects/:id/protected_branches`. - -Protects a single project repository branch. This is an idempotent function, -protecting an already protected repository branch still returns a `200 OK` -status code. - -``` -PUT /projects/:id/repository/branches/:branch/protect -``` - -```bash -curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches/master/protect?developers_can_push=true&developers_can_merge=true -``` - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `branch` | string | yes | The name of the branch | -| `developers_can_push` | boolean | no | Flag if developers can push to the branch | -| `developers_can_merge` | boolean | no | Flag if developers can merge to the branch | - -Example response: - -```json -{ - "commit": { - "author_email": "john@example.com", - "author_name": "John Smith", - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00", - "committer_email": "john@example.com", - "committer_name": "John Smith", - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "short_id": "7b5c3cc", - "title": "add projects API", - "message": "add projects API", - "parent_ids": [ - "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - ] - }, - "name": "master", - "merged": false, - "protected": true, - "default": true, - "developers_can_push": true, - "developers_can_merge": true, - "can_push": true -} -``` +See [`POST /projects/:id/protected_branches`](protected_branches.md#protect-repository-branches) for +information on protecting repository branches. ## Unprotect repository branch ->**Note:** This API endpoint is deprecated in favor of `DELETE /projects/:id/protected_branches/:name` +See [`DELETE /projects/:id/protected_branches/:name`](protected_branches.md#unprotect-repository-branches) +for information on unprotecting repository branches. -Unprotects a single project repository branch. This is an idempotent function, -unprotecting an already unprotected repository branch still returns a `200 OK` -status code. - -``` -PUT /projects/:id/repository/branches/:branch/unprotect -``` - -```bash -curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches/master/unprotect -``` - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `branch` | string | yes | The name of the branch | +## Create repository branch -Example response: +Create a new branch in the repository. -```json -{ - "commit": { - "author_email": "john@example.com", - "author_name": "John Smith", - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00", - "committer_email": "john@example.com", - "committer_name": "John Smith", - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "short_id": "7b5c3cc", - "title": "add projects API", - "message": "add projects API", - "parent_ids": [ - "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - ] - }, - "name": "master", - "merged": false, - "protected": false, - "default": true, - "developers_can_push": false, - "developers_can_merge": false, - "can_push": true -} +```text +POST /projects/:id/repository/branches ``` -## Create repository branch +Parameters: -``` -POST /projects/:id/repository/branches -``` +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:-------------------------------------------------------------------------------------------------------------| +| `id` | integer | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | +| `branch` | string | yes | Name of the branch. | +| `ref` | string | yes | Branch name or commit SHA to create branch from. | -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `branch` | string | yes | The name of the branch | -| `ref` | string | yes | The branch name or commit SHA to create branch from | +Example request: -```bash -curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/branches?branch=newbranch&ref=master" +```sh +curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches?branch=newbranch&ref=master ``` Example response: @@ -247,36 +177,47 @@ Example response: ## Delete repository branch -``` +Delete a branch from the repository. + +NOTE: **Note:** +In the case of an error, an explanation message is provided. + +```text DELETE /projects/:id/repository/branches/:branch ``` -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `branch` | string | yes | The name of the branch | +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:---------------|:---------|:-------------------------------------------------------------------------------------------------------------| +| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | +| `branch` | string | yes | Name of the branch. | -In case of an error, an explaining message is provided. +Example request: -```bash -curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/branches/newbranch" +```sh +curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/branches/newbranch ``` ## Delete merged branches Will delete all branches that are merged into the project's default branch. -Protected branches will not be deleted as part of this operation. +NOTE: **Note:** +[Protected branches](../user/project/protected_branches.md) will not be deleted as part of this operation. -``` +```text DELETE /projects/:id/repository/merged_branches ``` -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:---------------|:---------|:-------------------------------------------------------------------------------------------------------------| +| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | +Example request: -```bash -curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/merged_branches" +```sh +curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/repository/merged_branches ``` diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 15363b4750c..092bbdac037 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -284,8 +284,14 @@ Inside the document: ```md Markdown code ``` + + ```text + Code for which no specific highlighting class is available. + ``` ```` +- To display raw markdown instead of rendered markdown, use four backticks on their own lines around the + markdown to display. See [example](https://gitlab.com/gitlab-org/gitlab-ce/blob/8c1991b9bb7e3b8d606481fdea316d633cfa5eb7/doc/development/documentation/styleguide.md#L275-287). - For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks). ## Alert boxes diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md index bbb2313ea7b..f6bc9fb0979 100644 --- a/doc/development/testing_guide/flaky_tests.md +++ b/doc/development/testing_guide/flaky_tests.md @@ -5,6 +5,18 @@ It's a test that sometimes fails, but if you retry it enough times, it passes, eventually. +## 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/rspec --tag quarantine +``` + ## Automatic retries and flaky tests detection On our CI, we use [rspec-retry] to automatically retry a failing example a few diff --git a/doc/user/project/index.md b/doc/user/project/index.md index e9a930d2ebe..ce8bd2de61f 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -66,7 +66,7 @@ common actions on issues or merge requests - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job), timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more - [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project - with Google Kubernetes Engine + with a Kubernetes cluster - [GitLab Pages](pages/index.md): Build, test, and deploy your static website with GitLab Pages diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index 783081cec26..f05554ffc5b 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -2,16 +2,17 @@ Read through GiLab's branching documentation: -- [Create a branch](../web_editor.md#create-a-new-branch) -- [Default branch](#default-branch) -- [Protected branches](../../protected_branches.md#protected-branches) -- [Delete merged branches](#delete-merged-branches) -- [Branch filter search box](#branch-filter-search-box) +- [Create a branch](../web_editor.md#create-a-new-branch). +- [Default branch](#default-branch). +- [Protected branches](../../protected_branches.md#protected-branches). +- [Delete merged branches](#delete-merged-branches). +- [Branch filter search box](#branch-filter-search-box). See also: -- [GitLab Flow](../../../../university/training/gitlab_flow.md#gitlab-flow): use the best of GitLab for your branching strategies -- [Getting started with Git](../../../../topics/git/index.md) and GitLab +- [Branches API](../../../../api/branches.md), for information on operating on repository branches using the GitLab API. +- [GitLab Flow](../../../../university/training/gitlab_flow.md#gitlab-flow). Use the best of GitLab for your branching strategies. +- [Getting started with Git](../../../../topics/git/index.md) and GitLab. ## Default branch @@ -41,7 +42,6 @@ this operation. It's particularly useful to clean up old branches that were not deleted automatically when a merge request was merged. - ## Branch filter search box > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166) in GitLab 11.5. diff --git a/lib/api/settings.rb b/lib/api/settings.rb index f53ba0ab761..95371961398 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -35,7 +35,7 @@ module API end optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts" - optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master' + optional :default_branch_protection, type: Integer, values: Gitlab::Access.protection_values, desc: 'Determine if developers can push to master' optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility' optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility' optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects' diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb index 302b2797a34..ef0e3decc2c 100644 --- a/lib/api/wikis.rb +++ b/lib/api/wikis.rb @@ -22,7 +22,9 @@ module API end end - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + WIKI_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(slug: API::NO_SLASH_URL_PART_REGEX) + + resource :projects, requirements: WIKI_ENDPOINT_REQUIREMENTS do desc 'Get a list of wiki pages' do success Entities::WikiPageBasic end @@ -103,7 +105,7 @@ module API requires :file, type: ::API::Validations::Types::SafeFile, desc: 'The attachment file to be uploaded' optional :branch, type: String, desc: 'The name of the branch' end - post ":id/wikis/attachments", requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + post ":id/wikis/attachments" do authorize! :create_wiki, user_project result = ::Wikis::CreateAttachmentService.new(user_project, diff --git a/lib/gitlab/background_migration/backfill_project_repositories.rb b/lib/gitlab/background_migration/backfill_project_repositories.rb index aaf520d70f6..c8d83cc1803 100644 --- a/lib/gitlab/background_migration/backfill_project_repositories.rb +++ b/lib/gitlab/background_migration/backfill_project_repositories.rb @@ -83,7 +83,7 @@ module Gitlab extend ActiveSupport::Concern def full_path - @full_path ||= build_full_path + route&.path || build_full_path end def build_full_path @@ -99,7 +99,12 @@ module Gitlab end end - # Namespace model. + # Route model + class Route < ActiveRecord::Base + belongs_to :source, inverse_of: :route, polymorphic: true + end + + # Namespace model class Namespace < ActiveRecord::Base self.table_name = 'namespaces' self.inheritance_column = nil @@ -108,6 +113,8 @@ module Gitlab belongs_to :parent, class_name: 'Namespace', inverse_of: 'namespaces' + has_one :route, -> { where(source_type: 'Namespace') }, inverse_of: :source, foreign_key: :source_id + has_many :projects, inverse_of: :parent has_many :namespaces, inverse_of: :parent end @@ -134,6 +141,7 @@ module Gitlab belongs_to :parent, class_name: 'Namespace', foreign_key: :namespace_id, inverse_of: 'projects' + has_one :route, -> { where(source_type: 'Project') }, inverse_of: :source, foreign_key: :source_id has_one :project_repository, inverse_of: :project delegate :disk_path, to: :storage @@ -194,6 +202,8 @@ module Gitlab def project_repositories(start_id, stop_id) projects .without_project_repository + .includes(:route, parent: [:route]).references(:routes) + .includes(:parent).references(:namespaces) .where(id: start_id..stop_id) .map { |project| build_attributes_for_project(project) } .compact diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 3239743bfff..1d8904f7b29 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -16,13 +16,6 @@ module Gitlab dependencies before_script after_script variables environment coverage retry parallel extends].freeze - DEFAULT_ONLY_POLICY = { - refs: %w(branches tags) - }.freeze - - DEFAULT_EXCEPT_POLICY = { - }.freeze - validations do validates :config, allowed_keys: ALLOWED_KEYS validates :config, presence: true @@ -73,7 +66,8 @@ module Gitlab description: 'Services that will be used to execute this job.' entry :only, Entry::Policy, - description: 'Refs policy this job will be executed for.' + description: 'Refs policy this job will be executed for.', + default: { refs: %w[branches tags] } entry :except, Entry::Policy, description: 'Refs policy this job will be executed for.' @@ -156,8 +150,8 @@ module Gitlab services: services_value, stage: stage_value, cache: cache_value, - only: DEFAULT_ONLY_POLICY.deep_merge(only_value.to_h), - except: DEFAULT_EXCEPT_POLICY.deep_merge(except_value.to_h), + only: only_value, + except: except_value, variables: variables_defined? ? variables_value : nil, environment: environment_defined? ? environment_value : nil, environment_name: environment_defined? ? environment_value[:name] : nil, diff --git a/lib/gitlab/ci/config/entry/policy.rb b/lib/gitlab/ci/config/entry/policy.rb index 998da1f6837..9c677bf6617 100644 --- a/lib/gitlab/ci/config/entry/policy.rb +++ b/lib/gitlab/ci/config/entry/policy.rb @@ -64,7 +64,8 @@ module Gitlab end end - def self.default + def value + default.to_h.deep_merge(subject.value.to_h) end end end diff --git a/lib/gitlab/ci/config/entry/retry.rb b/lib/gitlab/ci/config/entry/retry.rb index eaf8b38aa3c..e9cbcb31e21 100644 --- a/lib/gitlab/ci/config/entry/retry.rb +++ b/lib/gitlab/ci/config/entry/retry.rb @@ -82,9 +82,6 @@ module Gitlab 'retry config' end end - - def self.default - end end end end diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb index 89d790ebfa6..c9d0c7cb568 100644 --- a/lib/gitlab/ci/config/entry/variables.rb +++ b/lib/gitlab/ci/config/entry/variables.rb @@ -14,7 +14,7 @@ module Gitlab validates :config, variables: true end - def self.default + def self.default(**) {} end diff --git a/lib/gitlab/config/entry/configurable.rb b/lib/gitlab/config/entry/configurable.rb index afdb60b2cd5..37ba16dba25 100644 --- a/lib/gitlab/config/entry/configurable.rb +++ b/lib/gitlab/config/entry/configurable.rb @@ -56,6 +56,7 @@ module Gitlab def entry(key, entry, metadata) factory = ::Gitlab::Config::Entry::Factory.new(entry) .with(description: metadata[:description]) + .with(default: metadata[:default]) (@nodes ||= {}).merge!(key.to_sym => factory) end diff --git a/lib/gitlab/config/entry/factory.rb b/lib/gitlab/config/entry/factory.rb index 30d43c9f9a1..79f9ff32514 100644 --- a/lib/gitlab/config/entry/factory.rb +++ b/lib/gitlab/config/entry/factory.rb @@ -12,7 +12,7 @@ module Gitlab def initialize(entry) @entry = entry @metadata = {} - @attributes = {} + @attributes = { default: entry.default } end def value(value) @@ -21,12 +21,12 @@ module Gitlab end def metadata(metadata) - @metadata.merge!(metadata) + @metadata.merge!(metadata.compact) self end def with(attributes) - @attributes.merge!(attributes) + @attributes.merge!(attributes.compact) self end @@ -38,9 +38,7 @@ module Gitlab # See issue #18775. # if @value.nil? - Entry::Unspecified.new( - fabricate_unspecified - ) + Entry::Unspecified.new(fabricate_unspecified) else fabricate(@entry, @value) end @@ -53,10 +51,12 @@ module Gitlab # If entry has a default value we fabricate concrete node # with default value. # - if @entry.default.nil? + default = @attributes.fetch(:default) + + if default.nil? fabricate(Entry::Undefined) else - fabricate(@entry, @entry.default) + fabricate(@entry, default) end end @@ -64,6 +64,7 @@ module Gitlab entry.new(value, @metadata).tap do |node| node.key = @attributes[:key] node.parent = @attributes[:parent] + node.default = @attributes[:default] node.description = @attributes[:description] end end diff --git a/lib/gitlab/config/entry/node.rb b/lib/gitlab/config/entry/node.rb index 30357b2c95b..9999ab4ff95 100644 --- a/lib/gitlab/config/entry/node.rb +++ b/lib/gitlab/config/entry/node.rb @@ -10,7 +10,7 @@ module Gitlab InvalidError = Class.new(StandardError) attr_reader :config, :metadata - attr_accessor :key, :parent, :description + attr_accessor :key, :parent, :default, :description def initialize(config, **metadata) @config = config @@ -85,7 +85,7 @@ module Gitlab "#<#{self.class.name} #{unspecified}{#{key}: #{val.inspect}}>" end - def self.default + def self.default(**) end def self.aspects diff --git a/lib/gitlab/config/entry/simplifiable.rb b/lib/gitlab/config/entry/simplifiable.rb index 3e148fe2e91..5fbf7565e2a 100644 --- a/lib/gitlab/config/entry/simplifiable.rb +++ b/lib/gitlab/config/entry/simplifiable.rb @@ -6,6 +6,8 @@ module Gitlab class Simplifiable < SimpleDelegator EntryStrategy = Struct.new(:name, :condition) + attr_reader :subject + def initialize(config, **metadata) unless self.class.const_defined?(:UnknownStrategy) raise ArgumentError, 'UndefinedStrategy not available!' @@ -17,7 +19,7 @@ module Gitlab entry = self.class.entry_class(strategy) - super(entry.new(config, metadata)) + super(@subject = entry.new(config, metadata)) end def self.strategy(name, **opts) @@ -37,6 +39,9 @@ module Gitlab self::UnknownStrategy end end + + def self.default + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d94daea9164..404cb079371 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -129,6 +129,9 @@ msgstr "" msgid "%{issuableType} will be removed! Are you sure?" msgstr "" +msgid "%{link_start}Read more%{link_end} about role permissions" +msgstr "" + msgid "%{loadingIcon} Started" msgstr "" @@ -399,6 +402,9 @@ msgstr "" msgid "Add reaction" msgstr "" +msgid "Add to project" +msgstr "" + msgid "Add todo" msgstr "" @@ -1305,6 +1311,9 @@ msgstr "" msgid "Choose a file" msgstr "" +msgid "Choose a role permission" +msgstr "" + msgid "Choose a template..." msgstr "" @@ -1890,6 +1899,9 @@ msgstr "" msgid "ClusterIntegration|sign up" msgstr "" +msgid "Code" +msgstr "" + msgid "Cohorts" msgstr "" @@ -1928,9 +1940,15 @@ msgid_plural "Commits" msgstr[0] "" msgstr[1] "" +msgid "Commit %{commit_id}" +msgstr "" + msgid "Commit Message" msgstr "" +msgid "Commit deleted" +msgstr "" + msgid "Commit duration in minutes for last 30 commits" msgstr "" @@ -2770,6 +2788,9 @@ msgstr "" msgid "Enable the Performance Bar for a given group." msgstr "" +msgid "Enable two-factor authentication" +msgstr "" + msgid "Enable usage ping" msgstr "" @@ -2947,6 +2968,9 @@ msgstr "" msgid "Error occurred when toggling the notification subscription" msgstr "" +msgid "Error rendering markdown preview" +msgstr "" + msgid "Error saving label update." msgstr "" @@ -3010,6 +3034,9 @@ msgstr "" msgid "Existing folder" msgstr "" +msgid "Existing members and groups" +msgstr "" + msgid "Expand" msgstr "" @@ -3136,6 +3163,12 @@ msgstr "" msgid "Filter by two-factor authentication" msgstr "" +msgid "Filter results by group" +msgstr "" + +msgid "Filter results by project" +msgstr "" + msgid "Filter..." msgstr "" @@ -3145,6 +3178,9 @@ msgstr "" msgid "Find by path" msgstr "" +msgid "Find existing members by name" +msgstr "" + msgid "Find file" msgstr "" @@ -3397,6 +3433,9 @@ msgstr "" msgid "Group name" msgstr "" +msgid "Group:" +msgstr "" + msgid "Group: %{group_name}" msgstr "" @@ -3436,6 +3475,9 @@ msgstr "" msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}." msgstr "" +msgid "Groups with access to <strong>%{project_name}</strong>" +msgstr "" + msgid "GroupsDropdown|Frequently visited" msgstr "" @@ -3663,12 +3705,21 @@ msgstr "" msgid "Import issues" msgstr "" +msgid "Import members" +msgstr "" + +msgid "Import members from another project" +msgstr "" + msgid "Import multiple repositories by uploading a manifest file." msgstr "" msgid "Import project" msgstr "" +msgid "Import project members" +msgstr "" + msgid "Import projects from Bitbucket" msgstr "" @@ -3774,6 +3825,12 @@ msgstr "" msgid "Invite" msgstr "" +msgid "Invite group" +msgstr "" + +msgid "Invite member" +msgstr "" + msgid "Invoke Count" msgstr "" @@ -4114,6 +4171,9 @@ msgstr "" msgid "Manage project labels" msgstr "" +msgid "Manage two-factor authentication" +msgstr "" + msgid "Manifest" msgstr "" @@ -4195,6 +4255,12 @@ msgstr "" msgid "Members" msgstr "" +msgid "Members can be added by project <i>Maintainers</i> or <i>Owners</i>" +msgstr "" + +msgid "Members of <strong>%{project_name}</strong>" +msgstr "" + msgid "Merge Request" msgstr "" @@ -4742,6 +4808,9 @@ msgstr "" msgid "Only project members can comment." msgstr "" +msgid "Only project members will be imported. Group members will be skipped." +msgstr "" + msgid "Oops, are you sure?" msgstr "" @@ -5144,6 +5213,12 @@ msgstr "" msgid "Profiles|Account scheduled for removal." msgstr "" +msgid "Profiles|Activate signin with one of the following services" +msgstr "" + +msgid "Profiles|Active" +msgstr "" + msgid "Profiles|Add key" msgstr "" @@ -5159,6 +5234,9 @@ msgstr "" msgid "Profiles|Change username" msgstr "" +msgid "Profiles|Changing your username can have unintended side effects." +msgstr "" + msgid "Profiles|Choose file..." msgstr "" @@ -5171,6 +5249,15 @@ msgstr "" msgid "Profiles|Clear status" msgstr "" +msgid "Profiles|Click on icon to activate signin with one of the following services" +msgstr "" + +msgid "Profiles|Connect" +msgstr "" + +msgid "Profiles|Connected Accounts" +msgstr "" + msgid "Profiles|Current path: %{path}" msgstr "" @@ -5189,6 +5276,9 @@ msgstr "" msgid "Profiles|Deleting an account has the following effects:" msgstr "" +msgid "Profiles|Disconnect" +msgstr "" + msgid "Profiles|Do not show on profile" msgstr "" @@ -5201,6 +5291,9 @@ msgstr "" msgid "Profiles|Enter your name, so people you know can recognize you" msgstr "" +msgid "Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)" +msgstr "" + msgid "Profiles|Invalid password" msgstr "" @@ -5237,6 +5330,9 @@ msgstr "" msgid "Profiles|Set new profile picture" msgstr "" +msgid "Profiles|Social sign-in" +msgstr "" + msgid "Profiles|Some options are unavailable for LDAP accounts" msgstr "" @@ -5264,6 +5360,9 @@ msgstr "" msgid "Profiles|This information will appear on your profile" msgstr "" +msgid "Profiles|Two-Factor Authentication" +msgstr "" + msgid "Profiles|Type your %{confirmationValue} to confirm:" msgstr "" @@ -5396,12 +5495,18 @@ msgstr "" msgid "Project export started. A download link will be sent by email." msgstr "" +msgid "Project members" +msgstr "" + msgid "Project name" msgstr "" msgid "Project slug" msgstr "" +msgid "Project:" +msgstr "" + msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -5964,6 +6069,9 @@ msgstr "" msgid "Search for projects, issues, etc." msgstr "" +msgid "Search groups" +msgstr "" + msgid "Search merge requests" msgstr "" @@ -5979,6 +6087,9 @@ msgstr "" msgid "Search project" msgstr "" +msgid "Search projects" +msgstr "" + msgid "Search users" msgstr "" @@ -6039,6 +6150,9 @@ msgstr "" msgid "Select branch/tag" msgstr "" +msgid "Select members to invite" +msgstr "" + msgid "Select project" msgstr "" @@ -6272,6 +6386,9 @@ msgstr "" msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job" msgstr "" +msgid "Snippet Contents" +msgstr "" + msgid "Snippets" msgstr "" @@ -7218,6 +7335,9 @@ msgstr "" msgid "Title" msgstr "" +msgid "Titles and Filenames" +msgstr "" + msgid "To GitLab" msgstr "" @@ -7641,6 +7761,9 @@ msgstr "" msgid "Want to see the data? Please ask an administrator for access." msgstr "" +msgid "We couldn't find any results matching" +msgstr "" + msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed." msgstr "" @@ -7866,6 +7989,9 @@ msgstr "" msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}" msgstr "" +msgid "You can invite a new member to <strong>%{project_name}</strong> or invite another group." +msgstr "" + msgid "You can move around the graph by using the arrow keys." msgstr "" @@ -8043,6 +8169,9 @@ msgstr "" msgid "command line instructions" msgstr "" +msgid "commented on %{link_to_project}" +msgstr "" + msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue." msgstr "" @@ -8116,6 +8245,12 @@ msgstr "" msgid "importing" msgstr "" +msgid "in group %{link_to_group}" +msgstr "" + +msgid "in project %{link_to_project}" +msgstr "" + msgid "issue boards" msgstr "" @@ -8368,6 +8503,9 @@ msgstr "" msgid "personal access token" msgstr "" +msgid "private" +msgstr "" + msgid "project" msgstr "" diff --git a/package.json b/package.json index b305ff76533..75df0ec3ff6 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "eslint-fix": "eslint --max-warnings 0 --report-unused-disable-directives --ext .js,.vue --fix .", "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .", "jest": "BABEL_ENV=jest jest", + "jsdoc": "jsdoc -c config/jsdocs.config.js", "karma": "BABEL_ENV=${BABEL_ENV:=karma} karma start --single-run true config/karma.config.js", "karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js", "karma-start": "BABEL_ENV=karma karma start config/karma.config.js", @@ -132,6 +133,7 @@ "babel-types": "^6.26.0", "chalk": "^2.4.1", "commander": "^2.18.0", + "docdash": "^1.0.2", "eslint": "~5.9.0", "eslint-import-resolver-jest": "^2.1.1", "eslint-import-resolver-webpack": "^0.10.1", @@ -148,6 +150,8 @@ "jasmine-jquery": "^2.1.1", "jest": "^23.6.0", "jest-junit": "^5.2.0", + "jsdoc": "^3.5.5", + "jsdoc-vue": "^1.0.0", "karma": "^3.0.0", "karma-chrome-launcher": "^2.2.0", "karma-coverage-istanbul-reporter": "^2.0.4", @@ -157,7 +161,7 @@ "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^4.0.0-beta.0", "nodemon": "^1.18.4", - "prettier": "1.15.2", + "prettier": "1.15.3", "vue-jest": "^3.0.2", "webpack-dev-server": "^3.1.14", "yarn-deduplicate": "^1.0.5" diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 7f65fe551e9..d8a331b3cf0 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -770,50 +770,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do end end - describe 'POST cancel_all' do - before do - project.add_developer(user) - sign_in(user) - end - - context 'when jobs are cancelable' do - before do - create_list(:ci_build, 2, :cancelable, pipeline: pipeline) - - post_cancel_all - end - - it 'redirects to a index page' do - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(namespace_project_jobs_path) - end - - it 'transits to canceled' do - expect(Ci::Build.all).to all(be_canceled) - end - end - - context 'when jobs are not cancelable' do - before do - create_list(:ci_build, 2, :canceled, pipeline: pipeline) - - post_cancel_all - end - - it 'redirects to a index page' do - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(namespace_project_jobs_path) - end - end - - def post_cancel_all - post :cancel_all, params: { - namespace_id: project.namespace, - project_id: project - } - end - end - describe 'POST erase' do let(:role) { :maintainer } diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb index 2335b5118dd..ae257d769e8 100644 --- a/spec/factories/wiki_pages.rb +++ b/spec/factories/wiki_pages.rb @@ -5,7 +5,7 @@ FactoryBot.define do transient do attrs do { - title: 'Title', + title: 'Title.with.dot', content: 'Content for wiki page', format: 'markdown' } diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index baa2b1d8af5..08c27354bd2 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -97,7 +97,7 @@ describe 'Issue Boards', :js do expect(find('.board:nth-child(4)')).to have_selector('.board-card') end - it 'shows description tooltip on list title' do + it 'shows description tooltip on list title', :quarantine do page.within('.board:nth-child(2)') do expect(find('.board-title span.has-tooltip')[:title]).to eq('Test') end @@ -411,7 +411,7 @@ describe 'Issue Boards', :js do wait_for_empty_boards((2..4)) end - it 'filters by label with space after reload' do + it 'filters by label with space after reload', :quarantine do set_filter("label", "\"#{accepting.title}") click_filter_link(accepting.title) submit_filter @@ -477,7 +477,7 @@ describe 'Issue Boards', :js do end end - it 'filters by multiple labels' do + it 'filters by multiple labels', :quarantine do set_filter("label", testing.title) click_filter_link(testing.title) diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index aff3ebaf632..8230396a4cc 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -28,7 +28,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end it "shows Pending tab jobs" do - expect(page).to have_link 'Cancel running' expect(page).to have_selector('.nav-links li.active', text: 'Pending') expect(page).to have_content job.short_sha expect(page).to have_content job.ref @@ -44,7 +43,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do it "shows Running tab jobs" do expect(page).to have_selector('.nav-links li.active', text: 'Running') - expect(page).to have_link 'Cancel running' expect(page).to have_content job.short_sha expect(page).to have_content job.ref expect(page).to have_content job.name @@ -60,7 +58,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do it "shows Finished tab jobs" do expect(page).to have_selector('.nav-links li.active', text: 'Finished') expect(page).to have_content 'No jobs to show' - expect(page).to have_link 'Cancel running' end end @@ -75,7 +72,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do expect(page).to have_content job.short_sha expect(page).to have_content job.ref expect(page).to have_content job.name - expect(page).not_to have_link 'Cancel running' end end @@ -94,23 +90,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end end - describe "POST /:project/jobs/:id/cancel_all" do - before do - job.run! - visit project_jobs_path(project) - click_link "Cancel running" - end - - it 'shows all necessary content' do - expect(page).to have_selector('.nav-links li.active', text: 'All') - expect(page).to have_content 'canceled' - expect(page).to have_content job.short_sha - expect(page).to have_content job.ref - expect(page).to have_content job.name - expect(page).not_to have_link 'Cancel running' - end - end - describe "GET /:project/jobs/:id" do context "Job from project" do let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline) } diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index f320f232687..0dc7e93539a 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -347,20 +347,31 @@ describe('common_utils', () => { }); describe('parseBoolean', () => { + const { parseBoolean } = commonUtils; + it('returns true for "true"', () => { - expect(commonUtils.parseBoolean('true')).toEqual(true); + expect(parseBoolean('true')).toEqual(true); }); it('returns false for "false"', () => { - expect(commonUtils.parseBoolean('false')).toEqual(false); + expect(parseBoolean('false')).toEqual(false); }); it('returns false for "something"', () => { - expect(commonUtils.parseBoolean('something')).toEqual(false); + expect(parseBoolean('something')).toEqual(false); }); it('returns false for null', () => { - expect(commonUtils.parseBoolean(null)).toEqual(false); + expect(parseBoolean(null)).toEqual(false); + }); + + it('is idempotent', () => { + const input = ['true', 'false', 'something', null]; + input.forEach(value => { + const result = parseBoolean(value); + + expect(parseBoolean(result)).toBe(result); + }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 2119a3b927a..e387367d1a2 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -1,11 +1,12 @@ import Vue from 'vue'; import ReadyToMerge from '~/vue_merge_request_widget/components/states/ready_to_merge.vue'; +import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue'; import eventHub from '~/vue_merge_request_widget/event_hub'; +import { createLocalVue, shallowMount } from '@vue/test-utils'; const commitMessage = 'This is the commit message'; const commitMessageWithDescription = 'This is the commit message description'; -const createComponent = (customConfig = {}) => { - const Component = Vue.extend(ReadyToMerge); +const createTestMr = customConfig => { const mr = { isPipelineActive: false, pipeline: null, @@ -16,6 +17,7 @@ const createComponent = (customConfig = {}) => { hasCI: false, ciStatus: null, sha: '12345678', + squash: false, commitMessage, commitMessageWithDescription, shouldRemoveSourceBranch: true, @@ -24,14 +26,23 @@ const createComponent = (customConfig = {}) => { Object.assign(mr, customConfig.mr); - const service = { - merge() {}, - poll() {}, - }; + return mr; +}; + +const createTestService = () => ({ + merge() {}, + poll() {}, +}); + +const createComponent = (customConfig = {}) => { + const Component = Vue.extend(ReadyToMerge); return new Component({ el: document.createElement('div'), - propsData: { mr, service }, + propsData: { + mr: createTestMr(customConfig), + service: createTestService(), + }, }); }; @@ -612,6 +623,47 @@ describe('ReadyToMerge', () => { }); }); + describe('Squash checkbox component', () => { + let wrapper; + const localVue = createLocalVue(); + + const createLocalComponent = (customConfig = {}) => { + wrapper = shallowMount(localVue.extend(ReadyToMerge), { + localVue, + propsData: { + mr: createTestMr(customConfig), + service: createTestService(), + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + const findCheckboxElement = () => wrapper.find(SquashBeforeMerge); + + it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => { + createLocalComponent({ + mr: { commitsCount: 2, enableSquashBeforeMerge: true }, + }); + + expect(findCheckboxElement().exists()).toBeTruthy(); + }); + + it('should not be rendered when squash before merge is disabled', () => { + createLocalComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: false } }); + + expect(findCheckboxElement().exists()).toBeFalsy(); + }); + + it('should not be rendered when there is only 1 commit', () => { + createLocalComponent({ mr: { commitsCount: 1, enableSquashBeforeMerge: true } }); + + expect(findCheckboxElement().exists()).toBeFalsy(); + }); + }); + describe('Merge controls', () => { describe('when allowed to merge', () => { beforeEach(() => { diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js new file mode 100644 index 00000000000..d6d8eecfcb9 --- /dev/null +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js @@ -0,0 +1,100 @@ +import { createLocalVue, shallowMount } from '@vue/test-utils'; +import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue'; + +const localVue = createLocalVue(); + +describe('Squash before merge component', () => { + let wrapper; + + const createComponent = props => { + wrapper = shallowMount(localVue.extend(SquashBeforeMerge), { + localVue, + sync: false, + propsData: { + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('checkbox', () => { + const findCheckbox = () => wrapper.find('.qa-squash-checkbox'); + + it('is unchecked if passed value prop is false', () => { + createComponent({ + value: false, + }); + + expect(findCheckbox().element.checked).toBeFalsy(); + }); + + it('is checked if passed value prop is true', () => { + createComponent({ + value: true, + }); + + expect(findCheckbox().element.checked).toBeTruthy(); + }); + + it('changes value on click', done => { + createComponent({ + value: false, + }); + + findCheckbox().element.checked = true; + + findCheckbox().trigger('change'); + + wrapper.vm.$nextTick(() => { + expect(findCheckbox().element.checked).toBeTruthy(); + done(); + }); + }); + + it('is disabled if isDisabled prop is true', () => { + createComponent({ + value: false, + isDisabled: true, + }); + + expect(findCheckbox().attributes('disabled')).toBeTruthy(); + }); + }); + + describe('about link', () => { + it('is not rendered if no help path is passed', () => { + createComponent({ + value: false, + }); + + const aboutLink = wrapper.find('a'); + + expect(aboutLink.exists()).toBeFalsy(); + }); + + it('is rendered if help path is passed', () => { + createComponent({ + value: false, + helpPath: 'test-path', + }); + + const aboutLink = wrapper.find('a'); + + expect(aboutLink.exists()).toBeTruthy(); + }); + + it('should have a correct help path if passed', () => { + createComponent({ + value: false, + helpPath: 'test-path', + }); + + const aboutLink = wrapper.find('a'); + + expect(aboutLink.attributes('href')).toEqual('test-path'); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js index 8187b3204b1..12ee804f668 100644 --- a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js @@ -31,6 +31,12 @@ describe('Suggestion Diff component', () => { expect(header.innerHTML.includes('Suggested change')).toBe(true); }); + it('renders a help button', () => { + const helpBtn = vm.$el.querySelector('.js-help-btn'); + + expect(helpBtn).not.toBeNull(); + }); + it('renders an apply button', () => { const applyBtn = vm.$el.querySelector('.qa-apply-btn'); diff --git a/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb index 53c071f0268..f4a6f4be754 100644 --- a/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb +++ b/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb @@ -34,6 +34,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectRepositories do let!(:project_hashed_storage_2) { create(:project, name: 'bar', path: 'bar', namespace: group, storage_version: 2) } let!(:project_legacy_storage_3) { create(:project, name: 'baz', path: 'baz', namespace: group, storage_version: 0) } let!(:project_legacy_storage_4) { create(:project, name: 'zoo', path: 'zoo', namespace: group, storage_version: nil) } + let!(:project_legacy_storage_5) { create(:project, name: 'test', path: 'test', namespace: group, storage_version: nil) } describe '.on_hashed_storage' do it 'finds projects with repository on hashed storage' do @@ -47,7 +48,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectRepositories do it 'finds projects with repository on legacy storage' do projects = described_class.on_legacy_storage.pluck(:id) - expect(projects).to match_array([project_legacy_storage_3.id, project_legacy_storage_4.id]) + expect(projects).to match_array([project_legacy_storage_3.id, project_legacy_storage_4.id, project_legacy_storage_5.id]) end end @@ -58,7 +59,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectRepositories do projects = described_class.without_project_repository.pluck(:id) - expect(projects).to contain_exactly(project_hashed_storage_2.id, project_legacy_storage_4.id) + expect(projects).to contain_exactly(project_hashed_storage_2.id, project_legacy_storage_4.id, project_legacy_storage_5.id) end end @@ -78,14 +79,23 @@ describe Gitlab::BackgroundMigration::BackfillProjectRepositories do expect(project.disk_path).to eq(project_legacy_storage_3.disk_path) end + it 'returns the correct disk_path using the route entry' do + project_legacy_storage_5.route.update(path: 'zoo/new-test') + project = described_class.find(project_legacy_storage_5.id) + + expect(project.disk_path).to eq('zoo/new-test') + end + it 'raises OrphanedNamespaceError when any parent namespace does not exist' do subgroup = create(:group, parent: group) project_orphaned_namespace = create(:project, name: 'baz', path: 'baz', namespace: subgroup, storage_version: nil) subgroup.update_column(:parent_id, Namespace.maximum(:id).succ) project = described_class.find(project_orphaned_namespace.id) + project.route.destroy + subgroup.route.destroy - expect { project.disk_path } + expect { project.reload.disk_path } .to raise_error(Gitlab::BackgroundMigration::BackfillProjectRepositories::OrphanedNamespaceError) end end diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb index 941ef33c8a4..7651f594a4c 100644 --- a/spec/lib/gitlab/ci/config/entry/global_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb @@ -160,8 +160,7 @@ describe Gitlab::Ci::Config::Entry::Global do variables: { 'VAR' => 'value' }, ignore: false, after_script: ['make clean'], - only: { refs: %w[branches tags] }, - except: {} }, + only: { refs: %w[branches tags] } }, spinach: { name: :spinach, before_script: [], script: %w[spinach], @@ -172,8 +171,7 @@ describe Gitlab::Ci::Config::Entry::Global do variables: {}, ignore: false, after_script: ['make clean'], - only: { refs: %w[branches tags] }, - except: {} } + only: { refs: %w[branches tags] } } ) end end diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 3d0b98eb238..0560eb42e4d 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -258,8 +258,7 @@ describe Gitlab::Ci::Config::Entry::Job do stage: 'test', ignore: false, after_script: %w[cleanup], - only: { refs: %w[branches tags] }, - except: {}) + only: { refs: %w[branches tags] }) end end end diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb index d97be76f0e0..271ee30df3c 100644 --- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb @@ -67,14 +67,12 @@ describe Gitlab::Ci::Config::Entry::Jobs do script: %w[rspec], ignore: false, stage: 'test', - only: { refs: %w[branches tags] }, - except: {} }, + only: { refs: %w[branches tags] } }, spinach: { name: :spinach, script: %w[spinach], ignore: false, stage: 'test', - only: { refs: %w[branches tags] }, - except: {} }) + only: { refs: %w[branches tags] } }) end end diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb index 83001b7fdd8..1c987e13a9a 100644 --- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb @@ -168,8 +168,33 @@ describe Gitlab::Ci::Config::Entry::Policy do end end + describe '#value' do + context 'when default value has been provided' do + before do + entry.default = { refs: %w[branches tags] } + end + + context 'when user overrides default values' do + let(:config) { { refs: %w[feature], variables: %w[$VARIABLE] } } + + it 'does not include default values' do + expect(entry.value).to eq config + end + end + + context 'when default value has not been defined' do + let(:config) { { variables: %w[$VARIABLE] } } + + it 'includes default values' do + expect(entry.value).to eq(refs: %w[branches tags], + variables: %w[$VARIABLE]) + end + end + end + end + describe '.default' do - it 'does not have a default value' do + it 'does not have default policy' do expect(described_class.default).to be_nil end end diff --git a/spec/lib/gitlab/config/entry/configurable_spec.rb b/spec/lib/gitlab/config/entry/configurable_spec.rb index 85a7cf1d241..37e38e49c0d 100644 --- a/spec/lib/gitlab/config/entry/configurable_spec.rb +++ b/spec/lib/gitlab/config/entry/configurable_spec.rb @@ -7,6 +7,10 @@ describe Gitlab::Config::Entry::Configurable do end end + before do + allow(entry).to receive(:default) + end + describe 'validations' do context 'when entry is a hash' do let(:instance) { entry.new(key: 'value') } @@ -26,9 +30,11 @@ describe Gitlab::Config::Entry::Configurable do end describe 'configured entries' do + let(:entry_class) { double('entry_class', default: nil) } + before do - entry.class_eval do - entry :object, Object, description: 'test object' + entry.class_exec(entry_class) do |entry_class| + entry :object, entry_class, description: 'test object' end end diff --git a/spec/models/project_import_data_spec.rb b/spec/models/project_import_data_spec.rb index e9910c0a5d1..fe47811f074 100644 --- a/spec/models/project_import_data_spec.rb +++ b/spec/models/project_import_data_spec.rb @@ -39,4 +39,15 @@ describe ProjectImportData do expect(row.credentials).to eq({ 'number' => 10, 'foo' => 'bar' }) end end + + describe '#clear_credentials' do + it 'clears out the Hash' do + row = described_class.new + + row.merge_credentials('number' => 10) + row.clear_credentials + + expect(row.credentials).to eq({}) + end + end end diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index 224bc9ed935..c06e9a08ab4 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -303,6 +303,25 @@ describe RemoteMirror, :mailer do end end + context '#url=' do + let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first } + + it 'resets all the columns when URL changes' do + remote_mirror.update(last_error: Time.now, + last_update_at: Time.now, + last_successful_update_at: Time.now, + update_status: 'started', + error_notification_sent: true) + + expect { remote_mirror.update_attribute(:url, 'http://new.example.com') } + .to change { remote_mirror.last_error }.to(nil) + .and change { remote_mirror.last_update_at }.to(nil) + .and change { remote_mirror.last_successful_update_at }.to(nil) + .and change { remote_mirror.update_status }.to('finished') + .and change { remote_mirror.error_notification_sent }.to(false) + end + end + context '#updated_since?' do let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first } let(:timestamp) { Time.now - 5.minutes } diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index cfbda63bb30..45fb1562e84 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -63,7 +63,8 @@ describe API::Settings, 'Settings' do terms: 'Hello world!', performance_bar_allowed_group_path: group.full_path, instance_statistics_visibility_private: true, - diff_max_patch_bytes: 150_000 + diff_max_patch_bytes: 150_000, + default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE } expect(response).to have_gitlab_http_status(200) @@ -88,6 +89,7 @@ describe API::Settings, 'Settings' do expect(json_response['performance_bar_allowed_group_id']).to eq(group.id) expect(json_response['instance_statistics_visibility_private']).to be(true) expect(json_response['diff_max_patch_bytes']).to eq(150_000) + expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) end end diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index f5092e8e2b5..6109829aad1 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -22,7 +22,7 @@ describe API::Wikis do context 'when wiki has pages' do let!(:pages) do [create(:wiki_page, wiki: project_wiki, attrs: { title: 'page1', content: 'content of page1' }), - create(:wiki_page, wiki: project_wiki, attrs: { title: 'page2', content: 'content of page2' })] + create(:wiki_page, wiki: project_wiki, attrs: { title: 'page2.with.dot', content: 'content of page2' })] end it 'returns the list of wiki pages without content' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ca977effcb6..72684caad32 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,7 +22,7 @@ if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled) require 'rspec_profiling/rspec' end -if ENV['CI'] && !ENV['NO_KNAPSACK'] +if ENV['CI'] && ENV['KNAPSACK_GENERATE_REPORT'] && !ENV['NO_KNAPSACK'] require 'knapsack' Knapsack::Adapters::RSpecAdapter.bind end @@ -132,6 +132,11 @@ RSpec.configure do |config| Gitlab::ReleaseBlogPost.instance.instance_variable_set(:@url, 'https://about.gitlab.com') end + config.before(:example, :quarantine) do + # Skip tests in quarantine unless we explicitly focus on them. + skip('In quarantine') unless config.inclusion_filter[:quarantine] + end + config.before(:example, :request_store) do RequestStore.begin! end diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb index 1f688c0f9d3..dcf7c1a90c2 100644 --- a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb @@ -32,11 +32,13 @@ shared_examples 'backfill migration for project repositories' do |storage| it 'inserts rows in a single query' do projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + group2 = namespaces.create!(name: 'gro', path: 'gro') control_count = ActiveRecord::QueryRecorder.new { described_class.new.perform(1, projects.last.id) } projects.create!(name: 'bar', path: 'bar', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) - projects.create!(name: 'zoo', path: 'zoo', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + projects.create!(name: 'top', path: 'top', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + projects.create!(name: 'zoo', path: 'zoo', namespace_id: group2.id, storage_version: storage_version, repository_storage: shard.name) expect { described_class.new.perform(1, projects.last.id) }.not_to exceed_query_limit(control_count) end diff --git a/spec/workers/remote_mirror_notification_worker_spec.rb b/spec/workers/remote_mirror_notification_worker_spec.rb new file mode 100644 index 00000000000..e3db10ed645 --- /dev/null +++ b/spec/workers/remote_mirror_notification_worker_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe RemoteMirrorNotificationWorker, :mailer do + set(:project) { create(:project, :repository, :remote_mirror) } + set(:mirror) { project.remote_mirrors.first } + + describe '#execute' do + it 'calls NotificationService#remote_mirror_update_failed when the mirror exists' do + mirror.update_column(:last_error, "There was a problem fetching") + + expect(NotificationService).to receive_message_chain(:new, :remote_mirror_update_failed) + + subject.perform(mirror.id) + + expect(mirror.reload.error_notification_sent?).to be_truthy + end + + it 'does nothing when the mirror has no errors' do + expect(NotificationService).not_to receive(:new) + + subject.perform(mirror.id) + end + + it 'does nothing when the mirror does not exist' do + expect(NotificationService).not_to receive(:new) + + subject.perform(RemoteMirror.maximum(:id).to_i.succ) + end + + it 'does nothing when a notification has already been sent' do + mirror.update_columns(last_error: "There was a problem fetching", + error_notification_sent: true) + + expect(NotificationService).not_to receive(:new) + + subject.perform(mirror.id) + end + end +end diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb index d73b0b53713..b582a3650b6 100644 --- a/spec/workers/repository_update_remote_mirror_worker_spec.rb +++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb @@ -22,6 +22,13 @@ describe RepositoryUpdateRemoteMirrorWorker do expect { subject.perform(remote_mirror.id, Time.now) }.to change { remote_mirror.reload.update_status }.to('finished') end + it 'resets the notification flag upon success' do + expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success) + remote_mirror.update_column(:error_notification_sent, true) + + expect { subject.perform(remote_mirror.id, Time.now) }.to change { remote_mirror.reload.error_notification_sent }.to(false) + end + it 'sets status as failed when update remote mirror service executes with errors' do error_message = 'fail!' diff --git a/yarn.lock b/yarn.lock index da00f335362..fadfeb3dc49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,18 +29,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@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== - dependencies: - "@babel/types" "^7.1.2" - jsesc "^2.5.1" - lodash "^4.17.10" - source-map "^0.5.0" - trim-right "^1.0.1" - -"@babel/generator@^7.2.2": +"@babel/generator@^7.0.0", "@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== @@ -182,17 +171,7 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-replace-supers@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz#5fc31de522ec0ef0899dc9b3e7cf6a5dd655f362" - integrity sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.0.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-replace-supers@^7.2.3": +"@babel/helper-replace-supers@^7.1.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== @@ -245,12 +224,7 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409" - integrity sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ== - -"@babel/parser@^7.2.2", "@babel/parser@^7.2.3": +"@babel/parser@^7.0.0", "@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== @@ -617,16 +591,7 @@ js-levenshtein "^1.1.3" semver "^5.3.0" -"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644" - integrity sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.1.2" - "@babel/types" "^7.1.2" - -"@babel/template@^7.2.2": +"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^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== @@ -635,22 +600,7 @@ "@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" - integrity sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.0.0" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - debug "^3.1.0" - globals "^11.1.0" - lodash "^4.17.10" - -"@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@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== @@ -665,16 +615,7 @@ 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" - integrity sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg== - dependencies: - esutils "^2.0.2" - lodash "^4.17.10" - to-fast-properties "^2.0.0" - -"@babel/types@^7.2.0", "@babel/types@^7.2.2": +"@babel/types@^7.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== @@ -1654,6 +1595,11 @@ babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.26. lodash "^4.17.4" to-fast-properties "^1.0.3" +babylon@7.0.0-beta.19: + version "7.0.0-beta.19" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503" + integrity sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A== + babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -1746,7 +1692,7 @@ 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, bluebird@^3.5.3: +bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@~3.5.0: version "3.5.3" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== @@ -2099,6 +2045,13 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +catharsis@~0.8.9: + version "0.8.9" + resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.9.tgz#98cc890ca652dd2ef0e70b37925310ff9e90fc8b" + integrity sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is= + dependencies: + underscore-contrib "~0.3.0" + chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -3246,6 +3199,11 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +docdash@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/docdash/-/docdash-1.0.2.tgz#0449a8f6bb247f563020b78a5485dea95ae2e094" + integrity sha512-IEM57bWPLtVXpUeCKbiGvHsHtW9O9ZiiBPfeQDAZ7JdQiAF3aNWQoJ3e/+uJ63lHO009yn0tbJjtKpXJ2AURCQ== + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3554,7 +3512,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -4639,7 +4597,7 @@ 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.15, graceful-fs@^4.1.2: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.9: 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== @@ -6128,11 +6086,41 @@ js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.7.0: argparse "^1.0.7" esprima "^4.0.0" +js2xmlparser@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" + integrity sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM= + dependencies: + xmlcreate "^1.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdoc-vue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-vue/-/jsdoc-vue-1.0.0.tgz#ff3ac1ba6bc4a74079bb79058a7bf0066e346235" + integrity sha1-/zrBumvEp0B5u3kFinvwBm40YjU= + +jsdoc@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.5.5.tgz#484521b126e81904d632ff83ec9aaa096708fa4d" + integrity sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg== + dependencies: + babylon "7.0.0-beta.19" + bluebird "~3.5.0" + catharsis "~0.8.9" + escape-string-regexp "~1.0.5" + js2xmlparser "~3.0.0" + klaw "~2.0.0" + marked "~0.3.6" + mkdirp "~0.5.1" + requizzle "~0.2.1" + strip-json-comments "~2.0.1" + taffydb "2.6.2" + underscore "~1.8.3" + jsdom@^11.5.1: version "11.12.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" @@ -6385,6 +6373,13 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== +klaw@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.0.0.tgz#59c128e0dc5ce410201151194eeb9cbf858650f6" + integrity sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY= + dependencies: + graceful-fs "^4.1.9" + kleur@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300" @@ -6672,10 +6667,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -marked@^0.3.12: - version "0.3.12" - resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519" - integrity sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA== +marked@^0.3.12, marked@~0.3.6: + version "0.3.19" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" + integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg== match-at@^0.1.1: version "0.1.1" @@ -6916,7 +6911,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -7879,10 +7874,10 @@ prettier@1.13.7: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281" integrity sha512-KIU72UmYPGk4MujZGYMFwinB7lOf2LsDNGSOC8ufevsrPLISrZbNJlWstRi3m0AMuszbH+EFSQ/r6w56RSPK6w== -prettier@1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" - integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug== +prettier@1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== pretty-format@^23.6.0: version "23.6.0" @@ -8441,6 +8436,13 @@ requires-port@1.x.x, requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +requizzle@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" + integrity sha1-aUPDUwxNmn5G8c3dUcFY/GcM294= + dependencies: + underscore "~1.6.0" + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -9329,6 +9331,11 @@ table@^5.0.2: slice-ansi "2.0.0" string-width "^2.1.1" +taffydb@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" + integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg= + tapable@^0.1.8: version "0.1.10" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" @@ -9651,11 +9658,28 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" +underscore-contrib@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" + integrity sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc= + dependencies: + underscore "1.6.0" + +underscore@1.6.0, underscore@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= + underscore@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4" integrity sha512-4IV1DSSxC1QK48j9ONFK1MoIAKKkbE8i7u55w2R6IqBqbT7A/iG7aZBCR2Bi8piF0Uz+i/MG1aeqLwl/5vqF+A== +underscore@~1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -10353,6 +10377,11 @@ xmlbuilder@8.2.2: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773" integrity sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M= +xmlcreate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" + integrity sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8= + xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" |