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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -6
- data/README.md +143 -77
- data/lib/rails_git_hooks/checks/base.rb +55 -0
- data/lib/rails_git_hooks/checks/commit_msg/jira_prefix.rb +31 -0
- data/lib/rails_git_hooks/checks/commit_msg.rb +10 -0
- data/lib/rails_git_hooks/checks/pre_commit/debugger_check.rb +43 -0
- data/lib/rails_git_hooks/checks/pre_commit/default_branch.rb +20 -0
- data/lib/rails_git_hooks/checks/pre_commit/json_format_check.rb +30 -0
- data/lib/rails_git_hooks/checks/pre_commit/migrations_check.rb +37 -0
- data/lib/rails_git_hooks/checks/pre_commit/rubocop.rb +30 -0
- data/lib/rails_git_hooks/checks/pre_commit/whitespace_check.rb +31 -0
- data/lib/rails_git_hooks/checks/pre_commit/yaml_format_check.rb +31 -0
- data/lib/rails_git_hooks/checks/pre_commit.rb +16 -0
- data/lib/rails_git_hooks/checks/pre_push/run_tests.rb +24 -0
- data/lib/rails_git_hooks/checks/pre_push.rb +10 -0
- data/lib/rails_git_hooks/checks.rb +6 -0
- data/lib/rails_git_hooks/cli.rb +78 -59
- data/lib/rails_git_hooks/config/constants.rb +20 -0
- data/lib/rails_git_hooks/config/defaults.yml +127 -0
- data/lib/rails_git_hooks/config/defaults_loader.rb +42 -0
- data/lib/rails_git_hooks/core/check_definition.rb +63 -0
- data/lib/rails_git_hooks/core/check_result.rb +41 -0
- data/lib/rails_git_hooks/core/error.rb +5 -0
- data/lib/rails_git_hooks/install/installer.rb +53 -0
- data/lib/rails_git_hooks/runtime/check_registry.rb +29 -0
- data/lib/rails_git_hooks/runtime/dependency_checker.rb +51 -0
- data/lib/rails_git_hooks/runtime/file_matcher.rb +23 -0
- data/lib/rails_git_hooks/runtime/override_config.rb +125 -0
- data/lib/rails_git_hooks/runtime/policy_resolver.rb +36 -0
- data/lib/rails_git_hooks/runtime/repository.rb +61 -0
- data/lib/rails_git_hooks/runtime/runner.rb +76 -0
- data/lib/rails_git_hooks/runtime.rb +25 -0
- data/lib/rails_git_hooks/version.rb +1 -1
- data/lib/rails_git_hooks.rb +14 -6
- data/templates/hooks/commit-msg +7 -17
- data/templates/hooks/pre-commit +7 -21
- data/templates/hooks/pre-push +7 -17
- metadata +30 -9
- data/lib/rails_git_hooks/constants.rb +0 -21
- data/lib/rails_git_hooks/installer.rb +0 -156
- data/templates/shared/commit_msg/jira_prefix.rb +0 -20
- data/templates/shared/pre_commit/debugger_check.rb +0 -48
- data/templates/shared/pre_commit/default_branch.rb +0 -9
- data/templates/shared/pre_commit/rubocop_check.rb +0 -24
- data/templates/shared/pre_commit/whitespace_check.rb +0 -25
- 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
|
data/lib/rails_git_hooks/cli.rb
CHANGED
|
@@ -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 '
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
if
|
|
52
|
-
warn 'Usage: rails_git_hooks disable
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
puts "Disabled: #{
|
|
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
|
-
|
|
68
|
-
if
|
|
69
|
-
warn 'Usage: rails_git_hooks enable
|
|
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
|
-
|
|
83
|
-
|
|
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
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
127
|
-
rails_git_hooks disable
|
|
128
|
-
rails_git_hooks
|
|
129
|
-
rails_git_hooks
|
|
130
|
-
rails_git_hooks
|
|
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,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
|