rails_git_hooks 0.7.0 → 0.7.2

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -6
  3. data/README.md +72 -115
  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/post_checkout/bundle_install.rb +16 -0
  8. data/lib/rails_git_hooks/checks/post_checkout/db_migrate.rb +16 -0
  9. data/lib/rails_git_hooks/checks/post_checkout.rb +11 -0
  10. data/lib/rails_git_hooks/checks/post_merge/bundle_install.rb +16 -0
  11. data/lib/rails_git_hooks/checks/post_merge/db_migrate.rb +16 -0
  12. data/lib/rails_git_hooks/checks/post_merge.rb +11 -0
  13. data/lib/rails_git_hooks/checks/pre_commit/debugger_check.rb +43 -0
  14. data/lib/rails_git_hooks/checks/pre_commit/default_branch.rb +20 -0
  15. data/lib/rails_git_hooks/checks/pre_commit/json_format_check.rb +30 -0
  16. data/lib/rails_git_hooks/checks/pre_commit/migrations_check.rb +37 -0
  17. data/lib/rails_git_hooks/checks/pre_commit/rubocop.rb +30 -0
  18. data/lib/rails_git_hooks/checks/pre_commit/whitespace_check.rb +31 -0
  19. data/lib/rails_git_hooks/checks/pre_commit/yaml_format_check.rb +31 -0
  20. data/lib/rails_git_hooks/checks/pre_commit.rb +16 -0
  21. data/lib/rails_git_hooks/checks/pre_push/run_tests.rb +24 -0
  22. data/lib/rails_git_hooks/checks/pre_push.rb +10 -0
  23. data/lib/rails_git_hooks/checks/shared/bundle_install_check.rb +28 -0
  24. data/lib/rails_git_hooks/checks/shared/db_migrate_check.rb +28 -0
  25. data/lib/rails_git_hooks/checks.rb +10 -0
  26. data/lib/rails_git_hooks/cli.rb +17 -90
  27. data/lib/rails_git_hooks/config/constants.rb +26 -0
  28. data/lib/rails_git_hooks/config/defaults.yml +190 -0
  29. data/lib/rails_git_hooks/config/defaults_loader.rb +42 -0
  30. data/lib/rails_git_hooks/core/check_definition.rb +63 -0
  31. data/lib/rails_git_hooks/core/check_result.rb +41 -0
  32. data/lib/rails_git_hooks/core/error.rb +5 -0
  33. data/lib/rails_git_hooks/install/installer.rb +79 -0
  34. data/lib/rails_git_hooks/runtime/check_registry.rb +33 -0
  35. data/lib/rails_git_hooks/runtime/dependency_checker.rb +51 -0
  36. data/lib/rails_git_hooks/runtime/file_matcher.rb +23 -0
  37. data/lib/rails_git_hooks/runtime/override_config.rb +131 -0
  38. data/lib/rails_git_hooks/runtime/policy_resolver.rb +36 -0
  39. data/lib/rails_git_hooks/runtime/repository.rb +69 -0
  40. data/lib/rails_git_hooks/runtime/runner.rb +80 -0
  41. data/lib/rails_git_hooks/runtime.rb +25 -0
  42. data/lib/rails_git_hooks/version.rb +1 -1
  43. data/lib/rails_git_hooks.rb +14 -6
  44. data/templates/hooks/commit-msg +7 -17
  45. data/templates/hooks/post-checkout +13 -0
  46. data/templates/hooks/post-merge +13 -0
  47. data/templates/hooks/pre-commit +7 -21
  48. data/templates/hooks/pre-push +7 -17
  49. metadata +41 -52
  50. data/lib/rails_git_hooks/constants.rb +0 -21
  51. data/lib/rails_git_hooks/installer.rb +0 -156
  52. data/templates/shared/commit_msg/jira_prefix.rb +0 -20
  53. data/templates/shared/pre_commit/debugger_check.rb +0 -48
  54. data/templates/shared/pre_commit/default_branch.rb +0 -9
  55. data/templates/shared/pre_commit/rubocop_check.rb +0 -24
  56. data/templates/shared/pre_commit/whitespace_check.rb +0 -25
  57. data/templates/shared/pre_push/run_tests.rb +0 -9
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PreCommit
6
+ class RuboCop < Base
7
+ check_definition key: 'rubocop-check',
8
+ hook: :pre_commit,
9
+ description: 'Run RuboCop on staged Ruby files',
10
+ file_based: true,
11
+ enabled: false,
12
+ quiet: true,
13
+ dependencies: { 'executables' => ['bundle'], 'libraries' => ['rubocop'] },
14
+ command: %w[bundle exec rubocop],
15
+ install_hint: 'Add `gem "rubocop"` to your Gemfile and run bundle install'
16
+
17
+ def run
18
+ ruby_files = applicable_files.select { |path| File.extname(path) == '.rb' && File.file?(path) }
19
+ return CheckResult.pass if ruby_files.empty?
20
+
21
+ output, status = capture(*Array(config['command']), *ruby_files)
22
+ return CheckResult.pass if status.success?
23
+
24
+ messages = output.split("\n").map(&:rstrip).reject(&:empty?)
25
+ CheckResult.fail(messages: messages)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PreCommit
6
+ class WhitespaceCheck < Base
7
+ check_definition key: 'whitespace-check',
8
+ hook: :pre_commit,
9
+ description: 'Reject trailing whitespace and conflict markers',
10
+ file_based: true,
11
+ enabled: false
12
+
13
+ def run
14
+ errors = []
15
+
16
+ applicable_files.each do |path|
17
+ next unless File.file?(path)
18
+
19
+ File.read(path).lines.each_with_index do |line, index|
20
+ errors << "#{path}:#{index + 1}: trailing whitespace" if line.match?(/[ \t]\z/)
21
+ stripped = line.strip
22
+ errors << "#{path}:#{index + 1}: conflict marker" if stripped.start_with?('<<<<<<<', '=======', '>>>>>>>')
23
+ end
24
+ end
25
+
26
+ errors.empty? ? CheckResult.pass : CheckResult.fail(messages: errors.uniq)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -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,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ # Shared logic and options for "run bundle install when Gemfile/Gemfile.lock changed"
6
+ # (post-checkout and post-merge). Each hook has its own class that passes hook + description.
7
+ module BundleInstallCheck
8
+ DEFINITION_OPTIONS = {
9
+ file_based: true,
10
+ enabled: false,
11
+ include: %w[Gemfile Gemfile.lock],
12
+ dependencies: { executables: ['bundle'] },
13
+ command: %w[bundle install],
14
+ install_hint: 'Ensure bundle is available'
15
+ }.freeze
16
+
17
+ def run
18
+ return CheckResult.pass if applicable_files.empty?
19
+
20
+ output, status = capture(*Array(config['command']))
21
+ return CheckResult.pass if status.success?
22
+
23
+ messages = output.split("\n").map(&:rstrip).reject(&:empty?)
24
+ CheckResult.fail(messages: messages)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ # Shared logic and options for "run db:migrate when migrations or schema changed"
6
+ # (post-checkout and post-merge). Each hook has its own class that passes hook + description.
7
+ module DbMigrateCheck
8
+ DEFINITION_OPTIONS = {
9
+ file_based: true,
10
+ enabled: false,
11
+ include: ['db/migrate/*.rb', 'db/schema.rb', 'db/structure.sql'],
12
+ dependencies: { executables: ['bundle'] },
13
+ command: %w[bundle exec rails db:migrate],
14
+ install_hint: 'Rails app with db:migrate (or override command in config)'
15
+ }.freeze
16
+
17
+ def run
18
+ return CheckResult.pass if applicable_files.empty?
19
+
20
+ output, status = capture(*Array(config['command']))
21
+ return CheckResult.pass if status.success?
22
+
23
+ messages = output.split("\n").map(&:rstrip).reject(&:empty?)
24
+ CheckResult.fail(messages: messages)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,10 @@
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'
7
+ require_relative 'checks/shared/bundle_install_check'
8
+ require_relative 'checks/shared/db_migrate_check'
9
+ require_relative 'checks/post_checkout'
10
+ require_relative 'checks/post_merge'
@@ -4,25 +4,18 @@ require 'rails_git_hooks'
4
4
 
