Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-23 12:08:41 +00:00
parent c71c2ba4c2
commit 097eb36475
49 changed files with 774 additions and 215 deletions

View File

@ -229,7 +229,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/index.md @axil
/doc/administration/instance_limits.md @axil
/doc/administration/instance_review.md @kpaizee
/doc/administration/integration/kroki.md @kpaizee
/doc/administration/integration/kroki.md @msedlakjakubowski
/doc/administration/integration/mailgun.md @kpaizee
/doc/administration/integration/plantuml.md @aqualls
/doc/administration/integration/terminal.md @kpaizee

View File

@ -483,7 +483,6 @@ Layout/HashAlignment:
- 'lib/backup/gitaly_backup.rb'
- 'lib/banzai/filter/references/abstract_reference_filter.rb'
- 'lib/banzai/reference_redactor.rb'
- 'lib/bulk_imports/projects/pipelines/project_attributes_pipeline.rb'
- 'lib/gitlab/abuse.rb'
- 'lib/gitlab/access.rb'
- 'lib/gitlab/application_rate_limiter.rb'

View File

@ -3314,7 +3314,6 @@ Layout/LineLength:
- 'lib/bulk_imports/common/pipelines/wiki_pipeline.rb'
- 'lib/bulk_imports/common/transformers/prohibited_attributes_transformer.rb'
- 'lib/bulk_imports/groups/loaders/group_loader.rb'
- 'lib/bulk_imports/projects/pipelines/project_attributes_pipeline.rb'
- 'lib/bulk_imports/projects/pipelines/project_pipeline.rb'
- 'lib/bulk_imports/projects/pipelines/repository_pipeline.rb'
- 'lib/bulk_imports/projects/transformers/project_attributes_transformer.rb'
@ -4794,7 +4793,6 @@ Layout/LineLength:
- 'spec/lib/bulk_imports/pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/project_feature_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/protected_branches_pipeline_spec.rb'

View File

@ -188,7 +188,6 @@ RSpec/ExpectInHook:
- 'spec/lib/backup/manager_spec.rb'
- 'spec/lib/banzai/reference_redactor_spec.rb'
- 'spec/lib/bulk_imports/ndjson_pipeline_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb'
- 'spec/lib/container_registry/gitlab_api_client_spec.rb'
- 'spec/lib/file_size_validator_spec.rb'
- 'spec/lib/gitlab/alert_management/fingerprint_spec.rb'

View File

@ -1 +1 @@
080f9fb5c6d7e1e5e3c3e2a5202b3f0a83ddd180
94055b253d05bc04f533c977be892b0cd6f225ea

View File

@ -1 +1 @@
14.6.0
14.6.1

View File

