diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-19 18:08:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-19 18:08:54 +0000 |
commit | b9d98fe10a624d9a6033c516c970954b4fc09372 (patch) | |
tree | 74cb5f7f97f7d40c4a5970855217bd2ee226b9a7 /app | |
parent | 589b674b06c4acb5c357f6444fb0b7344585fdc5 (diff) | |
download | gitlab-ce-b9d98fe10a624d9a6033c516c970954b4fc09372.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
16 files changed, 240 insertions, 111 deletions
diff --git a/app/assets/javascripts/analytics/instance_statistics/components/pipelines_chart.vue b/app/assets/javascripts/analytics/instance_statistics/components/pipelines_chart.vue index 279fcfe736f..b16d960402b 100644 --- a/app/assets/javascripts/analytics/instance_statistics/components/pipelines_chart.vue +++ b/app/assets/javascripts/analytics/instance_statistics/components/pipelines_chart.vue @@ -4,7 +4,11 @@ import { GlAlert } from '@gitlab/ui'; import { mapKeys, mapValues, pick, some, sum } from 'lodash'; import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; import { s__ } from '~/locale'; -import { formatDateAsMonth, getDayDifference } from '~/lib/utils/datetime_utility'; +import { + differenceInMonths, + formatDateAsMonth, + getDayDifference, +} from '~/lib/utils/datetime_utility'; import { getAverageByMonth, sortByDate, extractValues } from '../utils'; import pipelineStatsQuery from '../graphql/queries/pipeline_stats.query.graphql'; import { TODAY, START_DATE } from '../constants'; @@ -150,19 +154,14 @@ export default { max: this.$options.endDate, }; }, - differenceInMonths() { - const yearDiff = this.$options.endDate.getYear() - this.$options.startDate.getYear(); - const monthDiff = this.$options.endDate.getMonth() - this.$options.startDate.getMonth(); - - return monthDiff + 12 * yearDiff; - }, chartOptions() { + const { endDate, startDate, i18n } = this.$options; return { xAxis: { ...this.range, - name: this.$options.i18n.xAxisTitle, + name: i18n.xAxisTitle, type: 'time', - splitNumber: this.differenceInMonths + 1, + splitNumber: differenceInMonths(startDate, endDate) + 1, axisLabel: { interval: 0, showMinLabel: false, @@ -172,7 +171,7 @@ export default { }, }, yAxis: { - name: this.$options.i18n.yAxisTitle, + name: i18n.yAxisTitle, }, }; }, diff --git a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue index bbcb866c758..53fac09ab66 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue @@ -1,6 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { GlModal } from '@gitlab/ui'; +import { GlModal, GlButton } from '@gitlab/ui'; import { sprintf, __ } from '~/locale'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; @@ -8,6 +8,7 @@ import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; export default { components: { GlModal, + GlButton, FileIcon, ChangedFileIcon, }, @@ -52,15 +53,16 @@ export default { </strong> <changed-file-icon :file="activeFile" :is-centered="false" /> <div class="ml-auto"> - <button + <gl-button v-if="canDiscard" ref="discardButton" - type="button" - class="btn btn-remove btn-inverted gl-mr-3" + category="secondary" + variant="danger" + class="gl-mr-3" @click="showDiscardModal" > {{ __('Discard changes') }} - </button> + </gl-button> </div> <gl-modal ref="discardModal" diff --git a/app/assets/javascripts/logs/components/environment_logs.vue b/app/assets/javascripts/logs/components/environment_logs.vue index 97b96cb5839..f7c0bd5ae13 100644 --- a/app/assets/javascripts/logs/components/environment_logs.vue +++ b/app/assets/javascripts/logs/components/environment_logs.vue @@ -3,12 +3,11 @@ import { throttle } from 'lodash'; import { mapActions, mapState, mapGetters } from 'vuex'; import { GlSprintf, - GlIcon, GlAlert, - GlDeprecatedDropdown, - GlDeprecatedDropdownHeader, - GlDeprecatedDropdownItem, - GlDeprecatedDropdownDivider, + GlDropdown, + GlDropdownSectionHeader, + GlDropdownItem, + GlDropdownDivider, GlInfiniteScroll, } from '@gitlab/ui'; @@ -23,12 +22,11 @@ import { formatDate } from '../utils'; export default { components: { GlSprintf, - GlIcon, GlAlert, - GlDeprecatedDropdown, - GlDeprecatedDropdownHeader, - GlDeprecatedDropdownItem, - GlDeprecatedDropdownDivider, + GlDropdown, + GlDropdownSectionHeader, + GlDropdownItem, + GlDropdownDivider, GlInfiniteScroll, LogSimpleFilters, LogAdvancedFilters, @@ -174,46 +172,38 @@ export default { <div class="top-bar d-md-flex border bg-secondary-50 pt-2 pr-1 pb-0 pl-2"> <div class="flex-grow-0"> - <gl-deprecated-dropdown + <gl-dropdown id="environments-dropdown" :text="environments.current || managedApps.current" :disabled="environments.isLoading" - class="mb-2 gl-h-32 pr-2 d-flex d-md-block js-environments-dropdown" + class="gl-mr-3 gl-mb-3 gl-display-flex gl-display-md-block js-environments-dropdown" > - <gl-deprecated-dropdown-header class="gl-text-center"> + <gl-dropdown-section-header> {{ s__('Environments|Environments') }} - </gl-deprecated-dropdown-header> - <gl-deprecated-dropdown-item + </gl-dropdown-section-header> + <gl-dropdown-item v-for="env in environments.options" :key="env.id" + :is-check-item="true" + :is-checked="isCurrentEnvironment(env.name)" @click="showEnvironment(env.name)" > - <div class="d-flex"> - <gl-icon - :class="{ invisible: !isCurrentEnvironment(env.name) }" - name="status_success_borderless" - /> - <div class="gl-flex-grow-1">{{ env.name }}</div> - </div> - </gl-deprecated-dropdown-item> - <gl-deprecated-dropdown-divider /> - <gl-deprecated-dropdown-header class="gl-text-center"> + {{ env.name }} + </gl-dropdown-item> + <gl-dropdown-divider /> + <gl-dropdown-section-header> {{ s__('Environments|Managed apps') }} - </gl-deprecated-dropdown-header> - <gl-deprecated-dropdown-item + </gl-dropdown-section-header> + <gl-dropdown-item v-for="app in managedApps.options" :key="app.id" + :is-check-item="true" + :is-checked="isCurrentManagedApp(app.name)" @click="showManagedApp(app.name)" > - <div class="gl-display-flex"> - <gl-icon - :class="{ invisible: !isCurrentManagedApp(app.name) }" - name="status_success_borderless" - /> - <div class="gl-flex-grow-1">{{ app.name }}</div> - </div> - </gl-deprecated-dropdown-item> - </gl-deprecated-dropdown> + {{ app.name }} + </gl-dropdown-item> + </gl-dropdown> </div> <log-advanced-filters diff --git a/app/assets/javascripts/logs/components/log_simple_filters.vue b/app/assets/javascripts/logs/components/log_simple_filters.vue index 2e1270b5428..ba30d4628c9 100644 --- a/app/assets/javascripts/logs/components/log_simple_filters.vue +++ b/app/assets/javascripts/logs/components/log_simple_filters.vue @@ -1,19 +1,13 @@ <script> import { mapActions, mapState } from 'vuex'; -import { - GlIcon, - GlDeprecatedDropdown, - GlDeprecatedDropdownHeader, - GlDeprecatedDropdownItem, -} from '@gitlab/ui'; +import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem } from '@gitlab/ui'; import { s__ } from '~/locale'; export default { components: { - GlIcon, - GlDeprecatedDropdown, - GlDeprecatedDropdownHeader, - GlDeprecatedDropdownItem, + GlDropdown, + GlDropdownSectionHeader, + GlDropdownItem, }, props: { disabled: { @@ -44,35 +38,31 @@ export default { </script> <template> <div> - <gl-deprecated-dropdown + <gl-dropdown ref="podsDropdown" :text="podDropdownText" :disabled="disabled" - class="mb-2 gl-h-32 pr-2 d-flex d-md-block flex-grow-0 qa-pods-dropdown" + class="gl-mr-3 gl-mb-3 gl-display-flex gl-display-md-block qa-pods-dropdown" > - <gl-deprecated-dropdown-header class="text-center"> + <gl-dropdown-section-header> {{ s__('Environments|Select pod') }} - </gl-deprecated-dropdown-header> + </gl-dropdown-section-header> - <gl-deprecated-dropdown-item v-if="!pods.options.length" disabled> + <gl-dropdown-item v-if="!pods.options.length" disabled> <span ref="noPodsMsg" class="text-muted"> {{ s__('Environments|No pods to display') }} </span> - </gl-deprecated-dropdown-item> - <gl-deprecated-dropdown-item + </gl-dropdown-item> + <gl-dropdown-item v-for="podName in pods.options" :key="podName" + :is-check-item="true" + :is-checked="isCurrentPod(podName)" class="text-nowrap" @click="showPodLogs(podName)" > - <div class="d-flex"> - <gl-icon - :class="{ invisible: !isCurrentPod(podName) }" - name="status_success_borderless" - /> - <div class="flex-grow-1">{{ podName }}</div> - </div> - </gl-deprecated-dropdown-item> - </gl-deprecated-dropdown> + {{ podName }} + </gl-dropdown-item> + </gl-dropdown> </div> </template> diff --git a/app/assets/javascripts/milestones/stores/actions.js b/app/assets/javascripts/milestones/stores/actions.js new file mode 100644 index 00000000000..3859771aeba --- /dev/null +++ b/app/assets/javascripts/milestones/stores/actions.js @@ -0,0 +1,58 @@ +import Api from '~/api'; +import * as types from './mutation_types'; + +export const setProjectId = ({ commit }, projectId) => commit(types.SET_PROJECT_ID, projectId); + +export const setSelectedMilestones = ({ commit }, selectedMilestones) => + commit(types.SET_SELECTED_MILESTONES, selectedMilestones); + +export const toggleMilestones = ({ commit, state }, selectedMilestone) => { + const removeMilestone = state.selectedMilestones.includes(selectedMilestone); + + if (removeMilestone) { + commit(types.REMOVE_SELECTED_MILESTONE, selectedMilestone); + } else { + commit(types.ADD_SELECTED_MILESTONE, selectedMilestone); + } +}; + +export const search = ({ dispatch, commit }, query) => { + commit(types.SET_QUERY, query); + + dispatch('searchMilestones'); +}; + +export const fetchMilestones = ({ commit, state }) => { + commit(types.REQUEST_START); + + Api.projectMilestones(state.projectId) + .then(response => { + commit(types.RECEIVE_PROJECT_MILESTONES_SUCCESS, response); + }) + .catch(error => { + commit(types.RECEIVE_PROJECT_MILESTONES_ERROR, error); + }) + .finally(() => { + commit(types.REQUEST_FINISH); + }); +}; + +export const searchMilestones = ({ commit, state }) => { + commit(types.REQUEST_START); + + const options = { + search: state.query, + scope: 'milestones', + }; + + Api.projectSearch(state.projectId, options) + .then(response => { + commit(types.RECEIVE_PROJECT_MILESTONES_SUCCESS, response); + }) + .catch(error => { + commit(types.RECEIVE_PROJECT_MILESTONES_ERROR, error); + }) + .finally(() => { + commit(types.REQUEST_FINISH); + }); +}; diff --git a/app/assets/javascripts/milestones/stores/getters.js b/app/assets/javascripts/milestones/stores/getters.js new file mode 100644 index 00000000000..d8a283403ec --- /dev/null +++ b/app/assets/javascripts/milestones/stores/getters.js @@ -0,0 +1,2 @@ +/** Returns `true` if there is at least one in-progress request */ +export const isLoading = ({ requestCount }) => requestCount > 0; diff --git a/app/assets/javascripts/milestones/stores/index.js b/app/assets/javascripts/milestones/stores/index.js new file mode 100644 index 00000000000..2bebffc19ab --- /dev/null +++ b/app/assets/javascripts/milestones/stores/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import * as getters from './getters'; +import mutations from './mutations'; +import createState from './state'; + +Vue.use(Vuex); + +export default () => + new Vuex.Store({ + actions, + getters, + mutations, + state: createState(), + }); diff --git a/app/assets/javascripts/milestones/stores/mutation_types.js b/app/assets/javascripts/milestones/stores/mutation_types.js new file mode 100644 index 00000000000..370d386dba2 --- /dev/null +++ b/app/assets/javascripts/milestones/stores/mutation_types.js @@ -0,0 +1,13 @@ +export const SET_PROJECT_ID = 'SET_PROJECT_ID'; + +export const SET_SELECTED_MILESTONES = 'SET_SELECTED_MILESTONES'; +export const ADD_SELECTED_MILESTONE = 'ADD_SELECTED_MILESTONE'; +export const REMOVE_SELECTED_MILESTONE = 'REMOVE_SELECTED_MILESTONE'; + +export const SET_QUERY = 'SET_QUERY'; + +export const REQUEST_START = 'REQUEST_START'; +export const REQUEST_FINISH = 'REQUEST_FINISH'; + +export const RECEIVE_PROJECT_MILESTONES_SUCCESS = 'RECEIVE_PROJECT_MILESTONES_SUCCESS'; +export const RECEIVE_PROJECT_MILESTONES_ERROR = 'RECEIVE_PROJECT_MILESTONES_ERROR'; diff --git a/app/assets/javascripts/milestones/stores/mutations.js b/app/assets/javascripts/milestones/stores/mutations.js new file mode 100644 index 00000000000..7c75d09766c --- /dev/null +++ b/app/assets/javascripts/milestones/stores/mutations.js @@ -0,0 +1,44 @@ +import Vue from 'vue'; +import * as types from './mutation_types'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + +export default { + [types.SET_PROJECT_ID](state, projectId) { + state.projectId = projectId; + }, + [types.SET_SELECTED_MILESTONES](state, selectedMilestones) { + Vue.set(state, 'selectedMilestones', selectedMilestones); + }, + [types.ADD_SELECTED_MILESTONE](state, selectedMilestone) { + state.selectedMilestones.push(selectedMilestone); + }, + [types.REMOVE_SELECTED_MILESTONE](state, selectedMilestone) { + const filteredMilestones = state.selectedMilestones.filter( + milestone => milestone !== selectedMilestone, + ); + Vue.set(state, 'selectedMilestones', filteredMilestones); + }, + [types.SET_QUERY](state, query) { + state.query = query; + }, + [types.REQUEST_START](state) { + state.requestCount += 1; + }, + [types.REQUEST_FINISH](state) { + state.requestCount -= 1; + }, + [types.RECEIVE_PROJECT_MILESTONES_SUCCESS](state, response) { + state.matches.projectMilestones = { + list: convertObjectPropsToCamelCase(response.data).map(({ title }) => ({ title })), + totalCount: parseInt(response.headers['x-total'], 10), + error: null, + }; + }, + [types.RECEIVE_PROJECT_MILESTONES_ERROR](state, error) { + state.matches.projectMilestones = { + list: [], + totalCount: 0, + error, + }; + }, +}; diff --git a/app/assets/javascripts/milestones/stores/state.js b/app/assets/javascripts/milestones/stores/state.js new file mode 100644 index 00000000000..0944539f367 --- /dev/null +++ b/app/assets/javascripts/milestones/stores/state.js @@ -0,0 +1,14 @@ +export default () => ({ + projectId: null, + groupId: null, + query: '', + matches: { + projectMilestones: { + list: [], + totalCount: 0, + error: null, + }, + }, + selectedMilestones: [], + requestCount: 0, +}); diff --git a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue index 5d59880d497..a9079f91f50 100644 --- a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue +++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue @@ -1,11 +1,5 @@ <script> -import { - GlAlert, - GlDeprecatedDropdown, - GlDeprecatedDropdownItem, - GlIcon, - GlSprintf, -} from '@gitlab/ui'; +import { GlAlert, GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui'; import { GlAreaChart } from '@gitlab/ui/dist/charts'; import dateFormat from 'dateformat'; import { get } from 'lodash'; @@ -17,9 +11,8 @@ export default { components: { GlAlert, GlAreaChart, - GlDeprecatedDropdown, - GlDeprecatedDropdownItem, - GlIcon, + GlDropdown, + GlDropdownItem, GlSprintf, }, props: { @@ -140,25 +133,18 @@ export default { {{ __('It seems that there is currently no available data for code coverage') }} </span> </gl-alert> - <gl-deprecated-dropdown v-if="canShowData" :text="selectedDailyCoverageName"> - <gl-deprecated-dropdown-item + <gl-dropdown v-if="canShowData" :text="selectedDailyCoverageName"> + <gl-dropdown-item v-for="({ group_name }, index) in dailyCoverageData" :key="index" :value="group_name" + :is-check-item="true" + :is-checked="index === selectedCoverageIndex" @click="setSelectedCoverage(index)" > - <div class="gl-display-flex"> - <gl-icon - v-if="index === selectedCoverageIndex" - name="mobile-issue-close" - class="gl-absolute" - /> - <span class="gl-display-flex align-items-center ml-4"> - {{ group_name }} - </span> - </div> - </gl-deprecated-dropdown-item> - </gl-deprecated-dropdown> + {{ group_name }} + </gl-dropdown-item> + </gl-dropdown> </div> <gl-area-chart v-if="!isLoading" diff --git a/app/assets/javascripts/vue_shared/components/editor_lite.vue b/app/assets/javascripts/vue_shared/components/editor_lite.vue index bc3a9ee45f8..cfe3ce0a11c 100644 --- a/app/assets/javascripts/vue_shared/components/editor_lite.vue +++ b/app/assets/javascripts/vue_shared/components/editor_lite.vue @@ -58,7 +58,9 @@ export default { this.editor.updateModelLanguage(newVal); }, value(newVal) { - this.editor.setValue(newVal); + if (this.editor.getValue() !== newVal) { + this.editor.setValue(newVal); + } }, }, mounted() { diff --git a/app/models/container_expiration_policy.rb b/app/models/container_expiration_policy.rb index b1dd720d908..641d244b665 100644 --- a/app/models/container_expiration_policy.rb +++ b/app/models/container_expiration_policy.rb @@ -3,6 +3,7 @@ class ContainerExpirationPolicy < ApplicationRecord include Schedulable include UsageStatistics + include EachBatch belongs_to :project, inverse_of: :container_expiration_policy @@ -19,6 +20,16 @@ class ContainerExpirationPolicy < ApplicationRecord scope :active, -> { where(enabled: true) } scope :preloaded, -> { preload(project: [:route]) } + def self.executable + runnable_schedules.where( + 'EXISTS (?)', + ContainerRepository.select(1) + .where( + 'container_repositories.project_id = container_expiration_policies.project_id' + ) + ) + end + def self.keep_n_options { 1 => _('%{tags} tag per image name') % { tags: 1 }, diff --git a/app/services/merge_requests/cleanup_refs_service.rb b/app/services/merge_requests/cleanup_refs_service.rb index 0f03f5f09b4..d003124a112 100644 --- a/app/services/merge_requests/cleanup_refs_service.rb +++ b/app/services/merge_requests/cleanup_refs_service.rb @@ -17,7 +17,7 @@ module MergeRequests @repository = merge_request.project.repository @ref_path = merge_request.ref_path @merge_ref_path = merge_request.merge_ref_path - @ref_head_sha = @repository.commit(merge_request.ref_path).id + @ref_head_sha = @repository.commit(merge_request.ref_path)&.id @merge_ref_sha = merge_request.merge_ref_head&.id end diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index c5d7b148e69..f6dc808aa55 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -92,7 +92,7 @@ %li.nav-item %div - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in') - = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-sign-in' + = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in' %button.navbar-toggler.d-block.d-sm-none{ type: 'button' } %span.sr-only= _('Toggle navigation') diff --git a/app/workers/container_expiration_policy_worker.rb b/app/workers/container_expiration_policy_worker.rb index 96590e165ae..61ba27f00d2 100644 --- a/app/workers/container_expiration_policy_worker.rb +++ b/app/workers/container_expiration_policy_worker.rb @@ -7,13 +7,15 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo feature_category :container_registry def perform - ContainerExpirationPolicy.runnable_schedules.preloaded.find_each do |container_expiration_policy| - with_context(project: container_expiration_policy.project, - user: container_expiration_policy.project.owner) do |project:, user:| - ContainerExpirationPolicyService.new(project, user) - .execute(container_expiration_policy) - rescue ContainerExpirationPolicyService::InvalidPolicyError => e - Gitlab::ErrorTracking.log_exception(e, container_expiration_policy_id: container_expiration_policy.id) + ContainerExpirationPolicy.executable.preloaded.each_batch do |relation| + relation.each do |container_expiration_policy| + with_context(project: container_expiration_policy.project, + user: container_expiration_policy.project.owner) do |project:, user:| + ContainerExpirationPolicyService.new(project, user) + .execute(container_expiration_policy) + rescue ContainerExpirationPolicyService::InvalidPolicyError => e + Gitlab::ErrorTracking.log_exception(e, container_expiration_policy_id: container_expiration_policy.id) + end end end end |