rails_git_hooks 0.7.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b95455f85a5e5f3789055e84fc610dcfa199f593d3fc34e5dfb94b781b0ad050
4
- data.tar.gz: d2f37971fb832ba3c0e2b3651c7d27b4ca8a7665d6fdac879a43eac8ca38b022
3
+ metadata.gz: 8f679908a499fe6aed2f61cf433d27bf93042bf0dcf1ecfe2e4854f05b0e7b9e
4
+ data.tar.gz: 481df290f21058504a14d0b6995854861ed7d15099b40a34ec9ccdf6f18fcf3c
5
5
  SHA512:
6
- metadata.gz: 93b5d01ffd6b14ed1d94543a41e95286afbcaa1f808d9c1d4045dd193e9de3a9f02b3b8a5d5374eadaea450028b28ee89a09c1af20869e30cd8cdeeb8620f259
7
- data.tar.gz: 549b45c9aeb5f9607c5aa779d4fdf71196d7d08525f79cfde5e3bf2c3a7df107aa34e397258d4218e45a026e6ed474ab6bb4fc906174af0876eb61ba5cd4e44b
6
+ metadata.gz: 63518e1bcbca51fc17881473b90a0eb510eb2a6e62b26e6471e8890f8fd4e702392b6401f92124d4cd9cc1d398ad0aa03b3c9306a4ce34dc40313d6ab028da52
7
+ data.tar.gz: c549c30ca8f646f65bdf10a7281e0f2412bb8a796e0665a80e68c05cdac6ed0a39aed58f48011e23b3eba4622db43b6764bae103e434170a601938f4e5f8291e
data/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
 
