diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-28 21:14:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-28 21:14:03 +0300 |
commit | e804afddbf68cc6f306bc4aa9aaea88be774ebe4 (patch) | |
tree | 0ecb2ebf35e16b866b16da375920eb58f6353ef7 | |
parent | 8188ca655a7437381491e565406869c747c1b40a (diff) |
Add latest changes from gitlab-org/gitlab@master
104 files changed, 888 insertions, 1144 deletions
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml index c6572d9709c..efdae0715aa 100644 --- a/.gitlab/ci/memory.gitlab-ci.yml +++ b/.gitlab/ci/memory.gitlab-ci.yml @@ -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" diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 013dda2169d..b72b797aad8 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -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/**/*" diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7597ece5d08..a3835072aca 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -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 diff --git a/.rubocop_todo/graphql/ordered_fields.yml b/.rubocop_todo/graphql/ordered_fields.yml index 28202cbcf2e..b6ddd018f4e 100644 --- a/.rubocop_todo/graphql/ordered_fields.yml +++ b/.rubocop_todo/graphql/ordered_fields.yml @@ -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 diff --git a/Dangerfile b/Dangerfile index 1fc2005498d..aaa1aae813b 100644 --- a/Dangerfile +++ b/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 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 2b5213196ae..6a2b312872d 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -b6e1f3ce3799d61cb7cdbd67952d82c126f44c4f +c05b46905eacc7f4bd69f738fdf85ebb0b84ee4b @@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index 5e98b5d2981..f0063f0dee7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index 82bbbe891e2..91e39dcd466 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -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); } diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index c24eeed9f03..3620c884c5f 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -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" diff --git a/app/assets/javascripts/loading_icon_for_legacy_js.js b/app/assets/javascripts/loading_icon_for_legacy_js.js new file mode 100644 index 00000000000..d50a4275424 --- /dev/null +++ b/app/assets/javascripts/loading_icon_for_legacy_js.js @@ -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; +}; diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index 7f4e79976bc..996e12bc105 100644 --- a/app/assets/javascripts/pages/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -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, { diff --git a/app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb b/app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb index dff52d77109..5c7aecf16ee 100644 --- a/app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb +++ b/app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb @@ -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.' diff --git a/app/graphql/types/error_tracking/sentry_error_type.rb b/app/graphql/types/error_tracking/sentry_error_type.rb index aaa6cbfb28f..5f871155737 100644 --- a/app/graphql/types/error_tracking/sentry_error_type.rb +++ b/app/graphql/types/error_tracking/sentry_error_type.rb @@ -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, - null: false, - description: 'Short ID (Sentry ID) of the error.' - field :status, Types::ErrorTracking::SentryErrorStatusEnum, + field :first_seen, Types::TimeType, 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 diff --git a/app/graphql/types/evidence_type.rb b/app/graphql/types/evidence_type.rb index 33f46c712f1..ed644a4b2c6 100644 --- a/app/graphql/types/evidence_type.rb +++ b/app/graphql/types/evidence_type.rb @@ -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 diff --git a/app/graphql/types/grafana_integration_type.rb b/app/graphql/types/grafana_integration_type.rb index 26fefd51e08..2bbc0d34db6 100644 --- a/app/graphql/types/grafana_integration_type.rb +++ b/app/graphql/types/grafana_integration_type.rb @@ -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 diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb index ee57961ee4a..6167b661caa 100644 --- a/app/graphql/types/issue_type.rb +++ b/app/graphql/types/issue_type.rb @@ -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.' diff --git a/app/graphql/types/jira_import_type.rb b/app/graphql/types/jira_import_type.rb index 0cdfc178350..8477f0b97f0 100644 --- a/app/graphql/types/jira_import_type.rb +++ b/app/graphql/types/jira_import_type.rb @@ -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 diff --git a/app/graphql/types/jira_user_type.rb b/app/graphql/types/jira_user_type.rb index 6e1c349726c..aba05385ece 100644 --- a/app/graphql/types/jira_user_type.rb +++ b/app/graphql/types/jira_user_type.rb @@ -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 diff --git a/app/graphql/types/label_type.rb b/app/graphql/types/label_type.rb index 5a10bcfee74..b5b3e20bcbc 100644 --- a/app/graphql/types/label_type.rb +++ b/app/graphql/types/label_type.rb @@ -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.' diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index e0c95370072..94ed83a7d4a 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -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 } diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 1ee5c081840..3addd09e738 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -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 diff --git a/app/models/lfs_download_object.rb b/app/models/lfs_download_object.rb index 319499fd1b7..3df6742fbc9 100644 --- a/app/models/lfs_download_object.rb +++ b/app/models/lfs_download_object.rb @@ -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/ } diff --git a/app/models/storage/hashed.rb b/app/models/storage/hashed.rb index c61cd3b6b30..05e93f00912 100644 --- a/app/models/storage/hashed.rb +++ b/app/models/storage/hashed.rb @@ -3,6 +3,7 @@ module Storage class Hashed attr_accessor :container + delegate :gitlab_shell, :repository_storage, to: :container REPOSITORY_PATH_PREFIX = '@hashed' diff --git a/app/models/storage/legacy_project.rb b/app/models/storage/legacy_project.rb index 092e5249a3e..0d12a629b8e 100644 --- a/app/models/storage/legacy_project.rb +++ b/app/models/storage/legacy_project.rb @@ -3,6 +3,7 @@ module Storage class LegacyProject attr_accessor :project + delegate :namespace, :gitlab_shell, :repository_storage, to: :project def initialize(project) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 3dbbbcdfe23..c533c1c23f3 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -45,6 +45,7 @@ class WikiPage # The GitLab Wiki instance. attr_reader :wiki + delegate :container, to: :wiki # The raw Gitlab::Git::WikiPage instance. diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb index a3f45c1b9cd..7323ad417ea 100644 --- a/app/services/ci/test_failure_history_service.rb +++ b/app/services/ci/test_failure_history_service.rb @@ -17,6 +17,7 @@ module Ci MAX_TRACKABLE_FAILURES = 200 attr_reader :pipeline + delegate :project, to: :pipeline def initialize(pipeline) diff --git a/app/services/concerns/rate_limited_service.rb b/app/services/concerns/rate_limited_service.rb index 26aa150fd65..5d7247a5b99 100644 --- a/app/services/concerns/rate_limited_service.rb +++ b/app/services/concerns/rate_limited_service.rb @@ -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) diff --git a/app/services/concerns/update_repository_storage_methods.rb b/app/services/concerns/update_repository_storage_methods.rb index cbcd0b7f56b..b21d05f4178 100644 --- a/app/services/concerns/update_repository_storage_methods.rb +++ b/app/services/concerns/update_repository_storage_methods.rb @@ -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) diff --git a/app/services/notification_recipients/builder/merge_request_unmergeable.rb b/app/services/notification_recipients/builder/merge_request_unmergeable.rb index 24d96b98002..b9facf07a3a 100644 --- a/app/services/notification_recipients/builder/merge_request_unmergeable.rb +++ b/app/services/notification_recipients/builder/merge_request_unmergeable.rb @@ -4,6 +4,7 @@ module NotificationRecipients module Builder class MergeRequestUnmergeable < Base attr_reader :target + def initialize(merge_request) @target = merge_request end diff --git a/app/services/notification_recipients/builder/new_note.rb b/app/services/notification_recipients/builder/new_note.rb index 17e4728d352..dcf6d23298a 100644 --- a/app/services/notification_recipients/builder/new_note.rb +++ b/app/services/notification_recipients/builder/new_note.rb @@ -4,6 +4,7 @@ module NotificationRecipients module Builder class NewNote < Base attr_reader :note + def initialize(note) @note = note end diff --git a/app/services/notification_recipients/builder/new_review.rb b/app/services/notification_recipients/builder/new_review.rb index 3b1296f6967..84598c3d4ad 100644 --- a/app/services/notification_recipients/builder/new_review.rb +++ b/app/services/notification_recipients/builder/new_review.rb @@ -4,6 +4,7 @@ module NotificationRecipients module Builder class NewReview < Base attr_reader :review + def initialize(review) @review = review end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 5b1733422d0..90b9c25f3a7 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -18,6 +18,7 @@ class NotificationService class Async attr_reader :parent + delegate :respond_to_missing, to: :parent def initialize(parent) diff --git a/app/services/projects/base_move_relations_service.rb b/app/services/projects/base_move_relations_service.rb index 3a159cef58b..bd5a39d3b59 100644 --- a/app/services/projects/base_move_relations_service.rb +++ b/app/services/projects/base_move_relations_service.rb @@ -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? diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb index 9da72d9300e..76005a1c96e 100644 --- a/app/services/projects/lfs_pointers/lfs_download_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_service.rb @@ -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) diff --git a/app/uploaders/content_type_whitelist.rb b/app/uploaders/content_type_whitelist.rb index 64bde16cb69..82c6b9b3a61 100644 --- a/app/uploaders/content_type_whitelist.rb +++ b/app/uploaders/content_type_whitelist.rb @@ -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 diff --git a/config/feature_flags/development/update_all_mirrors_job_tracker.yml b/config/feature_flags/development/mirror_scheduling_tracking.yml index 507f32550c3..3710c0aeb86 100644 --- a/config/feature_flags/development/update_all_mirrors_job_tracker.yml +++ b/config/feature_flags/development/mirror_scheduling_tracking.yml @@ -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 diff --git a/config/feature_flags/development/project_import_schedule_worker_job_tracker.yml b/config/feature_flags/development/project_import_schedule_worker_job_tracker.yml deleted file mode 100644 index 5dae4ddc60c..00000000000 --- a/config/feature_flags/development/project_import_schedule_worker_job_tracker.yml +++ /dev/null @@ -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 diff --git a/config/initializers/http_hostname_override.rb b/config/initializers/http_hostname_override.rb index 5d2739c1f58..3d840cd3251 100644 --- a/config/initializers/http_hostname_override.rb +++ b/config/initializers/http_hostname_override.rb @@ -34,6 +34,7 @@ end class Net::HTTP attr_accessor :hostname_override + SSL_IVNAMES << :@hostname_override SSL_ATTRIBUTES << :hostname_override diff --git a/config/metrics/settings/20220222181654_certificate_based_clusters_ff.yml b/config/metrics/settings/20220222181654_certificate_based_clusters_ff.yml new file mode 100644 index 00000000000..6e17601e76d --- /dev/null +++ b/config/metrics/settings/20220222181654_certificate_based_clusters_ff.yml @@ -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 diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile deleted file mode 100644 index 83c6f68869b..00000000000 --- a/danger/changelog/Dangerfile +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -changelog.check! diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile index b4e06c21fe4..0128f0fa195 100644 --- a/danger/database/Dangerfile +++ b/danger/database/Dangerfile @@ -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 diff --git a/danger/feature_flag/Dangerfile b/danger/feature_flag/Dangerfile index d6c1c53cddc..5fe9d42a7a1 100644 --- a/danger/feature_flag/Dangerfile +++ b/danger/feature_flag/Dangerfile @@ -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 diff --git a/danger/plugins/changelog.rb b/danger/plugins/changelog.rb deleted file mode 100644 index 02ff405c410..00000000000 --- a/danger/plugins/changelog.rb +++ /dev/null @@ -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 diff --git a/danger/product_intelligence/Dangerfile b/danger/product_intelligence/Dangerfile index 01a2f9b6feb..77d714a7f60 100644 --- a/danger/product_intelligence/Dangerfile +++ b/danger/product_intelligence/Dangerfile @@ -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? diff --git a/danger/specialization_labels/Dangerfile b/danger/specialization_labels/Dangerfile index cb4c8c96f4f..7d1c83697fd 100644 --- a/danger/specialization_labels/Dangerfile +++ b/danger/specialization_labels/Dangerfile @@ -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? diff --git a/danger/z_metadata/Dangerfile b/danger/z_metadata/Dangerfile index 0a70554486f..546fdc8de5f 100644 --- a/danger/z_metadata/Dangerfile +++ b/danger/z_metadata/Dangerfile @@ -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 diff --git a/db/post_migrate/20220224204415_recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features.rb b/db/post_migrate/20220224204415_recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features.rb new file mode 100644 index 00000000000..feb0f2c83ab --- /dev/null +++ b/db/post_migrate/20220224204415_recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features.rb @@ -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 diff --git a/db/schema_migrations/20220224204415 b/db/schema_migrations/20220224204415 new file mode 100644 index 00000000000..e0faa994b54 --- /dev/null +++ b/db/schema_migrations/20220224204415 @@ -0,0 +1 @@ +1d7105559c8d2da1d86c5625c592edc792d7cd729b8c86c7a2b950c3dd98e975
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index d402eeb51e2..613fe897401 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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); diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md index 3587016ac6b..f14f1322184 100644 --- a/doc/api/system_hooks.md +++ b/doc/api/system_hooks.md @@ -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. diff --git a/doc/development/dangerbot.md b/doc/development/dangerbot.md index 8da1f5700e5..e2ff56351a7 100644 --- a/doc/development/dangerbot.md +++ b/doc/development/dangerbot.md @@ -121,12 +121,13 @@ to revert the change before merging! #### Adding labels via Danger NOTE: -This is currently applicable to the [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) -project only. +This is applicable to all the projects that use the [`gitlab-dangerfiles` gem](https://rubygems.org/gems/gitlab-dangerfiles). Danger is often used to improve MR hygiene by adding labels. Instead of calling the -API directly in your `Dangerfile`, add the labels to the `project_helper.labels_to_add` array. -The main `Dangerfile` will then take care of adding the labels to the MR with a single API call. +API directly in your `Dangerfile`, add the labels to `helper.labels_to_add` set (with `helper.labels_to_add << label` +or `helper.labels_to_add.merge(array_of_labels)`. +`gitlab-dangerfiles` will then take care of adding the labels to the MR with a single API call after all the rules +have had the chance to add to `helper.labels_to_add`. #### Shared rules and plugins @@ -135,11 +136,30 @@ upstreaming them to the [`gitlab-dangerfiles`](https://gitlab.com/gitlab-org/rub #### Enable Danger on a project -To enable the Dangerfile on another existing GitLab project, run the following -extra steps: +To enable the Dangerfile on another existing GitLab project, complete the following steps: -1. Create a [Project access tokens](../user/project/settings/project_access_tokens.md). -1. Add the token as a CI/CD project variable named `DANGER_GITLAB_API_TOKEN`. +1. Add [`gitlab-dangerfiles`](https://rubygems.org/gems/gitlab-dangerfiles) to your `Gemfile`. +1. Create a `Dangerfile` with the following content: + + ```ruby + require_relative "lib/gitlab-dangerfiles" + + Gitlab::Dangerfiles.for_project(self, &:import_defaults) + ``` + +1. Add the following to your CI/CD configuration: + + ```yaml + include: + - project: 'gitlab-org/quality/pipeline-common' + file: + - '/ci/danger-review.yml' + ``` + +1. If your project is in the `gitlab-org` group, you don't need to set up any token as the `DANGER_GITLAB_API_TOKEN` + variable is available at the group level. If not, follow these last steps: + 1. Create a [Project access tokens](../user/project/settings/project_access_tokens.md). + 1. Add the token as a CI/CD project variable named `DANGER_GITLAB_API_TOKEN`. You should add the ~"Danger bot" label to the merge request before sending it for review. diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md index 3058602479e..be9b502a58b 100644 --- a/doc/user/group/value_stream_analytics/index.md +++ b/doc/user/group/value_stream_analytics/index.md @@ -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. diff --git a/lefthook.yml b/lefthook.yml index f2b02045368..62bbb440b29 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -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 diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index e4133713c1f..7c91fbd36d9 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -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 diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb index ecda96af403..7adaaef86e4 100644 --- a/lib/gitlab/auth/auth_finders.rb +++ b/lib/gitlab/auth/auth_finders.rb @@ -12,6 +12,7 @@ module Gitlab class InsufficientScopeError < AuthenticationError attr_reader :scopes + def initialize(scopes) @scopes = scopes.map { |s| s.try(:name) || s } end diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb index 2ec75669d24..a45778159c7 100644 --- a/lib/gitlab/auth/o_auth/auth_hash.rb +++ b/lib/gitlab/auth/o_auth/auth_hash.rb @@ -7,6 +7,7 @@ module Gitlab module OAuth class AuthHash attr_reader :auth_hash + def initialize(auth_hash) @auth_hash = auth_hash end diff --git a/lib/gitlab/checks/base_bulk_checker.rb b/lib/gitlab/checks/base_bulk_checker.rb index 46a68fdf485..e2a016a9907 100644 --- a/lib/gitlab/checks/base_bulk_checker.rb +++ b/lib/gitlab/checks/base_bulk_checker.rb @@ -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) diff --git a/lib/gitlab/checks/base_single_checker.rb b/lib/gitlab/checks/base_single_checker.rb index 06519833d7c..435f4ccf5ba 100644 --- a/lib/gitlab/checks/base_single_checker.rb +++ b/lib/gitlab/checks/base_single_checker.rb @@ -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) diff --git a/lib/gitlab/ci/pipeline/logger.rb b/lib/gitlab/ci/pipeline/logger.rb index 10c0fe295f8..ee6c3898592 100644 --- a/lib/gitlab/ci/pipeline/logger.rb +++ b/lib/gitlab/ci/pipeline/logger.rb @@ -94,6 +94,7 @@ module Gitlab private attr_reader :project, :destination, :started_at, :log_conditions + delegate :current_monotonic_time, to: :class def age diff --git a/lib/gitlab/ci/trace/remote_checksum.rb b/lib/gitlab/ci/trace/remote_checksum.rb index 7f43d91e6d7..eaa9be9dd15 100644 --- a/lib/gitlab/ci/trace/remote_checksum.rb +++ b/lib/gitlab/ci/trace/remote_checksum.rb @@ -23,6 +23,7 @@ module Gitlab private attr_reader :trace_artifact + delegate :aws?, :google?, to: :object_store_config, prefix: :provider def fetch_md5_checksum diff --git a/lib/gitlab/ci/variables/builder.rb b/lib/gitlab/ci/variables/builder.rb index ec2ec3396ea..a51abe9f8de 100644 --- a/lib/gitlab/ci/variables/builder.rb +++ b/lib/gitlab/ci/variables/builder.rb @@ -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) diff --git a/lib/gitlab/database/count/exact_count_strategy.rb b/lib/gitlab/database/count/exact_count_strategy.rb index 0b8fe640bf8..345c7e44b05 100644 --- a/lib/gitlab/database/count/exact_count_strategy.rb +++ b/lib/gitlab/database/count/exact_count_strategy.rb @@ -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 diff --git a/lib/gitlab/database/count/reltuples_count_strategy.rb b/lib/gitlab/database/count/reltuples_count_strategy.rb index f60ead26b03..0f89c500688 100644 --- a/lib/gitlab/database/count/reltuples_count_strategy.rb +++ b/lib/gitlab/database/count/reltuples_count_strategy.rb @@ -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 diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb index ab414f91169..3ee9a193b45 100644 --- a/lib/gitlab/database/partitioning/partition_manager.rb +++ b/lib/gitlab/database/partitioning/partition_manager.rb @@ -46,6 +46,7 @@ module Gitlab private attr_reader :model + delegate :connection, to: :model def missing_partitions diff --git a/lib/gitlab/database/partitioning/replace_table.rb b/lib/gitlab/database/partitioning/replace_table.rb index a7686e97553..21a175a660d 100644 --- a/lib/gitlab/database/partitioning/replace_table.rb +++ b/lib/gitlab/database/partitioning/replace_table.rb @@ -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) diff --git a/lib/gitlab/email/html_parser.rb b/lib/gitlab/email/html_parser.rb index 77f299bcade..27ba5d2a314 100644 --- a/lib/gitlab/email/html_parser.rb +++ b/lib/gitlab/email/html_parser.rb @@ -8,6 +8,7 @@ module Gitlab end attr_reader :raw_body + def initialize(raw_body) @raw_body = raw_body end diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb index a5b1b7d914b..5669a65cbd9 100644 --- a/lib/gitlab/git/blame.rb +++ b/lib/gitlab/git/blame.rb @@ -63,6 +63,7 @@ module Gitlab class BlameLine attr_accessor :lineno, :oldlineno, :commit, :line + def initialize(lineno, oldlineno, commit, line) @lineno = lineno @oldlineno = oldlineno diff --git a/lib/gitlab/graphql/batch_key.rb b/lib/gitlab/graphql/batch_key.rb index 51203af5a43..553e0573c63 100644 --- a/lib/gitlab/graphql/batch_key.rb +++ b/lib/gitlab/graphql/batch_key.rb @@ -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) diff --git a/lib/gitlab/insecure_key_fingerprint.rb b/lib/gitlab/insecure_key_fingerprint.rb index 7b1cf5e7931..ef342f3819f 100644 --- a/lib/gitlab/insecure_key_fingerprint.rb +++ b/lib/gitlab/insecure_key_fingerprint.rb @@ -10,6 +10,7 @@ module Gitlab # class InsecureKeyFingerprint attr_accessor :key + alias_attribute :fingerprint_md5, :fingerprint # diff --git a/lib/gitlab/json_cache.rb b/lib/gitlab/json_cache.rb index 41c18f82a4b..d2916a01809 100644 --- a/lib/gitlab/json_cache.rb +++ b/lib/gitlab/json_cache.rb @@ -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] - end + 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) diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb index 99a3145104a..e76cab688cc 100644 --- a/lib/gitlab/pagination/gitaly_keyset_pager.rb +++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb @@ -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) diff --git a/lib/gitlab/pagination/keyset/cursor_based_request_context.rb b/lib/gitlab/pagination/keyset/cursor_based_request_context.rb index 18390f5b59d..e06d7e48ca3 100644 --- a/lib/gitlab/pagination/keyset/cursor_based_request_context.rb +++ b/lib/gitlab/pagination/keyset/cursor_based_request_context.rb @@ -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) diff --git a/lib/gitlab/pagination/keyset/header_builder.rb b/lib/gitlab/pagination/keyset/header_builder.rb index 888d93d5fe3..1036916e665 100644 --- a/lib/gitlab/pagination/keyset/header_builder.rb +++ b/lib/gitlab/pagination/keyset/header_builder.rb @@ -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) diff --git a/lib/gitlab/pagination/offset_pagination.rb b/lib/gitlab/pagination/offset_pagination.rb index 4f8a6ffb2cc..8cb959769ee 100644 --- a/lib/gitlab/pagination/offset_pagination.rb +++ b/lib/gitlab/pagination/offset_pagination.rb @@ -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) diff --git a/lib/gitlab/prometheus/queries/base_query.rb b/lib/gitlab/prometheus/queries/base_query.rb index 9ff414d5236..eabac6128b5 100644 --- a/lib/gitlab/prometheus/queries/base_query.rb +++ b/lib/gitlab/prometheus/queries/base_query.rb @@ -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) diff --git a/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric.rb b/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric.rb new file mode 100644 index 00000000000..6df6fef5d3a --- /dev/null +++ b/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric.rb @@ -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 diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 1250d3fd562..951ec5ea5c3 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -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 diff --git a/lib/sidebars/menu.rb b/lib/sidebars/menu.rb index 1af3d024291..d9d294ff982 100644 --- a/lib/sidebars/menu.rb +++ b/lib/sidebars/menu.rb @@ -15,6 +15,7 @@ module Sidebars include ::Sidebars::Concerns::HasPartial attr_reader :context + delegate :current_user, :container, to: :@context def initialize(context) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 874b9e4ac90..7000ea65eaf 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12783,6 +12783,9 @@ msgstr "" msgid "Direct member" msgstr "" +msgid "Direct members" +msgstr "" + msgid "Direct non-authenticated users to this page." msgstr "" diff --git a/qa/qa/git/location.rb b/qa/qa/git/location.rb index 032c6837db1..c3733572e70 100644 --- a/qa/qa/git/location.rb +++ b/qa/qa/git/location.rb @@ -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 diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_repository_spec.rb index b4ebb9dd475..ed038c36324 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_repository_spec.rb @@ -13,125 +13,174 @@ module QA let(:package_version) { '1.3.7' } let(:package_type) { 'maven' } - where(:authentication_token_type, :maven_header_name) do - :personal_access_token | 'Private-Token' - :ci_job_token | 'Job-Token' - :project_deploy_token | 'Deploy-Token' - end + context 'via maven' do + where do + { + 'using a personal access token' => { + authentication_token_type: :personal_access_token, + maven_header_name: 'Private-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347582' + }, + 'using a project deploy token' => { + authentication_token_type: :project_deploy_token, + maven_header_name: 'Deploy-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347585' + }, + 'using a ci job token' => { + authentication_token_type: :ci_job_token, + maven_header_name: 'Job-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579' + } + } + end - with_them do - let(:token) do - case authentication_token_type - when :personal_access_token - personal_access_token - when :ci_job_token - '${env.CI_JOB_TOKEN}' - when :project_deploy_token - project_deploy_token.token + with_them do + let(:token) do + case authentication_token_type + when :personal_access_token + personal_access_token + when :ci_job_token + '${env.CI_JOB_TOKEN}' + when :project_deploy_token + project_deploy_token.token + end end - end - it "pushes and pulls a maven package via maven using #{params[:authentication_token_type]}" do - Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do - Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + it 'pushes and pulls a maven package', testcase: params[:testcase] do + Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do + Resource::Repository::Commit.fabricate_via_api! do |commit| + maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) + package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) - commit.project = package_project - commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.project = package_project + commit.commit_message = 'Add files' + commit.add_files([ + { + file_path: '.gitlab-ci.yml', + content: maven_upload_package_yaml + }, + { + file_path: 'pom.xml', + content: package_pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml + } + ]) + end end - end - package_project.visit! + package_project.visit! - Flow::Pipeline.visit_latest_pipeline + Flow::Pipeline.visit_latest_pipeline - Page::Project::Pipeline::Show.perform do |pipeline| - pipeline.click_job('deploy') - end + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('deploy') + end - Page::Project::Job::Show.perform do |job| - expect(job).to be_successful(timeout: 800) - end + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end - Page::Project::Menu.perform(&:click_packages_link) + Page::Project::Menu.perform(&:click_packages_link) - Page::Project::Packages::Index.perform do |index| - expect(index).to have_package(package_name) + Page::Project::Packages::Index.perform do |index| + expect(index).to have_package(package_name) - index.click_package(package_name) - end + index.click_package(package_name) + end - Page::Project::Packages::Show.perform do |show| - expect(show).to have_package_info(package_name, package_version) - end + Page::Project::Packages::Show.perform do |show| + expect(show).to have_package_info(package_name, package_version) + end - Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do - Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_install_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_install_package.yaml.erb')).result(binding) - client_pom_xml = ERB.new(read_fixture('package_managers/maven', 'client_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do + Resource::Repository::Commit.fabricate_via_api! do |commit| + maven_install_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_install_package.yaml.erb')).result(binding) + client_pom_xml = ERB.new(read_fixture('package_managers/maven', 'client_pom.xml.erb')).result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) - commit.project = client_project - commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_install_package_yaml - }, - { - file_path: 'pom.xml', - content: client_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.project = client_project + commit.commit_message = 'Add files' + commit.add_files([ + { + file_path: '.gitlab-ci.yml', + content: maven_install_package_yaml + }, + { + file_path: 'pom.xml', + content: client_pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml + } + ]) + end end - end - client_project.visit! + client_project.visit! - Flow::Pipeline.visit_latest_pipeline + Flow::Pipeline.visit_latest_pipeline - Page::Project::Pipeline::Show.perform do |pipeline| - pipeline.click_job('install') - end + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('install') + end - Page::Project::Job::Show.perform do |job| - expect(job).to be_successful(timeout: 800) + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end end end + end - context 'duplication setting' do - before do - package_project.group.visit! + context 'duplication setting' do + before do + package_project.group.visit! - Page::Group::Menu.perform(&:go_to_package_settings) + Page::Group::Menu.perform(&:go_to_package_settings) + 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 - context 'when disabled' do + 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 - context 'when enabled' do before do Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled) end - it "allows users to publish group level Maven packages duplicates using #{params[:authentication_token_type]}" do + it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do create_duplicated_package push_duplicated_package @@ -163,68 +245,68 @@ module QA end end end + end - def create_duplicated_package - settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven', 'settings_with_pat.xml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) - - with_fixtures([ - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml_with_pat - } - ]) do |dir| - Service::DockerRun::Maven.new(dir).publish! - end + def create_duplicated_package + settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven', 'settings_with_pat.xml.erb')).result(binding) + package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) + + with_fixtures([ + { + file_path: 'pom.xml', + content: package_pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml_with_pat + } + ]) do |dir| + Service::DockerRun::Maven.new(dir).publish! + end - package_project.visit! + package_project.visit! - Page::Project::Menu.perform(&:click_packages_link) + Page::Project::Menu.perform(&:click_packages_link) - Page::Project::Packages::Index.perform do |index| - expect(index).to have_package(package_name) - end + Page::Project::Packages::Index.perform do |index| + expect(index).to have_package(package_name) end + end - def push_duplicated_package - Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do - Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + def push_duplicated_package + Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do + Resource::Repository::Commit.fabricate_via_api! do |commit| + maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) + package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) - commit.project = client_project - commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) - end + commit.project = client_project + commit.commit_message = 'Add .gitlab-ci.yml' + commit.add_files([ + { + file_path: '.gitlab-ci.yml', + content: maven_upload_package_yaml + }, + { + file_path: 'pom.xml', + content: package_pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml + } + ]) end end + end - def show_latest_deploy_job - client_project.visit! + def show_latest_deploy_job + client_project.visit! - Flow::Pipeline.visit_latest_pipeline + Flow::Pipeline.visit_latest_pipeline - Page::Project::Pipeline::Show.perform do |pipeline| - pipeline.click_job('deploy') - end + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('deploy') end end end diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb index a861c13a44c..026b0ddcd04 100644 --- a/qa/qa/specs/runner.rb +++ b/qa/qa/specs/runner.rb @@ -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 diff --git a/scripts/generate-memory-metrics-on-boot b/scripts/generate-memory-metrics-on-boot index 945661aa057..539446f7c0c 100755 --- a/scripts/generate-memory-metrics-on-boot +++ b/scripts/generate-memory-metrics-on-boot @@ -1,12 +1,29 @@ #!/usr/bin/env ruby # frozen_string_literal: true -abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1 -memory_bundle_mem_file_name = ARGV.first +abort "usage: #{__FILE__} <memory_bundle_mem_file_name_prefix> <test_count>" unless ARGV.length == 2 +memory_bundle_mem_file_name_prefix = ARGV.first +test_count = ARGV.last.to_i -full_report = File.open(memory_bundle_mem_file_name).read +results = [] +(1..test_count).each do |i| + report_filename = "#{memory_bundle_mem_file_name_prefix}#{i}.txt" -stats = /TOP: (?<total_mibs_str>.*) MiB/.match(full_report) -abort 'failed to process the benchmark output' unless stats + stats = nil + File.foreach(report_filename).detect do |line| + stats = /TOP: (?<total_mibs_str>.*) MiB/.match(line) + end + abort 'failed to process the benchmark output' unless stats -puts "total_memory_used_by_dependencies_on_boot_prod_env_mb #{stats[:total_mibs_str].to_f.round(1)}" + total_mibs = stats[:total_mibs_str].to_f + results << total_mibs +end + +res = results.sort +median = (res[(test_count - 1) / 2] + res[test_count / 2]) / 2.0 + +METRIC_NAME = "total_memory_used_by_dependencies_on_boot_prod_env_mb" + +puts "# TYPE #{METRIC_NAME} gauge" +puts "# UNIT #{METRIC_NAME} mebibytes" +puts "#{METRIC_NAME} #{median.round(1)}" diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 004bea02580..d49d4947cd5 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -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 diff --git a/spec/fixtures/api/schemas/public_api/v4/system_hook.json b/spec/fixtures/api/schemas/public_api/v4/system_hook.json new file mode 100644 index 00000000000..f992bc8b809 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/system_hook.json @@ -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 +} diff --git a/spec/fixtures/api/schemas/public_api/v4/system_hooks.json b/spec/fixtures/api/schemas/public_api/v4/system_hooks.json new file mode 100644 index 00000000000..a56542a8b99 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/system_hooks.json @@ -0,0 +1,9 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "$ref": "./system_hook.json" + } + } +} diff --git a/spec/frontend/loading_icon_for_legacy_js_spec.js b/spec/frontend/loading_icon_for_legacy_js_spec.js new file mode 100644 index 00000000000..46deee555ba --- /dev/null +++ b/spec/frontend/loading_icon_for_legacy_js_spec.js @@ -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); + }); +}); diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb index 7899d01b475..01c2120d9a6 100644 --- a/spec/lib/gitlab/json_cache_spec.rb +++ b/spec/lib/gitlab/json_cache_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::JsonCache do let(:backend) { double('backend').as_null_object } let(:namespace) { 'geo' } let(:key) { 'foo' } - let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}" } + let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab.revision}" } subject(:cache) { described_class.new(namespace: namespace, backend: backend) } @@ -35,69 +35,63 @@ RSpec.describe Gitlab::JsonCache do end describe '#cache_key' do - context 'when namespace is not defined' do - context 'when cache_key_with_version is true' do - it 'expands out the key with GitLab, and Rails versions' do - cache = described_class.new(cache_key_with_version: true) + using RSpec::Parameterized::TableSyntax - cache_key = cache.cache_key(key) - - expect(cache_key).to eq("#{key}:#{Gitlab::VERSION}:#{Rails.version}") - end - end + 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) - end - end + it { is_expected.to eq expanded_key } 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) + context 'when cache_key_strategy is unknown' do + let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: 'unknown') } - expect(cache_key).to eq("#{key}:#{Gitlab::VERSION}:#{Rails.version}") - end + it 'raises KeyError' do + expect { cache.cache_key('key') }.to raise_error(KeyError) end + end + end - context 'when cache_key_with_version is false' do - it 'returns the key' do - cache = described_class.new(namespace: nil, cache_key_with_version: false) + describe '#namespace' do + it 'defaults to nil' do + cache = described_class.new + expect(cache.namespace).to be_nil + end + end - cache_key = cache.cache_key(key) + describe '#strategy_key_component' do + subject { cache.strategy_key_component } - expect(cache_key).to eq(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) + it { is_expected.to eq Gitlab.revision } + end - expect(cache_key).to eq("#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}") - end - end + context 'when cache_key_strategy is :version' do + let(:cache) { described_class.new(cache_key_strategy: :version) } - 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) + it { is_expected.to eq [Gitlab::VERSION, Rails.version] } + end - cache_key = cache.cache_key(key) + context 'when cache_key_strategy is invalid' do + let(:cache) { described_class.new(cache_key_strategy: 'unknown') } - expect(cache_key).to eq("#{namespace}:#{key}") - end + it 'raises KeyError' do + expect { subject }.to raise_error(KeyError) end end end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric_spec.rb new file mode 100644 index 00000000000..09cc6ae71d4 --- /dev/null +++ b/spec/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric_spec.rb @@ -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 diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 868c667e69d..958df7baf72 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -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) diff --git a/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb new file mode 100644 index 00000000000..8ec51d86779 --- /dev/null +++ b/spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb @@ -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 diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index d981189c6f1..17f3b2e5072 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -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 diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index 3c095477ea9..9daea3438cb 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Mentionable do include Mentionable attr_accessor :project, :message + attr_mentionable :message def author diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 1511872d183..d94b70ec0f9 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -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 diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb index f19f8c12928..6ff6dbb7800 100644 --- a/spec/support/helpers/sorting_helper.rb +++ b/spec/support/helpers/sorting_helper.rb @@ -26,6 +26,7 @@ module SortingHelper include Comparable attr_reader :value + delegate :==, :eql?, :hash, to: :value def initialize(value) diff --git a/spec/tooling/danger/changelog_spec.rb b/spec/tooling/danger/changelog_spec.rb deleted file mode 100644 index 377c3e881c9..00000000000 --- a/spec/tooling/danger/changelog_spec.rb +++ /dev/null @@ -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 diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb index eaafb13b61a..03f5836beab 100644 --- a/spec/tooling/danger/project_helper_spec.rb +++ b/spec/tooling/danger/project_helper_spec.rb @@ -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 } diff --git a/spec/validators/array_members_validator_spec.rb b/spec/validators/array_members_validator_spec.rb index c6960925487..0e3ca4b5b7b 100644 --- a/spec/validators/array_members_validator_spec.rb +++ b/spec/validators/array_members_validator_spec.rb @@ -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 diff --git a/spec/validators/color_validator_spec.rb b/spec/validators/color_validator_spec.rb index bd77b3df182..27acbe5e89d 100644 --- a/spec/validators/color_validator_spec.rb +++ b/spec/validators/color_validator_spec.rb @@ -10,6 +10,7 @@ RSpec.describe ColorValidator do include ActiveModel::Model include ActiveModel::Validations attr_accessor :color + validates :color, color: true end.new end diff --git a/spec/validators/cron_validator_spec.rb b/spec/validators/cron_validator_spec.rb index dff3b506b89..bd7fe242957 100644 --- a/spec/validators/cron_validator_spec.rb +++ b/spec/validators/cron_validator_spec.rb @@ -8,6 +8,7 @@ RSpec.describe CronValidator do include ActiveModel::Model include ActiveModel::Validations attr_accessor :cron + validates :cron, cron: true def cron_timezone @@ -34,6 +35,7 @@ RSpec.describe CronValidator do include ActiveModel::Model include ActiveModel::Validations attr_accessor :cron_partytime + validates :cron_partytime, cron: true end.new end diff --git a/spec/validators/future_date_validator_spec.rb b/spec/validators/future_date_validator_spec.rb index 6814ba7c820..7af3d473bd9 100644 --- a/spec/validators/future_date_validator_spec.rb +++ b/spec/validators/future_date_validator_spec.rb @@ -8,6 +8,7 @@ RSpec.describe FutureDateValidator do include ActiveModel::Model include ActiveModel::Validations attr_accessor :expires_at + validates :expires_at, future_date: true end.new end diff --git a/tooling/danger/changelog.rb b/tooling/danger/changelog.rb deleted file mode 100644 index 6a392afac13..00000000000 --- a/tooling/danger/changelog.rb +++ /dev/null @@ -1,232 +0,0 @@ -# frozen_string_literal: true - -require 'gitlab/dangerfiles/title_linting' - -module Tooling - module Danger - module Changelog - NO_CHANGELOG_LABELS = [ - 'type::tooling', - 'tooling::pipelines', - 'tooling::workflow', - 'ci-build', - 'meta' - ].freeze - NO_CHANGELOG_CATEGORIES = %i[docs none].freeze - CHANGELOG_TRAILER_REGEX = /^(?<name>Changelog):\s*(?<category>.+)$/i.freeze - CHANGELOG_EE_TRAILER_REGEX = /^EE: true$/.freeze - CHANGELOG_MODIFIED_URL_TEXT = "**CHANGELOG.md was edited.** Please remove the additions and follow the [changelog guidelines](https://docs.gitlab.com/ee/development/changelog.html).\n\n" - CHANGELOG_MISSING_URL_TEXT = "**[CHANGELOG missing](https://docs.gitlab.com/ee/development/changelog.html)**:\n\n" - - OPTIONAL_CHANGELOG_MESSAGE = { - local: "If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.", - ci: <<~MSG - If you want to create a changelog entry for GitLab FOSS, add the `Changelog` trailer to the commit message you want to add to the changelog. - - If you want to create a changelog entry for GitLab EE, also [add the `EE: true` trailer](https://docs.gitlab.com/ee/development/changelog.html#gitlab-enterprise-changes) to your commit message. - - If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message. - MSG - }.freeze - SEE_DOC = "See the [changelog documentation](https://docs.gitlab.com/ee/development/changelog.html)." - - REQUIRED_CHANGELOG_REASONS = { - db_changes: 'introduces a database migration', - feature_flag_removed: 'removes a feature flag' - }.freeze - REQUIRED_CHANGELOG_MESSAGE = { - local: "This merge request requires a changelog entry because it [%<reason>s](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry).", - ci: <<~MSG - To create a changelog entry, add the `Changelog` trailer to one of your Git commit messages. - - This merge request requires a changelog entry because it [%<reason>s](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry). - MSG - }.freeze - - CATEGORIES = YAML - .load_file(File.expand_path('../../.gitlab/changelog_config.yml', __dir__)) - .fetch('categories') - .keys - .freeze - - class ChangelogCheckResult - attr_reader :errors, :warnings, :markdowns, :messages - - def initialize(errors: [], warnings: [], markdowns: [], messages: []) - @errors = errors - @warnings = warnings - @markdowns = markdowns - @messages = messages - end - private_class_method :new - - def self.empty - new - end - - def self.error(error) - new(errors: [error]) - end - - def self.warning(warning) - new(warnings: [warning]) - end - - def error(error) - errors << error - end - - def warning(warning) - warnings << warning - end - - def markdown(markdown) - markdowns << markdown - end - - def message(message) - messages << message - end - end - - # rubocop:disable Style/SignalException - def check! - if git.modified_files.include?("CHANGELOG.md") - fail modified_text - end - - if present? - add_danger_messages(check_changelog_path) - elsif required? - required_texts.each { |_, text| fail(text) } # rubocop:disable Lint/UnreachableLoop - elsif optional? - message optional_text - end - - check_changelog_commit_categories - end - # rubocop:enable Style/SignalException - - # rubocop:disable Style/SignalException - def add_danger_messages(check_result) - check_result.errors.each { |error| fail(error) } # rubocop:disable Lint/UnreachableLoop - check_result.warnings.each { |warning| warn(warning) } - check_result.markdowns.each { |markdown_hash| markdown(**markdown_hash) } - check_result.messages.each { |text| message(text) } - end - # rubocop:enable Style/SignalException - - def check_changelog_commit_categories - changelog_commits.each do |commit| - add_danger_messages(check_changelog_trailer(commit)) - end - end - - def check_changelog_trailer(commit) - trailer = commit.message.match(CHANGELOG_TRAILER_REGEX) - name = trailer[:name] - category = trailer[:category] - - unless name == 'Changelog' - return ChangelogCheckResult.error("The changelog trailer for commit #{commit.sha} must be `Changelog` (starting with a capital C), not `#{name}`") - end - - return ChangelogCheckResult.empty if CATEGORIES.include?(category) - - ChangelogCheckResult.error("Commit #{commit.sha} uses an invalid changelog category: #{category}") - end - - def check_changelog_path - check_result = ChangelogCheckResult.empty - return check_result unless present? - - ee_changes = project_helper.all_ee_changes.dup - - if ee_changes.any? && !ee_changelog? && !required? - check_result.warning("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 - - if ee_changes.empty? && ee_changelog? - check_result.warning("This MR has a Changelog commit for EE, but no code changes in `ee/`. Consider removing the `EE: true` trailer from your commits.") - end - - if ee_changes.any? && ee_changelog? && required_reasons.include?(:db_changes) - check_result.warning("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 - - check_result - end - - def required_reasons - [].tap do |reasons| - reasons << :db_changes if helper.changes.added.has_category?(:migration) - reasons << :feature_flag_removed if helper.changes.deleted.has_category?(:feature_flag) - end - end - - def required? - required_reasons.any? - end - - def optional? - categories_need_changelog? && mr_without_no_changelog_label? - end - - def present? - valid_changelog_commits.any? - end - - def changelog_commits - git.commits.select do |commit| - commit.message.match?(CHANGELOG_TRAILER_REGEX) - end - end - - def valid_changelog_commits - changelog_commits.select do |commit| - trailer = commit.message.match(CHANGELOG_TRAILER_REGEX) - - CATEGORIES.include?(trailer[:category]) - end - end - - def ee_changelog? - changelog_commits.any? do |commit| - commit.message.match?(CHANGELOG_EE_TRAILER_REGEX) - end - end - - def modified_text - CHANGELOG_MODIFIED_URL_TEXT + - (helper.ci? ? format(OPTIONAL_CHANGELOG_MESSAGE[:ci]) : OPTIONAL_CHANGELOG_MESSAGE[:local]) - end - - def required_texts - required_reasons.each_with_object({}) do |required_reason, memo| - memo[required_reason] = - CHANGELOG_MISSING_URL_TEXT + - (helper.ci? ? format(REQUIRED_CHANGELOG_MESSAGE[:ci], reason: REQUIRED_CHANGELOG_REASONS.fetch(required_reason)) : REQUIRED_CHANGELOG_MESSAGE[:local]) - end - end - - def optional_text - CHANGELOG_MISSING_URL_TEXT + - (helper.ci? ? format(OPTIONAL_CHANGELOG_MESSAGE[:ci]) : OPTIONAL_CHANGELOG_MESSAGE[:local]) - end - - private - - def read_file(path) - File.read(path) - end - - def categories_need_changelog? - (helper.changes.categories - NO_CHANGELOG_CATEGORIES).any? - end - - def mr_without_no_changelog_label? - (helper.mr_labels & NO_CHANGELOG_LABELS).empty? - end - end - end -end diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb index a597ef2b047..4f056325f40 100644 --- a/tooling/danger/project_helper.rb +++ b/tooling/danger/project_helper.rb @@ -4,7 +4,6 @@ module Tooling module Danger module ProjectHelper LOCAL_RULES ||= %w[ - changelog ci_config database documentation @@ -75,7 +74,9 @@ module Tooling spec/frontend/tracking/.*\.js | spec/frontend/tracking_spec\.js )\z}x => [:frontend, :product_intelligence], - %r{\A((ee|jh)/)?app/(assets|views)/} => :frontend, + %r{\A((ee|jh)/)?app/assets/} => :frontend, + %r{\A((ee|jh)/)?app/views/.*\.svg} => :frontend, + %r{\A((ee|jh)/)?app/views/} => [:frontend, :backend], %r{\A((ee|jh)/)?public/} => :frontend, %r{\A((ee|jh)/)?spec/(javascripts|frontend|frontend_integration)/} => :frontend, %r{\A((ee|jh)/)?vendor/assets/} => :frontend, @@ -194,18 +195,10 @@ module Tooling helper.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES end - def all_ee_changes - helper.changes.files.grep(%r{\Aee/}) - end - def file_lines(filename) read_file(filename).lines(chomp: true) end - def labels_to_add - @labels_to_add ||= [] - end - private def read_file(filename) |