Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-14 15:08:43 +00:00
parent 7a124e225e
commit 9b8269e570
104 changed files with 759 additions and 458 deletions

View File

@ -9,4 +9,3 @@
/sitespeed-result/
/fixtures/**/*.graphql
spec/fixtures/**/*.graphql
**/contracts/consumer/

View File

@ -18,6 +18,20 @@ export default {
required: true,
},
},
modal: {
actionPrimary: {
text: __('Discard changes'),
attributes: {
variant: 'danger',
},
},
actionCancel: {
text: __('Cancel'),
attributes: {
variant: 'default',
},
},
},
computed: {
discardModalId() {
return `discard-file-${this.activeFile.path}`;
@ -66,12 +80,11 @@ export default {
</div>
<gl-modal
ref="discardModal"
ok-variant="danger"
cancel-variant="light"
:ok-title="__('Discard changes')"
:modal-id="discardModalId"
:title="discardModalTitle"
@ok="discardChanges(activeFile.path)"
:action-primary="$options.modal.actionPrimary"
:action-cancel="$options.modal.actionCancel"
@primary="discardChanges(activeFile.path)"
>
{{ __("You will lose all changes you've made to this file. This action cannot be undone.") }}
</gl-modal>

View File

@ -38,6 +38,20 @@ export default {
default: __('No changes'),
},
},
modal: {
actionPrimary: {
text: __('Discard all changes'),
attributes: {
variant: 'danger',
},
},
actionCancel: {
text: __('Cancel'),
attributes: {
variant: 'default',
},
},
},
computed: {
titleText() {
if (!this.title) return __('Changes');
@ -106,11 +120,11 @@ export default {
<gl-modal
v-if="!stagedList"
ref="discardAllModal"
ok-variant="danger"
modal-id="discard-all-changes"
:ok-title="__('Discard all changes')"
:title="__('Discard all changes?')"
@ok="unstageAndDiscardAllChanges"
:action-primary="$options.modal.actionPrimary"
:action-cancel="$options.modal.actionCancel"
@primary="unstageAndDiscardAllChanges"
>
{{ $options.discardModalText }}
</gl-modal>

View File

@ -174,9 +174,7 @@ export default {
this.removeAllPointerEventListeners();
if (this.issuableType === IssuableType.Issue) {
this.renderSortableLists();
}
this.renderSortableLists();
if (this.workItemsEnabled) {
this.renderTaskActions();
@ -184,7 +182,10 @@ export default {
}
},
renderSortableLists() {
const lists = document.querySelectorAll('.description ul, .description ol');
// We exclude GLFM table of contents which have a `section-nav` class on the root `ul`.
const lists = document.querySelectorAll(
'.description .md > ul:not(.section-nav), .description .md > ul:not(.section-nav) ul, .description ol',
);
lists.forEach((list) => {
if (list.children.length <= 1) {
return;

View File

@ -31,6 +31,7 @@ export default class Todos {
$('.js-done-todo, .js-undo-todo, .js-add-todo').off('click', this.updateRowStateClickedWrapper);
$('.js-todos-mark-all', '.js-todos-undo-all').off('click', this.updateallStateClickedWrapper);
$('.todo').off('click', this.goToTodoUrl);
$('.todo').off('auxclick', this.goToTodoUrl);
}
bindEvents() {
@ -40,6 +41,7 @@ export default class Todos {
$('.js-done-todo, .js-undo-todo, .js-add-todo').on('click', this.updateRowStateClickedWrapper);
$('.js-todos-mark-all, .js-todos-undo-all').on('click', this.updateAllStateClickedWrapper);
$('.todo').on('click', this.goToTodoUrl);
$('.todo').on('auxclick', this.goToTodoUrl);
}
initFilters() {
@ -198,11 +200,13 @@ export default class Todos {
e.stopPropagation();
e.preventDefault();
const isPrimaryClick = e.button === 0;
if (isMetaClick(e)) {
const windowTarget = '_blank';
window.open(todoLink, windowTarget);
} else {
} else if (isPrimaryClick) {
visitUrl(todoLink);
}
}

View File

@ -4,10 +4,9 @@ import { __, s__ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import SectionLayout from '~/vue_shared/security_configuration/components/section_layout.vue';
import currentLicenseQuery from '~/security_configuration/graphql/current_license.query.graphql';
import AutoDevOpsAlert from './auto_dev_ops_alert.vue';
import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue';
import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, LICENSE_ULTIMATE } from './constants';
import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants';
import FeatureCard from './feature_card.vue';
import TrainingProviderList from './training_provider_list.vue';
import UpgradeBanner from './upgrade_banner.vue';
@ -51,17 +50,6 @@ export default {
TrainingProviderList,
},
inject: ['projectFullPath', 'vulnerabilityTrainingDocsPath'],
apollo: {
currentLicensePlan: {
query: currentLicenseQuery,
update({ currentLicense }) {
return currentLicense?.plan;
},
error() {
this.hasCurrentLicenseFetchError = true;
},
},
},
props: {
augmentedSecurityFeatures: {
type: Array,
@ -96,13 +84,15 @@ export default {
required: false,
default: '',
},
securityTrainingEnabled: {
type: Boolean,
required: true,
},
},
data() {
return {
autoDevopsEnabledAlertDismissedProjects: [],
errorMessage: '',
currentLicensePlan: '',
hasCurrentLicenseFetchError: false,
};
},
computed: {
@ -123,12 +113,6 @@ export default {
!this.autoDevopsEnabledAlertDismissedProjects.includes(this.projectFullPath)
);
},
shouldShowVulnerabilityManagementTab() {
// if the query fails (if the plan is `null` also means an error has occurred) we still want to show the feature
const hasQueryError = this.hasCurrentLicenseFetchError || this.currentLicensePlan === null;
return hasQueryError || this.currentLicensePlan === LICENSE_ULTIMATE;
},
},
methods: {
dismissAutoDevopsEnabledAlert() {
@ -270,7 +254,7 @@ export default {
</section-layout>
</gl-tab>
<gl-tab
v-if="shouldShowVulnerabilityManagementTab"
v-if="securityTrainingEnabled"
data-testid="vulnerability-management-tab"
:title="$options.i18n.vulnerabilityManagement"
query-param-value="vulnerability-management"

View File

@ -310,7 +310,3 @@ export const TEMP_PROVIDER_URLS = {
Kontra: 'https://application.security/',
[__('Secure Code Warrior')]: 'https://www.securecodewarrior.com/',
};
export const LICENSE_ULTIMATE = 'ultimate';
export const LICENSE_FREE = 'free';
export const LICENSE_PREMIUM = 'premium';

View File

@ -1,6 +0,0 @@
query getCurrentLicensePlan {
currentLicense {
id
plan
}
}

View File

@ -56,6 +56,7 @@ export const initSecurityConfiguration = (el) => {
'gitlabCiPresent',
'autoDevopsEnabled',
'canEnableAutoDevops',
'securityTrainingEnabled',
]),
},
});

View File

@ -49,14 +49,14 @@ export default {
},
request() {
const state = {
variant: 'default',
selected: false,
icon: 'attention',
direction: 'add',
};
if (this.user.attention_requested) {
Object.assign(state, {
variant: 'warning',
selected: true,
icon: 'attention-solid',
direction: 'remove',
});
@ -92,7 +92,7 @@ export default {
>
<gl-button
:loading="loading"
:variant="request.variant"
:selected="request.selected"
:icon="request.icon"
:aria-label="tooltipTitle"
:class="{ 'gl-pointer-events-none': !user.can_update_merge_request }"

View File

@ -32,7 +32,7 @@ export default {
computed: {
arrowIconName() {
return this.isCollapsed ? 'angle-right' : 'angle-down';
return this.isCollapsed ? 'chevron-lg-right' : 'chevron-lg-down';
},
ariaLabel() {
return this.isCollapsed ? __('Expand') : __('Collapse');

View File

@ -270,6 +270,10 @@
.reviewer-grid {
[data-css-area='attention'] {
grid-area: attention;
button.selected svg {
fill: $orange-500;
}
}
[data-css-area='user'] {

View File

@ -1,6 +1,6 @@
= render Pajamas::AlertComponent.new(title: _('Too many changes to show.'),
variant: :warning,
alert_class: 'gl-mb-5') do |c|
alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
= message

View File

@ -1,11 +1,10 @@
.gl-alert{ @alert_options, role: 'alert', class: [base_class, @alert_class], data: @alert_data }
.gl-alert{ @alert_options, role: 'alert', class: base_class }
- if @show_icon
= sprite_icon(icon, css_class: icon_classes)
- if @dismissible
%button.btn.gl-dismiss-btn.btn-default.btn-sm.gl-button.btn-default-tertiary.btn-icon.js-close{ type: 'button',
aria: { label: _('Dismiss') },
class: @close_button_class,
data: @close_button_data }
%button.btn.gl-dismiss-btn.btn-default.btn-sm.gl-button.btn-default-tertiary.btn-icon.js-close{ @close_button_options,
type: 'button',
aria: { label: _('Dismiss') } }
= sprite_icon('close')
.gl-alert-content{ role: 'alert' }
- if @title

View File

@ -7,22 +7,17 @@ module Pajamas
# @param [Symbol] variant
# @param [Boolean] dismissible
# @param [Boolean] show_icon
# @param [String] alert_class
# @param [Hash] alert_data
# @param [String] close_button_class
# @param [Hash] close_button_data
# @param [Hash] alert_options
# @param [Hash] close_button_options
def initialize(
title: nil, variant: :info, dismissible: true, show_icon: true,
alert_class: nil, alert_data: {}, alert_options: {}, close_button_class: nil, close_button_data: {})
alert_options: {}, close_button_options: {})
@title = title
@variant = variant
@dismissible = dismissible
@show_icon = show_icon
@alert_class = alert_class
@alert_data = alert_data
@alert_options = alert_options
@close_button_class = close_button_class
@close_button_data = close_button_data
@close_button_options = close_button_options
end
def base_class

View File

@ -142,16 +142,22 @@ module Types
# rubocop: disable CodeReuse/ActiveRecord
def batched_owners(runner_assoc_type, assoc_type, key, column_name)
BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader, args|
runner_and_owner_ids = runner_assoc_type.where(runner_id: runner_ids).pluck(:runner_id, column_name)
owner_ids_by_runner_id = runner_and_owner_ids.group_by(&:first).transform_values { |v| v.pluck(1) }
owner_ids = runner_and_owner_ids.pluck(1).uniq
BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader|
plucked_runner_and_owner_ids = runner_assoc_type
.select(:runner_id, column_name)
.where(runner_id: runner_ids)
.pluck(:runner_id, column_name)
# In plucked_runner_and_owner_ids, first() represents the runner ID, and second() the owner ID,
# so let's group the owner IDs by runner ID
runner_owner_ids_by_runner_id = plucked_runner_and_owner_ids
.group_by(&:first)
.transform_values { |runner_and_owner_id| runner_and_owner_id.map(&:second) }
owner_ids = runner_owner_ids_by_runner_id.values.flatten.uniq
owners = assoc_type.where(id: owner_ids).index_by(&:id)
runner_ids.each do |runner_id|
loader.call(runner_id, owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
loader.call(runner_id, runner_owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
end
end
end

View File

@ -30,8 +30,7 @@ module FormHelper
variant: :danger,
title: headline,
dismissible: false,
alert_class: 'gl-mb-5',
alert_options: { id: 'error_explanation' }
alert_options: { id: 'error_explanation', class: 'gl-mb-5' }
) do |c|
c.body do
tag.ul(class: 'gl-pl-5 gl-mb-0') do

View File

@ -2,11 +2,11 @@
= render Pajamas::AlertComponent.new(variant: :tip,
title: s_('AdminArea|Get security updates from GitLab and stay up to date'),
alert_class: 'js-security-newsletter-callout',
alert_data: { feature_id: Users::CalloutsHelper::SECURITY_NEWSLETTER_CALLOUT,
dismiss_endpoint: callouts_path,
defer_links: 'true' },
close_button_data: { testid: 'close-security-newsletter-callout' }) do |c|
alert_options: { class: 'js-security-newsletter-callout',
data: { feature_id: Users::CalloutsHelper::SECURITY_NEWSLETTER_CALLOUT,
dismiss_endpoint: callouts_path,
defer_links: 'true' }},
close_button_options: { data: { testid: 'close-security-newsletter-callout' }}) do |c|
= c.body do
= s_('AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates.')
= c.actions do

View File

@ -15,8 +15,8 @@
.row
.col-md-12
= render Pajamas::AlertComponent.new(variant: :danger,
alert_class: 'gl-mb-5',
alert_data: { testid: 'last-repository-check-failed-alert' }) do |c|
alert_options: { class: 'gl-mb-5',
data: { testid: 'last-repository-check-failed-alert' }}) do |c|
= c.body do
- last_check_message = _("Last repository check (%{last_check_timestamp}) failed. See the 'repocheck.log' file for error messages.")
- last_check_message = last_check_message % { last_check_timestamp: time_ago_with_tooltip(@project.last_repository_check_at) }

View File

@ -1,6 +1,6 @@
- if registration_features_can_be_prompted?
= render Pajamas::AlertComponent.new(variant: :tip,
alert_class: 'gl-my-5',
alert_options: { class: 'gl-my-5' },
dismissible: false) do |c|
= c.body do
= render 'shared/registration_features_discovery_message', feature_title: s_('RegistrationFeatures|send emails to users')

View File

@ -7,12 +7,12 @@
%span.gl-ml-2= s_('ClusterIntegration|Kubernetes cluster is being created...')
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'hidden js-cluster-api-unreachable') do |c|
alert_options: { class: 'hidden js-cluster-api-unreachable' }) do |c|
= c.body do
= s_('ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct.')
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'hidden js-cluster-authentication-failure js-cluster-api-unreachable') do |c|
alert_options: { class: 'hidden js-cluster-authentication-failure js-cluster-api-unreachable' }) do |c|
= c.body do
= s_('ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid.')

View File

@ -1,4 +1,4 @@
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_class: 'gl-mt-6 gl-mb-3') do |c|
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mt-6 gl-mb-3' }) do |c|
= c.body do
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
- issue_link_start = link_start % { url: 'https://gitlab.com/gitlab-org/configure/general/-/issues/199' }

View File

@ -1,8 +1,8 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
= render Pajamas::AlertComponent.new(title: s_('ClusterIntegration|Did you know?'),
alert_class: 'gcp-signup-offer',
alert_data: { feature_id: Users::CalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: callouts_path }) do |c|
alert_options: { class: 'gcp-signup-offer',
data: { feature_id: Users::CalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: callouts_path }}) do |c|
= c.body do
= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
= c.actions do

View File

@ -3,7 +3,7 @@
.sub-section
%h4= s_('GroupSettings|Export group')
%p= _('Export this group with all related data.')
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_class: 'gl-mb-4') do |c|
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-4' }) do |c|
= c.body do
- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md') }
- docs_link_end = '</a>'.html_safe
@ -12,7 +12,7 @@
- export_information = _('After the export is complete, download the data file from a notification email or from this page. You can then import the data file from the %{strong_text_start}Create new group%{strong_text_end} page of another GitLab instance.') % { strong_text_start: '<strong>'.html_safe, strong_text_end: '</strong>'.html_safe}
= export_information.html_safe
= link_to _('Learn more.'), help_page_path('user/group/settings/import_export.md'), target: '_blank', rel: 'noopener noreferrer'
= render Pajamas::AlertComponent.new(dismissible: false, alert_class: 'gl-mb-5') do |c|
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
%p.gl-mb-0
%p= _('The following items will be exported:')

View File

@ -1,7 +1,7 @@
- remove_form_id = local_assigns.fetch(:remove_form_id, nil)
- if group.paid?
= render Pajamas::AlertComponent.new(dismissible: false, alert_class: 'gl-mb-5', alert_data: { testid: 'group-has-linked-subscription-alert' }) do |c|
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5', data: { testid: 'group-has-linked-subscription-alert' }}) do |c|
= c.body do
= html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }

View File

@ -13,7 +13,7 @@
%li= s_('GroupSettings|You will need to update your local repositories to point to the new location.')
%li= s_("GroupSettings|If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
- if group.paid?
= render Pajamas::AlertComponent.new(dismissible: false, alert_class: 'gl-mb-5') do |c|
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
= html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-transfer-group-form{ data: initial_data }

View File

@ -1,7 +1,7 @@
- if @errors.present?
= render Pajamas::AlertComponent.new(variant: :danger,
dismissible: false,
alert_class: 'gl-mb-5') do |c|
alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
- @errors.each do |error|
= error

View File

@ -2,10 +2,10 @@
= render Pajamas::AlertComponent.new(title: _('Anyone can register for an account.'),
variant: :warning,
alert_class: 'js-registration-enabled-callout',
alert_data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
dismiss_endpoint: callouts_path },
close_button_data: { testid: 'close-registration-enabled-callout' }) do |c|
alert_options: { class: 'js-registration-enabled-callout',
data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
dismiss_endpoint: callouts_path }},
close_button_options: { data: { testid: 'close-registration-enabled-callout' }}) do |c|
= c.body do
= _('Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable.')
= c.actions do

View File

@ -4,11 +4,11 @@
- return unless banner_info.present?
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'js-storage-enforcement-banner',
alert_data: { feature_id: banner_info[:callouts_feature_name],
dismiss_endpoint: banner_info[:callouts_path],
group_id: namespace.id,
defer_links: "true" }) do |c|
alert_options: { class: 'js-storage-enforcement-banner',
data: { feature_id: banner_info[:callouts_feature_name],
dismiss_endpoint: banner_info[:callouts_path],
group_id: namespace.id,
defer_links: "true" }}) do |c|
= c.body do
= banner_info[:text]
= banner_info[:learn_more_link]

View File

@ -2,15 +2,15 @@
- @content_class = "limit-container-width" unless fluid_layout
- if current_user.ldap_user?
= render Pajamas::AlertComponent.new(alert_class: 'gl-my-5',
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-my-5' },
dismissible: false) do |c|
= c.body do
= s_('Profiles|Some options are unavailable for LDAP accounts')
- if params[:two_factor_auth_enabled_successfully]
= render Pajamas::AlertComponent.new(variant: :success,
alert_class: 'gl-my-5',
close_button_class: 'js-close-2fa-enabled-success-alert') do |c|
alert_options: { class: 'gl-my-5' },
close_button_options: { class: 'js-close-2fa-enabled-success-alert' }) do |c|
= c.body do
= html_escape(_('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can %{anchorOpen}use that key to generate additional recovery codes%{anchorClose}.')) % { anchorOpen: '<a href="%{href}">'.html_safe % { href: help_page_path('user/profile/account/two_factor_authentication', anchor: 'generate-new-recovery-codes-using-ssh') }, anchorClose: '</a>'.html_safe }

View File

@ -3,7 +3,7 @@
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
alert_class: 'project-deletion-failed-message') do |c|
alert_options: { class: 'project-deletion-failed-message' }) do |c|
= c.body do
This project was scheduled for deletion, but failed with the following message:
= project.delete_error

View File

@ -1,8 +1,8 @@
- event = last_push_event
- if event && show_last_push_widget?(event)
= render Pajamas::AlertComponent.new(variant: :success,
alert_class: 'gl-mt-3',
close_button_class: 'js-close-banner') do |c|
alert_options: { class: 'gl-mt-3' },
close_button_options: { class: 'js-close-banner' }) do |c|
= c.body do
%span= s_("LastPushEvent|You pushed to")
%strong.gl-display-inline-flex.gl-max-w-50p{ data: { toggle: 'tooltip' }, title: event.ref_name }

View File

@ -37,7 +37,7 @@
- link_start_group_path = '<a href="%{path}">' % { path: new_group_path }
- project_tip = s_('ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}') % { link_start: link_start_group_path, link_end: '</a>' }
= project_tip.html_safe
= render Pajamas::AlertComponent.new(alert_class: "gl-mb-4 gl-display-none js-user-readme-repo",
= render Pajamas::AlertComponent.new(alert_options: { class: "gl-mb-4 gl-display-none js-user-readme-repo" },
dismissible: false,
variant: :success) do |c|
= c.body do

View File

@ -16,7 +16,7 @@
%br
= render Pajamas::AlertComponent.new(variant: :danger,
alert_class: 'dropzone-alerts gl-alert gl-alert-danger gl-mb-5 data gl-display-none',
alert_options: { class: 'dropzone-alerts gl-alert gl-alert-danger gl-mb-5 data gl-display-none' },
dismissible: false)
= render 'shared/new_commit_form', placeholder: placeholder, ref: local_assigns[:ref]

View File

@ -4,7 +4,7 @@
- webpack_preload_asset_tag('monaco')
- if @conflict
= render Pajamas::AlertComponent.new(alert_class: 'gl-mb-5 gl-mt-5',
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-mb-5 gl-mt-5' },
variant: :danger,
dismissible: false) do |c|
- blob_url = project_blob_path(@project, @id)

View File

@ -2,7 +2,7 @@
- if @forked_project && !@forked_project.saved?
= render Pajamas::AlertComponent.new(title: _('Fork Error!'),
variant: :danger,
alert_class: 'gl-mt-5',
alert_options: { class: 'gl-mt-5' },
dismissible: false) do |c|
= c.body do
%p

View File

@ -3,6 +3,6 @@
- service_desk_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: service_desk_link_url }
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'hide js-alert-moved-from-service-desk-warning gl-mt-5') do |c|
alert_options: { class: 'hide js-alert-moved-from-service-desk-warning gl-mt-5' }) do |c|
= c.body do
= s_('This project does not have %{service_desk_link_start}Service Desk%{service_desk_link_end} enabled, so the user who created the issue will no longer receive email notifications about new activity.').html_safe % { service_desk_link_start: service_desk_link_start, service_desk_link_end: '</a>'.html_safe }

