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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b95455f85a5e5f3789055e84fc610dcfa199f593d3fc34e5dfb94b781b0ad050
|
|
4
|
+
data.tar.gz: d2f37971fb832ba3c0e2b3651c7d27b4ca8a7665d6fdac879a43eac8ca38b022
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93b5d01ffd6b14ed1d94543a41e95286afbcaa1f808d9c1d4045dd193e9de3a9f02b3b8a5d5374eadaea450028b28ee89a09c1af20869e30cd8cdeeb8620f259
|
|
7
|
+
data.tar.gz: 549b45c9aeb5f9607c5aa779d4fdf71196d7d08525f79cfde5e3bf2c3a7df107aa34e397258d4218e45a026e6ed474ab6bb4fc906174af0876eb61ba5cd4e44b
|
data/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,27 @@ 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.
|
|
7
|
+
## [0.7.1]
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
-
- **
|
|
11
|
+
- **Code-first runtime architecture** — Hook behavior now lives in Ruby classes with a central runner, registry, policy resolver, dependency checker, and sparse override config loader.
|
|
12
|
+
- **Sparse override config** — Added optional `.rails_git_hooks.yml` with Overcommit-style per-check overrides for `enabled`, `quiet`, `on_fail`, `on_warn`, `on_missing_dependency`, `include`, `exclude`, and `command`.
|
|
13
|
+
- **Thin hook bootstraps** — `templates/hooks/*` now dispatch into the embedded `rails_git_hooks` runtime instead of loading copied script files per check.
|
|
14
|
+
- **Pre-commit YAML format check** — Warns (does not block) when any staged `.yml` or `.yaml` file fails to parse (invalid YAML). Reports file and line from the parser. On by default with pre-commit.
|
|
15
|
+
- **Pre-commit JSON format check** — Warns (does not block) when any staged `.json` file fails to parse (invalid JSON). Reports file and parser message. On by default with pre-commit.
|
|
16
|
+
- **Pre-commit migrations check** — Warns (does not block) when: (a) migration file(s) are staged but neither `db/schema.rb` nor `db/structure.sql` is staged; (b) data migration file(s) in `db/data/` or `db/data_migrate/` are staged but `db/data_schema.rb` is not. **On by default.** Disable with `rails_git_hooks disable migrations-check`.
|
|
17
|
+
- **Defaults YAML** — `lib/rails_git_hooks/config/defaults.yml` holds default settings for every check and hook. `DefaultsLoader` reads it; `OverrideConfig` uses it as the base when merging with `.rails_git_hooks.yml`. Edit the YAML to change built-in defaults.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **CLI redesign** — Replaced per-check flag-file commands with generic config-driven commands: `install`, `list`, `init`, `enable`, `disable`, `set`, and `show-config`.
|
|
22
|
+
- **Manual install layout** — Manual install is via `bundle exec rails_git_hooks install` only; dropped `rake sync_hooks` and the repo `hooks/` tree.
|
|
23
|
+
- **Dependency handling** — Checks can now declare executables, Ruby libraries, files, and install hints, with centralized `on_missing_dependency` policy handling.
|
|
24
|
+
- **Runtime install** — Installer copies all runtime files (including `config/defaults.yml`), not only `*.rb`, into `.git/hooks/rails_git_hooks/`.
|
|
25
|
+
- **Removed `hooks/` directory** — `/hooks/` added to `.gitignore`. README manual-install section updated.
|
|
26
|
+
|
|
27
|
+
## [0.7.0]
|
|
12
28
|
|
|
13
29
|
### Changed
|
|
14
30
|
|
|
@@ -16,10 +32,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
16
32
|
- **Constants:** Extracted `lib/rails_git_hooks/constants.rb` (GEM_ROOT, HOOKS_DIR, DEFAULT_HOOKS, FEATURE_FLAG_FILES). Installer and CLI use it. Single source of truth; no config file.
|
|
17
33
|
- Gemspec includes `templates/**/*`. Version set to 0.7.0.
|
|
18
34
|
|
|
19
|
-
### Fixed
|
|
20
|
-
|
|
21
|
-
- **Hook scripts** — `RailsGitHooks::GIT_DIR` was set inside a `module` body, where the script’s local `git_dir` is out of scope (Ruby scope gate), causing `NameError` when hooks ran. All three hooks (commit-msg, pre-commit, pre-push) now set the constant via `RailsGitHooks.const_set(:GIT_DIR, git_dir.freeze)` from the script scope.
|
|
22
|
-
|
|
23
35
|
## [0.6.1]
|
|
24
36
|
|
|
25
37
|
### Changed
|
data/README.md
CHANGED
|
@@ -4,40 +4,40 @@
|
|
|
4
4
|
[](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/tests.yml?query=branch%3Amain)
|
|
5
5
|
[](https://github.com/NikitaNazarov1/rails_git_hooks/actions/workflows/rubocop.yml?query=branch%3Amain)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Git hooks for Rails and Ruby projects with built-in defaults, optional sparse overrides, and Overcommit-style per-check policies.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## What changed
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The project now uses a **code-first hook runtime**:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| **pre-push** | Runs `bundle exec rspec` before push and blocks push if tests fail. |
|
|
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`
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
- **Optional:** `pre-push`, RuboCop, and whitespace/conflict checks — enable when you want them.
|
|
18
|
+
## Included hooks
|
|
21
19
|
|
|
22
|
-
|
|
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 |
|
|
23
25
|
|
|
24
26
|
## Quick start
|
|
25
27
|
|
|
26
28
|
### 1. Install the gem
|
|
27
29
|
|
|
28
|
-
**Standalone:**
|
|
29
|
-
|
|
30
30
|
```bash
|
|
31
31
|
gem install rails_git_hooks
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Or add it to your `Gemfile`:
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
37
|
gem "rails_git_hooks"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Then:
|
|
40
|
+
Then install dependencies:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
bundle install
|
|
@@ -45,107 +45,176 @@ bundle install
|
|
|
45
45
|
|
|
46
46
|
### 2. Install hooks
|
|
47
47
|
|
|
48
|
-
From your project root
|
|
48
|
+
From your project root:
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
bundle exec rails_git_hooks install
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
This installs
|
|
54
|
+
This installs `commit-msg` and `pre-commit` by default.
|
|
55
55
|
|
|
56
|
-
### 3.
|
|
56
|
+
### 3. Inspect available checks
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
bundle exec rails_git_hooks list
|
|
60
|
+
```
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
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:
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
rails_git_hooks
|
|
72
|
+
```bash
|
|
73
|
+
bundle exec rails_git_hooks set debugger-check on_fail fail
|
|
67
74
|
```
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
Disable migrations warnings:
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
```bash
|
|
79
|
+
bundle exec rails_git_hooks disable migrations-check
|
|
80
|
+
```
|
|
72
81
|
|
|
73
|
-
|
|
82
|
+
Show effective config:
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
```bash
|
|
85
|
+
bundle exec rails_git_hooks show-config
|
|
86
|
+
```
|
|
76
87
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| `rails_git_hooks list` | List available hook names. |
|
|
81
|
-
| `rails_git_hooks disable HOOK [...]` | Disable hooks or options (use `*` for all). |
|
|
82
|
-
| `rails_git_hooks enable HOOK [...]` | Re-enable hooks or enable optional checks. |
|
|
83
|
-
| `rails_git_hooks disabled` | Show currently disabled hooks. |
|
|
88
|
+
## Config model
|
|
89
|
+
|
|
90
|
+
You do **not** need a config file for the defaults to work.
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
If you want overrides, create one:
|
|
86
93
|
|
|
87
94
|
```bash
|
|
88
|
-
|
|
89
|
-
rails_git_hooks install pre-push # add pre-push
|
|
90
|
-
rails_git_hooks disable pre-commit # turn off pre-commit temporarily
|
|
91
|
-
rails_git_hooks disable * # turn off all
|
|
92
|
-
rails_git_hooks enable pre-commit # turn pre-commit back on
|
|
93
|
-
rails_git_hooks enable rubocop-check # run RuboCop on staged .rb files
|
|
94
|
-
rails_git_hooks enable whitespace-check # reject trailing ws & conflict markers
|
|
95
|
+
bundle exec rails_git_hooks init
|
|
95
96
|
```
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
This creates `.rails_git_hooks.yml`. The file is sparse: only your changes are stored there.
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
Example:
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
```yaml
|
|
103
|
+
PreCommit:
|
|
104
|
+
DebuggerCheck:
|
|
105
|
+
on_fail: fail
|
|
106
|
+
|
|
107
|
+
RuboCop:
|
|
108
|
+
enabled: true
|
|
109
|
+
quiet: true
|
|
110
|
+
include:
|
|
111
|
+
- "app/**/*.rb"
|
|
112
|
+
- "lib/**/*.rb"
|
|
113
|
+
exclude:
|
|
114
|
+
- "db/schema.rb"
|
|
115
|
+
```
|
|
102
116
|
|
|
103
|
-
###
|
|
117
|
+
### Supported per-check options
|
|
104
118
|
|
|
105
|
-
|
|
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 |
|
|
106
129
|
|
|
107
|
-
|
|
130
|
+
## Dependency model
|
|
108
131
|
|
|
109
|
-
|
|
110
|
-
- Command: `git commit -m 'fix bug'`
|
|
111
|
-
- Result: **`[APD-1234] fix bug`**
|
|
132
|
+
Checks can declare dependencies such as:
|
|
112
133
|
|
|
113
|
-
|
|
134
|
+
- executables like `bundle`
|
|
135
|
+
- Ruby libraries like `rubocop`
|
|
136
|
+
- required files
|
|
114
137
|
|
|
115
|
-
|
|
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.
|
|
116
139
|
|
|
117
|
-
|
|
140
|
+
## Default checks
|
|
118
141
|
|
|
119
|
-
|
|
120
|
-
- **Debugger check** — Warns (does not block) when staged files contain debugger statements: Ruby (`binding.pry`, `debugger`, `byebug`, `binding.irb`), JavaScript/TypeScript (`.js`, `.jsx`, `.ts`, `.tsx` — `debugger`), Python (`breakpoint()`, `pdb.set_trace()`, `ipdb.set_trace()`).
|
|
142
|
+
### `commit-msg`
|
|
121
143
|
|
|
122
|
-
|
|
144
|
+
- `jira-prefix`
|
|
145
|
+
- enabled by default
|
|
146
|
+
- prefixes commit messages with `[TICKET-123]` when the branch name contains a Jira-style ticket ID
|
|
123
147
|
|
|
124
|
-
|
|
125
|
-
`rails_git_hooks enable rubocop-check` / `disable rubocop-check`. Requires the `rubocop` gem.
|
|
148
|
+
### `pre-commit`
|
|
126
149
|
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
- `default-branch`
|
|
151
|
+
- enabled by default
|
|
152
|
+
- fails on `master` / `main`
|
|
129
153
|
|
|
130
|
-
|
|
154
|
+
- `debugger-check`
|
|
155
|
+
- enabled by default
|
|
156
|
+
- warns on debugger statements in Ruby, JavaScript/TypeScript, and Python
|
|
131
157
|
|
|
132
|
-
|
|
158
|
+
- `yaml-format-check`
|
|
159
|
+
- enabled by default
|
|
160
|
+
- warns on invalid `.yml` / `.yaml`
|
|
133
161
|
|
|
134
|
-
|
|
162
|
+
- `json-format-check`
|
|
163
|
+
- enabled by default
|
|
164
|
+
- warns on invalid `.json`
|
|
135
165
|
|
|
136
|
-
|
|
166
|
+
- `migrations-check`
|
|
167
|
+
- enabled by default
|
|
168
|
+
- warns when schema/data schema files appear to be missing after migrations
|
|
137
169
|
|
|
138
|
-
|
|
170
|
+
- `whitespace-check`
|
|
171
|
+
- disabled by default
|
|
172
|
+
- fails on trailing whitespace and merge conflict markers
|
|
139
173
|
|
|
140
|
-
|
|
174
|
+
- `rubocop-check`
|
|
175
|
+
- disabled by default
|
|
176
|
+
- runs RuboCop on staged Ruby files
|
|
177
|
+
- default dependency behavior warns if `rubocop` is missing
|
|
141
178
|
|
|
142
|
-
|
|
143
|
-
|--------|--------|
|
|
144
|
-
| [commit-msg](hooks/commit-msg) | Jira-style ticket prefix; no config. |
|
|
145
|
-
| [pre-commit](hooks/pre-commit) | Branch protection + debugger warnings; optional RuboCop (requires `rubocop` gem). |
|
|
146
|
-
| [pre-push](hooks/pre-push) | Runs `bundle exec rspec`; edit for Minitest if needed. |
|
|
179
|
+
### `pre-push`
|
|
147
180
|
|
|
148
|
-
|
|
181
|
+
- `run-tests`
|
|
182
|
+
- enabled by default when `pre-push` is installed
|
|
183
|
+
- runs `bundle exec rspec`
|
|
184
|
+
|
|
185
|
+
## CLI reference
|
|
186
|
+
|
|
187
|
+
| 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
|
|
210
|
+
|
|
211
|
+
From your project (with the gem in the Gemfile or installed), run:
|
|
212
|
+
|
|
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/`.
|
|
149
218
|
|
|
150
219
|
## Development
|
|
151
220
|
|
|
@@ -154,11 +223,8 @@ bundle install
|
|
|
154
223
|
bundle exec rake # run specs
|
|
155
224
|
bundle exec rake build # build the gem
|
|
156
225
|
bundle exec rake install # install locally
|
|
157
|
-
bundle exec rake sync_hooks # copy templates (hooks + shared subdirs) → hooks/
|
|
158
226
|
```
|
|
159
227
|
|
|
160
|
-
---
|
|
161
|
-
|
|
162
228
|
## License
|
|
163
229
|
|
|
164
230
|
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
|
|
7
|
+
module GitHooks
|
|
8
|
+
module Checks
|
|
9
|
+
class Base
|
|
10
|
+
class << self
|
|
11
|
+
attr_reader :definition
|
|
12
|
+
|
|
13
|
+
def check_definition(key:, hook:, description:, config_name: name.split('::').last, **options)
|
|
14
|
+
@definition = CheckDefinition.new(
|
|
15
|
+
key: key,
|
|
16
|
+
config_name: config_name,
|
|
17
|
+
hook: hook,
|
|
18
|
+
klass: self,
|
|
19
|
+
description: description,
|
|
20
|
+
**options
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :config, :context
|
|
26
|
+
|
|
27
|
+
def initialize(config:, context:)
|
|
28
|
+
@config = config
|
|
29
|
+
@context = context
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def repo
|
|
35
|
+
context.fetch(:repo)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def applicable_files
|
|
39
|
+
context.fetch(:applicable_files, [])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def argv
|
|
43
|
+
context.fetch(:argv, [])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def stdin
|
|
47
|
+
context.fetch(:stdin, '')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def capture(*command)
|
|
51
|
+
Open3.capture2e(*command, chdir: repo.root)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitHooks
|
|
4
|
+
module Checks
|
|
5
|
+
module CommitMsg
|
|
6
|
+
class JiraPrefix < Base
|
|
7
|
+
TICKET_PATTERN = /([A-Z]{2,5}-\d+)/.freeze
|
|
8
|
+
|
|
9
|
+
check_definition key: 'jira-prefix',
|
|
10
|
+
hook: :commit_msg,
|
|
11
|
+
description: 'Prefix commit messages with ticket id from branch'
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
message_file = argv.first
|
|
15
|
+
return CheckResult.pass unless message_file && File.file?(message_file)
|
|
16
|
+
|
|
17
|
+
branch = repo.current_branch
|
|
18
|
+
ticket = branch[TICKET_PATTERN, 1]
|
|
19
|
+
return CheckResult.pass unless ticket
|
|
20
|
+
|
|
21
|
+
message = File.read(message_file)
|
|
22
|
+
prefix = "[#{ticket}]"
|
|
23
|
+
return CheckResult.pass if message.lstrip.start_with?(prefix)
|
|
24
|
+
|
|
25
|
+
File.write(message_file, "#{prefix} #{message}")
|
|
26
|
+
CheckResult.pass
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitHooks
|
|
4
|
+
module Checks
|
|
5
|
+
module PreCommit
|
|
6
|
+
class DebuggerCheck < Base
|
|
7
|
+
DEBUGGER_PATTERNS = {
|
|
8
|
+
'.rb' => [/\bbinding\.pry\b/, /\bbinding\.irb\b/, /\bdebugger\b/, /\bbyebug\b/],
|
|
9
|
+
'.js' => [/\bdebugger\s*;?/],
|
|
10
|
+
'.jsx' => [/\bdebugger\s*;?/],
|
|
11
|
+
'.ts' => [/\bdebugger\s*;?/],
|
|
12
|
+
'.tsx' => [/\bdebugger\s*;?/],
|
|
13
|
+
'.mjs' => [/\bdebugger\s*;?/],
|
|
14
|
+
'.cjs' => [/\bdebugger\s*;?/],
|
|
15
|
+
'.py' => [/\bbreakpoint\s*\(\s*\)/, /\bpdb\.set_trace\s*\(\s*\)/, /\bipdb\.set_trace\s*\(\s*\)/]
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
check_definition key: 'debugger-check',
|
|
19
|
+
hook: :pre_commit,
|
|
20
|
+
description: 'Warn on debugger statements',
|
|
21
|
+
file_based: true,
|
|
22
|
+
on_fail: :warn
|
|
23
|
+
|
|
24
|
+
def run
|
|
25
|
+
warnings = []
|
|
26
|
+
|
|
27
|
+
applicable_files.each do |path|
|
|
28
|
+
next unless File.file?(path)
|
|
29
|
+
|
|
30
|
+
patterns = DEBUGGER_PATTERNS[File.extname(path)]
|
|
31
|
+
next unless patterns
|
|
32
|
+
|
|
33
|
+
File.read(path).lines.each_with_index do |line, index|
|
|
34
|
+
warnings << "#{path}:#{index + 1}: debugger statement" if patterns.any? { |pattern| line.match?(pattern) }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
warnings.empty? ? CheckResult.pass : CheckResult.fail(messages: warnings.uniq)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitHooks
|
|
4
|
+
module Checks
|
|
5
|
+
module PreCommit
|
|
6
|
+
class DefaultBranch < Base
|
|
7
|
+
check_definition key: 'default-branch',
|
|
8
|
+
hook: :pre_commit,
|
|
9
|
+
description: 'Prevent commits on default branch'
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
branch = repo.current_branch
|
|
13
|
+
return CheckResult.pass unless %w[master main].include?(branch)
|
|
14
|
+
|
|
15
|
+
CheckResult.fail(messages: ["Commits on '#{branch}' are not allowed. Create a feature branch."])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitHooks
|
|
4
|
+
module Checks
|
|
5
|
+
module PreCommit
|
|
6
|
+
class JSONFormatCheck < Base
|
|
7
|
+
check_definition key: 'json-format-check',
|
|
8
|
+
hook: :pre_commit,
|
|
9
|
+
description: 'Warn on invalid JSON',
|
|
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 File.extname(path) == '.json'
|
|
19
|
+
|
|
20
|
+
JSON.parse(File.read(path))
|
|
21
|
+
rescue JSON::ParserError => e
|
|
22
|
+
warnings << "#{path}: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
warnings.empty? ? CheckResult.pass : CheckResult.fail(messages: warnings)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitHooks
|
|
4
|
+
module Checks
|
|
5
|
+
module PreCommit
|
|
6
|
+
class MigrationsCheck < Base
|
|
7
|
+
check_definition key: 'migrations-check',
|
|
8
|
+
hook: :pre_commit,
|
|
9
|
+
description: 'Warn on missing schema files after migrations',
|
|
10
|
+
file_based: true,
|
|
11
|
+
on_fail: :warn
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
messages = []
|
|
15
|
+
migration_files = applicable_files.grep(%r{\Adb/migrate/.*\.rb\z})
|
|
16
|
+
schema_staged = applicable_files.include?('db/schema.rb') || applicable_files.include?('db/structure.sql')
|
|
17
|
+
|
|
18
|
+
if migration_files.any? && !schema_staged
|
|
19
|
+
messages << 'Migration file(s) are staged but neither db/schema.rb nor db/structure.sql is staged.'
|
|
20
|
+
messages << 'Run `rails db:migrate` and add db/schema.rb (or db/structure.sql) to your commit.'
|
|
21
|
+
migration_files.each { |path| messages << "- #{path}" }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
data_migration_files = applicable_files.grep(%r{\Adb/(data/|data_migrate/).*\.rb\z})
|
|
25
|
+
data_schema_staged = applicable_files.include?('db/data_schema.rb')
|
|
26
|
+
if data_migration_files.any? && !data_schema_staged
|
|
27
|
+
messages << 'Data migration file(s) are staged but db/data_schema.rb is not staged.'
|
|
28
|
+
messages << 'Run your data migrate task and add db/data_schema.rb to your commit.'
|
|
29
|
+
data_migration_files.each { |path| messages << "- #{path}" }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
messages.empty? ? CheckResult.pass : CheckResult.fail(messages: messages)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -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
|