rails_git_hooks 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -6
  3. data/README.md +143 -77
  4. data/lib/rails_git_hooks/checks/base.rb +55 -0
  5. data/lib/rails_git_hooks/checks/commit_msg/jira_prefix.rb +31 -0
  6. data/lib/rails_git_hooks/checks/commit_msg.rb +10 -0
  7. data/lib/rails_git_hooks/checks/pre_commit/debugger_check.rb +43 -0
  8. data/lib/rails_git_hooks/checks/pre_commit/default_branch.rb +20 -0
  9. data/lib/rails_git_hooks/checks/pre_commit/json_format_check.rb +30 -0
  10. data/lib/rails_git_hooks/checks/pre_commit/migrations_check.rb +37 -0
  11. data/lib/rails_git_hooks/checks/pre_commit/rubocop.rb +30 -0
  12. data/lib/rails_git_hooks/checks/pre_commit/whitespace_check.rb +31 -0
  13. data/lib/rails_git_hooks/checks/pre_commit/yaml_format_check.rb +31 -0
  14. data/lib/rails_git_hooks/checks/pre_commit.rb +16 -0
  15. data/lib/rails_git_hooks/checks/pre_push/run_tests.rb +24 -0
  16. data/lib/rails_git_hooks/checks/pre_push.rb +10 -0
  17. data/lib/rails_git_hooks/checks.rb +6 -0
  18. data/lib/rails_git_hooks/cli.rb +78 -59
  19. data/lib/rails_git_hooks/config/constants.rb +20 -0
  20. data/lib/rails_git_hooks/config/defaults.yml +127 -0
  21. data/lib/rails_git_hooks/config/defaults_loader.rb +42 -0
  22. data/lib/rails_git_hooks/core/check_definition.rb +63 -0
  23. data/lib/rails_git_hooks/core/check_result.rb +41 -0
  24. data/lib/rails_git_hooks/core/error.rb +5 -0
  25. data/lib/rails_git_hooks/install/installer.rb +53 -0
  26. data/lib/rails_git_hooks/runtime/check_registry.rb +29 -0
  27. data/lib/rails_git_hooks/runtime/dependency_checker.rb +51 -0
  28. data/lib/rails_git_hooks/runtime/file_matcher.rb +23 -0
  29. data/lib/rails_git_hooks/runtime/override_config.rb +125 -0
  30. data/lib/rails_git_hooks/runtime/policy_resolver.rb +36 -0
  31. data/lib/rails_git_hooks/runtime/repository.rb +61 -0
  32. data/lib/rails_git_hooks/runtime/runner.rb +76 -0
  33. data/lib/rails_git_hooks/runtime.rb +25 -0
  34. data/lib/rails_git_hooks/version.rb +1 -1
  35. data/lib/rails_git_hooks.rb +14 -6
  36. data/templates/hooks/commit-msg +7 -17
  37. data/templates/hooks/pre-commit +7 -21
  38. data/templates/hooks/pre-push +7 -17
  39. metadata +30 -9
  40. data/lib/rails_git_hooks/constants.rb +0 -21
  41. data/lib/rails_git_hooks/installer.rb +0 -156
  42. data/templates/shared/commit_msg/jira_prefix.rb +0 -20
  43. data/templates/shared/pre_commit/debugger_check.rb +0 -48
  44. data/templates/shared/pre_commit/default_branch.rb +0 -9
  45. data/templates/shared/pre_commit/rubocop_check.rb +0 -24
  46. data/templates/shared/pre_commit/whitespace_check.rb +0 -25
  47. data/templates/shared/pre_push/run_tests.rb +0 -9
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PreCommit
6
+ class YAMLFormatCheck < Base
7
+ check_definition key: 'yaml-format-check',
8
+ hook: :pre_commit,
9
+ description: 'Warn on invalid YAML',
10
+ file_based: true,
11
+ on_fail: :warn
12
+
13
+ def run
14
+ warnings = []
15
+
16
+ applicable_files.each do |path|
17
+ next unless File.file?(path)
18
+ next unless %w[.yml .yaml].include?(File.extname(path))
19
+
20
+ YAML.load_file(path)
21
+ rescue Psych::SyntaxError => e
22
+ location = e.line ? "#{path}:#{e.line}" : path
23
+ warnings << "#{location}: #{e.message}"
24
+ end
25
+
26
+ warnings.empty? ? CheckResult.pass : CheckResult.fail(messages: warnings)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PreCommit
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'pre_commit/default_branch'
11
+ require_relative 'pre_commit/debugger_check'
12
+ require_relative 'pre_commit/yaml_format_check'
13
+ require_relative 'pre_commit/json_format_check'
14
+ require_relative 'pre_commit/migrations_check'
15
+ require_relative 'pre_commit/whitespace_check'
16
+ require_relative 'pre_commit/rubocop'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PrePush
6
+ class RunTests < Base
7
+ check_definition key: 'run-tests',
8
+ hook: :pre_push,
9
+ description: 'Run test suite before push',
10
+ dependencies: { 'executables' => ['bundle'] },
11
+ command: %w[bundle exec rspec],
12
+ install_hint: 'Install test dependencies and ensure `bundle exec rspec` runs successfully'
13
+
14
+ def run
15
+ output, status = capture(*Array(config['command']))
16
+ return CheckResult.pass if status.success?
17
+
18
+ messages = output.split("\n").map(&:rstrip).reject(&:empty?)
19
+ CheckResult.fail(messages: messages)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PrePush
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'pre_push/run_tests'
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'checks/base'
4
+ require_relative 'checks/pre_commit'
5
+ require_relative 'checks/commit_msg'
6
+ require_relative 'checks/pre_push'
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_git_hooks'
4
+ require 'yaml'
4
5
 