View File

@ -7,7 +7,7 @@
= cache(cache_key, expires_in: 1.day) do
- if @merge_request.closed_or_merged_without_fork?
= render Pajamas::AlertComponent.new(alert_class: 'gl-mb-5',
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-mb-5' },
variant: :danger,
dismissible: false) do |c|
= c.body do

View File

@ -13,8 +13,8 @@
- if can?(current_user, :read_issue, @project) && @milestone.total_issues_count == 0
= render Pajamas::AlertComponent.new(dismissible: false,
alert_data: { testid: 'no-issues-alert' },
alert_class: 'gl-mt-3 gl-mb-5') do |c|
alert_options: { class: 'gl-mt-3 gl-mb-5',
data: { testid: 'no-issues-alert' }}) do |c|
= c.body do
= _('Assign some issues to this milestone.')
- else

View File

@ -2,7 +2,7 @@
- default_ref = params[:ref] || @project.default_branch
- if @error
= render Pajamas::AlertComponent.new(variant: :danger, dismissible: true, close_button_class: 'gl-alert-dismiss') do |c|
= render Pajamas::AlertComponent.new(variant: :danger, dismissible: true, close_button_options: { class: 'gl-alert-dismiss' }) do |c|
= c.body do
= @error

View File

@ -2,7 +2,7 @@
= render Pajamas::AlertComponent.new(title: _('Repository usage recalculation started'),
variant: :info,
alert_class: 'js-recalculation-started-alert gl-mt-4 gl-mb-5 gl-display-none') do |c|
alert_options: { class: 'js-recalculation-started-alert gl-mt-4 gl-mb-5 gl-display-none' }) do |c|
= c.body do
= _('To view usage, refresh this page in a few minutes.')

