Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-01 18:09:17 +00:00
parent e92c95bf45
commit d7774ee304
52 changed files with 769 additions and 140 deletions

View File

@ -123,6 +123,7 @@
- ".gitlab/route-map.yml"
- "doc/**/*"
- ".markdownlint.json"
- "scripts/lint-doc.sh"
.frontend-dependency-patterns: &frontend-dependency-patterns
- "{package.json,yarn.lock}"

View File

@ -1,12 +1,14 @@
<script>
import { isEmpty } from 'lodash';
import { GlLink } from '@gitlab/ui';
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
TimeagoTooltip,
GlAlert,
GlLink,
GlSprintf,
TimeagoTooltip,
},
props: {
user: {
@ -27,17 +29,21 @@ export default {
};
</script>
<template>
<div class="gl-mt-3 js-build-erased">
<div class="erased alert alert-warning">
<div class="gl-mt-3">
<gl-alert variant="warning" :dismissible="false">
<template v-if="isErasedByUser">
{{ s__('Job|Job has been erased by') }}
<gl-link :href="user.web_url"> {{ user.username }} </gl-link>
<gl-sprintf :message="s__('Job|Job has been erased by %{userLink}')">
<template #userLink>
<gl-link :href="user.web_url" target="_blank">{{ user.username }}</gl-link>
</template>
</gl-sprintf>
</template>
<template v-else>
{{ s__('Job|Job has been erased') }}
</template>
<timeago-tooltip :time="erasedAt" />
</div>
</gl-alert>
</div>
</template>

View File

@ -35,6 +35,16 @@ export default (resolvers = {}, config = {}) => {
batchMax: config.batchMax || 10,
};
const requestCounterLink = new ApolloLink((operation, forward) => {
window.pendingApolloRequests = window.pendingApolloRequests || 0;
window.pendingApolloRequests += 1;
return forward(operation).map((response) => {
window.pendingApolloRequests -= 1;
return response;
});
});
const uploadsLink = ApolloLink.split(
(operation) => operation.getContext().hasUpload || operation.getContext().isSingleRequest,
createUploadLink(httpOptions),
@ -63,7 +73,12 @@ export default (resolvers = {}, config = {}) => {
return new ApolloClient({
typeDefs: config.typeDefs,
link: ApolloLink.from([performanceBarLink, new StartupJSLink(), uploadsLink]),
link: ApolloLink.from([
requestCounterLink,
performanceBarLink,
new StartupJSLink(),
uploadsLink,
]),
cache: new InMemoryCache({
...config.cacheConfig,
freezeResults: config.assumeImmutableResults,

View File

@ -6,6 +6,7 @@ import { languageCode, s__, __, n__ } from '../../locale';
const MILLISECONDS_IN_HOUR = 60 * 60 * 1000;
const MILLISECONDS_IN_DAY = 24 * MILLISECONDS_IN_HOUR;
const DAYS_IN_WEEK = 7;
window.timeago = timeago;
@ -693,6 +694,25 @@ export const nDaysAfter = (date, numberOfDays) =>
*/
export const nDaysBefore = (date, numberOfDays) => nDaysAfter(date, -numberOfDays);
/**
* Returns the date n weeks after the date provided
*
* @param {Date} date the initial date
* @param {Number} numberOfWeeks number of weeks after
* @return {Date} the date following the date provided
*/
export const nWeeksAfter = (date, numberOfWeeks) =>
new Date(newDate(date)).setDate(date.getDate() + DAYS_IN_WEEK * numberOfWeeks);
/**
* Returns the date n weeks before the date provided
*
* @param {Date} date the initial date
* @param {Number} numberOfWeeks number of weeks before
* @return {Date} the date following the date provided
*/
export const nWeeksBefore = (date, numberOfWeeks) => nWeeksAfter(date, -numberOfWeeks);
/**
* Returns the date n months after the date provided
*
@ -897,3 +917,19 @@ export const getOverlapDateInPeriods = (givenPeriodLeft = {}, givenPeriodRight =
overlapEndDate,
};
};
/**
* A utility function that checks that the date is today
*
* @param {Date} date
*
* @return {Boolean} true if provided date is today
*/
export const isToday = (date) => {
const today = new Date();
return (
date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear()
);
};

View File

@ -31,10 +31,11 @@ module AvatarsHelper
end
def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true)
return gravatar_icon(nil, size, scale) unless user
return default_avatar if user.blocked?
user.avatar_url(size: size, only_path: only_path) || default_avatar
if user
user.avatar_url(size: size, only_path: only_path) || default_avatar
else
gravatar_icon(nil, size, scale)
end
end
def gravatar_icon(user_email = '', size = nil, scale = 2)

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module Ci
class CodequalityMrDiffEntity < Grape::Entity
expose :files
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module Ci
class CodequalityMrDiffReportSerializer < BaseSerializer
entity CodequalityMrDiffEntity
end
end

View File

@ -22,11 +22,17 @@ module Ci
def build_carrierwave_file(pipeline)
CarrierWaveStringFile.new_file(
file_content: pipeline.codequality_reports.to_json,
file_content: build_quality_mr_diff_report(pipeline),
filename: Ci::PipelineArtifact::DEFAULT_FILE_NAMES.fetch(:code_quality),
content_type: 'application/json'
)
end
def build_quality_mr_diff_report(pipeline)
mr_diff_report = Gitlab::Ci::Reports::CodequalityMrDiff.new(pipeline.codequality_reports)
Ci::CodequalityMrDiffReportSerializer.new.represent(mr_diff_report).to_json # rubocop: disable CodeReuse/Serializer
end
end
end
end

View File

@ -22,6 +22,6 @@
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
.content{ id: "content-body", **page_itemtype }
%main.content{ id: "content-body", **page_itemtype }
= render "layouts/flash", extra_flash_class: 'limit-container-width'
= yield

View File

@ -3,7 +3,7 @@
- unless @skip_current_level_breadcrumb
- push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link)
%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] }
%nav.breadcrumbs{ class: [container, @content_class], 'aria-label': _('Breadcrumbs') }
.breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
- if defined?(@left_sidebar)
= button_tag class: 'toggle-mobile-nav', type: 'button' do

View File

@ -1,4 +1,4 @@
.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
%aside.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), 'aria-label': _('Admin navigation') }
.nav-sidebar-inner-scroll
.context-header
= link_to admin_root_path, title: _('Admin Overview') do

View File

@ -1,7 +1,9 @@
- issues_count = group_open_issues_count(@group)
- merge_requests_count = group_merge_requests_count(state: 'opened')
- aside_title = @group.subgroup? ? _('Subgroup navigation') : _('Group navigation')
- overview_title = @group.subgroup? ? _('Subgroup overview') : _('Group overview')
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('groups_side_navigation', 'render', 'groups_side_navigation') }
%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('groups_side_navigation', 'render', 'groups_side_navigation'), 'aria-label': aside_title }
.nav-sidebar-inner-scroll
.context-header
= link_to group_path(@group), title: @group.name do
@ -19,19 +21,13 @@
.nav-icon-container
= sprite_icon('home')
%span.nav-item-name
- if @group.subgroup?
= _('Subgroup overview')
- else
= _('Group overview')
= overview_title
%ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#details', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
= link_to group_path(@group) do
%strong.fly-out-top-item-name
- if @group.subgroup?
= _('Subgroup overview')
- else
= _('Group overview')
= overview_title
%li.divider.fly-out-top-item
= nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do