5
6
  module GitHooks
6
7
  class CLI
7
- FEATURE_FLAG_TOKENS = GitHooks::Constants::FEATURE_FLAG_FILES.keys.freeze
8
-
9
8
  def self.run(argv = ARGV)
10
9
  new.run(argv)
11
10
  end
@@ -16,12 +15,16 @@ module GitHooks
16
15
  run_install(argv[1..])
17
16
  when 'list'
18
17
  run_list
18
+ when 'init'
19
+ run_init
19
20
  when 'disable'
20
21
  run_disable(argv[1..])
21
22
  when 'enable'
22
23
  run_enable(argv[1..])
23
- when 'disabled'
24
- run_disabled
24
+ when 'set'
25
+ run_set(argv[1..])
26
+ when 'show-config'
27
+ run_show_config
25
28
  when nil, '-h', '--help'
26
29
  print_help
27
30
  else
@@ -43,91 +46,107 @@ module GitHooks
43
46
  end
44
47
 
45
48
  def run_list
46
- puts "Available hooks: #{Installer.available_hook_names.join(', ')}"
49
+ repo = Repository.new
50
+ config = OverrideConfig.new(repo: repo)
51
+
52
+ puts 'Available hooks:'
53
+ Installer.available_hook_names.each { |name| puts " #{name}" }
54
+ puts
55
+ puts 'Available checks:'
56
+ CheckRegistry.all.each do |definition|
57
+ check_config = config.config_for(definition)
58
+ puts " #{definition.key} (#{definition.hook_section}/#{definition.config_name}, enabled=#{check_config['enabled']})"
59
+ end
60
+ rescue GitHooks::Error
61
+ puts 'Available hooks:'
62
+ Installer.available_hook_names.each { |name| puts " #{name}" }
63
+ puts
64
+ puts 'Available checks:'
65
+ CheckRegistry.all.each do |definition|
66
+ puts " #{definition.key} (#{definition.hook_section}/#{definition.config_name})"
67
+ end
68
+ end
69
+
70
+ def run_init
71
+ config = OverrideConfig.new(repo: Repository.new)
72
+ config.init
73
+ puts "Initialized #{Constants::CONFIG_FILE}"
47
74
  end
48
75
 
49
76
  def run_disable(args)
50
- tokens = parse_tokens(args)
51
- if tokens.empty?
52
- warn 'Usage: rails_git_hooks disable HOOK [HOOK...] [whitespace-check] [rubocop-check]'
53
- warn "Use '*' to disable all hooks."
77
+ key = args.first
78
+ if key.nil?
79
+ warn 'Usage: rails_git_hooks disable CHECK_NAME'
54
80
  exit 1
55
81
  end