5
5
  module GitHooks
6
6
  class CLI
7
- FEATURE_FLAG_TOKENS = GitHooks::Constants::FEATURE_FLAG_FILES.keys.freeze
8
-
9
7
  def self.run(argv = ARGV)
10
8
  new.run(argv)
11
9
  end
12
10
 
13
11
  def run(argv)
14
- case argv[0]
12
+ cmd = argv[0].to_s.strip.tr('_', '-')
13
+ case cmd
15
14
  when 'install'
16
- run_install(argv[1..])
17
- when 'list'
18
- run_list
19
- when 'disable'
20
- run_disable(argv[1..])
21
- when 'enable'
22
- run_enable(argv[1..])
23
- when 'disabled'
24
- run_disabled
25
- when nil, '-h', '--help'
15
+ run_install
16
+ when 'init'
17
+ run_init
18
+ when '', '-h', '--help'
26
19
  print_help
27
20
  else
28
21
  warn "Unknown command: #{argv[0]}"
@@ -33,102 +26,36 @@ module GitHooks
33
26
 
34
27
  private
35
28
 
36
- def run_install(args)
29
+ def run_install
37
30
  installer = Installer.new
38
- installed = installer.install(*args)
31
+ installed = installer.install
39
32
  puts "Installed hooks: #{installed.join(', ')}"
40
33
  rescue GitHooks::Error => e
41
34
  warn "Error: #{e.message}"
42
35
  exit 1
43
36
  end
44
37
 
