Class: RosettAi::GitHooks::Installer

Inherits:
Object
  • Object
show all
Defined in:
lib/rosett_ai/git_hooks/installer.rb

Overview

Installs and uninstalls rosett-ai-managed git hooks in .git/hooks/.

Reads hook definitions from .rosett-ai/config.yml and generates POSIX sh scripts that invoke rai commands. Chains to pre-existing hooks (overcommit, husky, lefthook) without overwriting them.

Author:

  • hugo

  • claude

Constant Summary collapse

RAI_MARKER =
'# rosett-ai-managed hook'
BACKUP_SUFFIX =
'.rosett-ai-backup'
SUPPORTED_HOOKS =
[
  'pre-commit', 'post-commit', 'post-checkout', 'post-merge', 'pre-push'
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(project_root:, hooks_config:) ⇒ Installer

Returns a new instance of Installer.

Parameters:

  • project_root (Pathname)

    path to the project root

  • hooks_config (Hash)

    hook definitions from .rosett-ai/config.yml



26
27
28
29
30
# File 'lib/rosett_ai/git_hooks/installer.rb', line 26

def initialize(project_root:, hooks_config:)
  @project_root = Pathname.new(project_root)
  @hooks_config = hooks_config
  @hooks_dir = @project_root.join('.git', 'hooks')
end

Instance Method Details

#installHash

Installs all configured hooks.

Returns:

  • (Hash)

    result with :installed, :skipped, :errors keys



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rosett_ai/git_hooks/installer.rb', line 35

def install
  results = { installed: [], skipped: [], errors: [] }

  unless @hooks_dir.directory?
    results[:errors] << 'Not a git repository (missing .git/hooks/)'
    return results
  end

  @hooks_config.each do |hook_name, commands|
    normalized = normalize_hook_name(hook_name)
    install_hook(normalized, Array(commands), results)
  end

  results
end

#listArray<Hash>

Lists currently installed rai hooks.

Returns:

  • (Array<Hash>)

    list of hook info hashes with :name and :commands



69
70
71
72
73
74
75
76
# File 'lib/rosett_ai/git_hooks/installer.rb', line 69

def list
  SUPPORTED_HOOKS.filter_map do |hook|
    path = @hooks_dir.join(hook)
    next unless path.exist? && rai_managed?(path)

    { name: hook, commands: extract_commands(path) }
  end
end

#statusHash

Reports drift between config and installed hooks.

Returns:

  • (Hash)

    status with :in_sync, :missing, :extra, :drifted keys



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rosett_ai/git_hooks/installer.rb', line 81

def status
  result = { in_sync: [], missing: [], extra: [], drifted: [] }

  configured = @hooks_config.transform_keys { |key| normalize_hook_name(key) }

  configured.each do |hook_name, commands|
    path = @hooks_dir.join(hook_name)
    classify_hook(path, hook_name, Array(commands), result)
  end

  find_extra_hooks(configured, result)
  result
end

#uninstallHash

Uninstalls all rosett-ai-managed hooks, restoring backups.

Returns:

  • (Hash)

    result with :removed, :restored, :errors keys



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rosett_ai/git_hooks/installer.rb', line 54

def uninstall
  results = { removed: [], restored: [], errors: [] }

  unless @hooks_dir.directory?
    results[:errors] << 'Not a git repository (missing .git/hooks/)'
    return results
  end

  SUPPORTED_HOOKS.each { |hook| uninstall_hook(hook, results) }
  results
end