56
- installer = Installer.new
57
- hook_names, feature_flags = split_tokens(tokens)
58
- feature_flags.each { |name| installer.public_send(:"disable_#{name.tr('-', '_')}") }
59
- installer.disable(*hook_names) if hook_names.any?
60
- puts "Disabled: #{(hook_names + feature_flags).join(', ')}"
61
- rescue GitHooks::Error => e
62
- warn "Error: #{e.message}"
63
- exit 1
82
+
83
+ repo = Repository.new
84
+ definition = CheckRegistry.find!(key)
85
+ OverrideConfig.new(repo: repo).set_option(definition, 'enabled', false)
86
+ puts "Disabled: #{key}"
64
87
  end
65
88
 
66
89
  def run_enable(args)
67
- tokens = parse_tokens(args)
68
- if tokens.empty?
69
- warn 'Usage: rails_git_hooks enable HOOK [HOOK...] [whitespace-check] [rubocop-check]'
90
+ key = args.first
91
+ if key.nil?
92
+ warn 'Usage: rails_git_hooks enable CHECK_NAME'
70
93
  exit 1
71
94
  end
72
- installer = Installer.new
73
- hook_names, feature_flags = split_tokens(tokens)
74
- feature_flags.each { |name| installer.public_send(:"enable_#{name.tr('-', '_')}") }
75
- installer.enable(*hook_names) if hook_names.any?
76
- puts "Enabled: #{(hook_names + feature_flags).join(', ')}"
77
- rescue GitHooks::Error => e
78
- warn "Error: #{e.message}"
79
- exit 1
80
- end
81
95
 
82
- def parse_tokens(args)
83
- args.reject { |a| a.start_with?('-') }
96
+ repo = Repository.new
97
+ definition = CheckRegistry.find!(key)
98
+ OverrideConfig.new(repo: repo).set_option(definition, 'enabled', true)
99
+ puts "Enabled: #{key}"
84
100
  end
85
101
 
86
- def split_tokens(tokens)
87
- feature_flags = tokens & FEATURE_FLAG_TOKENS
88
- hook_names = tokens - FEATURE_FLAG_TOKENS
89
- [hook_names, feature_flags]
102
+ def run_set(args)
103
+ key, option, value = args
104
+ if key.nil? || option.nil? || value.nil?
105
+ warn 'Usage: rails_git_hooks set CHECK_NAME OPTION VALUE'
106
+ exit 1
107
+ end
108
+
109
+ definition = CheckRegistry.find!(key)
110
+ OverrideConfig.new(repo: Repository.new).set_option(definition, option, value)
111
+ puts "Updated: #{key} #{option}=#{value}"
90
112
  end
91
113
 
92
- def run_disabled
93
- installer = Installer.new
94
- list = installer.disabled_hooks
95
- if list.empty?
96
- puts 'No hooks disabled.'
97
- else
98
- puts "Disabled hooks: #{list.join(', ')}"
99
- end
100
- rescue GitHooks::Error => e
101
- warn "Error: #{e.message}"
102
- exit 1
114
+ def run_show_config
115
+ repo = Repository.new
116
+ config = OverrideConfig.new(repo: repo).effective_config(CheckRegistry.all)
117
+ puts YAML.dump(config)
103
118
  end
104
119
 
105
120
  def print_help
106
121
  puts <<~HELP
107
- rails_git_hooks - Install git hooks for Jira commit prefix and RuboCop
122
+ rails_git_hooks - Install configurable Git hooks
108
123
 
109
124
  Usage:
110
125
  rails_git_hooks install [HOOK...]
111
- rails_git_hooks disable HOOK [HOOK...] [whitespace-check] [rubocop-check] (use * for all hooks)
112
- rails_git_hooks enable HOOK [HOOK...] [whitespace-check] [rubocop-check]
113
- rails_git_hooks disabled
114
126
  rails_git_hooks list
127
+ rails_git_hooks init
128
+ rails_git_hooks enable CHECK_NAME
129
+ rails_git_hooks disable CHECK_NAME
130
+ rails_git_hooks set CHECK_NAME OPTION VALUE
131
+ rails_git_hooks show-config
115
132
  rails_git_hooks --help
116
133
 
117
134
  Commands:
118
135
  install Install hooks into current repo's .git/hooks.
119
- disable Disable hooks or whitespace-check / rubocop-check (pre-commit options).
120
- enable Re-enable disabled hooks or enable whitespace-check / rubocop-check.
121
- disabled List currently disabled hooks.
122
- list List available hook names.
136
+ list List available hooks and checks.
137
+ init Create a sparse #{Constants::CONFIG_FILE} override file.
138
+ enable Enable a check in #{Constants::CONFIG_FILE}.
139
+ disable Disable a check in #{Constants::CONFIG_FILE}.
140
+ set Set a check option like on_fail/on_warn/quiet.
141
+ show-config Print effective merged configuration.
123
142
 
