Commit daff9d36 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 911d55e6 ca790a62
<script>
import { GlTable, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { GlTable, GlIcon, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertIntegrationsViewsOptions } from '../constants';
......@@ -27,6 +27,7 @@ export default {
components: {
GlTable,
GlIcon,
GlLoadingIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -37,10 +38,15 @@ export default {
required: false,
default: () => [],
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
fields: [
{
key: 'activated',
key: 'active',
label: __('Status'),
},
{
......@@ -78,12 +84,13 @@ export default {
:empty-text="$options.i18n.emptyState"
:items="integrations"
:fields="$options.fields"
:busy="loading"
stacked="md"
:tbody-tr-class="tbodyTrClass"
show-empty
>
<template #cell(activated)="{ item }">
<span v-if="item.activated" data-testid="integration-activated-status">
<template #cell(active)="{ item }">
<span v-if="item.active" data-testid="integration-activated-status">
<gl-icon
v-gl-tooltip
name="check-circle-filled"
......@@ -104,6 +111,10 @@ export default {
{{ $options.i18n.status.disabled.name }}
</span>
</template>
<template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" />
</template>
</gl-table>
</div>
</template>
<script>
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue';
......@@ -19,19 +21,52 @@ export default {
prometheus: {
default: {},
},
projectPath: {
default: '',
},
},
apollo: {
integrations: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
query: getIntegrationsQuery,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {};
return {
list,
};
},
error() {
this.errored = true;
},
},
},
data() {
return {
errored: false,
integrations: {},
};
},
computed: {
integrations() {
loading() {
return this.$apollo.queries.integrations.loading;
},
intergrationsOptionsOld() {
return [
{
name: s__('AlertSettings|HTTP endpoint'),
type: s__('AlertsIntegrations|HTTP endpoint'),
activated: this.generic.activated,
active: this.generic.activated,
},
{
name: s__('AlertSettings|External Prometheus'),
type: s__('AlertsIntegrations|Prometheus'),
activated: this.prometheus.activated,
active: this.prometheus.activated,
},
];
},
......@@ -41,7 +76,10 @@ export default {
<template>
<div>
<integrations-list :integrations="integrations" />
<integrations-list
:integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld"
:loading="loading"
/>
<settings-form-new v-if="glFeatures.httpIntegrationsList" />
<settings-form-old v-else />
</div>
......
fragment IntegrationItem on AlertManagementIntegration {
id
type
active
name
url
token
apiUrl
}
#import "../fragments/integration_item.fragment.graphql"
query getIntegrations($projectPath: ID!) {
project(fullPath: $projectPath) {
alertManagementIntegrations {
nodes {
...IntegrationItem
}
}
}
}
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import AlertSettingsWrapper from './components/alerts_settings_wrapper.vue';
Vue.use(VueApollo);
export default el => {
if (!el) {
return null;
......@@ -24,8 +28,22 @@ export default el => {
opsgenieMvcFormPath,
opsgenieMvcEnabled,
opsgenieMvcTargetUrl,
projectPath,
} = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
cacheConfig: {},
},
),
});
apolloProvider.clients.defaultClient.cache.writeData({
data: {},
});
return new Vue({
el,
provide: {
......@@ -51,7 +69,9 @@ export default el => {
opsgenieMvcTargetUrl,
opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable),
},
projectPath,
},
apolloProvider,
components: {
AlertSettingsWrapper,
},
......
......@@ -103,7 +103,7 @@ export default {
>
{{ $options.translations.newFlagAlert }}
</gl-alert>
<gl-loading-icon v-if="isLoading" />
<gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-7" />
<template v-else-if="!isLoading && !hasError">
<gl-alert v-if="deprecatedAndEditable" variant="warning" :dismissible="false" class="gl-my-5">
......
......@@ -8,13 +8,12 @@
@include gl-text-gray-500;
tbody {
tr:not(.b-table-busy-slot) {
// TODO replace with gitlab/ui utilities: https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1791
tr:not(.b-table-busy-slot):not(.b-table-empty-row) {
&:hover {
border-top-style: double;
@include gl-border-t-double;
td {
border-bottom-style: initial;
@include gl-border-b-initial;
}
}
}
......@@ -22,7 +21,7 @@
tr {
&:focus {
outline: none;
@include gl-outline-none;
}
td,
......@@ -118,22 +117,22 @@
}
.gl-tabs-nav {
border-bottom-width: 0;
@include gl-border-b-0;
.gl-tab-nav-item {
color: $gray-500;
@include gl-text-gray-500;
> .gl-tab-counter-badge {
color: inherit;
@include gl-reset-color;
@include gl-font-sm;
background-color: $gray-50;
@include gl-bg-gray-50;
}
}
}
@include media-breakpoint-down(xs) {
.list-header {
flex-direction: column-reverse;
@include gl-flex-direction-column-reverse;
}
.create-incident-button {
......
......@@ -29,7 +29,8 @@ module OperationsHelper
'url' => alerts_service.url,
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(@project),
'disabled' => disabled.to_s
'disabled' => disabled.to_s,
'project_path' => project_path(@project)
}
end
......
......@@ -22,7 +22,7 @@ module Projects
def execute
return unless project&.lfs_enabled? && lfs_download_object
return error("LFS file with oid #{lfs_oid} has invalid attributes") unless lfs_download_object.valid?
return link_existing_lfs_object! if Feature.enabled?(:lfs_link_existing_object, project, default_enabled: true) && lfs_size > LARGE_FILE_SIZE && lfs_object
return link_existing_lfs_object! if lfs_size > LARGE_FILE_SIZE && lfs_object
wrap_download_errors do
download_lfs_file!
......
#!/usr/bin/env bash
cd $(dirname $0)/..
cd $(dirname $0)/.. || exit 1
if [ -n "$SIDEKIQ_WORKERS" ] ; then
exec bin/background_jobs_sk_cluster "$@"
......
#!/bin/sh
cd $(dirname $0)/..
cd $(dirname $0)/.. || exit 1
app_root=$(pwd)
mail_room_pidfile="$app_root/tmp/pids/mail_room.pid"
......
#!/bin/sh
cd $(dirname $0)/..
cd $(dirname $0)/.. || exit 1
app_root=$(pwd)
unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
......
---
title: Execute `exit 1` when shell script `cd` fails
merge_request: 46122
author: Peter Dave Hello
type: other
---
title: Make loading icon on feature flag edit page larger
merge_request: 46268
author:
type: fixed
---
name: lfs_link_existing_object
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41770
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249246
group: group::source code
type: development
default_enabled: true
......@@ -208,8 +208,8 @@ ask an administrator to execute the following commands:
```shell
> sudo gitlab-rails console # Login to Rails console of GitLab instance.
> Feature.enabled?(:disable_merge_trains) # Check if it's disabled or not.
> Feature.enable(:disable_merge_trains) # Disable Merge Trains.
> Feature.disable(:disable_merge_trains) # Enable Merge Trains.
> Feature.enable(:disable_merge_trains) # Enable Merge Trains.
> Feature.disable(:disable_merge_trains) # Disable Merge Trains.
```
When you disable this feature, all existing merge trains are cancelled and
......
......@@ -6,7 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Incidents
Incidents are critical entities in incident management workflows. They represent a service disruption or outage that needs to be restored urgently. GitLab provides tools for the triage, response, and remediation of incidents.
Incidents are critical entities in incident management workflows. They represent
a service disruption or outage that needs to be restored urgently. GitLab provides
tools for the triage, response, and remediation of incidents.
## Incident Creation
......@@ -14,7 +16,8 @@ You can create an incident manually or automatically.
### Create incidents manually
If you have at least Guest [permissions](../../user/permissions.md), to create an Incident, you have two options to do this manually.
If you have at least Guest [permissions](../../user/permissions.md), to create an
Incident, you have two options to do this manually.
**From the Incidents List:**
......@@ -43,17 +46,17 @@ If you have at least Guest [permissions](../../user/permissions.md), to create a
With Maintainer or higher [permissions](../../user/permissions.md), you can enable
GitLab to create incident automatically whenever an alert is triggered:
1. Navigate to **Settings > Operations > Incidents** and expand
**Incidents**:
1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**:
![Incident Management Settings](./img/incident_management_settings_v13_3.png)
1. Check the **Create an incident**
checkbox.
1. To customize the incident, select an [issue templates](../../user/project/description_templates.md#creating-issue-templates).
1. Check the **Create an incident** checkbox.
1. To customize the incident, select an
[issue template](../../user/project/description_templates.md#creating-issue-templates).
1. To send [an email notification](alert_notifications.md#email-notifications) to users
with [Developer permissions](../../user/permissions.md), select
**Send a separate email notification to Developers**. Email notifications will also be sent to users with **Maintainer** and **Owner** permissions.
**Send a separate email notification to Developers**. Email notifications are
also sent to users with **Maintainer** and **Owner** permissions.
1. Click **Save changes**.
### Create incidents via the PagerDuty webhook
......@@ -183,10 +186,22 @@ un-threaded and ordered chronologically, newest to oldest:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241663) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5.
After enabling **Incident SLA** in the Incident Management configuration, newly-created
incidents display a SLA (Service Level Agreement) timer showing the time remaining before
the SLA period expires. If the incident is not closed before the SLA period ends, GitLab
adds a `missed::SLA` label to the incident.
You can enable the Service Level Agreement Countdown timer on incidents to track
the Service Level Agreements (SLAs) you hold with your customers. The timer is
automatically started when the incident is created, and shows the time
remaining before the SLA period expires. To configure the timer:
1. Navigate to **Settings > Operations**.
1. Scroll to **Incidents** and click **Expand**, then select the
**Incident settings** tab.
1. Select **Activate "time to SLA" countdown timer**.
1. Set a time limit in increments of 15 minutes.
1. Click **Save changes**.
After you enable the SLA countdown timer, the **Time to SLA** attribute is displayed
as a column in the Incidents List, and as a field on newly created Incidents. If
the incident isn't closed before the SLA period ends, GitLab adds a `missed::SLA`
label to the incident.
## Incident Actions
......@@ -194,15 +209,18 @@ There are different actions available to help triage and respond to incidents.
### Assign incidents
Assign incidents to users that are actively responding. Select **Edit** in the right-hand side bar to select or deselect assignees.
Assign incidents to users that are actively responding. Select **Edit** in the
right-hand side bar to select or deselect assignees.
### Change severity
See [Incident List](#incident-list) for a full description of the severities available. Select **Edit** in the right-hand side bar to change the severity of an incident.
See [Incident List](#incident-list) for a full description of the severity levels available.
Select **Edit** in the right-hand side bar to change the severity of an incident.
### Add a to-do item
Add a to-do for incidents that you want to track in your to-do list. Click the **Add a to do** button at the top of the right-hand side bar to add a to-do item.
Add a to-do for incidents that you want to track in your to-do list. Click the
**Add a to do** button at the top of the right-hand side bar to add a to-do item.
### Manage incidents from Slack
......
......@@ -263,9 +263,9 @@ with the older Rails version - which could cause non-GET requests to
fail for [multi-node GitLab installations](https://docs.gitlab.com/omnibus/update/#multi-node--ha-deployment).
So, if you are using multiple Rails servers and specifically upgrading from 13.0,
all servers must first be upgraded to 13.1.0 before upgrading to later versions:
all servers must first be upgraded to 13.1.X before upgrading to 13.2.0 or later:
1. Ensure all GitLab web nodes are on GitLab 13.1.0.
1. Ensure all GitLab web nodes are on GitLab 13.1.X.
1. Optionally, enable the `global_csrf_token` feature flag to enable new
method of CSRF token generation:
......
......@@ -34,11 +34,6 @@ export default {
required: false,
default: () => ({}),
},
projectFullPath: {
type: String,
required: false,
default: '',
},
hasVulnerabilities: {
type: Boolean,
required: false,
......@@ -86,14 +81,13 @@ export default {
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" />
</div>
<project-pipeline-status :pipeline="pipeline" />
<vulnerabilities-count-list :project-full-path="projectFullPath" :filters="filters" />
<vulnerabilities-count-list :filters="filters" />
</template>
<template #sticky>
<filters @filterChange="handleFilterChange" />
</template>
<project-vulnerabilities-app
:dashboard-documentation="dashboardDocumentation"
:project-full-path="projectFullPath"
:filters="filters"
/>
</security-dashboard-layout>
......
<script>
import { GlLink } from '@gitlab/ui';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import PipelineStatusBadge from './pipeline_status_badge.vue';
import projectAutoFixMrsCountQuery from '../graphql/project_auto_fix_mrs_count.query.graphql';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
......@@ -10,6 +12,24 @@ export default {
TimeAgoTooltip,
PipelineStatusBadge,
},
mixins: [glFeatureFlagsMixin()],
inject: ['projectFullPath', 'autoFixMrsPath'],
apollo: {
autoFixMrsCount: {
query: projectAutoFixMrsCountQuery,
variables() {
return {
fullPath: this.projectFullPath,
};
},
update(data) {
return data?.project?.mergeRequests?.count || 0;
},
skip() {
return !this.glFeatures.securityAutoFix;
},
},
},
props: {
pipeline: { type: Object, required: true },
},
......@@ -22,7 +42,9 @@ export default {
title: __(
'The Security Dashboard shows the results of the last successful pipeline run on the default branch.',
),
label: __('Last updated'),
lastUpdated: __('Last updated'),
autoFixSolutions: s__('AutoRemediation|Auto-fix solutions'),
autoFixMrsLink: s__('AutoRemediation|%{mrsCount} ready for review'),
},
};
</script>
......@@ -33,10 +55,20 @@ export default {
<div
class="gl-display-flex gl-align-items-center gl-border-solid gl-border-1 gl-border-gray-100 gl-p-6"
>
<span class="gl-font-weight-bold">{{ $options.i18n.label }}</span>
<time-ago-tooltip class="gl-px-3" :time="pipeline.createdAt" />
<gl-link :href="pipeline.path" target="_blank">#{{ pipeline.id }}</gl-link>
<pipeline-status-badge :pipeline="pipeline" class="gl-ml-3" />
<div class="gl-mr-6">
<span class="gl-font-weight-bold gl-mr-3">{{ $options.i18n.lastUpdated }}</span>
<span class="gl-white-space-nowrap">
<time-ago-tooltip class="gl-pr-3" :time="pipeline.createdAt" />
<gl-link :href="pipeline.path" target="_blank">#{{ pipeline.id }}</gl-link>
<pipeline-status-badge :pipeline="pipeline" class="gl-ml-3" />
</span>
</div>
<div v-if="autoFixMrsCount" data-testid="auto-fix-mrs-link">
<span class="gl-font-weight-bold gl-mr-3">{{ $options.i18n.autoFixSolutions }}</span>
<gl-link :href="autoFixMrsPath" target="_blank" class="gl-white-space-nowrap">{{
sprintf($options.i18n.autoFixMrsLink, { mrsCount: autoFixMrsCount })
}}</gl-link>
</div>
</div>
</div>
</template>
......@@ -16,11 +16,8 @@ export default {
GlIntersectionObserver,
VulnerabilityList,
},
inject: ['projectFullPath'],
props: {
projectFullPath: {
type: String,
required: true,
},
filters: {
type: Object,
required: false,
......
......@@ -6,11 +6,8 @@ export default {
components: {
VulnerabilityCountListLayout,
},
inject: ['projectFullPath'],
props: {
projectFullPath: {
type: String,
required: true,
},
filters: {
type: Object,
required: false,
......
......@@ -55,8 +55,9 @@ export default (el, dashboardType) => {
securityBuildsFailedCount: Number(securityBuildsFailedCount),
securityBuildsFailedPath,
};
props.projectFullPath = el.dataset.projectFullPath;
provide.projectFullPath = el.dataset.projectFullPath;
provide.autoFixDocumentation = el.dataset.autoFixDocumentation;
provide.autoFixMrsPath = el.dataset.autoFixMrsPath;
} else if (dashboardType === DASHBOARD_TYPES.GROUP) {
component = FirstClassGroupSecurityDashboard;
props.groupFullPath = el.dataset.groupFullPath;
......
query autoFixMrsCount($fullPath: ID!) {
project(fullPath: $fullPath) {
mergeRequests(labels: "GitLab-auto-fix", state: opened) {
count
}
}
}
......@@ -208,7 +208,8 @@ module EE
not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'),
no_pipeline_run_scanners_help_path: new_project_pipeline_path(project),
security_dashboard_help_path: help_page_path('user/application_security/security_dashboard/index'),
auto_fix_documentation: help_page_path('user/application_security/index', anchor: 'auto-fix-merge-requests')
auto_fix_documentation: help_page_path('user/application_security/index', anchor: 'auto-fix-merge-requests'),
auto_fix_mrs_path: project_merge_requests_path(@project, label_name: 'GitLab-auto-fix')
}.merge!(security_dashboard_pipeline_data(project))
end
end
......
......@@ -17,9 +17,10 @@ module EE
override :exceeded_size
# @param change_size [int] in bytes
def exceeded_size(change_size = 0)
exceeded_size = super
exceeded_size -= remaining_additional_purchased_storage if additional_repo_storage_available?
exceeded_size
size = super
size -= remaining_additional_purchased_storage if additional_repo_storage_available?
[size, 0].max
end
private
......@@ -38,8 +39,16 @@ module EE
namespace&.additional_purchased_storage_size&.megabytes.to_i
end
def current_project_excess
[current_size - limit, 0].max
end
def total_excess_without_current_project
total_repository_size_excess - current_project_excess
end
def remaining_additional_purchased_storage
additional_purchased_storage - total_repository_size_excess
additional_purchased_storage - total_excess_without_current_project
end
end
end
......
......@@ -19,12 +19,12 @@ const props = {
id: '214',
path: '/mixed-vulnerabilities/dependency-list-test-01/-/pipelines/214',
},
projectFullPath: '/group/project',
securityDashboardHelpPath: '/security/dashboard/help-path',
vulnerabilitiesExportEndpoint: '/vulnerabilities/exports',
};
const provide = {
projectFullPath: '/group/project',
dashboardDocumentation: '/help/docs',
autoFixDocumentation: '/auto/fix/documentation',
emptyStateSvgPath: '/svgs/empty/svg',
......
import { merge } from 'lodash';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import ProjectPipelineStatus from 'ee/security_dashboard/components/project_pipeline_status.vue';
......@@ -18,12 +20,29 @@ describe('Project Pipeline Status Component', () => {
const findPipelineStatusBadge = () => wrapper.find(PipelineStatusBadge);
const findTimeAgoTooltip = () => wrapper.find(TimeAgoTooltip);
const findLink = () => wrapper.find(GlLink);
const findAutoFixMrsLink = () => wrapper.findByTestId('auto-fix-mrs-link');
const createWrapper = ({ props = {}, options = {} } = {}) => {
return shallowMount(ProjectPipelineStatus, {
propsData: { ...DEFAULT_PROPS, ...props },
...options,
});
const createWrapper = (options = {}) => {
return extendedWrapper(
shallowMount(
ProjectPipelineStatus,
merge(
{},
{
propsData: DEFAULT_PROPS,
provide: {
projectFullPath: '/group/project',
glFeatures: { securityAutoFix: true },
autoFixMrsPath: '/merge_requests?label_name=GitLab-auto-fix',
},
data() {
return { autoFixMrsCount: 0 };
},
},
options,
),
),
);
};
afterEach(() => {
......@@ -56,7 +75,7 @@ describe('Project Pipeline Status Component', () => {
describe('when no pipeline has run', () => {
beforeEach(() => {
wrapper = createWrapper({ props: { pipeline: { path: '' } } });
wrapper = createWrapper({ propsData: { pipeline: { path: '' } } });
});
it('should not show the project_pipeline_status component', () => {
......@@ -65,4 +84,32 @@ describe('Project Pipeline Status Component', () => {
expect(findPipelineStatusBadge().exists()).toBe(false);
});
});
describe('auto-fix MRs', () => {
describe('when there are auto-fix MRs', () => {
beforeEach(() => {
wrapper = createWrapper({
data() {
return { autoFixMrsCount: 12 };
},
});
});
it('renders the auto-fix container', () => {
expect(findAutoFixMrsLink().exists()).toBe(true);
});
it('renders a link to open auto-fix MRs if any', () => {
const link = findAutoFixMrsLink().find(GlLink);
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe('/merge_requests?label_name=GitLab-auto-fix');
});
});
it('does not render the link if there are no open auto-fix MRs', () => {
wrapper = createWrapper();
expect(findAutoFixMrsLink().exists()).toBe(false);
});
});
});
......@@ -12,10 +12,12 @@ describe('Vulnerabilities app component', () => {
const createWrapper = ({ props = {}, $apollo = apolloMock } = {}, options = {}) => {
wrapper = shallowMount(ProjectVulnerabilitiesApp, {
provide: {
projectFullPath: '#',
},
propsData: {
dashboardDocumentation: '#',
emptyStateSvgPath: '#',
projectFullPath: '#',
...props,
},
mocks: {
......
......@@ -9,7 +9,7 @@ describe('Vulnerabilities count list component', () => {
const createWrapper = ({ query } = {}) => {
return shallowMount(VulnerabilityCountList, {
propsData: {
provide: {
projectFullPath: '/root/security-project',
},
mocks: {
......
......@@ -155,7 +155,8 @@ RSpec.describe ProjectsHelper do
security_dashboard_help_path: '/help/user/application_security/security_dashboard/index',
not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'),
no_pipeline_run_scanners_help_path: "/#{project.full_path}/-/pipelines/new",
auto_fix_documentation: help_page_path('user/application_security/index', anchor: 'auto-fix-merge-requests')
auto_fix_documentation: help_page_path('user/application_security/index', anchor: 'auto-fix-merge-requests'),
auto_fix_mrs_path: end_with('/merge_requests?label_name=GitLab-auto-fix')
}
end
......
......@@ -136,41 +136,69 @@ RSpec.describe Gitlab::RepositorySizeChecker do
stub_feature_flags(namespace_storage_limit: false)
end
context 'when current size + total repository size excess are below or equal to the limit + additional purchased storage' do
let(:current_size) { 50 }
context 'with additional purchased storage' do
let(:total_repository_size_excess) { 10 }
let(:additional_purchased_storage) { 10 }
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
context 'when no change size provided' do
context 'when current size + total repository size excess is below the limit (additional purchase storage not used)' do
let(:current_size) { limit - 1 }
context 'when current size + total repository size excess are over the limit + additional purchased storage' do
let(:current_size) { 51 }
let(:total_repository_size_excess) { 10 }
let(:additional_purchased_storage) { 10 }
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
it 'returns 1' do
expect(subject.exceeded_size).to eq(1.megabytes)
end
end
context 'when current size + total repository size excess is equal to the limit (additional purchase storage not used)' do
let(:current_size) { limit }
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
context 'when there is remaining additional purchased storage (current size + other project excess use some additional purchased storage)' do
let(:current_size) { limit + 1 }
context 'when change size will be over the limit' do
let(:current_size) { 50 }
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
it 'returns 1' do
expect(subject.exceeded_size(1.megabytes)).to eq(1.megabytes)
context 'when additional purchased storage is depleted (current size + other project excess exceed additional purchased storage)' do
let(:total_repository_size_excess) { 15 }
let(:current_size) { 61 }
it 'returns a positive number' do
expect(subject.exceeded_size).to eq(5.megabytes)
end
end
end
end
context 'when change size will not be over the limit' do
let(:current_size) { 49 }
context 'when a change size is provided' do
let(:change_size) { 1.megabyte }
context 'when current size + total repository size excess is below the limit (additional purchase storage not used)' do
let(:current_size) { limit - 1 }
it 'returns zero' do
expect(subject.exceeded_size(change_size)).to eq(0)
end
end
it 'returns zero' do
expect(subject.exceeded_size(1.megabytes)).to eq(0)
context 'when current size + total repository size excess is equal to the limit (additional purchase storage depleted)' do
let(:current_size) { limit }
it 'returns a positive number' do
expect(subject.exceeded_size(change_size)).to eq(1.megabyte)
end
end
end
end
context 'without additional purchased storage' do
include_examples 'checker size exceeded'
end
end
context 'with feature flag :additional_repo_storage_by_namespace disabled' do
......
......@@ -37,7 +37,9 @@ module Gitlab
# @param change_size [int] in bytes
def exceeded_size(change_size = 0)
current_size + change_size - limit
size = current_size + change_size - limit
[size, 0].max
end
def error_message
......
......@@ -3952,6 +3952,12 @@ msgstr ""
msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found."
msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
msgid "AutoRemediation|If you're using dependency and/or container scanning, and auto-fix is enabled, auto-fix automatically creates merge requests with fixes to vulnerabilities."
msgstr ""
......
#!/usr/bin/env bash
cd "$(dirname "$0")/.."
cd "$(dirname "$0")/.." || exit 1
echo "=> Linting documents at path $(pwd) as $(whoami)..."
echo
ERRORCODE=0
......
......@@ -8,12 +8,12 @@ import { trackAlertIntegrationsViewsOptions } from '~/alerts_settings/constants'
const mockIntegrations = [
{
activated: true,
active: true,
name: 'Integration 1',
type: 'HTTP endpoint',
},
{
activated: false,
active: false,
name: 'Integration 2',
type: 'HTTP endpoint',
},
......
......@@ -3,8 +3,6 @@ import { GlForm, GlFormSelect, GlCollapse, GlFormInput } from '@gitlab/ui';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_new.vue';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/alerts_settings/services');
describe('AlertsSettingsFormNew', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import { defaultAlertSettingsConfig } from './util';
import mockIntegrations from './mocks/integrations.json';
jest.mock('~/alerts_settings/services');
describe('AlertsSettingsFormWrapper', () => {
describe('AlertsSettingsWrapper', () => {
let wrapper;
const createComponent = (data = {}, provide = {}) => {
wrapper = shallowMount(AlertsSettingsWrapper, {
const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
wrapper = mount(AlertsSettingsWrapper, {
data() {
return { ...data };
},
......@@ -20,6 +23,16 @@ describe('AlertsSettingsFormWrapper', () => {
glFeatures: { httpIntegrationsList: false },
...provide,
},
mocks: {
$apollo: {
query: jest.fn(),
queries: {
integrations: {
loading,
},
},
},
},
});
};
......@@ -30,19 +43,41 @@ describe('AlertsSettingsFormWrapper', () => {
}
});
describe('with default values', () => {
it('renders alerts integrations list and old form by default', () => {
describe('with httpIntegrationsList feature flag disabled', () => {
it('renders data driven alerts integrations list and old form by default', () => {
createComponent();
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(false);
});
});
it('renders alerts integrations list and new form when httpIntegrationsList feature flag is enabled', () => {
createComponent({}, { glFeatures: { httpIntegrationsList: true } });
describe('with httpIntegrationsList feature flag enabled', () => {
it('renders the GraphQL alerts integrations list and new form', () => {
createComponent({ provide: { glFeatures: { httpIntegrationsList: true } } });
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(false);
expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(true);
});
it('uses a loading state inside the IntegrationsList table', () => {
createComponent({
data: { integrations: {} },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: true,
});
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(findLoader().exists()).toBe(true);
});
it('renders the IntegrationsList table using the API data', () => {
createComponent({
data: { integrations: { list: mockIntegrations } },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
expect(findLoader().exists()).toBe(false);
expect(findIntegrations()).toHaveLength(mockIntegrations.length);
});
});
});
[
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/7",
"type": "HTTP",
"active": true,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/eddd36969b2d3d6a.json",
"token": "7eb24af194116411ec8d66b58c6b0d2e",
"apiUrl": null
},
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/6",
"type": "HTTP",
"active": false,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/abce123.json",
"token": "8639e0ce06c731b00ee3e8dcdfd14fe0",
"apiUrl": null
},
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/5",
"type": "HTTP",
"active": false,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/bcd64c85f918a2e2.json",
"token": "5c8101533d970a55d5c105f8abff2192",
"apiUrl": null
},
{
"id": "gid://gitlab/PrometheusService/12",
"type": "PROMETHEUS",
"active": true,
"name": "Prometheus",
"url": "http://192.168.1.152:3000/root/autodevops/prometheus/alerts/notify.json",
"token": "0b18c37caa8fe980799b349916fe5ddf",
"apiUrl": "https://another-url-2.com"
}
]
......@@ -43,7 +43,8 @@ RSpec.describe OperationsHelper do
'prometheus_api_url' => nil,
'prometheus_activated' => 'false',
'prometheus_url' => notify_project_prometheus_alerts_url(project, format: :json),
'disabled' => 'false'
'disabled' => 'false',
'project_path' => project_path(project)
)
end
end
......
......@@ -241,18 +241,6 @@ RSpec.describe Projects::LfsPointers::LfsDownloadService do
context 'and first fragments are the same' do
let(:lfs_content) { existing_lfs_object.file.read }
context 'when lfs_link_existing_object feature flag disabled' do
before do
stub_feature_flags(lfs_link_existing_object: false)
end
it 'does not call link_existing_lfs_object!' do
expect(subject).not_to receive(:link_existing_lfs_object!)
subject.execute
end
end
it 'returns success' do
expect(subject.execute).to eq({ status: :success })
end
......
......@@ -17,35 +17,58 @@ RSpec.shared_examples 'checker size not over limit' do
end
RSpec.shared_examples 'checker size exceeded' do
context 'when current size is below or equal to the limit' do
let(:current_size) { 50 }
context 'when no change size provided' do
context 'when current size is below the limit' do
let(:current_size) { limit - 1 }
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
end
context 'when current size is over the limit' do
let(:current_size) { 51 }
context 'when current size is equal to the limit' do
let(:current_size) { limit }
it 'returns zero' do
expect(subject.exceeded_size).to eq(1.megabytes)
it 'returns zero' do
expect(subject.exceeded_size).to eq(0)
end
end
end
context 'when change size will be over the limit' do
let(:current_size) { 50 }
context 'when current size is over the limit' do
let(:current_size) { limit + 1 }
let(:total_repository_size_excess) { 1 }
it 'returns zero' do
expect(subject.exceeded_size(1.megabytes)).to eq(1.megabytes)
it 'returns a positive number' do
expect(subject.exceeded_size).to eq(1.megabyte)
end
end
end
context 'when change size will not be over the limit' do
let(:current_size) { 49 }
context 'when a change size is provided' do
let(:change_size) { 1.megabyte }
context 'when change size will be over the limit' do
let(:current_size) { limit }
it 'returns a positive number' do
expect(subject.exceeded_size(change_size)).to eq(1.megabyte)
end
end
context 'when change size will be at the limit' do
let(:current_size) { limit - 1 }
it 'returns zero' do
expect(subject.exceeded_size(change_size)).to eq(0)
end
end
context 'when change size will be under the limit' do
let(:current_size) { limit - 2 }
it 'returns zero' do
expect(subject.exceeded_size(1.megabytes)).to eq(0)
it 'returns zero' do
expect(subject.exceeded_size(change_size)).to eq(0)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment