Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-20 06:10:53 +00:00
parent 060951c7d1
commit 62c4f80715
22 changed files with 144 additions and 103 deletions

View File

@ -1,4 +1,5 @@
import Vue from 'vue';
import { pickBy } from 'lodash';
import { parseBoolean } from './lib/utils/common_utils';
import ConfirmDanger from './vue_shared/components/confirm_danger/confirm_danger.vue';
@ -12,21 +13,32 @@ export default () => {
buttonText,
buttonClass = '',
buttonTestid = null,
buttonVariant = null,
confirmDangerMessage,
confirmButtonText = null,
disabled = false,
additionalInformation,
htmlConfirmationMessage,
} = el.dataset;
return new Vue({
el,
provide: {
confirmDangerMessage,
},
provide: pickBy(
{
htmlConfirmationMessage,
confirmDangerMessage,
additionalInformation,
confirmButtonText,
},
(v) => Boolean(v),
),
render: (createElement) =>
createElement(ConfirmDanger, {
props: {
phrase,
buttonText,
buttonClass,
buttonVariant,
buttonTestid,
disabled: parseBoolean(disabled),
},

View File

@ -36,6 +36,11 @@ export default {
required: false,
default: 'confirm-danger-button',
},
buttonVariant: {
type: String,
required: false,
default: 'danger',
},
},
modalId: CONFIRM_DANGER_MODAL_ID,
};
@ -45,7 +50,7 @@ export default {
<gl-button
v-gl-modal="$options.modalId"
:class="buttonClass"
variant="danger"
:variant="buttonVariant"
:disabled="disabled"
:data-testid="buttonTestid"
>{{ buttonText }}</gl-button

View File

@ -11,7 +11,9 @@ const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
template: '<confirm-danger v-bind="$props" />',
provide: {
confirmDangerMessage: 'You require more Vespene Gas',
additionalInformation: args.additionalInformation || null,
confirmDangerMessage: args.confirmDangerMessage || 'You require more Vespene Gas',
htmlConfirmationMessage: args.confirmDangerMessage || false,
},
});
@ -26,3 +28,16 @@ Disabled.args = {
...Default.args,
disabled: true,
};
export const AdditionalInformation = Template.bind({});
AdditionalInformation.args = {
...Default.args,
additionalInformation: 'This replaces the default warning information',
};
export const HtmlMessage = Template.bind({});
HtmlMessage.args = {
...Default.args,
confirmDangerMessage: 'You strongly require more <strong>Vespene Gas</strong>',
htmlConfirmationMessage: true,
};

View File

@ -1,5 +1,12 @@
<script>
import { GlAlert, GlModal, GlFormGroup, GlFormInput, GlSprintf } from '@gitlab/ui';
import {
GlAlert,
GlModal,
GlFormGroup,
GlFormInput,
GlSafeHtmlDirective as SafeHtml,
GlSprintf,
} from '@gitlab/ui';
import {
CONFIRM_DANGER_MODAL_BUTTON,
CONFIRM_DANGER_MODAL_TITLE,
@ -17,13 +24,22 @@ export default {
GlFormInput,
GlSprintf,
},
directives: {
SafeHtml,
},
inject: {
htmlConfirmationMessage: {
default: false,
},
confirmDangerMessage: {
default: '',
},
confirmButtonText: {
default: CONFIRM_DANGER_MODAL_BUTTON,
},
additionalInformation: {
default: CONFIRM_DANGER_WARNING,
},
},
props: {
modalId: {
@ -81,9 +97,12 @@ export default {
:dismissible="false"
class="gl-mb-4"
>
{{ confirmDangerMessage }}
<span v-if="htmlConfirmationMessage" v-safe-html="confirmDangerMessage"></span>
<span v-else>
{{ confirmDangerMessage }}
</span>
</gl-alert>
<p data-testid="confirm-danger-warning">{{ $options.i18n.CONFIRM_DANGER_WARNING }}</p>
<p data-testid="confirm-danger-warning">{{ additionalInformation }}</p>
<p data-testid="confirm-danger-phrase">
<gl-sprintf :message="$options.i18n.CONFIRM_DANGER_PHRASE_TEXT">
<template #phrase_code>

View File

@ -8,7 +8,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:swimlanes_buffered_rendering, project, default_enabled: :yaml)
push_frontend_feature_flag(:issue_boards_filtered_search, project, default_enabled: :yaml)
push_frontend_feature_flag(:issue_boards_filtered_search, project&.group, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml)
experiment(:prominent_create_board_btn, subject: current_user) do |e|

View File

@ -660,6 +660,33 @@ module ProjectsHelper
project.unlink_forks_upon_visibility_decrease_enabled? && project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && project.forks_count > 0
end
def confirm_reduce_visibility_message(project)
strong_start = "<strong>".html_safe
strong_end = "</strong>".html_safe
message = _("You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end}.")
if project.group
message = _("You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end} in %{strong_start}%{group_name}%{strong_end}.")
end
html_escape(message) % { strong_start: strong_start, strong_end: strong_end, project_name: project.name, group_name: project.group ? project.group.name : nil }
end
def visibility_confirm_modal_data(project, remove_form_id = nil)
{
remove_form_id: remove_form_id,
qa_selector: 'visibility_features_permissions_save_button',
button_text: _('Save changes'),
button_testid: 'reduce-project-visibility-button',
button_variant: 'confirm',
confirm_button_text: _('Reduce project visibility'),
confirm_danger_message: confirm_reduce_visibility_message(project),
phrase: project.full_path,
additional_information: _('Note: current forks will keep their visibility level.'),
html_confirmation_message: true
}
end
def build_project_breadcrumb_link(project)
project_name = simple_sanitize(project.name)

View File

@ -271,10 +271,6 @@ module Ci
self.where(project: project).sum(:size)
end
def self.distinct_job_ids
distinct.pluck(:job_id)
end
##
# FastDestroyAll concerns
# rubocop: disable CodeReuse/ServiceClass

View File

@ -8,13 +8,15 @@ module Ci
BATCH_SIZE = 100
LOOP_TIMEOUT = 5.minutes
LOOP_LIMIT = 1000
SMALL_LOOP_LIMIT = 10
LARGE_LOOP_LIMIT = 100
EXCLUSIVE_LOCK_KEY = 'expired_job_artifacts:destroy:lock'
LOCK_TIMEOUT = 6.minutes
def initialize
@removed_artifacts_count = 0
@start_at = Time.current
@loop_limit = ::Feature.enabled?(:ci_artifact_fast_removal_large_loop_limit, default_enabled: :yaml) ? LARGE_LOOP_LIMIT : SMALL_LOOP_LIMIT
end
##
@ -24,6 +26,8 @@ module Ci
# preventing multiple `ExpireBuildArtifactsWorker` CRON jobs run concurrently,
# which is scheduled every 7 minutes.
def execute
return 0 unless ::Feature.enabled?(:ci_destroy_all_expired_service, default_enabled: :yaml)
in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do
if ::Feature.enabled?(:ci_destroy_unlocked_job_artifacts)
destroy_unlocked_job_artifacts
@ -38,34 +42,13 @@ module Ci
private
def destroy_unlocked_job_artifacts
loop_until(timeout: LOOP_TIMEOUT, limit: LOOP_LIMIT) do
loop_until(timeout: LOOP_TIMEOUT, limit: @loop_limit) do
artifacts = Ci::JobArtifact.expired_before(@start_at).artifact_unlocked.limit(BATCH_SIZE)
service_response = destroy_batch(artifacts)
@removed_artifacts_count += service_response[:destroyed_artifacts_count]
update_locked_status_on_unknown_artifacts if service_response[:destroyed_artifacts_count] == 0
# Return a truthy value here to prevent exiting #loop_until
@removed_artifacts_count
end
end
def update_locked_status_on_unknown_artifacts
build_ids = Ci::JobArtifact.expired_before(@start_at).artifact_unknown.limit(BATCH_SIZE).distinct_job_ids
return unless build_ids.present?
locked_pipeline_build_ids = ::Ci::Build.with_pipeline_locked_artifacts.id_in(build_ids).pluck_primary_key
unlocked_pipeline_build_ids = build_ids - locked_pipeline_build_ids
update_unknown_artifacts(locked_pipeline_build_ids, Ci::JobArtifact.lockeds[:artifacts_locked])
update_unknown_artifacts(unlocked_pipeline_build_ids, Ci::JobArtifact.lockeds[:unlocked])
end
def update_unknown_artifacts(build_ids, locked_value)
Ci::JobArtifact.for_job_ids(build_ids).update_all(locked: locked_value) if build_ids.any?
end
def destroy_job_artifacts_with_slow_iteration
Ci::JobArtifact.expired_before(@start_at).each_batch(of: BATCH_SIZE, column: :expire_at, order: :desc) do |relation, index|
# For performance reasons, join with ci_pipelines after the batch is queried.
@ -76,7 +59,7 @@ module Ci
@removed_artifacts_count += service_response[:destroyed_artifacts_count]
break if loop_timeout?
break if index >= LOOP_LIMIT
break if index >= @loop_limit
end
end

View File

@ -8,14 +8,11 @@
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name gl-ml-3 qa-branch-name' do
= branch.name
- if branch.name == @repository.root_ref
%span.badge.gl-badge.sm.badge-pill.badge-primary.gl-ml-2 default
= gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :info, size: :sm }, { class: 'gl-ml-2' }
- elsif merged
%span.badge.gl-badge.sm.badge-pill.badge-info.has-tooltip.gl-ml-2{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
= s_('Branches|merged')
= gl_badge_tag s_('Branches|merged'), { variant: :info, size: :sm }, { class: 'gl-ml-2', title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref }, data: { toggle: 'tooltip', container: 'body' } }
- if protected_branch?(@project, branch)
%span.badge.gl-badge.sm.badge-pill.badge-success.gl-ml-2
= s_('Branches|protected')
= gl_badge_tag s_('Branches|protected'), { variant: :success, size: :sm }, { class: 'gl-ml-2' }
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch

View File

@ -2,6 +2,7 @@
- page_title _("General")
- @content_class = "limit-container-width" unless fluid_layout
- expanded = expanded_by_default?
- remove_visibility_form_id = 'reduce-visibility-form'
%section.settings.general-settings.no-animate.expanded#js-general-settings
.settings-header
@ -17,13 +18,11 @@
%p= _('Choose visibility level, enable/disable project features and their permissions, disable email notifications, and show default award emoji.')
.settings-content
= form_for @project, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f|
= form_for @project, html: { multipart: true, class: "sharing-permissions-form", id: remove_visibility_form_id }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
%template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project).to_json.html_safe
.js-project-permissions-form
- if show_visibility_confirm_modal?(@project)
= render "visibility_modal"
= f.submit _('Save changes'), class: "btn gl-button btn-confirm #{('js-legacy-confirm-danger' if show_visibility_confirm_modal?(@project))}", data: { qa_selector: 'visibility_features_permissions_save_button', check_field_name: ("project[visibility_level]" if show_visibility_confirm_modal?(@project)), check_compare_value: @project.visibility_level }
= f.submit _('Save changes'), class: "btn gl-button btn-confirm #{('js-confirm-danger' if show_visibility_confirm_modal?(@project))}", data: visibility_confirm_modal_data(@project, remove_visibility_form_id)
%section.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)], data: { qa_selector: 'merge_request_settings_content' } }
.settings-header