View File

@ -1,4 +1,4 @@
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('user_side_navigation', 'render', 'user_side_navigation') }
%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('user_side_navigation', 'render', 'user_side_navigation'), 'aria-label': _('User settings') }
.nav-sidebar-inner-scroll
.context-header
= link_to profile_path, title: _('Profile Settings') do

View File

@ -1,4 +1,4 @@
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation') }
%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation'), 'aria-label': _('Project navigation') }
.nav-sidebar-inner-scroll
.context-header
= link_to project_path(@project), title: @project.name do

View File

@ -1,6 +1,6 @@
%board-sidebar{ "inline-template" => true, ":current-user" => (UserSerializer.new.represent(current_user) || {}).to_json }
%transition{ name: "boards-sidebar-slide" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar", 'aria-label': s_('Boards|Board') }
.issuable-sidebar
.block.issuable-sidebar-header.position-relative
%span.issuable-header-text.hide-collapsed.float-left

View File

@ -3,7 +3,7 @@
- epic_bulk_edit_flag = @project&.group&.feature_available?(:epics) && type == :issues
- bulk_iterations_flag = @project&.group&.feature_available?(:iterations) && type == :issues
%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? }, 'aria-label': _('Bulk update') }
.issuable-sidebar.hidden
= form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header

View File

@ -8,7 +8,7 @@
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
- reviewers = local_assigns.fetch(:reviewers, nil)
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar
.block.issuable-sidebar-header
- if signed_in

View File

