Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8188ca655a
commit
e804afddbf
|
@ -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"
|
||||
|
|
|
@ -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/**/*"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
Dangerfile
21
Dangerfile
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
b6e1f3ce3799d61cb7cdbd67952d82c126f44c4f
|
||||
c05b46905eacc7f4bd69f738fdf85ebb0b84ee4b
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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, {
|
||||
|
|
|
@ -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.'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.'
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/ }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module Storage
|
||||
class Hashed
|
||||
attr_accessor :container
|
||||
|
||||
delegate :gitlab_shell, :repository_storage, to: :container
|
||||
|
||||
REPOSITORY_PATH_PREFIX = '@hashed'
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module Storage
|
||||
class LegacyProject
|
||||
attr_accessor :project
|
||||
|
||||
delegate :namespace, :gitlab_shell, :repository_storage, to: :project
|
||||
|
||||
def initialize(project)
|
||||
|
|
|
@ -45,6 +45,7 @@ class WikiPage
|
|||
|
||||
# The GitLab Wiki instance.
|
||||
attr_reader :wiki
|
||||
|
||||
delegate :container, to: :wiki
|
||||
|
||||
# The raw Gitlab::Git::WikiPage instance.
|
||||
|
|
|
@ -17,6 +17,7 @@ module Ci
|
|||
MAX_TRACKABLE_FAILURES = 200
|
||||
|
||||
attr_reader :pipeline
|
||||
|
||||
delegate :project, to: :pipeline
|
||||
|
||||
def initialize(pipeline)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -4,6 +4,7 @@ module NotificationRecipients
|
|||
module Builder
|
||||
class MergeRequestUnmergeable < Base
|
||||
attr_reader :target
|
||||
|
||||
def initialize(merge_request)
|
||||
@target = merge_request
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ module NotificationRecipients
|
|||
module Builder
|
||||
class NewNote < Base
|
||||
attr_reader :note
|
||||
|
||||
def initialize(note)
|
||||
@note = note
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ module NotificationRecipients
|
|||
module Builder
|
||||
class NewReview < Base
|
||||
attr_reader :review
|
||||
|
||||
def initialize(review)
|
||||
@review = review
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
class NotificationService
|
||||
class Async
|
||||
attr_reader :parent
|
||||
|
||||
delegate :respond_to_missing, to: :parent
|
||||
|
||||
def initialize(parent)
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -34,6 +34,7 @@ end
|
|||
|
||||
class Net::HTTP
|
||||
attr_accessor :hostname_override
|
||||
|
||||
SSL_IVNAMES << :@hostname_override
|
||||
SSL_ATTRIBUTES << :hostname_override
|
||||
|
||||
|
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
changelog.check!
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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?
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
1d7105559c8d2da1d86c5625c592edc792d7cd729b8c86c7a2b950c3dd98e975
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,9 +136,28 @@ 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. 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`.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,6 +12,7 @@ module Gitlab
|
|||
|
||||
class InsufficientScopeError < AuthenticationError
|
||||
attr_reader :scopes
|
||||
|
||||
def initialize(scopes)
|
||||
@scopes = scopes.map { |s| s.try(:name) || s }
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ module Gitlab
|
|||
module OAuth
|
||||
class AuthHash
|
||||
attr_reader :auth_hash
|
||||
|
||||
def initialize(auth_hash)
|
||||
@auth_hash = auth_hash
|
||||
end
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -94,6 +94,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
attr_reader :project, :destination, :started_at, :log_conditions
|
||||
|
||||
delegate :current_monotonic_time, to: :class
|
||||
|
||||
def age
|
||||
|
|
|
@ -23,6 +23,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
attr_reader :trace_artifact
|
||||
|
||||
delegate :aws?, :google?, to: :object_store_config, prefix: :provider
|
||||
|
||||
def fetch_md5_checksum
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -46,6 +46,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
attr_reader :model
|
||||
|
||||
delegate :connection, to: :model
|
||||
|
||||
def missing_partitions
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -8,6 +8,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
attr_reader :raw_body
|
||||
|
||||
def initialize(raw_body)
|
||||
@raw_body = raw_body
|
||||
end
|
||||
|
|
|
@ -63,6 +63,7 @@ module Gitlab
|
|||
|
||||
class BlameLine
|
||||
attr_accessor :lineno, :oldlineno, :commit, :line
|
||||
|
||||
def initialize(lineno, oldlineno, commit, line)
|
||||
@lineno = lineno
|
||||
@oldlineno = oldlineno
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -10,6 +10,7 @@ module Gitlab
|
|||
#
|
||||
class InsecureKeyFingerprint
|
||||
attr_accessor :key
|
||||
|
||||
alias_attribute :fingerprint_md5, :fingerprint
|
||||
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
||||
if cache_key_with_version
|
||||
expanded_cache_key << [Gitlab::VERSION, Rails.version]
|
||||
expanded_cache_key = [namespace, key, *strategy_key_component].compact
|
||||
expanded_cache_key.join(':').freeze
|
||||
end
|
||||
|
||||
expanded_cache_key.flatten.join(':').freeze
|
||||
def strategy_key_component
|
||||
STRATEGY_KEY_COMPONENTS.fetch(@cache_key_strategy)
|
||||
end
|
||||
|
||||
def expire(key)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -15,6 +15,7 @@ module Sidebars
|
|||
include ::Sidebars::Concerns::HasPartial
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :current_user, :container, to: :@context
|
||||
|
||||
def initialize(context)
|
||||
|
|
|
@ -12783,6 +12783,9 @@ msgstr ""
|
|||
msgid "Direct member"
|
||||
msgstr ""
|
||||
|
||||
msgid "Direct members"
|
||||
msgstr ""
|
||||
|
||||
msgid "Direct non-authenticated users to this page."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,10 +13,25 @@ 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
|
||||
|
@ -31,7 +46,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it "pushes and pulls a maven package via maven using #{params[:authentication_token_type]}" do
|
||||
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)
|
||||
|
@ -118,6 +133,8 @@ module QA
|
|||
expect(job).to be_successful(timeout: 800)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'duplication setting' do
|
||||
before do
|
||||
|
@ -127,11 +144,43 @@ module QA
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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,6 +245,7 @@ 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)
|
||||
|
@ -230,4 +313,3 @@ module QA
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
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)}"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties" : {
|
||||
"$ref": "./system_hook.json"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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
|
||||
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 cache_key_with_version is false' do
|
||||
it 'returns the key' do
|
||||
cache = described_class.new(namespace: nil, cache_key_with_version: false)
|
||||
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)
|
||||
it { is_expected.to eq expanded_key }
|
||||
end
|
||||
|
||||
context 'when cache_key_strategy is unknown' do
|
||||
let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: 'unknown') }
|
||||
|
||||
it 'raises KeyError' do
|
||||
expect { cache.cache_key('key') }.to raise_error(KeyError)
|
||||
end
|
||||
end
|
||||
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)
|
||||
|
||||
cache_key = cache.cache_key(key)
|
||||
|
||||
expect(cache_key).to eq("#{key}:#{Gitlab::VERSION}:#{Rails.version}")
|
||||
describe '#namespace' do
|
||||
it 'defaults to nil' do
|
||||
cache = described_class.new
|
||||
expect(cache.namespace).to be_nil
|
||||
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)
|
||||
describe '#strategy_key_component' do
|
||||
subject { cache.strategy_key_component }
|
||||
|
||||
cache_key = cache.cache_key(key)
|
||||
|
||||
expect(cache_key).to eq(key)
|
||||
end
|
||||
end
|
||||
it 'defaults to Gitlab.revision' do
|
||||
expect(described_class.new.strategy_key_component).to eq Gitlab.revision
|
||||
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 :revision' do
|
||||
let(:cache) { described_class.new(cache_key_strategy: :revision) }
|
||||
|
||||
cache_key = cache.cache_key(key)
|
||||
|
||||
expect(cache_key).to eq("#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}")
|
||||
end
|
||||
it { is_expected.to eq Gitlab.revision }
|
||||
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)
|
||||
context 'when cache_key_strategy is :version' do
|
||||
let(:cache) { described_class.new(cache_key_strategy: :version) }
|
||||
|
||||
cache_key = cache.cache_key(key)
|
||||
|
||||
expect(cache_key).to eq("#{namespace}:#{key}")
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -9,6 +9,7 @@ RSpec.describe Mentionable do
|
|||
include Mentionable
|
||||
|
||||
attr_accessor :project, :message
|
||||
|
||||
attr_mentionable :message
|
||||
|
||||
def author
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -26,6 +26,7 @@ module SortingHelper
|
|||
include Comparable
|
||||
|
||||
attr_reader :value
|
||||
|
||||
delegate :==, :eql?, :hash, to: :value
|
||||
|
||||
def initialize(value)
|
||||
|
|
|
@ -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
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue