main
rb 74 lines 2.15 KB
Raw
1 # frozen_string_literal: true
2
3 # One repository import, from queued through to done or failed. Drives the
4 # progress UI and makes a failed import retryable. The actual clone/push runs in
5 # RepositoryImportJob; this record is the state it advances.
6 class RepositoryImport < ApplicationRecord
7 belongs_to :user
8 belongs_to :repository, optional: true
9
10 MODES = %w[migrate mirror].freeze
11
12 # Ordered lifecycle. `cloning`/`pushing`/`fetching_lfs` are the long phases the
13 # UI narrates; terminal states are `done` and `failed`.
14 STATUSES = %w[queued cloning pushing fetching_lfs finalizing done failed].freeze
15 TERMINAL = %w[done failed].freeze
16 ACTIVE = STATUSES - TERMINAL
17
18 validates :mode, inclusion: { in: MODES }
19 validates :status, inclusion: { in: STATUSES }
20 validates :source_url, presence: true
21 validates :target_name, presence: true
22
23 scope :active, -> { where(status: ACTIVE) }
24 scope :recent, -> { order(created_at: :desc) }
25
26 def mirror?
27 mode == "mirror"
28 end
29
30 def migrate?
31 mode == "migrate"
32 end
33
34 def terminal?
35 TERMINAL.include?(status)
36 end
37
38 def failed?
39 status == "failed"
40 end
41
42 def done?
43 status == "done"
44 end
45
46 # A retry is allowed only from a failed state; re-running from any active or
47 # done state would risk duplicate work against a live import.
48 def retryable?
49 failed?
50 end
51
52 # Human-friendly one-liner for the progress UI.
53 def status_label
54 {
55 "queued" => "Queued",
56 "cloning" => "Cloning source",
57 "pushing" => "Pushing to siGit",
58 "fetching_lfs" => "Fetching LFS objects",
59 "finalizing" => "Finalizing",
60 "done" => "Imported",
61 "failed" => "Failed"
62 }.fetch(status, status.humanize)
63 end
64
65 # Advance the state machine, stamping timing on entry/exit. Kept tiny so the
66 # job body reads as a sequence of `transition_to(...)` calls.
67 def transition_to(new_status, error: nil)
68 attrs = { status: new_status }
69 attrs[:started_at] = Time.current if new_status == "cloning" && started_at.nil?
70 attrs[:finished_at] = Time.current if TERMINAL.include?(new_status)
71 attrs[:error_message] = error if error
72 update!(attrs)
73 end
74 end