@ -1,7 +1,7 @@
- affix_offset = local_assigns.fetch(:affix_offset, "50")
- project = local_assigns[:project]
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': _('Milestone') }
.issuable-sidebar.milestone-sidebar
.block.milestone-progress.issuable-sidebar-header
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => s_('MilestoneSidebar|Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }

View File

@ -1,6 +1,6 @@
- editing ||= false
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix" } }
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix" }, 'aria-label': _('Wiki') }
.sidebar-container
.block.wiki-sidebar-header.gl-mb-3.w-100
%a.gutter-toggle.float-right.d-block.d-md-none.js-sidebar-wiki-toggle{ href: "#" }

View File

@ -1,5 +0,0 @@
---
title: Remove avatar of the blocked user
merge_request: 52051
author: Yogi (@yo)
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add site landmarks for screen readers
merge_request: 52514
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix empty pipeline analytics charts when time_zone is non-UTC
merge_request: 52971
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Replace erase job alert background color with color consistent with UI
merge_request: 52810
author:
type: changed

View File

@ -1,8 +0,0 @@
---
name: import_requirements_csv
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48060
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284846
milestone: '13.7'
type: development
group: group::product planning
default_enabled: true

View File

@ -68,6 +68,8 @@ To create a new Auditor user:
To revoke Auditor permissions from a user, make them a regular user by
following the previous steps.
Additionally users can be set as an Auditor using [SAML groups](../integration/saml.md#auditor-groups).
## Permissions and restrictions of an Auditor user
An Auditor user should be able to access all projects and groups of a GitLab

View File

@ -307,37 +307,37 @@ Example response:
"health_status": "Healthy",
"missing_oauth_application": false,
"attachments_count": 1,
"attachments_synced_count": nil,
"attachments_failed_count": nil,
"attachments_synced_count": null,
"attachments_failed_count": null,
"attachments_synced_missing_on_primary_count": 0,
"attachments_synced_in_percentage": "0.00%",
"db_replication_lag_seconds": nil,
"db_replication_lag_seconds": null,
"lfs_objects_count": 0,
"lfs_objects_synced_count": nil,
"lfs_objects_failed_count": nil,
"lfs_objects_synced_count": null,
"lfs_objects_failed_count": null,
"lfs_objects_synced_missing_on_primary_count": 0,
"lfs_objects_synced_in_percentage": "0.00%",
"job_artifacts_count": 2,
"job_artifacts_synced_count": nil,
"job_artifacts_failed_count": nil,
"job_artifacts_synced_count": null,
"job_artifacts_failed_count": null,
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "0.00%",
"container_repositories_count": 3,
"container_repositories_synced_count": nil,
"container_repositories_failed_count": nil,
"container_repositories_synced_count": null,
"container_repositories_failed_count": null,
"container_repositories_synced_in_percentage": "0.00%",
"design_repositories_count": 3,
"design_repositories_synced_count": nil,
"design_repositories_failed_count": nil,
"design_repositories_synced_count": null,
"design_repositories_failed_count": null,
"design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_count": 41,
"repositories_failed_count": nil,
"repositories_synced_count": nil,
"repositories_failed_count": null,
"repositories_synced_count": null,
"repositories_synced_in_percentage": "0.00%",
"wikis_count": 41,
"wikis_failed_count": nil,
"wikis_synced_count": nil,
"wikis_failed_count": null,
"wikis_synced_count": null,
"wikis_synced_in_percentage": "0.00%",
"replication_slots_count": 1,
"replication_slots_used_count": 1,
@ -367,7 +367,7 @@ Example response:
"repositories_checked_in_percentage": "17.07%",
"last_event_id": 23,
"last_event_timestamp": 1509681166,
"cursor_last_event_id": nil,
"cursor_last_event_id": null,
"cursor_last_event_timestamp": 0,
"last_successful_status_check_timestamp": 1510125024,
"version": "10.3.0",
@ -408,12 +408,12 @@ Example response:
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "50.00%",
"container_repositories_count": 3,
"container_repositories_synced_count": nil,
"container_repositories_failed_count": nil,
"container_repositories_synced_count": null,
"container_repositories_failed_count": null,
"container_repositories_synced_in_percentage": "0.00%",
"design_repositories_count": 3,
"design_repositories_synced_count": nil,
"design_repositories_failed_count": nil,
"design_repositories_synced_count": null,
"design_repositories_failed_count": null,
"design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_count": 41,
@ -424,10 +424,10 @@ Example response:
"wikis_failed_count": 0,
"wikis_synced_count": 41,
"wikis_synced_in_percentage": "100.00%",
"replication_slots_count": nil,
"replication_slots_used_count": nil,
"replication_slots_count": null,
"replication_slots_used_count": null,
"replication_slots_used_in_percentage": "0.00%",
"replication_slots_max_retained_wal_bytes": nil,
"replication_slots_max_retained_wal_bytes": null,
"repositories_checksummed_count": 20,
"repositories_checksum_failed_count": 5,
"repositories_checksummed_in_percentage": "48.78%",
@ -518,12 +518,12 @@ Example response:
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "50.00%",
"container_repositories_count": 3,
"container_repositories_synced_count": nil,
"container_repositories_failed_count": nil,
"container_repositories_synced_count": null,
"container_repositories_failed_count": null,
"container_repositories_synced_in_percentage": "0.00%",
"design_repositories_count": 3,
"design_repositories_synced_count": nil,
"design_repositories_failed_count": nil,
"design_repositories_synced_count": null,
"design_repositories_failed_count": null,
"design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_count": 41,
@ -534,10 +534,10 @@ Example response:
"wikis_failed_count": 0,
"wikis_synced_count": 41,
"wikis_synced_in_percentage": "100.00%",
"replication_slots_count": nil,
"replication_slots_used_count": nil,
"replication_slots_count": null,
"replication_slots_used_count": null,
"replication_slots_used_in_percentage": "0.00%",
"replication_slots_max_retained_wal_bytes": nil,
"replication_slots_max_retained_wal_bytes": null,
"last_event_id": 23,
"last_event_timestamp": 1509681166,
"cursor_last_event_id": 23,

View File

@ -1779,7 +1779,7 @@ To add a tier badge to a heading, add the relevant [tier badge](#available-produ
after the heading text. For example:
```markdown
# Heading title `**(FREE)**`
# Heading title **(FREE)**
```
#### Product tier badges on other content

View File

@ -93,8 +93,8 @@ To execute documentation link tests locally:
### UI link tests
The `ui-docs-links lint` job uses `haml-lint` to test that all links to docs from
UI elements (`app/views` files, for example) are linking to valid docs and anchors.
The `ui-docs-links lint` job uses `haml-lint` to test that all documentation links from
UI elements (`app/views` files, for example) are linking to valid pages and anchors.
To run the `ui-docs-links` test locally:
@ -156,12 +156,12 @@ markdownlint configuration is found in the following projects:
- [`charts`](https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/.markdownlint.json)
- [`gitlab-development-kit`](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/.markdownlint.json)
This configuration is also used within build pipelines.
This configuration is also used in build pipelines.
You can use markdownlint:
- [On the command line](https://github.com/igorshubovych/markdownlint-cli#markdownlint-cli--).
- [Within a code editor](#configure-editors).
- [In a code editor](#configure-editors).
- [In a `pre-push` hook](#configure-pre-push-hooks).
### Vale
@ -172,10 +172,10 @@ English language. Vale's configuration is stored in the
directory of projects.
Vale supports creating [custom tests](https://errata-ai.github.io/vale/styles/) that extend any of
several types of checks, which we store in the `.linting/vale/styles/gitlab` directory within the
several types of checks, which we store in the `.linting/vale/styles/gitlab` directory in the
documentation directory of projects.
Vale configuration is found in the following projects:
You can find Vale configuration in the following projects:
- [`gitlab`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/.vale/gitlab)
- [`gitlab-runner`](https://gitlab.com/gitlab-org/gitlab-runner/-/tree/master/docs/.vale/gitlab)
@ -183,13 +183,13 @@ Vale configuration is found in the following projects:
- [`charts`](https://gitlab.com/gitlab-org/charts/gitlab/-/tree/master/doc/.vale/gitlab)
- [`gitlab-development-kit`](https://gitlab.com/gitlab-org/gitlab-development-kit/-/tree/master/doc/.vale/gitlab)
This configuration is also used within build pipelines, where
This configuration is also used in build pipelines, where
[error-level rules](#vale-result-types) are enforced.
You can use Vale:
- [On the command line](https://errata-ai.gitbook.io/vale/getting-started/usage).
- [Within a code editor](#configure-editors).
- [In a code editor](#configure-editors).
- [In a Git hook](#configure-pre-push-hooks). Vale only reports errors in the Git hook (the same
configuration as the CI/CD pipelines), and does not report suggestions or warnings.
@ -243,32 +243,40 @@ To match the versions of `markdownlint-cli` and `vale` used in the GitLab projec
[versions used](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/.gitlab-ci.yml#L447)
when building the `image:docs-lint-markdown` Docker image containing these tools for CI/CD.
| Tool | Version | Command | Additional information |
|--------------------|----------|-------------------------------------------|------------------------|
| `markdownlint-cli` | Latest | `yarn global add markdownlint-cli` | n/a |
| Tool | Version | Command | Additional information |
|--------------------|-----------|-------------------------------------------|------------------------|
| `markdownlint-cli` | Latest | `yarn global add markdownlint-cli` | n/a |
| `markdownlint-cli` | Specific | `yarn global add markdownlint-cli@0.23.2` | The `@` indicates a specific version, and this example updates the tool to version `0.23.2`. |
| Vale | Latest | `brew update && brew upgrade vale` | This command is for macOS only. |
| Vale | Specific | n/a | Not possible using `brew`, but can be [directly downloaded](https://github.com/errata-ai/vale/releases). |
| Vale | Latest | `brew update && brew upgrade vale` | This command is for macOS only. |
| Vale | Specific | n/a | Not possible using `brew`, but can be [directly downloaded](https://github.com/errata-ai/vale/releases). |
### Configure editors
Using linters in your editor is more convenient than having to run the commands from the
command line.
To configure markdownlint within your editor, install one of the following as appropriate:
To configure markdownlint in your editor, install one of the following as appropriate:
- [Sublime Text](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint)
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint)
- [Atom](https://atom.io/packages/linter-node-markdownlint)
- [Vim](https://github.com/dense-analysis/ale)
- Sublime Text [`SublimeLinter-contrib-markdownlint` package](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint).
- Visual Studio Code [`DavidAnson.vscode-markdownlint` extension](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint).
- Atom [`linter-node-markdownlint` package](https://atom.io/packages/linter-node-markdownlint).
- Vim [ALE plugin](https://github.com/dense-analysis/ale).
To configure Vale within your editor, install one of the following as appropriate:
To configure Vale in your editor, install one of the following as appropriate:
- The Sublime Text [`SublimeLinter-contrib-vale` plugin](https://packagecontrol.io/packages/SublimeLinter-contrib-vale).
- The Visual Studio Code [`errata-ai.vale-server` extension](https://marketplace.visualstudio.com/items?itemName=errata-ai.vale-server).
You don't need Vale Server to use the plugin. You can configure the plugin to
- Sublime Text [`SublimeLinter-contrib-vale` package](https://packagecontrol.io/packages/SublimeLinter-contrib-vale).
- Visual Studio Code [`errata-ai.vale-server` extension](https://marketplace.visualstudio.com/items?itemName=errata-ai.vale-server).
You can configure the plugin to
[display only a subset of alerts](#show-subset-of-vale-alerts).
- [Vim](https://github.com/dense-analysis/ale).
In the extension's settings:
- Select the **Use CLI** checkbox.
- In the **Config** setting, enter an absolute path to [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/blob/master/.vale.ini) in one of the cloned GitLab repositories on your computer.
- In the **Path** setting, enter the absolute path to the Vale binary. In most
cases, `vale` should work. To find the location, run `which vale` in a terminal.
- Vim [ALE plugin](https://github.com/dense-analysis/ale).
We don't use [Vale Server](https://errata-ai.github.io/vale/#using-vale-with-a-text-editor-or-another-third-party-application).

View File

@ -163,7 +163,9 @@ will be returned to GitLab and will be signed in.
## SAML Groups
You can require users to be members of a certain group, or assign users `external`, `admin` or `auditor` roles based on group membership. This feature **does not** allow you to
You can require users to be members of a certain group, or assign users [external](../user/permissions.md#external-users), admin or [auditor](../user/permissions.md#auditor-users) roles based on group membership.
These groups are checked on each SAML login and user attributes updated as necessary.
This feature **does not** allow you to
automatically add users to GitLab [Groups](../user/group/index.md).
### Requirements
@ -215,7 +217,7 @@ Example:
### External groups **(PREMIUM SELF)**
SAML login supports automatic identification on whether a user should be considered an [external](../user/permissions.md) user. This is based on the user's group membership in the SAML identity provider.
SAML login supports automatic identification on whether a user should be considered an [external user](../user/permissions.md#external-users). This is based on the user's group membership in the SAML identity provider.
```yaml
{ name: 'saml',
@ -257,7 +259,7 @@ considered admin users.
The requirements are the same as the previous settings, your IdP needs to pass Group information to GitLab, you need to tell
GitLab where to look for the groups in the SAML response, and which group(s) should be
considered auditor users.
considered [auditor users](../user/permissions.md#auditor-users).
```yaml
{ name: 'saml',
@ -385,7 +387,7 @@ This setting should be used only to map attributes that are part of the OmniAuth
`attribute_statements` is used to map Attribute Names in a SAMLResponse to entries
in the OmniAuth [`info` hash](https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later).
For example, if your SAMLResponse contains an Attribute called 'EmailAddress',
For example, if your SAMLResponse contains an Attribute called `EmailAddress`,
specify `{ email: ['EmailAddress'] }` to map the Attribute to the
corresponding key in the `info` hash. URI-named Attributes are also supported, e.g.
`{ email: ['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'] }`.
@ -582,8 +584,8 @@ GitLab will sign the request with the provided private key. GitLab will include
Avoid user control of the following attributes:
- [`*NameID*`](../user/group/saml_sso/index.md#nameid)
- *Email* when used with `omniauth_auto_link_saml_user`
- [`NameID`](../user/group/saml_sso/index.md#nameid)
- `Email` when used with `omniauth_auto_link_saml_user`
These attributes define the SAML user. If users can change these attributes, they can impersonate others.

View File

@ -26,6 +26,8 @@ variables:
# If not using GitLab's HTTP backend, remove this line and specify TF_HTTP_* variables
TF_STATE_NAME: default
TF_CACHE_KEY: default
# If your terraform files are in a subdirectory, set TF_ROOT accordingly
# TF_ROOT: terraform/production
```
This template uses `.latest.`, instead of stable, and may include breaking changes.
@ -39,6 +41,15 @@ This template also includes some opinionated decisions, which you can override:
[run the Terraform commands](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml)
`init`, `validate`, `plan`, `plan-json`, and `apply`. The `apply` command only runs on `master`.
This video from January 2021 walks you through all the GitLab Terraform integration features:
<div class="video-fallback">
See the video: <a href="https://www.youtube.com/watch?v=iGXjUrkkzDI">Terraform with GitLab</a>.
</div>
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/iGXjUrkkzDI" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
## GitLab Managed Terraform state
[Terraform remote backends](https://www.terraform.io/docs/backends/index.html)

View File

@ -352,6 +352,9 @@ An administrator can flag a user as external by either of the following methods:
or edit an existing one. There, you can find the option to flag the user as
external.
Additionally users can be set as external users using [SAML groups](../integration/saml.md#external-groups)
and [LDAP groups](../administration/auth/ldap/index.md#external-groups).
### Setting new users to external
By default, new users are not set as external users. This behavior can be changed

View File

@ -145,7 +145,7 @@ whitespace changes.
## Mark files as viewed
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51513) in GitLab 13.8.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51513) in GitLab 13.9.
> - It's deployed behind a feature flag, enabled by default.
> - It's enabled on GitLab.com.
> - It's recommended for production use.

View File

@ -21,13 +21,17 @@ By default, a protected branch does these things:
- It prevents **anyone** from force pushing to the branch.
- It prevents **anyone** from deleting the branch.
NOTE:
A GitLab administrator is allowed to push to the protected branches.
**Permissions:**
See the [Changelog](#changelog) section for changes over time.
- GitLab administrators are allowed to push to the protected branches.
- Users with [Developer permissions](../permissions.md) are allowed to
create a project in a group, but might not be allowed to initially
push to the [default branch](repository/branches/index.md#default-branch).
The default branch protection level is set in the [Admin Area](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
See the [Changelog](#changelog) section for changes over time.
## Configuring protected branches
To protect a branch, you need to have at least Maintainer permission level.

View File

@ -0,0 +1,14 @@
# See Usage Ping metrics dictionary docs https://docs.gitlab.com/ee/development/usage_ping/metrics_dictionary.html
key_path: <%= key_path %>
value_type:
product_category:
stage:
status:
milestone:
introduced_by_url:
group:
time_frame: <%= time_frame %>
data_source:
distribution: <%= distribution %>
# tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
tier:

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
require 'rails/generators'
module Gitlab
class UsageMetricDefinitionGenerator < Rails::Generators::Base
Directory = Struct.new(:name, :time_frame) do
def match?(str)
(name == str || time_frame == str) && str != 'none'
end
end
TIME_FRAME_DIRS = [
Directory.new('counts_7d', '7d'),
Directory.new('counts_28d', '28d'),
Directory.new('counts_all', 'all'),
Directory.new('settings', 'none'),
Directory.new('license', 'none')
].freeze
VALID_INPUT_DIRS = (TIME_FRAME_DIRS.flat_map { |d| [d.name, d.time_frame] } - %w(none)).freeze
source_root File.expand_path('../../../generator_templates/usage_metric_definition', __dir__)
desc 'Generates a metric definition yml file'
class_option :ee, type: :boolean, optional: true, default: false, desc: 'Indicates if metric is for ee'
class_option :dir,
type: :string, desc: "Indicates the metric location. It must be one of: #{VALID_INPUT_DIRS.join(', ')}"
argument :key_path, type: :string, desc: 'Unique JSON key path for the metric'
def create_metric_file
validate!
template "metric_definition.yml", file_path
end
def time_frame
directory&.time_frame
end
def distribution
value = ['ce']
value << 'ee' if ee?
value
end
private
def file_path
path = File.join('config', 'metrics', directory&.name, "#{file_name}.yml")
path = File.join('ee', path) if ee?
path
end
def validate!
raise "--dir option is required" unless input_dir.present?
raise "Invalid dir #{input_dir}, allowed options are #{VALID_INPUT_DIRS.join(', ')}" unless directory.present?
end
def ee?
options[:ee]
end
def input_dir
options[:dir]
end
def file_name
key_path.split('.').last
end
def directory
@directory ||= TIME_FRAME_DIRS.find { |d| d.match?(input_dir) }
end
end
end

View File

@ -6,6 +6,7 @@ module Gitlab
module Models
# isolated Namespace model
class Namespace < ApplicationRecord
include FeatureGate
include ::Gitlab::VisibilityLevel
include ::Gitlab::Utils::StrongMemoize
include Gitlab::BackgroundMigration::UserMentions::Models::Concerns::Namespace::RecursiveTraversal

View File

@ -31,9 +31,10 @@ module Gitlab
current = @from
while current <= @to
@labels << current.strftime(@format)
@total << (totals_count[current] || 0)
@success << (success_count[current] || 0)
label = current.strftime(@format)
@labels << label
@total << (totals_count[label] || 0)
@success << (success_count[label] || 0)
current += interval_step
end
@ -45,6 +46,7 @@ module Gitlab
query
.group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)")
.count(:created_at)
.transform_keys { |date| date.strftime(@format) }
end
# rubocop: enable CodeReuse/ActiveRecord

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Reports
class CodequalityMrDiff
attr_reader :files
def initialize(raw_report)
@raw_report = raw_report
@files = {}
build_report!
end
private
def build_report!
codequality_files = @raw_report.all_degradations.each_with_object({}) do |degradation, codequality_files|
unless codequality_files[degradation.dig(:location, :path)].present?
codequality_files[degradation.dig(:location, :path)] = []
end
build_mr_diff_payload(codequality_files, degradation)
end
@files = codequality_files
end
def build_mr_diff_payload(codequality_files, degradation)
codequality_files[degradation.dig(:location, :path)] << {
line: degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line),
description: degradation[:description],
severity: degradation[:severity]
}
end
end
end
end
end

View File

@ -8,9 +8,9 @@ module Rouge
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
#
# [+tag+] The tag (language) of the lexer used to generate the formatted tokens
def initialize(tag: nil)
def initialize(options = {})
@line_number = 1
@tag = tag
@tag = options[:tag]
end
def stream(tokens)

View File

@ -1905,6 +1905,9 @@ msgstr ""
msgid "Admin mode enabled"
msgstr ""
msgid "Admin navigation"
msgstr ""
msgid "Admin notes"
msgstr ""
@ -4656,6 +4659,9 @@ msgstr ""
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
msgid "Boards|Board"
msgstr ""
msgid "Boards|Collapse"
msgstr ""
@ -4851,6 +4857,9 @@ msgstr ""
msgid "Branches|protected"
msgstr ""
msgid "Breadcrumbs"
msgstr ""
msgid "Brief title about the change"
msgstr ""
@ -4890,6 +4899,9 @@ msgstr ""
msgid "Bulk request concurrency"
msgstr ""
msgid "Bulk update"
msgstr ""
msgid "BulkImport|From source group"
msgstr ""
@ -8904,7 +8916,7 @@ msgstr ""
msgid "Dashboard|%{firstProject}, %{rest}, and %{secondProject}"
msgstr ""
msgid "Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Silver plan."
msgid "Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Premium plan."
msgstr ""
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
@ -13876,6 +13888,9 @@ msgstr ""
msgid "Group name (your organization)"
msgstr ""
msgid "Group navigation"
msgstr ""
msgid "Group overview"
msgstr ""
@ -16723,7 +16738,7 @@ msgstr ""
msgid "Job|Job has been erased"
msgstr ""
msgid "Job|Job has been erased by"
msgid "Job|Job has been erased by %{userLink}"
msgstr ""
msgid "Job|Keep"
@ -20507,7 +20522,7 @@ msgstr ""
msgid "One or more of your personal access tokens will expire in %{days_to_expire} days or less."
msgstr ""
msgid "Only 'Reporter' roles and above on tiers Premium / Silver and above can see Value Stream Analytics."
msgid "Only 'Reporter' roles and above on tiers Premium and above can see Value Stream Analytics."
msgstr ""
msgid "Only 1 appearances row can exist"
@ -20543,7 +20558,7 @@ msgstr ""
msgid "Only verified users with an email address in any of these domains can be added to the group."
msgstr ""
msgid "Only Reporter roles and above on tiers Premium / Silver and above can see Productivity Analytics."
msgid "Only Reporter roles and above on tiers Premium and above can see Productivity Analytics."
msgstr ""
msgid "Oops, are you sure?"
@ -22613,6 +22628,9 @@ msgstr ""
msgid "Project name suffix"
msgstr ""
msgid "Project navigation"
msgstr ""
msgid "Project order will not be saved as local storage is not available."
msgstr ""
@ -25595,6 +25613,9 @@ msgstr ""
msgid "Security dashboard"
msgstr ""
msgid "Security navigation"
msgstr ""
msgid "Security report is out of date. Please update your branch with the latest changes from the target branch (%{targetBranchName})"
msgstr ""
@ -27710,6 +27731,9 @@ msgstr ""
msgid "Subgroup milestone"
msgstr ""
msgid "Subgroup navigation"
msgstr ""
msgid "Subgroup overview"
msgstr ""
@ -30322,7 +30346,7 @@ msgstr ""
msgid "To see all the user's personal access tokens you must impersonate them first."
msgstr ""
msgid "To see this project's operational details, %{linkStart}upgrade its group plan to Silver%{linkEnd}. You can also remove the project from the dashboard."
msgid "To see this project's operational details, %{linkStart}upgrade its group plan to Premium%{linkEnd}. You can also remove the project from the dashboard."
msgstr ""
msgid "To see this project's operational details, contact an owner of group %{groupName} to upgrade the plan. You can also remove the project from the dashboard."

View File

@ -0,0 +1,98 @@
# frozen_string_literal: true
FactoryBot.define do
factory :codequality_degradation_1, class: Hash do
skip_create
initialize_with do
{
"categories": [
"Complexity"
],
"check_name": "argument_count",
"content": {
"body": ""
},
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
"fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
"location": {
"path": "file_a.rb",
"lines": {
"begin": 10,
"end": 10
}
},
"other_locations": [],
"remediation_points": 900000,
"severity": "major",
"type": "issue",
"engine_name": "structure"
}.with_indifferent_access
end
end
factory :codequality_degradation_2, class: Hash do
skip_create
initialize_with do
{
"categories": [
"Complexity"
],
"check_name": "argument_count",
"content": {
"body": ""
},
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
"fingerprint": "f3bdc1e8c102ba5fbd9e7f6cda51c95e",
"location": {
"path": "file_a.rb",
"lines": {
"begin": 10,
"end": 10
}
},
"other_locations": [],
"remediation_points": 900000,
"severity": "major",
"type": "issue",
"engine_name": "structure"
}.with_indifferent_access
end
end
factory :codequality_degradation_3, class: Hash do
skip_create
initialize_with 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": "file_b.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
end
end

View File

@ -54,7 +54,7 @@ RSpec.describe 'Admin Groups' do
click_button "Create group"
expect(current_path).to eq admin_group_path(Group.find_by(path: path_component))
content = page.find('div#content-body')
content = page.find('#content-body')
h3_texts = content.all('h3').collect(&:text).join("\n")
expect(h3_texts).to match group_name
li_texts = content.all('li').collect(&:text).join("\n")

View File

@ -0,0 +1,21 @@
{
"type": "object",
"description": "The schema used to display codequality report in mr diff",
"required": ["files"],
"properties": {
"patternProperties": {
".*.": {
"type": "array",
"items": {
"required": ["line", "description", "severity"],
"properties": {
"line": { "type": "integer" },
"description": { "type": "string" },
"severity": { "type": "string" }
},
"additionalProperties": false
}
}
}
}
}

View File

@ -10,6 +10,8 @@ describe('Erased block', () => {
const timeago = getTimeago();
const formattedDate = timeago.format(erasedAt);
const findLink = () => wrapper.find(GlLink);
const createComponent = (props) => {
wrapper = mount(ErasedBlock, {
propsData: props,
@ -32,7 +34,7 @@ describe('Erased block', () => {
});
it('renders username and link', () => {
expect(wrapper.find(GlLink).attributes('href')).toEqual('gitlab.com/root');
expect(findLink().attributes('href')).toEqual('gitlab.com/root');
expect(wrapper.text().trim()).toContain('Job has been erased by');
expect(wrapper.text().trim()).toContain('root');

View File

@ -618,9 +618,12 @@ describe('nDaysAfter', () => {
${-1} | ${new Date('2019-07-15T00:00:00.000Z').valueOf()}
${0} | ${date.valueOf()}
${0.9} | ${date.valueOf()}
`('returns $numberOfDays day(s) after the provided date', ({ numberOfDays, expectedResult }) => {
expect(datetimeUtility.nDaysAfter(date, numberOfDays)).toBe(expectedResult);
});
`(
'returns the date $numberOfDays day(s) after the provided date',
({ numberOfDays, expectedResult }) => {
expect(datetimeUtility.nDaysAfter(date, numberOfDays)).toBe(expectedResult);
},
);
});
describe('nDaysBefore', () => {
@ -633,9 +636,48 @@ describe('nDaysBefore', () => {
${-1} | ${new Date('2019-07-17T00:00:00.000Z').valueOf()}
${0} | ${date.valueOf()}
${0.9} | ${new Date('2019-07-15T00:00:00.000Z').valueOf()}
`('returns $numberOfDays day(s) before the provided date', ({ numberOfDays, expectedResult }) => {
expect(datetimeUtility.nDaysBefore(date, numberOfDays)).toBe(expectedResult);
});
`(
'returns the date $numberOfDays day(s) before the provided date',
({ numberOfDays, expectedResult }) => {
expect(datetimeUtility.nDaysBefore(date, numberOfDays)).toBe(expectedResult);
},
);
});
describe('nWeeksAfter', () => {
const date = new Date('2021-07-16T00:00:00.000Z');
it.each`
numberOfWeeks | expectedResult
${1} | ${new Date('2021-07-23T00:00:00.000Z').valueOf()}
${3} | ${new Date('2021-08-06T00:00:00.000Z').valueOf()}
${-1} | ${new Date('2021-07-09T00:00:00.000Z').valueOf()}
${0} | ${date.valueOf()}
${0.6} | ${new Date('2021-07-20T00:00:00.000Z').valueOf()}
`(
'returns the date $numberOfWeeks week(s) after the provided date',
({ numberOfWeeks, expectedResult }) => {
expect(datetimeUtility.nWeeksAfter(date, numberOfWeeks)).toBe(expectedResult);
},
);
});
describe('nWeeksBefore', () => {
const date = new Date('2021-07-16T00:00:00.000Z');
it.each`
numberOfWeeks | expectedResult
${1} | ${new Date('2021-07-09T00:00:00.000Z').valueOf()}
${3} | ${new Date('2021-06-25T00:00:00.000Z').valueOf()}
${-1} | ${new Date('2021-07-23T00:00:00.000Z').valueOf()}
${0} | ${date.valueOf()}
${0.6} | ${new Date('2021-07-11T00:00:00.000Z').valueOf()}
`(
'returns the date $numberOfWeeks week(s) before the provided date',
({ numberOfWeeks, expectedResult }) => {
expect(datetimeUtility.nWeeksBefore(date, numberOfWeeks)).toBe(expectedResult);
},
);
});
describe('nMonthsAfter', () => {
@ -659,7 +701,7 @@ describe('nMonthsAfter', () => {
${may2020} | ${0} | ${may2020.valueOf()}
${may2020} | ${0.9} | ${may2020.valueOf()}
`(
'returns $numberOfMonths month(s) after the provided date',
'returns the date $numberOfMonths month(s) after the provided date',
({ date, numberOfMonths, expectedResult }) => {
expect(datetimeUtility.nMonthsAfter(date, numberOfMonths)).toBe(expectedResult);
},
@ -687,7 +729,7 @@ describe('nMonthsBefore', () => {
${june2020} | ${0} | ${june2020.valueOf()}
${june2020} | ${0.9} | ${new Date('2020-05-15T00:00:00.000Z').valueOf()}
`(
'returns $numberOfMonths month(s) before the provided date',
'returns the date $numberOfMonths month(s) before the provided date',
({ date, numberOfMonths, expectedResult }) => {
expect(datetimeUtility.nMonthsBefore(date, numberOfMonths)).toBe(expectedResult);
},
@ -898,3 +940,14 @@ describe('getOverlapDateInPeriods', () => {
});
});
});
describe('isToday', () => {
const today = new Date();
it.each`
date | expected | negation
${today} | ${true} | ${'is'}
${new Date('2021-01-21T12:00:00.000Z')} | ${false} | ${'is NOT'}
`('returns $expected as $date $negation today', ({ date, expected }) => {
expect(datetimeUtility.isToday(date)).toBe(expected);
});
});

View File

@ -135,15 +135,6 @@ RSpec.describe AvatarsHelper do
helper.avatar_icon_for_user(nil, 20, 2)
end
end
context 'for a blocked user' do
let(:user) { create(:user, :blocked) }
it 'returns the default avatar' do
expect(helper.avatar_icon_for_user(user).to_s)
.to eq(helper.default_avatar)
end
end
end
describe '#gravatar_icon' do

View File

@ -9,6 +9,10 @@ RSpec.describe Gitlab::Ci::Charts do
subject { chart.to }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'goes until the end of the current month (including the whole last day of the month)' do
is_expected.to eq(Date.today.end_of_month.end_of_day)
end
@ -20,6 +24,10 @@ RSpec.describe Gitlab::Ci::Charts do
it 'uses %B %Y as labels format' do
expect(chart.labels).to include(chart.from.strftime('%B %Y'))
end
it 'returns count of pipelines run each day in the current year' do
expect(chart.total.sum).to eq(1)
end
end
context 'monthchart' do
@ -28,6 +36,10 @@ RSpec.describe Gitlab::Ci::Charts do
subject { chart.to }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'includes the whole current day' do
is_expected.to eq(Date.today.end_of_day)
end
@ -39,6 +51,10 @@ RSpec.describe Gitlab::Ci::Charts do
it 'uses %d %B as labels format' do
expect(chart.labels).to include(chart.from.strftime('%d %B'))
end
it 'returns count of pipelines run each day in the current month' do
expect(chart.total.sum).to eq(1)
end
end
context 'weekchart' do
@ -47,6 +63,10 @@ RSpec.describe Gitlab::Ci::Charts do
subject { chart.to }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'includes the whole current day' do
is_expected.to eq(Date.today.end_of_day)
end
@ -58,6 +78,68 @@ RSpec.describe Gitlab::Ci::Charts do
it 'uses %d %B as labels format' do
expect(chart.labels).to include(chart.from.strftime('%d %B'))
end
it 'returns count of pipelines run each day in the current week' do
expect(chart.total.sum).to eq(1)
end
end
context 'weekchart_utc' do
today = Date.today
end_of_today = Time.use_zone(Time.find_zone('UTC')) { today.end_of_day }
let(:project) { create(:project) }
let(:chart) do
allow(Date).to receive(:today).and_return(today)
allow(today).to receive(:end_of_day).and_return(end_of_today)
Gitlab::Ci::Charts::WeekChart.new(project)
end
subject { chart.total }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'uses a utc time zone for range times' do
expect(chart.to.zone).to eq(end_of_today.zone)
expect(chart.from.zone).to eq(end_of_today.zone)
end
it 'returns count of pipelines run each day in the current week' do
expect(chart.total.sum).to eq(1)
end
end
context 'weekchart_non_utc' do
today = Date.today
end_of_today = Time.use_zone(Time.find_zone('Asia/Dubai')) { today.end_of_day }
let(:project) { create(:project) }
let(:chart) do
allow(Date).to receive(:today).and_return(today)
allow(today).to receive(:end_of_day).and_return(end_of_today)
Gitlab::Ci::Charts::WeekChart.new(project)
end
subject { chart.total }
before do
# The DB uses UTC always, so our use of a Time Zone in the application
# can cause the creation date of the pipeline to go unmatched depending
# on the offset. We can work around this by requesting the pipeline be
# created a with the `created_at` field set to a day ago in the same week.
create(:ci_empty_pipeline, project: project, duration: 120, created_at: today - 1.day)
end
it 'uses a non-utc time zone for range times' do
expect(chart.to.zone).to eq(end_of_today.zone)
expect(chart.from.zone).to eq(end_of_today.zone)
end
it 'returns count of pipelines run each day in the current week' do
expect(chart.total.sum).to eq(1)
end
end
context 'pipeline_times' do

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff do
let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
let(:degradation_1) { build(:codequality_degradation_1) }
let(:degradation_2) { build(:codequality_degradation_2) }
let(:degradation_3) { build(:codequality_degradation_3) }
describe '#initialize!' do
subject(:report) { described_class.new(codequality_report) }
context 'when quality has degradations' do
context 'with several degradations on the same line' do
before do
codequality_report.add_degradation(degradation_1)
codequality_report.add_degradation(degradation_2)
end
it 'generates quality report for mr diff' do
expect(report.files).to match(
"file_a.rb" => [
{ line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
{ line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
]
)
end
end
context 'with several degradations on several files' do
before do
codequality_report.add_degradation(degradation_1)
codequality_report.add_degradation(degradation_2)
codequality_report.add_degradation(degradation_3)
end
it 'returns quality report for mr diff' do
expect(report.files).to match(
"file_a.rb" => [
{ line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
{ line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
],
"file_b.rb" => [
{ line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "minor" }
]
)
end
end
end
context 'when quality has no degradation' do
it 'returns an empty hash' do
expect(report.files).to match({})
end
end
end
end

View File

@ -545,7 +545,7 @@ project:
- daily_build_group_report_results
- jira_imports
- compliance_framework_setting
- compliance_management_frameworks
- compliance_management_framework
- metrics_users_starred_dashboards
- alert_management_alerts
- repository_storage_moves

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CodequalityMrDiffEntity do
let(:entity) { described_class.new(mr_diff_report) }
let(:mr_diff_report) { Gitlab::Ci::Reports::CodequalityMrDiff.new(codequality_report) }
let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
let(:degradation_1) { build(:codequality_degradation_1) }
let(:degradation_2) { build(:codequality_degradation_2) }
describe '#as_json' do
subject(:report) { entity.as_json }
context 'when quality report has degradations' do
before do
codequality_report.add_degradation(degradation_1)
codequality_report.add_degradation(degradation_2)
end
it 'contains correct codequality mr diff report', :aggregate_failures do
expect(report[:files].keys).to eq(["file_a.rb"])
expect(report[:files]["file_a.rb"].first).to include(:line, :description, :severity)
end
end
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CodequalityMrDiffReportSerializer do
let(:serializer) { described_class.new.represent(mr_diff_report) }
let(:mr_diff_report) { Gitlab::Ci::Reports::CodequalityMrDiff.new(codequality_report) }
let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
let(:degradation_1) { build(:codequality_degradation_1) }
let(:degradation_2) { build(:codequality_degradation_2) }
describe '#to_json' do
subject { serializer.as_json }
context 'when quality report has degradations' do
before do
codequality_report.add_degradation(degradation_1)
codequality_report.add_degradation(degradation_2)
end
it 'matches the schema' do
expect(subject).to match_schema('entities/codequality_mr_diff_report')
end
end
context 'when quality report has no degradations' do
it 'matches the schema' do
expect(subject).to match_schema('entities/codequality_mr_diff_report')
end
end
end
end

View File

@ -52,6 +52,6 @@ module WaitForRequests
end
def finished_all_ajax_requests?
Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate
Capybara.page.evaluate_script('window.pendingRequests || window.pendingApolloRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate
end
end