45
- def run_list
46
- puts "Available hooks: #{Installer.available_hook_names.join(', ')}"
47
- end
48
-
49
- 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."
54
- exit 1
55
- 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
64
- end
65
-
66
- 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]'
70
- exit 1
71
- 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
-
82
- def parse_tokens(args)
83
- args.reject { |a| a.start_with?('-') }
84
- end
85
-
86
- def split_tokens(tokens)
87
- feature_flags = tokens & FEATURE_FLAG_TOKENS
88
- hook_names = tokens - FEATURE_FLAG_TOKENS
89
- [hook_names, feature_flags]
90
- end
91
-
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
38
+ def run_init
39
+ config = OverrideConfig.new(repo: Repository.new)
40
+ config.init
41
+ puts "Initialized #{Constants::CONFIG_FILE}"
103
42
  end
104
43
 
105
44
  def print_help
106
45
  puts <<~HELP
107
- rails_git_hooks - Install git hooks for Jira commit prefix and RuboCop
46
+ rails_git_hooks - Install configurable Git hooks
108
47
 
109
48
  Usage:
110
- 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
- rails_git_hooks list
49
+ rails_git_hooks install
50
+ rails_git_hooks init
115
51
  rails_git_hooks --help
116
52
 
117
53
  Commands:
118
- 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.
54
+ install Install hooks that have at least one enabled check (merged config: defaults + .rails_git_hooks.yml + .rails_git_hooks.local.yml).
55
+ init Create a sparse #{Constants::CONFIG_FILE} override file.
123
56
 
124
57
  Examples:
125
58
  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)
131
- rails_git_hooks install commit-msg pre-commit
132
59
  HELP
133
60
  end
134
61
  end
@@ -0,0 +1,26 @@
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
+ CONFIG_FILE_LOCAL = '.rails_git_hooks.local.yml'
12
+
13
+ # Default hooks when install is run with no arguments.
14
+ DEFAULT_HOOKS = %w[commit-msg pre-commit].freeze
15
+ HOOK_CONFIG_NAMES = {
16
+ pre_commit: 'PreCommit',
17
+ commit_msg: 'CommitMsg',
18
+ pre_push: 'PrePush',
19
+ post_checkout: 'PostCheckout',
20
+ post_merge: 'PostMerge'
21
+ }.freeze
22
+
23
+ # Section name (from YAML) -> hook script name (e.g. for install)
24
+ SECTION_TO_HOOK = HOOK_CONFIG_NAMES.invert.transform_values { |sym| sym.to_s.tr('_', '-') }.freeze
25
+ end
26
+ end
@@ -0,0 +1,190 @@
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: false
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
+ libraries: [rspec]
125
+ command: [bundle, exec, rspec]
126
+ description: Run test suite before push
127
+ file_based: false
128
+ install_hint: Install test dependencies and ensure `bundle exec rspec` runs successfully
129
+
130
+ PostCheckout:
131
+ BundleInstall:
132
+ enabled: true
133
+ quiet: false
134
+ on_fail: fail
135
+ on_warn: warn
136
+ on_missing_dependency: warn
137
+ include: [Gemfile, Gemfile.lock]
138
+ exclude: []
139
+ dependencies:
140
+ executables: [bundle]
141
+ command: [bundle, install]
142
+ description: Run bundle install when Gemfile or Gemfile.lock changed (branch checkout)
143
+ file_based: true
144
+ install_hint: Ensure bundle is available
145
+
146
+ DbMigrate:
147
+ enabled: true
148
+ quiet: false
149
+ on_fail: fail
150
+ on_warn: warn
151
+ on_missing_dependency: warn
152
+ include: [db/migrate/*.rb, db/schema.rb, db/structure.sql]
153
+ exclude: []
154
+ dependencies:
155
+ executables: [bundle]
156
+ command: [bundle, exec, rails, db:migrate]
157
+ description: Run db:migrate when migrations or schema changed (branch checkout)
158
+ file_based: true
159
+ install_hint: Rails app with db:migrate (or override command in config)
160
+
161
+ PostMerge:
162
+ BundleInstall:
163
+ enabled: true
164
+ quiet: false
165
+ on_fail: fail
166
+ on_warn: warn
167
+ on_missing_dependency: warn
168
+ include: [Gemfile, Gemfile.lock]
169
+ exclude: []
170
+ dependencies:
171
+ executables: [bundle]
172
+ command: [bundle, install]
173
+ description: Run bundle install when Gemfile or Gemfile.lock changed (after merge)
174
+ file_based: true
175
+ install_hint: Ensure bundle is available
176
+
177
+ DbMigrate:
178
+ enabled: true
179
+ quiet: false
180
+ on_fail: fail
181
+ on_warn: warn
182
+ on_missing_dependency: warn
183
+ include: [db/migrate/*.rb, db/schema.rb, db/structure.sql]
184
+ exclude: []
185
+ dependencies:
186
+ executables: [bundle]
187
+ command: [bundle, exec, rails, db:migrate]
188
+ description: Run db:migrate when migrations or schema changed (after merge)
189
+ file_based: true
190
+ install_hint: Rails app with db:migrate (or override command in config)
@@ -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