Class: Agent::Workspace::Local

Inherits:
Object
  • Object
show all
Defined in:
app/services/agent/workspace.rb

Direct Known Subclasses

Container

Constant Summary collapse

ROOT =
Rails.root.join(".cardinal", "workspaces")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(card) ⇒ Local

Returns a new instance of Local.



38
39
40
41
# File 'app/services/agent/workspace.rb', line 38

def initialize(card)
  @card = card
  @path = ROOT.join("card-#{card.number}")
end

Instance Attribute Details

#cardObject (readonly)

Returns the value of attribute card.



27
28
29
# File 'app/services/agent/workspace.rb', line 27

def card
  @card
end

#pathObject (readonly)

Returns the value of attribute path.



27
28
29
# File 'app/services/agent/workspace.rb', line 27

def path
  @path
end

Class Method Details

.attach(card) ⇒ Object

Reattach without resetting — used when resuming a parked run whose local commits aren't pushed yet.



33
34
35
36
# File 'app/services/agent/workspace.rb', line 33

def self.attach(card)
  ws = new(card)
  File.directory?(ws.path.join(".git")) ? ws : ws.tap(&:provision)
end

.provision(card) ⇒ Object



29
# File 'app/services/agent/workspace.rb', line 29

def self.provision(card) = new(card).tap(&:provision)

Instance Method Details

#agent_spawn(cmd) ⇒ Object

How the runner should spawn the agent process for this workspace.



87
# File 'app/services/agent/workspace.rb', line 87

def agent_spawn(cmd) = [cmd, { chdir: path.to_s }]

#ahead_of_default?Boolean

Returns:

  • (Boolean)


95
96
97
# File 'app/services/agent/workspace.rb', line 95

def ahead_of_default?
  git_out(path, "rev-list", "--count", "origin/#{card.board.default_branch}..HEAD").strip.to_i.positive?
end

#commit_all!(message) ⇒ Object

Shell-less agents (column shell access off) edit files but cannot run git — the runner commits on their behalf when a segment ends.



79
80
81
82
83
84
# File 'app/services/agent/workspace.rb', line 79

def commit_all!(message)
  return false if git_out(path, "status", "--porcelain").strip.empty?
  git!(path, "add", "-A")
  git!(path, "commit", "--quiet", "-m", message)
  true
end

#commits_since(sha) ⇒ Object



91
92
93
# File 'app/services/agent/workspace.rb', line 91

def commits_since(sha)
  git_out(path, "log", "--oneline", "#{sha}..HEAD").lines.map(&:strip)
end

#headObject



89
# File 'app/services/agent/workspace.rb', line 89

def head = git_out(path, "rev-parse", "HEAD").strip

#provisionObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'app/services/agent/workspace.rb', line 43

def provision
  FileUtils.mkdir_p(ROOT)
  unless File.directory?(path.join(".git"))
    git!(ROOT, "clone", "--quiet", (card.board.local_path.presence || Rails.root).to_s, path.to_s)
    git!(path, "remote", "set-url", "origin", card.board.repo_url) if card.board.repo_url.present?
  end
  salvage_dirty_tree!
  git!(path, "fetch", "--quiet", "origin")
  if git?(path, "rev-parse", "--verify", "origin/#{card.branch_name}")
    git!(path, "checkout", "--quiet", card.branch_name)
    git!(path, "reset", "--quiet", "--hard", "origin/#{card.branch_name}")
  elsif git?(path, "rev-parse", "--verify", card.branch_name)
    # Local-only branch (e.g. WIP salvaged but never pushed): keep it.
    git!(path, "checkout", "--quiet", card.branch_name)
  else
    git!(path, "checkout", "--quiet", "-B", card.branch_name, "origin/#{card.board.default_branch}")
  end
  self
end

#push!Object



99
100
101
# File 'app/services/agent/workspace.rb', line 99

def push!
  git!(path, "push", "--quiet", "-u", "origin", card.branch_name)
end

#salvage_dirty_tree!Object

A killed run can leave uncommitted edits that block checkout and would otherwise be silently destroyed. Commit them as WIP on the branch and push (best effort) so the interrupted work survives onto the PR.



66
67
68
69
70
71
72
73
74
75
# File 'app/services/agent/workspace.rb', line 66

def salvage_dirty_tree!
  return if git_out(path, "status", "--porcelain").strip.empty?
  git!(path, "add", "-A")
  git!(path, "commit", "--quiet", "-m", "WIP: salvage uncommitted work from an interrupted run")
  begin
    push!
  rescue RuntimeError
    nil # offline is fine — the local-branch checkout path keeps the WIP
  end
end