Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
132f8ac520
commit
5b6e9de025
28 changed files with 925 additions and 107 deletions
|
@ -1,4 +0,0 @@
|
||||||
fragment Count on InstanceStatisticsMeasurement {
|
|
||||||
count
|
|
||||||
recordedAt
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { __ } from '~/locale';
|
import { GlModal } from '@gitlab/ui';
|
||||||
|
import { __, s__ } from '~/locale';
|
||||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||||
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
|
|
||||||
import { visitUrl } from '~/lib/utils/url_utility';
|
import { visitUrl } from '~/lib/utils/url_utility';
|
||||||
import boardsStore from '~/boards/stores/boards_store';
|
import boardsStore from '~/boards/stores/boards_store';
|
||||||
|
|
||||||
|
@ -19,10 +19,28 @@ const boardDefaults = {
|
||||||
hide_closed_list: false,
|
hide_closed_list: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formType = {
|
||||||
|
new: 'new',
|
||||||
|
delete: 'delete',
|
||||||
|
edit: 'edit',
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
i18n: {
|
||||||
|
[formType.new]: { title: s__('Board|Create new board'), btnText: s__('Board|Create board') },
|
||||||
|
[formType.delete]: { title: s__('Board|Delete board'), btnText: __('Delete') },
|
||||||
|
[formType.edit]: { title: s__('Board|Edit board'), btnText: __('Save changes') },
|
||||||
|
scopeModalTitle: s__('Board|Board scope'),
|
||||||
|
cancelButtonText: __('Cancel'),
|
||||||
|
deleteErrorMessage: s__('Board|Failed to delete board. Please try again.'),
|
||||||
|
saveErrorMessage: __('Unable to save your changes. Please try again.'),
|
||||||
|
deleteConfirmationMessage: s__('Board|Are you sure you want to delete this board?'),
|
||||||
|
titleFieldLabel: __('Title'),
|
||||||
|
titleFieldPlaceholder: s__('Board|Enter board name'),
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
|
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
|
||||||
DeprecatedModal,
|
GlModal,
|
||||||
BoardConfigurationOptions,
|
BoardConfigurationOptions,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -74,25 +92,16 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isNewForm() {
|
isNewForm() {
|
||||||
return this.currentPage === 'new';
|
return this.currentPage === formType.new;
|
||||||
},
|
},
|
||||||
isDeleteForm() {
|
isDeleteForm() {
|
||||||
return this.currentPage === 'delete';
|
return this.currentPage === formType.delete;
|
||||||
},
|
},
|
||||||
isEditForm() {
|
isEditForm() {
|
||||||
return this.currentPage === 'edit';
|
return this.currentPage === formType.edit;
|
||||||
},
|
|
||||||
isVisible() {
|
|
||||||
return this.currentPage !== '';
|
|
||||||
},
|
},
|
||||||
buttonText() {
|
buttonText() {
|
||||||
if (this.isNewForm) {
|
return this.$options.i18n[this.currentPage].btnText;
|
||||||
return __('Create board');
|
|
||||||
}
|
|
||||||
if (this.isDeleteForm) {
|
|
||||||
return __('Delete');
|
|
||||||
}
|
|
||||||
return __('Save changes');
|
|
||||||
},
|
},
|
||||||
buttonKind() {
|
buttonKind() {
|
||||||
if (this.isNewForm) {
|
if (this.isNewForm) {
|
||||||
|
@ -104,16 +113,11 @@ export default {
|
||||||
return 'info';
|
return 'info';
|
||||||
},
|
},
|
||||||
title() {
|
title() {
|
||||||
if (this.isNewForm) {
|
|
||||||
return __('Create new board');
|
|
||||||
}
|
|
||||||
if (this.isDeleteForm) {
|
|
||||||
return __('Delete board');
|
|
||||||
}
|
|
||||||
if (this.readonly) {
|
if (this.readonly) {
|
||||||
return __('Board scope');
|
return this.$options.i18n.scopeModalTitle;
|
||||||
}
|
}
|
||||||
return __('Edit board');
|
|
||||||
|
return this.$options.i18n[this.currentPage].title;
|
||||||
},
|
},
|
||||||
readonly() {
|
readonly() {
|
||||||
return !this.canAdminBoard;
|
return !this.canAdminBoard;
|
||||||
|
@ -121,6 +125,24 @@ export default {
|
||||||
submitDisabled() {
|
submitDisabled() {
|
||||||
return this.isLoading || this.board.name.length === 0;
|
return this.isLoading || this.board.name.length === 0;
|
||||||
},
|
},
|
||||||
|
primaryProps() {
|
||||||
|
return {
|
||||||
|
text: this.buttonText,
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
variant: this.buttonKind,
|
||||||
|
disabled: this.submitDisabled,
|
||||||
|
loading: this.isLoading,
|
||||||
|
'data-qa-selector': 'save_changes_button',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
cancelProps() {
|
||||||
|
return {
|
||||||
|
text: this.$options.i18n.cancelButtonText,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.resetFormState();
|
this.resetFormState();
|
||||||
|
@ -136,10 +158,11 @@ export default {
|
||||||
boardsStore
|
boardsStore
|
||||||
.deleteBoard(this.currentBoard)
|
.deleteBoard(this.currentBoard)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.isLoading = false;
|
||||||
visitUrl(boardsStore.rootPath);
|
visitUrl(boardsStore.rootPath);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
Flash(__('Failed to delete board. Please try again.'));
|
Flash(this.$options.i18n.deleteErrorMessage);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -157,10 +180,11 @@ export default {
|
||||||
return resp.data ? resp.data : resp;
|
return resp.data ? resp.data : resp;
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
this.isLoading = false;
|
||||||
visitUrl(data.board_path);
|
visitUrl(data.board_path);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
Flash(__('Unable to save your changes. Please try again.'));
|
Flash(this.$options.i18n.saveErrorMessage);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -181,22 +205,26 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<deprecated-modal
|
<gl-modal
|
||||||
v-show="isVisible"
|
modal-id="board-config-modal"
|
||||||
|
modal-class="board-config-modal"
|
||||||
|
content-class="gl-absolute gl-top-7"
|
||||||
|
visible
|
||||||
:hide-footer="readonly"
|
:hide-footer="readonly"
|
||||||
:title="title"
|
:title="title"
|
||||||
:primary-button-label="buttonText"
|
:action-primary="primaryProps"
|
||||||
:kind="buttonKind"
|
:action-cancel="cancelProps"
|
||||||
:submit-disabled="submitDisabled"
|
@primary="submit"
|
||||||
modal-dialog-class="board-config-modal"
|
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@submit="submit"
|
@close="cancel"
|
||||||
|
@hide.prevent
|
||||||
>
|
>
|
||||||
<template #body>
|
<p v-if="isDeleteForm">{{ $options.i18n.deleteConfirmationMessage }}</p>
|
||||||
<p v-if="isDeleteForm">{{ __('Are you sure you want to delete this board?') }}</p>
|
|
||||||
<form v-else class="js-board-config-modal" @submit.prevent>
|
<form v-else class="js-board-config-modal" @submit.prevent>
|
||||||
<div v-if="!readonly" class="gl-mb-5">
|
<div v-if="!readonly" class="gl-mb-5">
|
||||||
<label class="label-bold gl-font-lg" for="board-new-name">{{ __('Title') }}</label>
|
<label class="gl-font-weight-bold gl-font-lg" for="board-new-name">
|
||||||
|
{{ $options.i18n.titleFieldLabel }}
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
id="board-new-name"
|
id="board-new-name"
|
||||||
ref="name"
|
ref="name"
|
||||||
|
@ -204,7 +232,7 @@ export default {
|
||||||
class="form-control"
|
class="form-control"
|
||||||
data-qa-selector="board_name_field"
|
data-qa-selector="board_name_field"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="__('Enter board name')"
|
:placeholder="$options.i18n.titleFieldPlaceholder"
|
||||||
@keyup.enter="submit"
|
@keyup.enter="submit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -228,6 +256,5 @@ export default {
|
||||||
:weights="weights"
|
:weights="weights"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</gl-modal>
|
||||||
</deprecated-modal>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
GlDropdownDivider,
|
GlDropdownDivider,
|
||||||
GlDropdownSectionHeader,
|
GlDropdownSectionHeader,
|
||||||
GlDropdownItem,
|
GlDropdownItem,
|
||||||
|
GlModalDirective,
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
|
|
||||||
import httpStatusCodes from '~/lib/utils/http_status';
|
import httpStatusCodes from '~/lib/utils/http_status';
|
||||||
|
@ -31,6 +32,9 @@ export default {
|
||||||
GlDropdownSectionHeader,
|
GlDropdownSectionHeader,
|
||||||
GlDropdownItem,
|
GlDropdownItem,
|
||||||
},
|
},
|
||||||
|
directives: {
|
||||||
|
GlModalDirective,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
currentBoard: {
|
currentBoard: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -313,6 +317,7 @@ export default {
|
||||||
|
|
||||||
<gl-dropdown-item
|
<gl-dropdown-item
|
||||||
v-if="multipleIssueBoardsAvailable"
|
v-if="multipleIssueBoardsAvailable"
|
||||||
|
v-gl-modal-directive="'board-config-modal'"
|
||||||
data-qa-selector="create_new_board_button"
|
data-qa-selector="create_new_board_button"
|
||||||
@click.prevent="showPage('new')"
|
@click.prevent="showPage('new')"
|
||||||
>
|
>
|
||||||
|
@ -321,6 +326,7 @@ export default {
|
||||||
|
|
||||||
<gl-dropdown-item
|
<gl-dropdown-item
|
||||||
v-if="showDelete"
|
v-if="showDelete"
|
||||||
|
v-gl-modal-directive="'board-config-modal'"
|
||||||
class="text-danger js-delete-board"
|
class="text-danger js-delete-board"
|
||||||
@click.prevent="showPage('delete')"
|
@click.prevent="showPage('delete')"
|
||||||
>
|
>
|
||||||
|
|
|
@ -91,6 +91,7 @@
|
||||||
|
|
||||||
body.modal-open {
|
body.modal-open {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-no-backdrop {
|
.modal-no-backdrop {
|
||||||
|
|
14
app/serializers/codequality_degradation_entity.rb
Normal file
14
app/serializers/codequality_degradation_entity.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CodequalityDegradationEntity < Grape::Entity
|
||||||
|
expose :description
|
||||||
|
expose :severity
|
||||||
|
|
||||||
|
expose :file_path do |degradation|
|
||||||
|
degradation.dig(:location, :path)
|
||||||
|
end
|
||||||
|
|
||||||
|
expose :line do |degradation|
|
||||||
|
degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line)
|
||||||
|
end
|
||||||
|
end
|
15
app/serializers/codequality_reports_comparer_entity.rb
Normal file
15
app/serializers/codequality_reports_comparer_entity.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CodequalityReportsComparerEntity < Grape::Entity
|
||||||
|
expose :status
|
||||||
|
|
||||||
|
expose :new_errors, using: CodequalityDegradationEntity
|
||||||
|
expose :resolved_errors, using: CodequalityDegradationEntity
|
||||||
|
expose :existing_errors, using: CodequalityDegradationEntity
|
||||||
|
|
||||||
|
expose :summary do
|
||||||
|
expose :total_count, as: :total
|
||||||
|
expose :resolved_count, as: :resolved
|
||||||
|
expose :errors_count, as: :errored
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CodequalityReportsComparerSerializer < BaseSerializer
|
||||||
|
entity CodequalityReportsComparerEntity
|
||||||
|
end
|
|
@ -36,6 +36,7 @@ module Projects
|
||||||
def log_response(response)
|
def log_response(response)
|
||||||
log_data = LOG_DATA_BASE.merge(
|
log_data = LOG_DATA_BASE.merge(
|
||||||
container_repository_id: @container_repository.id,
|
container_repository_id: @container_repository.id,
|
||||||
|
project_id: @container_repository.project_id,
|
||||||
message: 'deleted tags',
|
message: 'deleted tags',
|
||||||
deleted_tags_count: response[:deleted]&.size
|
deleted_tags_count: response[:deleted]&.size
|
||||||
).compact
|
).compact
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
.dropdown
|
.dropdown
|
||||||
%button.dropdown.dropdown-new.btn.gl-button.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') }
|
%button.dropdown.dropdown-new.btn.gl-button.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') }
|
||||||
= sprite_icon('play')
|
= sprite_icon('play')
|
||||||
= icon('caret-down')
|
= sprite_icon('chevron-down')
|
||||||
%ul.dropdown-menu.dropdown-menu-right
|
%ul.dropdown-menu.dropdown-menu-right
|
||||||
- actions.each do |action|
|
- actions.each do |action|
|
||||||
- next unless can?(current_user, :update_build, action)
|
- next unless can?(current_user, :update_build, action)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
Showing
|
Showing
|
||||||
%button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown", display: "static" } }<
|
%button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown", display: "static" } }<
|
||||||
= pluralize(diff_files.size, "changed file")
|
= pluralize(diff_files.size, "changed file")
|
||||||
= icon("caret-down", class: "gl-ml-2")
|
= sprite_icon("chevron-down", css_class: "gl-ml-2")
|
||||||
%span.diff-stats-additions-deletions-expanded#diff-stats
|
%span.diff-stats-additions-deletions-expanded#diff-stats
|
||||||
with
|
with
|
||||||
%strong.cgreen= pluralize(sum_added_lines, 'addition')
|
%strong.cgreen= pluralize(sum_added_lines, 'addition')
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
|
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
|
||||||
%span.js-clone-dropdown-label
|
%span.js-clone-dropdown-label
|
||||||
= default_clone_protocol.upcase
|
= default_clone_protocol.upcase
|
||||||
= icon('caret-down')
|
= sprite_icon('chevron-down')
|
||||||
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
|
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
|
||||||
%li
|
%li
|
||||||
= ssh_clone_button(container)
|
= ssh_clone_button(container)
|
||||||
|
|
5
changelogs/unreleased/291078-fix-typo-merge-locally.yml
Normal file
5
changelogs/unreleased/291078-fix-typo-merge-locally.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix typo on merge locally step
|
||||||
|
merge_request: 49330
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Convert fa-caret-down icons to chevron-down SVG
|
||||||
|
merge_request: 49332
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -257,6 +257,16 @@ For more information on tuning Geo, see [Tuning Geo](replication/tuning.md).
|
||||||
|
|
||||||
For an example of how to set up a location-aware Git remote URL with AWS Route53, see [Location-aware Git remote URL with AWS Route53](replication/location_aware_git_url.md).
|
For an example of how to set up a location-aware Git remote URL with AWS Route53, see [Location-aware Git remote URL with AWS Route53](replication/location_aware_git_url.md).
|
||||||
|
|
||||||
|
### Backfill
|
||||||
|
|
||||||
|
Once a **secondary** node is set up, it will start replicating missing data from
|
||||||
|
the **primary** node in a process known as **backfill**. You can monitor the
|
||||||
|
synchronization process on each Geo node from the **primary** node's **Geo Nodes**
|
||||||
|
dashboard in your browser.
|
||||||
|
|
||||||
|
Failures that happen during a backfill are scheduled to be retried at the end
|
||||||
|
of the backfill.
|
||||||
|
|
||||||
## Remove Geo node
|
## Remove Geo node
|
||||||
|
|
||||||
For more information on removing a Geo node, see [Removing **secondary** Geo nodes](replication/remove_geo_node.md).
|
For more information on removing a Geo node, see [Removing **secondary** Geo nodes](replication/remove_geo_node.md).
|
||||||
|
|
|
@ -425,6 +425,11 @@ GitLab you are running. GitLab versions 11.11.x or 12.0.x are affected by
|
||||||
|
|
||||||
To resolve the issue, upgrade to GitLab 12.1 or newer.
|
To resolve the issue, upgrade to GitLab 12.1 or newer.
|
||||||
|
|
||||||
|
### Failures during backfill
|
||||||
|
|
||||||
|
During a [backfill](../index.md#backfill), failures are scheduled to be retried at the end
|
||||||
|
of the backfill queue, therefore these failures only clear up **after** the backfill completes.
|
||||||
|
|
||||||
### Resetting Geo **secondary** node replication
|
### Resetting Geo **secondary** node replication
|
||||||
|
|
||||||
If you get a **secondary** node in a broken state and want to reset the replication state,
|
If you get a **secondary** node in a broken state and want to reset the replication state,
|
||||||
|
|
|
@ -261,6 +261,18 @@ The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time
|
||||||
(4 - 1) + (7 - 6) => 4
|
(4 - 1) + (7 - 6) => 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### How pipeline quota usage is calculated
|
||||||
|
|
||||||
|
Pipeline quota usage is calculated as the sum of the duration of each individual job. This is slightly different to how pipeline _duration_ is [calculated](#how-pipeline-duration-is-calculated). Pipeline quota usage doesn't consider any overlap of jobs running in parallel.
|
||||||
|
|
||||||
|
For example, a pipeline consists of the following jobs:
|
||||||
|
|
||||||
|
- Job A takes 3 minutes.
|
||||||
|
- Job B takes 3 minutes.
|
||||||
|
- Job C takes 2 minutes.
|
||||||
|
|
||||||
|
The pipeline quota usage is the sum of each job's duration. In this example, 8 runner minutes would be used, calculated as: 3 + 3 + 2.
|
||||||
|
|
||||||
### Pipeline security on protected branches
|
### Pipeline security on protected branches
|
||||||
|
|
||||||
A strict security model is enforced when pipelines are executed on
|
A strict security model is enforced when pipelines are executed on
|
||||||
|
|
|
@ -276,8 +276,13 @@ In its current state, the Rake task:
|
||||||
This uses some features from `graphql-docs` gem like its schema parser and helper methods.
|
This uses some features from `graphql-docs` gem like its schema parser and helper methods.
|
||||||
The docs generator code comes from our side giving us more flexibility, like using Haml templates and generating Markdown files.
|
The docs generator code comes from our side giving us more flexibility, like using Haml templates and generating Markdown files.
|
||||||
|
|
||||||
To edit the template used, please take a look at `lib/gitlab/graphql/docs/templates/default.md.haml`.
|
To edit the content, you may need to edit the following:
|
||||||
|
|
||||||
|
- The template. You can edit the template at `lib/gitlab/graphql/docs/templates/default.md.haml`.
|
||||||
The actual renderer is at `Gitlab::Graphql::Docs::Renderer`.
|
The actual renderer is at `Gitlab::Graphql::Docs::Renderer`.
|
||||||
|
- The applicable `description` field in the code, which
|
||||||
|
[Updates machine-readable schema files](#update-machine-readable-schema-files),
|
||||||
|
which is then used by the `rake` task described earlier.
|
||||||
|
|
||||||
`@parsed_schema` is an instance variable that the `graphql-docs` gem expects to have available.
|
`@parsed_schema` is an instance variable that the `graphql-docs` gem expects to have available.
|
||||||
`Gitlab::Graphql::Docs::Helper` defines the `object` method we currently use. This is also where you
|
`Gitlab::Graphql::Docs::Helper` defines the `object` method we currently use. This is also where you
|
||||||
|
|
57
lib/gitlab/ci/reports/codequality_reports_comparer.rb
Normal file
57
lib/gitlab/ci/reports/codequality_reports_comparer.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module Ci
|
||||||
|
module Reports
|
||||||
|
class CodequalityReportsComparer
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
|
STATUS_SUCCESS = 'success'
|
||||||
|
STATUS_FAILED = 'failed'
|
||||||
|
|
||||||
|
attr_reader :base_report, :head_report
|
||||||
|
|
||||||
|
def initialize(base_report, head_report)
|
||||||
|
@base_report = base_report || CodequalityReports.new
|
||||||
|
@head_report = head_report
|
||||||
|
end
|
||||||
|
|
||||||
|
def status
|
||||||
|
head_report.degradations_count > 0 ? STATUS_FAILED : STATUS_SUCCESS
|
||||||
|
end
|
||||||
|
|
||||||
|
def existing_errors
|
||||||
|
strong_memoize(:existing_errors) do
|
||||||
|
base_report.all_degradations & head_report.all_degradations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_errors
|
||||||
|
strong_memoize(:new_errors) do
|
||||||
|
fingerprints = head_report.degradations.keys - base_report.degradations.keys
|
||||||
|
head_report.degradations.fetch_values(*fingerprints)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolved_errors
|
||||||
|
strong_memoize(:resolved_errors) do
|
||||||
|
fingerprints = base_report.degradations.keys - head_report.degradations.keys
|
||||||
|
base_report.degradations.fetch_values(*fingerprints)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors_count
|
||||||
|
head_report.degradations_count
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolved_count
|
||||||
|
resolved_errors.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_count
|
||||||
|
existing_errors.size + new_errors.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1253,6 +1253,9 @@ msgstr ""
|
||||||
msgid "A merge request approval is required when the license compliance report contains a denied license."
|
msgid "A merge request approval is required when the license compliance report contains a denied license."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "A merge request hasn't yet been merged"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details"
|
msgid "A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3683,9 +3686,6 @@ msgstr ""
|
||||||
msgid "Are you sure you want to delete this SSH key?"
|
msgid "Are you sure you want to delete this SSH key?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Are you sure you want to delete this board?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Are you sure you want to delete this device? This action cannot be undone."
|
msgid "Are you sure you want to delete this device? This action cannot be undone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -4486,9 +4486,6 @@ msgstr ""
|
||||||
msgid "Blog"
|
msgid "Blog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Board scope"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Board scope affects which issues are displayed for anyone who visits this board"
|
msgid "Board scope affects which issues are displayed for anyone who visits this board"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -4543,6 +4540,30 @@ msgstr ""
|
||||||
msgid "Boards|View scope"
|
msgid "Boards|View scope"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Are you sure you want to delete this board?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Board scope"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Create board"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Create new board"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Delete board"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Edit board"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Enter board name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Board|Failed to delete board. Please try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Board|Load more issues"
|
msgid "Board|Load more issues"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -7943,9 +7964,6 @@ msgstr ""
|
||||||
msgid "Create and provide your GitHub %{link_start}Personal Access Token%{link_end}. You will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to import."
|
msgid "Create and provide your GitHub %{link_start}Personal Access Token%{link_end}. You will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to import."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create board"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Create branch"
|
msgid "Create branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8003,9 +8021,6 @@ msgstr ""
|
||||||
msgid "Create new Value Stream"
|
msgid "Create new Value Stream"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create new board"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Create new branch"
|
msgid "Create new branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8926,9 +8941,6 @@ msgstr ""
|
||||||
msgid "Delete badge"
|
msgid "Delete badge"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Delete board"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete comment"
|
msgid "Delete comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10062,9 +10074,6 @@ msgstr ""
|
||||||
msgid "Edit application"
|
msgid "Edit application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Edit board"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Edit comment"
|
msgid "Edit comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10479,9 +10488,6 @@ msgstr ""
|
||||||
msgid "Enter at least three characters to search"
|
msgid "Enter at least three characters to search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Enter board name"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enter domain"
|
msgid "Enter domain"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -11490,9 +11496,6 @@ msgstr ""
|
||||||
msgid "Failed to create wiki"
|
msgid "Failed to create wiki"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Failed to delete board. Please try again."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Failed to deploy to"
|
msgid "Failed to deploy to"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -19187,6 +19190,12 @@ msgstr ""
|
||||||
msgid "OnCallSchedules|The schedule could not be updated. Please try again."
|
msgid "OnCallSchedules|The schedule could not be updated. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "OnCallSchedules|Try adding a rotation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "OnCallSchedules|Your schedule has been successfully created and all alerts from this project will now be routed to this schedule. Currently, only one schedule can be created per project. More coming soon! To add individual users to this schedule, use the add a rotation button."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
|
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -27115,6 +27124,9 @@ msgstr ""
|
||||||
msgid "The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment."
|
msgid "The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The Compliance Dashboard gives you the ability to see a group's merge request activity by providing a high-level view for all projects in the group."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "The GitLab user to which the Jira user %{jiraDisplayName} will be mapped"
|
msgid "The GitLab user to which the Jira user %{jiraDisplayName} will be mapped"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
24
spec/fixtures/api/schemas/entities/codequality_degradation.json
vendored
Normal file
24
spec/fixtures/api/schemas/entities/codequality_degradation.json
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"description",
|
||||||
|
"severity",
|
||||||
|
"file_path",
|
||||||
|
"line"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"severity": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"file_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
43
spec/fixtures/api/schemas/entities/codequality_reports_comparer.json
vendored
Normal file
43
spec/fixtures/api/schemas/entities/codequality_reports_comparer.json
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["status", "summary", "new_errors", "resolved_errors", "existing_errors"],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"resolved": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"errored": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["total", "resolved", "errored"]
|
||||||
|
},
|
||||||
|
"new_errors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "codequality_degradation.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolved_errors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "codequality_degradation.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"existing_errors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "codequality_degradation.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
|
|
||||||
import { TEST_HOST } from 'jest/helpers/test_constants';
|
import { TEST_HOST } from 'jest/helpers/test_constants';
|
||||||
|
import { GlModal } from '@gitlab/ui';
|
||||||
import boardsStore from '~/boards/stores/boards_store';
|
import boardsStore from '~/boards/stores/boards_store';
|
||||||
import boardForm from '~/boards/components/board_form.vue';
|
import boardForm from '~/boards/components/board_form.vue';
|
||||||
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
|
|
||||||
|
|
||||||
describe('board_form.vue', () => {
|
describe('board_form.vue', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
@ -14,7 +14,7 @@ describe('board_form.vue', () => {
|
||||||
labelsWebUrl: `${TEST_HOST}/-/labels`,
|
labelsWebUrl: `${TEST_HOST}/-/labels`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const findModal = () => wrapper.find(DeprecatedModal);
|
const findModal = () => wrapper.find(GlModal);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
boardsStore.state.currentPage = 'edit';
|
boardsStore.state.currentPage = 'edit';
|
||||||
|
|
308
spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
Normal file
308
spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
|
let(:comparer) { described_class.new(base_report, head_report) }
|
||||||
|
let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:degradation_1) do
|
||||||
|
{
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"check_name": "argument_count",
|
||||||
|
"content": {
|
||||||
|
"body": ""
|
||||||
|
},
|
||||||
|
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
|
||||||
|
"fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"lines": {
|
||||||
|
"begin": 10,
|
||||||
|
"end": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_locations": [],
|
||||||
|
"remediation_points": 900000,
|
||||||
|
"severity": "major",
|
||||||
|
"type": "issue",
|
||||||
|
"engine_name": "structure"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:degradation_2) do
|
||||||
|
{
|
||||||
|
"type": "Issue",
|
||||||
|
"check_name": "Rubocop/Metrics/ParameterLists",
|
||||||
|
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"remediation_points": 550000,
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"positions": {
|
||||||
|
"begin": {
|
||||||
|
"column": 14,
|
||||||
|
"line": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"column": 39,
|
||||||
|
"line": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
|
||||||
|
},
|
||||||
|
"engine_name": "rubocop",
|
||||||
|
"fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
|
||||||
|
"severity": "minor"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#status' do
|
||||||
|
subject(:report_status) { comparer.status }
|
||||||
|
|
||||||
|
context 'when head report has an error' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns status failed' do
|
||||||
|
expect(report_status).to eq(described_class::STATUS_FAILED)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when head report does not have errors' do
|
||||||
|
it 'returns status success' do
|
||||||
|
expect(report_status).to eq(described_class::STATUS_SUCCESS)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#errors_count' do
|
||||||
|
subject(:errors_count) { comparer.errors_count }
|
||||||
|
|
||||||
|
context 'when head report has an error' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the number of new errors' do
|
||||||
|
expect(errors_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when head report does not have an error' do
|
||||||
|
it 'returns zero' do
|
||||||
|
expect(errors_count).to be_zero
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#resolved_count' do
|
||||||
|
subject(:resolved_count) { comparer.resolved_count }
|
||||||
|
|
||||||
|
context 'when base report has an error and head has a different error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'counts the base report error as resolved' do
|
||||||
|
expect(resolved_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors head has no errors' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'counts the base report errors as resolved' do
|
||||||
|
expect(resolved_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors and head has the same error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero' do
|
||||||
|
expect(resolved_count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report does not have errors and head has errors' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero' do
|
||||||
|
expect(resolved_count).to be_zero
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#total_count' do
|
||||||
|
subject(:total_count) { comparer.total_count }
|
||||||
|
|
||||||
|
context 'when base report has an error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero' do
|
||||||
|
expect(total_count).to be_zero
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when head report has an error' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes the head report error in the count' do
|
||||||
|
expect(total_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors and head report has errors' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes errors in the count' do
|
||||||
|
expect(total_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors and head report has the same error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes errors in the count' do
|
||||||
|
expect(total_count).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#existing_errors' do
|
||||||
|
subject(:existing_errors) { comparer.existing_errors }
|
||||||
|
|
||||||
|
context 'when base report has errors and head has the same error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes the base report errors' do
|
||||||
|
expect(existing_errors).to contain_exactly(degradation_1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors and head has a different error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(existing_errors).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report does not have errors and head has errors' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(existing_errors).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#new_errors' do
|
||||||
|
subject(:new_errors) { comparer.new_errors }
|
||||||
|
|
||||||
|
context 'when base report has errors and head has more errors' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes errors not found in the base report' do
|
||||||
|
expect(new_errors).to eq([degradation_2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has an error and head has no errors' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(new_errors).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report does not have errors and head has errors' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the head report error' do
|
||||||
|
expect(new_errors).to eq([degradation_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#resolved_errors' do
|
||||||
|
subject(:resolved_errors) { comparer.resolved_errors }
|
||||||
|
|
||||||
|
context 'when base report errors are still found in the head report' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(resolved_errors).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has errors and head has a different error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the base report error' do
|
||||||
|
expect(resolved_errors).to eq([degradation_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report does not have errors and head has errors' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(resolved_errors).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
88
spec/serializers/codequality_degradation_entity_spec.rb
Normal file
88
spec/serializers/codequality_degradation_entity_spec.rb
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe CodequalityDegradationEntity do
|
||||||
|
let(:entity) { described_class.new(codequality_degradation) }
|
||||||
|
|
||||||
|
describe '#as_json' do
|
||||||
|
subject { entity.as_json }
|
||||||
|
|
||||||
|
context 'when codequality contains an error' do
|
||||||
|
context 'when line is included in location' do
|
||||||
|
let(:codequality_degradation) do
|
||||||
|
{
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"check_name": "argument_count",
|
||||||
|
"content": {
|
||||||
|
"body": ""
|
||||||
|
},
|
||||||
|
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
|
||||||
|
"fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"lines": {
|
||||||
|
"begin": 10,
|
||||||
|
"end": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_locations": [],
|
||||||
|
"remediation_points": 900000,
|
||||||
|
"severity": "major",
|
||||||
|
"type": "issue",
|
||||||
|
"engine_name": "structure"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'contains correct codequality degradation details', :aggregate_failures do
|
||||||
|
expect(subject[:description]).to eq("Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.")
|
||||||
|
expect(subject[:severity]).to eq("major")
|
||||||
|
expect(subject[:file_path]).to eq("foo.rb")
|
||||||
|
expect(subject[:line]).to eq(10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when line is included in positions' do
|
||||||
|
let(:codequality_degradation) do
|
||||||
|
{
|
||||||
|
"type": "Issue",
|
||||||
|
"check_name": "Rubocop/Metrics/ParameterLists",
|
||||||
|
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"remediation_points": 550000,
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"positions": {
|
||||||
|
"begin": {
|
||||||
|
"column": 24,
|
||||||
|
"line": 14
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"column": 49,
|
||||||
|
"line": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
|
||||||
|
},
|
||||||
|
"engine_name": "rubocop",
|
||||||
|
"fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
|
||||||
|
"severity": "minor"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'contains correct codequality degradation details', :aggregate_failures do
|
||||||
|
expect(subject[:description]).to eq("Avoid parameter lists longer than 5 parameters. [12/5]")
|
||||||
|
expect(subject[:severity]).to eq("minor")
|
||||||
|
expect(subject[:file_path]).to eq("foo.rb")
|
||||||
|
expect(subject[:line]).to eq(14)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
85
spec/serializers/codequality_reports_comparer_entity_spec.rb
Normal file
85
spec/serializers/codequality_reports_comparer_entity_spec.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe CodequalityReportsComparerEntity do
|
||||||
|
let(:entity) { described_class.new(comparer) }
|
||||||
|
let(:comparer) { Gitlab::Ci::Reports::CodequalityReportsComparer.new(base_report, head_report) }
|
||||||
|
let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:degradation_1) do
|
||||||
|
{
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"check_name": "argument_count",
|
||||||
|
"content": {
|
||||||
|
"body": ""
|
||||||
|
},
|
||||||
|
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
|
||||||
|
"fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"lines": {
|
||||||
|
"begin": 10,
|
||||||
|
"end": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_locations": [],
|
||||||
|
"remediation_points": 900000,
|
||||||
|
"severity": "major",
|
||||||
|
"type": "issue",
|
||||||
|
"engine_name": "structure"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:degradation_2) do
|
||||||
|
{
|
||||||
|
"type": "Issue",
|
||||||
|
"check_name": "Rubocop/Metrics/ParameterLists",
|
||||||
|
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"remediation_points": 550000,
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"positions": {
|
||||||
|
"begin": {
|
||||||
|
"column": 14,
|
||||||
|
"line": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"column": 39,
|
||||||
|
"line": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
|
||||||
|
},
|
||||||
|
"engine_name": "rubocop",
|
||||||
|
"fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
|
||||||
|
"severity": "minor"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#as_json' do
|
||||||
|
subject { entity.as_json }
|
||||||
|
|
||||||
|
context 'when base and head report have errors' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'contains correct compared codequality report details', :aggregate_failures do
|
||||||
|
expect(subject[:status]).to eq(Gitlab::Ci::Reports::CodequalityReportsComparer::STATUS_FAILED)
|
||||||
|
expect(subject[:resolved_errors].first).to include(:description, :severity, :file_path, :line)
|
||||||
|
expect(subject[:new_errors].first).to include(:description, :severity, :file_path, :line)
|
||||||
|
expect(subject[:existing_errors]).to be_empty
|
||||||
|
expect(subject[:summary]).to include(total: 1, resolved: 1, errored: 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,92 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe CodequalityReportsComparerSerializer do
|
||||||
|
let(:project) { double(:project) }
|
||||||
|
let(:serializer) { described_class.new(project: project).represent(comparer) }
|
||||||
|
let(:comparer) { Gitlab::Ci::Reports::CodequalityReportsComparer.new(base_report, head_report) }
|
||||||
|
let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
|
||||||
|
let(:degradation_1) do
|
||||||
|
{
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"check_name": "argument_count",
|
||||||
|
"content": {
|
||||||
|
"body": ""
|
||||||
|
},
|
||||||
|
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
|
||||||
|
"fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"lines": {
|
||||||
|
"begin": 10,
|
||||||
|
"end": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_locations": [],
|
||||||
|
"remediation_points": 900000,
|
||||||
|
"severity": "major",
|
||||||
|
"type": "issue",
|
||||||
|
"engine_name": "structure"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:degradation_2) do
|
||||||
|
{
|
||||||
|
"type": "Issue",
|
||||||
|
"check_name": "Rubocop/Metrics/ParameterLists",
|
||||||
|
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
|
||||||
|
"categories": [
|
||||||
|
"Complexity"
|
||||||
|
],
|
||||||
|
"remediation_points": 550000,
|
||||||
|
"location": {
|
||||||
|
"path": "foo.rb",
|
||||||
|
"positions": {
|
||||||
|
"begin": {
|
||||||
|
"column": 14,
|
||||||
|
"line": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"column": 39,
|
||||||
|
"line": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
|
||||||
|
},
|
||||||
|
"engine_name": "rubocop",
|
||||||
|
"fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
|
||||||
|
"severity": "minor"
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_json' do
|
||||||
|
subject { serializer.as_json }
|
||||||
|
|
||||||
|
context 'when base report has error and head has a different error' do
|
||||||
|
before do
|
||||||
|
base_report.add_degradation(degradation_1)
|
||||||
|
head_report.add_degradation(degradation_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'matches the schema' do
|
||||||
|
expect(subject).to match_schema('entities/codequality_reports_comparer')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when base report has no error and head has errors' do
|
||||||
|
before do
|
||||||
|
head_report.add_degradation(degradation_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'matches the schema' do
|
||||||
|
expect(subject).to match_schema('entities/codequality_reports_comparer')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,6 +20,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
|
||||||
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
||||||
message: 'deleted tags',
|
message: 'deleted tags',
|
||||||
container_repository_id: repository.id,
|
container_repository_id: repository.id,
|
||||||
|
project_id: repository.project_id,
|
||||||
deleted_tags_count: tags.size
|
deleted_tags_count: tags.size
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +33,8 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
|
||||||
log_data = {
|
log_data = {
|
||||||
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
||||||
message: message,
|
message: message,
|
||||||
container_repository_id: repository.id
|
container_repository_id: repository.id,
|
||||||
|
project_id: repository.project_id
|
||||||
}
|
}
|
||||||
|
|
||||||
log_data.merge!(extra_log) if extra_log.any?
|
log_data.merge!(extra_log) if extra_log.any?
|
||||||
|
|
Loading…
Reference in a new issue