Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-28 18:14:03 +00:00
parent 8188ca655a
commit e804afddbf
104 changed files with 915 additions and 1171 deletions

View File

@ -9,7 +9,7 @@
artifacts:
reports:
metrics: "${METRICS_FILE}"
expire_in: 31d
expire_in: 62d
# Show memory usage caused by invoking require per gem.
@ -26,11 +26,17 @@ memory-on-boot:
NODE_ENV: "production"
RAILS_ENV: "production"
SETUP_DB: "true"
MEMORY_ON_BOOT_FILE: "tmp/memory_on_boot.txt"
MEMORY_ON_BOOT_FILE_PREFIX: "tmp/memory_on_boot_"
TEST_COUNT: 5
script:
- PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> "${MEMORY_ON_BOOT_FILE}"
- scripts/generate-memory-metrics-on-boot "${MEMORY_ON_BOOT_FILE}" >> "${METRICS_FILE}"
- |
for i in $(seq 1 $TEST_COUNT)
do
echo "Starting run $i out of $TEST_COUNT"
PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> "${MEMORY_ON_BOOT_FILE_PREFIX}$i.txt"
done
- scripts/generate-memory-metrics-on-boot "${MEMORY_ON_BOOT_FILE_PREFIX}" "$TEST_COUNT" >> "${METRICS_FILE}"
artifacts:
paths:
- "${METRICS_FILE}"
- "${MEMORY_ON_BOOT_FILE}"
- "${MEMORY_ON_BOOT_FILE_PREFIX}*.txt"

View File

@ -247,7 +247,7 @@
# List explicitly all the app/ dirs that are backend (i.e. all except app/assets).
- "{,ee/,jh/}{app/channels,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
- "{,ee/,jh/}{bin,cable,config,db,generator_templates,lib}/**/*"
- "{,ee/,jh/}spec/**/*.rb"
- "{,ee/,jh/}spec/**/*"
# CI changes
- ".gitlab-ci.yml"
- ".gitlab/ci/**/*"

View File

@ -32,13 +32,6 @@ Graphql/IDType:
Layout/ArgumentAlignment:
Enabled: false
# Offense count: 54
# Cop supports --auto-correct.
# Configuration parameters: AllowAliasSyntax, AllowedMethods.
# AllowedMethods: alias_method, public, protected, private
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: false
# Offense count: 771
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
@ -521,14 +514,6 @@ Rails/RenderInline:
Exclude:
- 'ee/app/controllers/sitemap_controller.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: conservative, aggressive
Rails/ShortI18n:
Exclude:
- 'app/uploaders/content_type_whitelist.rb'
# Offense count: 1144
# Configuration parameters: ForbiddenMethods, AllowedMethods.
# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all

View File

@ -22,14 +22,6 @@ GraphQL/OrderedFields:
- app/graphql/types/error_tracking/sentry_error_frequency_type.rb
- app/graphql/types/error_tracking/sentry_error_stack_trace_context_type.rb
- app/graphql/types/error_tracking/sentry_error_stack_trace_entry_type.rb
- app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb
- app/graphql/types/error_tracking/sentry_error_type.rb
- app/graphql/types/evidence_type.rb
- app/graphql/types/grafana_integration_type.rb
- app/graphql/types/issue_type.rb
- app/graphql/types/jira_import_type.rb
- app/graphql/types/jira_user_type.rb
- app/graphql/types/label_type.rb
- app/graphql/types/merge_request_type.rb
- app/graphql/types/metadata/kas_type.rb
- app/graphql/types/metadata_type.rb

View File

@ -24,24 +24,3 @@ return if helper.release_automation?
project_helper.rule_names.each do |rule|
danger.import_dangerfile(path: File.join('danger', rule))
end
anything_to_post = status_report.values.any? { |data| data.any? }
return unless helper.ci?
def post_labels
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
gitlab.mr_json['iid'],
add_labels: project_helper.labels_to_add.join(','))
rescue Gitlab::Error::Forbidden
labels = project_helper.labels_to_add.map { |label| %Q(~"#{label}") }
warn("This Merge Request needs to be labelled with #{labels.join(' ')}. Please request a reviewer or maintainer to add them.")
end
if project_helper.labels_to_add.any?
post_labels
end
if anything_to_post
markdown("**If needed, you can retry the [🔁 `danger-review` job](#{ENV['CI_JOB_URL']}) that generated this comment.**")
end

View File

@ -1 +1 @@
b6e1f3ce3799d61cb7cdbd67952d82c126f44c4f
c05b46905eacc7f4bd69f738fdf85ebb0b84ee4b

View File

@ -398,7 +398,7 @@ group :development, :test do
end
group :development, :test, :danger do
gem 'gitlab-dangerfiles', '~> 2.8.0', require: false
gem 'gitlab-dangerfiles', '~> 2.9.3', require: false
end
group :development, :test, :coverage do

View File

@ -221,7 +221,7 @@ GEM
css_parser (1.7.0)
addressable
daemons (1.3.1)
danger (8.4.2)
danger (8.4.3)
claide (~> 1.0)
claide-plugins (>= 0.9.2)
colored2 (~> 3.1)
@ -460,7 +460,7 @@ GEM
terminal-table (~> 1.5, >= 1.5.1)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-dangerfiles (2.8.0)
gitlab-dangerfiles (2.9.3)
danger (>= 8.3.1)
danger-gitlab (>= 8.0.0)
gitlab-experiment (0.7.0)
@ -1470,7 +1470,7 @@ DEPENDENCIES
gitaly (~> 14.8.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 2.8.0)
gitlab-dangerfiles (~> 2.9.3)
gitlab-experiment (~> 0.7.0)
gitlab-fog-azure-rm (~> 1.2.0)
gitlab-labkit (~> 0.22.0)

View File