@ -0,0 +1,249 @@
<script>
import {
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
GlFormInput,
GlFormText,
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, s__ } from '~/locale';
export default {
name: 'InactiveProjectDeletionForm',
components: {
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
GlFormInput,
GlFormText,
GlLink,
GlSprintf,
},
props: {
deleteInactiveProjects: {
type: Boolean,
required: false,
default: false,
},
inactiveProjectsDeleteAfterMonths: {
type: Number,
required: false,
default: 2,
},
inactiveProjectsMinSizeMb: {
type: Number,
required: false,
default: 0,
},
inactiveProjectsSendWarningEmailAfterMonths: {
type: Number,
required: false,
default: 1,
},
},
data() {
return {
enabled: this.deleteInactiveProjects,
deleteAfterMonths: this.inactiveProjectsDeleteAfterMonths,
minSizeMb: this.inactiveProjectsMinSizeMb,
sendWarningEmailAfterMonths: this.inactiveProjectsSendWarningEmailAfterMonths,
};
},
computed: {
isMinSizeMbValid() {
return parseInt(this.minSizeMb, 10) >= 0;
},
isDeleteAfterMonthsValid() {
return (
parseInt(this.deleteAfterMonths, 10) > 0 &&
parseInt(this.deleteAfterMonths, 10) > parseInt(this.sendWarningEmailAfterMonths, 10)
);
},
isSendWarningEmailAfterMonthsValid() {
return parseInt(this.sendWarningEmailAfterMonths, 10) > 0;
},
},
methods: {
checkValidity(ref, feedback, valid) {
// These form fields are used within a HAML created form and we don't have direct access to the submit button
// So we set the validity of the field so the HAML form can't be submitted until this is set back to blank
if (valid) {
ref.$el.setCustomValidity('');
} else {
ref.$el.setCustomValidity(feedback);
}
},
},
i18n: {
checkboxLabel: s__('AdminSettings|Delete inactive projects'),
checkboxHelp: s__(
'AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}',
),
checkboxHelpDocLink: helpPagePath('administration/inactive_project_deletion'),
minSizeMbLabel: s__('AdminSettings|When to delete inactive projects'),
minSizeMbDescription: s__('AdminSettings|Delete inactive projects that exceed'),
minSizeMbInvalidFeedback: s__('AdminSettings|Minimum size must be at least 0.'),
deleteAfterMonthsLabel: s__('AdminSettings|Delete project after'),
deleteAfterMonthsInvalidFeedback: s__(
"AdminSettings|You can't delete projects before the warning email is sent.",
),
sendWarningEmailAfterMonthsLabel: s__('AdminSettings|Send warning email'),
sendWarningEmailAfterMonthsDescription: s__(
'AdminSettings|Send email to maintainers after project is inactive for',
),
sendWarningEmailAfterMonthsHelp: s__(
'AdminSettings|Requires %{linkStart}email notifications%{linkEnd}',
),
sendWarningEmailAfterMonthsDocLink: helpPagePath('user/profile/notifications'),
sendWarningEmailAfterMonthsInvalidFeedback: s__(
'AdminSettings|Setting must be greater than 0.',
),
mbAppend: __('MB'),
monthsAppend: __('months'),
},
};
</script>
<template>
<div>
<gl-form-group>
<input name="application_setting[delete_inactive_projects]" type="hidden" :value="enabled" />
<gl-form-checkbox v-model="enabled">
{{ $options.i18n.checkboxLabel }}
<template #help>
<gl-sprintf :message="$options.i18n.checkboxHelp">
<template #link="{ content }">
<gl-link :href="$options.i18n.checkboxHelpDocLink" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</template>
</gl-form-checkbox>
</gl-form-group>
<div v-if="enabled" class="gl-ml-6" data-testid="inactive-project-deletion-settings">
<gl-form-group
:label="$options.i18n.minSizeMbLabel"
:state="isMinSizeMbValid"
data-testid="min-size-group"
>
<template #invalid-feedback>
<div class="gl-w-40p">{{ $options.i18n.minSizeMbInvalidFeedback }}</div>
</template>
<gl-form-text class="gl-mt-0 gl-mb-3 gl-text-body!">
{{ $options.i18n.minSizeMbDescription }}
</gl-form-text>
<gl-form-input-group data-testid="min-size-input-group">
<gl-form-input
ref="minSizeMbInput"
v-model="minSizeMb"
:state="isMinSizeMbValid"
name="application_setting[inactive_projects_min_size_mb]"
size="md"
type="number"
:min="0"
data-testid="min-size-input"
@change="
checkValidity(
$refs.minSizeMbInput,
$options.i18n.minSizeMbInvalidFeedback,
isMinSizeMbValid,
)
"
/>
<template #append>
<div class="input-group-text">{{ $options.i18n.mbAppend }}</div>
</template>
</gl-form-input-group>
</gl-form-group>
<div class="gl-pl-6 gl-border-l">
<gl-form-group
:label="$options.i18n.deleteAfterMonthsLabel"
:state="isDeleteAfterMonthsValid"
data-testid="delete-after-months-group"
>
<template #invalid-feedback>
<div class="gl-w-30p">{{ $options.i18n.deleteAfterMonthsInvalidFeedback }}</div>
</template>
<gl-form-input-group data-testid="delete-after-months-input-group">
<gl-form-input
ref="deleteAfterMonthsInput"
v-model="deleteAfterMonths"
:state="isDeleteAfterMonthsValid"
name="application_setting[inactive_projects_delete_after_months]"
size="sm"
type="number"
:min="0"
data-testid="delete-after-months-input"
@change="
checkValidity(
$refs.deleteAfterMonthsInput,
$options.i18n.deleteAfterMonthsInvalidFeedback,
isDeleteAfterMonthsValid,
)
"
/>
<template #append>
<div class="input-group-text">{{ $options.i18n.monthsAppend }}</div>
</template>
</gl-form-input-group>
</gl-form-group>
<gl-form-group
:label="$options.i18n.sendWarningEmailAfterMonthsLabel"
:state="isSendWarningEmailAfterMonthsValid"
data-testid="send-warning-email-after-months-group"
>
<template #invalid-feedback>
<div class="gl-w-30p">
{{ $options.i18n.sendWarningEmailAfterMonthsInvalidFeedback }}
</div>
</template>
<gl-form-text class="gl-max-w-26 gl-mt-0 gl-mb-3 gl-text-body!">
{{ $options.i18n.sendWarningEmailAfterMonthsDescription }}
</gl-form-text>
<gl-form-input-group data-testid="send-warning-email-after-months-input-group">
<gl-form-input
ref="sendWarningEmailAfterMonthsInput"
v-model="sendWarningEmailAfterMonths"
:state="isSendWarningEmailAfterMonthsValid"
name="application_setting[inactive_projects_send_warning_email_after_months]"
size="sm"
type="number"
:min="0"
data-testid="send-warning-email-after-months-input"
@change="
checkValidity(
$refs.sendWarningEmailAfterMonthsInput,
$options.i18n.sendWarningEmailAfterMonthsInvalidFeedback,
isSendWarningEmailAfterMonthsValid,
)
"
/>
<template #append>
<div class="input-group-text">{{ $options.i18n.monthsAppend }}</div>
</template>
</gl-form-input-group>
<template #description>
<gl-sprintf :message="$options.i18n.sendWarningEmailAfterMonthsHelp">
<template #link="{ content }">
<gl-link :href="$options.i18n.sendWarningEmailAfterMonthsDocLink" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</template>
</gl-form-group>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,36 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Form from './components/form.vue';
export default () => {
const el = document.querySelector('.js-inactive-project-deletion-form');
if (!el) {
return false;
}
const {
deleteInactiveProjects,
inactiveProjectsDeleteAfterMonths,
inactiveProjectsMinSizeMb,
inactiveProjectsSendWarningEmailAfterMonths,
} = el.dataset;
return new Vue({
el,
name: 'InactiveProjectDeletion',
render(createElement) {
return createElement(Form, {
props: {
deleteInactiveProjects: parseBoolean(deleteInactiveProjects),
inactiveProjectsDeleteAfterMonths: parseInt(inactiveProjectsDeleteAfterMonths, 10),
inactiveProjectsMinSizeMb: parseInt(inactiveProjectsMinSizeMb, 10),
inactiveProjectsSendWarningEmailAfterMonths: parseInt(
inactiveProjectsSendWarningEmailAfterMonths,
10,
),
},
});
},
});
};

View File