View File

@ -13,8 +13,8 @@ class ExpireBuildArtifactsWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :build_artifacts
def perform
service = Ci::JobArtifacts::DestroyAllExpiredService.new
artifacts_count = service.execute
artifacts_count = Ci::JobArtifacts::DestroyAllExpiredService.new.execute
log_extra_metadata_on_done(:destroyed_job_artifacts_count, artifacts_count)
end
end

View File

@ -0,0 +1,8 @@
---
name: ci_artifact_fast_removal_large_loop_limit
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76504
rollout_issue_url:
milestone: '14.6'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: ci_destroy_all_expired_service
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76504
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348786
milestone: '14.6'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -4,7 +4,7 @@ group: none
info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
---
# Deprecated feature removal schedule
# Deprecated features
DISCLAIMER:
This page contains information related to upcoming products, features, and functionality.

View File

@ -4,7 +4,7 @@ group: none
info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
---
# Deprecated feature removal schedule
# Deprecated features
DISCLAIMER:
This page contains information related to upcoming products, features, and functionality.

View File

@ -137,8 +137,6 @@ When SSO is enforced, users are not immediately revoked. If the user:
- Has an active session, they can continue accessing the group for up to 24 hours until the identity
provider session times out.
When SCIM updates, the user's access is immediately revoked.
## Providers
The SAML standard means that you can use a wide range of identity providers with GitLab. Your identity provider might have relevant documentation. It can be generic SAML documentation or specifically targeted for GitLab.
@ -304,7 +302,14 @@ If a user is already a member of the group, linking the SAML identity does not c
### Blocking access
Please refer to [Blocking access via SCIM](scim_setup.md#blocking-access).
To rescind a user's access to the group when only SAML SSO is configured, either:
- Remove (in order) the user from:
1. The user data store on the identity provider or the list of users on the specific app.
1. The GitLab.com group.
- Use Group Sync at the top-level of your group to [automatically remove the user](#automatic-member-removal).
To rescind a user's access to the group when also using SCIM, refer to [Blocking access](scim_setup.md#blocking-access).
### Unlinking accounts

View File

@ -184,8 +184,7 @@ For role information, please see the [Group SAML page](index.md#user-access-and-
### Blocking access
To rescind access to the top-level group, all sub-groups, and projects, remove or deactivate the user
on the identity provider. SCIM providers generally update GitLab with the changes on demand, which
is minutes at most. The user's membership is revoked and they immediately lose access.
on the identity provider. After the identity provider performs a sync, based on its configured schedule, the user's membership is revoked and they lose access.
NOTE:
Deprovisioning does not delete the GitLab user account.

View File

@ -24088,6 +24088,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
msgid "Note: current forks will keep their visibility level."
msgstr ""
msgid "NoteForm|Note"
msgstr ""

View File

@ -5,9 +5,7 @@ module QA
module Project
module Settings
class VisibilityFeaturesPermissions < Page::Base
include QA::Page::Component::Select2
view 'app/views/projects/edit.html.haml' do
view 'app/helpers/projects_helper.rb' do
element :visibility_features_permissions_save_button
end

View File

@ -22,7 +22,7 @@ RSpec.describe 'User changes public project visibility', :js do
click_button 'Save changes'
end
find('.js-legacy-confirm-danger-input').send_keys(project.path_with_namespace)
fill_in 'confirm_name_input', with: project.path_with_namespace
page.within '.modal' do
click_button 'Reduce project visibility'

View File

@ -10,6 +10,7 @@ describe('Confirm Danger Modal', () => {
const phrase = 'En Taro Adun';
const buttonText = 'Click me!';
const buttonClass = 'gl-w-full';
const buttonVariant = 'info';
const modalId = CONFIRM_DANGER_MODAL_ID;
const findBtn = () => wrapper.findComponent(GlButton);
@ -21,6 +22,7 @@ describe('Confirm Danger Modal', () => {
propsData: {
buttonText,
buttonClass,
buttonVariant,
phrase,
...props,
},
@ -57,6 +59,10 @@ describe('Confirm Danger Modal', () => {
expect(findBtn().classes()).toContain(buttonClass);
});
it('passes `buttonVariant` prop to button', () => {
expect(findBtn().attributes('variant')).toBe(buttonVariant);
});
it('will emit `confirm` when the modal confirms', () => {
expect(wrapper.emitted('confirm')).toBeUndefined();

View File

@ -20,7 +20,7 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
context 'with preloaded relationships' do
before do
stub_const("#{described_class}::LOOP_LIMIT", 1)
stub_const("#{described_class}::LARGE_LOOP_LIMIT", 1)
end
context 'with ci_destroy_unlocked_job_artifacts feature flag disabled' do
@ -53,46 +53,6 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
log = ActiveRecord::QueryRecorder.new { subject }
expect(log.count).to be_within(1).of(8)
end
context 'with several locked-unknown artifact records' do
before do
stub_const("#{described_class}::LOOP_LIMIT", 10)
stub_const("#{described_class}::BATCH_SIZE", 2)
end
let!(:lockable_artifact_records) do
[
create(:ci_job_artifact, :metadata, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: locked_job),
create(:ci_job_artifact, :junit, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: locked_job),
create(:ci_job_artifact, :sast, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: locked_job),
create(:ci_job_artifact, :cobertura, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: locked_job),
create(:ci_job_artifact, :trace, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: locked_job)
]
end
let!(:unlockable_artifact_records) do
[
create(:ci_job_artifact, :metadata, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: job),
create(:ci_job_artifact, :junit, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: job),
create(:ci_job_artifact, :sast, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: job),
create(:ci_job_artifact, :cobertura, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: job),
create(:ci_job_artifact, :trace, :expired, locked: ::Ci::JobArtifact.lockeds[:unknown], job: job),
artifact
]
end
it 'updates the locked status of job artifacts from artifacts-locked pipelines' do
subject
expect(lockable_artifact_records).to be_all(&:persisted?)
expect(lockable_artifact_records).to be_all { |artifact| artifact.reload.artifact_artifacts_locked? }
end
it 'unlocks and then destroys job artifacts from artifacts-unlocked pipelines' do
expect { subject }.to change { Ci::JobArtifact.count }.by(-6)
expect(Ci::JobArtifact.where(id: unlockable_artifact_records.map(&:id))).to be_empty
end
end
end
end
@ -159,7 +119,7 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
let!(:artifact) { create(:ci_job_artifact, :expired, job: job, locked: job.pipeline.locked) }
before do
stub_const("#{described_class}::LOOP_LIMIT", 10)
stub_const("#{described_class}::LARGE_LOOP_LIMIT", 10)
end
context 'when the import fails' do
@ -229,7 +189,8 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
context 'when loop reached loop limit' do
before do
stub_const("#{described_class}::LOOP_LIMIT", 1)
stub_feature_flags(ci_artifact_fast_removal_large_loop_limit: false)
stub_const("#{described_class}::SMALL_LOOP_LIMIT", 1)
end
it 'destroys one artifact' do