@ -1,6 +1,6 @@
/* eslint-disable no-restricted-properties, babel/camelcase,
no-unused-expressions, default-case,
consistent-return, no-alert, no-param-reassign,
consistent-return, no-param-reassign,
no-shadow, no-useless-escape,
class-methods-use-this */
@ -20,6 +20,7 @@ import AjaxCache from '~/lib/utils/ajax_cache';
import syntaxHighlight from '~/syntax_highlight';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
import * as constants from '~/notes/constants';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import Autosave from './autosave';
import loadAwardsHandler from './awards_handler';
import createFlash from './flash';
@ -243,7 +244,7 @@ export default class Notes {
});
}
keydownNoteText(e) {
async keydownNoteText(e) {
let discussionNoteForm;
let editNote;
let myLastNote;
@ -276,9 +277,11 @@ export default class Notes {
discussionNoteForm = $textarea.closest('.js-discussion-note-form');
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
if (!window.confirm(__('Your comment will be discarded.'))) {
return;
}
const confirmed = await confirmAction(__('Your comment will be discarded.'), {
primaryBtnVariant: 'danger',
primaryBtnText: __('Discard'),
});
if (!confirmed) return;
}
this.removeDiscussionNoteForm(discussionNoteForm);
return;
@ -288,9 +291,14 @@ export default class Notes {
originalText = $textarea.closest('form').data('originalNote');
newText = $textarea.val();
if (originalText !== newText) {
if (!window.confirm(__('Are you sure you want to discard this comment?'))) {
return;
}
const confirmed = await confirmAction(
__('Are you sure you want to discard this comment?'),
{
primaryBtnVariant: 'danger',
primaryBtnText: __('Discard'),
},
);
if (!confirmed) return;
}
return this.removeNoteEditForm(editNote);
}

View File

@ -68,7 +68,7 @@ export default {
/>
<item-stats-value
v-if="isGroup"
:title="__('Members')"
:title="__('Direct members')"
:value="item.memberCount"
css-class="number-users gl-ml-5"
icon-name="users"

View File

@ -0,0 +1,53 @@
import Vue from 'vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
const defaultValue = (prop) => GlLoadingIcon.props[prop]?.default;
/**
* Returns a loading icon/spinner element.
*
* This should *only* be used in existing legacy areas of code where Vue is not
* in use, as part of the migration strategy defined in
* https://gitlab.com/groups/gitlab-org/-/epics/7626.
*
* @param {object} props - The props to configure the spinner.
* @param {boolean} props.inline - Display the spinner inline; otherwise, as a block.
* @param {string} props.color - The color of the spinner ('dark' or 'light')
* @param {string} props.size - The size of the spinner ('sm', 'md', 'lg', 'xl')
* @param {string[]} props.classes - Additional classes to apply to the element.
* @param {string} props.label - The ARIA label to apply to the spinner.
* @returns {HTMLElement}
*/
export const loadingIconForLegacyJS = ({
inline = defaultValue('inline'),
color = defaultValue('color'),
size = defaultValue('size'),
classes = [],
label = __('Loading'),
} = {}) => {
const mountEl = document.createElement('div');
const vm = new Vue({
el: mountEl,
render(h) {
return h(GlLoadingIcon, {
class: classes,
props: {
inline,
color,
size,
label,
},
});
},
});
// Ensure it's rendered
vm.$forceUpdate();
const el = vm.$el.cloneNode(true);
vm.$destroy();
return el;
};

View File

@ -7,6 +7,7 @@ import axios from '~/lib/utils/axios_utils';
import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
import { formatDate } from '~/lib/utils/datetime/date_format_utility';
import { n__, s__, __ } from '~/locale';
import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
const d3 = { select };
@ -24,12 +25,6 @@ const CONTRIB_LEGENDS = [
{ title: __('30+ contributions'), min: 30 },
];
const LOADING_HTML = `
<div class="text-center">
<div class="spinner spinner-md"></div>
</div>
`;
function getSystemDate(systemUtcOffsetSeconds) {
const date = new Date();
const localUtcOffsetMinutes = 0 - date.getTimezoneOffset();
@ -286,7 +281,9 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(),
].join('-');
$(this.activitiesContainer).html(LOADING_HTML);
$(this.activitiesContainer)
.empty()
.append(loadingIconForLegacyJS({ size: 'lg' }));
axios
.get(this.calendarActivitiesPath, {

View File

@ -8,12 +8,12 @@ module Types
authorize :read_sentry_issue
field :issue_id, GraphQL::Types::String,
null: false,
description: 'ID of the Sentry error.'
field :date_received, GraphQL::Types::String,
null: false,
description: 'Time the stack trace was received by Sentry.'
field :issue_id, GraphQL::Types::String,
null: false,
description: 'ID of the Sentry error.'
field :stack_trace_entries, [Types::ErrorTracking::SentryErrorStackTraceEntryType],
null: false,
description: 'Stack trace entries for the Sentry error.'

View File

@ -9,49 +9,34 @@ module Types
present_using SentryErrorPresenter
field :id, GraphQL::Types::ID,
null: false,
description: 'ID (global ID) of the error.'
field :sentry_id, GraphQL::Types::String,
method: :id,
null: false,
description: 'ID (Sentry ID) of the error.'
field :first_seen, Types::TimeType,
null: false,
description: 'Timestamp when the error was first seen.'
field :last_seen, Types::TimeType,
null: false,
description: 'Timestamp when the error was last seen.'
field :title, GraphQL::Types::String,
null: false,
description: 'Title of the error.'
field :type, GraphQL::Types::String,
null: false,
description: 'Type of the error.'
field :user_count, GraphQL::Types::Int,
null: false,
description: 'Count of users affected by the error.'
field :count, GraphQL::Types::Int,
null: false,
description: 'Count of occurrences.'
field :message, GraphQL::Types::String,
null: true,
description: 'Sentry metadata message of the error.'
field :culprit, GraphQL::Types::String,
null: false,
description: 'Culprit of the error.'
field :external_url, GraphQL::Types::String,
null: false,
description: 'External URL of the error.'
field :short_id, GraphQL::Types::String,
field :first_seen, Types::TimeType,
null: false,
description: 'Short ID (Sentry ID) of the error.'
field :status, Types::ErrorTracking::SentryErrorStatusEnum,
null: false,
description: 'Status of the error.'
description: 'Timestamp when the error was first seen.'
field :frequency, [Types::ErrorTracking::SentryErrorFrequencyType],
null: false,
description: 'Last 24hr stats of the error.'
field :id, GraphQL::Types::ID,
null: false,
description: 'ID (global ID) of the error.'
field :last_seen, Types::TimeType,
null: false,
description: 'Timestamp when the error was last seen.'
field :message, GraphQL::Types::String,
null: true,
description: 'Sentry metadata message of the error.'
field :sentry_id, GraphQL::Types::String,
method: :id,
null: false,
description: 'ID (Sentry ID) of the error.'
field :sentry_project_id, GraphQL::Types::ID,
method: :project_id,
null: false,
@ -64,6 +49,21 @@ module Types
method: :project_slug,
null: false,
description: 'Slug of the project affected by the error.'
field :short_id, GraphQL::Types::String,
null: false,
description: 'Short ID (Sentry ID) of the error.'
field :status, Types::ErrorTracking::SentryErrorStatusEnum,
null: false,
description: 'Status of the error.'
field :title, GraphQL::Types::String,
null: false,
description: 'Title of the error.'
field :type, GraphQL::Types::String,
null: false,
description: 'Type of the error.'
field :user_count, GraphQL::Types::Int,
null: false,
description: 'Count of users affected by the error.'
end
# rubocop: enable Graphql/AuthorizeTypes
end

View File

@ -9,13 +9,13 @@ module Types
present_using Releases::EvidencePresenter
field :collected_at, Types::TimeType, null: true,
description: 'Timestamp when the evidence was collected.'
field :filepath, GraphQL::Types::String, null: true,
description: 'URL from where the evidence can be downloaded.'
field :id, GraphQL::Types::ID, null: false,
description: 'ID of the evidence.'
field :sha, GraphQL::Types::String, null: true,
description: 'SHA1 ID of the evidence hash.'
field :filepath, GraphQL::Types::String, null: true,
description: 'URL from where the evidence can be downloaded.'
field :collected_at, Types::TimeType, null: true,
description: 'Timestamp when the evidence was collected.'
end
end

View File

@ -6,14 +6,14 @@ module Types
authorize :admin_operations
field :id, GraphQL::Types::ID, null: false,
description: 'Internal ID of the Grafana integration.'
field :grafana_url, GraphQL::Types::String, null: false,
description: 'URL for the Grafana host for the Grafana integration.'
field :enabled, GraphQL::Types::Boolean, null: false,
description: 'Indicates whether Grafana integration is enabled.'
field :created_at, Types::TimeType, null: false,
description: 'Timestamp of the issue\'s creation.'
field :enabled, GraphQL::Types::Boolean, null: false,
description: 'Indicates whether Grafana integration is enabled.'
field :grafana_url, GraphQL::Types::String, null: false,
description: 'URL for the Grafana host for the Grafana integration.'
field :id, GraphQL::Types::ID, null: false,
description: 'Internal ID of the Grafana integration.'
field :updated_at, Types::TimeType, null: false,
description: 'Timestamp of the issue\'s last activity.'
end

View File

@ -15,16 +15,16 @@ module Types
present_using IssuePresenter
field :description, GraphQL::Types::String, null: true,
description: 'Description of the issue.'
field :id, GraphQL::Types::ID, null: false,
description: "ID of the issue."
field :iid, GraphQL::Types::ID, null: false,
description: "Internal ID of the issue."
field :title, GraphQL::Types::String, null: false,
description: 'Title of the issue.'
field :description, GraphQL::Types::String, null: true,
description: 'Description of the issue.'
field :state, IssueStateEnum, null: false,
description: 'State of the issue.'
field :title, GraphQL::Types::String, null: false,
description: 'Title of the issue.'
field :reference, GraphQL::Types::String, null: false,
description: 'Internal reference of the issue. Returned in shortened format by default.',
@ -47,52 +47,52 @@ module Types
field :milestone, Types::MilestoneType, null: true,
description: 'Milestone of the issue.'
field :due_date, Types::TimeType, null: true,
description: 'Due date of the issue.'
field :confidential, GraphQL::Types::Boolean, null: false,
description: 'Indicates the issue is confidential.'
field :discussion_locked, GraphQL::Types::Boolean, null: false,
description: 'Indicates discussion is locked on the issue.'
field :due_date, Types::TimeType, null: true,
description: 'Due date of the issue.'
field :hidden, GraphQL::Types::Boolean, null: true, resolver_method: :hidden?,
description: 'Indicates the issue is hidden because the author has been banned. ' \
'Will always return `null` if `ban_user_feature_flag` feature flag is disabled.'
field :discussion_locked, GraphQL::Types::Boolean, null: false,
description: 'Indicates discussion is locked on the issue.'
field :upvotes, GraphQL::Types::Int, null: false,
description: 'Number of upvotes the issue has received.'
field :downvotes, GraphQL::Types::Int, null: false,
description: 'Number of downvotes the issue has received.'
field :merge_requests_count, GraphQL::Types::Int, null: false,
description: 'Number of merge requests that close the issue on merge.',
resolver: Resolvers::MergeRequestsCountResolver
field :user_notes_count, GraphQL::Types::Int, null: false,
description: 'Number of user notes of the issue.',
resolver: Resolvers::UserNotesCountResolver
field :relative_position, GraphQL::Types::Int, null: true,
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :upvotes, GraphQL::Types::Int, null: false,
description: 'Number of upvotes the issue has received.'
field :user_discussions_count, GraphQL::Types::Int, null: false,
description: 'Number of user discussions in the issue.',
resolver: Resolvers::UserDiscussionsCountResolver
field :user_notes_count, GraphQL::Types::Int, null: false,
description: 'Number of user notes of the issue.',
resolver: Resolvers::UserNotesCountResolver
field :web_path, GraphQL::Types::String, null: false, method: :issue_path,
description: 'Web path of the issue.'
field :web_url, GraphQL::Types::String, null: false,
description: 'Web URL of the issue.'
field :relative_position, GraphQL::Types::Int, null: true,
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :participants, Types::UserType.connection_type, null: true, complexity: 5,
description: 'List of participants in the issue.',
resolver: Resolvers::Users::ParticipantsResolver
field :emails_disabled, GraphQL::Types::Boolean, null: false,
method: :project_emails_disabled?,
description: 'Indicates if a project has email notifications disabled: `true` if email notifications are disabled.'
field :human_time_estimate, GraphQL::Types::String, null: true,
description: 'Human-readable time estimate of the issue.'
field :human_total_time_spent, GraphQL::Types::String, null: true,
description: 'Human-readable total time reported as spent on the issue.'
field :participants, Types::UserType.connection_type, null: true, complexity: 5,
description: 'List of participants in the issue.',
resolver: Resolvers::Users::ParticipantsResolver
field :subscribed, GraphQL::Types::Boolean, method: :subscribed?, null: false, complexity: 5,
description: 'Indicates the currently logged in user is subscribed to the issue.'
field :time_estimate, GraphQL::Types::Int, null: false,
description: 'Time estimate of the issue.'
field :total_time_spent, GraphQL::Types::Int, null: false,
description: 'Total time reported as spent on the issue.'
field :human_time_estimate, GraphQL::Types::String, null: true,
description: 'Human-readable time estimate of the issue.'
field :human_total_time_spent, GraphQL::Types::String, null: true,
description: 'Human-readable total time reported as spent on the issue.'
field :closed_at, Types::TimeType, null: true,
description: 'Timestamp of when the issue was closed.'

View File

@ -8,16 +8,16 @@ module Types
field :created_at, Types::TimeType, null: true,
description: 'Timestamp of when the Jira import was created.'
field :failed_to_import_count, GraphQL::Types::Int, null: false,
description: 'Count of issues that failed to import.'
field :imported_issues_count, GraphQL::Types::Int, null: false,
description: 'Count of issues that were successfully imported.'
field :jira_project_key, GraphQL::Types::String, null: false,
description: 'Project key for the imported Jira project.'
field :scheduled_at, Types::TimeType, null: true,
description: 'Timestamp of when the Jira import was scheduled.'
field :scheduled_by, Types::UserType, null: true,
description: 'User that started the Jira import.'
field :jira_project_key, GraphQL::Types::String, null: false,
description: 'Project key for the imported Jira project.'
field :imported_issues_count, GraphQL::Types::Int, null: false,
description: 'Count of issues that were successfully imported.'
field :failed_to_import_count, GraphQL::Types::Int, null: false,
description: 'Count of issues that failed to import.'
field :total_issue_count, GraphQL::Types::Int, null: false,
description: 'Total count of issues that were attempted to import.'
end

View File

@ -6,18 +6,18 @@ module Types
class JiraUserType < BaseObject
graphql_name 'JiraUser'
field :gitlab_id, GraphQL::Types::Int, null: true,
description: 'ID of the matched GitLab user.'
field :gitlab_name, GraphQL::Types::String, null: true,
description: 'Name of the matched GitLab user.'
field :gitlab_username, GraphQL::Types::String, null: true,
description: 'Username of the matched GitLab user.'
field :jira_account_id, GraphQL::Types::String, null: false,
description: 'Account ID of the Jira user.'
field :jira_display_name, GraphQL::Types::String, null: false,
description: 'Display name of the Jira user.'
field :jira_email, GraphQL::Types::String, null: true,
description: 'Email of the Jira user, returned only for users with public emails.'
field :gitlab_id, GraphQL::Types::Int, null: true,
description: 'ID of the matched GitLab user.'
field :gitlab_username, GraphQL::Types::String, null: true,
description: 'Username of the matched GitLab user.'
field :gitlab_name, GraphQL::Types::String, null: true,
description: 'Name of the matched GitLab user.'
end
# rubocop: enable Graphql/AuthorizeTypes
end

View File

@ -8,18 +8,18 @@ module Types
authorize :read_label
field :id, GraphQL::Types::ID, null: false,
description: 'Label ID.'
field :description, GraphQL::Types::String, null: true,
description: 'Description of the label (Markdown rendered as HTML for caching).'
field :title, GraphQL::Types::String, null: false,
description: 'Content of the label.'
field :color, GraphQL::Types::String, null: false,
description: 'Background color of the label.'
field :text_color, GraphQL::Types::String, null: false,
description: 'Text color of the label.'
field :created_at, Types::TimeType, null: false,
description: 'When this label was created.'
field :description, GraphQL::Types::String, null: true,
description: 'Description of the label (Markdown rendered as HTML for caching).'
field :id, GraphQL::Types::ID, null: false,
description: 'Label ID.'
field :text_color, GraphQL::Types::String, null: false,
description: 'Text color of the label.'
field :title, GraphQL::Types::String, null: false,
description: 'Content of the label.'
field :updated_at, Types::TimeType, null: false,
description: 'When this label was last updated.'

View File

@ -7,6 +7,7 @@ class ApplicationMailer < ActionMailer::Base
helper MarkupHelper
attr_accessor :current_user
helper_method :current_user, :can?
default from: proc { default_sender_address.format }

View File

@ -53,7 +53,7 @@ class BroadcastMessage < ApplicationRecord
def cache
::Gitlab::SafeRequestStore.fetch(:broadcast_message_json_cache) do
Gitlab::JsonCache.new(cache_key_with_version: false)
Gitlab::JsonCache.new
end
end

View File

@ -4,6 +4,7 @@ class LfsDownloadObject
include ActiveModel::Validations
attr_accessor :oid, :size, :link, :headers
delegate :sanitized_url, :credentials, to: :sanitized_uri
validates :oid, format: { with: /\A\h{64}\z/ }

View File

@ -3,6 +3,7 @@
module Storage
class Hashed
attr_accessor :container
delegate :gitlab_shell, :repository_storage, to: :container
REPOSITORY_PATH_PREFIX = '@hashed'

View File

@ -3,6 +3,7 @@
module Storage
class LegacyProject
attr_accessor :project
delegate :namespace, :gitlab_shell, :repository_storage, to: :project
def initialize(project)

View File

@ -45,6 +45,7 @@ class WikiPage
# The GitLab Wiki instance.
attr_reader :wiki
delegate :container, to: :wiki
# The raw Gitlab::Git::WikiPage instance.

View File

@ -17,6 +17,7 @@ module Ci
MAX_TRACKABLE_FAILURES = 200
attr_reader :pipeline
delegate :project, to: :pipeline
def initialize(pipeline)

View File

@ -57,6 +57,7 @@ module RateLimitedService
prepended do
attr_accessor :rate_limiter_bypassed
cattr_accessor :rate_limiter_scoped_and_keyed
def self.rate_limit(key:, opts:, rate_limiter: ::Gitlab::ApplicationRateLimiter)

View File

@ -6,6 +6,7 @@ module UpdateRepositoryStorageMethods
Error = Class.new(StandardError)
attr_reader :repository_storage_move
delegate :container, :source_storage_name, :destination_storage_name, to: :repository_storage_move
def initialize(repository_storage_move)

View File

@ -4,6 +4,7 @@ module NotificationRecipients
module Builder
class MergeRequestUnmergeable < Base
attr_reader :target
def initialize(merge_request)
@target = merge_request
end

View File

@ -4,6 +4,7 @@ module NotificationRecipients
module Builder
class NewNote < Base
attr_reader :note
def initialize(note)
@note = note
end

View File

@ -4,6 +4,7 @@ module NotificationRecipients
module Builder
class NewReview < Base
attr_reader :review
def initialize(review)
@review = review
end

View File

@ -18,6 +18,7 @@
class NotificationService
class Async
attr_reader :parent
delegate :respond_to_missing, to: :parent
def initialize(parent)

View File

@ -3,6 +3,7 @@
module Projects
class BaseMoveRelationsService < BaseService
attr_reader :source_project
def execute(source_project, remove_remaining_elements: true)
return if source_project.blank?

View File

@ -11,6 +11,7 @@ module Projects
LARGE_FILE_SIZE = 1.megabytes
attr_reader :lfs_download_object
delegate :oid, :size, :credentials, :sanitized_url, :headers, to: :lfs_download_object, prefix: :lfs
def initialize(project, lfs_download_object)

View File

@ -30,7 +30,7 @@ module ContentTypeWhitelist
content_type = mime_magic_content_type(new_file.path)
unless whitelisted_content_type?(content_type)
message = I18n.translate(:"errors.messages.content_type_whitelist_error", allowed_types: Array(content_type_whitelist).join(", "))
message = I18n.t(:"errors.messages.content_type_whitelist_error", allowed_types: Array(content_type_whitelist).join(", "))
raise CarrierWave::IntegrityError, message
end
end

View File

@ -1,8 +1,8 @@
---
name: update_all_mirrors_job_tracker
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351420
milestone: '14.8'
name: mirror_scheduling_tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81249
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353440
milestone: '14.9'
type: development
group: group::scalability
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: project_import_schedule_worker_job_tracker
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351408
milestone: '14.8'
type: development
group: group::scalability
default_enabled: false

View File

@ -34,6 +34,7 @@ end
class Net::HTTP
attr_accessor :hostname_override
SSL_IVNAMES << :@hostname_override
SSL_ATTRIBUTES << :hostname_override

View File

@ -0,0 +1,24 @@
---
key_path: settings.certificate_based_clusters_ff
name: "certificate_based_clusters_ff"
description: "Certificate-based clusters feature flag"
product_section: ops
product_stage: configure
product_group: group::configure
product_category:
value_type: boolean
status: active
milestone: "14.9"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81311
time_frame: none
data_source: database
data_category: optional
instrumentation_class: CertBasedClustersFfMetric
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
changelog.check!

View File

@ -65,7 +65,7 @@ if gitlab.mr_labels.include?('database') || db_paths_to_review.any?
markdown(DB_REMOVE_MESSAGE)
end
unless helper.has_database_scoped_labels?
project_helper.labels_to_add << 'database::review pending'
unless helper.has_scoped_label_with_scope?("database")
helper.labels_to_add << 'database::review pending'
end
end

View File

@ -58,7 +58,7 @@ def message_for_feature_flag_with_group!(feature_flag:, mr_group_label:)
return if feature_flag.group_match_mr_label?(mr_group_label)
if mr_group_label.nil?
project_helper.labels_to_add << feature_flag.group
helper.labels_to_add << feature_flag.group
else
fail %(`group` is set to ~"#{feature_flag.group}" in #{gitlab.html_link(feature_flag.path)}, which does not match ~"#{mr_group_label}" set on the MR!)
end

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
require_relative '../../tooling/danger/changelog'
module Danger
class Changelog < ::Danger::Plugin
# Put the helper code somewhere it can be tested
include Tooling::Danger::Changelog
end
end

View File

@ -19,4 +19,4 @@ return if product_intelligence_paths_to_review.empty? || product_intelligence.sk
warn format(CHANGED_FILES_MESSAGE, changed_files: helper.markdown_list(product_intelligence_paths_to_review)) unless product_intelligence.has_approved_label?
project_helper.labels_to_add.concat(labels_to_add) unless labels_to_add.empty?
helper.labels_to_add.merge(labels_to_add) unless labels_to_add.empty?

View File

@ -26,4 +26,4 @@ labels_to_add = helper.changes_by_category.each_with_object([]) do |(category, _
memo << label
end
project_helper.labels_to_add.concat(labels_to_add) if labels_to_add.any?
helper.labels_to_add.merge(labels_to_add) if labels_to_add.any?

View File

@ -4,24 +4,10 @@
DEFAULT_BRANCH = 'master'
TYPE_LABELS = [
'type::feature',
'feature::addition',
'type::maintenance',
'type::tooling',
'tooling::pipelines',
'tooling::workflow',
'type::bug'
].freeze
if gitlab.mr_body.size < 5
fail "Please provide a proper merge request description."
end
if (TYPE_LABELS & (gitlab.mr_labels + project_helper.labels_to_add)).empty?
warn 'Please add a [merge request type](https://about.gitlab.com/handbook/engineering/metrics/#work-type-classification) to this merge request.'
end
unless gitlab.mr_json["assignee"]
warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time."
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RecreateIndexSecurityCiBuildsOnNameAndIdParserWithNewFeatures < Gitlab::Database::Migration[1.0]
TABLE = "ci_builds"
OLD_INDEX_NAME = "index_security_ci_builds_on_name_and_id_parser_features"
NEW_INDEX_NAME = "index_security_ci_builds_on_name_and_id_parser_features_old"
COLUMNS = %i[name id]
CONSTRAINTS = "(name::text = ANY (ARRAY['container_scanning'::character varying::text,
'dast'::character varying::text,
'dependency_scanning'::character varying::text,
'license_management'::character varying::text,
'sast'::character varying::text,
'secret_detection'::character varying::text,
'coverage_fuzzing'::character varying::text,
'license_scanning'::character varying::text,
'apifuzzer_fuzz'::character varying::text,
'apifuzzer_fuzz_dnd'::character varying::text])
) AND type::text = 'Ci::Build'::text"
enable_lock_retries!
def up
rename_index(TABLE, OLD_INDEX_NAME, NEW_INDEX_NAME)
prepare_async_index TABLE, COLUMNS, name: OLD_INDEX_NAME, where: CONSTRAINTS
end
def down
unprepare_async_index TABLE, COLUMNS, name: OLD_INDEX_NAME
rename_index(TABLE, NEW_INDEX_NAME, OLD_INDEX_NAME)
end
end

View File

@ -0,0 +1 @@
1d7105559c8d2da1d86c5625c592edc792d7cd729b8c86c7a2b950c3dd98e975

View File

@ -28901,7 +28901,7 @@ CREATE UNIQUE INDEX index_scim_oauth_access_tokens_on_group_id_and_token_encrypt
CREATE INDEX index_secure_ci_builds_on_user_id_name_created_at ON ci_builds USING btree (user_id, name, created_at) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text, ('secret_detection'::character varying)::text])));
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features_old ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_security_findings_on_confidence ON security_findings USING btree (confidence);

