|
5 | 5 |
|
6 | 6 | A framework for managing and maintaining multi-language pre-commit hooks. |
7 | 7 |
|
8 | | -## Introduction |
| 8 | +For more information see: http://pre-commit.com |
9 | 9 |
|
10 | | -At Yelp we rely heavily on pre-commit hooks to find and fix common |
11 | | -issues before changes are submitted for code review. We run our hooks before |
12 | | -every commit to automatically point out issues like missing semicolons, |
13 | | -whitespace problems, and testing statements in code. Automatically fixing these |
14 | | -issues before posting code reviews allows our code reviewer to pay attention to |
15 | | -architecture of a change and not worry about trivial errors. |
16 | | - |
17 | | -As we created more libraries and projects we recognized that sharing our pre |
18 | | -commit hooks across projects is painful. We copied and pasted bash scripts from |
19 | | -project to project. We also had to manually change the hooks to work for |
20 | | -different project structures. |
21 | | - |
22 | | -We believe that you should always use the best industry standard linters. Some |
23 | | -of the best linters are written in languages that you do not use in your |
24 | | -project or have installed on your machine. For example scss-lint is a linter |
25 | | -for SCSS written in ruby. If you're writing a project in node you should be able |
26 | | -to use scss-lint as a pre-commit hook without adding a Gemfile to your project |
27 | | -or understanding how to get scss-lint installed. |
28 | | - |
29 | | -We built pre-commit to solve our hook issues. pre-commit is a multi-language |
30 | | -package manager for pre-commit hooks. You specify a list of hooks you want |
31 | | -and pre-commit manages the installation and execution of any hook written in any |
32 | | -language before every commit. pre-commit is specifically designed to not |
33 | | -require root access; if one of your developers doesn't have node installed but |
34 | | -modifies a javascript file, pre-commit automatically handles downloading and |
35 | | -building node to run jshint without root. |
36 | | - |
37 | | -## Installation |
38 | | - |
39 | | -Before you can run hooks, you need to have the pre-commit package manager |
40 | | -installed. |
41 | | - |
42 | | -Using pip: |
43 | | - |
44 | | - pip install pre-commit |
45 | | - |
46 | | -Non Administrative Installation: |
47 | | - |
48 | | - curl http://pre-commit.github.io/local-install.py | python |
49 | | - |
50 | | -System Level Install: |
51 | | - |
52 | | - curl https://bootstrap.pypa.io/get-pip.py | sudo python - pre-commit |
53 | | - |
54 | | -In a Python Project, add the following to your requirements.txt (or requirements-dev.txt): |
55 | | - |
56 | | - pre-commit |
57 | | - |
58 | | - |
59 | | -## Adding pre-commit Plugins To Your Project |
60 | | - |
61 | | -Once you have pre-commit installed, adding pre-commit plugins to your project is |
62 | | -done with the `.pre-commit-config.yaml` configuration file. |
63 | | - |
64 | | -Add a file called `.pre-commit-config.yaml` to the root of your project. The |
65 | | -pre-commit config file describes: |
66 | | - |
67 | | -- `repo`, `sha` - where to get plugins (git repos). |
68 | | -- `id` - What plugins from the repo you want to use. |
69 | | -- `language_version` - (optional) Override the default language version for the hook. |
70 | | - See Advanced Features: "Overriding Language Version" |
71 | | -- `files` - (optional) Override the default pattern for files to run on. |
72 | | -- `exclude` - (optional) File exclude pattern. |
73 | | -- `args` - (optional) additional parameters to pass to the hook. |
74 | | - |
75 | | -For example: |
76 | | - |
77 | | - - repo: git://github.com/pre-commit/pre-commit-hooks |
78 | | - sha: 82344a4055f4e103afdc31e98a46de679fe55385 |
79 | | - hooks: |
80 | | - - id: trailing-whitespace |
81 | | - |
82 | | -This configuration says to download the pre-commit-hooks project and run it's |
83 | | -trailing-whitespace hook. |
84 | | - |
85 | | - |
86 | | -## Usage |
87 | | - |
88 | | -run `pre-commit install` to install pre-commit into your git hooks. pre-commit |
89 | | -will now run on every commit. Every time you clone a project using pre-commit |
90 | | -running `pre-commit install` should always be the first thing you do. |
91 | | - |
92 | | -If you want to manually run all pre-commit hooks on a repository, run |
93 | | -`pre-commit run --all-files`. To run individual hooks use |
94 | | -`pre-commit run <hook_id>`. |
95 | | - |
96 | | -The first time pre-commit runs on a file it will automatically download, install, |
97 | | -and run the hook. Note that running a hook for the first time may be slow. |
98 | | -For example: If the machine does not have node installed, pre-commit will download |
99 | | -and build a copy of node. |
100 | | - |
101 | | - |
102 | | -## Creating New Hooks |
103 | | - |
104 | | -pre-commit currently supports hooks written in JavaScript (node), Python, Ruby |
105 | | -and system installed scripts. As long as your git repo is an installable package |
106 | | -(gem, npm, pypi, etc) or exposes an executable, it can be used with pre-commit. |
107 | | -Each git repo can support as many languages/hooks as you want. |
108 | | - |
109 | | -An executable must satisfy the following things: |
110 | | - |
111 | | -- Returncode of hook must be different between success / failures |
112 | | - (Usually 0 for success, nonzero for failure) |
113 | | -- It must take filenames |
114 | | - |
115 | | -A git repo containing pre-commit plugins must contain a hooks.yaml file that |
116 | | -tells pre-commit: |
117 | | - |
118 | | -- `id` - The id of the hook - used in pre-commit-config.yaml |
119 | | -- `name` - The name of the hook - shown during hook execution |
120 | | -- `entry` - The entry point - The executable to run |
121 | | -- `files` - The pattern of files to run on. |
122 | | -- `language` - The language of the hook - tells pre-commit how to install the hook |
123 | | -- `description` - (optional) The description of the hook |
124 | | -- `language_version` - (optional) See advanced features "Overriding Language Version" |
125 | | -- `expected_return_value` - (optional) Defaults to 0 |
126 | | - |
127 | | -For example: |
128 | | - |
129 | | - - id: trailing-whitespace |
130 | | - name: Trim Trailing Whitespace |
131 | | - description: This hook trims trailing whitespace. |
132 | | - entry: trailing-whitespace-fixer |
133 | | - language: python |
134 | | - files: \.(js|rb|md|py|sh|txt|yaml|yml)$ |
135 | | - |
136 | | - |
137 | | -### Supported languages |
138 | | - |
139 | | -- `node` |
140 | | -- `python` |
141 | | -- `ruby` |
142 | | -- `pcre` - "Perl Compatible Regular Expression" Specify the regex as the `entry` |
143 | | -- `script` - A script existing inside of a repository |
144 | | -- `system` - Executables available at the system level |
145 | | - |
146 | | - |
147 | | -## Popular Hooks |
148 | | - |
149 | | -JSHint: |
150 | | - |
151 | | - - repo: git://github.com/pre-commit/mirrors-jshint |
152 | | - sha: 8e7fa9caad6f7b2aae8d2c7b64f457611416192b |
153 | | - hooks: |
154 | | - - id: jshint |
155 | | - |
156 | | -SCSS-Lint: |
157 | | - |
158 | | - - repo: git://github.com/pre-commit/mirrors-scss-lint |
159 | | - sha: d7266131da322d6d76a18d6a3659f21025d9ea11 |
160 | | - hooks: |
161 | | - - id: scss-lint |
162 | | - |
163 | | -Ruby-Lint: |
164 | | - |
165 | | - - repo: git://github.com/pre-commit/mirrors-ruby-lint |
166 | | - sha: f4b537e0bf868fc6baefcb61288a12b35aac2157 |
167 | | - hooks: |
168 | | - - id: ruby-lint |
169 | | - |
170 | | -Whitespace Fixers: |
171 | | - |
172 | | - - repo: git://github.com/pre-commit/pre-commit-hooks |
173 | | - sha: a751eb58f91d8fa70e8b87c9c95777c5a743a932 |
174 | | - hooks: |
175 | | - - id: trailing-whitespace |
176 | | - - id: end-of-file-fixer |
177 | | - |
178 | | -flake8: |
179 | | - |
180 | | - - repo: git://github.com/pre-commit/pre-commit-hooks |
181 | | - sha: a751eb58f91d8fa70e8b87c9c95777c5a743a932 |
182 | | - hooks: |
183 | | - - id: flake8 |
184 | | - |
185 | | -pyflakes: |
186 | | - |
187 | | - - repo: git://github.com/pre-commit/pre-commit-hooks |
188 | | - sha: a751eb58f91d8fa70e8b87c9c95777c5a743a932 |
189 | | - hooks: |
190 | | - - id: pyflakes |
191 | | - |
192 | | - |
193 | | -## Advanced features |
194 | | - |
195 | | -### Running in Migration Mode |
196 | | - |
197 | | -By default, if you have existing hooks `pre-commit install` will install in |
198 | | -a migration mode which runs both your existing hooks and hooks for pre-commit. |
199 | | -To disable this behavior, simply pass `-f` / `--overwrite` to the `install` |
200 | | -command. If you decide not to use pre-commit, `pre-commit uninstall` will |
201 | | -restore your hooks to the state prior to installation. |
202 | | - |
203 | | - |
204 | | -### Temporarily Disabling Hooks |
205 | | - |
206 | | -Not all hooks are perfect so sometimes you may need to skip execution of |
207 | | -one or more hooks. pre-commit solves this by querying a `SKIP` environment |
208 | | -variable. The `SKIP` environment variable is a comma separated list of |
209 | | -hook ids. This allows you to skip a single hook instead of `--no-verify`ing |
210 | | -the entire commit |
211 | | - |
212 | | - $ SKIP=flake8 git commit -m "foo" |
213 | | - |
214 | | -### pre-commit During Commits |
215 | | - |
216 | | -Running hooks on unstaged changes can lead to both false-positives and |
217 | | -false-negatives during committing. pre-commit only runs on the staged |
218 | | -contents of files by temporarily saving the contents of your files at |
219 | | -commit time and stashing the unstaged changes while running hooks. |
220 | | - |
221 | | -### pre-commit During Merges |
222 | | - |
223 | | -The biggest gripe we've had in the past with pre-commit hooks was during |
224 | | -merge conflict resolution. When working on very large projects a merge |
225 | | -often results in hundreds of committed files. I shouldn't need to run |
226 | | -hooks on all of these files that I didn't even touch! This often led |
227 | | -to running commit with `--no-verify` and allowed introduction of real |
228 | | -bugs that hooks could have caught through merge-conflict resolution. |
229 | | -pre-commit solves this by only running hooks on files that conflict or |
230 | | -were manually edited during conflict resolution. |
231 | | - |
232 | | - |
233 | | -### Passing Arguments to Hooks |
234 | | - |
235 | | -Sometimes hooks require arguments to run correctly. You can pass |
236 | | -static arguments by specifying the `args` property in your |
237 | | -`.pre-commit-config.yaml` as follows: |
238 | | - |
239 | | - - repo: git://github.com/pre-commit/pre-commit-hooks |
240 | | - sha: a751eb58f91d8fa70e8b87c9c95777c5a743a932 |
241 | | - hooks: |
242 | | - - id: flake8 |
243 | | - args: [--max-line-length=131] |
244 | | - |
245 | | -This will pass `--max-line-length=131` to `flake8` |
246 | | - |
247 | | - |
248 | | -### Overriding Language Version |
249 | | - |
250 | | -Sometimes you only want to run the hooks on a specific version of |
251 | | -the language. For each language, they default to using the system |
252 | | -installed language (So for example if I'm running `python2.6` and a |
253 | | -hook specifies `python`, pre-commit will run the hook using `python2.6`). |
254 | | -Sometimes you don't want the default system installed version so you can |
255 | | -override this on a per-hook basis by setting the `language_version`. |
256 | | - |
257 | | - |
258 | | - - repo: git://github.com/pre-commit/mirrors-scss-lint |
259 | | - sha: d7266131da322d6d76a18d6a3659f21025d9ea11 |
260 | | - hooks: |
261 | | - - id: scss-lint |
262 | | - language_version: 1.9.3-p484 |
263 | | - |
264 | | -This tells pre-commit to use `1.9.3-p484` to run the `scss-lint` hook. |
265 | | - |
266 | | -Valid values for specific languages are listed below: |
267 | | - |
268 | | -- python: Whatever system installed python interpreters you have. |
269 | | - The value of this argument is passed as the `-p` to `virtualenv` |
270 | | -- node: See https://github.com/ekalinin/nodeenv#advanced |
271 | | -- ruby: See https://github.com/sstephenson/ruby-build/tree/master/share/ruby-build |
272 | | - |
273 | | - |
274 | | -## Contributing |
275 | | - |
276 | | -We're looking to grow the project and get more contributors especially |
277 | | -to support more languages/versions. We'd also like to get the hooks.yaml |
278 | | -files added to popular linters without maintaining forks / mirrors. |
279 | | - |
280 | | -Feel free to submit Bug Reports, Pull Requests, and Feature Requests. |
281 | | - |
282 | | -When submitting a pull request, please enable travis-ci for your fork. |
283 | | - |
284 | | - |
285 | | -## Contributors |
286 | | - |
287 | | -- Anthony Sottile |
288 | | -- Ken Struys |
0 commit comments