@ -56,6 +56,9 @@ export default {
calculatedTimeTilNextRun() {
return timeTilRun(this.expirationPolicy?.next_run);
},
expireIconName() {
return this.failedDelete ? 'expire' : 'clock';
},
},
statusPopoverOptions: {
triggers: 'hover',
@ -75,7 +78,7 @@ export default {
class="gl-display-inline-flex gl-align-items-center"
>
<div class="gl-display-inline-flex gl-align-items-center">
<gl-icon name="expire" data-testid="main-icon" />
<gl-icon :name="expireIconName" data-testid="main-icon" />
</div>
<span class="gl-mx-2">
{{ statusText }}

View File

@ -107,7 +107,7 @@ export default {
<metadata-item
v-if="!hideExpirationPolicyData"
data-testid="expiration-policy"
icon="expire"
icon="clock"
:text="expirationPolicyText"
size="xl"
/>

View File

@ -0,0 +1,3 @@
import initInactiveProjectDeletion from '~/admin/application_settings/inactive_project_deletion';
initInactiveProjectDeletion();

View File

@ -20,7 +20,7 @@ $system-note-svg-size: 16px;
}
.note-wrapper {
padding: $gl-padding;
padding: $gl-padding $gl-padding-8 $gl-padding $gl-padding;
&.outlined {
@include outline-comment();

View File

@ -3,6 +3,10 @@
class Projects::UsageQuotasController < Projects::ApplicationController
before_action :authorize_read_usage_quotas!
before_action do
push_frontend_feature_flag(:container_registry_project_statistics, project)
end
layout "project_settings"
feature_category :utilization

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Admin
module ApplicationSettings
module SettingsHelper
def inactive_projects_deletion_data(settings)
{
delete_inactive_projects: settings.delete_inactive_projects.to_s,
inactive_projects_delete_after_months: settings.inactive_projects_delete_after_months,
inactive_projects_min_size_mb: settings.inactive_projects_min_size_mb,
inactive_projects_send_warning_email_after_months: settings.inactive_projects_send_warning_email_after_months
}
end
end
end
end

View File

@ -513,6 +513,10 @@ class Commit
# We don't want to do anything for `Commit` model, so this is empty.
end
# We are continuing to support `(fixup!|squash!)` here as it is the prefix
# added by `git commit --fixup` which is used by some community members.
# https://gitlab.com/gitlab-org/gitlab/-/issues/342937#note_892065311
#
DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze
def work_in_progress?

View File

@ -39,4 +39,8 @@
.form-text.text-muted
= html_escape(s_('Number of Git pushes after which %{code_start}git gc%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
.sub-section
%h4= s_("AdminSettings|Inactive project deletion")
.js-inactive-project-deletion-form{ data: inactive_projects_deletion_data(@application_setting) }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"

View File

@ -1,3 +1,6 @@
- bind_out_tag = content_tag(:span, nil, { data: { bind_out: :create_chat_team } })
- checkbox_help_text = "%s %s/%s".html_safe % [_('Mattermost URL:'), Settings.mattermost.host, bind_out_tag]
.form-group
.col-sm-2.col-form-label
= f.label :create_chat_team do
@ -5,13 +8,5 @@
= custom_icon('icon_mattermost')
%span.gl-ml-2= _('Mattermost')
.col-sm-12
.form-check.js-toggle-container
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: false }, true, false)
= f.label :create_chat_team, class: 'form-check-label' do
= _('Create a Mattermost team for this group')
%br
%small.light.js-toggle-content
= _('Mattermost URL:')
= Settings.mattermost.host
%span> /
%span{ "data-bind-out" => "create_chat_team" }
= f.gitlab_ui_checkbox_component :create_chat_team, _('Create a Mattermost team for this group'), help_text: checkbox_help_text, checkbox_options: { checked: false }, checked_value: true, unchecked_value: false

View File

@ -10,7 +10,7 @@
.row{ 'v-cloak': true }
#create-group-pane.tab-pane
= form_for @group, html: { class: 'group-form gl-show-field-errors gl-mt-3' } do |f|
= gitlab_ui_form_for @group, html: { class: 'group-form gl-show-field-errors gl-mt-3' } do |f|
= render 'new_group_fields', f: f, group_name_id: 'create-group-name'
#import-group-pane.tab-pane

View File

@ -21,4 +21,4 @@
- if can_edit_project_settings && !service_desk_enabled
.gl-mt-3
= link_to s_("ServiceDesk|Enable Service Desk"), edit_project_path(@project), class: 'gl-button btn btn-success'
= link_to s_("ServiceDesk|Enable Service Desk"), edit_project_path(@project), class: 'gl-button btn btn-confirm'

View File

@ -0,0 +1,8 @@
---
name: delayed_repository_update_mirror_worker
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87995
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362894
milestone: '15.1'
type: development
group: group::source code
default_enabled: false

View File

@ -3,7 +3,7 @@ data_category: operational
key_path: counts_monthly.aggregated_metrics.code_review_category_monthly_active_users
description: Unique users performing actions on code review events
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -3,7 +3,7 @@ data_category: optional
key_path: counts_monthly.aggregated_metrics.code_review_extension_category_monthly_active_users
description: Number of users performing i_code_review_user_vs_code_api_request event
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -3,7 +3,7 @@ data_category: optional
key_path: counts_monthly.aggregated_metrics.code_review_group_monthly_active_users
description: Number of users performing at least one of the code review events
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_start_monthly
description: The number of github projects that were enqueued to start monthy
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_success_monthly
description: The number of github projects that were successful monthly
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_failure_monthly
description: The number of github projects that failed monthly
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -3,7 +3,7 @@ data_category: optional
key_path: counts_weekly.aggregated_metrics.code_review_group_monthly_active_users
description: Number of users performing at least one of the code review events
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -3,7 +3,7 @@ data_category: optional
key_path: counts_weekly.aggregated_metrics.code_review_category_monthly_active_users
description: Unique users performing actions on code review events
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -3,7 +3,7 @@ data_category: optional
key_path: counts_weekly.aggregated_metrics.code_review_extension_category_monthly_active_users
description: Number of users performing code review extension_category events
product_section: dev
product_stage: devops::create
product_stage: create
product_group: group::code review
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_start_weekly
description: The number of github projects that were enqueued to start weekly
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_success_weekly
description: The number of github projects that were successful weekly
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -2,7 +2,7 @@
key_path: redis_hll_counters.importer.github_import_project_failure_weekly
description: The number of github projects that failed weekly
product_section: dev
product_stage: devops
product_stage: manage
product_group: group::import
product_category:
value_type: number

View File

@ -1,15 +0,0 @@
- name: "`fixup!` commit messages setting draft status of associated Merge Request" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
removal_date: "2022-06-22" # This should almost always be the 22nd of a month (YYYY-MM-22), the date of the milestone release when this feature is planned to be removed.
body: | # Do not modify this line, instead modify the lines below.
The use of `fixup!` as a commit message to trigger draft status
of the associated Merge Request is generally unused, and can cause
confusion with other uses of the term. "Draft" is the preferred
and supported trigger for triggering draft status from commit
messages, as part of our streamlining of the feature.
Support for `fixup!` is now considered deprecated, and will be
removed in GitLab 15.0.
documentation_url: "https://docs.gitlab.com/ee/user/project/merge_requests/drafts.html#mark-merge-requests-as-drafts"
issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/342937"

View File

@ -20,21 +20,24 @@ deleted, the action generates an audit event that it was performed by the first
## Configure inactive project deletion
You can configure inactive projects deletion or turn it off using the
[Application settings API](../api/settings.md#change-application-settings).
You can configure inactive projects deletion or turn it off using either:
- [The GitLab API](#using-the-api) (GitLab 15.0 and later).
- [The GitLab UI](#using-the-gitlab-ui) (GitLab 15.1 and later).
The following options are available:
- `delete_inactive_projects`: Enable or disable inactive project deletion.
- `inactive_projects_min_size_mb`: Minimum size (MB) of inactive projects to be considered for deletion.
Projects smaller in size than this threshold aren't considered inactive.
- `inactive_projects_delete_after_months`: Minimum duration (months) after which a project is scheduled for deletion if
it continues be inactive.
- `inactive_projects_send_warning_email_after_months`: Minimum duration (months) after which a deletion warning email is
sent if a project continues to be inactive. The warning email is sent to users with the Owner and Maintainer roles of
the inactive project. This duration should be less than the `inactive_projects_delete_after_months` duration.
- **Delete inactive projects** (`delete_inactive_projects`): Enable or disable inactive project deletion.
- **Delete inactive projects that exceed** (`inactive_projects_min_size_mb`): Minimum size (MB) of inactive projects to
be considered for deletion. Projects smaller in size than this threshold aren't considered inactive.
- **Delete project after** (`inactive_projects_delete_after_months`): Minimum duration (months) after which a project is
scheduled for deletion if it continues be inactive.
- **Send warning email** (`inactive_projects_send_warning_email_after_months`): Minimum duration (months) after which a
deletion warning email is sent if a project continues to be inactive. The warning email is sent to users with the
Owner and Maintainer roles of the inactive project. This duration must be less than the
**Delete project after** (`inactive_projects_delete_after_months`) duration.
For example:
For example (using the API):
- `delete_inactive_projects` enabled.
- `inactive_projects_min_size_mb` set to `50`.
@ -49,6 +52,22 @@ In this scenario, when a project's size is:
with the scheduled date of deletion.
- More than 12 months, the project is scheduled for deletion.
### Using the API
You can use the [Application settings API](../api/settings.md#change-application-settings) to configure inactive projects.
### Using the GitLab UI
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85575) in GitLab 15.1.
To configure inactive projects with the GitLab UI:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Repository maintenance**.
1. In the **Inactive project deletion** section, configure the necessary options.
1. Select **Save changes**.
## Determine when a project was last active
You can view a project's activities and determine when the project was last active in the following ways:

View File

@ -104,3 +104,13 @@ After that restart GitLab with:
```shell
sudo gitlab-ctl restart
```
### Search Sidekiq logs in Kibana
To locate a specific integration in Kibana, use the following KQL search string:
```plaintext
`json.integration_class.keyword : "Integrations::Jira" and json.project_path : "path/to/project"`
```
You can find information in `json.exception.backtrace`, `json.exception.class`, `json.exception.message`, and `json.message`.

View File

@ -91,6 +91,8 @@ set up for the integration has permission to:
Jira issue references and update comments do not work if the GitLab issue tracker is disabled.
If you [restrict IP addresses for Jira access](https://support.atlassian.com/security-and-access-policies/docs/specify-ip-addresses-for-product-access/), make sure you add your self-managed IP addresses or [GitLab.com IP range](../../user/gitlab_com/index.md#ip-range) to the allowlist in Jira.
### GitLab cannot close a Jira issue
If GitLab cannot close a Jira issue:

View File

@ -1094,21 +1094,6 @@ The predefined CI/CD variables that start with `CI_BUILD_*` were deprecated in G
**Planned removal milestone: <span class="removal-milestone">16.0</span> (2023-04-22)**
</div>
<div class="deprecation removal-150">
### `fixup!` commit messages setting draft status of associated Merge Request
The use of `fixup!` as a commit message to trigger draft status
of the associated Merge Request is generally unused, and can cause
confusion with other uses of the term. "Draft" is the preferred
and supported trigger for triggering draft status from commit
messages, as part of our streamlining of the feature.
Support for `fixup!` is now considered deprecated, and will be
removed in GitLab 15.0.
**Planned removal milestone: <span class="removal-milestone">15.0</span> (2022-06-22)**
</div>
<div class="deprecation removal-150 breaking-change">
### `projectFingerprint` in `PipelineSecurityReportFinding` GraphQL

View File

@ -116,7 +116,7 @@ You might also be interested in templates for various
### Set a default template for merge requests and issues
> `Default.md` template [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78302) in GitLab 14.8.
> `Default.md` (case insensitive) template [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78302) in GitLab 14.8.
In a project, you can choose a default description template for new issues and merge requests.
As a result, every time a new merge request or issue is created, it's pre-filled with the text you
@ -129,9 +129,9 @@ Prerequisites:
To set a default description template for merge requests, either:
- [Create a merge request template](#create-a-merge-request-template) named `Default.md` or `default.md`
- [In GitLab 14.8 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78302), [create a merge request template](#create-a-merge-request-template) named `Default.md` (case insensitive)
and save it in `.gitlab/merge_request_templates/`.
This doesn't overwrite the default template if one has been set in the project settings.
This [doesn't overwrite](#priority-of-default-description-templates) the default template if one has been set in the project settings.
- Users on GitLab Premium and higher: set the default template in project settings:
1. On the top bar, select **Menu > Projects** and find your project.
@ -142,9 +142,9 @@ To set a default description template for merge requests, either:
To set a default description template for issues, either:
- [Create an issue template](#create-an-issue-template) named `Default.md` or `default.md`
- [In GitLab 14.8 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78302), [create an issue template](#create-an-issue-template) named `Default.md` (case insensitive) [in GitLab 14.8 or higher]
and save it in `.gitlab/issue_templates/`.
This doesn't overwrite the default template if one has been set in the project settings.
This [doesn't overwrite](#priority-of-default-description-templates) the default template if one has been set in the project settings.
- Users on GitLab Premium and higher: set the default template in project settings:
1. On the top bar, select **Menu > Projects** and find your project.
@ -159,15 +159,15 @@ headings, lists, and so on.
You can also provide `issues_template` and `merge_requests_template` attributes in the
[Projects REST API](../../api/projects.md) to keep your default issue and merge request templates up to date.
#### Priority of description templates
#### Priority of default description templates
When you set [merge request and issue description templates](#set-a-default-template-for-merge-requests-and-issues)
in various places, they have the following priorities in a project.
The ones higher up override the ones below:
1. Template selected in project settings.
1. `Default.md` from the parent group.
1. `Default.md` from the project repository.
1. Template set in project settings.
1. `Default.md` (case insensitive) from the parent group.
1. `Default.md` (case insensitive) from the project repository.
## Example description template

View File

@ -10,17 +10,10 @@ module BulkImports
relation_name BulkImports::FileTransfer::BaseConfig::SELF_RELATION
extractor ::BulkImports::Common::Extractors::JsonExtractor, relation: relation
transformer ::BulkImports::Common::Transformers::ProhibitedAttributesTransformer
def extract(_context)
download_service.execute
decompression_service.execute
project_attributes = json_decode(json_attributes)
BulkImports::Pipeline::ExtractedData.new(data: project_attributes)
end
def transform(_context, data)
subrelations = config.portable_relations_tree.keys.map(&:to_s)
@ -39,51 +32,14 @@ module BulkImports
end
def after_run(_context)
FileUtils.remove_entry(tmpdir) if Dir.exist?(tmpdir)
end
def json_attributes
@json_attributes ||= File.read(File.join(tmpdir, filename))
extractor.remove_tmpdir
end
private
def tmpdir
@tmpdir ||= Dir.mktmpdir('bulk_imports')
end
def config
@config ||= BulkImports::FileTransfer.config_for(portable)
end
def download_service
@download_service ||= BulkImports::FileDownloadService.new(
configuration: context.configuration,
relative_url: context.entity.relation_download_url_path(self.class.relation),
tmpdir: tmpdir,
filename: compressed_filename
)
end
def decompression_service
@decompression_service ||= BulkImports::FileDecompressionService.new(tmpdir: tmpdir, filename: compressed_filename)
end
def compressed_filename
"#{filename}.gz"
end
def filename
"#{self.class.relation}.json"
end
def json_decode(string)
Gitlab::Json.parse(string)
rescue JSON::ParserError => e
Gitlab::ErrorTracking.log_exception(e)
raise BulkImports::Error, 'Incorrect JSON format'
end
end
end
end

View File

@ -2640,6 +2640,18 @@ msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
msgid "AdminSettings|Delete inactive projects"
msgstr ""
msgid "AdminSettings|Delete inactive projects that exceed"
msgstr ""
msgid "AdminSettings|Delete project after"
msgstr ""
msgid "AdminSettings|Disable Elasticsearch until indexing completes."
msgstr ""
@ -2706,6 +2718,9 @@ msgstr ""
msgid "AdminSettings|Import sources"
msgstr ""
msgid "AdminSettings|Inactive project deletion"
msgstr ""
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
msgstr ""
@ -2742,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Maximum number of runners registered per project"
msgstr ""
msgid "AdminSettings|Minimum size must be at least 0."
msgstr ""
msgid "AdminSettings|New CI/CD variables in projects and groups default to protected."
msgstr ""
@ -2772,6 +2790,9 @@ msgstr ""
msgid "AdminSettings|Required pipeline configuration"
msgstr ""
msgid "AdminSettings|Requires %{linkStart}email notifications%{linkEnd}"
msgstr ""
msgid "AdminSettings|Restrict group access by IP address. %{link_start}Learn more%{link_end}."
msgstr ""
@ -2790,6 +2811,12 @@ msgstr ""
msgid "AdminSettings|Select to disable public access for Pages sites, which requires users to sign in for access to the Pages sites in your instance. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "AdminSettings|Send email to maintainers after project is inactive for"
msgstr ""
msgid "AdminSettings|Send warning email"
msgstr ""
msgid "AdminSettings|Service ping is disabled in your configuration file, and cannot be enabled through this form. For more information, see the documentation on %{link_start}deactivating service ping%{link_end}."
msgstr ""
@ -2808,6 +2835,9 @@ msgstr ""
msgid "AdminSettings|Set the maximum size of GitLab Pages per project (0 for unlimited). %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "AdminSettings|Setting must be greater than 0."
msgstr ""
msgid "AdminSettings|Size and domain settings for Pages static sites."
msgstr ""
@ -2841,9 +2871,15 @@ msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr ""
msgid "AdminSettings|When to delete inactive projects"
msgstr ""
msgid "AdminSettings|You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service."
msgstr ""
msgid "AdminSettings|You can't delete projects before the warning email is sent."
msgstr ""
msgid "AdminStatistics|Active Users"
msgstr ""
@ -6028,6 +6064,9 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
msgid "Billings|The last owner cannot be removed from a seat."
msgstr ""
msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
msgstr ""
@ -6049,6 +6088,9 @@ msgstr ""
msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
msgstr ""
msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
msgstr ""
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@ -40764,6 +40806,9 @@ msgstr ""
msgid "UsageQuota|Code packages and container images."
msgstr ""
msgid "UsageQuota|Container Registry"
msgstr ""
msgid "UsageQuota|Current period usage"
msgstr ""
@ -40779,6 +40824,9 @@ msgstr ""
msgid "UsageQuota|Git repository."
msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images."
msgstr ""
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@ -45154,6 +45202,9 @@ msgstr ""
msgid "missing"
msgstr ""
msgid "months"
msgstr ""
msgid "most recent deployment"
msgstr ""

View File

@ -1,8 +1,9 @@
FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive" \
BUNDLE_WITHOUT=development
ENV DEBIAN_FRONTEND="noninteractive"
# Override config path to make sure local config doesn't override it when building image locally
ENV BUNDLE_APP_CONFIG=/home/gitlab/.bundle
##
# Install system libs
@ -27,7 +28,8 @@ WORKDIR /home/gitlab/qa
# Install qa dependencies or fetch from cache if unchanged
#
COPY ./qa/Gemfile* /home/gitlab/qa/
RUN bundle install --jobs=$(nproc) --retry=3
RUN bundle config set --local without development \
&& bundle install --retry=3
##
# Fetch chromedriver based on version of chrome

View File

@ -127,7 +127,7 @@ RSpec.describe 'Group' do
describe 'Mattermost team creation' do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
stub_mattermost_setting(enabled: mattermost_enabled, host: 'https://mattermost.test')
visit new_group_path
click_link 'Create group'
@ -145,13 +145,14 @@ RSpec.describe 'Group' do
end
it 'updates the team URL on graph path update', :js do
out_span = find('span[data-bind-out="create_chat_team"]', visible: false)
label = find('#group_create_chat_team ~ label[for=group_create_chat_team]')
url = 'https://mattermost.test/test-group'
expect(out_span.text).to be_empty
expect(label.text).not_to match(url)
fill_in('group_path', with: 'test-group')
expect(out_span.text).to eq('test-group')
expect(label.text).to match(url)
end
end

View File

@ -0,0 +1,148 @@
import { GlFormCheckbox } from '@gitlab/ui';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import SettingsForm from '~/admin/application_settings/inactive_project_deletion/components/form.vue';
describe('Form component', () => {
let wrapper;
const findEnabledCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const findProjectDeletionSettings = () =>
wrapper.findByTestId('inactive-project-deletion-settings');
const findMinSizeGroup = () => wrapper.findByTestId('min-size-group');
const findMinSizeInputGroup = () => wrapper.findByTestId('min-size-input-group');
const findMinSizeInput = () => wrapper.findByTestId('min-size-input');
const findDeleteAfterMonthsGroup = () => wrapper.findByTestId('delete-after-months-group');
const findDeleteAfterMonthsInputGroup = () =>
wrapper.findByTestId('delete-after-months-input-group');
const findDeleteAfterMonthsInput = () => wrapper.findByTestId('delete-after-months-input');
const findSendWarningEmailAfterMonthsGroup = () =>
wrapper.findByTestId('send-warning-email-after-months-group');
const findSendWarningEmailAfterMonthsInputGroup = () =>
wrapper.findByTestId('send-warning-email-after-months-input-group');
const findSendWarningEmailAfterMonthsInput = () =>
wrapper.findByTestId('send-warning-email-after-months-input');
const createComponent = (
mountFn = shallowMountExtended,
propsData = { deleteInactiveProjects: true },
) => {
wrapper = mountFn(SettingsForm, { propsData });
};
afterEach(() => {
wrapper.destroy();
});
describe('Enable inactive project deletion', () => {
it('has the checkbox', () => {
createComponent();
expect(findEnabledCheckbox().exists()).toBe(true);
});
it.each([[true], [false]])(
'when the checkbox is %s then the project deletion settings visibility is set to %s',
(visible) => {
createComponent(shallowMountExtended, { deleteInactiveProjects: visible });
expect(findProjectDeletionSettings().exists()).toBe(visible);
},
);
});
describe('Minimum size for deletion', () => {
beforeEach(() => {
createComponent(mountExtended);
});
it('has the minimum size input', () => {
expect(findMinSizeInput().exists()).toBe(true);
});
it('has the field description', () => {
expect(findMinSizeGroup().text()).toContain('Delete inactive projects that exceed');
});
it('has the appended text on the field', () => {
expect(findMinSizeInputGroup().text()).toContain('MB');
});
it.each`
value | valid
${'0'} | ${true}
${'250'} | ${true}
${'-1'} | ${false}
`(
'when the minimum size input has a value of $value, then its validity should be $valid',
async ({ value, valid }) => {
await findMinSizeInput().find('input').setValue(value);
expect(findMinSizeGroup().classes('is-valid')).toBe(valid);
expect(findMinSizeInput().classes('is-valid')).toBe(valid);
},
);
});
describe('Delete project after', () => {
beforeEach(() => {
createComponent(mountExtended);
});
it('has the delete after months input', () => {
expect(findDeleteAfterMonthsInput().exists()).toBe(true);
});
it('has the appended text on the field', () => {
expect(findDeleteAfterMonthsInputGroup().text()).toContain('months');
});
it.each`
value | valid
${'0'} | ${false}
${'1'} | ${false /* Less than the default send warning email months */}
${'2'} | ${true}
`(
'when the delete after months input has a value of $value, then its validity should be $valid',
async ({ value, valid }) => {
await findDeleteAfterMonthsInput().find('input').setValue(value);
expect(findDeleteAfterMonthsGroup().classes('is-valid')).toBe(valid);
expect(findDeleteAfterMonthsInput().classes('is-valid')).toBe(valid);
},
);
});
describe('Send warning email', () => {
beforeEach(() => {
createComponent(mountExtended);
});
it('has the send warning email after months input', () => {
expect(findSendWarningEmailAfterMonthsInput().exists()).toBe(true);
});
it('has the field description', () => {
expect(findSendWarningEmailAfterMonthsGroup().text()).toContain(
'Send email to maintainers after project is inactive for',
);
});
it('has the appended text on the field', () => {
expect(findSendWarningEmailAfterMonthsInputGroup().text()).toContain('months');
});
it.each`
value | valid
${'2'} | ${true}
${'0'} | ${false}
`(
'when the minimum size input has a value of $value, then its validity should be $valid',
async ({ value, valid }) => {
await findSendWarningEmailAfterMonthsInput().find('input').setValue(value);
expect(findSendWarningEmailAfterMonthsGroup().classes('is-valid')).toBe(valid);
expect(findSendWarningEmailAfterMonthsInput().classes('is-valid')).toBe(valid);
},
);
});
});

View File

@ -1,4 +1,4 @@
import { GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
import { GlIcon, GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { helpPagePath } from '~/helpers/help_page_helper';
import CleanupStatus from '~/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status.vue';
@ -16,6 +16,7 @@ describe('cleanup_status', () => {
let wrapper;
const findMainIcon = () => wrapper.findByTestId('main-icon');
const findMainIconName = () => wrapper.findByTestId('main-icon').find(GlIcon);
const findExtraInfoIcon = () => wrapper.findByTestId('extra-info');
const findPopover = () => wrapper.findComponent(GlPopover);
@ -61,6 +62,23 @@ describe('cleanup_status', () => {
expect(findMainIcon().exists()).toBe(true);
});
it.each`
status | visible | iconName
${UNFINISHED_STATUS} | ${true} | ${'expire'}
${SCHEDULED_STATUS} | ${true} | ${'clock'}
${ONGOING_STATUS} | ${true} | ${'clock'}
${UNSCHEDULED_STATUS} | ${false} | ${''}
`('matches "$iconName" when the status is "$status"', ({ status, visible, iconName }) => {
mountComponent({ status });
expect(findMainIcon().exists()).toBe(visible);
if (visible) {
const actualIcon = findMainIconName();
expect(actualIcon.exists()).toBe(true);
expect(actualIcon.props('name')).toBe(iconName);
}
});
});
describe('extra info icon', () => {

View File

@ -93,7 +93,7 @@ describe('registry_header', () => {
expect(text.exists()).toBe(true);
expect(text.props()).toMatchObject({
text: EXPIRATION_POLICY_DISABLED_TEXT,
icon: 'expire',
icon: 'clock',
size: 'xl',
});
});

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Admin::ApplicationSettings::SettingsHelper do
describe '#inactive_projects_deletion_data' do
let(:delete_inactive_projects) { true }
let(:inactive_projects_delete_after_months) { 2 }
let(:inactive_projects_min_size_mb) { 250 }
let(:inactive_projects_send_warning_email_after_months) { 1 }
let_it_be(:application_settings) { build(:application_setting) }
before do
stub_application_setting(delete_inactive_projects: delete_inactive_projects)
stub_application_setting(inactive_projects_delete_after_months: inactive_projects_delete_after_months)
stub_application_setting(inactive_projects_min_size_mb: inactive_projects_min_size_mb)
stub_application_setting(
inactive_projects_send_warning_email_after_months: inactive_projects_send_warning_email_after_months
)
end
subject(:result) { helper.inactive_projects_deletion_data(application_settings) }
it 'has the expected data' do
expect(result).to eq({
delete_inactive_projects: delete_inactive_projects.to_s,
inactive_projects_delete_after_months: inactive_projects_delete_after_months,
inactive_projects_min_size_mb: inactive_projects_min_size_mb,
inactive_projects_send_warning_email_after_months: inactive_projects_send_warning_email_after_months
})
end
end
end

View File

@ -54,17 +54,13 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectAttributesPipeline do
subject(:pipeline) { described_class.new(context) }
before do
allow(Dir).to receive(:mktmpdir).with('bulk_imports').and_return(tmpdir)
end
after do
FileUtils.remove_entry(tmpdir) if Dir.exist?(tmpdir)
end
describe '#run' do
before do
allow(pipeline).to receive(:extract).and_return(BulkImports::Pipeline::ExtractedData.new(data: project_attributes))
allow_next_instance_of(BulkImports::Common::Extractors::JsonExtractor) do |extractor|
allow(extractor).to receive(:extract).and_return(
BulkImports::Pipeline::ExtractedData.new(data: project_attributes)
)
end
pipeline.run
end
@ -84,46 +80,6 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectAttributesPipeline do
end
end
describe '#extract' do
before do
file_download_service = instance_double("BulkImports::FileDownloadService")
file_decompression_service = instance_double("BulkImports::FileDecompressionService")
expect(BulkImports::FileDownloadService)
.to receive(:new)
.with(
configuration: context.configuration,
relative_url: "/#{entity.pluralized_name}/#{entity.source_full_path}/export_relations/download?relation=self",
tmpdir: tmpdir,
filename: 'self.json.gz')
.and_return(file_download_service)
expect(BulkImports::FileDecompressionService)
.to receive(:new)
.with(tmpdir: tmpdir, filename: 'self.json.gz')
.and_return(file_decompression_service)
expect(file_download_service).to receive(:execute)
expect(file_decompression_service).to receive(:execute)
end
it 'downloads, decompresses & decodes json' do
allow(pipeline).to receive(:json_attributes).and_return("{\"test\":\"test\"}")
extracted_data = pipeline.extract(context)
expect(extracted_data.data).to match_array([{ 'test' => 'test' }])
end
context 'when json parsing error occurs' do
it 'raises an error' do
allow(pipeline).to receive(:json_attributes).and_return("invalid")
expect { pipeline.extract(context) }.to raise_error(BulkImports::Error)
end
end
end
describe '#transform' do
it 'removes prohibited attributes from hash' do
input = { 'description' => 'description', 'issues' => [], 'milestones' => [], 'id' => 5 }
@ -145,35 +101,13 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectAttributesPipeline do
end
end
describe '#json_attributes' do
it 'reads raw json from file' do
filepath = File.join(tmpdir, 'self.json')
FileUtils.touch(filepath)
expect_file_read(filepath)
pipeline.json_attributes
end
end
describe '#after_run' do
it 'removes tmp dir' do
allow(FileUtils).to receive(:remove_entry).and_call_original
expect(FileUtils).to receive(:remove_entry).with(tmpdir).and_call_original
it 'calls extractor#remove_tmpdir' do
expect_next_instance_of(BulkImports::Common::Extractors::JsonExtractor) do |extractor|
expect(extractor).to receive(:remove_tmpdir)
end
pipeline.after_run(nil)
expect(Dir.exist?(tmpdir)).to eq(false)
end
context 'when dir does not exist' do
it 'does not attempt to remove tmpdir' do
FileUtils.remove_entry(tmpdir)
expect(FileUtils).not_to receive(:remove_entry).with(tmpdir)
pipeline.after_run(nil)
end
end
end

View File

@ -35,5 +35,26 @@ RSpec.describe 'Project Usage Quotas' do
it_behaves_like 'response with 404 status'
end
context 'container_registry_project_statistics feature flag' do
subject(:body) { response.body }
before do
stub_feature_flags(container_registry_project_statistics: container_registry_project_statistics_enabled)
get project_usage_quotas_path(project)
end
context 'when disabled' do
let(:container_registry_project_statistics_enabled) { false }
it { is_expected.to have_pushed_frontend_feature_flags(containerRegistryProjectStatistics: false)}
end
context 'when enabled' do
let(:container_registry_project_statistics_enabled) { true }
it { is_expected.to have_pushed_frontend_feature_flags(containerRegistryProjectStatistics: true)}
end
end
end
end

View File

@ -0,0 +1,75 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'admin/application_settings/_repository_check.html.haml' do
let_it_be(:user) { create(:admin) }
let_it_be(:application_setting) { build(:application_setting) }
before do
assign(:application_setting, application_setting)
allow(view).to receive(:current_user).and_return(user)
end
describe 'repository checks' do
it 'has the setting subsection' do
render
expect(rendered).to have_content('Repository checks')
end
it 'renders the correct setting subsection content' do
render
expect(rendered).to have_field('Enable repository checks')
expect(rendered).to have_link(
'Clear all repository checks',
href: clear_repository_check_states_admin_application_settings_path
)
end
end
describe 'housekeeping' do
it 'has the setting subsection' do
render
expect(rendered).to have_content('Housekeeping')
end
it 'renders the correct setting subsection content' do
render
expect(rendered).to have_field('Enable automatic repository housekeeping')
expect(rendered).to have_field('Incremental repack period')
expect(rendered).to have_field('Full repack period')
expect(rendered).to have_field('Git GC period')
end
end
describe 'inactive project deletion' do
let_it_be(:application_setting) do
build(:application_setting,
delete_inactive_projects: true,
inactive_projects_delete_after_months: 2,
inactive_projects_min_size_mb: 250,
inactive_projects_send_warning_email_after_months: 1
)
end
it 'has the setting subsection' do
render
expect(rendered).to have_content('Inactive project deletion')
end
it 'renders the correct setting subsection content' do
render
expect(rendered).to have_selector('.js-inactive-project-deletion-form')
expect(rendered).to have_selector('[data-delete-inactive-projects="true"]')
expect(rendered).to have_selector('[data-inactive-projects-delete-after-months="2"]')
expect(rendered).to have_selector('[data-inactive-projects-min-size-mb="250"]')
expect(rendered).to have_selector('[data-inactive-projects-send-warning-email-after-months="1"]')
end
end
end