7
+ ## [0.7.2]
8
+
9
+ ### Added
10
+
11
+ - **post-checkout hook** — Runs after branch checkout. Includes `bundle-install` (run `bundle install` when Gemfile/Gemfile.lock changed) and `db-migrate` (run `rails db:migrate` when migrations or schema changed). Both enabled by default.
12
+ - **post-merge hook** — Runs after merge. Same checks as post-checkout: `bundle-install` and `db-migrate`, enabled by default. Keeps the app bundled and migrated when pulling or merging (inspired by [hookup](https://rubygems.org/gems/hookup)).
13
+ - **Shared check modules** — `checks/shared/bundle_install_check.rb` and `checks/shared/db_migrate_check.rb` hold common logic and options for the post-checkout and post-merge bundle/migrate checks.
14
+ - **Specs for every check** — RSpec examples for all 13 checks (pre-commit, commit-msg, pre-push, post-checkout, post-merge) under `spec/rails_git_hooks/checks/`.
15
+
16
+ ### Changed
17
+
18
+ - **Default install** — With no config file, `install` now installs **commit-msg**, **pre-commit**, **post-checkout**, and **post-merge** (post-checkout/post-merge bundle and db-migrate checks are on by default).
19
+ - **Runner** — `modified_files` now supports `post_checkout` (files changed between refs on branch checkout) and `post_merge` (files changed between ORIG_HEAD and HEAD). Repository adds `changed_files(ref1, ref2)`.
20
+ - **defaults.yml** — PostCheckout and PostMerge sections: `BundleInstall` and `DbMigrate` set to `enabled: true` by default.
21
+
7
22
  ## [0.7.1]
8
23
 
9
24
  ### Added
data/README.md CHANGED
@@ -4,45 +4,51 @@
4
4
  [![Build Status](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/tests.yml?query=branch%3Amain)
5
5
  [![RuboCop](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/rubocop.yml/badge.svg?branch=main)](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/rubocop.yml?query=branch%3Amain)
6
6
 
7
- Git hooks for Rails and Ruby projects with built-in defaults, optional sparse overrides, and Overcommit-style per-check policies.
8
-
9
- ## What changed
10
-
11
- The project now uses a **code-first hook runtime**:
12
-
13
- - checks declare their defaults in Ruby
14
- - hook scripts are thin bootstraps
15
- - `.rails_git_hooks.yml` is optional and contains only overrides
16
- - checks can be configured with `enabled`, `quiet`, `on_fail`, `on_warn`, `on_missing_dependency`, `include`, and `exclude`
17
-
18
- ## Included hooks
19
-
20
- | Git hook | Purpose |
21
- |---|---|
22
- | `commit-msg` | Prefix commit messages with Jira-style ticket IDs from the current branch |
23
- | `pre-commit` | Run commit-time checks like branch protection, debugger detection, format validation, and optional code quality checks |
24
- | `pre-push` | Run the test suite before push |
7
+ Git hooks for Rails and Ruby projects: sensible defaults out of the box, optional YAML overrides, and per-check policies (enable/disable, fail vs warn, include/exclude).
8
+
9
+ ## Features
10
+
11
+ - **Code-first runtime** Checks are Ruby classes; hook scripts are thin bootstraps that delegate to a shared runtime.
12
+ - **Sparse config** — Defaults live in the gem ([config/defaults.yml](https://github.com/NikitaNazarov1/rails_git_hooks/blob/main/lib/rails_git_hooks/config/defaults.yml)). But you can redefine them via your own yml config file in a root repository.
13
+ - **Flexible configuration options** Per check: `enabled`, `quiet`, `on_fail`, `on_warn`, `on_missing_dependency`, `include`, `exclude`, `command`.
14
+ - **Dependency handling** — Checks declare executables/libraries; missing deps are handled by a single policy (`on_missing_dependency`).
15
+
16
+ ## Included hooks and checks
17
+
18
+ | Hook | Check key | Enabled By Default | Description |
19
+ |-------------|----------------------|---------|-------------|
20
+ | **commit-msg** | `jira-prefix` | ✅ | Prefix commit messages with Jira-style ticket IDs from the branch name (e.g. `[TICKET-123]`). |
21
+ | **pre-commit** | `default-branch` | ✅ | Block commits on `master` / `main`; prompt to use a feature branch. |
22
+ | **pre-commit** | `debugger-check` | | Warn (or fail) on debugger statements in Ruby, JavaScript/TypeScript, and Python. |
23
+ | **pre-commit** | `yaml-format-check` | ✅ | Warn on invalid `.yml` / `.yaml` files. |
24
+ | **pre-commit** | `json-format-check` | | Warn on invalid `.json` files. |
25
+ | **pre-commit** | `migrations-check` | ✅ | Warn when migration files are staged but schema/data_schema files are not. |
26
+ | **pre-commit** | `whitespace-check` | Off | Fail on trailing whitespace and merge conflict markers. |
27
+ | **pre-commit** | `rubocop-check` | Off | Run RuboCop on staged Ruby files (requires `rubocop` in the project). |
28
+ | **pre-push** | `run-tests` | Off | Run test suite before push (default: `bundle exec rspec`). Enable in config to install pre-push. |
29
+ | **post-checkout** | `bundle-install` | ✅ | Run `bundle install` when Gemfile or Gemfile.lock changed after a branch checkout. |
30
+ | **post-checkout** | `db-migrate` | ✅ | Run `rails db:migrate` when migrations or schema changed after a branch checkout. |
31
+ | **post-merge** | `bundle-install` | ✅ | Run `bundle install` when Gemfile or Gemfile.lock changed after a merge. |
32
+ | **post-merge** | `db-migrate` | ✅ | Run `rails db:migrate` when migrations or schema changed after a merge. |
25
33
 
26
34
  ## Quick start
27
35
 
28
- ### 1. Install the gem
29
-
30
- ```bash
31
- gem install rails_git_hooks
32
- ```
36
+ ### 1. Add the gem
33
37
 
34
- Or add it to your `Gemfile`:
38
+ **Gemfile:**
35
39
 
36
40
  ```ruby
37
41
  gem "rails_git_hooks"
38
42
  ```
39
43
 
40
- Then install dependencies:
44
+ Then:
41
45
 
42
46
  ```bash
43
47
  bundle install
44
48
  ```
45
49
 
50
+ Or install globally: `gem install rails_git_hooks`.
51
+
46
52
  ### 2. Install hooks
47
53
 
48
54
  From your project root:
@@ -51,53 +57,25 @@ From your project root:
51
57
  bundle exec rails_git_hooks install
52
58
  ```
53
59
 
54
- This installs `commit-msg` and `pre-commit` by default.
55
-
56
- ### 3. Inspect available checks
57
-
58
- ```bash
59
- bundle exec rails_git_hooks list
60
- ```
61
-
62
- ### 4. Override behavior only when needed
63
-
64
- Enable RuboCop:
65
-
66
- ```bash
67
- bundle exec rails_git_hooks enable rubocop-check
68
- ```
69
-
70
- Make debugger statements fail instead of warn:
60
+ With default config this installs **commit-msg**, **pre-commit**, **post-checkout**, and **post-merge** into `.git/hooks/` and copies the runtime there (pre-push is off by default; enable `run-tests` in config to add it).
71
61
 
72
- ```bash
73
- bundle exec rails_git_hooks set debugger-check on_fail fail
74
- ```
62
+ ## Configuration
75
63
 
76
- Disable migrations warnings:
64
+ ### Priority (low → high)
77
65
 
78
- ```bash
79
- bundle exec rails_git_hooks disable migrations-check
80
- ```
66
+ 1. **Built-in defaults** (in the gem’s `config/defaults.yml`)
67
+ 2. **`.rails_git_hooks.yml`** in the repo root (sparse overrides; commit this)
68
+ 3. **`.rails_git_hooks.local.yml`** in the repo root (optional; overrides the above, typically gitignored)
81
69
 
82
- Show effective config:
83
-
84
- ```bash
85
- bundle exec rails_git_hooks show-config
86
- ```
87
-
88
- ## Config model
89
-
90
- You do **not** need a config file for the defaults to work.
91
-
92
- If you want overrides, create one:
70
+ **Create the main override file:**
93
71
 
94
72
  ```bash
95
73
  bundle exec rails_git_hooks init
96
74
  ```
97
75
 
98
- This creates `.rails_git_hooks.yml`. The file is sparse: only your changes are stored there.
76
+ **Optional:** Create `.rails_git_hooks.local.yml` in the repo root for personal overrides (merged on top of `.rails_git_hooks.yml`). Add it to `.gitignore` if you don’t want to commit it.
99
77
 
100
- Example:
78
+ ### Example override file
101
79
 
102
80
  ```yaml
103
81
  PreCommit:
@@ -114,116 +92,29 @@ PreCommit:
114
92
  - "db/schema.rb"
115
93
  ```
116
94
 
117
- ### Supported per-check options
118
-
119
- | Option | Meaning |
120
- |---|---|
121
- | `enabled` | Enable or disable a check |
122
- | `quiet` | Hide normal output unless the check warns or fails |
123
- | `on_fail` | Map a check failure to `fail`, `warn`, or `pass` |
124
- | `on_warn` | Map a warning to `warn`, `fail`, or `pass` |
125
- | `on_missing_dependency` | Control behavior when required tools/libraries are missing |
126
- | `include` | File paths or glob patterns that the check should apply to |
127
- | `exclude` | File paths or glob patterns to remove from the included set |
128
- | `command` | Override the command used by command-based checks |
95
+ ### Per-check options
129
96
 
130
- ## Dependency model
131
-
132
- Checks can declare dependencies such as:
133
-
134
- - executables like `bundle`
135
- - Ruby libraries like `rubocop`
136
- - required files
137
-
138
- If a dependency is missing, the runner produces a structured result and applies the check’s `on_missing_dependency` policy. This makes missing-tool behavior consistent across all checks.
139
-
140
- ## Default checks
141
-
142
- ### `commit-msg`
143
-
144
- - `jira-prefix`
145
- - enabled by default
146
- - prefixes commit messages with `[TICKET-123]` when the branch name contains a Jira-style ticket ID
147
-
148
- ### `pre-commit`
149
-
150
- - `default-branch`
151
- - enabled by default
152
- - fails on `master` / `main`
153
-
154
- - `debugger-check`
155
- - enabled by default
156
- - warns on debugger statements in Ruby, JavaScript/TypeScript, and Python
157
-
158
- - `yaml-format-check`
159
- - enabled by default
160
- - warns on invalid `.yml` / `.yaml`
161
-
162
- - `json-format-check`
163
- - enabled by default
164
- - warns on invalid `.json`
165
-
166
- - `migrations-check`
167
- - enabled by default
168
- - warns when schema/data schema files appear to be missing after migrations
169
-
170
- - `whitespace-check`
171
- - disabled by default
172
- - fails on trailing whitespace and merge conflict markers
173
-
174
- - `rubocop-check`
175
- - disabled by default
176
- - runs RuboCop on staged Ruby files
177
- - default dependency behavior warns if `rubocop` is missing
178
-
179
- ### `pre-push`
180
-
181
- - `run-tests`
182
- - enabled by default when `pre-push` is installed
183
- - runs `bundle exec rspec`
97
+ | Option | Description |
98
+ |--------|--------------|
99
+ | `enabled` | Turn the check on or off. |
100
+ | `quiet` | Suppress normal output unless the check warns or fails. |
101
+ | `on_fail` | `fail`, `warn`, or `pass` when the check fails. |
102
+ | `on_warn` | `warn`, `fail`, or `pass` when the check would warn. |
103
+ | `on_missing_dependency` | Behavior when a required executable/library is missing. |
104
+ | `include` | Glob patterns for files the check applies to. |
105
+ | `exclude` | Glob patterns to exclude from `include`. |
106
+ | `command` | Override the command for checks that run external commands. |
184
107
 
185
108
  ## CLI reference
186
109
 
187
110
  | Command | Description |
188
- |---|---|
189
- | `rails_git_hooks install [HOOK...]` | Install hook scripts into `.git/hooks` |
190
- | `rails_git_hooks list` | List available git hooks and check keys |
191
- | `rails_git_hooks init` | Create `.rails_git_hooks.yml` |
192
- | `rails_git_hooks enable CHECK_NAME` | Set `enabled: true` for a check override |
193
- | `rails_git_hooks disable CHECK_NAME` | Set `enabled: false` for a check override |
194
- | `rails_git_hooks set CHECK_NAME OPTION VALUE` | Set a single override option |
195
- | `rails_git_hooks show-config` | Print effective merged configuration |
196
-
197
- Examples:
198
-
199
- ```bash
200
- rails_git_hooks install
201
- rails_git_hooks install pre-push
202
- rails_git_hooks enable rubocop-check
203
- rails_git_hooks disable migrations-check
204
- rails_git_hooks set debugger-check on_fail fail
205
- rails_git_hooks set rubocop-check quiet true
206
- rails_git_hooks show-config
207
- ```
208
-
209
- ## Manual installation
111
+ |---------|--------------|
112
+ | `rails_git_hooks install` | Install hooks that have at least one enabled check in the merged config (defaults + .rails_git_hooks.yml + .rails_git_hooks.local.yml). |
113
+ | `rails_git_hooks init` | Create an empty `.rails_git_hooks.yml`. |
210
114
 
211
- From your project (with the gem in the Gemfile or installed), run:
115
+ ## Contributing
212
116
 
213
- ```bash
214
- bundle exec rails_git_hooks install
215
- ```
216
-
217
- This installs the thin hook bootstraps and the embedded `rails_git_hooks` runtime into `.git/hooks/`.
218
-
219
- ## Development
220
-
221
- ```bash
222
- bundle install
223
- bundle exec rake # run specs
224
- bundle exec rake build # build the gem
225
- bundle exec rake install # install locally
226
- ```
117
+ Contributions are welcome. Open an [issue](https://github.com/NikitaNazarov1/rails_git_hooks/issues) for bugs or ideas, or submit a pull request.
227
118
 
228
119
  ## License
229
120
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostCheckout
6
+ class BundleInstall < Base
7
+ include BundleInstallCheck
8
+
9
+ check_definition key: 'bundle-install',
10
+ hook: :post_checkout,
11
+ description: 'Run bundle install when Gemfile or Gemfile.lock changed (branch checkout)',
12
+ **BundleInstallCheck::DEFINITION_OPTIONS
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostCheckout
6
+ class DbMigrate < Base
7
+ include DbMigrateCheck
8
+
9
+ check_definition key: 'db-migrate',
10
+ hook: :post_checkout,
11
+ description: 'Run db:migrate when migrations or schema changed (branch checkout)',
12
+ **DbMigrateCheck::DEFINITION_OPTIONS
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostCheckout
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'post_checkout/bundle_install'
11
+ require_relative 'post_checkout/db_migrate'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostMerge
6
+ class BundleInstall < Base
7
+ include BundleInstallCheck
8
+
9
+ check_definition key: 'bundle-install',
10
+ hook: :post_merge,
11
+ description: 'Run bundle install when Gemfile or Gemfile.lock changed (after merge)',
12
+ **BundleInstallCheck::DEFINITION_OPTIONS
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostMerge
6
+ class DbMigrate < Base
7
+ include DbMigrateCheck
8
+
9
+ check_definition key: 'db-migrate',
10
+ hook: :post_merge,
11
+ description: 'Run db:migrate when migrations or schema changed (after merge)',
12
+ **DbMigrateCheck::DEFINITION_OPTIONS
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitHooks
4
+ module Checks
5
+ module PostMerge
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'post_merge/bundle_install'
11
+ require_relative 'post_merge/db_migrate'
@@ -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
@@ -4,3 +4,7 @@ require_relative 'checks/base'
4
4
  require_relative 'checks/pre_commit'
5
5
  require_relative 'checks/commit_msg'
6
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'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_git_hooks'
4
- require 'yaml'
5
4
 
6
5
  module GitHooks
7
6
  class CLI
@@ -10,22 +9,13 @@ module GitHooks
10
9
  end
11
10
 
12
11
  def run(argv)
13
- case argv[0]
12
+ cmd = argv[0].to_s.strip.tr('_', '-')
13
+ case cmd
14
14
  when 'install'
15
- run_install(argv[1..])
16
- when 'list'
17
- run_list
15
+ run_install
18
16
  when 'init'
19
17
  run_init
20
- when 'disable'
21
- run_disable(argv[1..])
22
- when 'enable'
23
- run_enable(argv[1..])
24
- when 'set'
25
- run_set(argv[1..])
26
- when 'show-config'
27
- run_show_config
28
- when nil, '-h', '--help'
18
+ when '', '-h', '--help'
29
19
  print_help
30
20
  else
31
21
  warn "Unknown command: #{argv[0]}"
@@ -36,118 +26,36 @@ module GitHooks
36
26
 
37
27
  private
38
28
 
39
- def run_install(args)
29
+ def run_install
40
30
  installer = Installer.new
41
- installed = installer.install(*args)
31
+ installed = installer.install
42
32
  puts "Installed hooks: #{installed.join(', ')}"
43
33
  rescue GitHooks::Error => e
44
34
  warn "Error: #{e.message}"
45
35
  exit 1
46
36
  end
47
37
 
48
- def run_list
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
38
  def run_init
71
39
  config = OverrideConfig.new(repo: Repository.new)
72
40
  config.init
73
41
  puts "Initialized #{Constants::CONFIG_FILE}"
74
42
  end
75
43
 
76
- def run_disable(args)
77
- key = args.first
78
- if key.nil?
79
- warn 'Usage: rails_git_hooks disable CHECK_NAME'
80
- exit 1
81
- end
82
-
83
- repo = Repository.new
84
- definition = CheckRegistry.find!(key)
85
- OverrideConfig.new(repo: repo).set_option(definition, 'enabled', false)
86
- puts "Disabled: #{key}"
87
- end
88
-
89
- def run_enable(args)
90
- key = args.first
91
- if key.nil?
92
- warn 'Usage: rails_git_hooks enable CHECK_NAME'
93
- exit 1
94
- end
95
-
96
- repo = Repository.new
97
- definition = CheckRegistry.find!(key)
98
- OverrideConfig.new(repo: repo).set_option(definition, 'enabled', true)
99
- puts "Enabled: #{key}"
100
- end
101
-
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}"
112
- end
113
-
114
- def run_show_config
115
- repo = Repository.new
116
- config = OverrideConfig.new(repo: repo).effective_config(CheckRegistry.all)
117
- puts YAML.dump(config)
118
- end
119
-
120
44
  def print_help
121
45
  puts <<~HELP
122
46
  rails_git_hooks - Install configurable Git hooks
123
47
 
124
48
  Usage:
125
- rails_git_hooks install [HOOK...]
126
- rails_git_hooks list
49
+ rails_git_hooks install
127
50
  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
132
51
  rails_git_hooks --help
133
52
 
134
53
  Commands:
135
- install Install hooks into current repo's .git/hooks.
136
- list List available hooks and checks.
54
+ install Install hooks that have at least one enabled check (merged config: defaults + .rails_git_hooks.yml + .rails_git_hooks.local.yml).
137
55
  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.
142
56
 
143
57
  Examples:
144
58
  rails_git_hooks install
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
150
- rails_git_hooks install commit-msg pre-commit
151
59
  HELP
152
60
  end
153
61
  end
@@ -8,13 +8,19 @@ module GitHooks
8
8
  RUNTIME_SOURCE_DIR = File.expand_path('lib/rails_git_hooks', GEM_ROOT).freeze
9
9
  RUNTIME_DIR_NAME = 'rails_git_hooks'
10
10
  CONFIG_FILE = '.rails_git_hooks.yml'
11
+ CONFIG_FILE_LOCAL = '.rails_git_hooks.local.yml'
11
12
 
12
13
  # Default hooks when install is run with no arguments.
13
14
  DEFAULT_HOOKS = %w[commit-msg pre-commit].freeze
14
15
  HOOK_CONFIG_NAMES = {
15
16
  pre_commit: 'PreCommit',
16
17
  commit_msg: 'CommitMsg',
17
- pre_push: 'PrePush'
18
+ pre_push: 'PrePush',
19
+ post_checkout: 'PostCheckout',
20
+ post_merge: 'PostMerge'
18
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
19
25
  end
20
26
  end
@@ -112,7 +112,7 @@ CommitMsg:
112
112
 
113
113
  PrePush:
114
114
  RunTests:
115
- enabled: true
115
+ enabled: false
116
116
  quiet: false
117
117
  on_fail: fail
118
118
  on_warn: warn
@@ -121,7 +121,70 @@ PrePush:
121
121
  exclude: []
122
122
  dependencies:
123
123
  executables: [bundle]
124
+ libraries: [rspec]
124
125
  command: [bundle, exec, rspec]
125
126
  description: Run test suite before push
126
127
  file_based: false
127
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)
@@ -5,19 +5,23 @@ require 'fileutils'
5
5
  module GitHooks
6
6
  class Installer
7
7
  def initialize(git_dir: nil)
8
- @git_dir = git_dir || Repository.new.git_dir
8
+ if git_dir
9
+ @git_dir = git_dir
10
+ @repo = nil
11
+ else
12
+ @repo = Repository.new
13
+ @git_dir = @repo.git_dir
14
+ end
9
15
  end
10
16
 
11
- def install(*hook_names)
17
+ def install
12
18
  target_dir = File.join(@git_dir, 'hooks')
13
19
  raise GitHooks::Error, "Not a git repository or .git/hooks not found: #{@git_dir}" unless Dir.exist?(target_dir)
14
20
 
15
21
  copy_runtime(target_dir)
16
22
 
17
- hooks = hook_names.empty? ? Constants::DEFAULT_HOOKS : hook_names
23
+ hooks = hooks_enabled_in_config.select { |name| self.class.available_hook_names.include?(name) }
18
24
  hooks.each_with_object([]) do |name, installed|
19
- next unless self.class.available_hook_names.include?(name)
20
-
21
25
  dest = File.join(target_dir, name)
22
26
  File.write(dest, File.read(File.join(Constants::HOOKS_DIR, name)))
23
27
  File.chmod(0o755, dest)
@@ -35,6 +39,28 @@ module GitHooks
35
39
 
36
40
  private
37
41
 
42
+ def repo_for_config
43
+ @repo_for_config ||= begin
44
+ root = File.dirname(@git_dir)
45
+ Struct.new(:root, :config_path, :local_config_path).new(
46
+ root,
47
+ File.join(root, Constants::CONFIG_FILE),
48
+ File.join(root, Constants::CONFIG_FILE_LOCAL)
49
+ )
50
+ end
51
+ end
52
+
53
+ def hooks_enabled_in_config
54
+ override_config = OverrideConfig.new(repo: repo_for_config)
55
+ effective = override_config.effective_config(CheckRegistry.all)
56
+ effective.each_with_object([]) do |(section_name, check_configs), out|
57
+ hook_name = Constants::SECTION_TO_HOOK[section_name]
58
+ next unless hook_name && self.class.available_hook_names.include?(hook_name)
59
+
60
+ out << hook_name if check_configs.values.any? { |cfg| cfg['enabled'] == true }
61
+ end.uniq
62
+ end
63
+
38
64
  def copy_runtime(target_dir)
39
65
  runtime_dir = File.join(target_dir, Constants::RUNTIME_DIR_NAME)
40
66
  FileUtils.rm_rf(runtime_dir)
@@ -11,7 +11,11 @@ module GitHooks
11
11
  Checks::PreCommit::WhitespaceCheck,
12
12
  Checks::PreCommit::RuboCop,
13
13
  Checks::CommitMsg::JiraPrefix,
14
- Checks::PrePush::RunTests
14
+ Checks::PrePush::RunTests,
15
+ Checks::PostCheckout::BundleInstall,
16
+ Checks::PostCheckout::DbMigrate,
17
+ Checks::PostMerge::BundleInstall,
18
+ Checks::PostMerge::DbMigrate
15
19
  ].freeze
16
20
 
17
21
  def self.all
@@ -15,9 +15,9 @@ module GitHooks
15
15
  end
16
16
 
17
17
  def load
18
- return {} unless File.exist?(@repo.config_path)
19
-
20
- deep_stringify(YAML.safe_load(File.read(@repo.config_path), aliases: true) || {})
18
+ main = load_file(@repo.config_path)
19
+ local = load_file(@repo.local_config_path)
20
+ deep_merge(main, local)
21
21
  end
22
22
 
23
23
  def config_for(definition)
@@ -35,7 +35,7 @@ module GitHooks
35
35
  end
36
36
 
37
37
  def set_option(definition, option, value)
38
- data = load
38
+ data = load_file(@repo.config_path)
39
39
  section = data[definition.hook_section] ||= {}
40
40
  section[definition.config_name] ||= {}
41
41
 
@@ -69,6 +69,12 @@ module GitHooks
69
69
 
70
70
  private
71
71
 
72
+ def load_file(path)
73
+ return {} unless File.exist?(path)
74
+
75
+ deep_stringify(YAML.safe_load(File.read(path), aliases: true) || {})
76
+ end
77
+
72
78
  def write(data)
73
79
  if data.empty?
74
80
  FileUtils.rm_f(@repo.config_path)
@@ -14,6 +14,10 @@ module GitHooks
14
14
  File.join(root, Constants::CONFIG_FILE)
15
15
  end
16
16
 
17
+ def local_config_path
18
+ File.join(root, Constants::CONFIG_FILE_LOCAL)
19
+ end
20
+
17
21
  def hook_runtime_dir(hooks_dir)
18
22
  File.join(hooks_dir, Constants::RUNTIME_DIR_NAME)
19
23
  end
@@ -38,6 +42,10 @@ module GitHooks
38
42
  git_output('diff', '--cached', '--name-only').split("\n").map(&:strip).reject(&:empty?)
39
43
  end
40
44
 
45
+ def changed_files(ref1, ref2)
46
+ git_output('diff', '--name-only', ref1.to_s, ref2.to_s).split("\n").map(&:strip).reject(&:empty?)
47
+ end
48
+
41
49
  private
42
50
 
43
51
  def resolve_paths(start_dir)
@@ -52,6 +52,10 @@ module GitHooks
52
52
  def modified_files
53
53
  @modified_files ||= case @hook_name
54
54
  when :pre_commit then @repo.staged_files
55
+ when :post_checkout
56
+ argv[2] == '1' ? @repo.changed_files(argv[0], argv[1]) : []
57
+ when :post_merge
58
+ @repo.changed_files('ORIG_HEAD', 'HEAD')
55
59
  else []
56
60
  end
57
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitHooks
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.2'
5
5
  end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ hooks_dir = File.dirname(File.expand_path(__FILE__))
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError, StandardError
8
+ nil
9
+ end
10
+
11
+ require File.join(hooks_dir, 'rails_git_hooks', 'runtime')
12
+
13
+ exit GitHooks::Runtime.execute(:post_checkout, argv: ARGV, stdin: $stdin.read)
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ hooks_dir = File.dirname(File.expand_path(__FILE__))
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError, StandardError
8
+ nil
9
+ end
10
+
11
+ require File.join(hooks_dir, 'rails_git_hooks', 'runtime')
12
+
13
+ exit GitHooks::Runtime.execute(:post_merge, argv: ARGV, stdin: $stdin.read)
metadata CHANGED
@@ -1,56 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_git_hooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Nazarov
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: rake
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '13.0'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '13.0'
26
- - !ruby/object:Gem::Dependency
27
- name: rspec
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '3.0'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3.0'
40
- - !ruby/object:Gem::Dependency
41
- name: rubocop
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '1.0'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '1.0'
11
+ dependencies: []
54
12
  description: Installs most useful git hooks for Rails and Ruby projects
55
13
  email:
56
14
  - nikenor11@gmail.com
@@ -68,6 +26,12 @@ files:
68
26
  - lib/rails_git_hooks/checks/base.rb
69
27
  - lib/rails_git_hooks/checks/commit_msg.rb
70
28
  - lib/rails_git_hooks/checks/commit_msg/jira_prefix.rb
29
+ - lib/rails_git_hooks/checks/post_checkout.rb
30
+ - lib/rails_git_hooks/checks/post_checkout/bundle_install.rb
31
+ - lib/rails_git_hooks/checks/post_checkout/db_migrate.rb
32
+ - lib/rails_git_hooks/checks/post_merge.rb
33
+ - lib/rails_git_hooks/checks/post_merge/bundle_install.rb
34
+ - lib/rails_git_hooks/checks/post_merge/db_migrate.rb
71
35
  - lib/rails_git_hooks/checks/pre_commit.rb
72
36
  - lib/rails_git_hooks/checks/pre_commit/debugger_check.rb
73
37
  - lib/rails_git_hooks/checks/pre_commit/default_branch.rb
@@ -78,6 +42,8 @@ files:
78
42
  - lib/rails_git_hooks/checks/pre_commit/yaml_format_check.rb
79
43
  - lib/rails_git_hooks/checks/pre_push.rb
80
44
  - lib/rails_git_hooks/checks/pre_push/run_tests.rb
45
+ - lib/rails_git_hooks/checks/shared/bundle_install_check.rb
46
+ - lib/rails_git_hooks/checks/shared/db_migrate_check.rb
81
47
  - lib/rails_git_hooks/cli.rb
82
48
  - lib/rails_git_hooks/config/constants.rb
83
49
  - lib/rails_git_hooks/config/defaults.yml
@@ -96,6 +62,8 @@ files:
96
62
  - lib/rails_git_hooks/runtime/runner.rb
97
63
  - lib/rails_git_hooks/version.rb
98
64
  - templates/hooks/commit-msg
65
+ - templates/hooks/post-checkout
66
+ - templates/hooks/post-merge
99
67
  - templates/hooks/pre-commit
100
68
  - templates/hooks/pre-push
101
69
  homepage: https://github.com/NikitaNazarov1/rails_git_hooks