Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-18 12:09:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-18 12:09:31 +0300
commit6763d2787670bc03a36a8eb601703e88fc70dece (patch)
treeedc653ffd3052e3f9898c4fa8a07621d51574767
parented9165c2abda1dca048a8d3cb8030d906c0bbb0c (diff)
Add latest changes from gitlab-org/gitlab@master
-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
-rw-r--r--changelogs/unreleased/196838-remove-routing-ff.yml5
-rw-r--r--changelogs/unreleased/208755.yml5
-rw-r--r--changelogs/unreleased/208897-migrate-bot-type-to-user-type.yml5
-rw-r--r--changelogs/unreleased/eb-code-coverage-graph-storage.yml5
-rw-r--r--config/pseudonymizer.yml1
-rw-r--r--db/migrate/20200204131831_create_daily_report_results.rb22
-rw-r--r--db/migrate/20200311074438_migrate_bot_type_to_user_type.rb13
-rw-r--r--db/migrate/20200311082301_add_user_state_index.rb21
-rw-r--r--db/schema.rb17
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/note.rb2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/db/schema_spec.rb1
-rw-r--r--spec/factories/ci/daily_report_results.rb13
-rw-r--r--spec/factories/users.rb2
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_actions_spec.js93
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_list_spec.js30
-rw-r--r--spec/frontend/error_tracking/components/list_mock.json9
-rw-r--r--spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/code_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/code_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/issue_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/plan_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/production_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/production_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/review_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/review_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/staging_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_event_fetcher_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_stage_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/migrations/migrate_bot_type_to_user_type_spec.rb20
-rw-r--r--spec/models/ci/daily_report_result_spec.rb62
-rw-r--r--spec/models/ci/pipeline_spec.rb23
-rw-r--r--spec/models/user_spec.rb4
-rw-r--r--spec/services/ci/daily_report_result_service_spec.rb140
-rw-r--r--spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/base_stage_shared_examples.rb (renamed from spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb)4
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/default_query_config_shared_examples.rb (renamed from spec/lib/gitlab/cycle_analytics/shared_event_spec.rb)0
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb (renamed from spec/support/shared_examples/lib/gitlab/cycle_analytics_event_shared_examples.rb)0
-rw-r--r--spec/workers/ci/daily_report_results_worker_spec.rb34
57 files changed, 712 insertions, 161 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
diff --git a/changelogs/unreleased/196838-remove-routing-ff.yml b/changelogs/unreleased/196838-remove-routing-ff.yml
new file mode 100644
index 00000000000..51529a12b6a
--- /dev/null
+++ b/changelogs/unreleased/196838-remove-routing-ff.yml
@@ -0,0 +1,5 @@
+---
+title: Improve Advanced global search performance by using routing
+merge_request: 27398
+author:
+type: performance
diff --git a/changelogs/unreleased/208755.yml b/changelogs/unreleased/208755.yml
new file mode 100644
index 00000000000..d8b02650e22
--- /dev/null
+++ b/changelogs/unreleased/208755.yml
@@ -0,0 +1,5 @@
+---
+title: Update icons in Sentry Error Tracking list for ignored/resolved errors
+merge_request: 27125
+author:
+type: other
diff --git a/changelogs/unreleased/208897-migrate-bot-type-to-user-type.yml b/changelogs/unreleased/208897-migrate-bot-type-to-user-type.yml
new file mode 100644
index 00000000000..b629827ffeb
--- /dev/null
+++ b/changelogs/unreleased/208897-migrate-bot-type-to-user-type.yml
@@ -0,0 +1,5 @@
+---
+title: Move bots functionality to user_type column
+merge_request: 26981
+author:
+type: performance
diff --git a/changelogs/unreleased/eb-code-coverage-graph-storage.yml b/changelogs/unreleased/eb-code-coverage-graph-storage.yml
new file mode 100644
index 00000000000..6ed6e2cae6b
--- /dev/null
+++ b/changelogs/unreleased/eb-code-coverage-graph-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Store daily code coverages into ci_daily_report_results table
+merge_request: 24695
+author:
+type: added
diff --git a/config/pseudonymizer.yml b/config/pseudonymizer.yml
index 1f4ed9a8421..7b5f8aad255 100644
--- a/config/pseudonymizer.yml
+++ b/config/pseudonymizer.yml
@@ -469,6 +469,7 @@ tables:
- ghost
- last_activity_on
- notified_of_own_activity
+ - user_type
- bot_type
- preferred_language
- theme_id
diff --git a/db/migrate/20200204131831_create_daily_report_results.rb b/db/migrate/20200204131831_create_daily_report_results.rb
new file mode 100644
index 00000000000..e18d4efb7fa
--- /dev/null
+++ b/db/migrate/20200204131831_create_daily_report_results.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class CreateDailyReportResults < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ create_table :ci_daily_report_results do |t|
+ t.date :date, null: false
+ t.bigint :project_id, null: false
+ t.bigint :last_pipeline_id, null: false
+ t.float :value, null: false
+ t.integer :param_type, limit: 8, null: false
+ t.string :ref_path, null: false # rubocop:disable Migration/AddLimitToStringColumns
+ t.string :title, null: false # rubocop:disable Migration/AddLimitToStringColumns
+
+ t.index :last_pipeline_id
+ t.index [:project_id, :ref_path, :param_type, :date, :title], name: 'index_daily_report_results_unique_columns', unique: true
+ t.foreign_key :projects, on_delete: :cascade
+ t.foreign_key :ci_pipelines, column: :last_pipeline_id, on_delete: :cascade
+ end
+ end
+end
diff --git a/db/migrate/20200311074438_migrate_bot_type_to_user_type.rb b/db/migrate/20200311074438_migrate_bot_type_to_user_type.rb
new file mode 100644
index 00000000000..b13842794d3
--- /dev/null
+++ b/db/migrate/20200311074438_migrate_bot_type_to_user_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class MigrateBotTypeToUserType < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ execute('UPDATE users SET user_type = bot_type WHERE bot_type IS NOT NULL AND user_type IS NULL')
+ end
+
+ def down
+ execute('UPDATE users SET user_type = NULL WHERE bot_type IS NOT NULL AND bot_type = user_type')
+ end
+end
diff --git a/db/migrate/20200311082301_add_user_state_index.rb b/db/migrate/20200311082301_add_user_state_index.rb
new file mode 100644
index 00000000000..953941c2a53
--- /dev/null
+++ b/db/migrate/20200311082301_add_user_state_index.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddUserStateIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:users, [:state, :user_type], where: 'ghost IS NOT TRUE', name: 'index_users_on_state_and_user_type_internal')
+ remove_concurrent_index_by_name(:users, 'index_users_on_state_and_internal_ee')
+ remove_concurrent_index_by_name(:users, 'index_users_on_state_and_internal')
+ end
+
+ def down
+ add_concurrent_index(:users, :state, where: 'ghost IS NOT TRUE AND bot_type IS NULL', name: 'index_users_on_state_and_internal_ee')
+ add_concurrent_index(:users, :state, where: 'ghost IS NOT TRUE', name: 'index_users_on_state_and_internal')
+ remove_concurrent_index_by_name(:users, 'index_users_on_state_and_internal_ee')
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 55c99cb1027..1e32f4e6bd4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -738,6 +738,18 @@ ActiveRecord::Schema.define(version: 2020_03_13_123934) do
t.index ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true
end
+ create_table "ci_daily_report_results", force: :cascade do |t|
+ t.date "date", null: false
+ t.bigint "project_id", null: false
+ t.bigint "last_pipeline_id", null: false
+ t.float "value", null: false
+ t.bigint "param_type", null: false
+ t.string "ref_path", null: false
+ t.string "title", null: false
+ t.index ["last_pipeline_id"], name: "index_ci_daily_report_results_on_last_pipeline_id"
+ t.index ["project_id", "ref_path", "param_type", "date", "title"], name: "index_daily_report_results_unique_columns", unique: true
+ end
+
create_table "ci_group_variables", id: :serial, force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -4419,9 +4431,8 @@ ActiveRecord::Schema.define(version: 2020_03_13_123934) do
t.index ["name"], name: "index_users_on_name_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["public_email"], name: "index_users_on_public_email", where: "((public_email)::text <> ''::text)"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
+ t.index ["state", "user_type"], name: "index_users_on_state_and_user_type_internal", where: "(ghost IS NOT TRUE)"
t.index ["state"], name: "index_users_on_state"
- t.index ["state"], name: "index_users_on_state_and_internal", where: "(ghost IS NOT TRUE)"
- t.index ["state"], name: "index_users_on_state_and_internal_ee", where: "((ghost IS NOT TRUE) AND (bot_type IS NULL))"
t.index ["static_object_token"], name: "index_users_on_static_object_token", unique: true
t.index ["unconfirmed_email"], name: "index_users_on_unconfirmed_email", where: "(unconfirmed_email IS NOT NULL)"
t.index ["user_type"], name: "index_users_on_user_type"
@@ -4765,6 +4776,8 @@ ActiveRecord::Schema.define(version: 2020_03_13_123934) do
add_foreign_key "ci_builds_metadata", "ci_builds", column: "build_id", on_delete: :cascade
add_foreign_key "ci_builds_metadata", "projects", on_delete: :cascade
add_foreign_key "ci_builds_runner_session", "ci_builds", column: "build_id", on_delete: :cascade
+ add_foreign_key "ci_daily_report_results", "ci_pipelines", column: "last_pipeline_id", on_delete: :cascade
+ add_foreign_key "ci_daily_report_results", "projects", on_delete: :cascade
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "ci_builds", column: "job_id", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "projects", on_delete: :cascade
diff --git a/lib/gitlab/background_migration/user_mentions/models/note.rb b/lib/gitlab/background_migration/user_mentions/models/note.rb
index 7a1a0223bc7..c32292ad704 100644
--- a/lib/gitlab/background_migration/user_mentions/models/note.rb
+++ b/lib/gitlab/background_migration/user_mentions/models/note.rb
@@ -24,7 +24,7 @@ module Gitlab
end
def for_project_noteable?
- !for_personal_snippet?
+ !for_personal_snippet? && !for_epic?
end
def skip_project_check?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 11f93ac0bce..16a07a52d62 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -21333,6 +21333,9 @@ msgstr ""
msgid "Undo"
msgstr ""
+msgid "Undo Ignore"
+msgstr ""
+
msgid "Undo ignore"
msgstr ""
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index c6c842ce1a1..08e4920b020 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -132,6 +132,7 @@ describe 'Database schema' do
'Ci::Build' => %w[failure_reason],
'Ci::BuildMetadata' => %w[timeout_source],
'Ci::BuildTraceChunk' => %w[data_store],
+ 'Ci::DailyReportResult' => %w[param_type],
'Ci::JobArtifact' => %w[file_type],
'Ci::Pipeline' => %w[source config_source failure_reason],
'Ci::Processable' => %w[failure_reason],
diff --git a/spec/factories/ci/daily_report_results.rb b/spec/factories/ci/daily_report_results.rb
new file mode 100644
index 00000000000..e2255e8a134
--- /dev/null
+++ b/spec/factories/ci/daily_report_results.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_daily_report_result, class: 'Ci::DailyReportResult' do
+ ref_path { Gitlab::Git::BRANCH_REF_PREFIX + 'master' }
+ date { Time.zone.now.to_date }
+ project
+ last_pipeline factory: :ci_pipeline
+ param_type { Ci::DailyReportResult.param_types[:coverage] }
+ title { 'rspec' }
+ value { 77.0 }
+ end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 34f6da682b6..0ce567e11fe 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -24,7 +24,7 @@ FactoryBot.define do
end
trait :bot do
- bot_type { User.bot_types[:alert_bot] }
+ user_type { :alert_bot }
end
trait :external do
diff --git a/spec/frontend/error_tracking/components/error_tracking_actions_spec.js b/spec/frontend/error_tracking/components/error_tracking_actions_spec.js
new file mode 100644
index 00000000000..b22805f5227
--- /dev/null
+++ b/spec/frontend/error_tracking/components/error_tracking_actions_spec.js
@@ -0,0 +1,93 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
+import ErrorTrackingActions from '~/error_tracking/components/error_tracking_actions.vue';
+
+describe('Error Tracking Actions', () => {
+ let wrapper;
+
+ function mountComponent(props) {
+ wrapper = shallowMount(ErrorTrackingActions, {
+ propsData: {
+ error: {
+ id: '1',
+ title: 'PG::ConnectionBad: FATAL',
+ type: 'error',
+ userCount: 0,
+ count: '52',
+ firstSeen: '2019-05-30T07:21:46Z',
+ lastSeen: '2019-11-06T03:21:39Z',
+ status: 'unresolved',
+ },
+ ...props,
+ },
+ stubs: { GlButton },
+ });
+ }
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ const findButtons = () => wrapper.findAll(GlButton);
+
+ describe('when error status is unresolved', () => {
+ it('renders the correct actions buttons to allow ignore and resolve', () => {
+ expect(findButtons().exists()).toBe(true);
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(
+ findButtons()
+ .at(0)
+ .attributes('title'),
+ ).toBe('Ignore');
+ expect(
+ findButtons()
+ .at(1)
+ .attributes('title'),
+ ).toBe('Resolve');
+ });
+ });
+ });
+
+ describe('when error status is ignored', () => {
+ beforeEach(() => {
+ mountComponent({ error: { status: 'ignored' } });
+ });
+
+ it('renders the correct action button to undo ignore', () => {
+ expect(findButtons().exists()).toBe(true);
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(
+ findButtons()
+ .at(0)
+ .attributes('title'),
+ ).toBe('Undo Ignore');
+ });
+ });
+ });
+
+ describe('when error status is resolved', () => {
+ beforeEach(() => {
+ mountComponent({ error: { status: 'resolved' } });
+ });
+
+ it('renders the correct action button to undo unresolve', () => {
+ expect(findButtons().exists()).toBe(true);
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(
+ findButtons()
+ .at(1)
+ .attributes('title'),
+ ).toBe('Unresolve');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
index cd6dd5c7519..3bea1d343be 100644
--- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js
+++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import { GlEmptyState, GlLoadingIcon, GlFormInput, GlPagination, GlDropdown } from '@gitlab/ui';
import stubChildren from 'helpers/stub_children';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
+import ErrorTrackingActions from '~/error_tracking/components/error_tracking_actions.vue';
import errorsList from './list_mock.json';
const localVue = createLocalVue();
@@ -30,6 +31,7 @@ describe('ErrorTrackingList', () => {
.find(GlDropdown);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPagination = () => wrapper.find(GlPagination);
+ const findErrorActions = () => wrapper.find(ErrorTrackingActions);
function mountComponent({
errorTrackingEnabled = true,
@@ -151,15 +153,9 @@ describe('ErrorTrackingList', () => {
});
});
- it('each error in the list should have an ignore button', () => {
+ it('each error in the list should have an action button set', () => {
findErrorListRows().wrappers.forEach(row => {
- expect(row.contains('glicon-stub[name="eye-slash"]')).toBe(true);
- });
- });
-
- it('each error in the list should have a resolve button', () => {
- findErrorListRows().wrappers.forEach(row => {
- expect(row.contains('glicon-stub[name="check-circle"]')).toBe(true);
+ expect(row.contains(ErrorTrackingActions)).toBe(true);
});
});
@@ -237,8 +233,6 @@ describe('ErrorTrackingList', () => {
});
describe('When the ignore button on an error is clicked', () => {
- const ignoreErrorButton = () => wrapper.find({ ref: 'ignoreError' });
-
beforeEach(() => {
store.state.list.loading = false;
store.state.list.errors = errorsList;
@@ -253,7 +247,10 @@ describe('ErrorTrackingList', () => {
});
it('sends the "ignored" status and error ID', () => {
- ignoreErrorButton().trigger('click');
+ findErrorActions().vm.$emit('update-issue-status', {
+ errorId: errorsList[0].id,
+ status: 'ignored',
+ });
expect(actions.updateStatus).toHaveBeenCalledWith(
expect.anything(),
{
@@ -265,7 +262,7 @@ describe('ErrorTrackingList', () => {
});
it('calls an action to remove the item from the list', () => {
- ignoreErrorButton().trigger('click');
+ findErrorActions().vm.$emit('update-issue-status', { errorId: '1', status: undefined });
expect(actions.removeIgnoredResolvedErrors).toHaveBeenCalledWith(
expect.anything(),
'1',
@@ -275,8 +272,6 @@ describe('ErrorTrackingList', () => {
});
describe('When the resolve button on an error is clicked', () => {
- const resolveErrorButton = () => wrapper.find({ ref: 'resolveError' });
-
beforeEach(() => {
store.state.list.loading = false;
store.state.list.errors = errorsList;
@@ -291,7 +286,10 @@ describe('ErrorTrackingList', () => {
});
it('sends "resolved" status and error ID', () => {
- resolveErrorButton().trigger('click');
+ findErrorActions().vm.$emit('update-issue-status', {
+ errorId: errorsList[0].id,
+ status: 'resolved',
+ });
expect(actions.updateStatus).toHaveBeenCalledWith(
expect.anything(),
{
@@ -303,7 +301,7 @@ describe('ErrorTrackingList', () => {
});
it('calls an action to remove the item from the list', () => {
- resolveErrorButton().trigger('click');
+ findErrorActions().vm.$emit('update-issue-status', { errorId: '1', status: undefined });
expect(actions.removeIgnoredResolvedErrors).toHaveBeenCalledWith(
expect.anything(),
'1',
diff --git a/spec/frontend/error_tracking/components/list_mock.json b/spec/frontend/error_tracking/components/list_mock.json
index a6e94c1a026..54ae0a4c7cf 100644
--- a/spec/frontend/error_tracking/components/list_mock.json
+++ b/spec/frontend/error_tracking/components/list_mock.json
@@ -6,7 +6,8 @@
"userCount": 0,
"count": "52",
"firstSeen": "2019-05-30T07:21:46Z",
- "lastSeen": "2019-11-06T03:21:39Z"
+ "lastSeen": "2019-11-06T03:21:39Z",
+ "status": "unresolved"
},
{
"id": "2",
@@ -15,7 +16,8 @@
"userCount": 0,
"count": "12",
"firstSeen": "2019-10-19T03:53:56Z",
- "lastSeen": "2019-11-05T03:51:54Z"
+ "lastSeen": "2019-11-05T03:51:54Z",
+ "status": "unresolved"
},
{
"id": "3",
@@ -24,6 +26,7 @@
"userCount": 0,
"count": "275",
"firstSeen": "2019-02-12T07:22:36Z",
- "lastSeen": "2019-10-22T03:20:48Z"
+ "lastSeen": "2019-10-22T03:20:48Z",
+ "status": "unresolved"
}
] \ No newline at end of file
diff --git a/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb b/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb
index a273dcf9e5c..ff8b9dd1005 100644
--- a/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb
+++ b/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb
@@ -12,6 +12,7 @@ describe Gitlab::BackgroundMigration::UserMentions::CreateResourceUserMention, s
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:notes) { table(:notes) }
+ let(:routes) { table(:routes) }
let(:author) { users.create!(email: 'author@example.com', notification_email: 'author@example.com', name: 'author', username: 'author', projects_limit: 10, state: 'active') }
let(:member) { users.create!(email: 'member@example.com', notification_email: 'member@example.com', name: 'member', username: 'member', projects_limit: 10, state: 'active') }
@@ -32,13 +33,14 @@ describe Gitlab::BackgroundMigration::UserMentions::CreateResourceUserMention, s
before do
# build personal namespaces and routes for users
- mentioned_users.each { |u| u.becomes(User).save! }
+ mentioned_users.each do |u|
+ namespace = namespaces.create!(path: u.username, name: u.name, runners_token: "my-token-u#{u.id}", owner_id: u.id, type: nil)
+ routes.create!(path: namespace.path, source_type: 'Namespace', source_id: namespace.id)
+ end
# build namespaces and routes for groups
mentioned_groups.each do |gr|
- gr.name += '-org'
- gr.path += '-org'
- gr.becomes(Namespace).save!
+ routes.create!(path: gr.path, source_type: 'Namespace', source_id: gr.id)
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/code_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/code_event_fetcher_spec.rb
index b521ad0c6ea..326a41a3af7 100644
--- a/spec/lib/gitlab/cycle_analytics/code_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/code_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::CodeEventFetcher do
let(:stage_name) { :code }
diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
index aa12bc21d22..9a4193b09f5 100644
--- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::CodeStage do
let(:stage_name) { :code }
diff --git a/spec/lib/gitlab/cycle_analytics/issue_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_event_fetcher_spec.rb
index afb7b6a13b0..a72e2952782 100644
--- a/spec/lib/gitlab/cycle_analytics/issue_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/issue_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::IssueEventFetcher do
let(:stage_name) { :issue }
diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
index 497db88d850..021d31bf160 100644
--- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::IssueStage do
let(:stage_name) { :issue }
diff --git a/spec/lib/gitlab/cycle_analytics/plan_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_event_fetcher_spec.rb
index 17786cd02c6..587f185b970 100644
--- a/spec/lib/gitlab/cycle_analytics/plan_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/plan_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::PlanEventFetcher do
let(:stage_name) { :plan }
diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
index 01a46f5ba65..e391fa6b999 100644
--- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::PlanStage do
let(:stage_name) { :plan }
diff --git a/spec/lib/gitlab/cycle_analytics/production_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_fetcher_spec.rb
index 3ecfad49acd..aeca72e8c91 100644
--- a/spec/lib/gitlab/cycle_analytics/production_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/production_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::ProductionEventFetcher do
let(:stage_name) { :production }
diff --git a/spec/lib/gitlab/cycle_analytics/production_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/production_stage_spec.rb
index eceea474988..aeeae291e2e 100644
--- a/spec/lib/gitlab/cycle_analytics/production_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/production_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::ProductionStage do
let(:stage_name) { 'Total' }
diff --git a/spec/lib/gitlab/cycle_analytics/review_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/review_event_fetcher_spec.rb
index 2c2169be58c..3eb62b45e6f 100644
--- a/spec/lib/gitlab/cycle_analytics/review_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/review_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::ReviewEventFetcher do
let(:stage_name) { :review }
diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
index 0f36a8c5c36..14100ee6f73 100644
--- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::ReviewStage do
let(:stage_name) { :review }
diff --git a/spec/lib/gitlab/cycle_analytics/staging_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_event_fetcher_spec.rb
index 016d2e8da5b..525f1608a70 100644
--- a/spec/lib/gitlab/cycle_analytics/staging_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/staging_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::StagingEventFetcher do
let(:stage_name) { :staging }
diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
index 306b08a60e1..930892edd31 100644
--- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::StagingStage do
let(:stage_name) { :staging }
diff --git a/spec/lib/gitlab/cycle_analytics/test_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/test_event_fetcher_spec.rb
index be7c0e9dd59..d550f083600 100644
--- a/spec/lib/gitlab/cycle_analytics/test_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/test_event_fetcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_event_spec'
describe Gitlab::CycleAnalytics::TestEventFetcher do
let(:stage_name) { :test }
diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
index e347f36dfce..56e90520e72 100644
--- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::TestStage do
let(:stage_name) { :test }
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 37b3e4a4a22..eb28e730499 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -208,6 +208,7 @@ ci_pipelines:
- vulnerability_findings
- pipeline_config
- security_scans
+- daily_report_results
pipeline_variables:
- pipeline
stages:
@@ -470,6 +471,7 @@ project:
- status_page_setting
- requirements
- export_jobs
+- daily_report_results
award_emoji:
- awardable
- user
diff --git a/spec/migrations/migrate_bot_type_to_user_type_spec.rb b/spec/migrations/migrate_bot_type_to_user_type_spec.rb
new file mode 100644
index 00000000000..9686aae0cd3
--- /dev/null
+++ b/spec/migrations/migrate_bot_type_to_user_type_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require Rails.root.join('db', 'migrate', '20200311074438_migrate_bot_type_to_user_type.rb')
+
+describe MigrateBotTypeToUserType, :migration do
+ let(:users) { table(:users) }
+
+ it 'updates bots & ignores humans' do
+ users.create!(email: 'human', bot_type: nil, projects_limit: 0)
+ users.create!(email: 'support_bot', bot_type: 1, projects_limit: 0)
+ users.create!(email: 'alert_bot', bot_type: 2, projects_limit: 0)
+ users.create!(email: 'visual_review_bot', bot_type: 3, projects_limit: 0)
+
+ migrate!
+
+ expect(users.where('user_type IS NOT NULL').map(&:user_type)).to match_array([1, 2, 3])
+ end
+end
diff --git a/spec/models/ci/daily_report_result_spec.rb b/spec/models/ci/daily_report_result_spec.rb
new file mode 100644
index 00000000000..61aa58c6692
--- /dev/null
+++ b/spec/models/ci/daily_report_result_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::DailyReportResult do
+ describe '.upsert_reports' do
+ let!(:rspec_coverage) do
+ create(
+ :ci_daily_report_result,
+ title: 'rspec',
+ date: '2020-03-09',
+ value: 71.2
+ )
+ end
+ let!(:new_pipeline) { create(:ci_pipeline) }
+
+ it 'creates or updates matching report results' do
+ described_class.upsert_reports([
+ {
+ project_id: rspec_coverage.project_id,
+ ref_path: rspec_coverage.ref_path,
+ param_type: described_class.param_types[rspec_coverage.param_type],
+ last_pipeline_id: new_pipeline.id,
+ date: rspec_coverage.date,
+ title: 'rspec',
+ value: 81.0
+ },
+ {
+ project_id: rspec_coverage.project_id,
+ ref_path: rspec_coverage.ref_path,
+ param_type: described_class.param_types[rspec_coverage.param_type],
+ last_pipeline_id: new_pipeline.id,
+ date: rspec_coverage.date,
+ title: 'karma',
+ value: 87.0
+ }
+ ])
+
+ rspec_coverage.reload
+
+ expect(rspec_coverage).to have_attributes(
+ last_pipeline_id: new_pipeline.id,
+ value: 81.0
+ )
+
+ expect(described_class.find_by_title('karma')).to have_attributes(
+ project_id: rspec_coverage.project_id,
+ ref_path: rspec_coverage.ref_path,
+ param_type: rspec_coverage.param_type,
+ last_pipeline_id: new_pipeline.id,
+ date: rspec_coverage.date,
+ value: 87.0
+ )
+ end
+
+ context 'when given data is empty' do
+ it 'does nothing' do
+ expect { described_class.upsert_reports([]) }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index f18c77988c8..c3f2e3aebdd 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1120,7 +1120,7 @@ describe Ci::Pipeline, :mailer do
let(:from_status) { status }
it 'schedules pipeline success worker' do
- expect(PipelineSuccessWorker).to receive(:perform_async).with(pipeline.id)
+ expect(Ci::DailyReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id)
pipeline.succeed
end
@@ -3114,4 +3114,25 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe '#source_ref_path' do
+ subject { pipeline.source_ref_path }
+
+ context 'when pipeline is for a branch' do
+ it { is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.source_ref.to_s) }
+ end
+
+ context 'when pipeline is for a merge request' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, head_pipeline_of: merge_request) }
+
+ it { is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.source_ref.to_s) }
+ end
+
+ context 'when pipeline is for a tag' do
+ let(:pipeline) { create(:ci_pipeline, project: project, tag: true) }
+
+ it { is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.source_ref.to_s) }
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 849494e7cd4..c87272b2309 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -4244,12 +4244,12 @@ describe User, :do_not_mock_admin_mode do
let!(:non_internal) { [user] }
let!(:internal) { [ghost, alert_bot] }
- it 'returns non internal users' do
+ it 'returns internal users' do
expect(described_class.internal).to eq(internal)
expect(internal.all?(&:internal?)).to eq(true)
end
- it 'returns internal users' do
+ it 'returns non internal users' do
expect(described_class.non_internal).to eq(non_internal)
expect(non_internal.all?(&:internal?)).to eq(false)
end
diff --git a/spec/services/ci/daily_report_result_service_spec.rb b/spec/services/ci/daily_report_result_service_spec.rb
new file mode 100644
index 00000000000..793fc956acb
--- /dev/null
+++ b/spec/services/ci/daily_report_result_service_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::DailyReportResultService, '#execute' do
+ let!(:pipeline) { create(:ci_pipeline, created_at: '2020-02-06 00:01:10') }
+ let!(:rspec_job) { create(:ci_build, pipeline: pipeline, name: '3/3 rspec', coverage: 80) }
+ let!(:karma_job) { create(:ci_build, pipeline: pipeline, name: '2/2 karma', coverage: 90) }
+ let!(:extra_job) { create(:ci_build, pipeline: pipeline, name: 'extra', coverage: nil) }
+
+ it 'creates daily code coverage record for each job in the pipeline that has coverage value' do
+ described_class.new.execute(pipeline)
+
+ Ci::DailyReportResult.find_by(title: 'rspec').tap do |coverage|
+ expect(coverage).to have_attributes(
+ project_id: pipeline.project.id,
+ last_pipeline_id: pipeline.id,
+ ref_path: pipeline.source_ref_path,
+ param_type: 'coverage',
+ title: rspec_job.group_name,
+ value: rspec_job.coverage,
+ date: pipeline.created_at.to_date
+ )
+ end
+
+ Ci::DailyReportResult.find_by(title: 'karma').tap do |coverage|
+ expect(coverage).to have_attributes(
+ project_id: pipeline.project.id,
+ last_pipeline_id: pipeline.id,
+ ref_path: pipeline.source_ref_path,
+ param_type: 'coverage',
+ title: karma_job.group_name,
+ value: karma_job.coverage,
+ date: pipeline.created_at.to_date
+ )
+ end
+
+ expect(Ci::DailyReportResult.find_by(title: 'extra')).to be_nil
+ end
+
+ context 'when there is an existing daily code coverage for the matching date, project, ref_path, and group name' do
+ let!(:new_pipeline) do
+ create(
+ :ci_pipeline,
+ project: pipeline.project,
+ ref: pipeline.ref,
+ created_at: '2020-02-06 00:02:20'
+ )
+ end
+ let!(:new_rspec_job) { create(:ci_build, pipeline: new_pipeline, name: '4/4 rspec', coverage: 84) }
+ let!(:new_karma_job) { create(:ci_build, pipeline: new_pipeline, name: '3/3 karma', coverage: 92) }
+
+ before do
+ # Create the existing daily code coverage records
+ described_class.new.execute(pipeline)
+ end
+
+ it "updates the existing record's coverage value and last_pipeline_id" do
+ rspec_coverage = Ci::DailyReportResult.find_by(title: 'rspec')
+ karma_coverage = Ci::DailyReportResult.find_by(title: 'karma')
+
+ # Bump up the coverage values
+ described_class.new.execute(new_pipeline)
+
+ rspec_coverage.reload
+ karma_coverage.reload
+
+ expect(rspec_coverage).to have_attributes(
+ last_pipeline_id: new_pipeline.id,
+ value: new_rspec_job.coverage
+ )
+
+ expect(karma_coverage).to have_attributes(
+ last_pipeline_id: new_pipeline.id,
+ value: new_karma_job.coverage
+ )
+ end
+ end
+
+ context 'when the ID of the pipeline is older than the last_pipeline_id' do
+ let!(:new_pipeline) do
+ create(
+ :ci_pipeline,
+ project: pipeline.project,
+ ref: pipeline.ref,
+ created_at: '2020-02-06 00:02:20'
+ )
+ end
+ let!(:new_rspec_job) { create(:ci_build, pipeline: new_pipeline, name: '4/4 rspec', coverage: 84) }
+ let!(:new_karma_job) { create(:ci_build, pipeline: new_pipeline, name: '3/3 karma', coverage: 92) }
+
+ before do
+ # Create the existing daily code coverage records
+ # but in this case, for the newer pipeline first.
+ described_class.new.execute(new_pipeline)
+ end
+
+ it 'updates the existing daily code coverage records' do
+ rspec_coverage = Ci::DailyReportResult.find_by(title: 'rspec')
+ karma_coverage = Ci::DailyReportResult.find_by(title: 'karma')
+
+ # Run another one but for the older pipeline.
+ # This simulates the scenario wherein the success worker
+ # of an older pipeline, for some network hiccup, was delayed
+ # and only got executed right after the newer pipeline's success worker.
+ # Ideally, we don't want to bump the coverage value with an older one
+ # but given this can be a rare edge case and can be remedied by re-running
+ # the pipeline we'll just let it be for now. In return, we are able to use
+ # Rails 6 shiny new method, upsert_all, and simplify the code a lot.
+ described_class.new.execute(pipeline)
+
+ rspec_coverage.reload
+ karma_coverage.reload
+
+ expect(rspec_coverage).to have_attributes(
+ last_pipeline_id: pipeline.id,
+ value: rspec_job.coverage
+ )
+
+ expect(karma_coverage).to have_attributes(
+ last_pipeline_id: pipeline.id,
+ value: karma_job.coverage
+ )
+ end
+ end
+
+ context 'when pipeline has no builds with coverage' do
+ let!(:new_pipeline) do
+ create(
+ :ci_pipeline,
+ created_at: '2020-02-06 00:02:20'
+ )
+ end
+ let!(:some_job) { create(:ci_build, pipeline: new_pipeline, name: 'foo') }
+
+ it 'does nothing' do
+ expect { described_class.new.execute(new_pipeline) }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
index a3f0c84bd1f..a40c38106e2 100644
--- a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
@@ -22,14 +22,6 @@ shared_examples 'resource mentions migration' do |migration_class, resource_clas
end
shared_examples 'resource notes mentions migration' do |migration_class, resource_class|
- before do
- note1.becomes(Note).save!
- note2.becomes(Note).save!
- note3.becomes(Note).save!
- note4.becomes(Note).save!
- note5.becomes(Note).save(validate: false)
- end
-
it 'migrates mentions from note' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/base_stage_shared_examples.rb
index 9c16fb6f6dc..851ed9c65a3 100644
--- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/base_stage_shared_examples.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-shared_examples 'base stage' do
- ISSUES_MEDIAN = 30.minutes.to_i
+ISSUES_MEDIAN = 30.minutes.to_i
+shared_examples 'base stage' do
let(:stage) { described_class.new(options: { project: double }) }
before do
diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/default_query_config_shared_examples.rb
index c053af010b3..c053af010b3 100644
--- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/default_query_config_shared_examples.rb
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics_event_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
index a00359ce979..a00359ce979 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics_event_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
diff --git a/spec/workers/ci/daily_report_results_worker_spec.rb b/spec/workers/ci/daily_report_results_worker_spec.rb
new file mode 100644
index 00000000000..b6543b32b09
--- /dev/null
+++ b/spec/workers/ci/daily_report_results_worker_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::DailyReportResultsWorker do
+ describe '#perform' do
+ let!(:pipeline) { create(:ci_pipeline) }
+
+ subject { described_class.new.perform(pipeline_id) }
+
+ context 'when pipeline is found' do
+ let(:pipeline_id) { pipeline.id }
+
+ it 'executes service' do
+ expect_any_instance_of(Ci::DailyReportResultService)
+ .to receive(:execute).with(pipeline)
+
+ subject
+ end
+ end
+
+ context 'when pipeline is not found' do
+ let(:pipeline_id) { 123 }
+
+ it 'does not execute service' do
+ expect_any_instance_of(Ci::DailyReportResultService)
+ .not_to receive(:execute)
+
+ expect { subject }
+ .not_to raise_error
+ end
+ end
+ end
+end