summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-18 09:09:31 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-18 09:09:31 +0000
commit6763d2787670bc03a36a8eb601703e88fc70dece (patch)
treeedc653ffd3052e3f9898c4fa8a07621d51574767 /app
parented9165c2abda1dca048a8d3cb8030d906c0bbb0c (diff)
downloadgitlab-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.vue80
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue66
-rw-r--r--app/assets/stylesheets/pages/error_list.scss13
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/ci/daily_report_result.rb22
-rw-r--r--app/models/ci/pipeline.rb15
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/user.rb15
-rw-r--r--app/models/user_bot_type_enums.rb11
-rw-r--r--app/models/user_type_enums.rb6
-rw-r--r--app/services/ci/daily_report_result_service.rb30
-rw-r--r--app/workers/all_queues.yml14
-rw-r--r--app/workers/ci/daily_report_results_worker.rb16
-rw-r--r--app/workers/pipeline_success_worker.rb13
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