diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-18 09:09:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-18 09:09:31 +0000 |
commit | 6763d2787670bc03a36a8eb601703e88fc70dece (patch) | |
tree | edc653ffd3052e3f9898c4fa8a07621d51574767 /app | |
parent | ed9165c2abda1dca048a8d3cb8030d906c0bbb0c (diff) | |
download | gitlab-ce-6763d2787670bc03a36a8eb601703e88fc70dece.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/error_tracking/components/error_tracking_actions.vue | 80 | ||||
-rw-r--r-- | app/assets/javascripts/error_tracking/components/error_tracking_list.vue | 66 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/error_list.scss | 13 | ||||
-rw-r--r-- | app/models/ci/build.rb | 2 | ||||
-rw-r--r-- | app/models/ci/daily_report_result.rb | 22 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 15 | ||||
-rw-r--r-- | app/models/project.rb | 2 | ||||
-rw-r--r-- | app/models/user.rb | 15 | ||||
-rw-r--r-- | app/models/user_bot_type_enums.rb | 11 | ||||
-rw-r--r-- | app/models/user_type_enums.rb | 6 | ||||
-rw-r--r-- | app/services/ci/daily_report_result_service.rb | 30 | ||||
-rw-r--r-- | app/workers/all_queues.yml | 14 | ||||
-rw-r--r-- | app/workers/ci/daily_report_results_worker.rb | 16 | ||||
-rw-r--r-- | app/workers/pipeline_success_worker.rb | 13 |
14 files changed, 198 insertions, 107 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue new file mode 100644 index 00000000000..49eb04e331b --- /dev/null +++ b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue @@ -0,0 +1,80 @@ +<script> +import { GlButton, GlIcon, GlButtonGroup, GlTooltipDirective } from '@gitlab/ui'; +import { __ } from '~/locale'; + +const IGNORED = 'ignored'; +const RESOLVED = 'resolved'; +const UNRESOLVED = 'unresolved'; + +const statusValidation = [IGNORED, RESOLVED, UNRESOLVED]; + +export default { + components: { + GlButton, + GlIcon, + GlButtonGroup, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + error: { + type: Object, + required: true, + validator: ({ status }) => statusValidation.includes(status), + }, + }, + computed: { + ignoreBtn() { + return this.error.status !== IGNORED + ? { status: IGNORED, icon: 'eye-slash', title: __('Ignore') } + : { status: UNRESOLVED, icon: 'eye', title: __('Undo Ignore') }; + }, + resolveBtn() { + return this.error.status !== RESOLVED + ? { status: RESOLVED, icon: 'check-circle', title: __('Resolve') } + : { status: UNRESOLVED, icon: 'canceled-circle', title: __('Unresolve') }; + }, + detailsLink() { + return `error_tracking/${this.error.id}/details`; + }, + }, +}; +</script> + +<template> + <div> + <gl-button-group class="flex-column flex-md-row ml-0 ml-md-n4"> + <gl-button + :key="ignoreBtn.status" + :ref="`${ignoreBtn.title.toLowerCase()}Error`" + v-gl-tooltip.hover + class="d-block mb-2 mb-md-0 w-100" + :title="ignoreBtn.title" + @click="$emit('update-issue-status', { errorId: error.id, status: ignoreBtn.status })" + > + <gl-icon class="d-none d-md-inline m-0" :name="ignoreBtn.icon" :size="12" /> + <span class="d-md-none">{{ ignoreBtn.title }}</span> + </gl-button> + <gl-button + :key="resolveBtn.status" + :ref="`${resolveBtn.title.toLowerCase()}Error`" + v-gl-tooltip.hover + class="d-block mb-2 mb-md-0 w-100" + :title="resolveBtn.title" + @click="$emit('update-issue-status', { errorId: error.id, status: resolveBtn.status })" + > + <gl-icon class="d-none d-md-inline m-0" :name="resolveBtn.icon" :size="12" /> + <span class="d-md-none">{{ resolveBtn.title }}</span> + </gl-button> + </gl-button-group> + <gl-button + :href="detailsLink" + category="secondary" + variant="info" + class="d-block d-md-none mb-2 mb-md-0" + > + {{ __('More details') }} + </gl-button> + </div> +</template> diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue index 0e160e8d568..ea0baaf4569 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue @@ -13,12 +13,12 @@ import { GlDropdownDivider, GlTooltipDirective, GlPagination, - GlButtonGroup, } from '@gitlab/ui'; import AccessorUtils from '~/lib/utils/accessor'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import { __ } from '~/locale'; import { isEmpty } from 'lodash'; +import ErrorTrackingActions from './error_tracking_actions.vue'; export const tableDataClass = 'table-col d-flex d-md-table-cell align-items-center'; @@ -26,10 +26,6 @@ export default { FIRST_PAGE: 1, PREV_PAGE: 1, NEXT_PAGE: 2, - statusButtons: [ - { status: 'ignored', icon: 'eye-slash', title: __('Ignore') }, - { status: 'resolved', icon: 'check-circle', title: __('Resolve') }, - ], fields: [ { key: 'error', @@ -58,12 +54,7 @@ export default { { key: 'status', label: '', - tdClass: `table-col d-none d-md-table-cell align-items-center pl-md-0`, - }, - { - key: 'details', - tdClass: 'table-col d-md-none d-flex align-items-center rounded-bottom bg-secondary', - thClass: 'invisible w-0', + tdClass: `${tableDataClass}`, }, ], statusFilters: { @@ -89,7 +80,7 @@ export default { GlFormInput, GlPagination, TimeAgo, - GlButtonGroup, + ErrorTrackingActions, }, directives: { GlTooltip: GlTooltipDirective, @@ -206,7 +197,7 @@ export default { this.filterValue = label; return this.filterByStatus(status); }, - updateIssueStatus(errorId, status) { + updateIssueStatus({ errorId, status }) { this.updateStatus({ endpoint: this.getIssueUpdatePath(errorId), status, @@ -220,8 +211,10 @@ export default { <template> <div class="error-list"> <div v-if="errorTrackingEnabled"> - <div class="row flex-column flex-sm-row align-items-sm-center row-top m-0 mt-sm-2 p-0 p-sm-3"> - <div class="search-box flex-fill mr-sm-2 my-3 m-sm-0 p-3 p-sm-0 bg-secondary"> + <div + class="row flex-column flex-md-row align-items-md-center m-0 mt-sm-2 p-3 p-sm-3 bg-secondary border" + > + <div class="search-box flex-fill mb-1 mb-md-0"> <div class="filtered-search-box mb-0"> <gl-dropdown :text="__('Recent searches')" @@ -273,7 +266,7 @@ export default { <gl-dropdown :text="$options.statusFilters[statusFilter]" - class="status-dropdown mr-2" + class="status-dropdown mx-md-1 mb-1 mb-md-0" menu-class="dropdown" :disabled="loading" > @@ -366,46 +359,7 @@ export default { </div> </template> <template #cell(status)="errors"> - <gl-button-group> - <gl-button - v-for="button in $options.statusButtons" - :key="button.status" - :ref="button.title.toLowerCase() + 'Error'" - v-gl-tooltip.hover - :title="button.title" - @click="updateIssueStatus(errors.item.id, button.status)" - > - <gl-icon :name="button.icon" :size="12" /> - </gl-button> - </gl-button-group> - </template> - <template #cell(details)="errors"> - <gl-button - category="primary" - variant="info" - block - class="mb-1 mt-2" - @click="updateIssueStatus(errors.item.id, 'resolved')" - > - {{ __('Resolve') }} - </gl-button> - <gl-button - category="secondary" - variant="default" - block - class="mb-2" - @click="updateIssueStatus(errors.item.id, 'ignored')" - > - {{ __('Ignore') }} - </gl-button> - <gl-button - :href="getDetailsLink(errors.item.id)" - category="secondary" - variant="info" - class="d-block mb-2" - > - {{ __('More details') }} - </gl-button> + <error-tracking-actions :error="errors.item" @update-issue-status="updateIssueStatus" /> </template> <template #empty> {{ __('No errors to display.') }} diff --git a/app/assets/stylesheets/pages/error_list.scss b/app/assets/stylesheets/pages/error_list.scss index 88fdcc47492..a61a85649b8 100644 --- a/app/assets/stylesheets/pages/error_list.scss +++ b/app/assets/stylesheets/pages/error_list.scss @@ -1,5 +1,3 @@ -$gray-border: 1px solid $border-color; - .error-list { .sort-control { .btn { @@ -13,19 +11,14 @@ $gray-border: 1px solid $border-color; } } - @include media-breakpoint-up(sm) { - .row-top { - border: $gray-border; - background-color: $gray-50; - } - } - - @include media-breakpoint-down(md) { + @include media-breakpoint-down(sm) { .error-list-table { .table-col { min-height: 68px; &:last-child { + background-color: $gray-normal; + &::before { content: none !important; } diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 74a1985ca50..517f2312a76 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -174,6 +174,8 @@ module Ci pipeline: Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE) end + scope :with_coverage, -> { where.not(coverage: nil) } + acts_as_taggable add_authentication_token_field :token, encrypted: :optional diff --git a/app/models/ci/daily_report_result.rb b/app/models/ci/daily_report_result.rb new file mode 100644 index 00000000000..3c1c5f11ed4 --- /dev/null +++ b/app/models/ci/daily_report_result.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Ci + class DailyReportResult < ApplicationRecord + extend Gitlab::Ci::Model + + belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id + belongs_to :project + + # TODO: Refactor this out when BuildReportResult is implemented. + # They both need to share the same enum values for param. + REPORT_PARAMS = { + coverage: 0 + }.freeze + + enum param_type: REPORT_PARAMS + + def self.upsert_reports(data) + upsert_all(data, unique_by: :index_daily_report_results_unique_columns) if data.any? + end + end +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 61b28a3e712..ef22b429df9 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -82,6 +82,8 @@ module Ci has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline + has_many :daily_report_results, class_name: 'Ci::DailyReportResult', foreign_key: :last_pipeline_id + accepts_nested_attributes_for :variables, reject_if: :persisted? delegate :id, to: :project, prefix: true @@ -189,7 +191,10 @@ module Ci end after_transition [:created, :waiting_for_resource, :preparing, :pending, :running] => :success do |pipeline| - pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) } + # We wait a little bit to ensure that all BuildFinishedWorkers finish first + # because this is where some metrics like code coverage is parsed and stored + # in CI build records which the daily build metrics worker relies on. + pipeline.run_after_commit { Ci::DailyReportResultsWorker.perform_in(10.minutes, pipeline.id) } end after_transition do |pipeline, transition| @@ -941,6 +946,14 @@ module Ci Ci::PipelineEnums.ci_config_sources.key?(config_source.to_sym) end + def source_ref_path + if branch? || merge_request? + Gitlab::Git::BRANCH_REF_PREFIX + source_ref.to_s + elsif tag? + Gitlab::Git::TAG_REF_PREFIX + source_ref.to_s + end + end + private def pipeline_data diff --git a/app/models/project.rb b/app/models/project.rb index 8578cd0e44a..4892c5310ec 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -314,6 +314,8 @@ class Project < ApplicationRecord has_many :import_failures, inverse_of: :project + has_many :daily_report_results, class_name: 'Ci::DailyReportResult' + accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :project_feature, update_only: true accepts_nested_attributes_for :import_data diff --git a/app/models/user.rb b/app/models/user.rb index 0c7dfac5776..6972a465c30 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,6 +21,7 @@ class User < ApplicationRecord include OptionallySearch include FromUnion include BatchDestroyDependentAssociations + include IgnorableColumns DEFAULT_NOTIFICATION_LEVEL = :participating @@ -59,9 +60,10 @@ class User < ApplicationRecord MINIMUM_INACTIVE_DAYS = 180 - enum bot_type: ::UserBotTypeEnums.bots enum user_type: ::UserTypeEnums.types + ignore_column :bot_type, remove_with: '12.11', remove_after: '2020-04-22' + # Override Devise::Models::Trackable#update_tracked_fields! # to limit database writes to at most once every hour # rubocop: disable CodeReuse/ServiceClass @@ -337,8 +339,9 @@ class User < ApplicationRecord scope :with_emails, -> { preload(:emails) } scope :with_dashboard, -> (dashboard) { where(dashboard: dashboard) } scope :with_public_profile, -> { where(private_profile: false) } - scope :bots, -> { where.not(bot_type: nil) } - scope :humans, -> { where(user_type: nil, bot_type: nil) } + scope :bots, -> { where(user_type: UserTypeEnums.bots.values) } + scope :not_bots, -> { humans.or(where.not(user_type: UserTypeEnums.bots.values)) } + scope :humans, -> { where(user_type: nil) } scope :with_expiring_and_not_notified_personal_access_tokens, ->(at) do where('EXISTS (?)', @@ -618,7 +621,7 @@ class User < ApplicationRecord def alert_bot email_pattern = "alert%s@#{Settings.gitlab.host}" - unique_internal(where(bot_type: :alert_bot), 'alert-bot', email_pattern) do |u| + unique_internal(where(user_type: :alert_bot), 'alert-bot', email_pattern) do |u| u.bio = 'The GitLab alert bot' u.name = 'GitLab Alert Bot' end @@ -640,7 +643,7 @@ class User < ApplicationRecord end def bot? - bot_type.present? + UserTypeEnums.bots.has_key?(user_type) end def internal? @@ -652,7 +655,7 @@ class User < ApplicationRecord end def self.non_internal - without_ghosts.humans + without_ghosts.not_bots end # diff --git a/app/models/user_bot_type_enums.rb b/app/models/user_bot_type_enums.rb deleted file mode 100644 index 1a9c02a3998..00000000000 --- a/app/models/user_bot_type_enums.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module UserBotTypeEnums - def self.bots - { - alert_bot: 2 - } - end -end - -UserBotTypeEnums.prepend_if_ee('EE::UserBotTypeEnums') diff --git a/app/models/user_type_enums.rb b/app/models/user_type_enums.rb index 4e9dd70aee8..2d0d2f3a4ce 100644 --- a/app/models/user_type_enums.rb +++ b/app/models/user_type_enums.rb @@ -2,13 +2,13 @@ module UserTypeEnums def self.types - bots + bots.merge(human: nil) end def self.bots { - AlertBot: 2 - } + alert_bot: 2 + }.with_indifferent_access end end diff --git a/app/services/ci/daily_report_result_service.rb b/app/services/ci/daily_report_result_service.rb new file mode 100644 index 00000000000..79b5015c076 --- /dev/null +++ b/app/services/ci/daily_report_result_service.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Ci + class DailyReportResultService + def execute(pipeline) + return unless Feature.enabled?(:ci_daily_code_coverage, pipeline.project, default_enabled: true) + + DailyReportResult.upsert_reports(coverage_reports(pipeline)) + end + + private + + def coverage_reports(pipeline) + base_attrs = { + project_id: pipeline.project_id, + ref_path: pipeline.source_ref_path, + param_type: DailyReportResult.param_types[:coverage], + date: pipeline.created_at.to_date, + last_pipeline_id: pipeline.id + } + + pipeline.builds.with_coverage.map do |build| + base_attrs.merge( + title: build.group_name, + value: build.coverage + ) + end + end + end +end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 28fab10d931..3df86e3314d 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -605,6 +605,13 @@ :resource_boundary: :unknown :weight: 1 :idempotent: +- :name: pipeline_background:ci_daily_report_results + :feature_category: :continuous_integration + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true - :name: pipeline_cache:expire_job_cache :feature_category: :continuous_integration :has_external_dependencies: @@ -745,13 +752,6 @@ :resource_boundary: :unknown :weight: 5 :idempotent: -- :name: pipeline_processing:pipeline_success - :feature_category: :continuous_integration - :has_external_dependencies: - :urgency: :high - :resource_boundary: :unknown - :weight: 5 - :idempotent: - :name: pipeline_processing:pipeline_update :feature_category: :continuous_integration :has_external_dependencies: diff --git a/app/workers/ci/daily_report_results_worker.rb b/app/workers/ci/daily_report_results_worker.rb new file mode 100644 index 00000000000..314fd44f86c --- /dev/null +++ b/app/workers/ci/daily_report_results_worker.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Ci + class DailyReportResultsWorker + include ApplicationWorker + include PipelineBackgroundQueue + + idempotent! + + def perform(pipeline_id) + Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline| + Ci::DailyReportResultService.new.execute(pipeline) + end + end + end +end diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb deleted file mode 100644 index d84612c52d1..00000000000 --- a/app/workers/pipeline_success_worker.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -class PipelineSuccessWorker # rubocop:disable Scalability/IdempotentWorker - include ApplicationWorker - include PipelineQueue - - queue_namespace :pipeline_processing - urgency :high - - def perform(pipeline_id) - # no-op - end -end |