Module: Harnex::DispatchHistory

Defined in:
lib/harnex/dispatch_history.rb

Constant Summary collapse

MAX_REPO_WALK_LEVELS =
10

Class Method Summary collapse

Class Method Details

.append(path, record) ⇒ Object



54
55
56
57
58
59
60
61
62
63
# File 'lib/harnex/dispatch_history.rb', line 54

def append(path, record)
  FileUtils.mkdir_p(File.dirname(path))
  line = JSON.generate(record) + "\n"
  File.open(path, File::WRONLY | File::APPEND | File::CREAT, 0o644) do |file|
    file.flock(File::LOCK_EX)
    file.write(line)
  ensure
    file.flock(File::LOCK_UN) unless file.closed?
  end
end

.build_record(session) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/harnex/dispatch_history.rb', line 65

def build_record(session)
  ended_at = session.ended_at || Time.now
  status, terminal_event = classify(session)
  {
    schema_version: 1,
    id: session.id,
    description: session.description,
    cli: session.adapter.key,
    started_at: session.started_at.utc.iso8601,
    ended_at: ended_at.utc.iso8601,
    duration_s: (ended_at - session.started_at).to_i,
    status: status,
    terminal_event: terminal_event,
    commit_sha: commit_sha(session.git_start, session.git_end),
    tier: session.__send__(:meta_hash)["tier"],
    meta: session.__send__(:meta_hash),
    summary_out_path: session.summary_out,
    events_log_path: session.events_log_path,
    tmux_state: tmux_state(session.__send__(:summary_tmux_session))
  }
end

.classify(session) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/harnex/dispatch_history.rb', line 87

def classify(session)
  return ["completed", "task_complete"] if session.task_complete?
  return ["timeout", "timeout"] if session.exit_code == 124
  return ["killed", "process_kill"] if session.term_signal
  return ["completed", "process_exit"] if session.exit_code == 0

  ["failed", "dispatch_failed"]
end

.commit_sha(git_start, git_end) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/harnex/dispatch_history.rb', line 96

def commit_sha(git_start, git_end)
  start_sha = git_start[:sha].to_s
  end_sha = git_end[:sha].to_s
  return nil if start_sha.empty? || end_sha.empty? || start_sha == end_sha

  end_sha
end

.find_git_root(start_path) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/harnex/dispatch_history.rb', line 25

def find_git_root(start_path)
  path = File.expand_path(start_path.to_s.empty? ? Dir.pwd : start_path)
  path = File.dirname(path) unless File.directory?(path)

  git_root = git_toplevel(path)
  return git_root if git_root

  (MAX_REPO_WALK_LEVELS + 1).times do
    return path if File.directory?(File.join(path, ".git"))

    parent = File.dirname(path)
    break if parent == path

    path = parent
  end

  nil
end

.git_toplevel(path) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/harnex/dispatch_history.rb', line 44

def git_toplevel(path)
  output, status = Open3.capture2("git", "-C", path, "rev-parse", "--show-toplevel", err: File::NULL)
  root = output.to_s.strip
  return root if status.success? && !root.empty?

  nil
rescue StandardError
  nil
end

.global_pathObject



12
13
14
# File 'lib/harnex/dispatch_history.rb', line 12

def global_path
  File.join(STATE_DIR, "dispatch.jsonl")
end

.path_for(start_path = Dir.pwd, global: false) ⇒ Object



16
17
18
19
20
21
22
23
# File 'lib/harnex/dispatch_history.rb', line 16

def path_for(start_path = Dir.pwd, global: false)
  return global_path if global

  repo_root = find_git_root(start_path)
  return global_path unless repo_root

  File.join(repo_root, ".harnex", "dispatch.jsonl")
end

.tmux_state(tmux_session) ⇒ Object



104
105
106
107
108
109
110
# File 'lib/harnex/dispatch_history.rb', line 104

def tmux_state(tmux_session)
  return "torn-down" if tmux_session.to_s.empty?

  system("tmux", "has-session", "-t", tmux_session.to_s, out: File::NULL, err: File::NULL) ? "live" : "torn-down"
rescue StandardError
  "torn-down"
end