Class: Textus::Action::Blame

Inherits:
Base
  • Object
show all
Defined in:
lib/textus/action/blame.rb

Class Method Summary collapse

Methods inherited from Base

inherited, proposal_from

Methods included from Contract::DSL

#arg, #cli, #cli_stdin, #contract, #contract?, #summary, #surfaces, #verb, #view

Class Method Details

.call(container:, key:, limit: nil) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/textus/action/blame.rb', line 16

def self.call(container:, key:, limit: nil, **)
  manifest = container.manifest
  root = container.root

  audit_result = Textus::Action::Audit.call(container: container, key: key, limit: limit)
  audit_rows = Value::Result.unwrap(audit_result)
  path = resolve_path(key, manifest: manifest)
  return Success(audit_rows.map { |row| row.merge("git" => nil) }) unless git_tracked?(path, root: root)

  Success(audit_rows.map { |row| row.merge("git" => git_commit_at(path, timestamp: row["ts"], root: root)) })
end

.git_commit_at(path, timestamp:, root:) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/textus/action/blame.rb', line 63

def self.git_commit_at(path, timestamp:, root:)
  args = ["git", "log", "-1"]
  args << "--before=#{timestamp}" if timestamp
  args += ["--format=%H%x09%an%x09%aI%x09%s", "--", path]
  out, _err, status = Open3.capture3(*args, chdir: root)
  return nil unless status.success?

  sha, author, date, subject = out.strip.split("\t", 4)
  return nil if sha.nil? || sha.empty?

  { "sha" => sha, "author" => author, "date" => date, "subject" => subject }
rescue Errno::ENOENT
  nil
end

.git_repo?(root) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
54
55
56
57
58
59
60
61
# File 'lib/textus/action/blame.rb', line 51

def self.git_repo?(root)
  dir = root
  loop do
    return true if File.directory?(File.join(dir, ".git"))

    parent = File.dirname(dir)
    return false if parent == dir

    dir = parent
  end
end

.git_tracked?(path, root:) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/textus/action/blame.rb', line 37

def self.git_tracked?(path, root:)
  return false if path.nil?
  return false unless File.exist?(path)
  return false unless git_repo?(root)

  _out, _err, status = Open3.capture3(
    "git", "ls-files", "--error-unmatch", path,
    chdir: root
  )
  status.success?
rescue Errno::ENOENT
  false
end

.resolve_path(key, manifest:) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/textus/action/blame.rb', line 28

def self.resolve_path(key, manifest:)
  res = manifest.resolver.resolve(key)
  mentry = res.entry
  path = res.path
  path || Textus::Key::Path.resolve(manifest.data, mentry)
rescue Textus::Error
  nil
end