View File

@ -46,6 +46,43 @@ Example response:
]
```
## Get system hook
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81595) in GitLab 14.9.
Get a system hook by its ID.
```plaintext
GET /hooks/:id
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the hook |
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/hooks/1"
```
Example response:
```json
[
{
"id": 1,
"url": "https://gitlab.example.com/hook",
"created_at": "2016-10-31T12:32:15.192Z",
"push_events": true,
"tag_push_events": false,
"merge_requests_events": true,
"repository_update_events": true,
"enable_ssl_verification": true
}
]
```
## Add new system hook
Add a new system hook.

View File

@ -121,12 +121,13 @@ to revert the change before merging!
#### Adding labels via Danger
NOTE:
This is currently applicable to the [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab)
project only.
This is applicable to all the projects that use the [`gitlab-dangerfiles` gem](https://rubygems.org/gems/gitlab-dangerfiles).
Danger is often used to improve MR hygiene by adding labels. Instead of calling the
API directly in your `Dangerfile`, add the labels to the `project_helper.labels_to_add` array.
The main `Dangerfile` will then take care of adding the labels to the MR with a single API call.
API directly in your `Dangerfile`, add the labels to `helper.labels_to_add` set (with `helper.labels_to_add << label`
or `helper.labels_to_add.merge(array_of_labels)`.
`gitlab-dangerfiles` will then take care of adding the labels to the MR with a single API call after all the rules
have had the chance to add to `helper.labels_to_add`.
#### Shared rules and plugins
@ -135,11 +136,30 @@ upstreaming them to the [`gitlab-dangerfiles`](https://gitlab.com/gitlab-org/rub
#### Enable Danger on a project
To enable the Dangerfile on another existing GitLab project, run the following
extra steps:
To enable the Dangerfile on another existing GitLab project, complete the following steps:
1. Create a [Project access tokens](../user/project/settings/project_access_tokens.md).
1. Add the token as a CI/CD project variable named `DANGER_GITLAB_API_TOKEN`.
1. Add [`gitlab-dangerfiles`](https://rubygems.org/gems/gitlab-dangerfiles) to your `Gemfile`.
1. Create a `Dangerfile` with the following content:
```ruby
require_relative "lib/gitlab-dangerfiles"
Gitlab::Dangerfiles.for_project(self, &:import_defaults)
```
1. Add the following to your CI/CD configuration:
```yaml
include:
- project: 'gitlab-org/quality/pipeline-common'
file:
- '/ci/danger-review.yml'
```
1. If your project is in the `gitlab-org` group, you don't need to set up any token as the `DANGER_GITLAB_API_TOKEN`
variable is available at the group level. If not, follow these last steps:
1. Create a [Project access tokens](../user/project/settings/project_access_tokens.md).
1. Add the token as a CI/CD project variable named `DANGER_GITLAB_API_TOKEN`.
You should add the ~"Danger bot" label to the merge request before sending it
for review.

View File

@ -38,7 +38,8 @@ To view value stream analytics for your group:
1. Select a value or enter text to refine the results.
1. To adjust the date range:
- In the **From** field, select a start date.
- In the **To** field, select an end date.
- In the **To** field, select an end date. The charts and list show workflow items created
during the date range.
1. Optional. Sort results by ascending or descending:
- To sort by most recent or oldest workflow item, select the **Merge requests** or **Issues**
header. The header name differs based on the stage you select.

View File

@ -2,7 +2,7 @@ pre-push:
parallel: true
commands:
danger:
run: bundle exec danger dry_run
run: CI_PROJECT_DIR=. bundle exec danger dry_run
eslint:
tags: frontend style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD

View File

@ -22,6 +22,18 @@ module API
present paginate(SystemHook.all), with: Entities::Hook
end
desc 'Get a hook' do
success Entities::Hook
end
params do
requires :id, type: Integer, desc: 'The ID of the system hook'
end
get ":id" do
hook = SystemHook.find(params[:id])
present hook, with: Entities::Hook
end
desc 'Create a new system hook' do
success Entities::Hook
end

View File

@ -12,6 +12,7 @@ module Gitlab
class InsufficientScopeError < AuthenticationError
attr_reader :scopes
def initialize(scopes)
@scopes = scopes.map { |s| s.try(:name) || s }
end

View File

@ -7,6 +7,7 @@ module Gitlab
module OAuth
class AuthHash
attr_reader :auth_hash
def initialize(auth_hash)
@auth_hash = auth_hash
end

View File

@ -4,6 +4,7 @@ module Gitlab
module Checks
class BaseBulkChecker < BaseChecker
attr_reader :changes_access
delegate(*ChangesAccess::ATTRIBUTES, to: :changes_access)
def initialize(changes_access)

View File

@ -4,6 +4,7 @@ module Gitlab
module Checks
class BaseSingleChecker < BaseChecker
attr_reader :change_access
delegate(*SingleChangeAccess::ATTRIBUTES, to: :change_access)
def initialize(change_access)

View File

@ -94,6 +94,7 @@ module Gitlab
private
attr_reader :project, :destination, :started_at, :log_conditions
delegate :current_monotonic_time, to: :class
def age

View File

@ -23,6 +23,7 @@ module Gitlab
private
attr_reader :trace_artifact
delegate :aws?, :google?, to: :object_store_config, prefix: :provider
def fetch_md5_checksum

View File

@ -96,6 +96,7 @@ module Gitlab
attr_reader :instance_variables_builder
attr_reader :project_variables_builder
attr_reader :group_variables_builder
delegate :project, to: :pipeline
def predefined_variables(job)

View File

@ -12,6 +12,7 @@ module Gitlab
# Note that for very large tables, this may even timeout.
class ExactCountStrategy
attr_reader :models
def initialize(models)
@models = models
end

View File

@ -14,6 +14,7 @@ module Gitlab
# however is guaranteed to be "fast", because it only looks up statistics.
class ReltuplesCountStrategy
attr_reader :models
def initialize(models)
@models = models
end

View File

@ -46,6 +46,7 @@ module Gitlab
private
attr_reader :model
delegate :connection, to: :model
def missing_partitions

View File

@ -31,6 +31,7 @@ module Gitlab
private
attr_reader :connection
delegate :execute, :quote_table_name, :quote_column_name, to: :connection
def default_sequence(table, column)

View File

@ -8,6 +8,7 @@ module Gitlab
end
attr_reader :raw_body
def initialize(raw_body)
@raw_body = raw_body
end

View File

@ -63,6 +63,7 @@ module Gitlab
class BlameLine
attr_accessor :lineno, :oldlineno, :commit, :line
def initialize(lineno, oldlineno, commit, line)
@lineno = lineno
@oldlineno = oldlineno

View File

@ -4,6 +4,7 @@ module Gitlab
module Graphql
class BatchKey
attr_reader :object
delegate :hash, to: :object
def initialize(object, lookahead = nil, object_name: nil)

View File

@ -10,6 +10,7 @@ module Gitlab
#
class InsecureKeyFingerprint
attr_accessor :key
alias_attribute :fingerprint_md5, :fingerprint
#

View File

@ -2,12 +2,17 @@
module Gitlab
class JsonCache
attr_reader :backend, :cache_key_with_version, :namespace
attr_reader :backend, :namespace
STRATEGY_KEY_COMPONENTS = {
revision: Gitlab.revision,
version: [Gitlab::VERSION, Rails.version]
}.freeze
def initialize(options = {})
@backend = options.fetch(:backend, Rails.cache)
@namespace = options.fetch(:namespace, nil)
@cache_key_with_version = options.fetch(:cache_key_with_version, true)
@cache_key_strategy = options.fetch(:cache_key_strategy, :revision)
end
def active?
@ -19,13 +24,12 @@ module Gitlab
end
def cache_key(key)
expanded_cache_key = [namespace, key].compact
expanded_cache_key = [namespace, key, *strategy_key_component].compact
expanded_cache_key.join(':').freeze
end
if cache_key_with_version
expanded_cache_key << [Gitlab::VERSION, Rails.version]
end
expanded_cache_key.flatten.join(':').freeze
def strategy_key_component
STRATEGY_KEY_COMPONENTS.fetch(@cache_key_strategy)
end
def expire(key)

View File

@ -4,6 +4,7 @@ module Gitlab
module Pagination
class GitalyKeysetPager
attr_reader :request_context, :project
delegate :params, to: :request_context
def initialize(request_context, project)

View File

@ -6,6 +6,7 @@ module Gitlab
class CursorBasedRequestContext
DEFAULT_SORT_DIRECTION = :desc
attr_reader :request_context
delegate :params, to: :request_context
def initialize(request_context)

View File

@ -5,6 +5,7 @@ module Gitlab
module Keyset
class HeaderBuilder
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
def initialize(request_context)

View File

@ -4,6 +4,7 @@ module Gitlab
module Pagination
class OffsetPagination < Base
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
def initialize(request_context)

View File

@ -5,6 +5,7 @@ module Gitlab
module Queries
class BaseQuery
attr_accessor :client
delegate :query_range, :query, :label_values, :series, to: :client, prefix: true
def raw_memory_usage_query(environment_slug)

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CertBasedClustersFfMetric < GenericMetric
value do
Feature.enabled?(:certificate_based_clusters, default_enabled: :yaml, type: :ops)
end
end
end
end
end
end

View File

@ -224,7 +224,8 @@ module Gitlab
collected_data_categories: add_metric('CollectedDataCategoriesMetric', time_frame: 'none'),
service_ping_features_enabled: add_metric('ServicePingFeaturesMetric', time_frame: 'none'),
snowplow_enabled: add_metric('SnowplowEnabledMetric', time_frame: 'none'),
snowplow_configured_to_gitlab_collector: add_metric('SnowplowConfiguredToGitlabCollectorMetric', time_frame: 'none')
snowplow_configured_to_gitlab_collector: add_metric('SnowplowConfiguredToGitlabCollectorMetric', time_frame: 'none'),
certificate_based_clusters_ff: add_metric('CertBasedClustersFfMetric')
}
}
end

View File

@ -15,6 +15,7 @@ module Sidebars
include ::Sidebars::Concerns::HasPartial
attr_reader :context
delegate :current_user, :container, to: :@context
def initialize(context)

View File

@ -12783,6 +12783,9 @@ msgstr ""
msgid "Direct member"
msgstr ""
msgid "Direct members"
msgstr ""
msgid "Direct non-authenticated users to this page."
msgstr ""

View File

@ -9,6 +9,7 @@ module QA
extend Forwardable
attr_reader :git_uri, :uri
def_delegators :@uri, :user, :host, :path
# See: config/initializers/1_settings.rb

View File

@ -13,125 +13,174 @@ module QA
let(:package_version) { '1.3.7' }
let(:package_type) { 'maven' }
where(:authentication_token_type, :maven_header_name) do
:personal_access_token | 'Private-Token'
:ci_job_token | 'Job-Token'
:project_deploy_token | 'Deploy-Token'
context 'via maven' do
where do
{
'using a personal access token' => {
authentication_token_type: :personal_access_token,
maven_header_name: 'Private-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347582'
},
'using a project deploy token' => {
authentication_token_type: :project_deploy_token,
maven_header_name: 'Deploy-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347585'
},
'using a ci job token' => {
authentication_token_type: :ci_job_token,
maven_header_name: 'Job-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579'
}
}
end
with_them do
let(:token) do
case authentication_token_type
when :personal_access_token
personal_access_token
when :ci_job_token
'${env.CI_JOB_TOKEN}'
when :project_deploy_token
project_deploy_token.token
end
end
it 'pushes and pulls a maven package', testcase: params[:testcase] do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
commit.project = package_project
commit.commit_message = 'Add files'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_upload_package_yaml
},
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
end
end
package_project.visit!
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('deploy')
end
Page::Project::Job::Show.perform do |job|
expect(job).to be_successful(timeout: 800)
end
Page::Project::Menu.perform(&:click_packages_link)
Page::Project::Packages::Index.perform do |index|
expect(index).to have_package(package_name)
index.click_package(package_name)
end
Page::Project::Packages::Show.perform do |show|
expect(show).to have_package_info(package_name, package_version)
end
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_install_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_install_package.yaml.erb')).result(binding)
client_pom_xml = ERB.new(read_fixture('package_managers/maven', 'client_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
commit.project = client_project
commit.commit_message = 'Add files'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_install_package_yaml
},
{
file_path: 'pom.xml',
content: client_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
end
end
client_project.visit!
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('install')
end
Page::Project::Job::Show.perform do |job|
expect(job).to be_successful(timeout: 800)
end
end
end
end
with_them do
let(:token) do
case authentication_token_type
when :personal_access_token
personal_access_token
when :ci_job_token
'${env.CI_JOB_TOKEN}'
when :project_deploy_token
project_deploy_token.token
end
context 'duplication setting' do
before do
package_project.group.visit!
Page::Group::Menu.perform(&:go_to_package_settings)
end
it "pushes and pulls a maven package via maven using #{params[:authentication_token_type]}" do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
context 'when disabled' do
where do
{
'using a personal access token' => {
authentication_token_type: :personal_access_token,
maven_header_name: 'Private-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347581'
},
'using a project deploy token' => {
authentication_token_type: :project_deploy_token,
maven_header_name: 'Deploy-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347584'
},
'using a ci job token' => {
authentication_token_type: :ci_job_token,
maven_header_name: 'Job-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347578'
}
}
end
commit.project = package_project
commit.commit_message = 'Add files'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_upload_package_yaml
},
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
with_them do
let(:token) do
case authentication_token_type
when :personal_access_token
personal_access_token
when :ci_job_token
'${env.CI_JOB_TOKEN}'
when :project_deploy_token
project_deploy_token.token
end
end
end
package_project.visit!
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('deploy')
end
Page::Project::Job::Show.perform do |job|
expect(job).to be_successful(timeout: 800)
end
Page::Project::Menu.perform(&:click_packages_link)
Page::Project::Packages::Index.perform do |index|
expect(index).to have_package(package_name)
index.click_package(package_name)
end
Page::Project::Packages::Show.perform do |show|
expect(show).to have_package_info(package_name, package_version)
end
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_install_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_install_package.yaml.erb')).result(binding)
client_pom_xml = ERB.new(read_fixture('package_managers/maven', 'client_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
commit.project = client_project
commit.commit_message = 'Add files'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_install_package_yaml
},
{
file_path: 'pom.xml',
content: client_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
end
end
client_project.visit!
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('install')
end
Page::Project::Job::Show.perform do |job|
expect(job).to be_successful(timeout: 800)
end
end
context 'duplication setting' do
before do
package_project.group.visit!
Page::Group::Menu.perform(&:go_to_package_settings)
end
context 'when disabled' do
before do
Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled)
end
it "prevents users from publishing group level Maven packages duplicates using #{params[:authentication_token_type]}" do
it 'prevents users from publishing group level Maven packages duplicates', testcase: params[:testcase] do
create_duplicated_package
push_duplicated_package
@ -145,13 +194,46 @@ module QA
end
end
end
end
context 'when enabled' do
where do
{
'using a personal access token' => {
authentication_token_type: :personal_access_token,
maven_header_name: 'Private-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347580'
},
'using a project deploy token' => {
authentication_token_type: :project_deploy_token,
maven_header_name: 'Deploy-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347583'
},
'using a ci job token' => {
authentication_token_type: :ci_job_token,
maven_header_name: 'Job-Token',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347577'
}
}
end
with_them do
let(:token) do
case authentication_token_type
when :personal_access_token
personal_access_token
when :ci_job_token
'${env.CI_JOB_TOKEN}'
when :project_deploy_token
project_deploy_token.token
end
end
context 'when enabled' do
before do
Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled)
end
it "allows users to publish group level Maven packages duplicates using #{params[:authentication_token_type]}" do
it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do
create_duplicated_package
push_duplicated_package
@ -163,68 +245,68 @@ module QA
end
end
end
end
def create_duplicated_package
settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven', 'settings_with_pat.xml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
def create_duplicated_package
settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven', 'settings_with_pat.xml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
with_fixtures([
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml_with_pat
}
]) do |dir|
Service::DockerRun::Maven.new(dir).publish!
end
package_project.visit!
Page::Project::Menu.perform(&:click_packages_link)
Page::Project::Packages::Index.perform do |index|
expect(index).to have_package(package_name)
end
with_fixtures([
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml_with_pat
}
]) do |dir|
Service::DockerRun::Maven.new(dir).publish!
end
def push_duplicated_package
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
package_project.visit!
commit.project = client_project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_upload_package_yaml
},
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
end
Page::Project::Menu.perform(&:click_packages_link)
Page::Project::Packages::Index.perform do |index|
expect(index).to have_package(package_name)
end
end
def push_duplicated_package
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding)
package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding)
settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding)
commit.project = client_project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files([
{
file_path: '.gitlab-ci.yml',
content: maven_upload_package_yaml
},
{
file_path: 'pom.xml',
content: package_pom_xml
},
{
file_path: 'settings.xml',
content: settings_xml
}
])
end
end
end
def show_latest_deploy_job
client_project.visit!
def show_latest_deploy_job
client_project.visit!
Flow::Pipeline.visit_latest_pipeline
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('deploy')
end
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('deploy')
end
end
end

View File

@ -7,6 +7,7 @@ module QA
module Specs
class Runner < Scenario::Template
attr_accessor :tty, :tags, :options
RegexMismatchError = Class.new(StandardError)
DEFAULT_TEST_PATH_ARGS = ['--', File.expand_path('./features', __dir__)].freeze

View File

@ -1,12 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1
memory_bundle_mem_file_name = ARGV.first
abort "usage: #{__FILE__} <memory_bundle_mem_file_name_prefix> <test_count>" unless ARGV.length == 2
memory_bundle_mem_file_name_prefix = ARGV.first
test_count = ARGV.last.to_i
full_report = File.open(memory_bundle_mem_file_name).read
results = []
(1..test_count).each do |i|
report_filename = "#{memory_bundle_mem_file_name_prefix}#{i}.txt"
stats = /TOP: (?<total_mibs_str>.*) MiB/.match(full_report)
abort 'failed to process the benchmark output' unless stats
stats = nil
File.foreach(report_filename).detect do |line|
stats = /TOP: (?<total_mibs_str>.*) MiB/.match(line)
end
abort 'failed to process the benchmark output' unless stats
puts "total_memory_used_by_dependencies_on_boot_prod_env_mb #{stats[:total_mibs_str].to_f.round(1)}"
total_mibs = stats[:total_mibs_str].to_f
results << total_mibs
end
res = results.sort
median = (res[(test_count - 1) / 2] + res[test_count / 2]) / 2.0
METRIC_NAME = "total_memory_used_by_dependencies_on_boot_prod_env_mb"
puts "# TYPE #{METRIC_NAME} gauge"
puts "# UNIT #{METRIC_NAME} mebibytes"
puts "#{METRIC_NAME} #{median.round(1)}"

View File

@ -501,6 +501,7 @@ RSpec.describe ApplicationController do
describe '#append_info_to_payload' do
controller(described_class) do
attr_reader :last_payload
urgency :high, [:foo]
def index

View File

@ -0,0 +1,24 @@
{
"type": "object",
"required": [
"id",
"url",
"created_at",
"push_events",
"tag_push_events",
"merge_requests_events",
"repository_update_events",
"enable_ssl_verification"
],
"properties": {
"id": { "type": "integer" },
"url": { "type": "string" },
"created_at": { "type": "string" },
"push_events": { "type": "boolean" },
"tag_push_events": { "type": "boolean" },
"merge_requests_events": { "type": "boolean" },
"repository_update_events": { "type": "boolean" },
"enable_ssl_verification": { "type": "boolean" }
},
"additionalProperties": false
}

View File

@ -0,0 +1,9 @@
{
"type": "array",
"items": {
"type": "object",
"properties" : {
"$ref": "./system_hook.json"
}
}
}

View File

@ -0,0 +1,43 @@
import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
describe('loadingIconForLegacyJS', () => {
it('sets the correct defaults', () => {
const el = loadingIconForLegacyJS();
expect(el.tagName).toBe('DIV');
expect(el.className).toBe('gl-spinner-container');
expect(el.querySelector('.gl-spinner-sm')).toEqual(expect.any(HTMLElement));
expect(el.querySelector('.gl-spinner-dark')).toEqual(expect.any(HTMLElement));
expect(el.querySelector('[aria-label="Loading"]')).toEqual(expect.any(HTMLElement));
expect(el.getAttribute('role')).toBe('status');
});
it('renders a span if inline = true', () => {
expect(loadingIconForLegacyJS({ inline: true }).tagName).toBe('SPAN');
});
it('can render a different size', () => {
const el = loadingIconForLegacyJS({ size: 'lg' });
expect(el.querySelector('.gl-spinner-lg')).toEqual(expect.any(HTMLElement));
});
it('can render a different color', () => {
const el = loadingIconForLegacyJS({ color: 'light' });
expect(el.querySelector('.gl-spinner-light')).toEqual(expect.any(HTMLElement));
});
it('can render a different aria-label', () => {
const el = loadingIconForLegacyJS({ label: 'Foo' });
expect(el.querySelector('[aria-label="Foo"]')).toEqual(expect.any(HTMLElement));
});
it('can render additional classes', () => {
const classes = ['foo', 'bar'];
const el = loadingIconForLegacyJS({ classes });
expect(el.classList).toContain(...classes);
});
});

View File

@ -8,7 +8,7 @@ RSpec.describe Gitlab::JsonCache do
let(:backend) { double('backend').as_null_object }
let(:namespace) { 'geo' }
let(:key) { 'foo' }
let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}" }
let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab.revision}" }
subject(:cache) { described_class.new(namespace: namespace, backend: backend) }
@ -35,69 +35,63 @@ RSpec.describe Gitlab::JsonCache do
end
describe '#cache_key' do
context 'when namespace is not defined' do
context 'when cache_key_with_version is true' do
it 'expands out the key with GitLab, and Rails versions' do
cache = described_class.new(cache_key_with_version: true)
using RSpec::Parameterized::TableSyntax
cache_key = cache.cache_key(key)
expect(cache_key).to eq("#{key}:#{Gitlab::VERSION}:#{Rails.version}")
end
end
context 'when cache_key_with_version is false' do
it 'returns the key' do
cache = described_class.new(namespace: nil, cache_key_with_version: false)
cache_key = cache.cache_key(key)
expect(cache_key).to eq(key)
end
end
where(:namespace, :cache_key_strategy, :expanded_key) do
nil | :revision | "#{key}:#{Gitlab.revision}"
nil | :version | "#{key}:#{Gitlab::VERSION}:#{Rails.version}"
namespace | :revision | "#{namespace}:#{key}:#{Gitlab.revision}"
namespace | :version | "#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}"
end
context 'when namespace is nil' do
context 'when cache_key_with_version is true' do
it 'expands out the key with GitLab, and Rails versions' do
cache = described_class.new(cache_key_with_version: true)
with_them do
let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: cache_key_strategy) }
cache_key = cache.cache_key(key)
subject { cache.cache_key(key) }
expect(cache_key).to eq("#{key}:#{Gitlab::VERSION}:#{Rails.version}")
end
end
context 'when cache_key_with_version is false' do
it 'returns the key' do
cache = described_class.new(namespace: nil, cache_key_with_version: false)
cache_key = cache.cache_key(key)
expect(cache_key).to eq(key)
end
end
it { is_expected.to eq expanded_key }
end
context 'when namespace is set' do
context 'when cache_key_with_version is true' do
it 'expands out the key with namespace and Rails version' do
cache = described_class.new(namespace: namespace, cache_key_with_version: true)
context 'when cache_key_strategy is unknown' do
let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: 'unknown') }
cache_key = cache.cache_key(key)
expect(cache_key).to eq("#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}")
end
it 'raises KeyError' do
expect { cache.cache_key('key') }.to raise_error(KeyError)
end
end
end
context 'when cache_key_with_version is false' do
it 'expands out the key with namespace' do
cache = described_class.new(namespace: namespace, cache_key_with_version: false)
describe '#namespace' do
it 'defaults to nil' do
cache = described_class.new
expect(cache.namespace).to be_nil
end
end
cache_key = cache.cache_key(key)
describe '#strategy_key_component' do
subject { cache.strategy_key_component }
expect(cache_key).to eq("#{namespace}:#{key}")
end
it 'defaults to Gitlab.revision' do
expect(described_class.new.strategy_key_component).to eq Gitlab.revision
end
context 'when cache_key_strategy is :revision' do
let(:cache) { described_class.new(cache_key_strategy: :revision) }
it { is_expected.to eq Gitlab.revision }
end
context 'when cache_key_strategy is :version' do
let(:cache) { described_class.new(cache_key_strategy: :version) }
it { is_expected.to eq [Gitlab::VERSION, Rails.version] }
end
context 'when cache_key_strategy is invalid' do
let(:cache) { described_class.new(cache_key_strategy: 'unknown') }
it 'raises KeyError' do
expect { subject }.to raise_error(KeyError)
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CertBasedClustersFfMetric do
context 'with FF enabled' do
it_behaves_like 'a correct instrumented metric value', { time_frame: '7d', data_source: 'database' } do
let(:expected_value) { true }
end
end
context 'with FF disabled' do
before do
stub_feature_flags(certificate_based_clusters: false)
end
it_behaves_like 'a correct instrumented metric value', { time_frame: '7d', data_source: 'database' } do
let(:expected_value) { false }
end
end
end

View File

@ -1100,6 +1100,20 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
expect(subject[:settings][:user_cap_feature_enabled]).to eq(Gitlab::CurrentSettings.new_user_signups_cap)
end
it 'reports status of the certificate_based_clusters feature flag as true' do
expect(subject[:settings][:certificate_based_clusters_ff]).to eq(true)
end
context 'with certificate_based_clusters disabled' do
before do
stub_feature_flags(certificate_based_clusters: false)
end
it 'reports status of the certificate_based_clusters feature flag as false' do
expect(subject[:settings][:certificate_based_clusters_ff]).to eq(false)
end
end
context 'snowplow stats' do
before do
stub_feature_flags(usage_data_instrumentation: false)

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RecreateIndexSecurityCiBuildsOnNameAndIdParserWithNewFeatures, :migration do
let(:db) { described_class.new }
let(:pg_class) { table(:pg_class) }
let(:pg_index) { table(:pg_index) }
let(:async_indexes) { table(:postgres_async_indexes) }
it 'recreates index' do
reversible_migration do |migration|
migration.before -> {
expect(async_indexes.where(name: described_class::OLD_INDEX_NAME).exists?).to be false
expect(db.index_exists?(described_class::TABLE, described_class::COLUMNS, name: described_class::OLD_INDEX_NAME)).to be true
expect(db.index_exists?(described_class::TABLE, described_class::COLUMNS, name: described_class::NEW_INDEX_NAME)).to be false
}
migration.after -> {
expect(async_indexes.where(name: described_class::OLD_INDEX_NAME).exists?).to be true
expect(db.index_exists?(described_class::TABLE, described_class::COLUMNS, name: described_class::OLD_INDEX_NAME)).to be false
expect(db.index_exists?(described_class::TABLE, described_class::COLUMNS, name: described_class::NEW_INDEX_NAME)).to be true
}
end
end
end

View File

@ -286,9 +286,9 @@ RSpec.describe BroadcastMessage do
it 'flushes the Redis cache' do
message = create(:broadcast_message)
expect(Rails.cache).to receive(:delete).with(described_class::CACHE_KEY)
expect(Rails.cache).to receive(:delete).with(described_class::BANNER_CACHE_KEY)
expect(Rails.cache).to receive(:delete).with(described_class::NOTIFICATION_CACHE_KEY)
expect(Rails.cache).to receive(:delete).with("#{described_class::CACHE_KEY}:#{Gitlab.revision}")
expect(Rails.cache).to receive(:delete).with("#{described_class::BANNER_CACHE_KEY}:#{Gitlab.revision}")
expect(Rails.cache).to receive(:delete).with("#{described_class::NOTIFICATION_CACHE_KEY}:#{Gitlab.revision}")
message.flush_redis_cache
end

View File

@ -9,6 +9,7 @@ RSpec.describe Mentionable do
include Mentionable
attr_accessor :project, :message
attr_mentionable :message
def author

View File

@ -36,12 +36,57 @@ RSpec.describe API::SystemHooks do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(response).to match_response_schema('public_api/v4/system_hooks')
expect(json_response.first).not_to have_key("token")
expect(json_response.first['url']).to eq(hook.url)
expect(json_response.first['push_events']).to be false
expect(json_response.first['tag_push_events']).to be false
expect(json_response.first['merge_requests_events']).to be false
expect(json_response.first['repository_update_events']).to be true
expect(json_response.first['enable_ssl_verification']).to be true
end
end
end
describe "GET /hooks/:id" do
context "when no user" do
it "returns authentication error" do
get api("/hooks/#{hook.id}")
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context "when not an admin" do
it "returns forbidden error" do
get api("/hooks/#{hook.id}", user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context "when authenticated as admin" do
it "gets a hook", :aggregate_failures do
get api("/hooks/#{hook.id}", admin)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/system_hook')
expect(json_response).to match(
'id' => be(hook.id),
'url' => eq(hook.url),
'created_at' => eq(hook.created_at.iso8601(3)),
'push_events' => be(hook.push_events),
'tag_push_events' => be(hook.tag_push_events),
'merge_requests_events' => be(hook.merge_requests_events),
'repository_update_events' => be(hook.repository_update_events),
'enable_ssl_verification' => be(hook.enable_ssl_verification)
)
end
it 'returns 404 if the system hook does not exist' do
get api("/hooks/#{non_existing_record_id}", admin)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
@ -77,6 +122,7 @@ RSpec.describe API::SystemHooks do
post api('/hooks', admin), params: { url: 'http://mep.mep' }
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/system_hook')
expect(json_response['enable_ssl_verification']).to be true
expect(json_response['push_events']).to be false
expect(json_response['tag_push_events']).to be false
@ -98,6 +144,7 @@ RSpec.describe API::SystemHooks do
}
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/system_hook')
expect(json_response['enable_ssl_verification']).to be false
expect(json_response['push_events']).to be true
expect(json_response['tag_push_events']).to be true

View File

@ -26,6 +26,7 @@ module SortingHelper
include Comparable
attr_reader :value
delegate :==, :eql?, :hash, to: :value
def initialize(value)

View File

@ -1,467 +0,0 @@
# frozen_string_literal: true
require 'gitlab-dangerfiles'
require 'gitlab/dangerfiles/spec_helper'
require_relative '../../../tooling/danger/changelog'
require_relative '../../../tooling/danger/project_helper'
RSpec.describe Tooling::Danger::Changelog do
include_context "with dangerfile"
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
let(:fake_project_helper) { double('fake-project-helper', helper: fake_helper).tap { |h| h.class.include(Tooling::Danger::ProjectHelper) } }
subject(:changelog) { fake_danger.new(helper: fake_helper) }
before do
allow(changelog).to receive(:project_helper).and_return(fake_project_helper)
end
describe '#check_changelog_commit_categories' do
context 'when all changelog commits are correct' do
it 'does not produce any messages' do
commit = double(:commit, message: "foo\nChangelog: fixed")
allow(changelog).to receive(:changelog_commits).and_return([commit])
expect(changelog).not_to receive(:fail)
changelog.check_changelog_commit_categories
end
end
context 'when a commit has an incorrect trailer' do
it 'adds a message' do
commit = double(:commit, message: "foo\nChangelog: foo", sha: '123')
allow(changelog).to receive(:changelog_commits).and_return([commit])
expect(changelog).to receive(:fail)
changelog.check_changelog_commit_categories
end
end
end
describe '#check_changelog_trailer' do
subject { changelog.check_changelog_trailer(commit) }
context "when commit include a changelog trailer with an unknown category" do
let(:commit) { double('commit', message: "Hello world\n\nChangelog: foo", sha: "abc123") }
it { is_expected.to have_attributes(errors: ["Commit #{commit.sha} uses an invalid changelog category: foo"]) }
end
context 'when a commit uses the wrong casing for a trailer' do
let(:commit) { double('commit', message: "Hello world\n\nchangelog: foo", sha: "abc123") }
it { is_expected.to have_attributes(errors: ["The changelog trailer for commit #{commit.sha} must be `Changelog` (starting with a capital C), not `changelog`"]) }
end
described_class::CATEGORIES.each do |category|
context "when commit include a changelog trailer with category set to '#{category}'" do
let(:commit) { double('commit', message: "Hello world\n\nChangelog: #{category}", sha: "abc123") }
it { is_expected.to have_attributes(errors: []) }
end
end
end
describe '#check_changelog_path' do
let(:changelog_path) { 'changelog-path.yml' }
let(:foss_change) { nil }
let(:ee_change) { nil }
let(:changelog_change) { nil }
let(:changes) { changes_class.new([foss_change, ee_change, changelog_change].compact) }
before do
allow(changelog).to receive(:present?).and_return(true)
end
subject { changelog.check_changelog_path }
context "when changelog is not present" do
before do
allow(changelog).to receive(:present?).and_return(false)
end
it { is_expected.to have_attributes(errors: [], warnings: [], markdowns: [], messages: []) }
end
context "with EE changes" do
let(:ee_change) { change_class.new('ee/app/models/foo.rb', :added, :backend) }
context "and a non-EE changelog, and changelog not required" do
before do
allow(changelog).to receive(:required?).and_return(false)
allow(changelog).to receive(:ee_changelog?).and_return(false)
end
it { is_expected.to have_attributes(warnings: ["This MR changes code in `ee/`, but its Changelog commit is missing the [`EE: true` trailer](https://docs.gitlab.com/ee/development/changelog.html#gitlab-enterprise-changes). Consider adding it to your Changelog commits."]) }
end
context "and a EE changelog" do
before do
allow(changelog).to receive(:ee_changelog?).and_return(true)
end
it { is_expected.to have_attributes(errors: [], warnings: [], markdowns: [], messages: []) }
context "and there are DB changes" do
let(:foss_change) { change_class.new('db/migrate/foo.rb', :added, :migration) }
it { is_expected.to have_attributes(warnings: ["This MR has a Changelog commit with the `EE: true` trailer, but there are database changes which [requires](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry) the Changelog commit to not have the `EE: true` trailer. Consider removing the `EE: true` trailer from your commits."]) }
end
end
end
context "with no EE changes" do
let(:foss_change) { change_class.new('app/models/foo.rb', :added, :backend) }
context "and a non-EE changelog" do
before do
allow(changelog).to receive(:ee_changelog?).and_return(false)
end
it { is_expected.to have_attributes(errors: [], warnings: [], markdowns: [], messages: []) }
end
context "and a EE changelog" do
before do
allow(changelog).to receive(:ee_changelog?).and_return(true)
end
it { is_expected.to have_attributes(warnings: ["This MR has a Changelog commit for EE, but no code changes in `ee/`. Consider removing the `EE: true` trailer from your commits."]) }
end
end
end
describe '#required_reasons' do
subject { changelog.required_reasons }
context "added files contain a migration" do
let(:changes) { changes_class.new([change_class.new('foo', :added, :migration)]) }
it { is_expected.to include(:db_changes) }
end
context "removed files contains a feature flag" do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :feature_flag)]) }
it { is_expected.to include(:feature_flag_removed) }
end
context "added files do not contain a migration" do
let(:changes) { changes_class.new([change_class.new('foo', :added, :frontend)]) }
it { is_expected.to be_empty }
end
context "removed files do not contain a feature flag" do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :backend)]) }
it { is_expected.to be_empty }
end
end
describe '#required?' do
subject { changelog.required? }
context 'added files contain a migration' do
let(:changes) { changes_class.new([change_class.new('foo', :added, :migration)]) }
it { is_expected.to be_truthy }
end
context "removed files contains a feature flag" do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :feature_flag)]) }
it { is_expected.to be_truthy }
end
context 'added files do not contain a migration' do
let(:changes) { changes_class.new([change_class.new('foo', :added, :frontend)]) }
it { is_expected.to be_falsey }
end
context "removed files do not contain a feature flag" do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :backend)]) }
it { is_expected.to be_falsey }
end
end
describe '#optional?' do
let(:category_with_changelog) { :backend }
let(:label_with_changelog) { 'frontend' }
let(:category_without_changelog) { Tooling::Danger::Changelog::NO_CHANGELOG_CATEGORIES.first }
let(:label_without_changelog) { Tooling::Danger::Changelog::NO_CHANGELOG_LABELS.first }
subject { changelog.optional? }
context 'when MR contains only categories requiring no changelog' do
let(:changes) { changes_class.new([change_class.new('foo', :modified, category_without_changelog)]) }
it 'is falsey' do
is_expected.to be_falsy
end
end
context 'when MR contains a label that require no changelog' do
let(:changes) { changes_class.new([change_class.new('foo', :modified, category_with_changelog)]) }
let(:mr_labels) { [label_with_changelog, label_without_changelog] }
it 'is falsey' do
is_expected.to be_falsy
end
end
context 'when MR contains a category that require changelog and a category that require no changelog' do
let(:changes) { changes_class.new([change_class.new('foo', :modified, category_with_changelog), change_class.new('foo', :modified, category_without_changelog)]) }
context 'with no labels' do
it 'is truthy' do
is_expected.to be_truthy
end
end
context 'with changelog label' do
let(:mr_labels) { ['type::feature'] }
it 'is truthy' do
is_expected.to be_truthy
end
end
context 'with no changelog label' do
let(:mr_labels) { ['type::tooling'] }
it 'is truthy' do
is_expected.to be_falsey
end
end
end
end
describe '#present?' do
it 'returns true when a Changelog commit is present' do
allow(changelog)
.to receive(:valid_changelog_commits)
.and_return([double(:commit)])
expect(changelog).to be_present
end
it 'returns false when a Changelog commit is missing' do
allow(changelog).to receive(:valid_changelog_commits).and_return([])
expect(changelog).not_to be_present
end
end
describe '#changelog_commits' do
it 'returns the commits that include a Changelog trailer' do
commit1 = double(:commit, message: "foo\nChangelog: fixed")
commit2 = double(:commit, message: "bar\nChangelog: kittens")
commit3 = double(:commit, message: 'testing')
git = double(:git)
allow(changelog).to receive(:git).and_return(git)
allow(git).to receive(:commits).and_return([commit1, commit2, commit3])
expect(changelog.changelog_commits).to eq([commit1, commit2])
end
end
describe '#valid_changelog_commits' do
it 'returns the commits with a valid Changelog trailer' do
commit1 = double(:commit, message: "foo\nChangelog: fixed")
commit2 = double(:commit, message: "bar\nChangelog: kittens")
allow(changelog)
.to receive(:changelog_commits)
.and_return([commit1, commit2])
expect(changelog.valid_changelog_commits).to eq([commit1])
end
end
describe '#ee_changelog?' do
it 'returns true when an EE changelog commit is present' do
commit = double(:commit, message: "foo\nEE: true")
allow(changelog).to receive(:changelog_commits).and_return([commit])
expect(changelog.ee_changelog?).to eq(true)
end
it 'returns false when an EE changelog commit is missing' do
commit = double(:commit, message: 'foo')
allow(changelog).to receive(:changelog_commits).and_return([commit])
expect(changelog.ee_changelog?).to eq(false)
end
end
describe '#modified_text' do
subject { changelog.modified_text }
context 'when in CI context' do
shared_examples 'changelog modified text' do |key|
specify do
expect(subject).to include('CHANGELOG.md was edited')
expect(subject).to include('`Changelog` trailer')
expect(subject).to include('`EE: true`')
end
end
before do
allow(fake_helper).to receive(:ci?).and_return(true)
end
context "when title is not changed from sanitization", :aggregate_failures do
let(:mr_title) { 'Fake Title' }
it_behaves_like 'changelog modified text'
end
context "when title needs sanitization", :aggregate_failures do
let(:mr_title) { 'DRAFT: Fake Title' }
it_behaves_like 'changelog modified text'
end
end
context 'when in local context' do
let(:mr_title) { 'Fake Title' }
before do
allow(fake_helper).to receive(:ci?).and_return(false)
end
specify do
expect(subject).to include('CHANGELOG.md was edited')
expect(subject).not_to include('`Changelog` trailer')
end
end
end
describe '#required_texts' do
let(:mr_title) { 'Fake Title' }
subject { changelog.required_texts }
context 'when in CI context' do
before do
allow(fake_helper).to receive(:ci?).and_return(true)
end
shared_examples 'changelog required text' do |key|
specify do
expect(subject).to have_key(key)
expect(subject[key]).to include('CHANGELOG missing')
expect(subject[key]).to include('`Changelog` trailer')
end
end
context 'with a new migration file' do
let(:changes) { changes_class.new([change_class.new('foo', :added, :migration)]) }
context "when title is not changed from sanitization", :aggregate_failures do
it_behaves_like 'changelog required text', :db_changes
end
context "when title needs sanitization", :aggregate_failures do
let(:mr_title) { 'DRAFT: Fake Title' }
it_behaves_like 'changelog required text', :db_changes
end
end
context 'with a removed feature flag file' do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :feature_flag)]) }
it_behaves_like 'changelog required text', :feature_flag_removed
end
end
context 'when in local context' do
before do
allow(fake_helper).to receive(:ci?).and_return(false)
end
shared_examples 'changelog required text' do |key|
specify do
expect(subject).to have_key(key)
expect(subject[key]).to include('CHANGELOG missing')
expect(subject[key]).not_to include('`Changelog` trailer')
end
end
context 'with a new migration file' do
let(:changes) { changes_class.new([change_class.new('foo', :added, :migration)]) }
context "when title is not changed from sanitization", :aggregate_failures do
it_behaves_like 'changelog required text', :db_changes
end
context "when title needs sanitization", :aggregate_failures do
let(:mr_title) { 'DRAFT: Fake Title' }
it_behaves_like 'changelog required text', :db_changes
end
end
context 'with a removed feature flag file' do
let(:changes) { changes_class.new([change_class.new('foo', :deleted, :feature_flag)]) }
it_behaves_like 'changelog required text', :feature_flag_removed
end
end
end
describe '#optional_text' do
subject { changelog.optional_text }
context 'when in CI context' do
shared_examples 'changelog optional text' do |key|
specify do
expect(subject).to include('CHANGELOG missing')
expect(subject).to include('`Changelog` trailer')
expect(subject).to include('EE: true')
end
end
before do
allow(fake_helper).to receive(:ci?).and_return(true)
end
context "when title is not changed from sanitization", :aggregate_failures do
let(:mr_title) { 'Fake Title' }
it_behaves_like 'changelog optional text'
end
context "when title needs sanitization", :aggregate_failures do
let(:mr_title) { 'DRAFT: Fake Title' }
it_behaves_like 'changelog optional text'
end
end
context 'when in local context' do
let(:mr_title) { 'Fake Title' }
before do
allow(fake_helper).to receive(:ci?).and_return(false)
end
specify do
expect(subject).to include('CHANGELOG missing')
end
end
end
end

View File

@ -44,7 +44,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do
'ee/README' | [:unknown]
'app/assets/foo' | [:frontend]
'app/views/foo' | [:frontend]
'app/views/foo' | [:frontend, :backend]
'public/foo' | [:frontend]
'scripts/frontend/foo' | [:frontend]
'spec/frontend/bar' | [:frontend]
@ -58,7 +58,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do
'config/deep/foo.js' | [:frontend]
'ee/app/assets/foo' | [:frontend]
'ee/app/views/foo' | [:frontend]
'ee/app/views/foo' | [:frontend, :backend]
'ee/spec/frontend/bar' | [:frontend]
'ee/spec/frontend_integration/bar' | [:frontend]
@ -231,9 +231,12 @@ RSpec.describe Tooling::Danger::ProjectHelper do
'ee/app/assets/javascripts/integrations/zentao/issues_list/graphql/queries/get_zentao_issues.query.graphql' | [:integrations_fe, :frontend]
'app/assets/javascripts/pages/projects/settings/integrations/show/index.js' | [:integrations_fe, :frontend]
'ee/app/assets/javascripts/pages/groups/hooks/index.js' | [:integrations_fe, :frontend]
'app/views/clusters/clusters/_integrations_tab.html.haml' | [:frontend]
'app/views/clusters/clusters/_integrations_tab.html.haml' | [:frontend, :backend]
'app/assets/javascripts/alerts_settings/graphql/fragments/integration_item.fragment.graphql' | [:frontend]
'app/assets/javascripts/filtered_search/droplab/hook_input.js' | [:frontend]
'app/views/layouts/header/_default.html.haml' | [:frontend, :backend]
'app/views/layouts/header/_default.html.erb' | [:frontend, :backend]
end
with_them do
@ -274,7 +277,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do
describe '.local_warning_message' do
it 'returns an informational message with rules that can run' do
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, ci_config, database, documentation, duplicate_yarn_dependencies, eslint, gitaly, pajamas, pipeline, prettier, product_intelligence, utility_css, vue_shared_documentation, datateam')
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: ci_config, database, documentation, duplicate_yarn_dependencies, eslint, gitaly, pajamas, pipeline, prettier, product_intelligence, utility_css, vue_shared_documentation, datateam')
end
end
@ -306,18 +309,6 @@ RSpec.describe Tooling::Danger::ProjectHelper do
end
end
describe '#all_ee_changes' do
subject { project_helper.all_ee_changes }
it 'returns all changed files starting with ee/' do
changes = double
expect(fake_helper).to receive(:changes).and_return(changes)
expect(changes).to receive(:files).and_return(%w[fr/ee/beer.rb ee/wine.rb ee/lib/ido.rb ee.k])
is_expected.to match_array(%w[ee/wine.rb ee/lib/ido.rb])
end
end
describe '#file_lines' do
let(:filename) { 'spec/foo_spec.rb' }
let(:file_spy) { spy }

View File

@ -12,6 +12,7 @@ RSpec.describe ArrayMembersValidator do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :children
validates :children, array_members: { member_class: child_class }
end
end

View File

@ -10,6 +10,7 @@ RSpec.describe ColorValidator do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :color
validates :color, color: true
end.new
end

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