View File

@ -1,7 +1,7 @@
- if show_auto_devops_implicitly_enabled_banner?(project, current_user)
= render Pajamas::AlertComponent.new(alert_class: 'qa-auto-devops-banner auto-devops-implicitly-enabled-banner',
close_button_class: 'hide-auto-devops-implicitly-enabled-banner',
close_button_data: { project_id: project.id }) do |c|
= render Pajamas::AlertComponent.new(alert_options: { class: 'qa-auto-devops-banner auto-devops-implicitly-enabled-banner' },
close_button_options: { class: 'hide-auto-devops-implicitly-enabled-banner',
data: { project_id: project.id }}) do |c|
= c.body do
= s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found.")
- unless Gitlab.config.registry.enabled

View File

@ -22,9 +22,9 @@
= f.text_field :import_url, value: import_url.sanitized_url,
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
= render Pajamas::AlertComponent.new(variant: :danger,
alert_class: 'gl-mt-3 js-import-url-error hide',
alert_options: { class: 'gl-mt-3 js-import-url-error hide' },
dismissible: false,
close_button_class: 'js-close-2fa-enabled-success-alert') do |c|
close_button_options: { class: 'js-close-2fa-enabled-success-alert' }) do |c|
= c.body do
= s_('Import|There is not a valid Git repository at this URL. If your HTTP repository is not publicly accessible, verify your credentials.')
= render_if_exists 'shared/ee/import_form', f: f, ci_cd_only: ci_cd_only

View File

@ -1,7 +1,7 @@
- if show_no_password_message?
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'js-no-password-message',
close_button_class: 'js-hide-no-password-message') do |c|
alert_options: { class: 'js-no-password-message' },
close_button_options: { class: 'js-hide-no-password-message' }) do |c|
= c.body do
= no_password_message
= c.actions do

View File

@ -1,7 +1,7 @@
- if show_no_ssh_key_message?
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'js-no-ssh-message',
close_button_class: 'js-hide-no-ssh-message') do |c|
alert_options: { class: 'js-no-ssh-message' },
close_button_options: { class: 'js-hide-no-ssh-message'}) do |c|
= c.body do
= s_("MissingSSHKeyWarningLink|You can't push or pull repositories using SSH until you add an SSH key to your profile.")
= c.actions do

View File

@ -1,7 +1,7 @@
- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project? && current_user.projects_limit > 0
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
alert_class: 'project-limit-message') do |c|
alert_options: { class: 'project-limit-message' }) do |c|
= c.body do
= _("You won't be able to create new projects because you have reached your project limit.")
= c.actions do

View File

@ -1,5 +1,5 @@
- if session[:ask_for_usage_stats_consent]
= render Pajamas::AlertComponent.new(alert_class: 'service-ping-consent-message') do |c|
= render Pajamas::AlertComponent.new(alert_options: { class: 'service-ping-consent-message' }) do |c|
= c.body do
- docs_link = link_to _('collect usage information'), help_page_path('user/admin_area/settings/usage_statistics.md'), class: 'gl-link'
- settings_link = link_to _('your settings'), metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), class: 'gl-link'

View File

@ -1,9 +1,9 @@
= render Pajamas::AlertComponent.new(variant: :warning,
alert_class: 'js-recovery-settings-callout gl-mt-5',
alert_data: { feature_id: Users::CalloutsHelper::TWO_FACTOR_AUTH_RECOVERY_SETTINGS_CHECK,
dismiss_endpoint: callouts_path,
defer_links: 'true' },
close_button_data: { testid: 'close-account-recovery-regular-check-callout' }) do |c|
alert_options: { class: 'js-recovery-settings-callout gl-mt-5',
data: { feature_id: Users::CalloutsHelper::TWO_FACTOR_AUTH_RECOVERY_SETTINGS_CHECK,
dismiss_endpoint: callouts_path,
defer_links: 'true' }},
close_button_options: { data: { testid: 'close-account-recovery-regular-check-callout' }}) do |c|
= c.body do
= s_('Profiles|Ensure you have two-factor authentication recovery codes stored in a safe place.')
= link_to _('Learn more.'), help_page_path('user/profile/account/two_factor_authentication', anchor: 'recovery-codes'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -1,4 +1,4 @@
= render Pajamas::AlertComponent.new(alert_class: 'gl-my-5',
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-my-5' },
variant: :danger,
dismissible: false,
title: reason) do |c|

View File

@ -8,7 +8,7 @@
- if @conflict
= render Pajamas::AlertComponent.new(variant: :danger,
dismissible: false,
alert_class: 'gl-mb-5') do |c|
alert_options: { class: 'gl-mb-5' }) do |c|
= c.body do
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
Please check out

