diff options
Diffstat (limited to 'app')
62 files changed, 649 insertions, 133 deletions
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js index 798114b4b0b..d0b7f3ff7a2 100644 --- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js @@ -15,7 +15,7 @@ import { sprintf, __ } from '../../locale'; // </pre> // -// This is an arbitary number; Can be iterated upon when suitable. +// This is an arbitrary number; Can be iterated upon when suitable. const MAX_CHAR_LIMIT = 5000; export default function renderMermaid($els) { diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js new file mode 100644 index 00000000000..da82b52330a --- /dev/null +++ b/app/assets/javascripts/boards/stores/actions.js @@ -0,0 +1,65 @@ +const notImplemented = () => { + throw new Error('Not implemented!'); +}; + +export default { + setEndpoints: () => { + notImplemented(); + }, + + fetchLists: () => { + notImplemented(); + }, + + generateDefaultLists: () => { + notImplemented(); + }, + + createList: () => { + notImplemented(); + }, + + updateList: () => { + notImplemented(); + }, + + deleteList: () => { + notImplemented(); + }, + + fetchIssuesForList: () => { + notImplemented(); + }, + + moveIssue: () => { + notImplemented(); + }, + + createNewIssue: () => { + notImplemented(); + }, + + fetchBacklog: () => { + notImplemented(); + }, + + bulkUpdateIssues: () => { + notImplemented(); + }, + + fetchIssue: () => { + notImplemented(); + }, + + toggleIssueSubscription: () => { + notImplemented(); + }, + + showPage: () => { + notImplemented(); + }, + + toggleEmptyState: () => { + notImplemented(); + }, +}; diff --git a/app/assets/javascripts/boards/stores/index.js b/app/assets/javascripts/boards/stores/index.js new file mode 100644 index 00000000000..f70395a3771 --- /dev/null +++ b/app/assets/javascripts/boards/stores/index.js @@ -0,0 +1,14 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import state from 'ee_else_ce/boards/stores/state'; +import actions from 'ee_else_ce/boards/stores/actions'; +import mutations from 'ee_else_ce/boards/stores/mutations'; + +Vue.use(Vuex); + +export default () => + new Vuex.Store({ + state, + actions, + mutations, + }); diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js new file mode 100644 index 00000000000..fcdfa6799b6 --- /dev/null +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -0,0 +1,21 @@ +export const SET_ENDPOINTS = 'SET_ENDPOINTS'; +export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST'; +export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS'; +export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR'; +export const REQUEST_UPDATE_LIST = 'REQUEST_UPDATE_LIST'; +export const RECEIVE_UPDATE_LIST_SUCCESS = 'RECEIVE_UPDATE_LIST_SUCCESS'; +export const RECEIVE_UPDATE_LIST_ERROR = 'RECEIVE_UPDATE_LIST_ERROR'; +export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST'; +export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS'; +export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR'; +export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE'; +export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS'; +export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR'; +export const REQUEST_MOVE_ISSUE = 'REQUEST_MOVE_ISSUE'; +export const RECEIVE_MOVE_ISSUE_SUCCESS = 'RECEIVE_MOVE_ISSUE_SUCCESS'; +export const RECEIVE_MOVE_ISSUE_ERROR = 'RECEIVE_MOVE_ISSUE_ERROR'; +export const REQUEST_UPDATE_ISSUE = 'REQUEST_UPDATE_ISSUE'; +export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS'; +export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR'; +export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE'; +export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js new file mode 100644 index 00000000000..77ba68be07e --- /dev/null +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -0,0 +1,91 @@ +import * as mutationTypes from './mutation_types'; + +const notImplemented = () => { + throw new Error('Not implemented!'); +}; + +export default { + [mutationTypes.SET_ENDPOINTS]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_ADD_LIST]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_ADD_LIST_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_ADD_LIST_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_UPDATE_LIST]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_UPDATE_LIST_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_UPDATE_LIST_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_REMOVE_LIST]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_REMOVE_LIST_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_REMOVE_LIST_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_ADD_ISSUE]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_ADD_ISSUE_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_ADD_ISSUE_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_MOVE_ISSUE]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_MOVE_ISSUE_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_MOVE_ISSUE_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.REQUEST_UPDATE_ISSUE]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_UPDATE_ISSUE_SUCCESS]: () => { + notImplemented(); + }, + + [mutationTypes.RECEIVE_UPDATE_ISSUE_ERROR]: () => { + notImplemented(); + }, + + [mutationTypes.SET_CURRENT_PAGE]: () => { + notImplemented(); + }, + + [mutationTypes.TOGGLE_EMPTY_STATE]: () => { + notImplemented(); + }, +}; diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js new file mode 100644 index 00000000000..dd16abb01a5 --- /dev/null +++ b/app/assets/javascripts/boards/stores/state.js @@ -0,0 +1,3 @@ +export default () => ({ + // ... +}); diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js index a5b8c357e8a..04301c9ce12 100644 --- a/app/assets/javascripts/gl_field_error.js +++ b/app/assets/javascripts/gl_field_error.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { __ } from '~/locale'; /** * This class overrides the browser's validation error bubbles, displaying custom @@ -61,7 +62,7 @@ export default class GlFieldError { this.inputElement = $(input); this.inputDomElement = this.inputElement.get(0); this.form = formErrors; - this.errorMessage = this.inputElement.attr('title') || 'This field is required.'; + this.errorMessage = this.inputElement.attr('title') || __('This field is required.'); this.fieldErrorElement = $(`<p class='${errorMessageClass} hidden'>${this.errorMessage}</p>`); this.state = { diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index efba6fc1aff..96051b612b5 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -20,7 +20,7 @@ export default class GpgBadges { const endpoint = tag.data('signaturesPath'); if (!endpoint) { displayError(); - return Promise.reject(new Error('Missing commit signatures endpoint!')); + return Promise.reject(new Error(__('Missing commit signatures endpoint!'))); } const params = parseQueryStringIntoObject(tag.serialize()); diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index bdadbb1bb2a..a1263d1cdab 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import axios from './lib/utils/axios_utils'; import Api from './api'; import { normalizeHeaders } from './lib/utils/common_utils'; +import { __ } from '~/locale'; export default function groupsSelect() { import(/* webpackChunkName: 'select2' */ 'select2/select2') @@ -18,7 +19,7 @@ export default function groupsSelect() { : Api.groupsPath; $select.select2({ - placeholder: 'Search for a group', + placeholder: __('Search for a group'), allowClear: $select.hasClass('allowClear'), multiple: $select.hasClass('multiselect'), minimumInputLength: 0, diff --git a/app/assets/javascripts/lib/utils/highlight.js b/app/assets/javascripts/lib/utils/highlight.js index 4f7eff2cca1..8f0afa3467d 100644 --- a/app/assets/javascripts/lib/utils/highlight.js +++ b/app/assets/javascripts/lib/utils/highlight.js @@ -27,14 +27,14 @@ export default function highlight(string, match = '', matchPrefix = '<b>', match const sanitizedValue = sanitize(string.toString(), { allowedTags: [] }); - // occurences is an array of character indices that should be + // occurrences is an array of character indices that should be // highlighted in the original string, i.e. [3, 4, 5, 7] - const occurences = fuzzaldrinPlus.match(sanitizedValue, match.toString()); + const occurrences = fuzzaldrinPlus.match(sanitizedValue, match.toString()); return sanitizedValue .split('') .map((character, i) => { - if (_.contains(occurences, i)) { + if (_.contains(occurrences, i)) { return `${matchPrefix}${character}${matchSuffix}`; } diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 1af21715551..9f30a989295 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -148,7 +148,7 @@ function deferredInitialisation() { const canaryBadge = document.querySelector('.js-canary-badge'); const canaryLink = document.querySelector('.js-canary-link'); if (canaryBadge) { - canaryBadge.classList.add('hidden'); + canaryBadge.classList.remove('hidden'); } if (canaryLink) { canaryLink.classList.add('hidden'); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 869f70e7d33..6aa41d0825b 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -46,4 +46,12 @@ document.addEventListener('DOMContentLoaded', () => { GpgBadges.fetch(); leaveByUrl('project'); + + if (document.getElementById('js-tree-list')) { + import('~/repository') + .then(m => m.default()) + .catch(e => { + throw e; + }); + } }); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 400aed35e32..7b90a3a4f6e 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -40,4 +40,12 @@ document.addEventListener('DOMContentLoaded', () => { } GpgBadges.fetch(); + + if (document.getElementById('js-tree-list')) { + import('~/repository') + .then(m => m.default()) + .catch(e => { + throw e; + }); + } }); diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue index f4243522ef8..50f2910e02d 100644 --- a/app/assets/javascripts/reports/components/issues_list.vue +++ b/app/assets/javascripts/reports/components/issues_list.vue @@ -52,6 +52,11 @@ export default { required: false, default: '', }, + showReportSectionStatus: { + type: Boolean, + required: false, + default: true, + }, }, computed: { issuesWithState() { @@ -81,6 +86,7 @@ export default { :status="wrapped.status" :component="component" :is-new="wrapped.isNew" + :show-report-section-status="showReportSectionStatus" /> </smart-virtual-list> </template> diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue index d2106f9ad2e..01a30809e1a 100644 --- a/app/assets/javascripts/reports/components/report_item.vue +++ b/app/assets/javascripts/reports/components/report_item.vue @@ -34,12 +34,22 @@ export default { required: false, default: false, }, + showReportSectionStatusIcon: { + type: Boolean, + required: false, + default: true, + }, }, }; </script> <template> <li :class="{ 'is-dismissed': issue.isDismissed }" class="report-block-list-issue"> - <issue-status-icon :status="status" :status-icon-size="statusIconSize" class="append-right-5" /> + <issue-status-icon + v-if="showReportSectionStatusIcon" + :status="status" + :status-icon-size="statusIconSize" + class="append-right-5" + /> <component :is="component" v-if="component" :issue="issue" :status="status" :is-new="isNew" /> </li> diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue index d6483e95278..420e71f5e86 100644 --- a/app/assets/javascripts/reports/components/report_section.vue +++ b/app/assets/javascripts/reports/components/report_section.vue @@ -73,6 +73,11 @@ export default { default: () => ({}), required: false, }, + showReportSectionStatusIcon: { + type: Boolean, + required: false, + default: true, + }, }, data() { @@ -166,6 +171,7 @@ export default { :resolved-issues="resolvedIssues" :neutral-issues="neutralIssues" :component="component" + :show-report-section-status-icon="showReportSectionStatusIcon" /> </slot> </div> diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js index 5484900276c..25f9f70d095 100644 --- a/app/assets/javascripts/reports/store/state.js +++ b/app/assets/javascripts/reports/store/state.js @@ -40,6 +40,11 @@ export default () => ({ text: s__('Reports|Class'), type: fieldTypes.link, }, + classname: { + value: null, + text: s__('Reports|Classname'), + type: fieldTypes.text, + }, execution_time: { value: null, text: s__('Reports|Execution time'), diff --git a/app/assets/javascripts/repository/components/app.vue b/app/assets/javascripts/repository/components/app.vue new file mode 100644 index 00000000000..98240aef810 --- /dev/null +++ b/app/assets/javascripts/repository/components/app.vue @@ -0,0 +1,3 @@ +<template> + <router-view /> +</template> diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js new file mode 100644 index 00000000000..febfcce780c --- /dev/null +++ b/app/assets/javascripts/repository/graphql.js @@ -0,0 +1,11 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; + +Vue.use(VueApollo); + +const defaultClient = createDefaultClient({}); + +export default new VueApollo({ + defaultClient, +}); diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js new file mode 100644 index 00000000000..00b69362312 --- /dev/null +++ b/app/assets/javascripts/repository/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue'; +import createRouter from './router'; +import App from './components/app.vue'; +import apolloProvider from './graphql'; + +export default function setupVueRepositoryList() { + const el = document.getElementById('js-tree-list'); + const { projectPath, ref } = el.dataset; + + apolloProvider.clients.defaultClient.cache.writeData({ + data: { + projectPath, + ref, + }, + }); + + return new Vue({ + el, + router: createRouter(projectPath, ref), + apolloProvider, + render(h) { + return h(App); + }, + }); +} diff --git a/app/assets/javascripts/repository/pages/index.vue b/app/assets/javascripts/repository/pages/index.vue new file mode 100644 index 00000000000..fdbf195f0f6 --- /dev/null +++ b/app/assets/javascripts/repository/pages/index.vue @@ -0,0 +1,24 @@ +<script> +import getRef from '../queries/getRef.graphql'; + +export default { + apollo: { + ref: { + query: getRef, + }, + }, + data() { + return { + ref: '', + }; + }, +}; +</script> + +<template> + <div> + <router-link :to="{ path: `/tree/${ref}/app` }"> + Go to tree + </router-link> + </div> +</template> diff --git a/app/assets/javascripts/repository/pages/tree.vue b/app/assets/javascripts/repository/pages/tree.vue new file mode 100644 index 00000000000..f51aafee775 --- /dev/null +++ b/app/assets/javascripts/repository/pages/tree.vue @@ -0,0 +1,15 @@ +<script> +export default { + props: { + path: { + type: String, + required: false, + default: '/', + }, + }, +}; +</script> + +<template> + <div>{{ path }}</div> +</template> diff --git a/app/assets/javascripts/repository/queries/getRef.graphql b/app/assets/javascripts/repository/queries/getRef.graphql new file mode 100644 index 00000000000..58c09844c3f --- /dev/null +++ b/app/assets/javascripts/repository/queries/getRef.graphql @@ -0,0 +1,3 @@ +query getRef { + ref @client +} diff --git a/app/assets/javascripts/repository/router.js b/app/assets/javascripts/repository/router.js new file mode 100644 index 00000000000..b42a96a4ee2 --- /dev/null +++ b/app/assets/javascripts/repository/router.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import { joinPaths } from '../lib/utils/url_utility'; +import IndexPage from './pages/index.vue'; +import TreePage from './pages/tree.vue'; + +Vue.use(VueRouter); + +export default function createRouter(base, baseRef) { + return new VueRouter({ + mode: 'history', + base: joinPaths(gon.relative_url_root || '', base), + routes: [ + { + path: '/', + name: 'projectRoot', + component: IndexPage, + }, + { + path: `/tree/${baseRef}(/.*)?`, + name: 'treePath', + component: TreePage, + props: route => ({ + path: route.params.pathMatch, + }), + beforeEnter(to, from, next) { + document + .querySelectorAll('.js-hide-on-navigation') + .forEach(el => el.classList.add('hidden')); + + next(); + }, + }, + ], + }); +} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index da0a9483f8e..8f4cae8ae58 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -23,6 +23,8 @@ export default { TooltipOnTruncate, FilteredSearchDropdown, ReviewAppLink, + VisualReviewAppLink: () => + import('ee_component/vue_merge_request_widget/components/visual_review_app_link.vue'), }, directives: { GlTooltip: GlTooltipDirective, @@ -37,6 +39,20 @@ export default { type: Boolean, required: true, }, + showVisualReviewApp: { + type: Boolean, + required: false, + default: false, + }, + visualReviewAppMeta: { + type: Object, + required: false, + default: () => ({ + sourceProjectId: '', + issueId: '', + appUrl: '', + }), + }, }, deployedTextMap: { running: __('Deploying to'), @@ -168,6 +184,11 @@ export default { :link="deploymentExternalUrl" :css-class="`deploy-link js-deploy-url inline ${slotProps.className}`" /> + <visual-review-app-link + v-if="showVisualReviewApp" + :link="deploymentExternalUrl" + :app-metadata="visualReviewAppMeta" + /> </template> <template slot="result" slot-scope="slotProps"> @@ -187,11 +208,17 @@ export default { </a> </template> </filtered-search-dropdown> - <review-app-link - v-else - :link="deploymentExternalUrl" - css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inlin" - /> + <template v-else> + <review-app-link + :link="deploymentExternalUrl" + css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inline" + /> + <visual-review-app-link + v-if="showVisualReviewApp" + :link="deploymentExternalUrl" + :app-metadata="visualReviewAppMeta" + /> + </template> </template> <span v-if="deployment.stop_url" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue index 5f5fe67b3c1..b9f5f602117 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue @@ -30,9 +30,6 @@ export default { }, }, computed: { - pipeline() { - return this.isPostMerge ? this.mr.mergePipeline : this.mr.pipeline; - }, branch() { return this.isPostMerge ? this.mr.targetBranch : this.mr.sourceBranch; }, @@ -48,6 +45,19 @@ export default { hasDeploymentMetrics() { return this.isPostMerge; }, + visualReviewAppMeta() { + return { + appUrl: this.mr.appUrl, + issueId: this.mr.iid, + sourceProjectId: this.mr.sourceProjectId, + }; + }, + pipeline() { + return this.isPostMerge ? this.mr.mergePipeline : this.mr.pipeline; + }, + showVisualReviewAppLink() { + return !!(this.mr.visualReviewFF && this.mr.visualReviewAppAvailable); + }, }, }; </script> @@ -61,14 +71,18 @@ export default { :source-branch-link="branchLink" :troubleshooting-docs-path="mr.troubleshootingDocsPath" /> - <div v-if="deployments.length" slot="footer" class="mr-widget-extension"> - <deployment - v-for="deployment in deployments" - :key="deployment.id" - :class="deploymentClass" - :deployment="deployment" - :show-metrics="hasDeploymentMetrics" - /> - </div> + <template v-slot:footer> + <div v-if="deployments.length" class="mr-widget-extension"> + <deployment + v-for="deployment in deployments" + :key="deployment.id" + :class="deploymentClass" + :deployment="deployment" + :show-metrics="hasDeploymentMetrics" + :show-visual-review-app="true" + :visual-review-app-meta="visualReviewAppMeta" + /> + </div> + </template> </mr-widget-container> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue index de9c122f268..457a71cab95 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue @@ -19,6 +19,6 @@ export default { </script> <template> <a :href="link" target="_blank" rel="noopener noreferrer nofollow" :class="cssClass"> - {{ __('View app') }} <icon name="external-link" /> + {{ __('View app') }} <icon css-classes="fgray" name="external-link" /> </a> </template> diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index 671b4909839..a620f560b52 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -7,7 +7,7 @@ * * @example * <clipboard-button - * title="Copy to clipbard" + * title="Copy to clipboard" * text="Content to be copied" * css-class="btn-transparent" * /> diff --git a/app/assets/stylesheets/components/toast.scss b/app/assets/stylesheets/components/toast.scss index 91d16c8e98d..33e1c4e5349 100644 --- a/app/assets/stylesheets/components/toast.scss +++ b/app/assets/stylesheets/components/toast.scss @@ -1,3 +1,53 @@ -.toast-close { - font-size: $default-icon-size !important; +/* +* These styles are specific to the gl-toast component. +* Documentation: https://design.gitlab.com/components/toasts +* Note: Styles below are nested in order to override some of vue-toasted's default styling +*/ +.toasted-container { + + max-width: $toast-max-width; + + @include media-breakpoint-down(xs) { + width: 100%; + padding-right: $toast-padding-right; + } + + .toasted.gl-toast { + border-radius: $border-radius-default; + font-size: $gl-font-size; + padding: $gl-padding-8 $gl-padding-24; + margin-top: $toast-default-margin; + line-height: $gl-line-height; + background-color: rgba($gray-900, $toast-background-opacity); + + @include media-breakpoint-down(xs) { + .action:first-child { + // Ensures actions buttons are right aligned on mobile + margin-left: auto; + } + } + + .action { + color: $blue-300; + margin: 0 0 0 $toast-action-margin-left; + text-transform: none; + font-size: $gl-font-size; + + &:first-child { + padding-right: 0; + } + } + + .toast-close { + font-size: $default-icon-size; + margin-left: $toast-default-margin; + padding-left: $gl-padding; + } + } +} + +// Overrides the default positioning of toasts +body .toasted-container.bottom-left { + bottom: $toast-offset; + left: $toast-offset; } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 2e38b260f5f..fc488b85138 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -5,6 +5,9 @@ .cgreen { color: $green-600; } .cdark { color: $common-gray-dark; } +.fwhite { fill: $white-light; } +.fgray { fill: $gray-700; } + .text-plain, .text-plain:hover { color: $gl-text-color; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 4a034e1e547..1cf122102cc 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -432,7 +432,7 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%); */ $monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; -$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, +$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans', Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; @@ -498,6 +498,17 @@ $pagination-line-height: 20px; $pagination-disabled-color: #cdcdcd; /* +* Toasts +*/ +$toast-offset: 24px; +$toast-height: 48px; +$toast-max-width: 586px; +$toast-padding-right: 42px; +$toast-default-margin: 8px; +$toast-action-margin-left: 16px; +$toast-background-opacity: 0.95; + +/* * Status icons */ $status-icon-size: 22px; diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 47ffdbae4b6..f8e273a2735 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -14,7 +14,7 @@ } .member { - &.is-overriden { + &.is-overridden { .btn-ldap-override { display: none !important; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 709940ba6c8..44b558dd5ff 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -972,10 +972,6 @@ } } - .btn svg { - fill: $gray-700; - } - .dropdown-menu { width: 400px; } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 12a3b8c88f3..aa6bbc8e473 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -903,7 +903,7 @@ button.mini-pipeline-graph-dropdown-toggle { // Match dropdown.scss for all `a` tags &.non-details-job-component { - padding: 8px 16px; + padding: $gl-padding-8 $gl-btn-horz-padding; } .ci-job-name-component { diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 5a4adea497b..c342e1c80b0 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -221,7 +221,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController def metrics_params return unless Feature.enabled?(:metrics_time_window, project) - return unless params[:start].present? || params[:end].present? params.require([:start, :end]) end diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb index 1c3c24ad6dc..f7e49166ca0 100644 --- a/app/graphql/resolvers/issues_resolver.rb +++ b/app/graphql/resolvers/issues_resolver.rb @@ -46,7 +46,7 @@ module Resolvers def resolve(**args) # The project could have been loaded in batch by `BatchLoader`. # At this point we need the `id` of the project to query for issues, so - # make sure it's loaded and not `nil` before continueing. + # make sure it's loaded and not `nil` before continuing. project.sync if project.respond_to?(:sync) return Issue.none if project.nil? diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d2f5ff13408..1b67a7272bc 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -140,6 +140,8 @@ module Ci where("EXISTS (?)", matcher) end + scope :queued_before, ->(time) { where(arel_table[:queued_at].lt(time)) } + ## # TODO: Remove these mounters when we remove :ci_enable_legacy_artifacts feature flag mount_uploader :legacy_artifacts_file, LegacyArtifactUploader, mount_on: :artifacts_file @@ -378,8 +380,6 @@ module Ci end def any_unmet_prerequisites? - return false unless Feature.enabled?(:ci_preparing_state, default_enabled: true) - prerequisites.present? end diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb index 987c057ad6d..36c51522089 100644 --- a/app/models/clusters/applications/jupyter.rb +++ b/app/models/clusters/applications/jupyter.rb @@ -39,7 +39,7 @@ module Clusters end # Will be addressed in future MRs - # We need to investigate and document what will be permenantly deleted. + # We need to investigate and document what will be permanently deleted. def allowed_to_uninstall? false end diff --git a/app/models/clusters/concerns/application_data.rb b/app/models/clusters/concerns/application_data.rb index a48ee340fac..3479fea415e 100644 --- a/app/models/clusters/concerns/application_data.rb +++ b/app/models/clusters/concerns/application_data.rb @@ -3,56 +3,52 @@ module Clusters module Concerns module ApplicationData - extend ActiveSupport::Concern - - included do - def uninstall_command - Gitlab::Kubernetes::Helm::DeleteCommand.new( - name: name, - rbac: cluster.platform_kubernetes_rbac?, - files: files - ) - end + def uninstall_command + Gitlab::Kubernetes::Helm::DeleteCommand.new( + name: name, + rbac: cluster.platform_kubernetes_rbac?, + files: files + ) + end - def repository - nil - end + def repository + nil + end - def values - File.read(chart_values_file) - end + def values + File.read(chart_values_file) + end - def files - @files ||= begin - files = { 'values.yaml': values } + def files + @files ||= begin + files = { 'values.yaml': values } - files.merge!(certificate_files) if cluster.application_helm.has_ssl? + files.merge!(certificate_files) if cluster.application_helm.has_ssl? - files - end + files end + end - private + private - def certificate_files - { - 'ca.pem': ca_cert, - 'cert.pem': helm_cert.cert_string, - 'key.pem': helm_cert.key_string - } - end + def certificate_files + { + 'ca.pem': ca_cert, + 'cert.pem': helm_cert.cert_string, + 'key.pem': helm_cert.key_string + } + end - def ca_cert - cluster.application_helm.ca_cert - end + def ca_cert + cluster.application_helm.ca_cert + end - def helm_cert - @helm_cert ||= cluster.application_helm.issue_client_cert - end + def helm_cert + @helm_cert ||= cluster.application_helm.issue_client_cert + end - def chart_values_file - "#{Rails.root}/vendor/#{name}/values.yaml" - end + def chart_values_file + "#{Rails.root}/vendor/#{name}/values.yaml" end end end diff --git a/app/models/environment.rb b/app/models/environment.rb index 0eda7a2513f..aff20dae09b 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -155,11 +155,11 @@ class Environment < ApplicationRecord end def has_terminals? - project.deployment_platform.present? && available? && last_deployment.present? + deployment_platform.present? && available? && last_deployment.present? end def terminals - project.deployment_platform.terminals(self) if has_terminals? + deployment_platform.terminals(self) if has_terminals? end def has_metrics? diff --git a/app/serializers/test_case_entity.rb b/app/serializers/test_case_entity.rb index ec60055ba5b..5c915c1302c 100644 --- a/app/serializers/test_case_entity.rb +++ b/app/serializers/test_case_entity.rb @@ -3,6 +3,7 @@ class TestCaseEntity < Grape::Entity expose :status expose :name + expose :classname expose :execution_time expose :system_output expose :stack_trace diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index 6707a1363d0..baa3f898b2d 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -36,6 +36,11 @@ module Ci builds = builds.with_any_tags end + # pick builds that older than specified age + if params.key?(:job_age) + builds = builds.queued_before(params[:job_age].seconds.ago) + end + builds.each do |build| next unless runner.can_pick?(build) diff --git a/app/services/clusters/refresh_service.rb b/app/services/clusters/refresh_service.rb index b02bb9c0247..3752a306793 100644 --- a/app/services/clusters/refresh_service.rb +++ b/app/services/clusters/refresh_service.rb @@ -21,11 +21,7 @@ module Clusters private_class_method :projects_with_missing_kubernetes_namespaces_for_cluster def self.clusters_with_missing_kubernetes_namespaces_for_project(project) - if Feature.enabled?(:ci_preparing_state, default_enabled: true) - project.clusters.managed.missing_kubernetes_namespace(project.kubernetes_namespaces) - else - project.all_clusters.managed.missing_kubernetes_namespace(project.kubernetes_namespaces) - end + project.clusters.managed.missing_kubernetes_namespace(project.kubernetes_namespaces) end private_class_method :clusters_with_missing_kubernetes_namespaces_for_project diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb index 9d371e234ee..d30df34e54b 100644 --- a/app/services/git/base_hooks_service.rb +++ b/app/services/git/base_hooks_service.rb @@ -17,6 +17,8 @@ module Git # Not a hook, but it needs access to the list of changed commits enqueue_invalidate_cache + update_remote_mirrors + push_data end @@ -92,5 +94,12 @@ module Git def pipeline_options {} end + + def update_remote_mirrors + return unless project.has_remote_mirror? + + project.mark_stuck_remote_mirrors_as_failed! + project.update_remote_mirrors + end end end diff --git a/app/services/git/branch_push_service.rb b/app/services/git/branch_push_service.rb index abf11f253f6..c4910180787 100644 --- a/app/services/git/branch_push_service.rb +++ b/app/services/git/branch_push_service.rb @@ -27,7 +27,6 @@ module Git execute_related_hooks perform_housekeeping - update_remote_mirrors stop_environments true diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 4ea40e3c8ce..9f335cceb67 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -100,8 +100,6 @@ module Projects current_user.invalidate_personal_projects_count create_readme if @initialize_with_readme - - configure_group_clusters_for_project end # Refresh the current user's authorizations inline (so they can access the @@ -127,10 +125,6 @@ module Projects Files::CreateService.new(@project, current_user, commit_attrs).execute end - def configure_group_clusters_for_project - ClusterProjectConfigureWorker.perform_async(@project.id) - end - def skip_wiki? !@project.feature_available?(:wiki, current_user) || @skip_wiki end diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 91c01eca75c..233dcf37e35 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -54,7 +54,6 @@ module Projects end attempt_transfer_transaction - configure_group_clusters_for_project end # rubocop: enable CodeReuse/ActiveRecord @@ -164,9 +163,5 @@ module Projects @new_namespace.full_path ) end - - def configure_group_clusters_for_project - ClusterProjectConfigureWorker.perform_async(project.id) - end end end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 6dfe2bed0ba..1c7582533ad 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -109,12 +109,20 @@ class FileUploader < GitlabUploader def upload_path if file_storage? # Legacy path relative to project.full_path - File.join(dynamic_segment, identifier) + local_storage_path(identifier) else - File.join(store_dir, identifier) + remote_storage_path(identifier) end end + def local_storage_path(file_identifier) + File.join(dynamic_segment, file_identifier) + end + + def remote_storage_path(file_identifier) + File.join(store_dir, file_identifier) + end + def store_dirs { Store::LOCAL => File.join(base_dir, dynamic_segment), diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb index f4697d898af..b43162f0935 100644 --- a/app/uploaders/personal_file_uploader.rb +++ b/app/uploaders/personal_file_uploader.rb @@ -6,15 +6,12 @@ class PersonalFileUploader < FileUploader options.storage_path end - def self.base_dir(model, store = nil) - base_dirs(model)[store || Store::LOCAL] - end - - def self.base_dirs(model) - { - Store::LOCAL => File.join(options.base_dir, model_path_segment(model)), - Store::REMOTE => model_path_segment(model) - } + def self.base_dir(model, _store = nil) + # base_dir is the path seen by the user when rendering Markdown, so + # it should be the same for both local and object storage. It is + # typically prefaced with uploads/-/system, but that prefix + # is omitted in the path stored on disk. + File.join(options.base_dir, model_path_segment(model)) end def self.model_path_segment(model) @@ -40,8 +37,61 @@ class PersonalFileUploader < FileUploader store_dirs[object_store] end + # A personal snippet path is stored using FileUploader#upload_path. + # + # The format for the path: + # + # Local storage: :random_hex/:filename. + # Object storage: personal_snippet/:id/:random_hex/:filename. + # + # upload_paths represent the possible paths for a given identifier, + # which will vary depending on whether the file is stored in local or + # object storage. upload_path should match an element in upload_paths. + # + # base_dir represents the path seen by the user in Markdown, and it + # should always be prefixed with uploads/-/system. + # + # store_dirs represent the paths that are actually used on disk. For + # object storage, this should omit the prefix /uploads/-/system. + # + # For example, consider the requested path /uploads/-/system/personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20/file.png. + # + # For local storage: + # + # File on disk: /opt/gitlab/embedded/service/gitlab-rails/public/uploads/-/system/personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20/file.png. + # + # base_dir: uploads/-/system/personal_snippet/172 + # upload_path: ff4ad5c2e40b39ae57cda51577317d20/file.png + # upload_paths: ["ff4ad5c2e40b39ae57cda51577317d20/file.png", "personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20/file.png"]. + # store_dirs: + # => {1=>"uploads/-/system/personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20", 2=>"personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20"} + # + # For object storage: + # + # upload_path: personal_snippet/172/ff4ad5c2e40b39ae57cda51577317d20/file.png + def upload_paths(identifier) + [ + local_storage_path(identifier), + File.join(remote_storage_base_path, identifier) + ] + end + + def store_dirs + { + Store::LOCAL => File.join(base_dir, dynamic_segment), + Store::REMOTE => remote_storage_base_path + } + end + private + # To avoid prefacing the remote storage path with `/uploads/-/system`, + # we just drop that part so that the destination path will be + # personal_snippet/:id/:random_hex/:filename. + def remote_storage_base_path + File.join(self.class.model_path_segment(model), dynamic_segment) + end + def secure_url File.join('/', base_dir, secret, file.filename) end diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 8fb38f6a690..62d1d01cc83 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -2,7 +2,7 @@ = form_errors(@group) = render 'shared/group_form', f: f - = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group + = render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group = render_if_exists 'admin/namespace_plan', f: f .form-group.row.group-description-holder diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index a733f420d11..e7dde7985fd 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -6,6 +6,7 @@ %span.cred (Internal) - if @user.admin %span.cred (Admin) + = render_if_exists 'admin/users/audtior_user_badge' .float-right - if impersonation_enabled? && @user != current_user && @user.can?(:log_in) diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml index c382a1ed168..e12748666c8 100644 --- a/app/views/groups/settings/_general.html.haml +++ b/app/views/groups/settings/_general.html.haml @@ -17,17 +17,17 @@ = f.label :description, _('Group description (optional)'), class: 'label-bold' = f.text_area :description, class: 'form-control', rows: 3, maxlength: 250 - = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group + = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group - .form-group.prepend-top-default.append-bottom-20 - .avatar-container.rect-avatar.s90 - = group_icon(@group, alt: '', class: 'avatar group-avatar s90') - = f.label :avatar, _('Group avatar'), class: 'label-bold d-block' - = render 'shared/choose_avatar_button', f: f - - if @group.avatar? - %hr - = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'btn btn-link' + .form-group.prepend-top-default.append-bottom-20 + .avatar-container.rect-avatar.s90 + = group_icon(@group, alt: '', class: 'avatar group-avatar s90') + = f.label :avatar, _('Group avatar'), class: 'label-bold d-block' + = render 'shared/choose_avatar_button', f: f + - if @group.avatar? + %hr + = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'btn btn-link' - = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group + = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group = f.submit _('Save changes'), class: 'btn btn-success mt-4 js-dirty-submit' diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 1c1d1ea4645..f8b7d0c530a 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -18,7 +18,7 @@ %span.logo-text.d-none.d-lg-block.prepend-left-8 = logo_text - if Gitlab.com? - = link_to 'https://next.gitlab.com', class: 'label-link js-canary-badge canary-badge bg-transparent', target: :_blank do + = link_to 'https://next.gitlab.com', class: 'label-link js-canary-badge canary-badge bg-transparent hidden', target: :_blank do %span.color-label.has-tooltip.badge.badge-pill.green-badge = _('Next') diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 22a721ee9ad..0edd8ee5e46 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -4,6 +4,7 @@ - project = local_assigns.fetch(:project) { @project } - content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) } - show_auto_devops_callout = show_auto_devops_callout?(@project) +- vue_file_list = Feature.enabled?(:vue_file_list, @project) #tree-holder.tree-holder.clearfix .nav-block @@ -13,7 +14,12 @@ = render 'shared/commit_well', commit: commit, ref: ref, project: project - if is_project_overview - .project-buttons.append-bottom-default + .project-buttons.append-bottom-default{ class: ("js-hide-on-navigation" if vue_file_list) } = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout) - = render 'projects/tree/tree_content', tree: @tree, content_url: content_url + - if vue_file_list + #js-tree-list{ data: { project_path: @project.full_path, ref: ref } } + - if @tree.readme + = render "projects/tree/readme", readme: @tree.readme + - else + = render 'projects/tree/tree_content', tree: @tree, content_url: content_url diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 3ca4abddbb8..a97322dace4 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,7 +1,7 @@ - empty_repo = @project.empty_repo? - show_auto_devops_callout = show_auto_devops_callout?(@project) - max_project_topic_length = 15 -.project-home-panel{ class: ("empty-project" if empty_repo) } +.project-home-panel{ class: [("empty-project" if empty_repo), ("js-hide-on-navigation" if Feature.enabled?(:vue_file_list, @project))] } .row.append-bottom-8 .home-panel-title-row.col-md-12.col-lg-6.d-flex .avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 9d254463fb6..2db1efdd52f 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -30,6 +30,8 @@ = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do = icon("rss") + = render_if_exists 'projects/commits/mirror_status' + %div{ id: dom_id(@project) } %ol#commits-list.list-unstyled.content_list = render 'commits', project: @project, ref: @ref diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml index b12ae995ece..366d7a7a2eb 100644 --- a/app/views/projects/protected_branches/_protected_branch.html.haml +++ b/app/views/projects/protected_branches/_protected_branch.html.haml @@ -1,2 +1,2 @@ = render layout: 'projects/protected_branches/shared/protected_branch', locals: { protected_branch: protected_branch } do - = render partial: 'projects/protected_branches/update_protected_branch', locals: { protected_branch: protected_branch } + = render_if_exists 'projects/protected_branches/update_protected_branch', protected_branch: protected_branch diff --git a/app/views/projects/settings/_general.html.haml b/app/views/projects/settings/_general.html.haml index 380430ff52b..520f342f567 100644 --- a/app/views/projects/settings/_general.html.haml +++ b/app/views/projects/settings/_general.html.haml @@ -27,7 +27,7 @@ .row= render_if_exists 'projects/classification_policy_settings', f: f - .row= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project + = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project .form-group.prepend-top-default.append-bottom-20 .avatar-container.s90 diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml index 4daacbe157c..e935af23659 100644 --- a/app/views/projects/tree/_readme.html.haml +++ b/app/views/projects/tree/_readme.html.haml @@ -1,5 +1,5 @@ - if readme.rich_viewer - %article.file-holder.readme-holder{ id: 'readme', class: ("limited-width-container" unless fluid_layout) } + %article.file-holder.readme-holder{ id: 'readme', class: [("limited-width-container" unless fluid_layout), ("js-hide-on-navigation" if Feature.enabled?(:vue_file_list, @project))] } .js-file-title.file-title = blob_icon readme.mode, readme.name = link_to project_blob_path(@project, tree_join(@ref, readme.path)) do diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 2c185549b24..63557c882f4 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -164,7 +164,7 @@ = dropdown_content = dropdown_loading = dropdown_footer add_content_class: true do - %button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ disabled: true } + %button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ type: 'button', disabled: true } = _('Move') = icon('spinner spin', class: 'sidebar-move-issue-confirmation-loading-icon') diff --git a/app/workers/cluster_configure_worker.rb b/app/workers/cluster_configure_worker.rb index 37ea7dde7a1..6f64b7ea0ab 100644 --- a/app/workers/cluster_configure_worker.rb +++ b/app/workers/cluster_configure_worker.rb @@ -6,7 +6,7 @@ class ClusterConfigureWorker def perform(cluster_id) Clusters::Cluster.managed.find_by_id(cluster_id).try do |cluster| - if cluster.project_type? || Feature.disabled?(:ci_preparing_state, default_enabled: true) + if cluster.project_type? Clusters::RefreshService.create_or_update_namespaces_for_cluster(cluster) end end diff --git a/app/workers/pages_domain_removal_cron_worker.rb b/app/workers/pages_domain_removal_cron_worker.rb index 3aca123e5ac..79f38e1b89f 100644 --- a/app/workers/pages_domain_removal_cron_worker.rb +++ b/app/workers/pages_domain_removal_cron_worker.rb @@ -5,8 +5,6 @@ class PagesDomainRemovalCronWorker include CronjobQueue def perform - return unless Feature.enabled?(:remove_disabled_domains) - PagesDomain.for_removal.find_each do |domain| domain.destroy! rescue => e |