124
143
  Examples:
125
144
  rails_git_hooks install
126
- rails_git_hooks disable pre-commit
127
- rails_git_hooks disable * # disable all hooks
128
- rails_git_hooks enable pre-commit
129
- rails_git_hooks enable whitespace-check # trailing ws/conflict markers (off by default)
130
- rails_git_hooks enable rubocop-check # RuboCop on staged .rb files (off by default)
145
+ rails_git_hooks enable rubocop-check
146
+ rails_git_hooks disable migrations-check
147
+ rails_git_hooks set debugger-check on_fail fail
148
+ rails_git_hooks set rubocop-check quiet true
149
+ rails_git_hooks show-config
131
150
  rails_git_hooks install commit-msg pre-commit
132
151
  HELP
133
152
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ # Paths and default config (single source of truth).
5
+ module Constants
6
+ GEM_ROOT = File.expand_path('../../..', __dir__)
7
+ HOOKS_DIR = File.expand_path('templates/hooks', GEM_ROOT).freeze
8
+ RUNTIME_SOURCE_DIR = File.expand_path('lib/rails_git_hooks', GEM_ROOT).freeze
9
+ RUNTIME_DIR_NAME = 'rails_git_hooks'
10
+ CONFIG_FILE = '.rails_git_hooks.yml'
11
+
12
+ # Default hooks when install is run with no arguments.
13
+ DEFAULT_HOOKS = %w[commit-msg pre-commit].freeze
14
+ HOOK_CONFIG_NAMES = {
15
+ pre_commit: 'PreCommit',
16
+ commit_msg: 'CommitMsg',
17
+ pre_push: 'PrePush'
18
+ }.freeze
19
+ end
20
+ end
@@ -0,0 +1,127 @@
1
+ # Default config for each check. Overrides live in .rails_git_hooks.yml (sparse).
2
+ # Structure matches .rails_git_hooks.yml: hook section -> check config_name -> options.
3
+
4
+ PreCommit:
5
+ DefaultBranch:
6
+ enabled: true
7
+ quiet: false
8
+ on_fail: fail
9
+ on_warn: warn
10
+ on_missing_dependency: warn
11
+ include: []
12
+ exclude: []
13
+ dependencies: {}
14
+ command: []
15
+ description: Prevent commits on default branch
16
+ file_based: false
17
+
18
+ DebuggerCheck:
19
+ enabled: true
20
+ quiet: false
21
+ on_fail: warn
22
+ on_warn: warn
23
+ on_missing_dependency: warn
24
+ include: []
25
+ exclude: []
26
+ dependencies: {}
27
+ command: []
28
+ description: Warn on debugger statements
29
+ file_based: true
30
+
31
+ YAMLFormatCheck:
32
+ enabled: true
33
+ quiet: false
34
+ on_fail: warn
35
+ on_warn: warn
36
+ on_missing_dependency: warn
37
+ include: []
38
+ exclude: []
39
+ dependencies: {}
40
+ command: []
41
+ description: Warn on invalid YAML
42
+ file_based: true
43
+
44
+ JSONFormatCheck:
45
+ enabled: true
46
+ quiet: false
47
+ on_fail: warn
48
+ on_warn: warn
49
+ on_missing_dependency: warn
50
+ include: []
51
+ exclude: []
52
+ dependencies: {}
53
+ command: []
54
+ description: Warn on invalid JSON
55
+ file_based: true
56
+
57
+ MigrationsCheck:
58
+ enabled: true
59
+ quiet: false
60
+ on_fail: warn
61
+ on_warn: warn
62
+ on_missing_dependency: warn
63
+ include: []
64
+ exclude: []
65
+ dependencies: {}
66
+ command: []
67
+ description: Warn on missing schema files after migrations
68
+ file_based: true
69
+
70
+ WhitespaceCheck:
71
+ enabled: false
72
+ quiet: false
73
+ on_fail: fail
74
+ on_warn: warn
75
+ on_missing_dependency: warn
76
+ include: []
77
+ exclude: []
78
+ dependencies: {}
79
+ command: []
80
+ description: Reject trailing whitespace and conflict markers
81
+ file_based: true
82
+
83
+ RuboCop:
84
+ enabled: false
85
+ quiet: true
86
+ on_fail: fail
87
+ on_warn: warn
88
+ on_missing_dependency: warn
89
+ include: []
90
+ exclude: []
91
+ dependencies:
92
+ executables: [bundle]
93
+ libraries: [rubocop]
94
+ command: [bundle, exec, rubocop]
95
+ description: Run RuboCop on staged Ruby files
96
+ file_based: true
97
+ install_hint: Add `gem "rubocop"` to your Gemfile and run bundle install
98
+
99
+ CommitMsg:
100
+ JiraPrefix:
101
+ enabled: true
102
+ quiet: false
103
+ on_fail: fail
104
+ on_warn: warn
105
+ on_missing_dependency: warn
106
+ include: []
107
+ exclude: []
108
+ dependencies: {}
109
+ command: []
110
+ description: Prefix commit messages with ticket id from branch
111
+ file_based: false
112
+
113
+ PrePush:
114
+ RunTests:
115
+ enabled: true
116
+ quiet: false
117
+ on_fail: fail
118
+ on_warn: warn
119
+ on_missing_dependency: warn
120
+ include: []
121
+ exclude: []
122
+ dependencies:
123
+ executables: [bundle]
124
+ command: [bundle, exec, rspec]
125
+ description: Run test suite before push
126
+ file_based: false
127
+ install_hint: Install test dependencies and ensure `bundle exec rspec` runs successfully
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module GitHooks
6
+ class DefaultsLoader
7
+ DEFAULTS_PATH = File.expand_path('defaults.yml', __dir__)
8
+
9
+ class << self
10
+ def config_for(hook_section, config_name)
11
+ section = data.fetch(hook_section, {})
12
+ return nil if section.empty?
13
+
14
+ section.fetch(config_name, nil)
15
+ end
16
+
17
+ private
18
+
19
+ def data
20
+ @data ||= load
21
+ end
22
+
23
+ def load
24
+ return {} unless File.exist?(DEFAULTS_PATH)
25
+
26
+ raw = YAML.safe_load(File.read(DEFAULTS_PATH), aliases: true) || {}
27
+ deep_stringify(raw)
28
+ end
29
+
30
+ def deep_stringify(value)
31
+ case value
32
+ when Hash
33
+ value.each_with_object({}) { |(k, v), out| out[k.to_s] = deep_stringify(v) }
34
+ when Array
35
+ value.map { |v| deep_stringify(v) }
36
+ else
37
+ value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ class CheckDefinition
5
+ DEFAULTS = {
6
+ enabled: true,
7
+ quiet: false,
8
+ on_fail: :fail,
9
+ on_warn: :warn,
10
+ on_missing_dependency: :warn,
11
+ include: [],
12
+ exclude: [],
13
+ dependencies: {},
14
+ command: nil,
15
+ file_based: false
16
+ }.freeze
17
+
18
+ attr_reader :key, :config_name, :hook, :klass, :description
19
+
20
+ def initialize(key:, config_name:, hook:, klass:, description:, **options)
21
+ @key = key
22
+ @config_name = config_name
23
+ @hook = hook
24
+ @klass = klass
25
+ @description = description
26
+ @options = DEFAULTS.merge(options)
27
+ end
28
+
29
+ def default_config
30
+ {
31
+ 'enabled' => @options[:enabled],
32
+ 'quiet' => @options[:quiet],
33
+ 'on_fail' => @options[:on_fail].to_s,
34
+ 'on_warn' => @options[:on_warn].to_s,
35
+ 'on_missing_dependency' => @options[:on_missing_dependency].to_s,
36
+ 'include' => Array(@options[:include]),
37
+ 'exclude' => Array(@options[:exclude]),
38
+ 'dependencies' => deep_stringify(@options[:dependencies]),
39
+ 'command' => Array(@options[:command]).compact,
40
+ 'description' => description,
41
+ 'file_based' => @options[:file_based],
42
+ 'install_hint' => @options[:install_hint]
43
+ }.compact
44
+ end
45
+
46
+ def hook_section
47
+ Constants::HOOK_CONFIG_NAMES.fetch(hook)
48
+ end
49
+
50
+ private
51
+
52
+ def deep_stringify(value)
53
+ case value
54
+ when Hash
55
+ value.each_with_object({}) { |(key, nested), out| out[key.to_s] = deep_stringify(nested) }
56
+ when Array
57
+ value.map { |nested| deep_stringify(nested) }
58
+ else
59
+ value
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ class CheckResult
5
+ attr_reader :status, :messages, :reason
6
+
7
+ def initialize(status:, messages: [], reason: nil)
8
+ @status = status.to_sym
9
+ @messages = Array(messages).compact
10
+ @reason = reason&.to_sym
11
+ end
12
+
13
+ def self.pass(messages: [])
14
+ new(status: :pass, messages: messages, reason: :pass)
15
+ end
16
+
17
+ def self.warn(messages:, reason: :warning)
18
+ new(status: :warn, messages: messages, reason: reason)
19
+ end
20
+
21
+ def self.fail(messages:, reason: :failure)
22
+ new(status: :fail, messages: messages, reason: reason)
23
+ end
24
+
25
+ def with_status(status)
26
+ self.class.new(status: status, messages: messages, reason: reason)
27
+ end
28
+
29
+ def pass?
30
+ status == :pass
31
+ end
32
+
33
+ def warn?
34
+ status == :warn
35
+ end
36
+
37
+ def fail?
38
+ status == :fail
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module GitHooks
6
+ class Installer
7
+ def initialize(git_dir: nil)
8
+ @git_dir = git_dir || Repository.new.git_dir
9
+ end
10
+
11
+ def install(*hook_names)
12
+ target_dir = File.join(@git_dir, 'hooks')
13
+ raise GitHooks::Error, "Not a git repository or .git/hooks not found: #{@git_dir}" unless Dir.exist?(target_dir)
14
+
15
+ copy_runtime(target_dir)
16
+
17
+ hooks = hook_names.empty? ? Constants::DEFAULT_HOOKS : hook_names
18
+ hooks.each_with_object([]) do |name, installed|
19
+ next unless self.class.available_hook_names.include?(name)
20
+
21
+ dest = File.join(target_dir, name)
22
+ File.write(dest, File.read(File.join(Constants::HOOKS_DIR, name)))
23
+ File.chmod(0o755, dest)
24
+ installed << name
25
+ end
26
+ end
27
+
28
+ def self.available_hook_names
29
+ Dir.children(Constants::HOOKS_DIR).select { |name| File.file?(File.join(Constants::HOOKS_DIR, name)) }
30
+ end
31
+
32
+ def available_hooks
33
+ self.class.available_hook_names
34
+ end
35
+
36
+ private
37
+
38
+ def copy_runtime(target_dir)
39
+ runtime_dir = File.join(target_dir, Constants::RUNTIME_DIR_NAME)
40
+ FileUtils.rm_rf(runtime_dir)
41
+ FileUtils.mkdir_p(runtime_dir)
42
+
43
+ Dir.glob(File.join(Constants::RUNTIME_SOURCE_DIR, '**', '*')).each do |src|
44
+ next unless File.file?(src)
45
+
46
+ rel = src.sub(%r{\A#{Regexp.escape(Constants::RUNTIME_SOURCE_DIR)}/}, '')
47
+ dest = File.join(runtime_dir, rel)
48
+ FileUtils.mkdir_p(File.dirname(dest))
49
+ File.write(dest, File.read(src))
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ class CheckRegistry
5
+ CHECK_CLASSES = [
6
+ Checks::PreCommit::DefaultBranch,
7
+ Checks::PreCommit::DebuggerCheck,
8
+ Checks::PreCommit::YAMLFormatCheck,
9
+ Checks::PreCommit::JSONFormatCheck,
10
+ Checks::PreCommit::MigrationsCheck,
11
+ Checks::PreCommit::WhitespaceCheck,
12
+ Checks::PreCommit::RuboCop,
13
+ Checks::CommitMsg::JiraPrefix,
14
+ Checks::PrePush::RunTests
15
+ ].freeze
16
+
17
+ def self.all
18
+ CHECK_CLASSES.map(&:definition)
19
+ end
20
+
21
+ def self.for(hook_name)
22
+ all.select { |definition| definition.hook == hook_name.to_sym }
23
+ end
24
+
25
+ def self.find!(key)
26
+ all.find { |definition| definition.key == key } || raise(GitHooks::Error, "Unknown check: #{key}")
27
+ end
28
+ end
29
+ end