View File

@ -6,7 +6,7 @@
.dropdown-page-two.dropdown-new-label
= dropdown_title(create_label_title(subject), options: { back: true, close: show_close })
= dropdown_content do
= render Pajamas::AlertComponent.new(variant: :danger, alert_class: 'js-label-error gl-mb-3', dismissible: false)
= render Pajamas::AlertComponent.new(variant: :danger, alert_options: { class: 'js-label-error gl-mb-3' }, dismissible: false)
%input#new_label_name.default-dropdown-input{ type: "text", placeholder: _('Name new label') }
.suggest-colors.suggest-colors-dropdown
= render_suggested_colors

View File

@ -37,7 +37,7 @@
data: { placeholder: _('Select branch'), endpoint: refs_project_path(@project, sort: 'updated_desc', find: 'branches') }})
- if source_level < target_level
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_class: 'gl-mb-4') do |c|
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-4' }) do |c|
= c.body do
= visibilityMismatchString
%br

View File

@ -2,7 +2,7 @@
- if milestone.complete? && milestone.active?
= render Pajamas::AlertComponent.new(variant: :success,
alert_data: { testid: 'all-issues-closed-alert' },
alert_options: { data: { testid: 'all-issues-closed-alert' }},
dismissible: false) do |c|
= c.body do
= yield

View File

@ -4,4 +4,4 @@
- deletion_date = inactive_project_deletion_date(@project)
- title = _('Due to inactivity, this project is scheduled to be deleted on %{deletion_date}. %{link_start}Why is this scheduled?%{link_end}').html_safe % { deletion_date: deletion_date, link_start: link_start, link_end: link_end }
= render Pajamas::AlertComponent.new(title: title, variant: :warning, alert_class: 'gl-pb-3', dismissible: false)
= render Pajamas::AlertComponent.new(title: title, variant: :warning, alert_options: { class: 'gl-pb-3' }, dismissible: false)

View File

