Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue4
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue4
-rw-r--r--app/controllers/projects/import/jira_controller.rb4
-rw-r--r--app/helpers/blob_helper.rb2
-rw-r--r--app/helpers/ci_variables_helper.rb2
-rw-r--r--app/models/ci/group_variable.rb4
-rw-r--r--app/models/ci/job_variable.rb2
-rw-r--r--app/models/ci/pipeline_schedule_variable.rb2
-rw-r--r--app/models/ci/pipeline_variable.rb2
-rw-r--r--app/models/ci/variable.rb4
-rw-r--r--app/models/concerns/ci/has_variable.rb36
-rw-r--r--app/models/concerns/ci/maskable.rb25
-rw-r--r--app/models/concerns/ci/new_has_variable.rb16
-rw-r--r--app/models/concerns/has_variable.rb34
-rw-r--r--app/models/concerns/maskable.rb23
-rw-r--r--app/models/concerns/new_has_variable.rb14
-rw-r--r--app/models/jira_import_data.rb31
-rw-r--r--app/models/project.rb14
-rw-r--r--app/services/milestones/transfer_service.rb4
-rw-r--r--app/workers/all_queues.yml49
-rw-r--r--app/workers/concerns/gitlab/jira_import/import_worker.rb35
-rw-r--r--app/workers/concerns/gitlab/jira_import/queue_options.rb16
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb4
-rw-r--r--app/workers/gitlab/jira_import/advance_stage_worker.rb26
-rw-r--r--app/workers/gitlab/jira_import/stage/finish_import_worker.rb20
-rw-r--r--app/workers/gitlab/jira_import/stage/import_attachments_worker.rb22
-rw-r--r--app/workers/gitlab/jira_import/stage/import_issues_worker.rb22
-rw-r--r--app/workers/gitlab/jira_import/stage/import_labels_worker.rb20
-rw-r--r--app/workers/gitlab/jira_import/stage/import_notes_worker.rb22
-rw-r--r--app/workers/gitlab/jira_import/stage/start_import_worker.rb44
30 files changed, 412 insertions, 95 deletions
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index be70bfc7399..03766c4877e 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -1,6 +1,6 @@
<script>
import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
-import { joinPaths } from '~/lib/utils/url_utility';
+import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility';
import { __ } from '../../locale';
import Icon from '../../vue_shared/components/icon.vue';
import getRefMixin from '../mixins/get_ref';
@@ -103,7 +103,7 @@ export default {
.filter(p => p !== '')
.reduce(
(acc, name, i) => {
- const path = joinPaths(i > 0 ? acc[i].path : '', encodeURIComponent(name));
+ const path = joinPaths(i > 0 ? acc[i].path : '', escapeFileUrl(name));
return acc.concat({
name,
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index b81e6a38b4c..f3e6e3686a3 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -1,7 +1,7 @@
<script>
import { escapeRegExp } from 'lodash';
import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
-import { visitUrl } from '~/lib/utils/url_utility';
+import { visitUrl, escapeFileUrl } from '~/lib/utils/url_utility';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Icon from '~/vue_shared/components/icon.vue';
import { getIconName } from '../../utils/icon';
@@ -92,7 +92,7 @@ export default {
computed: {
routerLinkTo() {
return this.isFolder
- ? { path: `/-/tree/${escape(this.ref)}/${encodeURIComponent(this.path)}` }
+ ? { path: `/-/tree/${escape(this.ref)}/${escapeFileUrl(this.path)}` }
: null;
},
iconName() {
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index c74c180fa20..d38d9e27347 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -42,11 +42,13 @@ module Projects
def schedule_import(params)
import_data = @project.create_or_update_import_data(data: {}).becomes(JiraImportData)
- import_data << JiraImportData::JiraProjectDetails.new(
+ jira_project_details = JiraImportData::JiraProjectDetails.new(
params[:jira_project_key],
Time.now.strftime('%Y-%m-%d %H:%M:%S'),
{ user_id: current_user.id, name: current_user.name }
)
+ import_data << jira_project_details
+ import_data.force_import!
@project.import_type = 'jira'
@project.import_state.schedule if @project.save
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index cc5ae32856a..4debf66db64 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -344,8 +344,8 @@ module BlobHelper
def show_suggest_pipeline_creation_celebration?
experiment_enabled?(:suggest_pipeline) &&
- @blob.auxiliary_viewer.valid?(project: @project, sha: @commit.sha, user: current_user) &&
@blob.path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] &&
+ @blob.auxiliary_viewer.valid?(project: @project, sha: @commit.sha, user: current_user) &&
@project.uses_default_ci_config? &&
cookies[suggest_pipeline_commit_cookie_name].present?
end
diff --git a/app/helpers/ci_variables_helper.rb b/app/helpers/ci_variables_helper.rb
index 3f4c04070b5..b271f069778 100644
--- a/app/helpers/ci_variables_helper.rb
+++ b/app/helpers/ci_variables_helper.rb
@@ -45,6 +45,6 @@ module CiVariablesHelper
end
def ci_variable_maskable_regex
- Maskable::REGEX.inspect.sub('\\A', '^').sub('\\z', '$').sub(/^\//, '').sub(/\/[a-z]*$/, '').gsub('\/', '/')
+ Ci::Maskable::REGEX.inspect.sub('\\A', '^').sub('\\z', '$').sub(/^\//, '').sub(/\/[a-z]*$/, '').gsub('\/', '/')
end
end
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index 0e50265c7ba..1e1dd68ee6c 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -3,9 +3,9 @@
module Ci
class GroupVariable < ApplicationRecord
extend Gitlab::Ci::Model
- include HasVariable
+ include Ci::HasVariable
include Presentable
- include Maskable
+ include Ci::Maskable
belongs_to :group, class_name: "::Group"
diff --git a/app/models/ci/job_variable.rb b/app/models/ci/job_variable.rb
index f2968c037c7..7eea8a37150 100644
--- a/app/models/ci/job_variable.rb
+++ b/app/models/ci/job_variable.rb
@@ -3,7 +3,7 @@
module Ci
class JobVariable < ApplicationRecord
extend Gitlab::Ci::Model
- include NewHasVariable
+ include Ci::NewHasVariable
include BulkInsertSafe
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb
index be6e5e76c31..adef9911ae1 100644
--- a/app/models/ci/pipeline_schedule_variable.rb
+++ b/app/models/ci/pipeline_schedule_variable.rb
@@ -3,7 +3,7 @@
module Ci
class PipelineScheduleVariable < ApplicationRecord
extend Gitlab::Ci::Model
- include HasVariable
+ include Ci::HasVariable
belongs_to :pipeline_schedule
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 51a6272e1ff..84ca4833cd7 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -3,7 +3,7 @@
module Ci
class PipelineVariable < ApplicationRecord
extend Gitlab::Ci::Model
- include HasVariable
+ include Ci::HasVariable
belongs_to :pipeline
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 760872d3e6b..08d39595c61 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -3,9 +3,9 @@
module Ci
class Variable < ApplicationRecord
extend Gitlab::Ci::Model
- include HasVariable
+ include Ci::HasVariable
include Presentable
- include Maskable
+ include Ci::Maskable
prepend HasEnvironmentScope
belongs_to :project
diff --git a/app/models/concerns/ci/has_variable.rb b/app/models/concerns/ci/has_variable.rb
new file mode 100644
index 00000000000..9bf2b409080
--- /dev/null
+++ b/app/models/concerns/ci/has_variable.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Ci
+ module HasVariable
+ extend ActiveSupport::Concern
+
+ included do
+ enum variable_type: {
+ env_var: 1,
+ file: 2
+ }
+
+ validates :key,
+ presence: true,
+ length: { maximum: 255 },
+ format: { with: /\A[a-zA-Z0-9_]+\z/,
+ message: "can contain only letters, digits and '_'." }
+
+ scope :order_key_asc, -> { reorder(key: :asc) }
+
+ attr_encrypted :value,
+ mode: :per_attribute_iv_and_salt,
+ insecure_mode: true,
+ key: Settings.attr_encrypted_db_key_base,
+ algorithm: 'aes-256-cbc'
+
+ def key=(new_key)
+ super(new_key.to_s.strip)
+ end
+ end
+
+ def to_runner_variable
+ { key: key, value: value, public: false, file: file? }
+ end
+ end
+end
diff --git a/app/models/concerns/ci/maskable.rb b/app/models/concerns/ci/maskable.rb
new file mode 100644
index 00000000000..15bc48bf964
--- /dev/null
+++ b/app/models/concerns/ci/maskable.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ module Maskable
+ extend ActiveSupport::Concern
+
+ # * Single line
+ # * No escape characters
+ # * No variables
+ # * No spaces
+ # * Minimal length of 8 characters
+ # * Characters must be from the Base64 alphabet (RFC4648) with the addition of @ and :
+ # * Absolutely no fun is allowed
+ REGEX = /\A[a-zA-Z0-9_+=\/@:-]{8,}\z/.freeze
+
+ included do
+ validates :masked, inclusion: { in: [true, false] }
+ validates :value, format: { with: REGEX }, if: :masked?
+ end
+
+ def to_runner_variable
+ super.merge(masked: masked?)
+ end
+ end
+end
diff --git a/app/models/concerns/ci/new_has_variable.rb b/app/models/concerns/ci/new_has_variable.rb
new file mode 100644
index 00000000000..546d243e5de
--- /dev/null
+++ b/app/models/concerns/ci/new_has_variable.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Ci
+ module NewHasVariable
+ extend ActiveSupport::Concern
+ include Ci::HasVariable
+
+ included do
+ attr_encrypted :value,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_32,
+ insecure_mode: false
+ end
+ end
+end
diff --git a/app/models/concerns/has_variable.rb b/app/models/concerns/has_variable.rb
deleted file mode 100644
index b4e99569071..00000000000
--- a/app/models/concerns/has_variable.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module HasVariable
- extend ActiveSupport::Concern
-
- included do
- enum variable_type: {
- env_var: 1,
- file: 2
- }
-
- validates :key,
- presence: true,
- length: { maximum: 255 },
- format: { with: /\A[a-zA-Z0-9_]+\z/,
- message: "can contain only letters, digits and '_'." }
-
- scope :order_key_asc, -> { reorder(key: :asc) }
-
- attr_encrypted :value,
- mode: :per_attribute_iv_and_salt,
- insecure_mode: true,
- key: Settings.attr_encrypted_db_key_base,
- algorithm: 'aes-256-cbc'
-
- def key=(new_key)
- super(new_key.to_s.strip)
- end
- end
-
- def to_runner_variable
- { key: key, value: value, public: false, file: file? }
- end
-end
diff --git a/app/models/concerns/maskable.rb b/app/models/concerns/maskable.rb
deleted file mode 100644
index d70e47bc4ff..00000000000
--- a/app/models/concerns/maskable.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Maskable
- extend ActiveSupport::Concern
-
- # * Single line
- # * No escape characters
- # * No variables
- # * No spaces
- # * Minimal length of 8 characters
- # * Characters must be from the Base64 alphabet (RFC4648) with the addition of @ and :
- # * Absolutely no fun is allowed
- REGEX = /\A[a-zA-Z0-9_+=\/@:-]{8,}\z/.freeze
-
- included do
- validates :masked, inclusion: { in: [true, false] }
- validates :value, format: { with: REGEX }, if: :masked?
- end
-
- def to_runner_variable
- super.merge(masked: masked?)
- end
-end
diff --git a/app/models/concerns/new_has_variable.rb b/app/models/concerns/new_has_variable.rb
deleted file mode 100644
index 429bf496872..00000000000
--- a/app/models/concerns/new_has_variable.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module NewHasVariable
- extend ActiveSupport::Concern
- include HasVariable
-
- included do
- attr_encrypted :value,
- mode: :per_attribute_iv,
- algorithm: 'aes-256-gcm',
- key: Settings.attr_encrypted_db_key_base_32,
- insecure_mode: false
- end
-end
diff --git a/app/models/jira_import_data.rb b/app/models/jira_import_data.rb
index 3f882deb24d..63be190aa0d 100644
--- a/app/models/jira_import_data.rb
+++ b/app/models/jira_import_data.rb
@@ -3,17 +3,40 @@
class JiraImportData < ProjectImportData
JiraProjectDetails = Struct.new(:key, :scheduled_at, :scheduled_by)
+ FORCE_IMPORT_KEY = 'force-import'
+
def projects
return [] unless data
- projects = data.dig('jira', 'projects').map do |p|
+ projects = data.dig('jira', 'projects')&.map do |p|
JiraProjectDetails.new(p['key'], p['scheduled_at'], p['scheduled_by'])
end
- projects.sort_by { |jp| jp.scheduled_at }
+
+ projects&.sort_by { |jp| jp.scheduled_at } || []
end
def <<(project)
- self.data ||= { jira: { projects: [] } }
- self.data['jira']['projects'] << project.to_h.deep_stringify_keys!
+ self.data ||= { 'jira' => { 'projects' => [] } }
+ self.data['jira'] ||= { 'projects' => [] }
+ self.data['jira']['projects'] = [] if data['jira']['projects'].blank? || !data['jira']['projects'].is_a?(Array)
+
+ self.data['jira']['projects'] << project.to_h
+ self.data.deep_stringify_keys!
+ end
+
+ def force_import!
+ self.data ||= {}
+ self.data.deep_merge!({ 'jira' => { FORCE_IMPORT_KEY => true } })
+ self.data.deep_stringify_keys!
+ end
+
+ def force_import?
+ !!data&.dig('jira', FORCE_IMPORT_KEY) && !projects.blank?
+ end
+
+ def finish_import!
+ return if data&.dig('jira', FORCE_IMPORT_KEY).nil?
+
+ data['jira'].delete(FORCE_IMPORT_KEY)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 4892c5310ec..7e006e734c5 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -868,6 +868,8 @@ class Project < ApplicationRecord
elsif gitlab_project_import?
# Do not retry on Import/Export until https://gitlab.com/gitlab-org/gitlab-foss/issues/26189 is solved.
RepositoryImportWorker.set(retry: false).perform_async(self.id)
+ elsif jira_import?
+ Gitlab::JiraImport::Stage::StartImportWorker.perform_async(self.id)
else
RepositoryImportWorker.perform_async(self.id)
end
@@ -900,7 +902,7 @@ class Project < ApplicationRecord
# This method is overridden in EE::Project model
def remove_import_data
- import_data&.destroy
+ import_data&.destroy unless jira_import?
end
def ci_config_path=(value)
@@ -947,7 +949,7 @@ class Project < ApplicationRecord
end
def import?
- external_import? || forked? || gitlab_project_import? || bare_repository_import?
+ external_import? || forked? || gitlab_project_import? || jira_import? || bare_repository_import?
end
def external_import?
@@ -962,6 +964,14 @@ class Project < ApplicationRecord
import_type == 'bare_repository'
end
+ def jira_import?
+ import_type == 'jira' && Feature.enabled?(:jira_issue_import, self)
+ end
+
+ def jira_force_import?
+ jira_import? && import_data&.becomes(JiraImportData)&.force_import?
+ end
+
def gitlab_project_import?
import_type == 'gitlab_project'
end
diff --git a/app/services/milestones/transfer_service.rb b/app/services/milestones/transfer_service.rb
index 213c6f8f1dd..18d7e41adc7 100644
--- a/app/services/milestones/transfer_service.rb
+++ b/app/services/milestones/transfer_service.rb
@@ -46,7 +46,7 @@ module Milestones
Milestone.joins(:issues)
.where(
issues: { project_id: project.id },
- group_id: old_group.id
+ group_id: old_group.self_and_ancestors
)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -56,7 +56,7 @@ module Milestones
Milestone.joins(:merge_requests)
.where(
merge_requests: { target_project_id: project.id },
- group_id: old_group.id
+ group_id: old_group.self_and_ancestors
)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 3df86e3314d..dd0eeaa9359 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -528,6 +528,55 @@
:resource_boundary: :unknown
:weight: 2
:idempotent:
+- :name: jira_importer:jira_import_advance_stage
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_finish_import
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_import_attachments
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_import_issues
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_import_labels
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_import_notes
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+- :name: jira_importer:jira_import_stage_start_import
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
- :name: mail_scheduler:mail_scheduler_issue_due
:feature_category: :issue_tracking
:has_external_dependencies:
diff --git a/app/workers/concerns/gitlab/jira_import/import_worker.rb b/app/workers/concerns/gitlab/jira_import/import_worker.rb
new file mode 100644
index 00000000000..7cc650bfc29
--- /dev/null
+++ b/app/workers/concerns/gitlab/jira_import/import_worker.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module ImportWorker
+ extend ActiveSupport::Concern
+
+ included do
+ include ApplicationWorker
+ include Gitlab::JiraImport::QueueOptions
+ end
+
+ def perform(project_id)
+ project = Project.find_by(id: project_id) # rubocop: disable CodeReuse/ActiveRecord
+
+ return unless can_import?(project)
+
+ import(project)
+ end
+
+ private
+
+ def import(project)
+ raise NotImplementedError
+ end
+
+ def can_import?(project)
+ return false unless project
+ return false if Feature.disabled?(:jira_issue_import, project)
+
+ project.import_state.started?
+ end
+ end
+ end
+end
diff --git a/app/workers/concerns/gitlab/jira_import/queue_options.rb b/app/workers/concerns/gitlab/jira_import/queue_options.rb
new file mode 100644
index 00000000000..bc1148f7d3b
--- /dev/null
+++ b/app/workers/concerns/gitlab/jira_import/queue_options.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module QueueOptions
+ extend ActiveSupport::Concern
+
+ included do
+ queue_namespace :jira_importer
+ feature_category :importers
+
+ sidekiq_options retry: 5
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index 8fbf88a1762..8bbfb10ed6e 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -13,8 +13,6 @@ module Gitlab
sidekiq_options dead: false
feature_category :importers
- private
-
# The known importer stages and their corresponding Sidekiq workers.
STAGES = {
issues_and_diff_notes: Stage::ImportIssuesAndDiffNotesWorker,
@@ -23,6 +21,8 @@ module Gitlab
finish: Stage::FinishImportWorker
}.freeze
+ private
+
def next_stage_worker(next_stage)
STAGES.fetch(next_stage.to_sym)
end
diff --git a/app/workers/gitlab/jira_import/advance_stage_worker.rb b/app/workers/gitlab/jira_import/advance_stage_worker.rb
new file mode 100644
index 00000000000..1b6fc54151e
--- /dev/null
+++ b/app/workers/gitlab/jira_import/advance_stage_worker.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ class AdvanceStageWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include QueueOptions
+ include ::Gitlab::Import::AdvanceStage
+
+ # The known importer stages and their corresponding Sidekiq workers.
+ STAGES = {
+ labels: Gitlab::JiraImport::Stage::ImportLabelsWorker,
+ issues: Gitlab::JiraImport::Stage::ImportIssuesWorker,
+ attachments: Gitlab::JiraImport::Stage::ImportAttachmentsWorker,
+ notes: Gitlab::JiraImport::Stage::ImportNotesWorker,
+ finish: Gitlab::JiraImport::Stage::FinishImportWorker
+ }.freeze
+
+ private
+
+ def next_stage_worker(next_stage)
+ STAGES.fetch(next_stage.to_sym)
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/finish_import_worker.rb b/app/workers/gitlab/jira_import/stage/finish_import_worker.rb
new file mode 100644
index 00000000000..5b1661d68c6
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/finish_import_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class FinishImportWorker # rubocop:disable Scalability/IdempotentWorker
+ include Gitlab::JiraImport::ImportWorker
+
+ private
+
+ def import(project)
+ project.after_import
+ ensure
+ project.import_data.becomes(JiraImportData).finish_import!
+ project.import_data.save!
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/import_attachments_worker.rb b/app/workers/gitlab/jira_import/stage/import_attachments_worker.rb
new file mode 100644
index 00000000000..3b209a279b5
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/import_attachments_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class ImportAttachmentsWorker # rubocop:disable Scalability/IdempotentWorker
+ include Gitlab::JiraImport::ImportWorker
+
+ private
+
+ def import(project)
+ # fake a attahcments import workers for now.
+ # new job waiter will have zero jobs_remaining by default, so it will just pass on to next stage
+ fake_waiter = JobWaiter.new
+
+ project.import_state.refresh_jid_expiration
+ Gitlab::JiraImport::AdvanceStageWorker.perform_async(project.id, { fake_waiter.key => fake_waiter.jobs_remaining }, :notes)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/import_issues_worker.rb b/app/workers/gitlab/jira_import/stage/import_issues_worker.rb
new file mode 100644
index 00000000000..79ed8e1f2da
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/import_issues_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class ImportIssuesWorker # rubocop:disable Scalability/IdempotentWorker
+ include Gitlab::JiraImport::ImportWorker
+
+ private
+
+ def import(project)
+ # fake issues import workers for now
+ # new job waiter will have zero jobs_remaining by default, so it will just pass on to next stage
+ jobs_waiter = JobWaiter.new
+ project.import_state.refresh_jid_expiration
+
+ Gitlab::JiraImport::AdvanceStageWorker.perform_async(project.id, { jobs_waiter.key => jobs_waiter.jobs_remaining }, :attachments)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/import_labels_worker.rb b/app/workers/gitlab/jira_import/stage/import_labels_worker.rb
new file mode 100644
index 00000000000..b96bb1bbdda
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/import_labels_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class ImportLabelsWorker # rubocop:disable Scalability/IdempotentWorker
+ include Gitlab::JiraImport::ImportWorker
+
+ private
+
+ def import(project)
+ # fake labels import workers for now
+ # new job waiter will have zero jobs_remaining by default, so it will just pass on to next stage
+ fake_waiter = JobWaiter.new
+ Gitlab::JiraImport::AdvanceStageWorker.perform_async(project.id, { fake_waiter.key => fake_waiter.jobs_remaining }, :issues)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/import_notes_worker.rb b/app/workers/gitlab/jira_import/stage/import_notes_worker.rb
new file mode 100644
index 00000000000..9eef0d31a8c
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/import_notes_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class ImportNotesWorker # rubocop:disable Scalability/IdempotentWorker
+ include Gitlab::JiraImport::ImportWorker
+
+ private
+
+ def import(project)
+ # fake notes import workers for now
+ # new job waiter will have zero jobs_remaining by default, so it will just pass on to next stage
+ jobs_waiter = JobWaiter.new
+ project.import_state.refresh_jid_expiration
+
+ Gitlab::JiraImport::AdvanceStageWorker.perform_async(project.id, { jobs_waiter.key => jobs_waiter.jobs_remaining }, :finish)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/stage/start_import_worker.rb b/app/workers/gitlab/jira_import/stage/start_import_worker.rb
new file mode 100644
index 00000000000..8abbfab647b
--- /dev/null
+++ b/app/workers/gitlab/jira_import/stage/start_import_worker.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module JiraImport
+ module Stage
+ class StartImportWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include ProjectStartImport
+ include ProjectImportOptions
+ include Gitlab::JiraImport::QueueOptions
+
+ attr_reader :project
+
+ def perform(project_id)
+ @project = Project.find_by(id: project_id) # rubocop: disable CodeReuse/ActiveRecord
+
+ return unless start_import
+
+ Gitlab::Import::SetAsyncJid.set_jid(project)
+
+ Gitlab::JiraImport::Stage::ImportLabelsWorker.perform_async(project.id)
+ end
+
+ private
+
+ def start_import
+ return false unless project
+ return false if Feature.disabled?(:jira_issue_import, project)
+ return true if start(project.import_state)
+
+ Gitlab::Import::Logger.info(
+ {
+ project_id: project.id,
+ project_path: project.full_path,
+ state: project&.import_status,
+ message: 'inconsistent state while importing'
+ }
+ )
+ false
+ end
+ end
+ end
+ end
+end