From 91d37b971433b16c25ec501429ac643f9c49839e Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 19 Jul 2022 00:09:42 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../javascripts/header_search/constants.js | 2 + app/assets/javascripts/header_search/init.js | 25 +++-- .../ci/runner_upgrade_status_type_enum.rb | 9 +- app/models/ci/runner_version.rb | 2 +- app/models/repository.rb | 4 +- .../repositories/changelog_service.rb | 7 +- app/workers/concerns/waitable_worker.rb | 6 +- ...ations_refresh_only_for_single_element.yml | 8 -- doc/api/repositories.md | 4 +- lib/api/repositories.rb | 8 ++ lib/gitlab/changelog/config.rb | 9 +- .../importer/events/cross_referenced.rb | 86 +++++++++++++++++ .../importer/issue_event_importer.rb | 3 + .../github_import/importer/note_importer.rb | 11 ++- .../single_endpoint_issue_events_importer.rb | 8 ++ lib/gitlab/github_import/markdown_text.rb | 30 +++++- .../representation/issue_event.rb | 4 +- spec/frontend/header_search/init_spec.js | 13 +-- .../runner_upgrade_status_type_enum_spec.rb | 14 ++- spec/lib/gitlab/changelog/config_spec.rb | 12 +++ .../importer/events/cross_referenced_spec.rb | 96 +++++++++++++++++++ .../importer/issue_event_importer_spec.rb | 7 ++ .../github_import/markdown_text_spec.rb | 48 ++++++++++ .../representation/issue_event_spec.rb | 8 +- spec/models/repository_spec.rb | 27 ++++++ spec/requests/api/repositories_spec.rb | 74 ++++++++++++++ .../repositories/changelog_service_spec.rb | 19 ++++ spec/workers/concerns/waitable_worker_spec.rb | 19 ---- 28 files changed, 494 insertions(+), 69 deletions(-) delete mode 100644 config/feature_flags/development/inline_project_authorizations_refresh_only_for_single_element.yml create mode 100644 lib/gitlab/github_import/importer/events/cross_referenced.rb create mode 100644 spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js index c9b05c3deb5..a026386b2bd 100644 --- a/app/assets/javascripts/header_search/constants.js +++ b/app/assets/javascripts/header_search/constants.js @@ -49,3 +49,5 @@ export const SEARCH_RESULTS_DESCRIPTION = 'search-results-description'; export const SCOPE_TOKEN_MAX_LENGTH = 36; export const INPUT_FIELD_PADDING = 52; + +export const HEADER_INIT_EVENTS = ['input', 'focus']; diff --git a/app/assets/javascripts/header_search/init.js b/app/assets/javascripts/header_search/init.js index 2b9fac0b395..4e9404007ec 100644 --- a/app/assets/javascripts/header_search/init.js +++ b/app/assets/javascripts/header_search/init.js @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/browser'; +import { HEADER_INIT_EVENTS } from './constants'; async function eventHandler(callback = () => {}) { if (this.newHeaderSearchFeatureFlag) { @@ -28,21 +29,25 @@ async function eventHandler(callback = () => {}) { } function cleanEventListeners() { - document.querySelector('#search').removeEventListener('focus', eventHandler); + HEADER_INIT_EVENTS.forEach((eventType) => { + document.querySelector('#search').removeEventListener(eventType, eventHandler); + }); } function initHeaderSearch() { const searchInputBox = document.querySelector('#search'); - searchInputBox?.addEventListener( - 'focus', - eventHandler.bind( - { searchInputBox, newHeaderSearchFeatureFlag: gon?.features?.newHeaderSearch }, - cleanEventListeners, - ), - { once: true }, - ); + HEADER_INIT_EVENTS.forEach((eventType) => { + searchInputBox?.addEventListener( + eventType, + eventHandler.bind( + { searchInputBox, newHeaderSearchFeatureFlag: gon?.features?.newHeaderSearch }, + cleanEventListeners, + ), + { once: true }, + ); + }); } export default initHeaderSearch; -export { eventHandler }; +export { eventHandler, cleanEventListeners }; diff --git a/app/graphql/types/ci/runner_upgrade_status_type_enum.rb b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb index 7c52ab5562a..8e32eee5e6e 100644 --- a/app/graphql/types/ci/runner_upgrade_status_type_enum.rb +++ b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb @@ -6,9 +6,14 @@ module Types graphql_name 'CiRunnerUpgradeStatusType' ::Ci::RunnerVersion::STATUS_DESCRIPTIONS.each do |status, description| - status = :invalid if status == :invalid_version + status_name_src = + if status == :invalid_version + :invalid + else + status + end - value status.to_s.upcase, description: description, value: status + value status_name_src.to_s.upcase, description: description, value: status end end end diff --git a/app/models/ci/runner_version.rb b/app/models/ci/runner_version.rb index ea05b336a2b..6b2d0060c9b 100644 --- a/app/models/ci/runner_version.rb +++ b/app/models/ci/runner_version.rb @@ -7,7 +7,7 @@ module Ci enum_with_nil status: { not_processed: nil, - invalid_version: -1, # Named invalid_version to avoid clash with auto-generated `invalid?` ActiveRecord method + invalid_version: -1, unknown: 0, not_available: 1, available: 2, diff --git a/app/models/repository.rb b/app/models/repository.rb index 0135020e586..0da71d87457 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1049,8 +1049,8 @@ class Repository blob_data_at(sha, '.lfsconfig') end - def changelog_config(ref = 'HEAD') - blob_data_at(ref, Gitlab::Changelog::Config::FILE_PATH) + def changelog_config(ref, path) + blob_data_at(ref, path) end def fetch_ref(source_repository, source_ref:, target_ref:) diff --git a/app/services/repositories/changelog_service.rb b/app/services/repositories/changelog_service.rb index 7a78b323453..447d4d979a6 100644 --- a/app/services/repositories/changelog_service.rb +++ b/app/services/repositories/changelog_service.rb @@ -41,6 +41,9 @@ module Repositories # The `trailer` argument is the Git trailer to use for determining what # commits to include in the changelog. # + # The `config_file` arguments specifies the path to the configuration file as + # stored in the project's Git repository. + # # The `file` arguments specifies the name/path of the file to commit the # changes to. If the file doesn't exist, it's created automatically. # @@ -57,6 +60,7 @@ module Repositories to: branch, date: DateTime.now, trailer: DEFAULT_TRAILER, + config_file: Gitlab::Changelog::Config::DEFAULT_FILE_PATH, file: DEFAULT_FILE, message: "Add changelog for version #{version}" ) @@ -68,13 +72,14 @@ module Repositories @date = date @branch = branch @trailer = trailer + @config_file = config_file @file = file @message = message end # rubocop: enable Metrics/ParameterLists def execute(commit_to_changelog: true) - config = Gitlab::Changelog::Config.from_git(@project, @user) + config = Gitlab::Changelog::Config.from_git(@project, @user, @config_file) from = start_of_commit_range(config) # For every entry we want to only include the merge request that diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb index a69028e2a2c..336d60d46ac 100644 --- a/app/workers/concerns/waitable_worker.rb +++ b/app/workers/concerns/waitable_worker.rb @@ -7,7 +7,7 @@ module WaitableWorker # Schedules multiple jobs and waits for them to be completed. def bulk_perform_and_wait(args_list) # Short-circuit: it's more efficient to do small numbers of jobs inline - if args_list.size == 1 || (args_list.size <= 3 && !inline_refresh_only_for_single_element?) + if args_list.size == 1 return bulk_perform_inline(args_list) end @@ -29,10 +29,6 @@ module WaitableWorker bulk_perform_async(failed) if failed.present? end - - def inline_refresh_only_for_single_element? - Feature.enabled?(:inline_project_authorizations_refresh_only_for_single_element) - end end def perform(*args) diff --git a/config/feature_flags/development/inline_project_authorizations_refresh_only_for_single_element.yml b/config/feature_flags/development/inline_project_authorizations_refresh_only_for_single_element.yml deleted file mode 100644 index 3a44490285d..00000000000 --- a/config/feature_flags/development/inline_project_authorizations_refresh_only_for_single_element.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: inline_project_authorizations_refresh_only_for_single_element -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91572 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366815 -milestone: '15.2' -type: development -group: group::workspace -default_enabled: false diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 1a1eee175f7..34b86902271 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -319,6 +319,7 @@ Supported attributes: | `date` | datetime | no | The date and time of the release, defaults to the current time. | | `branch` | string | no | The branch to commit the changelog changes to, defaults to the project's default branch. | | `trailer` | string | no | The Git trailer to use for including commits, defaults to `Changelog`. | +| `config_file` | string | no | The path of changelog configuration file in the project's Git repository, defaults to `.gitlab/changelog_config.yml`. | | `file` | string | no | The file to commit the changes to, defaults to `CHANGELOG.md`. | | `message` | string | no | The commit message to produce when committing the changes, defaults to `Add changelog for version X` where X is the value of the `version` argument. | @@ -453,7 +454,7 @@ If a revert commit includes the trailer used for generating changelogs ### Customize the changelog output The output is customized using a YAML configuration file stored in your -project's Git repository. This file must reside in +project's Git repository. This default configuration file path is `.gitlab/changelog_config.yml`. You can set the following variables in this file: @@ -736,6 +737,7 @@ Supported attributes: | `to` | string | no | The end of the range of commits (as a SHA) to use for the changelog. This commit _is_ included in the list. Defaults to the branch specified in the `branch` attribute. | | `date` | datetime | no | The date and time of the release, ISO 8601 formatted. Example: `2016-03-11T03:45:40Z`. Defaults to the current time. | | `trailer` | string | no | The Git trailer to use for including commits, defaults to `Changelog`. | +| `config_file` | string | no | The path of changelog configuration file in the project's Git repository, defaults to `.gitlab/changelog_config.yml`. | ```shell curl --header "PRIVATE-TOKEN: token" "https://gitlab.com/api/v4/projects/42/repository/changelog?version=1.0.0" diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 2e21f591667..4c7cc6be8b6 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -238,6 +238,10 @@ module API end params do use :release_params + + optional :config_file, + type: String, + desc: "The file path to the configuration file as stored in the project's Git repository. Defaults to '.gitlab/changelog_config.yml'" end get ':id/repository/changelog' do service = ::Repositories::ChangelogService.new( @@ -262,6 +266,10 @@ module API type: String, desc: 'The branch to commit the changelog changes to' + optional :config_file, + type: String, + desc: "The file path to the configuration file as stored in the project's Git repository. Defaults to '.gitlab/changelog_config.yml'" + optional :file, type: String, desc: 'The file to commit the changelog changes to', diff --git a/lib/gitlab/changelog/config.rb b/lib/gitlab/changelog/config.rb index 9cb3d71f5c3..8fcc03ec437 100644 --- a/lib/gitlab/changelog/config.rb +++ b/lib/gitlab/changelog/config.rb @@ -7,9 +7,9 @@ module Gitlab # When rendering changelog entries, authors are not included. AUTHORS_NONE = 'none' - # The path to the configuration file as stored in the project's Git + # The default path to the configuration file as stored in the project's Git # repository. - FILE_PATH = '.gitlab/changelog_config.yml' + DEFAULT_FILE_PATH = '.gitlab/changelog_config.yml' # The default date format to use for formatting release dates. DEFAULT_DATE_FORMAT = '%Y-%m-%d' @@ -36,8 +36,9 @@ module Gitlab attr_accessor :date_format, :categories, :template, :tag_regex, :always_credit_user_ids - def self.from_git(project, user = nil) - if (yaml = project.repository.changelog_config.presence) + def self.from_git(project, user = nil, path = nil) + yaml = project.repository.changelog_config('HEAD', path.presence || DEFAULT_FILE_PATH) + if yaml.present? from_hash(project, YAML.safe_load(yaml), user) else new(project) diff --git a/lib/gitlab/github_import/importer/events/cross_referenced.rb b/lib/gitlab/github_import/importer/events/cross_referenced.rb new file mode 100644 index 00000000000..20b902cfe50 --- /dev/null +++ b/lib/gitlab/github_import/importer/events/cross_referenced.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module Gitlab + module GithubImport + module Importer + module Events + class CrossReferenced + attr_reader :project, :user_id + + def initialize(project, user_id) + @project = project + @user_id = user_id + end + + # issue_event - An instance of `Gitlab::GithubImport::Representation::IssueEvent`. + def execute(issue_event) + mentioned_in_record_class = mentioned_in_type(issue_event) + mentioned_in_number = issue_event.source.dig(:issue, :number) + mentioned_in_record = init_mentioned_in( + mentioned_in_record_class, mentioned_in_number + ) + return if mentioned_in_record.nil? + + note_body = cross_reference_note_content(mentioned_in_record.gfm_reference(project)) + track_activity(mentioned_in_record_class) + create_note(issue_event, note_body) + end + + private + + def track_activity(mentioned_in_class) + return if mentioned_in_class != Issue + + Gitlab::UsageDataCounters::HLLRedisCounter.track_event( + Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CROSS_REFERENCED, + values: user_id + ) + end + + def create_note(issue_event, note_body) + Note.create!( + system: true, + noteable_type: Issue.name, + noteable_id: issue_event.issue_db_id, + project: project, + author_id: user_id, + note: note_body, + system_note_metadata: SystemNoteMetadata.new(action: 'cross_reference'), + created_at: issue_event.created_at + ) + end + + def mentioned_in_type(issue_event) + is_pull_request = issue_event.source.dig(:issue, :pull_request).present? + is_pull_request ? MergeRequest : Issue + end + + # record_class - Issue/MergeRequest + def init_mentioned_in(record_class, iid) + db_id = fetch_mentioned_in_db_id(record_class, iid) + return if db_id.nil? + + record = record_class.new(id: db_id, iid: iid) + record.project = project + record.readonly! + record + end + + # record_class - Issue/MergeRequest + def fetch_mentioned_in_db_id(record_class, number) + sawyer_mentioned_in_adapter = Struct.new(:iid, :issuable_type, keyword_init: true) + mentioned_in_adapter = sawyer_mentioned_in_adapter.new( + iid: number, issuable_type: record_class.name + ) + + Gitlab::GithubImport::IssuableFinder.new(project, mentioned_in_adapter).database_id + end + + def cross_reference_note_content(gfm_reference) + "#{::SystemNotes::IssuablesService.cross_reference_note_prefix}#{gfm_reference}" + end + end + end + end + end +end diff --git a/lib/gitlab/github_import/importer/issue_event_importer.rb b/lib/gitlab/github_import/importer/issue_event_importer.rb index 955cfa5a0d0..e451af61ec3 100644 --- a/lib/gitlab/github_import/importer/issue_event_importer.rb +++ b/lib/gitlab/github_import/importer/issue_event_importer.rb @@ -30,6 +30,9 @@ module Gitlab when 'renamed' Gitlab::GithubImport::Importer::Events::Renamed.new(project, author_id) .execute(issue_event) + when 'cross-referenced' + Gitlab::GithubImport::Importer::Events::CrossReferenced.new(project, author_id) + .execute(issue_event) else Gitlab::GithubImport::Logger.debug( message: 'UNSUPPORTED_EVENT_TYPE', diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb index 673f56b5753..1410006af26 100644 --- a/lib/gitlab/github_import/importer/note_importer.rb +++ b/lib/gitlab/github_import/importer/note_importer.rb @@ -21,14 +21,12 @@ module Gitlab author_id, author_found = user_finder.author_id_for(note) - note_body = MarkdownText.format(note.note, note.author, author_found) - attributes = { noteable_type: note.noteable_type, noteable_id: noteable_id, project_id: project.id, author_id: author_id, - note: note_body, + note: note_body(author_found), discussion_id: note.discussion_id, system: false, created_at: note.created_at, @@ -48,6 +46,13 @@ module Gitlab def find_noteable_id GithubImport::IssuableFinder.new(project, note).database_id end + + private + + def note_body(author_found) + text = MarkdownText.convert_ref_links(note.note, project) + MarkdownText.format(text, note.author, author_found) + end end end end diff --git a/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb b/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb index 00267400a6c..45bbc25e637 100644 --- a/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb +++ b/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb @@ -19,6 +19,7 @@ module Gitlab end def each_associated(parent_record, associated) + compose_associated_id!(parent_record, associated) return if already_imported?(associated) Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched) @@ -68,6 +69,13 @@ module Gitlab def collection_options { state: 'all', sort: 'created', direction: 'asc' } end + + # Cross-referenced events on Github doesn't have id. + def compose_associated_id!(issue, event) + return if event.event != 'cross-referenced' + + event.id = "cross-reference##{issue.id}-in-#{event.source.issue.id}" + end end end end diff --git a/lib/gitlab/github_import/markdown_text.rb b/lib/gitlab/github_import/markdown_text.rb index 0b1c221bbec..692016bd005 100644 --- a/lib/gitlab/github_import/markdown_text.rb +++ b/lib/gitlab/github_import/markdown_text.rb @@ -5,8 +5,34 @@ module Gitlab class MarkdownText include Gitlab::EncodingHelper - def self.format(*args) - new(*args).to_s + ISSUE_REF_MATCHER = '%{github_url}/%{import_source}/issues' + PULL_REF_MATCHER = '%{github_url}/%{import_source}/pull' + + class << self + def format(*args) + new(*args).to_s + end + + # Links like `https://domain.github.com///pull/` needs to be converted + def convert_ref_links(text, project) + matcher_options = { github_url: github_url, import_source: project.import_source } + issue_ref_matcher = ISSUE_REF_MATCHER % matcher_options + pull_ref_matcher = PULL_REF_MATCHER % matcher_options + + url_helpers = Rails.application.routes.url_helpers + text.gsub(issue_ref_matcher, url_helpers.project_issues_url(project)) + .gsub(pull_ref_matcher, url_helpers.project_merge_requests_url(project)) + end + + private + + # Returns github domain without slash in the end + def github_url + oauth_config = Gitlab::Auth::OAuth::Provider.config_for('github') || {} + url = oauth_config['url'].presence || 'https://github.com' + url = url.chop if url.end_with?('/') + url + end end # text - The Markdown text as a String. diff --git a/lib/gitlab/github_import/representation/issue_event.rb b/lib/gitlab/github_import/representation/issue_event.rb index f62507a331b..9016338db3b 100644 --- a/lib/gitlab/github_import/representation/issue_event.rb +++ b/lib/gitlab/github_import/representation/issue_event.rb @@ -9,7 +9,8 @@ module Gitlab attr_reader :attributes - expose_attribute :id, :actor, :event, :commit_id, :label_title, :old_title, :new_title, :created_at + expose_attribute :id, :actor, :event, :commit_id, :label_title, :old_title, :new_title, + :source, :created_at expose_attribute :issue_db_id # set in SingleEndpointIssueEventsImporter#each_associated # Builds a event from a GitHub API response. @@ -24,6 +25,7 @@ module Gitlab label_title: event.label && event.label[:name], old_title: event.rename && event.rename[:from], new_title: event.rename && event.rename[:to], + source: event.source, issue_db_id: event.issue_db_id, created_at: event.created_at ) diff --git a/spec/frontend/header_search/init_spec.js b/spec/frontend/header_search/init_spec.js index 2feef79e8d1..9515ca8c812 100644 --- a/spec/frontend/header_search/init_spec.js +++ b/spec/frontend/header_search/init_spec.js @@ -1,6 +1,6 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import initHeaderSearch, { eventHandler } from '~/header_search/init'; +import initHeaderSearch, { eventHandler, cleanEventListeners } from '~/header_search/init'; describe('Header Search EventListener', () => { beforeEach(() => { @@ -21,24 +21,25 @@ describe('Header Search EventListener', () => { it('attached event listener', () => { const searchInputBox = document?.querySelector('#search'); - const addEventListener = jest.spyOn(searchInputBox, 'addEventListener'); + const addEventListenerSpy = jest.spyOn(searchInputBox, 'addEventListener'); initHeaderSearch(); - expect(addEventListener).toBeCalled(); + expect(addEventListenerSpy).toBeCalledTimes(2); }); it('removes event listener ', async () => { - const removeEventListener = jest.fn(); + const searchInputBox = document?.querySelector('#search'); + const removeEventListenerSpy = jest.spyOn(searchInputBox, 'removeEventListener'); jest.mock('~/header_search', () => ({ initHeaderSearchApp: jest.fn() })); await eventHandler.apply( { newHeaderSearchFeatureFlag: true, searchInputBox: document.querySelector('#search'), }, - [removeEventListener], + [cleanEventListeners], ); - expect(removeEventListener).toBeCalled(); + expect(removeEventListenerSpy).toBeCalledTimes(2); }); it('attaches new vue dropdown when feature flag is enabled', async () => { diff --git a/spec/graphql/types/ci/runner_upgrade_status_type_enum_spec.rb b/spec/graphql/types/ci/runner_upgrade_status_type_enum_spec.rb index d85808d9d61..03c784dcbe7 100644 --- a/spec/graphql/types/ci/runner_upgrade_status_type_enum_spec.rb +++ b/spec/graphql/types/ci/runner_upgrade_status_type_enum_spec.rb @@ -3,14 +3,22 @@ require 'spec_helper' RSpec.describe Types::Ci::RunnerUpgradeStatusTypeEnum do + let(:model_only_enum_values) { %w[not_processed] } + let(:expected_graphql_source_values) do + Ci::RunnerVersion.statuses.keys - model_only_enum_values + end + specify { expect(described_class.graphql_name).to eq('CiRunnerUpgradeStatusType') } it 'exposes all upgrade status values except not_processed' do expect(described_class.values.keys).to match_array( - Ci::RunnerVersion.statuses.keys - .reject { |k| k == 'not_processed' } - .map { |k| k.upcase } + expected_graphql_source_values + .map(&:upcase) .map { |v| v == 'INVALID_VERSION' ? 'INVALID' : v } ) end + + it 'exposes all upgrade status values except enum-only values' do + expect(described_class.values.values.map(&:value).map(&:to_s)).to match_array(expected_graphql_source_values) + end end diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb index 600682d30ad..92cad366cfd 100644 --- a/spec/lib/gitlab/changelog/config_spec.rb +++ b/spec/lib/gitlab/changelog/config_spec.rb @@ -20,6 +20,18 @@ RSpec.describe Gitlab::Changelog::Config do described_class.from_git(project) end + it "retrieves the specified configuration from git" do + allow(project.repository) + .to receive(:changelog_config).with('HEAD', 'specified_changelog_config.yml') + .and_return("---\ndate_format: '%Y'") + + expect(described_class) + .to receive(:from_hash) + .with(project, { 'date_format' => '%Y' }, nil) + + described_class.from_git(project, nil, 'specified_changelog_config.yml') + end + it 'returns the default configuration when no YAML file exists in Git' do allow(project.repository) .to receive(:changelog_config) diff --git a/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb new file mode 100644 index 00000000000..118c482a7d9 --- /dev/null +++ b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_gitlab_redis_cache do + subject(:importer) { described_class.new(project, user.id) } + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + + let(:sawyer_stub) { Struct.new(:iid, :issuable_type, keyword_init: true) } + + let(:issue) { create(:issue, project: project) } + let(:referenced_in) { build_stubbed(:issue, project: project) } + let(:commit_id) { nil } + + let(:issue_event) do + Gitlab::GithubImport::Representation::IssueEvent.from_json_hash( + 'id' => 6501124486, + 'node_id' => 'CE_lADOHK9fA85If7x0zwAAAAGDf0mG', + 'url' => 'https://api.github.com/repos/elhowm/test-import/issues/events/6501124486', + 'actor' => { 'id' => 4, 'login' => 'alice' }, + 'event' => 'cross-referenced', + 'source' => { + 'type' => 'issue', + 'issue' => { + 'number' => referenced_in.iid, + 'pull_request' => pull_request_resource + } + }, + 'created_at' => '2022-04-26 18:30:53 UTC', + 'issue_db_id' => issue.id + ) + end + + let(:pull_request_resource) { nil } + let(:expected_note_attrs) do + { + system: true, + noteable_type: Issue.name, + noteable_id: issue_event.issue_db_id, + project_id: project.id, + author_id: user.id, + note: expected_note_body, + created_at: issue_event.created_at + }.stringify_keys + end + + context 'when referenced in other issue' do + let(:expected_note_body) { "mentioned in issue ##{issue.iid}" } + + before do + other_issue_resource = sawyer_stub.new(iid: referenced_in.iid, issuable_type: 'Issue') + Gitlab::GithubImport::IssuableFinder.new(project, other_issue_resource) + .cache_database_id(referenced_in.iid) + end + + it 'creates expected note' do + importer.execute(issue_event) + + expect(issue.notes.count).to eq 1 + expect(issue.notes[0]).to have_attributes expected_note_attrs + expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference' + end + end + + context 'when referenced in pull request' do + let(:referenced_in) { build_stubbed(:merge_request, project: project) } + let(:pull_request_resource) { { 'id' => referenced_in.iid } } + + let(:expected_note_body) { "mentioned in merge request !#{referenced_in.iid}" } + + before do + other_issue_resource = + sawyer_stub.new(iid: referenced_in.iid, issuable_type: 'MergeRequest') + Gitlab::GithubImport::IssuableFinder.new(project, other_issue_resource) + .cache_database_id(referenced_in.iid) + end + + it 'creates expected note' do + importer.execute(issue_event) + + expect(issue.notes.count).to eq 1 + expect(issue.notes[0]).to have_attributes expected_note_attrs + expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference' + end + end + + context 'when referenced in out of project issue/pull_request' do + it 'creates expected note' do + importer.execute(issue_event) + + expect(issue.notes.count).to eq 0 + end + end +end diff --git a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb index 5e8fe26d3de..da32a3b3766 100644 --- a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb @@ -87,6 +87,13 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventImporter, :clean_gitlab Gitlab::GithubImport::Importer::Events::Renamed end + context "when it's cross-referenced issue event" do + let(:event_name) { 'cross-referenced' } + + it_behaves_like 'triggers specific event importer', + Gitlab::GithubImport::Importer::Events::CrossReferenced + end + context "when it's unknown issue event" do let(:event_name) { 'fake' } diff --git a/spec/lib/gitlab/github_import/markdown_text_spec.rb b/spec/lib/gitlab/github_import/markdown_text_spec.rb index 2d159580b5f..ad45469a4c3 100644 --- a/spec/lib/gitlab/github_import/markdown_text_spec.rb +++ b/spec/lib/gitlab/github_import/markdown_text_spec.rb @@ -12,6 +12,54 @@ RSpec.describe Gitlab::GithubImport::MarkdownText do end end + describe '.convert_ref_links' do + let_it_be(:project) { create(:project) } + + let(:paragraph) { FFaker::Lorem.paragraph } + let(:sentence) { FFaker::Lorem.sentence } + let(:issue_id) { rand(100) } + let(:pull_id) { rand(100) } + + let(:text_in) do + <<-TEXT + #{paragraph} + https://github.com/#{project.import_source}/issues/#{issue_id} + #{sentence} + https://github.com/#{project.import_source}/pull/#{pull_id} + TEXT + end + + let(:text_out) do + <<-TEXT + #{paragraph} + http://localhost/#{project.full_path}/-/issues/#{issue_id} + #{sentence} + http://localhost/#{project.full_path}/-/merge_requests/#{pull_id} + TEXT + end + + it { expect(described_class.convert_ref_links(text_in, project)).to eq text_out } + + context 'when Github EE with custom domain name' do + let(:github_domain) { 'https://custom.github.com/' } + let(:text_in) do + <<-TEXT + #{paragraph} + #{github_domain}#{project.import_source}/issues/#{issue_id} + #{sentence} + #{github_domain}#{project.import_source}/pull/#{pull_id} + TEXT + end + + before do + allow(Gitlab::Auth::OAuth::Provider) + .to receive(:config_for).with('github').and_return({ 'url' => github_domain }) + end + + it { expect(described_class.convert_ref_links(text_in, project)).to eq text_out } + end + end + describe '#to_s' do it 'returns the text when the author was found' do author = double(:author, login: 'Alice') diff --git a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb index d36037a257d..23da8276f64 100644 --- a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb +++ b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb @@ -21,6 +21,10 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do expect(issue_event.commit_id).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') end + it 'includes the issue event source' do + expect(issue_event.source).to eq({ type: 'issue', id: 123456 }) + end + it 'includes the issue_db_id' do expect(issue_event.issue_db_id).to eq(100500) end @@ -89,7 +93,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do let(:response) do event_resource = Struct.new( :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, - :rename, :issue_db_id, :created_at, :performed_via_github_app, + :rename, :issue_db_id, :created_at, :performed_via_github_app, :source, keyword_init: true ) user_resource = Struct.new(:id, :login, keyword_init: true) @@ -103,6 +107,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do commit_url: 'https://api.github.com/repos/octocat/Hello-World/commits'\ '/570e7b2abdd848b95f2f578043fc23bd6f6fd24d', rename: with_rename ? { from: 'old title', to: 'new title' } : nil, + source: { type: 'issue', id: 123456 }, issue_db_id: 100500, label: with_label ? { name: 'label title' } : nil, created_at: '2022-04-26 18:30:53 UTC', @@ -134,6 +139,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do 'label_title' => (with_label ? 'label title' : nil), 'old_title' => with_rename ? 'old title' : nil, 'new_title' => with_rename ? 'new title' : nil, + 'source' => { 'type' => 'issue', 'id' => 123456 }, "issue_db_id" => 100500, 'created_at' => '2022-04-26 18:30:53 UTC', 'performed_via_github_app' => nil diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 29250177f30..11323c40d28 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2735,6 +2735,33 @@ RSpec.describe Repository do end end + describe '#changelog_config' do + let(:user) { create(:user) } + let(:changelog_config_path) { Gitlab::Changelog::Config::DEFAULT_FILE_PATH } + + before do + repository.create_file( + user, + changelog_config_path, + 'CONTENT', + message: '...', + branch_name: 'master' + ) + end + + context 'when there is a changelog_config_path at the commit' do + it 'returns the content' do + expect(repository.changelog_config(repository.commit.sha, changelog_config_path)).to eq('CONTENT') + end + end + + context 'when there is no changelog_config_path at the commit' do + it 'returns nil' do + expect(repository.changelog_config(repository.commit.parent.sha, changelog_config_path)).to be_nil + end + end + end + describe '#route_map_for' do before do repository.create_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master') diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index d6d2bd5baf2..cf0165d123f 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -784,6 +784,40 @@ RSpec.describe API::Repositories do expect(json_response['notes']).to be_present end + it 'supports specified config file path' do + spy = instance_spy(Repositories::ChangelogService) + + expect(Repositories::ChangelogService) + .to receive(:new) + .with( + project, + user, + version: '1.0.0', + from: 'foo', + to: 'bar', + date: DateTime.new(2020, 1, 1), + trailer: 'Foo', + config_file: 'specified_changelog_config.yml' + ) + .and_return(spy) + + expect(spy).to receive(:execute).with(commit_to_changelog: false) + + get( + api("/projects/#{project.id}/repository/changelog", user), + params: { + version: '1.0.0', + from: 'foo', + to: 'bar', + date: '2020-01-01', + trailer: 'Foo', + config_file: 'specified_changelog_config.yml' + } + ) + + expect(response).to have_gitlab_http_status(:ok) + end + context 'when previous tag version does not exist' do it_behaves_like '422 response' do let(:request) { get api("/projects/#{project.id}/repository/changelog", user), params: { version: 'v0.0.0' } } @@ -905,5 +939,45 @@ RSpec.describe API::Repositories do expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(json_response['message']).to eq('Failed to generate the changelog: oops') end + + it "support specified config file path" do + spy = instance_spy(Repositories::ChangelogService) + + expect(Repositories::ChangelogService) + .to receive(:new) + .with( + project, + user, + version: '1.0.0', + from: 'foo', + to: 'bar', + date: DateTime.new(2020, 1, 1), + branch: 'kittens', + trailer: 'Foo', + config_file: 'specified_changelog_config.yml', + file: 'FOO.md', + message: 'Commit message' + ) + .and_return(spy) + + allow(spy).to receive(:execute).with(commit_to_changelog: true) + + post( + api("/projects/#{project.id}/repository/changelog", user), + params: { + version: '1.0.0', + from: 'foo', + to: 'bar', + date: '2020-01-01', + branch: 'kittens', + trailer: 'Foo', + config_file: 'specified_changelog_config.yml', + file: 'FOO.md', + message: 'Commit message' + } + ) + + expect(response).to have_gitlab_http_status(:ok) + end end end diff --git a/spec/services/repositories/changelog_service_spec.rb b/spec/services/repositories/changelog_service_spec.rb index 82546ae810b..3615747e191 100644 --- a/spec/services/repositories/changelog_service_spec.rb +++ b/spec/services/repositories/changelog_service_spec.rb @@ -194,6 +194,25 @@ RSpec.describe Repositories::ChangelogService do end end end + + context 'with specified changelog config file path' do + it 'return specified changelog content' do + config = Gitlab::Changelog::Config.from_hash(project, { 'template' => 'specified_changelog_content' }, creator) + + allow(Gitlab::Changelog::Config) + .to receive(:from_git) + .with(project, creator, 'specified_changelog_config.yml') + .and_return(config) + + described_class + .new(project, creator, version: '1.0.0', from: sha1, config_file: 'specified_changelog_config.yml') + .execute(commit_to_changelog: commit_to_changelog) + + changelog = project.repository.blob_at('master', 'CHANGELOG.md')&.data + + expect(changelog).to include('specified_changelog_content') + end + end end describe '#start_of_commit_range' do diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb index 1c23c4e3758..f6d4cc4679d 100644 --- a/spec/workers/concerns/waitable_worker_spec.rb +++ b/spec/workers/concerns/waitable_worker_spec.rb @@ -54,25 +54,6 @@ RSpec.describe WaitableWorker do worker.bulk_perform_and_wait(arguments) end - - context 'when the feature flag `inline_project_authorizations_refresh_only_for_single_element` is turned off' do - before do - stub_feature_flags(inline_project_authorizations_refresh_only_for_single_element: false) - end - - it 'inlines the jobs' do - args_list = [[1], [2], [3]] - expect(worker).to receive(:bulk_perform_inline).with(args_list).and_call_original - expect(Gitlab::AppJsonLogger).to( - receive(:info).with(a_hash_including('message' => 'running inline', - 'class' => 'Gitlab::Foo::Bar::DummyWorker', - 'job_status' => 'running', - 'queue' => 'foo_bar_dummy')) - .exactly(3).times) - - worker.bulk_perform_and_wait(args_list) - end - end end context '>= 4 jobs' do