@ -1,14 +1,14 @@
- alert_class = 'gl-mb-5'
- alert_options = { class: 'gl-mb-5' }
- if runner.group_type?
= render Pajamas::AlertComponent.new(alert_class: alert_class,
= render Pajamas::AlertComponent.new(alert_options: alert_options,
title: s_('Runners|This runner is available to all projects and subgroups in a group.'),
dismissible: false) do |c|
= c.body do
= s_('Runners|Use Group runners when you want all projects in a group to have access to a set of runners.')
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'group-runners'), target: '_blank', rel: 'noopener noreferrer'
- else
= render Pajamas::AlertComponent.new(alert_class: alert_class,
= render Pajamas::AlertComponent.new(alert_options: alert_options,
title: s_('Runners|This runner is associated with specific projects.'),
dismissible: false) do |c|
= c.body do

View File

@ -14,6 +14,7 @@ first: '\b([A-Z]{3,5})\b'
second: '(?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)'
# ... with the exception of these:
exceptions:
- ACL
- AJAX
- ANSI
- APAC

View File

@ -18,8 +18,8 @@ If you have any doubts about the consistency of the data on this site, we recomm
## Configure the former **primary** site to be a **secondary** site
Since the former **primary** site will be out of sync with the current **primary** site, the first step is to bring the former **primary** site up to date. Note, deletion of data stored on disk like
repositories and uploads will not be replayed when bringing the former **primary** site back
Since the former **primary** site is out of sync with the current **primary** site, the first step is to bring the former **primary** site up to date. Note, deletion of data stored on disk like
repositories and uploads is not replayed when bringing the former **primary** site back
into sync, which may result in increased disk usage.
Alternatively, you can [set up a new **secondary** GitLab instance](../setup/index.md) to avoid this.

View File

@ -143,7 +143,7 @@ If the **primary** site uses custom or self-signed TLS certificates to secure in
### Ensure Geo replication is up-to-date
The maintenance window won't end until Geo replication and verification is
The maintenance window does not end until Geo replication and verification is
completely finished. To keep the window as short as possible, you should
ensure these processes are close to 100% as possible during active use.
@ -201,7 +201,7 @@ be disabled on the **primary** site:
## Finish replicating and verifying all data
NOTE:
GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary site statuses will appear to stop updating and become unhealthy. For more information, see [Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](../replication/troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary site statuses appears to stop updating and become unhealthy. For more information, see [Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](../replication/troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
1. If you are manually replicating any data not managed by Geo, trigger the
final replication process now.

View File

@ -32,10 +32,10 @@ To ensure that problems with pipelines (for example, syncs failing too many time
the number of concurrent syncs falls below `repos_max_capacity` and there are no new projects waiting to be synced.
Geo also has a checksum feature which runs a SHA256 sum across all the Git references to the SHA values.
If the refs don't match between the **primary** site and the **secondary** site, then the **secondary** site will mark that project as dirty and try to resync it.
If the refs don't match between the **primary** site and the **secondary** site, then the **secondary** site marks that project as dirty and try to resync it.
So even if we have an outdated tracking database, the validation should activate and find discrepancies in the repository state and resync.
## Can I use Geo in a disaster recovery situation?
## Can you use Geo in a disaster recovery situation?
Yes, but there are limitations to what we replicate (see
[What data is replicated to a **secondary** site?](#what-data-is-replicated-to-a-secondary-site)).
@ -46,7 +46,7 @@ Read the documentation for [Disaster Recovery](../disaster_recovery/index.md).
We currently replicate project repositories, LFS objects, generated
attachments and avatars, and the whole database. This means user accounts,
issues, merge requests, groups, project data, and so on, will be available for
issues, merge requests, groups, project data, and so on, are available for
query.
For more details, see the [supported Geo data types](datatypes.md).
@ -69,6 +69,6 @@ That's totally fine. We use HTTP(s) to fetch repository changes from the **prima
Yes. See [Docker Registry for a **secondary** site](docker_registry.md).
## Can I login to a secondary site?
## Can you login to a secondary site?
Yes, but secondary sites receive all authentication data (like user accounts and logins) from the primary instance. This means you are re-directed to the primary for authentication and then routed back.

View File

@ -44,7 +44,7 @@ Once GitLab has been uninstalled from each node on the **secondary** site, the r
```
NOTE:
Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
Using `gitlab-rails dbconsole` does not work, because managing replication slots requires superuser permissions.
1. Find the name of the relevant replication slot. This is the slot that is specified with `--slot-name` when running the replicate command: `gitlab-ctl replicate-geo-database`.

View File

@ -398,7 +398,7 @@ where some queries never complete due to being canceled on every replication.
These long-running queries are
[planned to be removed in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/34269),
but as a workaround, we recommend enabling
[hot_standby_feedback](https://www.postgresql.org/docs/10/hot-standby.html#HOT-STANDBY-CONFLICT).
[`hot_standby_feedback`](https://www.postgresql.org/docs/10/hot-standby.html#HOT-STANDBY-CONFLICT).
This increases the likelihood of bloat on the **primary** node as it prevents
`VACUUM` from removing recently-dead rows. However, it has been used
successfully in production on GitLab.com.
@ -767,7 +767,7 @@ The appropriate action sometimes depends on the cause. For example, you can remo
In some cases, a file may be determined to be of low value, and so it may be worth deleting the record.
Geo itself is an excellent mitigation for files missing on the primary. If a file disappears on the primary but it was already synced to the secondary, you can grab the secondary's file. In cases like this, the `File is not checksummable` error message will not occur on Geo secondary sites, and only the primary will log this error message.
Geo itself is an excellent mitigation for files missing on the primary. If a file disappears on the primary but it was already synced to the secondary, you can grab the secondary's file. In cases like this, the `File is not checksummable` error message does not occur on Geo secondary sites, and only the primary logs this error message.
This problem is more likely to show up in Geo secondary sites which were set up long after the original GitLab site. In this case, Geo is only surfacing an existing problem.
@ -1104,9 +1104,9 @@ If using a load balancer, ensure that the load balancer's URL is set as the `ext
### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
In GitLab 13.9 through GitLab 14.3, when [GitLab Maintenance Mode](../../maintenance_mode/index.md) is enabled, the status of Geo secondary sites will stop getting updated. After 10 minutes, the status changes to `Unhealthy`.
In GitLab 13.9 through GitLab 14.3, when [GitLab Maintenance Mode](../../maintenance_mode/index.md) is enabled, the status of Geo secondary sites stops getting updated. After 10 minutes, the status changes to `Unhealthy`.
Geo secondary sites will continue to replicate and verify data, and the secondary sites should still be usable. You can use the [Sync status Rake task](#sync-status-rake-task) to determine the actual status of a secondary site during Maintenance Mode.
Geo secondary sites continue to replicate and verify data, and the secondary sites should still be usable. You can use the [Sync status Rake task](#sync-status-rake-task) to determine the actual status of a secondary site during Maintenance Mode.
This bug was [fixed in GitLab 14.4](https://gitlab.com/gitlab-org/gitlab/-/issues/292983).

View File

@ -25,7 +25,7 @@ On the **primary** site:
- Container repositories synchronization concurrency limit
- Verification concurrency limit
Increasing the concurrency values will increase the number of jobs that are scheduled.
Increasing the concurrency values increases the number of jobs that are scheduled.
However, this may not lead to more downloads in parallel unless the number of
available Sidekiq threads is also increased. For example, if repository synchronization
concurrency is increased from 25 to 50, you may also want to increase the number

View File

@ -22,7 +22,7 @@ Upgrading Geo sites involves performing:
NOTE:
These general upgrade steps are not intended for multi-site deployments,
and will cause downtime. If you want to avoid downtime, consider using
and cause downtime. If you want to avoid downtime, consider using
[zero downtime upgrades](../../../update/zero_downtime.md#multi-node--ha-deployment-with-geo).
To upgrade the Geo sites when a new GitLab version is released, upgrade **primary**

View File

@ -11,9 +11,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
After you set up the [database replication and configure the Geo nodes](../index.md#setup-instructions), use your closest GitLab site as you would do with the primary one.
You can push directly to a **secondary** site (for both HTTP, SSH including
Git LFS), and the request will be proxied to the primary site instead.
Git LFS), and the request is proxied to the primary site instead.
Example of the output you will see when pushing to a **secondary** site:
Example of the output you see when pushing to a **secondary** site:
```shell
$ git push
@ -31,7 +31,7 @@ If you're using HTTPS instead of [SSH](../../../user/ssh.md) to push to the seco
you can't store credentials in the URL like `user:password@URL`. Instead, you can use a
[`.netrc` file](https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html)
for Unix-like operating systems or `_netrc` for Windows. In that case, the credentials
will be stored as a plain text. If you're looking for a more secure way to store credentials,
are stored as a plain text. If you're looking for a more secure way to store credentials,
you can use [Git Credential Storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
## Fetch Go modules from Geo secondary sites

View File

@ -51,7 +51,7 @@ developed and tested. We aim to be compatible with most external
gitlab-ctl set-geo-primary-node
```
This command will use your defined `external_url` in `/etc/gitlab/gitlab.rb`.
This command uses your defined `external_url` in `/etc/gitlab/gitlab.rb`.
### Configure the external database to be replicated
@ -64,7 +64,7 @@ To set up an external database, you can either:
Given you have a primary node set up on AWS EC2 that uses RDS.
You can now just create a read-only replica in a different region and the
replication process will be managed by AWS. Make sure you've set Network ACL, Subnet, and
replication process is managed by AWS. Make sure you've set Network ACL (Access Control List), Subnet, and
Security Group according to your needs, so the secondary application node can access the database.
The following instructions detail how to create a read-only replica for common

View File

@ -19,7 +19,7 @@ The steps below should be followed in the order they appear. **Make sure the Git
If you installed GitLab using the Omnibus packages (highly recommended):
1. [Install GitLab Enterprise Edition](https://about.gitlab.com/install/) on the nodes that will serve as the **secondary** site. Do not create an account or log in to the new **secondary** site. The **GitLab version must match** across primary and secondary sites.
1. [Install GitLab Enterprise Edition](https://about.gitlab.com/install/) on the nodes that serve as the **secondary** site. Do not create an account or log in to the new **secondary** site. The **GitLab version must match** across primary and secondary sites.
1. [Add the GitLab License](../../../user/admin_area/license.md) on the **primary** site to unlock Geo. The license must be for [GitLab Premium](https://about.gitlab.com/pricing/) or higher.
1. [Set up the database replication](database.md) (`primary (read-write) <-> secondary (read-only)` topology).
1. [Configure fast lookup of authorized SSH keys in the database](../../operations/fast_ssh_key_lookup.md). This step is required and needs to be done on **both** the **primary** and **secondary** sites.

View File

@ -33,7 +33,7 @@ sudo -u git -H bundle exec rake geo:git:housekeeping:incremental_repack RAILS_EN
### Full Repack
This is equivalent of running `git repack -d -A --pack-kept-objects` on a
_bare_ repository which will optionally, write a reachability bitmap index
_bare_ repository which optionally, writes a reachability bitmap index
when this is enabled in GitLab.
**Omnibus Installation**

View File

@ -6,21 +6,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Release links API **(FREE)**
> Support for [GitLab CI/CD job token](../../ci/jobs/ci_job_token.md) authentication [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250819) in GitLab 15.1.
Use this API to manipulate GitLab [Release](../../user/project/releases/index.md)
links. For manipulating other Release assets, see [Release API](index.md).
GitLab supports links to `http`, `https`, and `ftp` assets.
## Authentication
For authentication, the Release Links API accepts:
- A [Personal Access Token](../../user/profile/personal_access_tokens.md) using the
`PRIVATE-TOKEN` header.
- A [Project Access Token](../../user/project/settings/project_access_tokens.md) using the `PRIVATE-TOKEN` header.
The [GitLab CI/CD job token](../../ci/jobs/ci_job_token.md) `$CI_JOB_TOKEN` is not supported. See [GitLab issue #50819](https://gitlab.com/gitlab-org/gitlab/-/issues/250819) for more details.
## Get links
Get assets as links from a Release.

View File

@ -1,7 +1,8 @@
---
stage: Data Stores
group: Database
comments: false
description: 'Database Scalability / Limit table sizes'
group: database
---
# Database Scalability: Limit on-disk table size to < 100 GB for GitLab.com

View File

@ -20,7 +20,7 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints
- [Get job artifacts](../../api/job_artifacts.md#get-job-artifacts).
- [Get job token's job](../../api/jobs.md#get-job-tokens-job).
- [Pipeline triggers](../../api/pipeline_triggers.md), using the `token=` parameter.
- [Releases](../../api/releases/index.md).
- [Releases](../../api/releases/index.md) and [Release links](../../api/releases/links.md).
- [Terraform plan](../../user/infrastructure/index.md).
NOTE:

View File

@ -33,15 +33,15 @@ for new events and creates background jobs for each specific event type.
For example when a repository is updated, the Geo **primary** site creates
a Geo event with an associated repository updated event. The Geo Log Cursor daemon
picks the event up and schedules a `Geo::ProjectSyncWorker` job which will
use the `Geo::RepositorySyncService` and `Geo::WikiSyncService` classes
picks the event up and schedules a `Geo::ProjectSyncWorker` job which
uses the `Geo::RepositorySyncService` and `Geo::WikiSyncService` classes
to update the repository and the wiki respectively.
The Geo Log Cursor daemon can operate in High Availability mode automatically.
The daemon will try to acquire a lock from time to time and once acquired, it
will behave as the *active* daemon.
The daemon tries to acquire a lock from time to time and once acquired, it
behaves as the *active* daemon.
Any additional running daemons on the same site, will be in standby
Any additional running daemons on the same site, is in standby
mode, ready to resume work if the *active* daemon releases its lock.
We use the [`ExclusiveLease`](https://www.rubydoc.info/github/gitlabhq/gitlabhq/Gitlab/ExclusiveLease) lock type with a small TTL, that is renewed at every

View File

@ -59,7 +59,7 @@ naming conventions:
consume) events. It takes care of the communication between the
primary site (where events are produced) and the secondary site
(where events are consumed). The engineer who wants to incorporate
Geo in their feature will use the API of replicators to make this
Geo in their feature uses the API of replicators to make this
happen.
- **Geo Domain-Specific Language**:
@ -99,7 +99,7 @@ end
The class name should be unique. It also is tightly coupled to the
table name for the registry, so for this example the registry table
will be `package_file_registry`.
is `package_file_registry`.
For the different data types Geo supports there are different
strategies to include. Pick one that fits your needs.

View File

@ -1,7 +1,7 @@
---
type: reference, dev
stage: create
group: code_review
stage: Create
group: Code Review
info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
---

View File

@ -1015,9 +1015,9 @@ more of the following options:
- `BACKUP=timestamp_of_backup`: Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
- `force=yes`: Doesn't ask if the authorized_keys file should get regenerated,
- `force=yes`: Doesn't ask if the `authorized_keys` file should get regenerated,
and assumes 'yes' for warning about database tables being removed,
enabling the "Write to authorized_keys file" setting, and updating LDAP
enabling the `Write to authorized_keys file` setting, and updating LDAP
providers.
If you're restoring into directories that are mount points, you must ensure these directories are
@ -1407,7 +1407,7 @@ There is an **experimental** script that attempts to automate this process in
## Back up and restore for installations using PgBouncer
Do NOT back up or restore GitLab through a PgBouncer connection. These
Do not back up or restore GitLab through a PgBouncer connection. These
tasks must [bypass PgBouncer and connect directly to the PostgreSQL primary database node](#bypassing-pgbouncer),
or they cause a GitLab outage.
@ -1418,7 +1418,7 @@ following error message is shown:
ActiveRecord::StatementInvalid: PG::UndefinedTable
```
Each time the GitLab backup runs, GitLab will start generating 500 errors and errors about missing
Each time the GitLab backup runs, GitLab starts generating 500 errors and errors about missing
tables will [be logged by PostgreSQL](../administration/logs.md#postgresql-logs):
```plaintext
@ -1480,7 +1480,7 @@ WARNING:
Avoid uncoordinated data processing by both the new and old servers, where multiple
servers could connect concurrently and process the same data. For example, when using
[incoming email](../administration/incoming_email.md), if both GitLab instances are
processing email at the same time, then both instances will end up missing some data.
processing email at the same time, then both instances miss some data.
This type of problem can occur with other services as well, such as a
[non-packaged database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server),
a non-packaged Redis instance, or non-packaged Sidekiq.

View File

@ -1,7 +1,7 @@
---
type: reference
stage: Manage
group: Authentication & Authorization
group: Authentication and Authorization
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

View File

@ -87,6 +87,26 @@ To edit an epic's start date, due date, or labels:
1. Next to each section in the right sidebar, select **Edit**.
1. Select the dates or labels for your epic.
### Reorder list items in the epic description
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15260) in GitLab 15.1.
When you view an epic that has a list in the description, you can also reorder the list items.
Prerequisites:
- You must have at least the Reporter role for the project, be the author of the epic, or be
assigned to the epic.
- The epic's description must have an [ordered, unordered](../../markdown.md#lists), or
[task](../../markdown.md#task-lists) list.
To reorder list items, when viewing an epic:
1. Hover over the list item row to make the drag icon (**{drag-vertical}**) visible.
1. Select and hold the drag icon.
1. Drag the row to the new position in the list.
1. Release the drag icon.
## Bulk edit epics
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7250) in GitLab 12.2.

View File

@ -29,6 +29,7 @@ module API
params do
use :pagination
end
route_setting :authentication, job_token_allowed: true
get 'links' do
authorize! :read_release, release
@ -45,6 +46,7 @@ module API
optional :filepath, type: String, desc: 'The filepath of the link'
optional :link_type, type: String, desc: 'The link type, one of: "runbook", "image", "package" or "other"'
end
route_setting :authentication, job_token_allowed: true
post 'links' do
authorize! :create_release, release
@ -65,6 +67,7 @@ module API
detail 'This feature was introduced in GitLab 11.7.'
success Entities::Releases::Link
end
route_setting :authentication, job_token_allowed: true
get do
authorize! :read_release, release
@ -82,6 +85,7 @@ module API
optional :link_type, type: String, desc: 'The link type'
at_least_one_of :name, :url
end
route_setting :authentication, job_token_allowed: true
put do
authorize! :update_release, release
@ -96,6 +100,7 @@ module API
detail 'This feature was introduced in GitLab 11.7.'
success Entities::Releases::Link
end
route_setting :authentication, job_token_allowed: true
delete do
authorize! :destroy_release, release

View File

@ -12,6 +12,10 @@ module Gitlab
def name
@name || _('An unauthenticated user')
end
def impersonated?
false
end
end
end
end

View File

@ -3,9 +3,10 @@ module Gitlab
module Diff
module Rendered
module Notebook
include Gitlab::Utils::StrongMemoize
class DiffFile < Gitlab::Diff::File
include Gitlab::Diff::Rendered::Notebook::DiffFileHelper
include Gitlab::Utils::StrongMemoize
RENDERED_TIMEOUT_BACKGROUND = 10.seconds
RENDERED_TIMEOUT_FOREGROUND = 1.5.seconds
BACKGROUND_EXECUTION = 'background'
@ -14,7 +15,6 @@ module Gitlab
LOG_IPYNBDIFF_TIMEOUT = 'IPYNB_DIFF_TIMEOUT'
LOG_IPYNBDIFF_INVALID = 'IPYNB_DIFF_INVALID'
LOG_IPYNBDIFF_TRUNCATED = 'IPYNB_DIFF_TRUNCATED'
EMBEDDED_IMAGE_PATTERN = ' ![](data:image'
attr_reader :source_diff
@ -51,7 +51,8 @@ module Gitlab
def highlighted_diff_lines
@highlighted_diff_lines ||= begin
removal_line_maps, addition_line_maps = compute_end_start_map
removal_line_maps, addition_line_maps = map_diff_block_to_source_line(
source_diff.highlighted_diff_lines, source_diff.new_file?, source_diff.deleted_file?)
Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight.map do |line|
mutate_line(line, addition_line_maps, removal_line_maps)
end
@ -90,55 +91,6 @@ module Gitlab
diff
end
def strip_diff_frontmatter(diff_content)
diff_content.scan(/.*\n/)[2..]&.join('') if diff_content.present?
end
def transformed_line_to_source(transformed_line, transformed_blocks)
transformed_blocks.empty? ? 0 : ( transformed_blocks[transformed_line - 1][:source_line] || -1 ) + 1
end
def mutate_line(line, addition_line_maps, removal_line_maps)
line.new_pos = transformed_line_to_source(line.new_pos, notebook_diff.to.blocks)
line.old_pos = transformed_line_to_source(line.old_pos, notebook_diff.from.blocks)
line.old_pos = addition_line_maps[line.new_pos] if line.old_pos == 0 && line.new_pos != 0
line.new_pos = removal_line_maps[line.old_pos] if line.new_pos == 0 && line.old_pos != 0
# Lines that do not appear on the original diff should not be commentable
line.type = "#{line.type || 'unchanged'}-nomappinginraw" unless addition_line_maps[line.new_pos] || removal_line_maps[line.old_pos]
line.line_code = line_code(line)
line.rich_text = image_as_rich_text(line)
line
end
def compute_end_start_map
# line_codes are used for assigning notes to diffs, and these depend on the line on the new version and the
# line that would have been that one in the previous version. However, since we do a transformation on the
# file, that map gets lost. To overcome this, we look at the original source lines and build two maps:
# - For additions, we look at the latest line change for that line and pick the old line for that id
# - For removals, we look at the first line in the old version, and pick the first line on the new version
#
#
# The caveat here is that we can't have notes on lines that are not a translation of a line in the source
# diff
#
# (gitlab/diff/file.rb:75)
removals = {}
additions = {}
source_diff.highlighted_diff_lines.each do |line|
removals[line.old_pos] = line.new_pos unless source_diff.new_file?
additions[line.new_pos] = line.old_pos unless source_diff.deleted_file?
end
[removals, additions]
end
def rendered_timeout
@rendered_timeout ||= Gitlab::Metrics.counter(
:ipynb_semantic_diff_timeouts_total,
@ -156,16 +108,27 @@ module Gitlab
nil
end
def image_as_rich_text(line)
# Strip the initial +, -, or space for the diff context
line_text = line.text[1..]
def compute_line_numbers(transformed_old_pos, transformed_new_pos, addition_line_maps, removal_line_maps)
new_pos = map_transformed_line_to_source(transformed_new_pos, notebook_diff.to.blocks)
old_pos = map_transformed_line_to_source(transformed_old_pos, notebook_diff.from.blocks)
if line_text.starts_with?(EMBEDDED_IMAGE_PATTERN)
image_body = line_text.delete_prefix(EMBEDDED_IMAGE_PATTERN).delete_suffix(')')
"<img src=\"data:image#{CGI.escapeHTML(image_body)}\">".html_safe
else
line.rich_text
end
old_pos = addition_line_maps[new_pos] if old_pos == 0 && new_pos != 0
new_pos = removal_line_maps[old_pos] if new_pos == 0 && old_pos != 0
[old_pos, new_pos]
end
def mutate_line(line, addition_line_maps, removal_line_maps)
line.old_pos, line.new_pos = compute_line_numbers(line.old_pos, line.new_pos, addition_line_maps, removal_line_maps)
# Lines that do not appear on the original diff should not be commentable
line.type = "#{line.type || 'unchanged'}-nomappinginraw" unless addition_line_maps[line.new_pos] || removal_line_maps[line.old_pos]
line.line_code = line_code(line)
line.rich_text = image_as_rich_text(line.text) || line.rich_text
line
end
end
end

View File

@ -0,0 +1,89 @@
# frozen_string_literal: true
module Gitlab
module Diff
module Rendered
module Notebook
module DiffFileHelper
EMBEDDED_IMAGE_PATTERN = ' ![](data:image'
def strip_diff_frontmatter(diff_content)
diff_content.scan(/.*\n/)[2..]&.join('') if diff_content.present?
end
def map_transformed_line_to_source(transformed_line, transformed_blocks)
transformed_blocks.empty? ? 0 : ( transformed_blocks[transformed_line - 1][:source_line] || -1 ) + 1
end
# line_codes are used for assigning notes to diffs, and these depend on the line on the new version and the
# line that would have been that one in the previous version. However, since we do a transformation on the
# file, that mapping gets lost. To overcome this, we look at the original source lines and build two maps:
# - For additions, we look at the latest line change for that line and pick the old line for that id
# - For removals, we look at the first line in the old version, and pick the first line on the new version
#
# Note: ipynb files never change the first or last line (open and closure of the
# json object), unless the file is removed or deleted
#
# Example: Additions and removals
# Old: New:
# A A
# B D
# C E
# F F
#
# Diff:
# 1 A A 1 | line code: 1_1
# 2 -B | line code: 2_2 -> new line is what it is after been without the removal, 2
# 3 -C | line code: 3_2
# + D 2 | line code: 4_2 -> old line is what would have been before the addition, 4
# + E 3 | line code: 4_3
# 4 F F 4 | line code: 4_4
#
# Example: only additions
# Old: New:
# A A
# F B
# C
# F
#
# Diff:
# A A | line code: 1_1
# + B | line code: 2_2 -> old line is the next after the additions, 2
# + C | line code: 2_3
# F F | line code: 2_4
#
# Example: only removals
# Old: New:
# A A
# B F
# C
# F
#
# Diff:
# A A | line code: 1_1
# -B | line code: 2_2 -> new line is what it is after been without the removal, 2
# -C | line code: 3_2
# F F | line code: 4_2
def map_diff_block_to_source_line(lines, file_added, file_deleted)
removals = {}
additions = {}
lines.each do |line|
removals[line.old_pos] = line.new_pos unless file_added
additions[line.new_pos] = line.old_pos unless file_deleted
end
[removals, additions]
end
def image_as_rich_text(line_text)
return unless line_text[1..].starts_with?(EMBEDDED_IMAGE_PATTERN)
image_body = line_text[1..].delete_prefix(EMBEDDED_IMAGE_PATTERN).delete_suffix(')')
"<img src=\"data:image#{CGI.escapeHTML(image_body)}\">".html_safe
end
end
end
end
end
end

View File

@ -34527,9 +34527,6 @@ msgstr ""
msgid "SecurityReports|Unable to add %{invalidProjectsMessage}: %{errorMessage}"
msgstr ""
msgid "SecurityReports|Unable to add %{invalidProjects}"
msgstr ""
msgid "SecurityReports|Undo dismiss"
msgstr ""
@ -35923,9 +35920,6 @@ msgstr ""
msgid "Something went wrong, unable to add %{project} to dashboard"
msgstr ""
msgid "Something went wrong, unable to add projects to dashboard"
msgstr ""
msgid "Something went wrong, unable to delete project"
msgstr ""

View File

@ -50,10 +50,12 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do
before do
render_inline described_class.new(
title: '_title_',
alert_class: '_alert_class_',
alert_data: {
feature_id: '_feature_id_',
dismiss_endpoint: '_dismiss_endpoint_'
alert_options: {
class: '_alert_class_',
data: {
feature_id: '_feature_id_',
dismiss_endpoint: '_dismiss_endpoint_'
}
}
)
end
@ -106,9 +108,11 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do
context 'with dismissible content' do
before do
render_inline described_class.new(
close_button_class: '_close_button_class_',
close_button_data: {
testid: '_close_button_testid_'
close_button_options: {
class: '_close_button_class_',
data: {
testid: '_close_button_testid_'
}
}
)
end
@ -138,35 +142,5 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do
end
end
end
context 'with alert_options' do
let(:options) { { alert_options: { id: 'test_id', class: 'baz', data: { foo: 'bar' } } } }
before do
render_inline described_class.new(**options)
end
it 'renders the extra options' do
expect(rendered_component).to have_css "#test_id.gl-alert.baz[data-foo='bar']"
end
context 'with custom classes or data' do
let(:options) do
{
variant: :danger,
alert_class: 'custom',
alert_data: { foo: 'bar' },
alert_options: {
class: 'extra special',
data: { foo: 'conflict' }
}
}
end
it 'doesn\'t conflict with internal alert_class or alert_data' do
expect(rendered_component).to have_css ".extra.special.custom.gl-alert.gl-alert-danger[data-foo='bar']"
end
end
end
end
end

View File

@ -0,0 +1,7 @@
---
extends:
- 'plugin:@gitlab/jest'
settings:
import/core-modules:
- '@pact-foundation/pact'
- jest-pact

View File

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

View File

@ -1,42 +1,34 @@
'use strict';
import { request } from 'axios';
const axios = require('axios');
export function getMetadata(endpoint) {
const { url } = endpoint;
exports.getMetadata = (endpoint) => {
const url = endpoint.url;
return request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
headers: { Accept: '*/*' },
}).then((response) => response.data);
}
return axios
.request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};
export function getDiscussions(endpoint) {
const { url } = endpoint;
exports.getDiscussions = (endpoint) => {
const url = endpoint.url;
return request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
headers: { Accept: '*/*' },
}).then((response) => response.data);
}
return axios
.request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};
export function getDiffs(endpoint) {
const { url } = endpoint;
exports.getDiffs = (endpoint) => {
const url = endpoint.url;
return axios
.request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};
return request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0',
headers: { Accept: '*/*' },
}).then((response) => response.data);
}

View File

@ -1,6 +1,6 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { Matchers } = require('@pact-foundation/pact');
import { Matchers } from '@pact-foundation/pact';
const body = {
diff_files: Matchers.eachLike({
@ -48,9 +48,9 @@ const body = {
context_lines_path: Matchers.string('/gitlab-qa-bot/...'),
highlighted_diff_lines: Matchers.eachLike({
// The following values can also be null which is not supported
//line_code: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579_1_1'),
//old_line: Matchers.integer(1),
//new_line: Matchers.integer(1),
// line_code: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579_1_1'),
// old_line: Matchers.integer(1),
// new_line: Matchers.integer(1),
text: Matchers.string('source'),
rich_text: Matchers.string('<span></span>'),
can_receive_suggestion: Matchers.boolean(true),
@ -70,7 +70,7 @@ const Diffs = {
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
body,
},
request: {
@ -86,4 +86,5 @@ const Diffs = {
},
};
exports.Diffs = Diffs;
export { Diffs };
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -1,6 +1,6 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { Matchers } = require('@pact-foundation/pact');
import { Matchers } from '@pact-foundation/pact';
const body = Matchers.eachLike({
id: Matchers.string('fd73763cbcbf7b29eb8765d969a38f7d735e222a'),
@ -67,7 +67,7 @@ const Discussions = {
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
body,
},
request: {
@ -82,4 +82,5 @@ const Discussions = {
},
};
exports.Discussions = Discussions;
export { Discussions };
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -1,6 +1,6 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { Matchers } = require('@pact-foundation/pact');
import { Matchers } from '@pact-foundation/pact';
const body = {
real_size: Matchers.string('1'),
@ -78,7 +78,7 @@ const Metadata = {
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
body,
},
request: {
@ -93,4 +93,5 @@ const Metadata = {
},
};
exports.Metadata = Metadata;
export { Metadata };
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -13,5 +13,14 @@
},
"scripts": {
"test": "jest --runInBand"
},
"jest": {
"transform": {
"^.+\\.[t|j]sx?$": "babel-jest"
}
},
"devDependencies": {
"@babel/preset-env": "^7.18.2",
"babel-jest": "^28.1.1"
}
}

View File

@ -1,9 +1,9 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { pactWith } = require('jest-pact');
import { pactWith } from 'jest-pact';
const { Diffs } = require('../fixtures/diffs.fixture');
const { getDiffs } = require('../endpoints/merge_requests');
import { Diffs } from '../fixtures/diffs.fixture';
import { getDiffs } from '../endpoints/merge_requests';
pactWith(
{
@ -34,3 +34,4 @@ pactWith(
});
},
);
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -1,9 +1,9 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { pactWith } = require('jest-pact');
import { pactWith } from 'jest-pact';
const { Discussions } = require('../fixtures/discussions.fixture');
const { getDiscussions } = require('../endpoints/merge_requests');
import { Discussions } from '../fixtures/discussions.fixture';
import { getDiscussions } from '../endpoints/merge_requests';
pactWith(
{
@ -34,3 +34,4 @@ pactWith(
});
},
);
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -1,9 +1,9 @@
'use strict';
/* eslint-disable @gitlab/require-i18n-strings */
const { pactWith } = require('jest-pact');
import { pactWith } from 'jest-pact';
const { Metadata } = require('../fixtures/metadata.fixture');
const { getMetadata } = require('../endpoints/merge_requests');
import { Metadata } from '../fixtures/metadata.fixture';
import { getMetadata } from '../endpoints/merge_requests';
pactWith(
{
@ -34,3 +34,4 @@ pactWith(
});
},
);
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -68,7 +68,7 @@ describe('IDE commit editor header', () => {
it('calls discardFileChanges if dialog result is confirmed', () => {
expect(store.dispatch).not.toHaveBeenCalled();
findDiscardModal().vm.$emit('ok');
findDiscardModal().vm.$emit('primary');
expect(store.dispatch).toHaveBeenCalledWith('discardFileChanges', TEST_FILE_PATH);
});

View File

@ -2,7 +2,6 @@ import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { GlTab, GlTabs, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
import stubChildren from 'helpers/stub_children';
@ -20,22 +19,14 @@ import {
LICENSE_COMPLIANCE_DESCRIPTION,
LICENSE_COMPLIANCE_HELP_PATH,
AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY,
LICENSE_ULTIMATE,
LICENSE_PREMIUM,
LICENSE_FREE,
} from '~/security_configuration/components/constants';
import FeatureCard from '~/security_configuration/components/feature_card.vue';
import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import currentLicenseQuery from '~/security_configuration/graphql/current_license.query.graphql';
import waitForPromises from 'helpers/wait_for_promises';
import UpgradeBanner from '~/security_configuration/components/upgrade_banner.vue';
import {
REPORT_TYPE_LICENSE_COMPLIANCE,
REPORT_TYPE_SAST,
} from '~/vue_shared/security_reports/constants';
import { getCurrentLicensePlanResponse } from '../mock_data';
const upgradePath = '/upgrade';
const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath';
@ -50,31 +41,16 @@ Vue.use(VueApollo);
describe('App component', () => {
let wrapper;
let userCalloutDismissSpy;
let mockApollo;
const createComponent = ({
shouldShowCallout = true,
licenseQueryResponse = LICENSE_ULTIMATE,
...propsData
}) => {
const createComponent = ({ shouldShowCallout = true, ...propsData }) => {
userCalloutDismissSpy = jest.fn();
mockApollo = createMockApollo([
[
currentLicenseQuery,
jest
.fn()
.mockResolvedValue(
licenseQueryResponse instanceof Error
? licenseQueryResponse
: getCurrentLicensePlanResponse(licenseQueryResponse),
),
],
]);
wrapper = extendedWrapper(
mount(SecurityConfigurationApp, {
propsData,
propsData: {
securityTrainingEnabled: true,
...propsData,
},
provide: {
upgradePath,
autoDevopsHelpPagePath,
@ -82,7 +58,6 @@ describe('App component', () => {
projectFullPath,
vulnerabilityTrainingDocsPath,
},
apolloProvider: mockApollo,
stubs: {
...stubChildren(SecurityConfigurationApp),
GlLink: false,
@ -157,7 +132,6 @@ describe('App component', () => {
afterEach(() => {
wrapper.destroy();
mockApollo = null;
});
describe('basic structure', () => {
@ -166,7 +140,6 @@ describe('App component', () => {
augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
});
await waitForPromises();
});
it('renders main-heading with correct text', () => {
@ -469,47 +442,42 @@ describe('App component', () => {
});
describe('Vulnerability management', () => {
beforeEach(async () => {
it('does not show tab if security training is disabled', () => {
createComponent({
augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
securityTrainingEnabled: false,
});
await waitForPromises();
expect(findVulnerabilityManagementTab().exists()).toBe(false);
});
it('renders TrainingProviderList component', () => {
expect(findTrainingProviderList().exists()).toBe(true);
});
it('renders security training description', () => {
expect(findVulnerabilityManagementTab().text()).toContain(i18n.securityTrainingDescription);
});
it('renders link to help docs', () => {
const trainingLink = findVulnerabilityManagementTab().findComponent(GlLink);
expect(trainingLink.text()).toBe('Learn more about vulnerability training');
expect(trainingLink.attributes('href')).toBe(vulnerabilityTrainingDocsPath);
});
it.each`
licenseQueryResponse | display
${LICENSE_ULTIMATE} | ${true}
${LICENSE_PREMIUM} | ${false}
${LICENSE_FREE} | ${false}
${null} | ${true}
${new Error()} | ${true}
`(
'displays $display for license $licenseQueryResponse',
async ({ licenseQueryResponse, display }) => {
describe('security training enabled', () => {
beforeEach(async () => {
createComponent({
licenseQueryResponse,
augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
});
await waitForPromises();
expect(findVulnerabilityManagementTab().exists()).toBe(display);
},
);
});
it('shows the tab if security training is enabled', () => {
expect(findVulnerabilityManagementTab().exists()).toBe(true);
});
it('renders TrainingProviderList component', () => {
expect(findTrainingProviderList().exists()).toBe(true);
});
it('renders security training description', () => {
expect(findVulnerabilityManagementTab().text()).toContain(i18n.securityTrainingDescription);
});
it('renders link to help docs', () => {
const trainingLink = findVulnerabilityManagementTab().findComponent(GlLink);
expect(trainingLink.text()).toBe('Learn more about vulnerability training');
expect(trainingLink.attributes('href')).toBe(vulnerabilityTrainingDocsPath);
});
});
});
});

View File

@ -111,12 +111,3 @@ export const tempProviderLogos = {
svg: `<svg>${[testProviderName[1]]}</svg>`,
},
};
export const getCurrentLicensePlanResponse = (plan) => ({
data: {
currentLicense: {
id: 'gid://gitlab/License/1',
plan,
},
},
});

View File

@ -41,18 +41,18 @@ describe('Attention require toggle', () => {
);
it.each`
attentionRequested | variant
${true} | ${'warning'}
${false} | ${'default'}
attentionRequested | selected
${true} | ${true}
${false} | ${false}
`(
'renders button with variant $variant when attention_requested is $attentionRequested',
({ attentionRequested, variant }) => {
'renders button with as selected when $selected when attention_requested is $attentionRequested',
({ attentionRequested, selected }) => {
factory({
type: 'reviewer',
user: { attention_requested: attentionRequested, can_update_merge_request: true },
});
expect(findToggle().props('variant')).toBe(variant);
expect(findToggle().props('selected')).toBe(selected);
},
);

View File

@ -42,8 +42,8 @@ describe('Merge Request Collapsible Extension', () => {
expect(wrapper.find('[data-testid="collapsed-header"]').text()).toBe('hello there');
});
it('renders angle-right icon', () => {
expect(findIcon().props('name')).toBe('angle-right');
it('renders chevron-lg-right icon', () => {
expect(findIcon().props('name')).toBe('chevron-lg-right');
});
describe('onClick', () => {
@ -60,8 +60,8 @@ describe('Merge Request Collapsible Extension', () => {
expect(findTitle().text()).toBe('Collapse');
});
it('renders angle-down icon', () => {
expect(findIcon().props('name')).toBe('angle-down');
it('renders chevron-lg-down icon', () => {
expect(findIcon().props('name')).toBe('chevron-lg-down');
});
});
});

View File

@ -18,7 +18,7 @@ RSpec.describe FormHelper do
expect(helper.form_errors(model, pajamas_alert: true))
.to include(
'<div class="gl-alert gl-alert-danger gl-alert-not-dismissible gl-mb-5" id="error_explanation" role="alert">'
'<div class="gl-alert gl-mb-5 gl-alert-danger gl-alert-not-dismissible" id="error_explanation" role="alert">'
)
end

View File

@ -13,5 +13,11 @@ RSpec.describe Gitlab::Audit::UnauthenticatedAuthor do
expect(described_class.new)
.to have_attributes(id: -1, name: 'An unauthenticated user')
end
describe '#impersonated?' do
it 'returns false' do
expect(described_class.new.impersonated?).to be(false)
end
end
end
end

View File

@ -0,0 +1,134 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
let(:dummy) { Class.new { include Gitlab::Diff::Rendered::Notebook::DiffFileHelper }.new }
describe '#strip_diff_frontmatter' do
using RSpec::Parameterized::TableSyntax
subject { dummy.strip_diff_frontmatter(diff) }
where(:diff, :result) do
"FileLine1\nFileLine2\n@@ -1,76 +1,74 @@\nhello\n" | "@@ -1,76 +1,74 @@\nhello\n"
"" | nil
nil | nil
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#map_transformed_line_to_source' do
using RSpec::Parameterized::TableSyntax
subject { dummy.map_transformed_line_to_source(1, transformed_blocks) }
where(:case, :transformed_blocks, :result) do
'if transformed diff is empty' | [] | 0
'if the transformed line does not map to any in the original file' | [{ source_line: nil }] | 0
'if the transformed line maps to a line in the source file' | [{ source_line: 2 }] | 3
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#map_diff_block_to_source_line' do
let(:file_added) { false }
let(:file_deleted) { false }
let(:old_positions) { [1] }
let(:new_positions) { [1] }
let(:lines) { old_positions.zip(new_positions).map { |old, new| Gitlab::Diff::Line.new("", "", 0, old, new) } }
subject { dummy.map_diff_block_to_source_line(lines, file_added, file_deleted)}
context 'only additions' do
let(:old_positions) { [1, 2, 2, 2] }
let(:new_positions) { [1, 2, 3, 4] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 4 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
end
end
context 'only additions' do
let(:old_positions) { [1, 2, 3, 4] }
let(:new_positions) { [1, 2, 2, 2] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 4 })
end
end
context 'with additions and removals' do
let(:old_positions) { [1, 2, 3, 4, 4, 4] }
let(:new_positions) { [1, 2, 2, 2, 3, 4] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 4 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 4, 3 => 4, 4 => 4 })
end
end
context 'is new file' do
let(:file_added) { true }
it 'removals is empty' do
expect(subject[0]).to be_empty
end
end
context 'is deleted file' do
let(:file_deleted) { true }
it 'additions is empty' do
expect(subject[1]).to be_empty
end
end
end
describe '#image_as_rich_text' do
let(:img) { 'data:image/png;base64,some_image_here' }
let(:line_text) { " ![](#{img})"}
subject { dummy.image_as_rich_text(line_text) }
context 'text does not contain image' do
let(:img) { "not an image" }
it { is_expected.to be_nil }
end
context 'text contains image' do
it { is_expected.to eq("<img src=\"#{img}\">") }
end
context 'text contains image that has malicious html' do
let(:img) { 'data:image/png;base64,some_image_here"<div>Hello</div>' }
it 'sanitizes the html' do
expect(subject).not_to include('<div>Hello')
end
it 'adds image to src' do
expect(subject).to end_with('/div&gt;">')
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More