├── .github └── CONTRIBUTING.md ├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── Rakefile ├── Readme.md ├── bin └── pre-commit ├── changelog.md ├── docs └── choose_your_own_template.md ├── lib ├── plugins │ ├── pluginator │ │ └── extensions │ │ │ └── find_check.rb │ └── pre_commit │ │ ├── checks │ │ ├── before_all.rb │ │ ├── ci.rb │ │ ├── coffeelint.rb │ │ ├── common.rb │ │ ├── console_log.rb │ │ ├── csslint.rb │ │ ├── debugger.rb │ │ ├── gemfile_path.rb │ │ ├── go.rb │ │ ├── go_build.rb │ │ ├── go_fmt.rb │ │ ├── jshint.rb │ │ ├── jslint.rb │ │ ├── json.rb │ │ ├── local.rb │ │ ├── merge_conflict.rb │ │ ├── migration.rb │ │ ├── nb_space.rb │ │ ├── pry.rb │ │ ├── rails.rb │ │ ├── rspec_focus.rb │ │ ├── rubocop.rb │ │ ├── ruby.rb │ │ ├── ruby_symbol_hashrockets.rb │ │ ├── scss_lint.rb │ │ ├── tabs.rb │ │ ├── whitespace.rb │ │ └── yaml.rb │ │ └── configuration │ │ └── providers │ │ ├── README.md │ │ ├── default.rb │ │ ├── env.rb │ │ ├── git.rb │ │ ├── git_old.rb │ │ └── yaml.rb ├── pre-commit.rb └── pre-commit │ ├── checks.rb │ ├── checks │ ├── grep.rb │ ├── js.rb │ ├── plugin.rb │ ├── plugin │ │ └── config_file.rb │ └── shell.rb │ ├── cli.rb │ ├── configuration.rb │ ├── configuration │ ├── providers.rb │ └── top_level.rb │ ├── error_list.rb │ ├── installer.rb │ ├── line.rb │ ├── list_evaluator.rb │ ├── message.rb │ ├── plugins_list.rb │ ├── runner.rb │ ├── support │ ├── csslint │ │ └── csslint.js │ ├── jshint │ │ └── jshint.js │ └── jslint │ │ └── lint.js │ ├── template.rb │ └── utils │ ├── git_conversions.rb │ └── staged_files.rb ├── pre-commit.gemspec ├── script ├── benchmark │ └── staged_files.rb └── bootstrap ├── templates ├── gem │ ├── .gitignore │ ├── .travis.yml │ ├── GEM_NAME.gemspec │ ├── Gemfile │ ├── LICENSE │ ├── README.md │ ├── Rakefile │ ├── lib │ │ ├── plugins │ │ │ └── pre_commit │ │ │ │ └── checks │ │ │ │ └── PLUGIN_NAME.rb │ │ └── pre-commit │ │ │ └── PLUGIN_NAME │ │ │ └── version.rb │ └── test │ │ ├── files │ │ └── .keep │ │ ├── minitest_helper.rb │ │ └── plugins │ │ └── pre_commit │ │ └── checks │ │ └── PLUGIN_NAME_test.rb └── hooks │ ├── automatic │ ├── manual │ ├── simple │ └── sourcetree └── test ├── files ├── bad-spec.rb ├── bad.coffee ├── bad.json ├── bad.scss ├── bad.yml ├── bad_file.css ├── bad_file.js ├── bad_fmt.go ├── bad_spec.rb ├── bad_tabs2.rb ├── before_all_spec.rb ├── before_all_spec_2.rb ├── byebug_file.rb ├── changelog.md ├── console_log.js ├── debugger_file.rb ├── dont_compile.go ├── file_with_nb_space.rb ├── filename with spaces.rb ├── good.coffee ├── good.go ├── good.json ├── good.scss ├── good.yml ├── good_spec.rb ├── initial_tab.rb ├── merge_conflict.rb ├── pre-commit.rb ├── property_sets-0.3.0.gem ├── pry_file.rb ├── rspec_focus_bad_spec.rb ├── rspec_focus_good_spec.rb ├── rspec_focus_sugar_bad_spec.rb ├── rspec_focus_sugar_good_spec.rb ├── serialized.yml ├── tabs.rb ├── valid_file.css ├── valid_file.js ├── valid_file.rb ├── valid_hashrockets.rb ├── valid_spec.rb ├── with_debugger │ ├── Gemfile │ └── Gemfile.lock └── wrong_hashrockets.rb ├── integration_test.rb ├── minitest_helper.rb └── unit ├── plugins ├── pluginator │ └── extensions │ │ └── find_check_test.rb └── pre_commit │ ├── checks │ ├── before_all_test.rb │ ├── ci_test.rb │ ├── coffeelint_test.rb │ ├── console_log_test.rb │ ├── csslint_test.rb │ ├── debugger_test.rb │ ├── gemfile_path_test.rb │ ├── go_build_test.rb │ ├── go_fmt_test.rb │ ├── jshint_test.rb │ ├── jslint_test.rb │ ├── json_test.rb │ ├── local_test.rb │ ├── merge_conflict_test.rb │ ├── migration_test.rb │ ├── nb_space_test.rb │ ├── pry_test.rb │ ├── rspec_focus_test.rb │ ├── rubocop_test.rb │ ├── ruby_symbol_hashrockets_test.rb │ ├── scss_lint_test.rb │ ├── tabs_test.rb │ ├── whitespace_test.rb │ └── yaml_test.rb │ └── configuration │ └── providers │ ├── default_test.rb │ ├── env_test.rb │ ├── git_old_test.rb │ ├── git_test.rb │ └── yaml_test.rb └── pre-commit ├── checks ├── grep_test.rb ├── js_test.rb ├── plugin │ └── config_file_test.rb ├── plugin_test.rb └── shell_test.rb ├── cli_test.rb ├── configuration └── providers_test.rb ├── configuration_test.rb ├── error_list_test.rb ├── installer_test.rb ├── line_test.rb ├── list_evaluator_test.rb ├── plugins_list_test.rb ├── runner_test.rb ├── template_test.rb └── utils ├── git_conversions_test.rb └── staged_files_test.rb /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Reporting issues 4 | 5 | We would love to help if you are having a problem. Feel free to [open an 6 | issue](https://github.com/jish/pre-commit/issues). We ask that you please 7 | provide as much detail as possible. 8 | 9 | ## Adding extra checks 10 | 11 | A set of `pre-commit` checks is included in this repo. If you find a bug or 12 | wish to create an enhancement to one of these checks, please open an issue or a 13 | pull request. 14 | 15 | You can also create your own `pre-commit-plugin` in a separate repo. See the 16 | [pre-commit-plugins organization](https://github.com/pre-commit-plugins) on 17 | GitHub for examples. 18 | 19 | ## Adding extra configuration sources 20 | 21 | Currently `pre-commit` supports reading configuration from `git config`, your 22 | shell environment (through `ENV`), or a YAML file. See 23 | `lib/plugins/pre_commit/configuration/providers/` for more information and 24 | details on how to create a new configuration provider if necessary. 25 | 26 | ## Development 27 | 28 | Bootstrap a development environment: 29 | 30 | $ ./script/bootstrap 31 | 32 | Run tests: 33 | 34 | $ bundle exec rake test 35 | 36 | --- 37 | 38 | ### Closing old issues 39 | 40 | Issues that require user feedback will be marked with the `need info` 41 | label. If there is no feedback in two months we will close the issue. If an 42 | issue is closed in this way, the requester or the core team will reopen the 43 | issue when more information is provided. 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | bin/rake 3 | coverage/ 4 | /rdoc/html 5 | Gemfile.lock 6 | *.gem 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | addons: 3 | apt: 4 | packages: 5 | - php5 6 | bundler_args: "" 7 | before_install: 8 | - npm install -g coffeelint 9 | - rvm default do gem install execjs 10 | - git config --global user.name "Pre Commit" 11 | - git config --global user.email "pre-commit+travis@example.com" 12 | - gem update bundler 13 | cache: 14 | - bundler 15 | - npm 16 | language: ruby 17 | rvm: 18 | - jruby 19 | - 2.1.10 20 | - 2.2.10 21 | - 2.3.7 22 | - 2.4.4 23 | - 2.5.1 24 | matrix: 25 | fast_finish: true 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :development do 6 | # checks that do not have to be enabled 7 | gem "execjs" 8 | gem "scss-lint" 9 | 10 | # statistics only on MRI 2.0 - avoid problems on older rubies 11 | gem "redcarpet", :platforms => [:mri_20] 12 | gem "simplecov", :platforms => [:mri_20] 13 | gem "coveralls", :platforms => [:mri_20] 14 | end 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2016 Shajith Chacko, Josh Lubaway 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | require 'rdoc/task' 3 | 4 | Rake::TestTask.new do |test| 5 | test.libs << "test" << "lib" 6 | test.pattern = "test/**/*_test.rb" 7 | test.verbose = true 8 | end 9 | 10 | task :ci => [:test] 11 | task :default => [:test] 12 | 13 | namespace :pre_commit do 14 | desc "run the tests" 15 | task :ci => [:test] 16 | end 17 | 18 | RDoc::Task.new do |task| 19 | task.main = 'Readme.md' 20 | task.rdoc_files.include('Readme.md', 'lib/**/*.rb') 21 | task.rdoc_dir = 'rdoc/html' 22 | end 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | A better pre-commit hook for git. 2 | 3 | [![Current version](https://badge.fury.io/rb/pre-commit.svg)](https://rubygems.org/gems/pre-commit) 4 | [![Code Climate](https://img.shields.io/codeclimate/github/jish/pre-commit.svg)](https://codeclimate.com/github/jish/pre-commit) 5 | [![Coverage Status](https://img.shields.io/coveralls/jish/pre-commit/master.svg)](https://coveralls.io/r/jish/pre-commit?branch=master) 6 | [![Build status](https://travis-ci.org/jish/pre-commit.svg?branch=master)](https://travis-ci.org/jish/pre-commit) 7 | [![Dependency Status](https://gemnasium.com/jish/pre-commit.png)](https://gemnasium.com/jish/pre-commit) 8 | [![Documentation](https://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/pre-commit/frames) 9 | 10 | ## Installation 11 | 12 | Install the gem 13 | 14 | $ gem install pre-commit 15 | 16 | Use the pre-commit command to generate a stub pre-commit hook 17 | 18 | # In your git repo 19 | $ pre-commit install 20 | 21 | This creates a .git/hooks/pre-commit script which will check your git config and run checks that are enabled. 22 | 23 | ### Bundler 24 | 25 | If you want to use Bundler to specify a version of RuboCop, add the following to `Gemfile`: 26 | 27 | ```ruby 28 | group :development do 29 | gem "pre-commit", require: false 30 | gem "rubocop", require: false 31 | end 32 | ``` 33 | 34 | And run the following to run `pre-commit` via Bundler: 35 | 36 | ``` 37 | $ git config pre-commit.ruby "bundle exec ruby" 38 | ``` 39 | 40 | ### RVM 41 | 42 | If you are using rvm you need to install pre-commit into the ```default``` gemset, because it does not use the ```current``` environment 43 | 44 | $ rvm default do gem install pre-commit 45 | 46 | Alternatively you can configure pre-commit to use the ```current``` rvm gemset 47 | 48 | $ git config pre-commit.ruby "rvm `rvm current` do ruby" # OR: 49 | $ git config pre-commit.ruby `rvm wrapper current show ruby` # available in RVM 1.26.12 50 | 51 | ## Available checks 52 | 53 | These are the available checks: 54 | 55 | * white_space 56 | * console_log 57 | * debugger 58 | * pry 59 | * tabs 60 | * jshint 61 | * js_lint 62 | * php (Runs php -l on all staged files) 63 | * rspec_focus (Will check if you are about to check in a :focus in a spec file) 64 | * ruby_symbol_hashrockets (1.9 syntax. BAD :foo => "bar". GOOD foo: "bar") 65 | * local (executes `config/pre-commit.rb` with list of changed files) 66 | * merge_conflict (Will check if you are about to check in a merge conflict) 67 | * migrations (Will make sure you check in the proper files after creating a Rails migration) 68 | * ci (Will run the `pre_commit:ci` rake task and pass or fail accordingly) 69 | * rubocop (Check ruby code style using the rubocop gem. Rubocop must be installed) 70 | * before_all (Check your RSpec tests for the use of `before(:all)`) 71 | * coffeelint (Check your coffeescript files using the [coffeelint gem.](https://github.com/clutchski/coffeelint)) 72 | * gobuild (Runs go build and fails if can't compile) 73 | * gofmt (Runs go fmt on go source files and fail if formatting is incorrect) 74 | * scss_lint (Check your SCSS files using the [scss-lint gem](https://github.com/brigade/scss-lint)) 75 | * yaml (Check that your YAML is parsable) 76 | * json (Checks if JSON is parsable) 77 | 78 | ## Default checks 79 | 80 | Use `pre-commit list` to see the list of default and enabled checks and warnings. 81 | 82 | ## Enabling / Disabling Checks / Warnings 83 | 84 | ### Git configuration 85 | 86 | git config pre-commit.checks "[whitespace, jshint, debugger]" 87 | 88 | To disable, simply leave one off the list 89 | 90 | git config pre-commit.checks "[whitespace, jshint]" 91 | 92 | ### CLI configuration 93 | 94 | ```ssh 95 | pre-commit check1 [check2...] 96 | ``` 97 | 98 | The `git` provider can be used for local machine configuration, the `yaml` can be used for shared 99 | project configuration. 100 | 101 | Example move `jshint` from `checks` to `warnings` in `yaml` provider and save configuration to git: 102 | ```bash 103 | pre-commit disable yaml checks jshint 104 | pre-commit enable yaml warnings jshint 105 | git add config/pre_commit.yml 106 | git commit -m "pre-commit: move jshint from checks to warnings" 107 | ``` 108 | 109 | Example `config/pre_commit.yml`: 110 | ```yaml 111 | --- 112 | :warnings_remove: [] 113 | :warnings_add: 114 | - :jshint 115 | - :tabs 116 | ``` 117 | 118 | ## Running test manually 119 | 120 | This functionality was added in version `0.17.0` 121 | 122 | ```bash 123 | pre-commit run # run on the files added to index not yet commited 124 | pre-commit run all # run on all files in current directory 125 | pre-commit run git # run on all git-tracked files, respect gitignore (added in 0.19.0) 126 | pre-commit run # run on the list of files, patterns not supported 127 | ``` 128 | 129 | ## Configuration providers 130 | 131 | `pre-commit` comes with 4 configuration providers: 132 | 133 | - `default` - basic settings, read only 134 | - `git` - reads configuration from `git config pre-commit.*`, allow local update 135 | - `yaml` - reads configuration from `/etc/pre_commit.yml`, `$HOME/.pre_commit.yml` and `config/pre_commit.yml`, allows `config/pre_commit.yml` updates 136 | - `env` - reads configuration from environment variables 137 | 138 | ## Excluding files from checks 139 | 140 | `pre-commit` uses `git` to get list of files to check, you can ignore 141 | the list of git files to check with: 142 | 143 | 1. `.gitignore` - git supported file shared beteen all checkouts 144 | 2. `.git/info/exclude` - git supported file only for this checkout 145 | 3. `.pre_commit.ignore` - `pre-commit` specific list can be shared, 146 | [Allowed filters](http://ruby-doc.org/core-2.1.3/File.html#method-c-fnmatch) 147 | 148 | ## [Contributing](.github/CONTRIBUTING.md) 149 | -------------------------------------------------------------------------------- /bin/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pre-commit/cli' 4 | 5 | # Change directory to the root of the git repository. 6 | repo_root = `git rev-parse --show-toplevel`.strip 7 | abort "No .git directory found." unless File.directory?(repo_root) 8 | Dir.chdir repo_root 9 | 10 | PreCommit::Cli.new(*ARGV).execute or exit 1 11 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## 0.40.0 2 | 3 | * Add Ruby 3.2 support -- deprecate `File.exists?` 4 | * Missing migrations check only lists versions missing from schema 5 | 6 | ## 0.39.0 7 | 8 | * Handle new Rails migration format 9 | 10 | ## 0.38.1 11 | 12 | * Fix bug when there is no standard output when detecting the grep version 13 | 14 | ## 0.38.0 15 | 16 | * Ignore standard error when checking grep version 17 | * Security: Depend on RuboCop ~> 0.49 18 | 19 | ## 0.37.0 20 | 21 | * Skip running binary test on known binary files and known source files. 22 | This improves performance by not opening and reading each file before it is 23 | committed. 24 | 25 | ## 0.36.0 26 | 27 | * Add more RSpec focus syntaxes to the RSpec focus check 28 | 29 | ## 0.35.0 30 | 31 | * Fix a bug in the `go_fmt` plugin 32 | 33 | ## 0.34.0 34 | 35 | * Update to latest pluginator version to fix bug with bundler 1.15 36 | 37 | ## 0.33.0 38 | 39 | * Remove post-install message, it wasn't functioning properly anyway :( 40 | * Upgrade JSHint to 2.9.4 41 | * Fix Ruby warnings 42 | 43 | ## 0.32.0 44 | 45 | * Go back to original binary file check. It was creating false-positives. 46 | 47 | ## 0.31.0 48 | 49 | * Handle empty file sizes on Windows 50 | * Add a SourceTree pre-commit hook template `pre-commit install --sourcetree` 51 | 52 | ## 0.30.0 53 | 54 | * Fixed a bug where the default pre-commit hook could not be installed on some 55 | systems. 56 | 57 | ## 0.29.0 58 | 59 | * Fixed a bug where version 0.27.0 and 0.28.0 could not be installed on Windows. 60 | 61 | ## 0.28.0 62 | 63 | * The binary check handles unicode source files. 64 | 65 | ## 0.27.0 66 | 67 | * Removed the closure check 68 | * Separated gofmt and gobuild checks 69 | * Run tests against more (newer) versions of Ruby -- 2.2 & 2.3 70 | * config.ru is an allowed file for the rubocop check 71 | * Added a `pre-commit new` command to generate a new pre-commit plugin 72 | * The pry check now cheks for `binding.remote_pry` and `binding.remote_pry_em` 73 | 74 | ## 0.26.0 75 | 76 | * Add byebug support to the debugger check 77 | 78 | ## 0.25.0 79 | 80 | * Rubocop check was not filtering file extensions properly. It was ignoring the dot `.` (e.g. `.foo`). 81 | * Users should now be able to commit from subdirectories (e.g. `$ cd subdir`, `$ git commit`). 82 | 83 | ## 0.24.0 84 | 85 | * Whitespace check will not run if there are no staged files. 86 | * The `pre-commit` script now runs properly in GitHub for Mac. 87 | 88 | ## 0.23.0 89 | 90 | * RuboCop checks more than just .rb files (`.gemspec`, `Rakefile`, etc.) 91 | 92 | ## 0.22.1 93 | 94 | * `pre-commit run` used to always exit with the same status code. Now it exits with 0 on success, 1 on error 95 | 96 | ## 0.22.0 97 | 98 | * Read all versions from schema files in the migration check. 99 | 100 | ## 0.21.0 101 | 102 | * Ignore `no rvm in path` errors if rvm is not present. 103 | 104 | ## 0.20.0 105 | 106 | * Add the ability to pass command line flags to Rubocop. `rubocop.flags` 107 | * Upgrade JSHint to 2.5.4 108 | * The `local` check will look for and prefer `config/pre_commit.rb` in addition to `config/pre-commit.rb` 109 | * Allow committing a large number of files (> 7,000) 110 | 111 | ## 0.19.0 112 | 113 | * Add a `pre-commit run git` command to run `pre-commit` checks on all files tracked by git. 114 | * Filenames containing spaces no longer break the Grep check 115 | 116 | ## 0.18.0 117 | 118 | * Improve version number discovery in schema and migration files so that a blank `schema.rb` (with version `0`) does not fail the migration check 119 | * Fix `pre-commit run `. 120 | 121 | ## 0.17.0 122 | 123 | * Fix a bug where `pre-commit list` would bomb when you had older versions of `pre-commit` installed 124 | * Smarter debugger check allows you to leave `debugger` in comments, quotes, or other intended uses 125 | * Add a `pre-commit run` CLI command 126 | * Fix intermittent install failures #154 127 | 128 | ## 0.16.3 129 | 130 | * Account for the `RuboCop` top level namespace case sensitivity change. 131 | 132 | ## 0.16.2 133 | 134 | * Fix an error that occurred when adding a git submodule 135 | 136 | ## 0.16.1 137 | 138 | * Adds --force-exclusion option to rubocop CLI 139 | 140 | ## 0.16.0 141 | 142 | * Add a JSON check -- checks if JSON is parsable. 143 | * Add a YAML check -- checks if YAML is parsable. 144 | * Add an SCSS-Lint check -- scss-lint is a tool to help keep your SCSS files clean and readable. 145 | * The `console.log` check will run on coffe script files as well. 146 | 147 | ## 0.15.0 148 | 149 | * Add a standard way for checks to find a config file. `check_name.config` 150 | 151 | ## 0.14.1 152 | 153 | * use `get` to read rubocop.config, fix #124 154 | 155 | ## 0.14.0 156 | 157 | * Add a `Go` check 158 | * Fix for bug: "Could not find template default" 159 | 160 | ## 0.13.0 161 | 162 | * Hashrockets check only runs on Ruby files 163 | * Fix "uninitialized constant" bug when running Rubocop 164 | * Add [CSSLint](http://csslint.net/) support 165 | * New configuration strategies -- backwards compatible with old configuration 166 | 167 | ## 0.12.0 168 | 169 | * Add a `before_all` check for RSpec `before(:all)` blocks. 170 | * Add a `coffeelint` check. 171 | * Do not load `execjs` unless JavaScript checks are enabled 172 | * Load checks with [pluginator](https://github.com/rvm/pluginator) 173 | * Allow configuration of warnings (log to `stderr`, but do not abort the commit) in `git config pre-commit.warnings` 174 | * Use the Apache 2.0 liscense 175 | * The `nb_space` check reads files in utf-8 176 | 177 | ## 0.11.0 178 | 179 | * Converted the hook template to shell (instead of ruby) keep an eye out for problems, and file an issue if anything comes up https://github.com/jish/pre-commit/issues 180 | * Added a `pre-commit.ruby` git config option. If this option is set, the hook will use that ruby. `git config pre-commit.ruby "ruby"` 181 | * Drop `Ruby 1.8.7` support 182 | 183 | ## 0.10.0 184 | 185 | * Enhancement: Migration check will ensure the proper versions are in the schema file 186 | 187 | ## 0.9.2 188 | 189 | * Does not run the debugger check on `Gemfile`, `Gemfile.lock` 190 | 191 | ## 0.9.0 192 | 193 | * adding spec directory to checked dirs of pry and debugger 194 | 195 | ## 0.8.1 196 | 197 | * Better system ruby suppot on Mac OS. 198 | 199 | ## 0.8.0 200 | 201 | * Added a check for `binding.pry` 202 | * Allowing `mount Application::API => '/api'` syntax in the hashrocket check 203 | * Added a check for `:focus` in rspec tests. 204 | 205 | ## 0.7.0 206 | 207 | * Added a `local` check. Will run `config/pre-commit.rb` and pass or fail accordingly. 208 | 209 | ## 0.6.1 210 | 211 | * Properly require `ruby_symbol_hashrockets`. 212 | 213 | ## 0.6.0 214 | 215 | * Adding a Ruby hashrocket syntax check. If you're into that kind of thing. 216 | 217 | ## 0.5.0 218 | 219 | * Checking for `rbenv` on boot as well as `rvm` 220 | 221 | ## 0.4.0 222 | 223 | * Detecting if the pre-commit gem is no longer installed. This is usually due to a Ruby version upgrade. 224 | * Only running the ConsoleLog check on javascript files. 225 | 226 | ## 0.3.1 227 | 228 | * Fix for Mountain Lion's grep. 229 | 230 | ## 0.3.0 231 | 232 | * Adding the merge conflict check to the list of default checks 233 | 234 | ## 0.2.0 235 | 236 | * Fixing a segmentation fault that was occurring when some people did not have the proper ruby setup in their environment 237 | * Adding the option to overwrite existing pre-commit hooks during installation 238 | 239 | ## 0.1.19 240 | 241 | * Removing arguments from the shebang line as these are not interpreted the same way on all operating systems 242 | 243 | ## 0.1.18 244 | 245 | * Upgrading JSHint 246 | * Playing nicely with execjs 247 | 248 | ## 0.1.17 249 | 250 | * Fixing typos 251 | * Adding a php check 252 | 253 | ## 0.1.16 254 | 255 | * Detecting leading whitespace before leading tabs in the tabs check. 256 | 257 | ## 0.1.15 258 | 259 | * The previous release handled some error reporting when using therubyracer vm. This release fixes errors if you're using ExecJS and *do not* have therubyracer installed. 260 | 261 | ## 0.1.14 262 | 263 | * Better error reporting when JSHint stops scanning a file for errors half way through 264 | 265 | ## 0.1.13 266 | 267 | * Adding a JSHint config file. You can put your options in a .jshintrc file 268 | * Adding a ci check. You can run a quick test suite each time you commit. 269 | 270 | ## 0.1.10 271 | 272 | * Adding a migration sanity check 273 | 274 | ## 0.1.9 275 | 276 | * Allowing commented out console.log to pass (only single line comment support for now =/) 277 | 278 | ## 0.1.7 279 | 280 | * Adding JSHint support 281 | * Making JSHint a default check 282 | 283 | ## 0.1.6 284 | 285 | ### Bugs 286 | * Pre commit would fail -- silently :( -- when adding new .js files due to an error in the jslint check. 287 | 288 | ## 0.1.3 289 | 290 | ### Bugs 291 | * On the debugger check, only checking the lines that the committer has added. (thanks to staugaard for pointing this out) 292 | 293 | ### Enhancements 294 | * Adding a reminder that the pre-commit check can be bypassed using `git commit -n` 295 | 296 | ## 0.1.2 297 | 298 | ### Bugs 299 | * The tabs check was detecting leading tabs in binary files. The tabs check no longer checks binary files. (thanks to morten for pointing this out) 300 | -------------------------------------------------------------------------------- /docs/choose_your_own_template.md: -------------------------------------------------------------------------------- 1 | 2 | ## Choose your own template 3 | 4 | The most common criticism of this project is the `pre-commit` hook template we use. Everyone has their own environment, and everyone has their own opinions. 5 | 6 | Right now the template's goal is portability. We want the hook to work on as many machines, and in as many environments as possible -- that's why it looks so strange. 7 | 8 | --- 9 | 10 | Everyone does not share this opinion. Some people value speed, others their Ruby version switcher of choice. This document is a proposal to allow users to choose the template that is appropriate for their repository. 11 | 12 | ## Options 13 | 14 | ### Portable template 15 | 16 | The goal of this template will be to work in as many environments as possible. 17 | 18 | $ pre-commit install --portable 19 | $ pre-commit install --simple 20 | $ pre-commit install --automatic 21 | $ pre-commit install # maybe the default option? 22 | 23 | ### Fast (Manual? Advanced?) template 24 | 25 | This template will assume your environment is set up properly, and will value simplicity and speed 26 | 27 | ```ruby 28 | require 'pre-commit' 29 | 30 | PreCommit.run 31 | ``` 32 | 33 | Installation options: 34 | 35 | $ pre-commit install --advanced 36 | $ pre-commit install --manual 37 | $ pre-commit install --fast 38 | $ pre-commit install # maybe the default option? 39 | 40 | ### RVM template 41 | 42 | This template will assume you are using RVM as a Ruby version switcher, and will attempt to setup the RVM environment for you. 43 | 44 | $ pre-commit install --rvm 45 | 46 | ### rbenv template 47 | 48 | This template will assume you are using rbenv as a Ruby version switcher, and will attempt to setup the rbenv environment for you. 49 | 50 | $ pre-commit install --rbenv 51 | -------------------------------------------------------------------------------- /lib/plugins/pluginator/extensions/find_check.rb: -------------------------------------------------------------------------------- 1 | require "plugins/pluginator/extensions/conversions" 2 | 3 | module Pluginator::Extensions 4 | # Extension to find class or first plugin that answers the question with true or to print warning 5 | module FindCheck 6 | include Conversions 7 | 8 | def find_check(name) 9 | klass = string2class(name) 10 | @plugins["checks"].detect do |plugin| 11 | class2name(plugin) == klass || 12 | plugin.respond_to?(:aliases) && plugin.public_send(:aliases).include?(name.to_sym) 13 | end || 14 | begin 15 | $stderr.puts "Could not find plugin supporting #{name} / #{klass}, 16 | available plugins: #{available_plugins}" 17 | nil 18 | end 19 | end 20 | 21 | def available_plugins 22 | @plugins["checks"].map{|plugin| class2name(plugin) }.join(", ") 23 | end 24 | 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/before_all.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class BeforeAll < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.grep(/\.rb$/) 9 | end 10 | 11 | def extra_grep 12 | %w{-v //} 13 | end 14 | 15 | def message 16 | "before(:all) found:" 17 | end 18 | 19 | def pattern 20 | "before.*:all" 21 | end 22 | 23 | def self.description 24 | "Find ruby files with 'before :all' pattern" 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/ci.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | ## 6 | # The CI check will run `rake pre_commmit:ci` before the commit and check 7 | # its exit code. If the task runs successfully, the commit will proceed. 8 | # If it fails, the commit will be aborted. 9 | # 10 | class Ci < Plugin 11 | CI_TASK_NAME = 'pre_commit:ci' 12 | 13 | def self.description 14 | "Runs 'rake #{CI_TASK_NAME} --silent'" 15 | end 16 | 17 | def call(_) 18 | return if system("rake", CI_TASK_NAME, "--silent") 19 | 20 | PreCommit::ErrorList.new( 21 | "your test suite has failed, for the full output run `#{CI_TASK_NAME}`" 22 | ) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/coffeelint.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/shell' 2 | 3 | module PreCommit 4 | module Checks 5 | class Coffeelint < Shell 6 | 7 | def call(staged_files) 8 | staged_files = staged_files.grep(/\.coffee$/) 9 | return if staged_files.empty? 10 | 11 | result = 12 | in_groups(staged_files).map do |files| 13 | args = %w{coffeelint} + config_file_flag + files 14 | execute(args) 15 | end.compact 16 | 17 | result.empty? ? nil : result.join("\n") 18 | end 19 | 20 | def config_file_flag 21 | config_file ? ['-f', config_file] : [] 22 | end 23 | 24 | def alternate_config_file 25 | 'coffeelint.json' 26 | end 27 | 28 | def self.description 29 | "Runs coffeelint to detect errors" 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/common.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Common < Plugin 6 | 7 | def self.includes 8 | [:tabs, :nb_space, :whitespace, :merge_conflict, :debugger] 9 | end 10 | 11 | def self.description 12 | "Plugins common for all languages." 13 | end 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/console_log.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class ConsoleLog < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.grep(/\.(js|coffee)$/) 9 | end 10 | 11 | def extra_grep 12 | %w{-v //} 13 | end 14 | 15 | def message 16 | "console.log found:" 17 | end 18 | 19 | def pattern 20 | "console\\.log" 21 | end 22 | 23 | def self.description 24 | "Finds javascript files with 'console.log'." 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/csslint.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/js' 2 | 3 | module PreCommit 4 | module Checks 5 | class Csslint < Js 6 | 7 | SOURCE = "https://github.com/stubbornella/csslint/blob/v0.10.0/release/csslint.js" 8 | 9 | def run_check(file) 10 | context = ExecJS.compile(File.read(linter_src)) 11 | context.call("CSSLint.verify", File.read(file))["messages"] 12 | end 13 | 14 | def linter_src 15 | File.expand_path("../../../../pre-commit/support/csslint/csslint.js", __FILE__) 16 | end 17 | 18 | def error_selector 19 | 'message' 20 | end 21 | 22 | def files_filter(staged_files) 23 | staged_files.grep(/\.css$/) 24 | end 25 | 26 | def self.description 27 | "Checks CSS files with CSSLint." 28 | end 29 | 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/debugger.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class Debugger < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.reject { |file| File.basename(file) =~ /^Gemfile/ } 9 | end 10 | 11 | def message 12 | "debugger statement(s) found:" 13 | end 14 | 15 | def pattern 16 | "^[ ]*(debugger|byebug)" 17 | end 18 | 19 | def self.description 20 | "Finds files with 'debugger'." 21 | end 22 | 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/gemfile_path.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class GemfilePath < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.grep(/^Gemfile$/) 9 | end 10 | 11 | def message 12 | "local path found in Gemfile:" 13 | end 14 | 15 | def pattern 16 | "path:|:path\\s*=>" 17 | end 18 | 19 | def extra_grep 20 | %w{-v #} 21 | end 22 | 23 | def self.description 24 | "Checks 'Gemfile' for local paths." 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/go.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Go < Plugin 6 | 7 | def self.includes 8 | [:gobuild, :gofmt] 9 | end 10 | 11 | 12 | def self.description 13 | "Plugins for Go code" 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/go_build.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class GoBuild < Plugin 6 | 7 | def call(staged_files) 8 | staged_files = staged_files.grep(/\.go$/) 9 | return if staged_files.empty? 10 | 11 | errors = staged_files.map { |file| run_check(file) }.compact 12 | return if errors.empty? 13 | 14 | errors.join("\n") 15 | end 16 | 17 | def run_check(file) 18 | cmd = "go build -o /dev/null #{file} 2>&1" 19 | %x[ #{cmd} ] 20 | end 21 | 22 | def self.description 23 | "Detects Go compiler errors" 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/go_fmt.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class GoFmt < Plugin 6 | 7 | def call(staged_files) 8 | staged_files = staged_files.grep(/\.go$/) 9 | return if staged_files.empty? 10 | 11 | errors = staged_files.map { |file| run_check(file) }.compact 12 | return if errors.empty? 13 | 14 | errors.join("\n") 15 | end 16 | 17 | def run_check(file) 18 | cmd = "gofmt -l #{file} 2>&1" 19 | %x[ #{cmd} ] 20 | end 21 | 22 | def self.description 23 | "Detects bad Go formatting" 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/jshint.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/js' 2 | 3 | module PreCommit 4 | module Checks 5 | class Jshint < Js 6 | 7 | def run_check(file) 8 | context.call("JSHINT._getErrors", File.read(file), js_config, js_config["globals"]) 9 | end 10 | 11 | def linter_src 12 | File.expand_path("../../../../pre-commit/support/jshint/jshint.js", __FILE__) 13 | end 14 | 15 | def alternate_config_file 16 | ".jshintrc" 17 | end 18 | 19 | def self.description 20 | "Checks javascript files with JSHint." 21 | end 22 | 23 | private 24 | 25 | def context 26 | @context ||= ExecJS.compile("global = this;" << File.read(linter_src) << <<-JAVASCRIPT) 27 | ;JSHINT._getErrors = function(source, options, globals) { 28 | JSHINT(source, options, globals); 29 | return JSHINT.errors; 30 | } 31 | JAVASCRIPT 32 | end 33 | 34 | def js_config 35 | @js_config ||= if config_file 36 | ExecJS.exec("return (#{File.read(config_file)});") 37 | else 38 | {} 39 | end 40 | end 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/jslint.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/js' 2 | 3 | module PreCommit 4 | module Checks 5 | class Jslint < Js 6 | 7 | def self.aliases 8 | [ :js_lint, :js_lint_all, :js_lint_new ] 9 | end 10 | 11 | def run_check(file) 12 | context = ExecJS.compile(File.read(linter_src)) 13 | if !(context.call('JSLINT', File.read(file))) 14 | context.exec('return JSLINT.errors;') 15 | else 16 | [] 17 | end 18 | end 19 | 20 | def linter_src 21 | File.expand_path("../../../../pre-commit/support/jslint/lint.js", __FILE__) 22 | end 23 | 24 | def self.description 25 | "Checks javascript files with JSLint." 26 | end 27 | 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/json.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'pre-commit/checks/plugin' 3 | 4 | module PreCommit 5 | module Checks 6 | class Json < Plugin 7 | def call(staged_files) 8 | staged_files = staged_files.grep(/\.json$/) 9 | return if staged_files.empty? 10 | 11 | errors = staged_files.map {|file| load_file(file)}.compact 12 | 13 | errors.join("\n") + "\n" unless errors.empty? 14 | end 15 | 16 | def load_file(file) 17 | File.open(file) {|io| JSON.load(io)} 18 | nil 19 | rescue JSON::ParserError => e 20 | "#{e.message} parsing #{file}" 21 | end 22 | 23 | def self.description 24 | 'Runs json to detect errors.' 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/local.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Local < Plugin 6 | 7 | attr_writer :script 8 | 9 | def call(staged_files) 10 | return unless script 11 | output = `ruby #{script} #{staged_files.join(" ")} 2>&1` 12 | "#{script} failed:\n#{output}" unless $?.success? 13 | end 14 | 15 | def self.description 16 | "Executes a custom script located at config/pre_commit.rb" 17 | end 18 | 19 | def script 20 | @script ||= ["config/pre_commit.rb", "config/pre-commit.rb"].detect do |file| 21 | File.exist?(file) 22 | end 23 | end 24 | 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/merge_conflict.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class MergeConflict < Grep 6 | 7 | def message 8 | "detected a merge conflict" 9 | end 10 | 11 | def pattern 12 | "<<<<<<<" 13 | end 14 | 15 | def self.description 16 | "Finds files with unresolved merge conflicts." 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/migration.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Migration < Plugin 6 | VERSION_PATTERN = /(\d{4}_?\d{2}_?\d{2}_?\d{6})/ 7 | 8 | VersionedFile = Struct.new(:file, :version) 9 | 10 | def self.aliases 11 | [:migrations] 12 | end 13 | 14 | def call(staged_files) 15 | migration_files = versioned_migration_files(staged_files) 16 | schema_files = versioned_schema_files(staged_files) 17 | 18 | if migration_files.any? && schema_files.none? 19 | "It looks like you're adding a migration, but did not update the schema file" 20 | elsif migration_files.none? && schema_files.any? 21 | "You're trying to change the schema without adding a migration file" 22 | elsif migration_files.any? && schema_files.any? 23 | migration_versions = migration_files.map(&:version) 24 | schema_versions = schema_files.map(&:version) 25 | missing_versions = migration_versions - schema_versions 26 | 27 | if missing_versions.any? 28 | "You did not add the schema versions for "\ 29 | "#{missing_versions.join(', ')} to #{schema_files.map(&:file).join(' or ')}" 30 | end 31 | end 32 | end 33 | 34 | private 35 | 36 | def versioned_migration_files(staged_files) 37 | files = staged_files.grep(/db\/migrate\/.*\.rb/) 38 | 39 | files.each_with_object([]) do |f, result| 40 | if f =~ VERSION_PATTERN 41 | result << VersionedFile.new(f, $1) 42 | end 43 | end 44 | end 45 | 46 | def versioned_schema_files(staged_files) 47 | files = staged_files.select do |f| 48 | File.basename(f) =~ /schema\.rb|structure.*\.sql/ 49 | end 50 | 51 | files.each_with_object([]) do |f, result| 52 | File.read(f).scan(VERSION_PATTERN) do |i| 53 | version = i.first.gsub(/_/, "") 54 | result << VersionedFile.new(f, version) 55 | end 56 | end 57 | end 58 | 59 | def self.description 60 | "Detects rails database migrations and schema incompatibilities." 61 | end 62 | 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/nb_space.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'pre-commit/checks/plugin' 3 | 4 | module PreCommit 5 | module Checks 6 | class NbSpace < Plugin 7 | 8 | def call(staged_files) 9 | nb_space = " " 10 | raise "you messed that up" unless nb_space.bytes.to_a == [194, 160] 11 | 12 | staged_files.reject! { |f| f =~ /^vendor\// || !File.read(f, encoding: 'utf-8').include?(nb_space) } 13 | 14 | bad = staged_files.map do |file| 15 | content = File.read(file).lines.to_a 16 | line_no = content.index { |l| l.include?(nb_space) } 17 | char_no = content[line_no].index(nb_space) 18 | [file, line_no, char_no] 19 | end 20 | 21 | return if bad.empty? 22 | "Detected non-breaking space in #{bad.map { |f,l,c| "#{f}:#{l+1} character:#{c+1}" }.join(" and")}, remove it!" 23 | end 24 | 25 | def self.description 26 | "Detected non-breaking spaces 194, 160." 27 | end 28 | 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/pry.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class Pry < Grep 6 | 7 | def message 8 | "binding.pry found:" 9 | end 10 | 11 | def pattern 12 | "binding\.(remote_)?pry" 13 | end 14 | 15 | def self.description 16 | "Finds files with 'binding.pry'." 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/rails.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Rails < Plugin 6 | 7 | def self.includes 8 | [:ruby, :jshint, :console_log, :migration] 9 | end 10 | 11 | def self.description 12 | "Plugins common for ruby on rails." 13 | end 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/rspec_focus.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class RspecFocus < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.grep(/_spec\.rb$/) 9 | end 10 | 11 | def message 12 | "focus found in specs:" 13 | end 14 | 15 | def pattern 16 | "(describe|context|it).*(:focus|focus:).*do" 17 | end 18 | 19 | def extra_pattern 20 | "(fdescribe|fcontext|fit).*(\"|').*(\"|').*do" 21 | end 22 | 23 | def self.description 24 | "Finds ruby specs that are focused." 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/rubocop.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | require 'pre-commit/checks/plugin' 3 | 4 | module PreCommit 5 | module Checks 6 | class Rubocop < Plugin 7 | 8 | WHITELIST = %w[ 9 | \.gemspec \.jbuilder \.opal \.podspec \.rake \.rb config\.ru 10 | Berksfile Capfile Cheffile Gemfile Guardfile Podfile 11 | Rakefile Thorfile Vagabondfile Vagrantfile 12 | ] 13 | 14 | def self.aliases 15 | [ :rubocop_all, :rubocop_new ] 16 | end 17 | 18 | def self.excludes 19 | [ :ruby_symbol_hashrocket ] 20 | end 21 | 22 | def call(staged_files) 23 | require 'rubocop' 24 | rescue LoadError => e 25 | $stderr.puts "Could not find rubocop: #{e}" 26 | else 27 | staged_files = filter_staged_files(staged_files) 28 | return if staged_files.empty? 29 | 30 | args = config_file_flag + user_supplied_flags + ["--force-exclusion"] + staged_files 31 | 32 | success, captured = capture { ::RuboCop::CLI.new.run(args) == 0 } 33 | captured unless success 34 | end 35 | 36 | def filter_staged_files(staged_files) 37 | expression = Regexp.new(WHITELIST.map { |i| i + "\\Z" }.join("|")) 38 | staged_files.grep(expression) 39 | end 40 | 41 | def capture 42 | $stdout, stdout = StringIO.new, $stdout 43 | $stderr, stderr = StringIO.new, $stderr 44 | result = yield 45 | [result, $stdout.string + $stderr.string] 46 | ensure 47 | $stdout = stdout 48 | $stderr = stderr 49 | end 50 | 51 | def config_file_flag 52 | config_file ? ['-c', config_file] : [] 53 | end 54 | 55 | def user_supplied_flags 56 | Array(config.get('rubocop.flags')).reject(&:empty?) 57 | end 58 | 59 | def alternate_config_file 60 | '.rubocop.yml' 61 | end 62 | 63 | def self.description 64 | "Runs rubocop to detect errors." 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/ruby.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Ruby < Plugin 6 | 7 | def self.includes 8 | [:pry, :local] 9 | end 10 | 11 | def self.description 12 | "Plugins common for ruby." 13 | end 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/ruby_symbol_hashrockets.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class RubySymbolHashrockets < Grep 6 | 7 | def files_filter(staged_files) 8 | staged_files.grep(/\.rb$/) 9 | end 10 | 11 | def message 12 | "detected :symbol => value hashrocket:" 13 | end 14 | 15 | def pattern 16 | '[^:](:{1}(?:\$|@|@@|[_A-Za-z])?\w*[=!?]?\s*=>\s*)' 17 | end 18 | 19 | def self.description 20 | "Finds ruby 1.8 '=>' hash definitions." 21 | end 22 | 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/scss_lint.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/shell' 2 | 3 | module PreCommit 4 | module Checks 5 | class ScssLint < Shell 6 | 7 | def call(staged_files) 8 | staged_files = staged_files.grep(/\.scss$/) 9 | return if staged_files.empty? 10 | 11 | result = 12 | in_groups(staged_files).map do |files| 13 | args = %w{scss-lint} + config_file_flag + files 14 | execute(args) 15 | end.compact 16 | 17 | result.empty? ? nil : result.join("\n") 18 | end 19 | 20 | def config_file_flag 21 | config_file ? ['-c', config_file] : [] 22 | end 23 | 24 | def alternate_config_file 25 | '.scss-lint.yml' 26 | end 27 | 28 | def self.description 29 | "Runs scss lint to detect errors" 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/tabs.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/grep' 2 | 3 | module PreCommit 4 | module Checks 5 | class Tabs < Grep 6 | 7 | def message 8 | "detected tab before initial space:" 9 | end 10 | 11 | def pattern 12 | "^ *\t" 13 | end 14 | 15 | def self.description 16 | "Finds ruby files with tabulation character before text in line." 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/whitespace.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Whitespace < Plugin 6 | 7 | def self.aliases 8 | [:white_space] 9 | end 10 | 11 | def files_filter(staged_files) 12 | if 13 | @list.map(&:name).include?("PreCommit::Checks::Rubocop") 14 | then 15 | staged_files.reject{|name| name =~ /\.rb$/ } 16 | else 17 | staged_files 18 | end 19 | end 20 | 21 | def files_string(staged_files) 22 | files_filter(staged_files).map{|file| "'#{file}'" }.join(" ") 23 | end 24 | 25 | def call(staged_files) 26 | return if staged_files.empty? 27 | 28 | errors = `git diff-index --check --cached HEAD -- #{files_string(staged_files)} 2>&1` 29 | return if $?.success? 30 | 31 | # Initial commit: diff against the empty tree object 32 | if errors =~ /fatal: bad revision 'HEAD'/ 33 | errors = `git diff-index --check --cached 4b825dc642cb6eb9a060e54bf8d69288fbee4904 -- 2>&1` 34 | return if $?.success? 35 | end 36 | 37 | errors 38 | end 39 | 40 | def self.description 41 | "Finds white space." 42 | end 43 | 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/checks/yaml.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require 'yaml' 4 | require 'pre-commit/checks/plugin' 5 | 6 | module PreCommit 7 | module Checks 8 | ## 9 | # The Yaml check will read and parse YAML files to ensure they have valid 10 | # syntax. 11 | # 12 | class Yaml < Plugin 13 | def self.description 14 | 'Runs yaml to detect errors.' 15 | end 16 | 17 | def call(staged_files) 18 | staged_files = staged_files.grep(/\.(yml|yaml)$/) 19 | return if staged_files.empty? 20 | 21 | errors = staged_files.map {|file| load_file(file)}.compact 22 | 23 | errors.join("\n") + "\n" unless errors.empty? 24 | end 25 | 26 | private 27 | 28 | def load_file(file) 29 | if YAML.respond_to?(:safe_load) 30 | safe_load_file(file) 31 | else 32 | normal_load_file(file) 33 | end 34 | 35 | rescue Psych::SyntaxError => e 36 | e.message 37 | end 38 | 39 | def safe_load_file(file) 40 | YAML.safe_load(File.read(file), [::Symbol], [], true, file) 41 | 42 | nil 43 | rescue Psych::DisallowedClass 44 | $stdout.puts "Warning: Skipping '#{file}' because it contains serialized ruby objects." 45 | end 46 | 47 | def normal_load_file(file) 48 | YAML.load_file(file) 49 | 50 | nil 51 | rescue ArgumentError 52 | $stdout.puts "Warning: Skipping '#{file}' because it contains serialized ruby objects." 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/README.md: -------------------------------------------------------------------------------- 1 | # PreCommit::Configurations::Providers 2 | 3 | ## Ordering 4 | 5 | Providers are ordered based on `priority`. 6 | 7 | ## Adding new one 8 | 9 | You can add extra providers by creating gem with a new provider in 10 | `lib/plugins/pre_commit/configuration/providers`. 11 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/default.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | class CanNotUpdateDefauls < StandardError 3 | end 4 | 5 | class Configuration 6 | class Providers 7 | class Default 8 | 9 | DEFAULTS = 10 | { 11 | :warnings => [], 12 | :checks => [:common, :rails] 13 | } 14 | 15 | def self.priority 16 | 0 17 | end 18 | 19 | def initialize(defaults = nil) 20 | @defaults = defaults || DEFAULTS 21 | end 22 | 23 | def [](name) 24 | @defaults[name] 25 | end 26 | 27 | def update(name, value) 28 | raise PreCommit::CanNotUpdateDefauls.new("Can not update default settings") 29 | end 30 | 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/env.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | class Configuration 3 | class Providers 4 | 5 | class Env 6 | def self.priority 7 | 30 8 | end 9 | 10 | def [](name) 11 | ENV[key(name)] 12 | end 13 | 14 | def update(name, value) 15 | ENV[key(name)] = value 16 | end 17 | 18 | private 19 | 20 | def key(name) 21 | name.to_s.upcase.split('.').join('_') 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/git.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/utils/git_conversions' 2 | 3 | module PreCommit 4 | class Configuration 5 | class Providers 6 | 7 | class Git 8 | include PreCommit::Utils::GitConversions 9 | 10 | def self.priority 11 | 10 12 | end 13 | 14 | def [](name) 15 | git_to_ruby(`git config pre-commit.#{name.to_s.gsub(/_/,".")} 2>/dev/null`) 16 | end 17 | 18 | def update(name, value) 19 | `git config pre-commit.#{name.to_s.gsub(/_/,".")} "#{ruby_to_git(value)}" 2>/dev/null` 20 | end 21 | 22 | end 23 | 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/git_old.rb: -------------------------------------------------------------------------------- 1 | require 'plugins/pre_commit/configuration/providers/git' 2 | 3 | module PreCommit 4 | class Configuration 5 | class Providers 6 | 7 | class GitOld < Git 8 | 9 | def self.priority 10 | 11 11 | end 12 | 13 | def [](name) 14 | value = super(name) 15 | if 16 | name == :checks && value && ! value.kind_of?(Array) 17 | then 18 | value = value.chomp.split(/,\s*/).map(&:to_sym) || [] 19 | update(name, value) unless value.empty? 20 | end 21 | value 22 | end 23 | 24 | end 25 | 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/plugins/pre_commit/configuration/providers/yaml.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require "pre-commit/configuration/top_level" 3 | 4 | module PreCommit 5 | class Configuration 6 | class Providers 7 | 8 | class Yaml 9 | include PreCommit::Configuration::TopLevel 10 | 11 | def self.priority 12 | 20 13 | end 14 | 15 | def [](name) 16 | config[name] 17 | end 18 | 19 | def update(name, value) 20 | content = read_config(local_file) 21 | content[name] = value 22 | save_config(local_file, content) 23 | end 24 | 25 | private 26 | 27 | def config 28 | @config ||= begin 29 | config = {} 30 | config.merge!(read_config(system_file)) 31 | config.merge!(read_config(global_file)) 32 | config.merge!(read_config(local_file)) 33 | config 34 | end 35 | end 36 | 37 | def read_config(path) 38 | content = YAML.load_file(path) if File.exist?(path) 39 | content || {} 40 | end 41 | 42 | def save_config(path, content) 43 | parent = File.expand_path('..', path) 44 | Dir.mkdir(parent) unless Dir.exist?(parent) 45 | File.open(path, "w") do |file| 46 | file.write(YAML.dump(content)) 47 | end 48 | end 49 | 50 | def system_file 51 | @system_file ||= '/etc/pre_commit.yml' 52 | end 53 | 54 | def global_file 55 | @global_file ||= File.join(ENV['HOME'], '.pre_commit.yml') 56 | end 57 | 58 | def local_file 59 | File.join(top_level, 'config', 'pre_commit.yml') 60 | end 61 | 62 | end 63 | 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/pre-commit.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/runner' 2 | 3 | ## 4 | # The pre-commit gem. 5 | # 6 | module PreCommit 7 | 8 | # Can not delete this method with out a deprecation strategy. 9 | # It is refered to in the generated pre-commit hook in versions 0.0-0.1.1 10 | # 11 | # NOTE: The deprecation strategy *may* be just delete it since, we're still 12 | # pre 1.0. 13 | # 14 | # Actually, on the deprecation note. This method isn't really the problem. 15 | # The problem is the default generated pre-commit hook. It shouldn't have 16 | # logic in it. The we have freedom to change the gem implementation however 17 | # we want, and nobody is forced to update their pre-commit binary. 18 | def self.checks_to_run 19 | warn "WARNING: You are using old hook version, you can update it with: pre-commit install" 20 | runner.list_to_run(:checks) 21 | end 22 | 23 | def self.run 24 | runner.run or exit 1 25 | end 26 | 27 | def self.runner 28 | @runner ||= PreCommit::Runner.new 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/pre-commit/checks.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit' 2 | -------------------------------------------------------------------------------- /lib/pre-commit/checks/grep.rb: -------------------------------------------------------------------------------- 1 | require 'open3' 2 | 3 | require 'pre-commit/checks/shell' 4 | require 'pre-commit/error_list' 5 | require 'pre-commit/line' 6 | 7 | module PreCommit 8 | module Checks 9 | class Grep < Shell 10 | def initialize(*) 11 | super 12 | 13 | @extra_grep = nil 14 | @message = nil 15 | @pattern = nil 16 | end 17 | 18 | class PaternNotSet < StandardError 19 | def message 20 | "Please define 'pattern' method." 21 | end 22 | end 23 | 24 | # overwrite those: 25 | 26 | def files_filter(staged_files) 27 | staged_files 28 | end 29 | 30 | def extra_grep 31 | @extra_grep or [] 32 | end 33 | 34 | def message 35 | @message or "" 36 | end 37 | 38 | def pattern 39 | @pattern or raise PaternNotSet.new 40 | end 41 | 42 | def extra_pattern 43 | @extra_pattern 44 | end 45 | 46 | # general code: 47 | 48 | def call(staged_files) 49 | staged_files = files_filter(staged_files) 50 | return if staged_files.empty? 51 | 52 | result = 53 | in_groups(staged_files).map do |files| 54 | args = grep + [pattern] + files 55 | args += ["|", "grep"] + extra_grep if !extra_grep.nil? and !extra_grep.empty? 56 | 57 | results = [ 58 | execute(args, success_status: false), 59 | extra_execute(files) 60 | ].compact 61 | 62 | results.empty? ? nil : results.join('') 63 | end.compact 64 | 65 | result.empty? ? nil : parse_errors(message, result) 66 | end 67 | 68 | private 69 | 70 | def parse_errors(message, list) 71 | result = PreCommit::ErrorList.new(message) 72 | result.errors += 73 | list.map do |group| 74 | group.split(/\n/) 75 | end.flatten.compact.map do |line| 76 | PreCommit::Line.new(nil, *parse_error(line)) 77 | end 78 | result 79 | end 80 | 81 | def parse_error(line) 82 | matches = /^([^:]+):([[:digit:]]+):(.*)$/.match(line) 83 | matches and matches.captures 84 | end 85 | 86 | def grep(grep_version = nil) 87 | grep_version ||= detect_grep_version 88 | if grep_version =~ /FreeBSD/ 89 | %w{grep -EnIH} 90 | else 91 | %w{grep -PnIH} 92 | end 93 | end 94 | 95 | def detect_grep_version 96 | Open3.popen3('grep', '--version') do |_, stdout, _| 97 | return '' if stdout.eof? 98 | 99 | first_line = stdout.readline 100 | return first_line.sub(/^[^0-9.]*\([0-9.]*\)$/, '\1') 101 | end 102 | end 103 | 104 | def extra_execute(files) 105 | return nil if extra_pattern.nil? or extra_pattern.empty? 106 | args = grep + [extra_pattern] + files 107 | 108 | execute(args, success_status: false) 109 | end 110 | 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /lib/pre-commit/checks/js.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | 3 | module PreCommit 4 | module Checks 5 | class Js < Plugin 6 | def call(staged_files) 7 | require 'execjs' 8 | rescue RuntimeError, LoadError => e 9 | $stderr.puts "Could not load execjs: #{e}. You need to manually install execjs to use JavaScript plugins for security reasons." 10 | else 11 | staged_files = files_filter(staged_files) 12 | return if staged_files.empty? 13 | 14 | errors = [] 15 | staged_files.each do |file| 16 | error_list = Array(run_check(file)) 17 | error_list.each { |error_object| errors << display_error(error_object, file) } 18 | end 19 | 20 | return if errors.empty? 21 | errors.join("\n") 22 | end 23 | 24 | def linter_src 25 | raise "Must be defined by subclass" 26 | end 27 | 28 | def files_filter(staged_files) 29 | staged_files.grep(/\.js$/) 30 | end 31 | 32 | def error_selector 33 | 'reason' 34 | end 35 | 36 | def display_error(error_object, file) 37 | return "" unless error_object 38 | 39 | line = error_object['line'].to_i + 1 40 | "#{error_object[error_selector]}\n#{file}:#{line} #{error_object['evidence']}" 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/pre-commit/checks/plugin.rb: -------------------------------------------------------------------------------- 1 | require 'plugins/pluginator/extensions/conversions' 2 | require 'pre-commit/checks/plugin/config_file' 3 | require 'pre-commit/line' 4 | 5 | module PreCommit 6 | module Checks 7 | class Plugin 8 | include Pluginator::Extensions::Conversions 9 | 10 | attr_accessor :pluginator, :config 11 | 12 | def initialize(pluginator, config, list) 13 | @pluginator = pluginator 14 | @config = config 15 | @list = list 16 | end 17 | 18 | def name 19 | class2string(class2name(self.class)) 20 | end 21 | 22 | private 23 | 24 | def config_file 25 | @config_file ||= ConfigFile.new(name, config, alternate_config_file) 26 | @config_file.location 27 | end 28 | 29 | def alternate_config_file 30 | '' 31 | end 32 | 33 | # group files in packs smaller then 127kB (1000 files) 34 | # 127k based on http://www.in-ulm.de/~mascheck/various/argmax/ 35 | # and 262144 limit on OSX - my env size /2 to be safe 36 | # assuming mean file length shorter then 127 chars splitting to 37 | # groups of 1000 files, each_slice for simplicity, doing real 38 | # check could be to time consuming 39 | def in_groups(files, group_size = 1000) 40 | files.each_slice(group_size) 41 | end 42 | 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/pre-commit/checks/plugin/config_file.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | module Checks 3 | class Plugin 4 | class ConfigFile 5 | def initialize(name, config, alternate_location) 6 | @name = name 7 | @config = config 8 | @alternate_location = alternate_location 9 | end 10 | 11 | def location 12 | return @location if defined?(@location) 13 | 14 | @location = location_from_config || location_from_alternate 15 | end 16 | 17 | private 18 | attr_reader :name, :config, :alternate_location 19 | 20 | def location_from_config 21 | location_from(config_location, true) 22 | end 23 | 24 | def location_from_alternate 25 | location_from(alternate_location) 26 | end 27 | 28 | def location_from(location, show_usage = false) 29 | if location && !location.empty? 30 | if File.exist?(location) 31 | location 32 | else 33 | usage if show_usage 34 | nil 35 | end 36 | end 37 | end 38 | 39 | def config_location 40 | @config_location ||= config.get(property_name) 41 | end 42 | 43 | def property_name 44 | "#{name}.config" 45 | end 46 | 47 | def environment_variable_name 48 | "#{name.upcase}_CONFIG" 49 | end 50 | 51 | def yaml_property_name 52 | property_name.gsub(/_/, '.') 53 | end 54 | 55 | def usage 56 | $stderr.puts "Warning: #{name} config file '#{config_location}' does not exist" 57 | $stderr.puts "Set the path to the config file using:" 58 | $stderr.puts "\tgit config pre-commit.#{property_name} 'path/relative/to/git/dir/#{name}.config'" 59 | $stderr.puts "Or in 'config/pre_commit.yml':" 60 | $stderr.puts "\t#{yaml_property_name}: path/relative/to/git/dir/#{name}.config" 61 | $stderr.puts "Or set the environment variable:" 62 | $stderr.puts "\texport #{environment_variable_name}='path/relative/to/git/dir/#{name}.config'" 63 | $stderr.puts "#{name} will look for a configuration file in the project root or use its default behavior.\n\n" 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/pre-commit/checks/shell.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/checks/plugin' 2 | require 'shellwords' 3 | 4 | module PreCommit 5 | module Checks 6 | class Shell < Plugin 7 | 8 | private 9 | 10 | def execute(*args) 11 | options = args.last.is_a?(::Hash) ? args.pop : {} 12 | args = build_command(*args) 13 | execute_raw(args, options) 14 | end 15 | 16 | def build_command(*args) 17 | args.flatten.map do |arg| 18 | arg = arg.shellescape if arg != '|' && arg != '&&' && arg != '||' 19 | arg 20 | end.join(" ") 21 | end 22 | 23 | def execute_raw(command, options = {}) 24 | result = `#{command} 2>&1` 25 | $?.success? == (options.fetch(:success_status, true)) ? nil : result 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/pre-commit/cli.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'pre-commit/configuration' 3 | require 'pre-commit/installer' 4 | require 'pre-commit/list_evaluator' 5 | require 'pre-commit/template' 6 | 7 | module PreCommit 8 | 9 | TemplateNotFound = Class.new(StandardError) 10 | 11 | class Cli 12 | 13 | def initialize(*args) 14 | @args = args 15 | end 16 | 17 | def execute() 18 | action_name = @args.shift or 'help' 19 | action = :"execute_#{action_name}" 20 | if respond_to?(action) 21 | then send(action, *@args) 22 | else execute_help(action_name, *@args) 23 | end 24 | end 25 | 26 | def execute_help(*args) 27 | warn "Unknown parameters: #{args.map(&:inspect) * " "}" unless args.empty? 28 | warn "Usage: pre-commit install" 29 | warn "Usage: pre-commit list" 30 | warn "Usage: pre-commit plugins" 31 | warn "Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin'" 32 | warn "Usage: pre-commit check1 [check2...]" 33 | args.empty? # return status, it's ok if user requested help 34 | end 35 | 36 | def execute_run(*args) 37 | require 'pre-commit/runner' 38 | PreCommit::Runner.new.run(*args).tap { |ok| puts "No failed checks." if ok } 39 | end 40 | 41 | def execute_install(key = nil, *args) 42 | PreCommit::Installer.new(key).install 43 | end 44 | 45 | def execute_list(*args) 46 | puts list_evaluator.list 47 | true 48 | end 49 | 50 | def execute_plugins(*args) 51 | puts list_evaluator.plugins 52 | true 53 | end 54 | 55 | def execute_new(*args) 56 | PreCommit::Template.new(*args).save 57 | rescue ArgumentError => e 58 | warn e 59 | warn "Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin'" 60 | end 61 | 62 | def execute_enable(*args) 63 | config.enable(*args) 64 | rescue ArgumentError 65 | execute_help('enable', *args) 66 | end 67 | 68 | def execute_disable(*args) 69 | config.disable(*args) 70 | rescue ArgumentError 71 | execute_help('disable', *args) 72 | end 73 | 74 | def config 75 | @config ||= PreCommit::Configuration.new(PreCommit.pluginator) 76 | end 77 | 78 | def list_evaluator 79 | @list_evaluator ||= PreCommit::ListEvaluator.new(config) 80 | end 81 | 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/pre-commit/configuration.rb: -------------------------------------------------------------------------------- 1 | require 'pluginator' 2 | require 'pre-commit/configuration/providers' 3 | require 'pre-commit/plugins_list' 4 | 5 | module PreCommit 6 | class Configuration 7 | attr_reader :pluginator, :providers 8 | 9 | def initialize(pluginator, providers = nil) 10 | @pluginator = (pluginator or PreCommit.pluginator) 11 | @providers = (providers or Providers.new(@pluginator)) 12 | end 13 | 14 | def get(name) 15 | @providers[name.to_sym] 16 | end 17 | 18 | def get_arr(name) 19 | value = get(name) 20 | case value 21 | when nil then [] 22 | when Array then value 23 | else raise PreCommit::NotAnArray.new 24 | end 25 | end 26 | 27 | def get_combined(name) 28 | get_arr(name) + get_arr("#{name}_add") 29 | end 30 | 31 | def enable(plugin_name, type, check1, *checks) 32 | checks.unshift(check1) # check1 is ArgumentError triger 33 | checks.map!(&:to_sym) 34 | @providers.update( plugin_name, "#{type}_remove", :-, checks ) 35 | @providers.update( plugin_name, "#{type}_add", :+, (checks or []) - (@providers.default(type) or []) ) 36 | true 37 | rescue PreCommit::PluginNotFound => e 38 | $stderr.puts e 39 | false 40 | end 41 | 42 | def disable(plugin_name, type, check1, *checks) 43 | checks.unshift(check1) # check1 is ArgumentError triger 44 | checks.map!(&:to_sym) 45 | @providers.update( plugin_name, "#{type}_add", :-, checks ) 46 | @providers.update( plugin_name, "#{type}_remove", :+, checks ) 47 | true 48 | rescue PreCommit::PluginNotFound => e 49 | warn e.message 50 | false 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/pre-commit/configuration/providers.rb: -------------------------------------------------------------------------------- 1 | require 'plugins/pluginator/extensions/conversions' 2 | 3 | module PreCommit 4 | class NotAnArray < StandardError 5 | end 6 | 7 | class PluginNotFound < StandardError 8 | end 9 | 10 | class Configuration 11 | class Providers 12 | include Pluginator::Extensions::Conversions 13 | 14 | def initialize(pluginator, plugins = nil) 15 | @pluginator = pluginator 16 | @plugins = plugins 17 | end 18 | 19 | def [](name) 20 | plugins.map{|plugin| plugin[name] }.compact.last 21 | end 22 | 23 | def default(name) 24 | plugins[0][name] 25 | end 26 | 27 | def update(plugin_name, name, operation, list) 28 | plugin = find_update_plugin(plugin_name) 29 | name = name.to_sym 30 | value = plugin[name] || [] 31 | raise PreCommit::NotAnArray.new unless Array === value 32 | value = value.send(operation, list) 33 | plugin.update(name, value) 34 | end 35 | 36 | def list 37 | plugins.map{|plugin| "#{class2string(class2name(plugin.class))}(#{plugin.class.priority})" } 38 | end 39 | 40 | private 41 | def plugins 42 | @plugins ||= @pluginator['configuration/providers'].sort_by(&:priority).map(&:new) 43 | end 44 | 45 | def find_update_plugin(plugin_name) 46 | plugins.detect{|plugin| class2string(class2name(plugin.class)) == plugin_name.to_s} || raise(PluginNotFound.new("Plugin not found for #{plugin_name}.")) 47 | end 48 | 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/pre-commit/configuration/top_level.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | class Configuration 3 | module TopLevel 4 | def top_level 5 | top_level = `git rev-parse --show-toplevel`.chomp.strip 6 | raise "no git repo!" if top_level == "" 7 | top_level 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/pre-commit/error_list.rb: -------------------------------------------------------------------------------- 1 | require 'pre-commit/line' 2 | 3 | module PreCommit 4 | class ErrorList < Struct.new :errors 5 | 6 | def initialize(errors = []) 7 | case errors 8 | when "",nil then errors = [] 9 | when String then errors = [PreCommit::Line.new(errors)] 10 | end 11 | super errors 12 | end 13 | 14 | def to_a 15 | errors.map(&:to_s) 16 | end 17 | 18 | def to_s 19 | to_a.join("\n") 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/pre-commit/installer.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'pre-commit/configuration' 3 | 4 | module PreCommit 5 | class Installer 6 | 7 | TARGET_GIT_PATH = '.git' 8 | TARGET_HOOKS_PATH = 'hooks/pre-commit' 9 | TEMPLATE_DIR = File.expand_path("../../../templates/hooks/", __FILE__) 10 | 11 | attr_reader :key 12 | 13 | def initialize(key = nil) 14 | @key = key || "automatic" 15 | end 16 | 17 | def hook 18 | templates[key.sub(/^--/, "")] 19 | end 20 | 21 | def target 22 | target_git_path = 23 | if File.directory?(TARGET_GIT_PATH) 24 | then TARGET_GIT_PATH 25 | else File.readlines('.git').first.match(/gitdir: (.*)$/)[1] 26 | end 27 | File.join(target_git_path, TARGET_HOOKS_PATH) 28 | end 29 | 30 | def install 31 | if 32 | hook 33 | then 34 | FileUtils.mkdir_p(File.dirname(target)) 35 | FileUtils.cp(hook, target) 36 | FileUtils.chmod(0755, target) 37 | puts "Installed #{hook} to #{target}" 38 | true 39 | else 40 | warn "Could not find template #{key}" 41 | false 42 | end 43 | end 44 | 45 | def templates 46 | @templates ||= begin 47 | pattern = File.join(TEMPLATE_DIR, "*") 48 | 49 | Dir.glob(pattern).inject({}) do |hash, file| 50 | key = file.match(/\/([^\/]+?)$/)[1] 51 | hash[key] = file 52 | hash 53 | end 54 | end 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/pre-commit/line.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | class Line < Struct.new :message, :file, :line, :code 3 | 4 | def to_s 5 | result = message.to_s 6 | unless empty? file 7 | result = "#{result}#{"\n" unless empty?(result)}#{file}" 8 | result = "#{result}:#{line}" unless empty? line 9 | result = "#{result}:#{code}" unless empty? code 10 | end 11 | result 12 | end 13 | 14 | protected 15 | 16 | def empty?(string) 17 | string == nil || string == "" 18 | end 19 | 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/pre-commit/list_evaluator.rb: -------------------------------------------------------------------------------- 1 | require 'pluginator' 2 | require 'pre-commit/configuration' 3 | require 'plugins/pluginator/extensions/conversions' 4 | require 'pre-commit/plugins_list' 5 | 6 | module PreCommit 7 | class ListEvaluator 8 | include Pluginator::Extensions::Conversions 9 | 10 | attr_reader :config 11 | 12 | def initialize(config) 13 | @config = config 14 | end 15 | 16 | def list 17 | <<-DATA 18 | Available providers: #{config.providers.list.join(" ")} 19 | Available checks : #{plugin_names.join(" ")} 20 | Default checks : #{config.get_arr(:checks).join(" ")} 21 | Enabled checks : #{checks_config.join(" ")} 22 | Evaluated checks : #{checks_evaluated.join(" ")} 23 | Default warnings : #{config.get_arr(:warnings).join(" ")} 24 | Enabled warnings : #{warnings_config.join(" ")} 25 | Evaluated warnings : #{warnings_evaluated.join(" ")} 26 | DATA 27 | end 28 | 29 | def plugins 30 | list = config.pluginator['checks'].map{|plugin| [class2string(class2name(plugin)), plugin] }.sort 31 | separator = list.map{|name, plugin| name.length }.max 32 | list.map{ |name, plugin| format_plugin(name, separator, plugin) }.flatten 33 | end 34 | 35 | def checks_config 36 | get_combined_checks - get_arr_checks_remove 37 | end 38 | 39 | def checks_evaluated(type = :evaluated_names) 40 | PreCommit::PluginsList.new(get_combined_checks, get_arr_checks_remove) do |name| 41 | config.pluginator.find_check(name) 42 | end.send(type) 43 | end 44 | 45 | def warnings_config 46 | get_combined_warnings - get_arr_warnings_remove 47 | end 48 | 49 | def warnings_evaluated(type = :evaluated_names) 50 | PreCommit::PluginsList.new(get_combined_warnings, get_arr_warnings_remove) do |name| 51 | config.pluginator.find_check(name) 52 | end.send(type) 53 | end 54 | 55 | private 56 | 57 | def plugin_names 58 | config.pluginator['checks'].map{|plugin| class2string(class2name(plugin)) }.sort 59 | end 60 | 61 | def format_plugin(name, separator, plugin) 62 | line = [sprintf("%#{separator}s : %s", name, plugin.description)] 63 | line << sprintf("%#{separator}s - includes: %s", "", plugin.includes.join(" ")) if plugin.respond_to?(:includes) 64 | line << sprintf("%#{separator}s - excludes: %s", "", plugin.excludes.join(" ")) if plugin.respond_to?(:excludes) 65 | line 66 | end 67 | 68 | def get_combined_checks 69 | @get_combined_checks ||= config.get_combined(:checks) 70 | end 71 | 72 | def get_arr_checks_remove 73 | @get_arr_checks_remove ||= config.get_arr("checks_remove") 74 | end 75 | 76 | def get_combined_warnings 77 | @get_combined_warnings ||= config.get_combined(:warnings) 78 | end 79 | 80 | def get_arr_warnings_remove 81 | @get_arr_warnings_remove ||= config.get_arr("warnings_remove") 82 | end 83 | 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/pre-commit/message.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | class Message < Struct.new :message, :lines 3 | 4 | def to_s 5 | result = message 6 | result = "#{message}\n#{lines.map(&:to_s).join("\n")}" unless lines.nil? 7 | result 8 | end 9 | 10 | protected 11 | 12 | def empty?(string) 13 | string == nil || string == "" 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/pre-commit/plugins_list.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | 3 | def self.pluginator 4 | Pluginator.find('pre_commit', :extends => [:find_check]) 5 | end 6 | 7 | class PluginsList 8 | attr_reader :configured_names, :configured_remove 9 | 10 | def initialize(configured_names, configured_remove, &block) 11 | @configured_names = configured_names 12 | @configured_remove = configured_remove 13 | @class_finder = block 14 | end 15 | 16 | def evaluated_names 17 | evaluated_names_(evaluated_names_pairs).flatten.compact 18 | end 19 | 20 | def list_to_run 21 | list_to_run_(evaluated_names_pairs).flatten.compact 22 | end 23 | 24 | private 25 | 26 | def evaluated_names_(list) 27 | list.map{|name, klass, includes| [name] + evaluated_names_(includes||[]) } 28 | end 29 | 30 | def list_to_run_(list) 31 | list.map{|name, klass, includes| [klass] + list_to_run_(includes||[]) } 32 | end 33 | 34 | def evaluated_names_pairs 35 | list = find_classes_and_includes(configured_names) 36 | excludes = excludes(list).flatten.compact 37 | list = filter_excludes(list, excludes) 38 | list = filter_excludes(list, configured_remove_aliases) 39 | list = filter_callable(list) 40 | list 41 | end 42 | 43 | def find_classes_and_includes(list) 44 | find_classes(list).map{ |name, klass| class_and_includes(name, klass) } 45 | end 46 | 47 | def find_class(name) 48 | @class_finder.call(name) 49 | end 50 | 51 | def find_classes(list) 52 | list.map{|name| [name, find_class(name)] }.reject{|name, klass| klass.nil? } 53 | end 54 | 55 | def class_and_includes(name, klass) 56 | [ name, klass, klass.respond_to?(:includes) ? find_classes_and_includes(klass.includes) : [] ] 57 | end 58 | 59 | def excludes(list) 60 | list.map{|name, klass, includes| class_excludes(klass) + excludes(includes) } 61 | end 62 | 63 | def class_excludes(klass) 64 | klass.respond_to?(:excludes) ? klass.excludes : [] 65 | end 66 | 67 | def filter_excludes(list, excludes) 68 | list.map{|name, klass, includes| excludes.include?(name) ? nil : [name, klass, filter_excludes(includes, excludes)] }.compact 69 | end 70 | 71 | def filter_callable(list) 72 | list.map{|name, klass, includes| 73 | (klass.instance_methods.include?(:call) ? [name, klass] : [nil, nil]) << filter_callable(includes) 74 | }.compact 75 | end 76 | 77 | def configured_remove_aliases 78 | @configured_remove_aliases ||= configured_remove.map{|remove| 79 | list = [remove] 80 | klass = find_class(remove) 81 | list += klass.aliases if klass.respond_to?(:aliases) 82 | list 83 | }.flatten.compact 84 | end 85 | 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/pre-commit/runner.rb: -------------------------------------------------------------------------------- 1 | require 'pluginator' 2 | require 'benchmark' 3 | 4 | require 'pre-commit/utils/staged_files' 5 | require 'pre-commit/configuration' 6 | require 'pre-commit/list_evaluator' 7 | require 'pre-commit/error_list' 8 | 9 | module PreCommit 10 | class Runner 11 | include PreCommit::Utils::StagedFiles 12 | 13 | attr_reader :pluginator, :config, :debug 14 | 15 | def initialize(stderr = nil, staged_files = nil, config = nil, pluginator = nil) 16 | @stderr = (stderr or $stderr) 17 | @pluginator = (pluginator or PreCommit.pluginator) 18 | @config = (config or PreCommit::Configuration.new(@pluginator)) 19 | @staged_files = staged_files 20 | @debug = ENV["PRE_COMMIT_DEBUG"] 21 | end 22 | 23 | def run(*args) 24 | set_staged_files(*args) 25 | run_single(:warnings) 26 | run_single(:checks ) or return false 27 | true 28 | end 29 | 30 | def run_single(name) 31 | show_output(name, execute(list_to_run(name))) 32 | end 33 | 34 | def show_output(name, list) 35 | if list.any? 36 | @stderr.puts send(name, list) 37 | return false 38 | end 39 | true 40 | end 41 | 42 | def execute(list) 43 | list.map do |cmd| 44 | result = nil 45 | 46 | seconds = Benchmark.realtime do 47 | result = cmd.new(pluginator, config, list).call(staged_files.dup) 48 | end 49 | 50 | puts "#{cmd} #{seconds*1000}ms" if debug 51 | 52 | result 53 | end.compact 54 | end 55 | 56 | def list_to_run(name) 57 | list_evaluator.send(:"#{name}_evaluated", :list_to_run) 58 | end 59 | 60 | def list_evaluator 61 | @list_evaluator ||= PreCommit::ListEvaluator.new(config) 62 | end 63 | 64 | def warnings(list) 65 | <<-WARNINGS 66 | pre-commit: Some warnings were raised. These will not stop commit: 67 | #{errors_to_string(list)} 68 | WARNINGS 69 | end 70 | 71 | def checks(list) 72 | <<-ERRORS 73 | pre-commit: Stopping commit because of errors. 74 | #{errors_to_string(list)} 75 | pre-commit: You can bypass this check using `git commit -n` 76 | 77 | ERRORS 78 | end 79 | 80 | def errors_to_string(list) 81 | list.map(&:to_s).join("\n") 82 | end 83 | 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/pre-commit/template.rb: -------------------------------------------------------------------------------- 1 | require "date" 2 | require "erb" 3 | require "fileutils" 4 | 5 | module PreCommit 6 | class Template 7 | TEMPLATE_DIR = File.expand_path("../../../templates/gem", __FILE__) 8 | 9 | attr_reader :name, :author, :email, :description, :gem_name, :copyright 10 | 11 | def initialize(*args) 12 | @name, @author, @email, @description = args 13 | @gem_name = "pre-commit-#{name}" 14 | @copyright = "#{Date.today.year} #{author} #{email}" 15 | validate_params 16 | end 17 | 18 | def save 19 | puts "Generating #{gem_name}" 20 | all_files.each{|file| parse_and_save(file) } 21 | initialize_git 22 | puts <<-STEPS 23 | 24 | Next steps: 25 | - write your checks and tests for them 26 | - push code to github 27 | - open a ticket to merge your project: https://github.com/jish/pre-commit/issues 28 | STEPS 29 | end 30 | 31 | def all_files 32 | Dir.glob("#{TEMPLATE_DIR}/**/*", File::FNM_DOTMATCH) 33 | .reject { |path| File.directory?(path) } 34 | end 35 | 36 | def target_path(file) 37 | file 38 | .sub(TEMPLATE_DIR, gem_name) 39 | .sub("GEM_NAME", gem_name) 40 | .sub("PLUGIN_NAME", name) 41 | end 42 | 43 | private 44 | 45 | def validate_params 46 | raise ArgumentError, "Missing name" if name.nil? || name.empty? 47 | raise ArgumentError, "Missing author" if author.nil? || author.empty? 48 | raise ArgumentError, "Missing email" if email.nil? || email.empty? 49 | raise ArgumentError, "Missing description" if description.nil? || description.empty? 50 | raise ArgumentError, "#{gem_name} already exists" if File.directory?(gem_name) 51 | end 52 | 53 | def parse_and_save(file) 54 | save_file( 55 | target_path(file), 56 | parse_template(file), 57 | ) 58 | end 59 | 60 | def save_file(path, content) 61 | FileUtils.mkdir_p(File.expand_path("..", path)) 62 | File.write(path, content, 0, mode: "w") 63 | end 64 | 65 | def parse_template(path) 66 | ERB.new( 67 | File.read(path) 68 | ).result( 69 | binding 70 | ) 71 | end 72 | 73 | def initialize_git 74 | return if `which git 2>/dev/null`.empty? 75 | 76 | Dir.chdir gem_name do 77 | puts `git init` 78 | puts `git add .` 79 | puts `git commit -m "created #{gem_name}"` 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/pre-commit/utils/git_conversions.rb: -------------------------------------------------------------------------------- 1 | module PreCommit 2 | module Utils 3 | module GitConversions 4 | 5 | # git_to_ruby related 6 | 7 | def git_to_ruby(value) 8 | value = value.chomp.strip if String === value 9 | case value 10 | when /\A\[(.*)\]\Z/ 11 | str2arr($1) 12 | when '' 13 | nil 14 | when String 15 | str_symbolize(value) 16 | else 17 | value 18 | end 19 | end 20 | 21 | def str2arr(string) 22 | string.split(/, ?/).map{|s| str_symbolize(s.chomp.strip) } 23 | end 24 | 25 | def str_symbolize(string) 26 | if string =~ /\A:(.*)\Z/ 27 | then $1.to_sym 28 | else string 29 | end 30 | end 31 | 32 | # ruby_to_git related 33 | 34 | def ruby_to_git(value) 35 | case value 36 | when Array 37 | arr2str(value) 38 | else 39 | sym_symbolize(value) 40 | end 41 | end 42 | 43 | def arr2str(value) 44 | "[#{value.map{|v| sym_symbolize(v) }.join(", ")}]" 45 | end 46 | 47 | def sym_symbolize(value) 48 | if Symbol === value 49 | then ":#{value}" 50 | else value.to_s 51 | end 52 | end 53 | 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/pre-commit/utils/staged_files.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require 'pre-commit/configuration/top_level' 4 | 5 | module PreCommit 6 | module Utils 7 | module StagedFiles 8 | include PreCommit::Configuration::TopLevel 9 | 10 | BINARIES = [ 11 | ".dmg", ".gz", ".img", ".iso", ".rar", ".tar", ".xz", ".zip" 12 | ].freeze 13 | IMAGES = [".gif", ".ico", ".jpeg", ".jpg", ".pdf", ".png"].freeze 14 | IGNORED_EXTENSIONS = BINARIES + IMAGES 15 | 16 | SOURCE_FILES = [ 17 | ".coffee", ".css", ".erb", ".feature", ".html", ".js", ".json", 18 | ".liquid", ".md", ".rb", ".sass", ".scss", ".slim", ".yml" 19 | ].freeze 20 | 21 | def set_staged_files(*args) 22 | case args[0].to_s 23 | when "all" then staged_files_all 24 | when "git" then staged_files_git_all 25 | when "" then staged_files 26 | else self.staged_files=args 27 | end 28 | end 29 | 30 | def staged_files=(args) 31 | @staged_files = args 32 | end 33 | 34 | def staged_files 35 | @staged_files ||= filter_files(staged_from_git) 36 | end 37 | 38 | def staged_files_all 39 | @staged_files = filter_files(staged_from_dir) 40 | end 41 | 42 | def staged_files_git_all 43 | @staged_files = filter_files(staged_from_git_all) 44 | end 45 | 46 | # Definitely include this file in the checks. 47 | def source_file?(filename) 48 | SOURCE_FILES.include?(File.extname(filename)) 49 | end 50 | 51 | # Try to bail out quickly based on filename. 52 | # 53 | # If the extension is `.jpg` this is likely not a source code file. 54 | # So let's not waste time checking to see if it's "binary" (as best we 55 | # can guess) and let's not run any checks on it. 56 | def ignore_extension?(filename) 57 | IGNORED_EXTENSIONS.include?(File.extname(filename)) 58 | end 59 | 60 | # Make a best guess to determine if this is a binary file. 61 | # This is not an exact science ;) 62 | def appears_binary?(filename) 63 | size = File.size(filename) 64 | size > 1_000_000 || (size > 20 && binary?(filename)) 65 | end 66 | 67 | def repo_ignored?(filename) 68 | repo_ignores.any? { |ignore| File.fnmatch?(ignore, filename) } 69 | end 70 | 71 | private 72 | # from https://github.com/djberg96/ptools/blob/master/lib/ptools.rb#L90 73 | def binary?(file) 74 | bytes = File.stat(file).blksize.to_i 75 | bytes = 4096 if bytes > 4096 76 | s = (File.read(file, bytes) || "").split(//) 77 | 78 | ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30 79 | end 80 | 81 | def filter_files(files) 82 | first_pass = files.reject do |file| 83 | repo_ignored?(file) || 84 | ignore_extension?(file) || 85 | File.directory?(file) || 86 | !File.exist?(file) 87 | end 88 | 89 | # If it's a source file, definitely check it. 90 | # Otherwise, attempt to guess if the file is binary or not. 91 | first_pass.select do |file| 92 | source_file?(file) || !appears_binary?(file) 93 | end 94 | end 95 | 96 | def staged_from_git 97 | `git diff --cached --name-only --diff-filter=ACM`.split(/\n/) 98 | end 99 | 100 | def staged_from_git_all 101 | `git ls-files`.split(/\n/) 102 | end 103 | 104 | def staged_from_dir 105 | Dir["**/*"] 106 | end 107 | 108 | def repo_ignores_file 109 | File.join(top_level, ".pre_commit.ignore") 110 | end 111 | 112 | def repo_ignores 113 | @repo_ignores ||= File.read(repo_ignores_file).split("\n").compact 114 | rescue Errno::ENOENT 115 | @repo_ignores = [] 116 | end 117 | 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /pre-commit.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{pre-commit} 5 | s.version = "0.40.0" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["Shajith Chacko, Josh Lubaway"] 9 | s.default_executable = %q{pre-commit} 10 | s.email = %q{dontneedmoreemail@example.com} 11 | s.executables = ["pre-commit"] 12 | s.extra_rdoc_files = ["README.md"] 13 | s.files = Dir["bin/*", "lib/**/*", "templates/**/*"] 14 | s.homepage = %q{http://github.com/jish/pre-commit} 15 | s.rdoc_options = ["--main", "README.md"] 16 | s.require_paths = ["lib"] 17 | s.rubygems_version = %q{1.3.7} 18 | s.license = 'Apache-2.0' 19 | s.summary = "A slightly better git pre-commit hook" 20 | s.description = "A git pre-commit hook written in ruby with a few more tricks up its sleeve" 21 | 22 | s.add_dependency('pluginator', '~> 1.5') 23 | 24 | s.add_development_dependency('benchmark-ips') 25 | s.add_development_dependency('minitest', '~> 5.0') 26 | s.add_development_dependency('minitest-reporters', '~> 1.0') 27 | s.add_development_dependency('rake', '~> 10.0') 28 | s.add_development_dependency('rubocop', '~> 0.49') 29 | 30 | if s.respond_to? :specification_version then 31 | s.specification_version = 3 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /script/benchmark/staged_files.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark/ips' 2 | require 'pre-commit/utils/staged_files' 3 | 4 | class Subject 5 | include PreCommit::Utils::StagedFiles 6 | 7 | def filter_files1(files) 8 | first_pass = files 9 | .reject { |file| repo_ignored?(file) } 10 | .reject { |file| ignore_extension?(file) } 11 | .reject { |file| File.directory?(file) } 12 | .select { |file| File.exist?(file) } 13 | 14 | # If it's a source file, definitely check it. 15 | # Otherwise, attempt to guess if the file is binary or not. 16 | first_pass.select do |file| 17 | source_file?(file) || !appears_binary?(file) 18 | end 19 | end 20 | 21 | def filter_files2(files) 22 | first_pass = files.lazy 23 | .reject { |file| repo_ignored?(file) } 24 | .reject { |file| ignore_extension?(file) } 25 | .reject { |file| File.directory?(file) } 26 | .select { |file| File.exist?(file) } 27 | 28 | # If it's a source file, definitely check it. 29 | # Otherwise, attempt to guess if the file is binary or not. 30 | first_pass.select do |file| 31 | source_file?(file) || !appears_binary?(file) 32 | end 33 | end 34 | 35 | def filter_files3(files) 36 | first_pass = files.reject do |file| 37 | repo_ignored?(file) || 38 | ignore_extension?(file) || 39 | File.directory?(file) || 40 | !File.exist?(file) 41 | end 42 | 43 | # If it's a source file, definitely check it. 44 | # Otherwise, attempt to guess if the file is binary or not. 45 | first_pass.select do |file| 46 | source_file?(file) || !appears_binary?(file) 47 | end 48 | end 49 | end 50 | 51 | files = Dir['lib/**/*'] 52 | subject = Subject.new 53 | 54 | Benchmark.ips do |x| 55 | x.report('filter files 1') { subject.filter_files1(files) } 56 | x.report('filter files 2') { subject.filter_files2(files).to_a } 57 | x.report('filter files 3') { subject.filter_files3(files) } 58 | 59 | x.compare! 60 | end 61 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | which npm > /dev/null 2>&1 4 | if [[ $? -eq 0 ]]; then 5 | 6 | echo "npm installed ✓" 7 | echo "Checking for coffeelint..." 8 | 9 | npm list --global coffeelint > /dev/null 2>&1 10 | if [[ $? -ne 0 ]]; then 11 | 12 | echo "Installing coffeelint..." 13 | npm install -g coffeelint 14 | 15 | else 16 | echo "coffeelint installed ✓" 17 | fi 18 | 19 | else 20 | 21 | echo "npm is missing, aborting bootstrap." 22 | echo "Please install npm." 23 | 24 | fi 25 | 26 | bundle check || bundle install 27 | -------------------------------------------------------------------------------- /templates/gem/.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | Gemfile.lock 3 | coverage/ 4 | *.gem 5 | -------------------------------------------------------------------------------- /templates/gem/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | sudo: false 4 | bundler_args: "" 5 | rvm: 6 | - jruby 7 | - rubinius 8 | - ruby-2.0.0 9 | - ruby-2.1 10 | - ruby-2.2 11 | - ruby-2.3.0 12 | - ruby-2.5.1 13 | matrix: 14 | fast_finish: true 15 | allow_failures: 16 | - rvm: rubinius 17 | -------------------------------------------------------------------------------- /templates/gem/GEM_NAME.gemspec: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | # -*- encoding: utf-8 -*- 8 | 9 | lib = File.expand_path('../lib', __FILE__) 10 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 11 | require 'pre-commit/<%= name %>/version' 12 | 13 | Gem::Specification.new do |s| 14 | s.name = "<%= gem_name %>" 15 | s.version = PreCommit::<%= name.capitalize %>::VERSION 16 | s.author = "<%= author %>" 17 | s.email = "<%= email %>" 18 | s.homepage = "https://github.com/pre-commit-plugins/<%= gem_name %>" 19 | s.license = "MIT" 20 | s.summary = "<%= description %>" 21 | 22 | s.extra_rdoc_files = ["README.md"] 23 | s.files = Dir["lib/**/*"] 24 | 25 | s.add_dependency("pre-commit") 26 | 27 | s.add_development_dependency("guard", "~> 2.0") 28 | s.add_development_dependency("guard-minitest", "~> 2.0") 29 | s.add_development_dependency("minitest", "~> 4.0") 30 | s.add_development_dependency("minitest-reporters", "~> 0") 31 | s.add_development_dependency("mocha", "~>1.1") 32 | s.add_development_dependency("rake", "~> 10.0") 33 | end 34 | -------------------------------------------------------------------------------- /templates/gem/Gemfile: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | source "https://rubygems.org" 8 | 9 | gemspec 10 | 11 | group :development do 12 | # statistics only on MRI 2.0 - avoid problems on older rubies 13 | gem "redcarpet", :platforms => [:mri_20] 14 | gem "simplecov", :platforms => [:mri_20] 15 | gem "coveralls", :platforms => [:mri_20] 16 | end 17 | -------------------------------------------------------------------------------- /templates/gem/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) <%= copyright %> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /templates/gem/README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/<%= gem_name %>.png)](https://rubygems.org/gems/<%= gem_name %>) 2 | [![Build Status](https://secure.travis-ci.org/pre-commit-plugins/<%= gem_name %>.png?branch=master)](https://travis-ci.org/pre-commit-plugins/<%= gem_name %>) 3 | [![Dependency Status](https://gemnasium.com/pre-commit-plugins/<%= gem_name %>.png)](https://gemnasium.com/pre-commit-plugins/<%= gem_name %>) 4 | [![Code Climate](https://codeclimate.com/github/pre-commit-plugins/<%= gem_name %>.png)](https://codeclimate.com/github/pre-commit-plugins/<%= gem_name %>) 5 | [![Coverage Status](https://img.shields.io/coveralls/pre-commit-plugins/<%= gem_name %>.svg)](https://coveralls.io/r/pre-commit-plugins/<%= gem_name %>?branch=master) 6 | [![Inline docs](http://inch-ci.org/github/pre-commit-plugins/<%= gem_name %>.png)](http://inch-ci.org/github/pre-commit-plugins/<%= gem_name %>) 7 | [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/pre-commit-plugins/<%= gem_name %>/master/frames) 8 | [![Github Code](http://img.shields.io/badge/github-code-blue.svg)](https://github.com/pre-commit-plugins/<%= gem_name %>) 9 | 10 | # <%= name.capitalize %> for pre-commit gem 11 | 12 | ### Installation 13 | 14 | gem install <%= gem_name %> 15 | 16 | ### Usage 17 | 18 | pre-commit enable yaml checks <%= name %> 19 | -------------------------------------------------------------------------------- /templates/gem/Rakefile: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | require "rake/testtask" 8 | 9 | Rake::TestTask.new do |test| 10 | test.libs << "test" << "lib" 11 | test.pattern = "test/**/*_test.rb" 12 | test.verbose = true 13 | end 14 | 15 | namespace :pre_commit do 16 | task :ci => [:test] 17 | end 18 | 19 | task :ci => [:test] 20 | task :default => [:test] 21 | -------------------------------------------------------------------------------- /templates/gem/lib/plugins/pre_commit/checks/PLUGIN_NAME.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | require 'pre-commit/error_list' 8 | require 'pre-commit/checks/plugin' 9 | 10 | # :nodoc: 11 | module PreCommit 12 | # :nodoc: 13 | module Checks 14 | 15 | # 16 | # <%= description %> 17 | # 18 | class <%= name.capitalize %> < Plugin 19 | 20 | # 21 | # description of the plugin 22 | # 23 | def self.description 24 | "<%= description %>" 25 | end 26 | 27 | # 28 | # Finds files and verify them 29 | # 30 | # @param staged_files [Array] list of files to check 31 | # 32 | # @return [nil|Array] nil when no errors, 33 | # list of errors otherwise 34 | def call(staged_files) 35 | errors = staged_files.map { |file| run_check(file) }.compact 36 | return if errors.empty? 37 | 38 | errors 39 | end 40 | 41 | private 42 | 43 | # 44 | # <%= description %> 45 | # 46 | # @param file [String] path to file to verify 47 | # 48 | # @return [nil|PreCommit::ErrorList] nil when file verified, 49 | # ErrorList when verification failed 50 | # 51 | def run_check(file) 52 | if 53 | true # add a check here to verify files 54 | then 55 | nil 56 | else 57 | PreCommit::ErrorList.new(PreCommit::Line.new("Describe why verification failed", file)) 58 | end 59 | end 60 | 61 | end 62 | 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /templates/gem/lib/pre-commit/PLUGIN_NAME/version.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | # :nodoc: 8 | module PreCommit 9 | # placeholder for gem VERSION 10 | module <%= name.capitalize %> 11 | # the gem version, rules of versioning: http://semver.org/ 12 | VERSION = "1.0.0" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /templates/gem/test/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jish/pre-commit/c901efa81b86c7c371ef4f64df6770b2f9df4d2d/templates/gem/test/files/.keep -------------------------------------------------------------------------------- /templates/gem/test/minitest_helper.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | if 8 | RUBY_VERSION == "2.0.0" # check Gemfile 9 | then 10 | require "coveralls" 11 | require "simplecov" 12 | 13 | SimpleCov.start do 14 | formatter SimpleCov::Formatter::MultiFormatter[ 15 | SimpleCov::Formatter::HTMLFormatter, 16 | Coveralls::SimpleCov::Formatter, 17 | ] 18 | command_name "Spesc Tests" 19 | add_filter "/test/" 20 | end 21 | 22 | Coveralls.noisy = true unless ENV['CI'] 23 | end 24 | 25 | require 'minitest/autorun' 26 | require "minitest/reporters" 27 | require "mocha/setup" 28 | require 'pluginator' 29 | 30 | module PreCommit; module Helpers 31 | 32 | def test_files 33 | File.expand_path("../files", __FILE__) 34 | end 35 | 36 | end; end 37 | 38 | class MiniTest::Unit::TestCase 39 | include PreCommit::Helpers 40 | end 41 | 42 | Dir['lib/**/*.rb'].each { |file| require "./#{file}" } # coverals trick for all files 43 | 44 | Minitest::Reporters.use! 45 | -------------------------------------------------------------------------------- /templates/gem/test/plugins/pre_commit/checks/PLUGIN_NAME_test.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright <%= copyright %> 3 | 4 | See the file LICENSE for copying permission. 5 | =end 6 | 7 | require 'minitest_helper' 8 | require 'plugins/pre_commit/checks/<%= name %>' 9 | 10 | describe PreCommit::Checks::<%= name.capitalize %> do 11 | let(:check){ PreCommit::Checks::<%= name.capitalize %>.new(nil, nil, []) } 12 | 13 | it "does nothing" do 14 | check.send(:run_check, "rake").must_be_nil 15 | end 16 | 17 | it "checks files" do 18 | Dir.chdir(test_files) do 19 | # TODO: create example files in test/files 20 | check.send(:run_check, ".keep").must_be_nil 21 | end 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /templates/hooks/automatic: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This hook has a focus on portability. 4 | # This hook will attempt to setup your environment before running checks. 5 | # 6 | # If you would like `pre-commit` to get out of your way and you are comfortable 7 | # setting up your own environment, you can install the manual hook using: 8 | # 9 | # pre-commit install --manual 10 | # 11 | 12 | # This is a work-around to get GitHub for Mac to be able to run `node` commands 13 | # https://stackoverflow.com/questions/12881975/git-pre-commit-hook-failing-in-github-for-mac-works-on-command-line 14 | PATH=$PATH:/usr/local/bin:/usr/local/sbin 15 | 16 | 17 | cmd=`git config pre-commit.ruby 2>/dev/null` 18 | if test -n "${cmd}" 19 | then true 20 | elif which rvm >/dev/null 2>/dev/null 21 | then cmd="rvm default do ruby" 22 | elif which rbenv >/dev/null 2>/dev/null 23 | then cmd="rbenv exec ruby" 24 | else cmd="ruby" 25 | fi 26 | 27 | export rvm_silence_path_mismatch_check_flag=1 28 | 29 | ${cmd} -rrubygems -e ' 30 | begin 31 | require "pre-commit" 32 | true 33 | rescue LoadError => e 34 | $stderr.puts <<-MESSAGE 35 | pre-commit: WARNING: Skipping checks because: #{e} 36 | pre-commit: Did you set your Ruby version? 37 | MESSAGE 38 | false 39 | end and PreCommit.run 40 | ' 41 | -------------------------------------------------------------------------------- /templates/hooks/manual: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This hook has a focus on simplicity. 4 | # This hook puts the burden on you to set up your environment properly. 5 | # 6 | # If you would like `pre-commit` to attempt to setup your environment for you 7 | # before the checks run, you can install the automatic environment hook using: 8 | # 9 | # pre-commit install --automatic 10 | # 11 | 12 | require 'pre-commit' 13 | 14 | PreCommit.run 15 | -------------------------------------------------------------------------------- /templates/hooks/simple: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This hook has a focus on simplicity. 4 | # This hook puts the burden on you to set up your environment properly. 5 | # 6 | # If you would like `pre-commit` to attempt to setup your environment for you 7 | # before the checks run, you can install the automatic environment hook using: 8 | # 9 | # pre-commit install --automatic 10 | # 11 | 12 | require 'pre-commit' 13 | 14 | PreCommit.run 15 | -------------------------------------------------------------------------------- /templates/hooks/sourcetree: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This hook can be used with SourceTree. 4 | # This hook will attempt to setup your environment before running checks. 5 | # 6 | # If you would like `pre-commit` to get out of your way and you are comfortable 7 | # setting up your own environment, you can install the simple hook using: 8 | # 9 | # pre-commit install --simple 10 | # 11 | 12 | # This is a work-around to get GitHub for Mac to be able to run `node` commands 13 | # https://stackoverflow.com/questions/12881975/git-pre-commit-hook-failing-in-github-for-mac-works-on-command-line 14 | PATH=$PATH:/usr/local/bin:/usr/local/sbin 15 | 16 | RVM_PATH=$HOME/.rvm/bin 17 | if [ -d "$RVM_PATH" ] && [[ ! $PATH =~ (^|:)$RVM_PATH(:|$) ]]; then 18 | PATH+=:$RVM_PATH 19 | fi 20 | 21 | export LC_CTYPE="en_US.UTF-8" 22 | export rvm_silence_path_mismatch_check_flag=1 23 | 24 | rvm default do bundle exec ruby -rrubygems -e ' 25 | begin 26 | require "pre-commit" 27 | true 28 | rescue LoadError => e 29 | $stderr.puts <<-MESSAGE 30 | pre-commit: WARNING: Skipping checks because: #{e} 31 | pre-commit: Did you set your Ruby version? 32 | MESSAGE 33 | false 34 | end and PreCommit.run 35 | ' 36 | -------------------------------------------------------------------------------- /test/files/bad-spec.rb: -------------------------------------------------------------------------------- 1 | describe Bad do 2 | context "functionality", :focus do 3 | it "behaves as desired" do 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/files/bad.coffee: -------------------------------------------------------------------------------- 1 | class helloWorld 2 | say: -> 3 | console.log('Hello World!') 4 | -------------------------------------------------------------------------------- /test/files/bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "bad": "json" 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /test/files/bad.scss: -------------------------------------------------------------------------------- 1 | .border { 2 | margin: 2px; 3 | border: 2px solid; 4 | } 5 | -------------------------------------------------------------------------------- /test/files/bad.yml: -------------------------------------------------------------------------------- 1 | dbal: 2 | driver: %database_driver% 3 | -------------------------------------------------------------------------------- /test/files/bad_file.css: -------------------------------------------------------------------------------- 1 | a 2 | [ 3 | color:#cfc2b5 4 | ] 5 | -------------------------------------------------------------------------------- /test/files/bad_file.js: -------------------------------------------------------------------------------- 1 | var f = function() { 2 | if (false) 3 | alert('foo'); 4 | } 5 | -------------------------------------------------------------------------------- /test/files/bad_fmt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /test/files/bad_spec.rb: -------------------------------------------------------------------------------- 1 | describe Bad do 2 | context "functionality", :focus do 3 | it "behaves as desired" do 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/files/bad_tabs2.rb: -------------------------------------------------------------------------------- 1 | class BadTabs2 2 | def foo 3 | puts "hello" # space, then tab, then start code 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/files/before_all_spec.rb: -------------------------------------------------------------------------------- 1 | describe Foo do 2 | before(:all) do 3 | # la la la 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/files/before_all_spec_2.rb: -------------------------------------------------------------------------------- 1 | describe Foo do 2 | before :all do 3 | # la la la 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/files/byebug_file.rb: -------------------------------------------------------------------------------- 1 | class ByebugFile 2 | def complicated_method 3 | byebug 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/files/changelog.md: -------------------------------------------------------------------------------- 1 | ### v0.11.0 -- April 10th, 2012 2 | 3 | Public: 4 | 5 | * Added agent collision to ticket pages 6 | * Added a reload your browser modal when we update. 7 | 8 | Bugs: 9 | 10 | * Voice availability icon should not show up if voice is disabled. 11 | * Chat availability icon should not show up if chat is disabled. 12 | 13 | Internal: 14 | 15 | * Make chat shut up (console.log) in production. 16 | -------------------------------------------------------------------------------- /test/files/console_log.js: -------------------------------------------------------------------------------- 1 | var foo = function() { 2 | }; 3 | 4 | foo.prototype = { 5 | bar: function() { 6 | console.log("I'm in bar"); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test/files/debugger_file.rb: -------------------------------------------------------------------------------- 1 | class DebuggerFile 2 | def blam 3 | debugger 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/files/dont_compile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /test/files/file_with_nb_space.rb: -------------------------------------------------------------------------------- 1 | test 2 | nb_space = " " 3 | blub 4 | -------------------------------------------------------------------------------- /test/files/filename with spaces.rb: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/files/good.coffee: -------------------------------------------------------------------------------- 1 | class HelloWorld 2 | say: -> 3 | console.log('Hello World!') 4 | -------------------------------------------------------------------------------- /test/files/good.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Printf("I'm good\n") 7 | } 8 | -------------------------------------------------------------------------------- /test/files/good.json: -------------------------------------------------------------------------------- 1 | { 2 | "good": "json" 3 | } 4 | -------------------------------------------------------------------------------- /test/files/good.scss: -------------------------------------------------------------------------------- 1 | .border { 2 | border: 2px solid; 3 | margin: 2px; 4 | } 5 | -------------------------------------------------------------------------------- /test/files/good.yml: -------------------------------------------------------------------------------- 1 | dbal: 2 | driver: "%database_driver%" 3 | -------------------------------------------------------------------------------- /test/files/good_spec.rb: -------------------------------------------------------------------------------- 1 | describe Good do 2 | context "functionality" do 3 | it "behaves as desired" do 4 | 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/files/initial_tab.rb: -------------------------------------------------------------------------------- 1 | class Greeter 2 | def say_hello 3 | 'hello' 4 | 'goodbye' 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/files/merge_conflict.rb: -------------------------------------------------------------------------------- 1 | class Greeter 2 | def say_hello 3 | <<<<<<< HEAD 4 | 'hello' 5 | ======= 6 | 'goodbye' 7 | >>>>>>> foo 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/files/pre-commit.rb: -------------------------------------------------------------------------------- 1 | raise unless ARGV.empty? 2 | -------------------------------------------------------------------------------- /test/files/property_sets-0.3.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jish/pre-commit/c901efa81b86c7c371ef4f64df6770b2f9df4d2d/test/files/property_sets-0.3.0.gem -------------------------------------------------------------------------------- /test/files/pry_file.rb: -------------------------------------------------------------------------------- 1 | class PryFile 2 | def blam 3 | binding.pry 4 | binding.remote_pry 5 | binding.remote_pry_em 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/files/rspec_focus_bad_spec.rb: -------------------------------------------------------------------------------- 1 | describe RspecFocusChecks do 2 | context 'with old hash syntax', :focus => true do 3 | describe 'focus on describe', :focus => true do 4 | it 'alerts with focus on example too', :focus => true do 5 | end 6 | end 7 | end 8 | 9 | context 'with new hash syntax', focus: true do 10 | describe 'focus on describe', focus: true do 11 | it 'alerts with focus on example too', focus: true do 12 | end 13 | end 14 | end 15 | 16 | context 'with symbols as keys', :focus do 17 | describe 'focus on describe', :focus do 18 | it 'alerts with focus on example too', :focus do 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/files/rspec_focus_good_spec.rb: -------------------------------------------------------------------------------- 1 | describe FocusModel do 2 | it 'should not trigger the pre-commit focus detector with old hash syntax' do 3 | expect(FocusModel.new(:focus => true)).to be_valid 4 | end 5 | 6 | it 'should not trigger the pre-commit focus detector with new hash syntax' do 7 | expect(FocusModel.new(focus: true)).to be_valid 8 | end 9 | 10 | it 'should not trigger the pre-commit focus detector with a has key' do 11 | focus = FocusModel.new 12 | expect(focus.value_for[:focus]).to eq 'undetected' 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/files/rspec_focus_sugar_bad_spec.rb: -------------------------------------------------------------------------------- 1 | describe RspecFocusSugarChecks do 2 | fcontext 'f prefixed to context' do 3 | fdescribe 'f prefixed to describe' do 4 | fit 'f prefixed to it' do 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/files/rspec_focus_sugar_good_spec.rb: -------------------------------------------------------------------------------- 1 | describe FitModel do 2 | it 'does not trigger the pre-commit focus with fit in description' do 3 | end 4 | 5 | it 'does not trigger the pre-commit focus with fdescribe in description' do 6 | end 7 | 8 | it 'does not trigger the pre-commit focus with fcontext in description' do 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/files/serialized.yml: -------------------------------------------------------------------------------- 1 | --- !ruby/object:X {} 2 | -------------------------------------------------------------------------------- /test/files/tabs.rb: -------------------------------------------------------------------------------- 1 | class Foo 2 | puts 'hi' 3 | end 4 | -------------------------------------------------------------------------------- /test/files/valid_file.css: -------------------------------------------------------------------------------- 1 | a 2 | { 3 | color:#cfc2b5 4 | } 5 | -------------------------------------------------------------------------------- /test/files/valid_file.js: -------------------------------------------------------------------------------- 1 | var foo = function() { 2 | }; 3 | 4 | foo.prototype = { 5 | addTwo: function(x) { 6 | return x + 2; 7 | } 8 | }; 9 | 10 | // console underscore log should be valid 11 | var console_log = function() { 12 | }; 13 | 14 | var x = 7; // console.log in a single-line comment should be valid 15 | -------------------------------------------------------------------------------- /test/files/valid_file.rb: -------------------------------------------------------------------------------- 1 | # class docs for rubocop 2 | class ValidFile 3 | # Comments with the word: debugger should be allowed 4 | def no_problems_here! 5 | 1 + 2 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/files/valid_hashrockets.rb: -------------------------------------------------------------------------------- 1 | gem 'foo', ref: 'v2.6.0' 2 | mount Application::API => '/api' 3 | -------------------------------------------------------------------------------- /test/files/valid_spec.rb: -------------------------------------------------------------------------------- 1 | describe Foo do 2 | before do 3 | # ... 4 | end 5 | 6 | before(:each) do 7 | # ... 8 | end 9 | 10 | before :each do 11 | # ... 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/files/with_debugger/Gemfile: -------------------------------------------------------------------------------- 1 | # Putting this in a sub directory so it doesn't conflict with other Gemfiles 2 | # that we may or may not add in the future. 3 | 4 | gem "debugger" 5 | -------------------------------------------------------------------------------- /test/files/with_debugger/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | debugger (1.6.0) 5 | columnize (>= 0.3.1) 6 | debugger-linecache (~> 1.2.0) 7 | debugger-ruby_core_source (~> 1.2.1) 8 | debugger-linecache (1.2.0) 9 | debugger-ruby_core_source (1.2.3) 10 | 11 | PLATFORMS 12 | ruby 13 | 14 | DEPENDENCIES 15 | debugger 16 | -------------------------------------------------------------------------------- /test/files/wrong_hashrockets.rb: -------------------------------------------------------------------------------- 1 | # Some examples of ruby1.8 old style hash rocket 2 | 3 | gem 'foo', :ref => 'v2.6.0' 4 | { :'foo-bar' => 42 } #It's ok to use hashrockets for a symbolized string 5 | { :@test => "foo_bar" } 6 | { :_test => "foo_bar" } 7 | { :$test => "foo_bar" } 8 | { :test! => "foo_bar" } 9 | { :test? => "foo_bar" } 10 | { :test= => "foo_bar" } 11 | { :@@test => "foo_bar" } 12 | -------------------------------------------------------------------------------- /test/integration_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require "tmpdir" 3 | require "pre-commit/cli" 4 | 5 | describe "integration" do 6 | before do 7 | create_temp_dir 8 | start_git 9 | install 10 | end 11 | after(&:destroy_temp_dir) 12 | 13 | it "prevents bad commits" do 14 | result = commit_a_file :fail => true 15 | assert_includes result, "detected tab before initial" 16 | assert_includes result, "new blank line at EOF" 17 | assert_includes result, "You can bypass this check using" 18 | end 19 | 20 | it "bypasses pre-commit checks when using the no-verify option" do 21 | result = commit_a_file :no_check => true 22 | refute_includes result, "detected tab before initial" 23 | assert_includes result, "create mode 100644 xxx.rb" 24 | end 25 | 26 | it "does not prevent bad commits when checks are disabled" do 27 | sh "git config 'pre-commit.checks' '[jshint]'" 28 | result = commit_a_file 29 | refute_includes result, "detected tab before initial" 30 | assert_includes result, "create mode 100644 xxx.rb" 31 | end 32 | 33 | it "prevents bad commits when certain checks are enabled" do 34 | sh "git config 'pre-commit.checks' '[:tabs]'" 35 | result = commit_a_file :fail => true 36 | assert_includes result, "detected tab before initial" 37 | refute_includes result, "new blank line at EOF" 38 | assert_includes result, "You can bypass this check using" 39 | end 40 | 41 | it "can overwrite existing hook" do 42 | write ".git/hooks/pre-commit", "XXX" 43 | sh "ruby -I #{project_dir}/lib #{project_dir}/bin/pre-commit install" 44 | read(".git/hooks/pre-commit").must_include "pre-commit" 45 | end 46 | 47 | describe "local checks" do 48 | it "prevents bad commits when local checks fail" do 49 | write("config/pre-commit.rb", "raise 'FOOO'") 50 | result = commit_a_file :content => "XXX", :fail => true 51 | assert_includes result, "FOOO" 52 | end 53 | 54 | it "allows good commits when local checks succeed" do 55 | write("config/pre-commit.rb", "") 56 | result = commit_a_file :content => "XXX" 57 | assert_includes result, "create mode 100644 xxx.rb" 58 | end 59 | end 60 | 61 | 62 | def commit_a_file(options={}) 63 | write("xxx.rb", options[:content] || "\t\tMuahaha\n\n\n") 64 | sh "git add -A" 65 | sh("git commit #{options[:no_check] ? "-n" : ""} -m 'EVIL'", options) 66 | end 67 | 68 | def install 69 | sh "git config pre-commit.ruby 'ruby #{ruby_includes}'" 70 | sh "ruby -I #{project_dir}/lib #{project_dir}/bin/pre-commit install" 71 | assert File.exist?(".git/hooks/pre-commit"), "The hook file does not exist" 72 | assert File.executable?(".git/hooks/pre-commit"), "The hook file is not executable" 73 | sh "git commit -m Initial --allow-empty" # or travis fails with: No HEAD commit to compare with 74 | end 75 | 76 | def ensure_folder(folder) 77 | FileUtils.mkdir_p(folder) unless File.exist?(folder) 78 | end 79 | 80 | def write(file, content) 81 | ensure_folder File.dirname(file) 82 | File.open(file, 'w'){|f| f.write content } 83 | end 84 | 85 | def read(file) 86 | File.read file 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /test/minitest_helper.rb: -------------------------------------------------------------------------------- 1 | if 2 | RUBY_VERSION == "2.0.0" # check Gemfile 3 | then 4 | require "coveralls" 5 | require "simplecov" 6 | 7 | SimpleCov.start do 8 | formatter SimpleCov::Formatter::MultiFormatter[ 9 | SimpleCov::Formatter::HTMLFormatter, 10 | Coveralls::SimpleCov::Formatter, 11 | ] 12 | command_name "Spesc Tests" 13 | add_filter "/test/" 14 | #add_filter "/demo/" 15 | end 16 | 17 | Coveralls.noisy = true unless ENV['CI'] 18 | end 19 | 20 | require 'minitest/autorun' 21 | require "minitest/reporters" 22 | require 'tmpdir' 23 | require 'pluginator' 24 | 25 | Minitest::Reporters.use! 26 | 27 | module PreCommit; module Helpers 28 | 29 | def fixture_file(filename) 30 | file_dir = File.expand_path('../files', __FILE__).sub("#{project_dir}/", "") 31 | File.join(file_dir, filename) 32 | end 33 | 34 | def write(file, content) 35 | File.open(file, "w") { |f| f.write content } 36 | end 37 | 38 | def create_temp_dir 39 | @dir = Dir.mktmpdir(nil, ENV['TMPDIR'] || '/tmp') 40 | @old_dir = Dir.pwd 41 | Dir.chdir(@dir) 42 | end 43 | 44 | def destroy_temp_dir 45 | Dir.chdir(@old_dir) 46 | FileUtils.rm_rf(@dir) 47 | end 48 | 49 | def in_tmpdir 50 | Dir.mktmpdir do |dir| 51 | Dir.chdir(dir) do 52 | yield 53 | end 54 | end 55 | end 56 | 57 | def ruby_includes 58 | "-I #{Gem::Specification.find_by_name('pluginator').full_gem_path}/lib -I #{project_dir}/lib" 59 | end 60 | 61 | def start_git 62 | sh "git init" 63 | end 64 | 65 | def project_dir 66 | File.expand_path("../../", __FILE__) 67 | end 68 | 69 | def sh(command, options={}) 70 | result = `#{command} 2>&1` 71 | raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail] 72 | result 73 | end 74 | 75 | def available_checks 76 | @available_checks ||= 77 | Dir["#{project_dir}/lib/plugins/pre_commit/checks/*.rb"].map{|path| 78 | path.gsub(/^.*\/([^\/]*)\.rb$/, "\\1") 79 | }.sort.join(" ") 80 | end 81 | 82 | end; end 83 | 84 | class MiniTest::Test 85 | include PreCommit::Helpers 86 | end 87 | 88 | class MiniTest::Unit::TestCase 89 | include PreCommit::Helpers 90 | end 91 | -------------------------------------------------------------------------------- /test/unit/plugins/pluginator/extensions/find_check_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pluginator/extensions/find_check' 3 | require 'plugins/pre_commit/checks/before_all' 4 | 5 | class FirstAskTester 6 | attr_accessor :plugins 7 | include Pluginator::Extensions::FindCheck 8 | end 9 | 10 | describe Pluginator::Extensions::FindCheck do 11 | before do 12 | @tester = FirstAskTester.new 13 | @tester.plugins = { "checks" => [PreCommit::Checks::BeforeAll] } 14 | $stderr = StringIO.new 15 | end 16 | after do 17 | $stderr = STDERR 18 | end 19 | 20 | it "finds existing plugin" do 21 | @tester.find_check("before_all").must_equal( PreCommit::Checks::BeforeAll ) 22 | $stderr.string.must_equal("") 23 | end 24 | 25 | it "does not find missing plugin" do 26 | @tester.find_check("missing_plugin").must_be_nil 27 | $stderr.string.must_equal(<<-EXPECTED) 28 | Could not find plugin supporting missing_plugin / MissingPlugin, 29 | available plugins: BeforeAll 30 | EXPECTED 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/before_all_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/before_all' 3 | 4 | describe PreCommit::Checks::BeforeAll do 5 | subject { PreCommit::Checks::BeforeAll.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | subject.call([]).must_be_nil 9 | end 10 | 11 | it "filters out rb files" do 12 | subject.files_filter([ 13 | fixture_file('valid_file.rb'),fixture_file('changelog.md'),fixture_file('console_log.rb') 14 | ]).must_equal([ 15 | fixture_file('valid_file.rb'),fixture_file('console_log.rb') 16 | ]) 17 | end 18 | 19 | it "succeeds if only good changes" do 20 | subject.call([fixture_file('valid_spec.rb')]).must_be_nil 21 | end 22 | 23 | it "fails if file contains before(:all)" do 24 | subject.call([fixture_file('before_all_spec.rb')]).to_a.must_equal([ 25 | "before(:all) found:", 26 | "test/files/before_all_spec.rb:2: before(:all) do" 27 | ]) 28 | end 29 | 30 | it "fails if file contains before :all" do 31 | subject.call([fixture_file('before_all_spec_2.rb')]).to_a.must_equal([ 32 | "before(:all) found:", 33 | "test/files/before_all_spec_2.rb:2: before :all do" 34 | ]) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/ci_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/ci' 3 | 4 | describe PreCommit::Checks::Ci do 5 | let(:check) { PreCommit::Checks::Ci.new(nil, nil, []) } 6 | 7 | it "succeeds if rake succeeds" do 8 | check.stub :system, true do 9 | check.call([]).must_be_nil 10 | end 11 | end 12 | 13 | it "fails if rake fails" do 14 | check.stub :system, false do 15 | check.call([]).to_s.must_equal "your test suite has failed, for the full output run `pre_commit:ci`" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/coffeelint_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'minitest_helper' 3 | require 'plugins/pre_commit/checks/coffeelint' 4 | 5 | describe PreCommit::Checks::Coffeelint do 6 | let(:config) do 7 | mock = MiniTest::Mock.new 8 | mock.expect(:get, '', ['coffeelint.config']) 9 | mock 10 | end 11 | let(:check) {PreCommit::Checks::Coffeelint.new(nil, config, [])} 12 | 13 | it "succeds if nothing changed" do 14 | check.call([]).must_be_nil 15 | end 16 | 17 | it "succeeds for good code" do 18 | check.call([fixture_file('good.coffee')]).must_be_nil 19 | end 20 | 21 | it "fails for bad formatted code" do 22 | check.call([fixture_file("bad.coffee")]).must_match(/camel.*case/i) 23 | end 24 | end unless `which coffeelint 2>/dev/null`.empty? 25 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/console_log_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/console_log' 3 | 4 | describe PreCommit::Checks::ConsoleLog do 5 | subject do 6 | PreCommit::Checks::ConsoleLog.new(nil, nil, []) 7 | end 8 | 9 | it "filters out js files" do 10 | subject.files_filter([ 11 | fixture_file('valid_file.js'),fixture_file('changelog.md'),fixture_file('console_log.js') 12 | ]).must_equal([ 13 | fixture_file('valid_file.js'),fixture_file('console_log.js') 14 | ]) 15 | end 16 | 17 | it "succeeds if nothing changed" do 18 | subject.call([]).must_be_nil 19 | end 20 | 21 | it "succeeds with valid .js file changed" do 22 | subject.call([fixture_file('valid_file.js')]).must_be_nil 23 | end 24 | 25 | it "succeeds if non js files has console.log" do 26 | subject.call([fixture_file('changelog.md')]).must_be_nil 27 | end 28 | 29 | it "fails if a js file has a console.log" do 30 | subject.call([fixture_file('console_log.js')]).to_a.must_equal([ 31 | "console.log found:", 32 | "test/files/console_log.js:6: console.log(\"I'm in bar\");" 33 | ]) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/csslint_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/csslint' 3 | require 'execjs' 4 | 5 | describe PreCommit::Checks::Csslint do 6 | let(:check){ PreCommit::Checks::Csslint.new(nil, nil, []) } 7 | 8 | it "succeeds if nothing changed" do 9 | check.call([]).must_be_nil 10 | end 11 | 12 | it "succeeds if only good changes" do 13 | check.run_check(fixture_file('valid_file.css')).must_equal [] 14 | end 15 | 16 | it "succeeds if only good changes" do 17 | check.call([fixture_file('valid_file.css')]).must_be_nil 18 | end 19 | 20 | it "fails if file contains errors" do 21 | check.run_check(fixture_file('bad_file.css')).must_equal [ 22 | { 23 | "type"=>"error", "line"=>3, "col"=>8, "message"=>"Expected RBRACKET at line 3, col 8.", "evidence"=>" color:#cfc2b5", 24 | "rule"=>{"id"=>"errors", "name"=>"Parsing Errors", "desc"=>"This rule looks for recoverable syntax errors.", "browsers"=>"All"} 25 | }, { 26 | "type"=>"error", "line"=>3, "col"=>8, "message"=>"Expected RBRACKET at line 3, col 8.", "evidence"=>" color:#cfc2b5", 27 | "rule"=>{"id"=>"errors", "name"=>"Parsing Errors", "desc"=>"This rule looks for recoverable syntax errors.", "browsers"=>"All"} 28 | } 29 | ] 30 | end 31 | 32 | it "formats errors" do 33 | data = { 34 | "type"=>"error", "line"=>3, "col"=>8, "message"=>"Expected RBRACKET at line 3, col 8.", "evidence"=>" color:#cfc2b5", 35 | "rule"=>{"id"=>"errors", "name"=>"Parsing Errors", "desc"=>"This rule looks for recoverable syntax errors.", "browsers"=>"All"} 36 | } 37 | check.display_error(data, "test_file.css").must_equal("Expected RBRACKET at line 3, col 8.\ntest_file.css:4 color:#cfc2b5") 38 | end 39 | 40 | it "fails if file contains errors" do 41 | check.call([fixture_file('bad_file.css')]).must_equal "Expected RBRACKET at line 3, col 8.\ntest/files/bad_file.css:4 color:#cfc2b5\nExpected RBRACKET at line 3, col 8.\ntest/files/bad_file.css:4 color:#cfc2b5" 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/debugger_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/debugger' 3 | 4 | describe PreCommit::Checks::Debugger do 5 | subject{ PreCommit::Checks::Debugger.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | subject.call([]).must_be_nil 9 | end 10 | 11 | it "filters out Gemfiles files" do 12 | subject.files_filter([ 13 | fixture_file('valid_file.rb'), 14 | fixture_file('Gemfiles'), 15 | fixture_file('console_log.rb') 16 | ]).must_equal([ 17 | fixture_file('valid_file.rb'),fixture_file('console_log.rb') 18 | ]) 19 | end 20 | 21 | it "succeeds if only good changes" do 22 | subject.call([fixture_file('valid_file.rb')]).must_be_nil 23 | end 24 | 25 | it "fails if file contains debugger" do 26 | subject.call([fixture_file('debugger_file.rb')]).to_a.must_equal([ 27 | "debugger statement(s) found:", 28 | "test/files/debugger_file.rb:3: \tdebugger" 29 | ]) 30 | end 31 | 32 | it "fails if file contains a byebug statement" do 33 | actual = subject.call([fixture_file("byebug_file.rb")]).to_a 34 | expected = [ 35 | "debugger statement(s) found:", 36 | "test/files/byebug_file.rb:3: byebug" 37 | ] 38 | actual.must_equal expected, "Did not detect byebug in byebug_file.rb" 39 | end 40 | 41 | it "Skips checking the Gemfile" do 42 | files = [fixture_file("with_debugger/Gemfile")] 43 | subject.call(files).must_be_nil 44 | end 45 | 46 | it "Skips checking the Gemfile.lock" do 47 | files = [fixture_file("with_debugger/Gemfile.lock")] 48 | subject.call(files).must_be_nil 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/gemfile_path_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/gemfile_path' 3 | 4 | describe PreCommit::Checks::GemfilePath do 5 | let(:check){ PreCommit::Checks::GemfilePath.new(nil, nil, []) } 6 | 7 | before do 8 | create_temp_dir 9 | start_git 10 | end 11 | after(&:destroy_temp_dir) 12 | 13 | it "succeeds if nothing changed" do 14 | check.call([]).must_be_nil 15 | end 16 | 17 | it "succeeds if non-gemfile changes" do 18 | write "Foofile", <<-RUBY 19 | gem "foo", :path => "xxx" 20 | RUBY 21 | check.call(["Foofile"]).must_be_nil 22 | end 23 | 24 | it "succeeds if only good changes" do 25 | write "Gemfile", <<-RUBY 26 | gem "foo" 27 | RUBY 28 | check.call(["Gemfile"]).must_be_nil 29 | end 30 | 31 | it "succeeds with innocent path check" do 32 | write "Gemfile", <<-RUBY 33 | gem "foo_path" 34 | RUBY 35 | check.call(["Gemfile"]).must_be_nil 36 | end 37 | 38 | it "fails if Gemfile contains path =>" do 39 | write "Gemfile", <<-RUBY 40 | gem "foo", :path => "xxxx" 41 | RUBY 42 | check.call(["Gemfile"]).to_a.must_equal([ 43 | "local path found in Gemfile:", 44 | 'Gemfile:1: gem "foo", :path => "xxxx"' 45 | ]) 46 | end 47 | 48 | it "fails if Gemfile contains path:" do 49 | write "Gemfile", <<-RUBY 50 | gem "foo", path: "xxxx" 51 | RUBY 52 | check.call(["Gemfile"]).to_a.must_equal([ 53 | "local path found in Gemfile:", 54 | 'Gemfile:1: gem "foo", path: "xxxx"' 55 | ]) 56 | end 57 | 58 | it "allows a Gemfile path that is commented out" do 59 | write "Gemfile", <<-RUBY 60 | # gem 'my-internal-app-gem', '~> 0.0.1', :path => './lib/my-internal-app-gem' 61 | RUBY 62 | check.call(["Gemfile"]).must_be_nil 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/go_build_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/go_build' 3 | 4 | describe PreCommit::Checks::GoBuild do 5 | let(:check) {PreCommit::Checks::GoBuild.new(nil, nil, [])} 6 | 7 | it "succeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds for good code" do 12 | check.call([fixture_file('good.go')]).must_equal "" 13 | end 14 | 15 | it "fails for compiler errors" do 16 | check.call([fixture_file("dont_compile.go")]).must_match(/imported and not used/) 17 | end 18 | end unless `which go 2>/dev/null`.empty? 19 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/go_fmt_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/go_fmt' 3 | 4 | describe PreCommit::Checks::GoFmt do 5 | let(:check) {PreCommit::Checks::GoFmt.new(nil, nil, [])} 6 | 7 | it "succeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds for good code" do 12 | check.call([fixture_file('good.go')]).must_equal "" 13 | end 14 | 15 | it "fails for bad formatted code" do 16 | check.call([fixture_file("bad_fmt.go")]).must_match(/bad_fmt.go/) 17 | end 18 | 19 | end unless `which go 2>/dev/null`.empty? 20 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/jshint_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/jshint' 3 | require 'execjs' 4 | 5 | describe PreCommit::Checks::Jshint do 6 | let(:config) do 7 | mock = MiniTest::Mock.new 8 | mock.expect(:get, '', ['jshint.config']) 9 | mock 10 | end 11 | 12 | let(:check){ PreCommit::Checks::Jshint.new(nil, config, []) } 13 | 14 | it "succeeds if nothing changed" do 15 | check.call([]).must_be_nil 16 | end 17 | 18 | it "succeeds if only good changes" do 19 | check.run_check(fixture_file('valid_file.js')).must_equal [] 20 | end 21 | 22 | it "succeeds if only good changes" do 23 | check.call([fixture_file('valid_file.js')]).must_be_nil 24 | end 25 | 26 | it "fails if file contains debugger" do 27 | check.run_check(fixture_file('bad_file.js')).must_equal [{ 28 | "id" => "(error)", 29 | "raw" => "Missing semicolon.", 30 | "evidence" => "}", 31 | "line" => 4, 32 | "character" => 2, 33 | "reason" => "Missing semicolon.", 34 | "code" => "W033", 35 | "scope" => "(main)" 36 | }] 37 | end 38 | 39 | it "fails if file contains debugger" do 40 | check.call([fixture_file('bad_file.js')]).must_equal "Missing semicolon.\ntest/files/bad_file.js:5 }" 41 | end 42 | 43 | describe "filesystem" do 44 | before do 45 | @example = File.join(Dir.pwd, fixture_file('bad_file.js')) 46 | create_temp_dir 47 | start_git 48 | end 49 | after(&:destroy_temp_dir) 50 | 51 | it "uses proper config file" do 52 | File.open(".jshintrc", "w") do |file| 53 | file.write <<-CONFIG 54 | { 55 | "asi": true, 56 | "lastsemic": true 57 | } 58 | CONFIG 59 | end 60 | check.run_check(@example).must_equal [] 61 | end 62 | 63 | it "uses extended config file" do 64 | File.open(".jshintrc", "w") do |file| 65 | file.write <<-CONFIG 66 | { 67 | "node": true, 68 | "browser": true, 69 | "esnext": true, 70 | "bitwise": true, 71 | "camelcase": false, 72 | "curly": true, 73 | "eqeqeq": true, 74 | "indent": 2, 75 | "latedef": true, 76 | "newcap": true, 77 | "noarg": true, 78 | "quotmark": "single", 79 | "regexp": true, 80 | "undef": true, 81 | "unused": true, 82 | "strict": true, 83 | "trailing": true, 84 | "smarttabs": true, 85 | "globals" : { 86 | "angular": false, 87 | "chrome": false 88 | } 89 | } 90 | CONFIG 91 | end 92 | File.open("test.js", "w") do |file| 93 | file.write <<-TEST 94 | (function() { 95 | 'use strict'; 96 | angular.module('app').controller('TestCtrl', function($scope) { 97 | $scope.test = function() { 98 | return chrome.app.window.create('test.html', { 99 | id: 'mocha' 100 | }); 101 | }; 102 | }); 103 | }).call(this); 104 | TEST 105 | end 106 | check.run_check("test.js").must_equal [] 107 | end 108 | 109 | 110 | it "does not use broken config file" do 111 | File.open(".jshintrc", "w") do |file| 112 | file.write <<-CONFIG 113 | { 114 | "asi": true, 115 | "lastsemic": true 116 | } 117 | } 118 | CONFIG 119 | end 120 | ->(){ check.call([@example]) }.must_raise ExecJS::RuntimeError 121 | end 122 | 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/jslint_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/jslint' 3 | 4 | describe PreCommit::Checks::Jslint do 5 | let(:check){ PreCommit::Checks::Jslint.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds if only good changes" do 12 | check.call([fixture_file('valid_file.js')]).must_be_nil 13 | end 14 | 15 | it "fails if file contains debugger" do 16 | skip do 17 | check.call([fixture_file('bad_file.js')]).must_equal "Missing semicolon.\ntest/files/bad_file.js:5 }" 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/json_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/json' 3 | 4 | describe PreCommit::Checks::Json do 5 | let(:check) {PreCommit::Checks::Json.new(nil, nil, [])} 6 | 7 | it "succeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds for good code" do 12 | check.call([fixture_file('good.json')]).must_be_nil 13 | end 14 | 15 | it "fails for bad formatted code" do 16 | errors = check.call([fixture_file("bad.json")]) 17 | errors.must_match(/unexpected token at '}/) 18 | errors.must_match(%r{parsing test/files/bad.json}) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/local_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/local' 3 | 4 | describe PreCommit::Checks::Local do 5 | 6 | let(:config_file) { fixture_file("pre-commit.rb") } 7 | let(:check) { PreCommit::Checks::Local.new(nil, nil, []) } 8 | 9 | it "succeeds if there is no config" do 10 | check.call([]).must_be_nil 11 | end 12 | 13 | it "succeeds if script succeeds" do 14 | check.script = config_file 15 | check.call([]).must_be_nil 16 | end 17 | 18 | it "fails if script fails" do 19 | check.script = config_file 20 | check.call(["xxx"]).must_include "pre-commit.rb failed" 21 | end 22 | 23 | it "finds a local script" do 24 | in_tmpdir do 25 | FileUtils.mkdir_p("config") 26 | FileUtils.touch(File.join("config", "pre-commit.rb")) 27 | check.script.must_equal "config/pre-commit.rb" 28 | end 29 | end 30 | 31 | it "finds a local script with an underscored name" do 32 | in_tmpdir do 33 | FileUtils.mkdir_p("config") 34 | FileUtils.touch(File.join("config", "pre_commit.rb")) 35 | check.script.must_equal "config/pre_commit.rb" 36 | end 37 | end 38 | 39 | it "prefers the underscored local script name" do 40 | in_tmpdir do 41 | FileUtils.mkdir_p("config") 42 | FileUtils.touch(File.join("config", "pre_commit.rb")) 43 | FileUtils.touch(File.join("config", "pre-commit.rb")) 44 | check.script.must_equal "config/pre_commit.rb" 45 | end 46 | end 47 | 48 | end 49 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/merge_conflict_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/merge_conflict' 3 | 4 | describe PreCommit::Checks::MergeConflict do 5 | let(:check){ PreCommit::Checks::MergeConflict.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds if only good changes" do 12 | check.call([fixture_file('valid_file.rb')]).must_be_nil 13 | end 14 | 15 | it "fails if file contains merge conflict" do 16 | check.call([fixture_file('merge_conflict.rb')]).to_a.must_equal([ 17 | "detected a merge conflict", 18 | "test/files/merge_conflict.rb:3:<<<<<<< HEAD" 19 | ]) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/migration_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'tmpdir' 3 | require 'plugins/pre_commit/checks/migration' 4 | 5 | describe PreCommit::Checks::Migration do 6 | let(:check) { PreCommit::Checks::Migration.new(nil, nil, []) } 7 | 8 | def in_new_directory(&block) 9 | Dir.mktmpdir do |dir| 10 | Dir.chdir(dir, &block) 11 | end 12 | end 13 | 14 | def write(file, content) 15 | FileUtils.mkdir_p(File.dirname(file)) 16 | File.open(file, 'w') { |f| f.write content } 17 | end 18 | 19 | it "succeeds if there is no change" do 20 | check.call([]).must_be_nil 21 | end 22 | 23 | it "succeeds if there is a migration and a schema change" do 24 | in_new_directory do 25 | write "db/migrate/20140718171920_foo.rb", "Yep" 26 | write "db/schema.rb", "version 20140718171920 bla" 27 | check.call(['db/migrate/20140718171920_foo.rb', 'db/schema.rb']).must_be_nil 28 | end 29 | end 30 | 31 | it "succeeds if there is a migration and a sql schema change" do 32 | in_new_directory do 33 | write "db/migrate/20140718171920_foo.rb", "Yep" 34 | write "db/foo_structure.sql", "version 20140718171920 bla" 35 | check.call(['db/migrate/20140718171920_foo.rb', 'db/foo_structure.sql']).must_be_nil 36 | end 37 | end 38 | 39 | it 'detects multiple versions in a structure file' do 40 | in_new_directory do 41 | write "db/migrate/20140718171920_foo.rb", "Yep" 42 | write "db/foo_structure.sql", <<-STRUCTURE 43 | INSERT INTO schema_migrations (version) VALUES ('20111115214127'); 44 | 45 | INSERT INTO schema_migrations (version) VALUES ('20140718171920'); 46 | STRUCTURE 47 | check.call(['db/migrate/20140718171920_foo.rb', 'db/foo_structure.sql']).must_be_nil 48 | end 49 | end 50 | 51 | it "succeeds if unrelated files are changed" do 52 | check.call(['public/javascript/foo.js', 'lib/bar.rb']).must_be_nil 53 | end 54 | 55 | it "succeeds when initial schema is added with version 0" do 56 | in_new_directory do 57 | write "db/schema.rb", "brand new 0 version" 58 | check.call(['db/schema.rb']).must_be_nil 59 | end 60 | end 61 | 62 | it "succeeds when schema files use human-friendly date formats" do 63 | in_new_directory do 64 | write "db/migrate/20181012040244_add_books.rb", <<-RUBY 65 | class AddBooks < ActiveRecord::Migration[5.2] 66 | def change 67 | end 68 | end 69 | RUBY 70 | write "db/schema.rb", <<-RUBY 71 | ActiveRecord::Schema.define(version: 2018_10_12_040244) do 72 | end 73 | RUBY 74 | 75 | check.call([ 76 | "db/migrate/20181012040244_add_books.rb", 77 | "db/schema.rb" 78 | ]).must_be_nil 79 | end 80 | end 81 | 82 | it "fails if schema change is missing" do 83 | check.call(['db/migrate/20140718171920_foo.rb']).must_equal "It looks like you're adding a migration, but did not update the schema file" 84 | end 85 | 86 | it "fails if migration is missing" do 87 | in_new_directory do 88 | write "db/schema.rb", "Nope 20130111131344" 89 | check.call(['db/schema.rb']).must_equal "You're trying to change the schema without adding a migration file" 90 | end 91 | end 92 | 93 | it "fails if the schema change does not include the added versions" do 94 | in_new_directory do 95 | write "db/migrate/20140506010100_foo.rb", "class CreateFoos < ActiveRecord::Migration" 96 | write "db/migrate/20140506010200_bar.rb", "class CreateBars < ActiveRecord::Migration" 97 | 98 | write "db/schema.rb", <<-RUBY 99 | ActiveRecord::Schema.define(version: 2011_01_01_010100) do 100 | end 101 | RUBY 102 | 103 | result = check.call([ 104 | "db/schema.rb", 105 | "db/migrate/20140506010100_foo.rb", 106 | "db/migrate/20140506010200_bar.rb" 107 | ]) 108 | 109 | assert_equal( 110 | "You did not add the schema versions for 20140506010100, 20140506010200 to db/schema.rb", 111 | result 112 | ) 113 | end 114 | end 115 | 116 | it "only lists the missing migration versions" do 117 | in_new_directory do 118 | write "db/migrate/20140506010100_foo.rb", "class CreateFoos < ActiveRecord::Migration" 119 | write "db/migrate/20140506010200_bar.rb", "class CreateBars < ActiveRecord::Migration" 120 | write "db/migrate/20140506010300_baz.rb", "class CreateBazs < ActiveRecord::Migration" 121 | 122 | write "db/schema.rb", <<-RUBY 123 | ActiveRecord::Schema.define(version: 2014_05_06_010200) do 124 | end 125 | RUBY 126 | 127 | result = check.call([ 128 | "db/schema.rb", 129 | "db/migrate/20140506010100_foo.rb", 130 | "db/migrate/20140506010200_bar.rb", 131 | "db/migrate/20140506010300_baz.rb" 132 | ]) 133 | 134 | assert_equal( 135 | "You did not add the schema versions for 20140506010100, 20140506010300 to db/schema.rb", 136 | result 137 | ) 138 | end 139 | end 140 | end 141 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/nb_space_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/nb_space' 3 | 4 | describe PreCommit::Checks::NbSpace do 5 | let(:check){ PreCommit::Checks::NbSpace.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds if only good changes" do 12 | check.call([fixture_file("pre-commit.rb")]).must_be_nil 13 | end 14 | 15 | it "fails if script fails" do 16 | check.call([fixture_file("file_with_nb_space.rb")]).must_equal "Detected non-breaking space in test/files/file_with_nb_space.rb:2 character:13, remove it!" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/pry_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/pry' 3 | 4 | describe PreCommit::Checks::Pry do 5 | let(:check){ PreCommit::Checks::Pry.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds if only good changes" do 12 | check.call([fixture_file('valid_file.rb')]).must_be_nil 13 | end 14 | 15 | it "fails if file contains pry" do 16 | check.call([fixture_file('pry_file.rb')]).to_a.must_equal([ 17 | "binding.pry found:", 18 | "test/files/pry_file.rb:3: binding.pry", 19 | "test/files/pry_file.rb:4: binding.remote_pry", 20 | "test/files/pry_file.rb:5: binding.remote_pry_em" 21 | ]) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/rspec_focus_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/rspec_focus' 3 | 4 | describe PreCommit::Checks::RspecFocus do 5 | let(:check){ PreCommit::Checks::RspecFocus.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds on non-specs" do 12 | check.call([fixture_file('bad-spec.rb')]).must_be_nil 13 | end 14 | 15 | it "succeeds if only good changes" do 16 | check.call([fixture_file('rspec_focus_good_spec.rb')]).must_be_nil 17 | end 18 | 19 | it "succeeds when there are keywords in the description" do 20 | check.call([fixture_file('rspec_focus_sugar_good_spec.rb')]).must_be_nil 21 | end 22 | 23 | it 'fails if focus specified on describe, context or example block using any valid syntax' do 24 | check.call([fixture_file('rspec_focus_bad_spec.rb')]).to_s.must_equal("\ 25 | focus found in specs: 26 | test/files/rspec_focus_bad_spec.rb:2: context 'with old hash syntax', :focus => true do 27 | test/files/rspec_focus_bad_spec.rb:3: describe 'focus on describe', :focus => true do 28 | test/files/rspec_focus_bad_spec.rb:4: it 'alerts with focus on example too', :focus => true do 29 | test/files/rspec_focus_bad_spec.rb:9: context 'with new hash syntax', focus: true do 30 | test/files/rspec_focus_bad_spec.rb:10: describe 'focus on describe', focus: true do 31 | test/files/rspec_focus_bad_spec.rb:11: it 'alerts with focus on example too', focus: true do 32 | test/files/rspec_focus_bad_spec.rb:16: context 'with symbols as keys', :focus do 33 | test/files/rspec_focus_bad_spec.rb:17: describe 'focus on describe', :focus do 34 | test/files/rspec_focus_bad_spec.rb:18: it 'alerts with focus on example too', :focus do") 35 | end 36 | 37 | it 'fails if focus specified on describe, context or example block using any valid syntax' do 38 | check.call([fixture_file('rspec_focus_sugar_bad_spec.rb')]).to_s.must_equal("\ 39 | focus found in specs: 40 | test/files/rspec_focus_sugar_bad_spec.rb:2: fcontext 'f prefixed to context' do 41 | test/files/rspec_focus_sugar_bad_spec.rb:3: fdescribe 'f prefixed to describe' do 42 | test/files/rspec_focus_sugar_bad_spec.rb:4: fit 'f prefixed to it' do") 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/rubocop_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/rubocop' 3 | 4 | describe PreCommit::Checks::Rubocop do 5 | let(:config) do 6 | mock = MiniTest::Mock.new 7 | mock.expect(:get, '', ['rubocop.config']) 8 | mock.expect(:get, flags, ['rubocop.flags']) 9 | mock 10 | end 11 | 12 | let(:flags) { '' } 13 | 14 | let(:check){ PreCommit::Checks::Rubocop.new(nil, config, []) } 15 | 16 | it "succeeds if nothing changed" do 17 | check.call([]).must_be_nil 18 | end 19 | 20 | it "succeeds if only good changes" do 21 | check.call([fixture_file('valid_file.rb')]).must_be_nil 22 | end 23 | 24 | it "fails if file contains errors" do 25 | # rubinius finds only 1 offense, all others find 2 26 | result = check.call([fixture_file('merge_conflict.rb')]) 27 | result.must_match(/offense/i) 28 | result.must_match(/inspect/i) 29 | result.must_match(/file/i) 30 | end 31 | 32 | [".gemspec", ".jbuilder", ".opal", ".podspec", ".rake", ".rb"].each do |ext| 33 | it "Runs checks on #{ext} files" do 34 | file = fixture_file("test#{ext}") 35 | check.filter_staged_files([file]).must_include(file) 36 | end 37 | end 38 | 39 | [ 40 | "Berksfile", "Capfile", "Cheffile", "Gemfile", "Guardfile", "Podfile", 41 | "Rakefile", "Thorfile", "Vagabondfile", "Vagrantfile" 42 | ].each do |filename| 43 | it "Runs checks on #{filename}" do 44 | file = fixture_file("Gemfile") 45 | check.filter_staged_files([file]).must_include(file) 46 | end 47 | end 48 | 49 | it "ignores erb files" do 50 | file = fixture_file("test.erb") 51 | check.filter_staged_files([file]).wont_include(file) 52 | end 53 | 54 | describe 'with --fail-level=warn' do 55 | let(:flags) { '--fail-level=warn' } 56 | 57 | it "fails if file contains errors" do 58 | result = check.call([fixture_file('pry_file.rb')]) 59 | result.must_match(/offense/i) 60 | result.must_match(/inspect/i) 61 | result.must_match(/file/i) 62 | end 63 | end 64 | 65 | describe 'with --fail-level=fatal' do 66 | let(:flags) { '--fail-level=fatal' } 67 | 68 | it "succeeds if file contains errors" do 69 | check.call([fixture_file('pry_file.rb')]).must_be_nil 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/ruby_symbol_hashrockets_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/ruby_symbol_hashrockets' 3 | 4 | describe PreCommit::Checks::RubySymbolHashrockets do 5 | let(:check){ PreCommit::Checks::RubySymbolHashrockets.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds with valid" do 12 | check.call([fixture_file('valid_hashrockets.rb')]).must_be_nil 13 | end 14 | 15 | it "fails with invalid" do 16 | check.call([fixture_file('wrong_hashrockets.rb')]).to_s.must_equal("\ 17 | detected :symbol => value hashrocket: 18 | test/files/wrong_hashrockets.rb:3:gem 'foo', :ref => 'v2.6.0' 19 | test/files/wrong_hashrockets.rb:5:{ :@test => \"foo_bar\" } 20 | test/files/wrong_hashrockets.rb:6:{ :_test => \"foo_bar\" } 21 | test/files/wrong_hashrockets.rb:7:{ :$test => \"foo_bar\" } 22 | test/files/wrong_hashrockets.rb:8:{ :test! => \"foo_bar\" } 23 | test/files/wrong_hashrockets.rb:9:{ :test? => \"foo_bar\" } 24 | test/files/wrong_hashrockets.rb:10:{ :test= => \"foo_bar\" } 25 | test/files/wrong_hashrockets.rb:11:{ :@@test => \"foo_bar\" }") 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/scss_lint_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/scss_lint' 3 | 4 | describe PreCommit::Checks::ScssLint do 5 | let(:config) do 6 | mock = MiniTest::Mock.new 7 | mock.expect(:get, '', ['scss_lint.config']) 8 | mock 9 | end 10 | let(:check) {PreCommit::Checks::ScssLint.new(nil, config, [])} 11 | 12 | it "succeds if nothing changed" do 13 | check.call([]).must_be_nil 14 | end 15 | 16 | it "succeeds for good code" do 17 | check.call([fixture_file('good.scss')]).must_be_nil 18 | end 19 | 20 | it "fails for bad formatted code" do 21 | check.call([fixture_file("bad.scss")]).must_match(/test\/files\/bad\.scss/) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/tabs_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/tabs' 3 | 4 | describe PreCommit::Checks::Tabs do 5 | let(:check) { PreCommit::Checks::Tabs.new(nil, nil, []) } 6 | 7 | it "passes without files" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "detect a tab" do 12 | check.call([fixture_file('tabs.rb')]).wont_equal nil 13 | end 14 | 15 | it "detects leading whitespace followed by a tab" do 16 | check.call([fixture_file('bad_tabs2.rb')]).wont_equal nil 17 | end 18 | 19 | it "passes with a valid file" do 20 | check.call([fixture_file('valid_file.rb')]).must_be_nil 21 | end 22 | 23 | it "passes with a binary file with initial tab" do 24 | check.call([fixture_file('property_sets-0.3.0.gem')]).must_be_nil 25 | end 26 | 27 | it "shows error message when an initial tab is found" do 28 | check.call([fixture_file('initial_tab.rb')]).to_a.must_equal([ 29 | "detected tab before initial space:", 30 | "test/files/initial_tab.rb:3:\t 'hello'" 31 | ]) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/whitespace_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/whitespace' 3 | 4 | describe PreCommit::Checks::Whitespace do 5 | before do 6 | create_temp_dir 7 | start_git 8 | sh "touch a && git add a && git commit -am 'a'" 9 | end 10 | after(&:destroy_temp_dir) 11 | 12 | let(:check){ PreCommit::Checks::Whitespace.new(nil, nil, []) } 13 | 14 | it "succeeds if nothing changed" do 15 | check.call(['a']).must_be_nil 16 | end 17 | 18 | it "succeeds if only good changes" do 19 | `echo aaa > b && git add b` 20 | check.call(['a', 'b']).must_be_nil 21 | end 22 | 23 | describe 'staged bad changes' do 24 | it "fails on bad changes" do 25 | `echo ' ' > b && git add b` 26 | check.call(['a', 'b']).must_equal "b:1: trailing whitespace.\n+ \nb:1: new blank line at EOF.\n" 27 | end 28 | 29 | it "succeeds if the target files don't have bad changes" do 30 | `echo ' ' > b && git add b` 31 | check.call(['a']).must_be_nil 32 | end 33 | 34 | it "succeeds if no target files" do 35 | `echo ' ' > b && git add b` 36 | check.call([]).must_be_nil 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/checks/yaml_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/checks/yaml' 3 | 4 | describe PreCommit::Checks::Yaml do 5 | let(:check) {PreCommit::Checks::Yaml.new(nil, nil, [])} 6 | 7 | it "succeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds for good code" do 12 | check.call([fixture_file('good.yml')]).must_be_nil 13 | end 14 | 15 | it "skips files with serialized ruby" do 16 | $stdout, stdout = StringIO.new, $stdout 17 | check.call([fixture_file('serialized.yml')]) 18 | $stdout, stdout = stdout, $stdout 19 | 20 | stdout.string.must_equal("Warning: Skipping 'test/files/serialized.yml' because it contains serialized ruby objects.\n") 21 | end 22 | 23 | it "fails for bad formatted code" do 24 | # JRuby Error: (test/files/bad.yml): found character % '%' that cannot start any token. (Do not use % for indentation) while scanning for the next token at line 2 column 11 25 | # Other Rubies: (test/files/bad.yml): found character that cannot start any token while scanning for the next token at line 2 column 11 26 | 27 | check.call([fixture_file("bad.yml")]).must_match(%r{test/files/bad.yml.* found character.* that cannot start any token.* line 2 column 11}) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/configuration/providers/default_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/configuration/providers/default' 3 | 4 | describe PreCommit::Configuration::Providers::Default do 5 | subject do 6 | PreCommit::Configuration::Providers::Default 7 | end 8 | 9 | it "has priority" do 10 | subject.priority.must_equal(0) 11 | end 12 | 13 | it "has defaults" do 14 | subject::DEFAULTS.must_be_kind_of(Hash) 15 | subject::DEFAULTS.keys.must_include(:warnings) 16 | subject::DEFAULTS.keys.must_include(:checks) 17 | end 18 | 19 | it "uses DEFAULTS" do 20 | example = subject.new 21 | example.instance_variable_get(:@defaults).must_equal(subject::DEFAULTS) 22 | end 23 | 24 | it "reads default values" do 25 | example = subject.new({:test1 => 1, :test2 => 2}) 26 | example[:test1].must_equal(1) 27 | example[:test2].must_equal(2) 28 | example[:test3].must_be_nil 29 | end 30 | 31 | it "does not allow update" do 32 | example = subject.new({:test1 => 1, :test2 => 2}) 33 | ->() { example.update("", "") }.must_raise PreCommit::CanNotUpdateDefauls 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/configuration/providers/env_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/configuration/providers/env' 3 | 4 | describe PreCommit::Configuration::Providers::Env do 5 | subject do 6 | PreCommit::Configuration::Providers::Env 7 | end 8 | 9 | it "has priority" do 10 | subject.priority.must_equal(30) 11 | end 12 | 13 | describe :environment do 14 | subject do 15 | PreCommit::Configuration::Providers::Env.new 16 | end 17 | 18 | before do 19 | ENV['GETTABLE_CONFIGURATION'] = 'test' 20 | ENV['SETTABLE_CONFIGURATION'] = nil 21 | end 22 | 23 | after do 24 | ENV['GETTABLE_CONFIGURATION'] = nil 25 | ENV['SETTABLE_CONFIGURATION'] = nil 26 | end 27 | 28 | it 'reads from the environment' do 29 | subject['gettable.configuration'].must_equal('test') 30 | end 31 | 32 | it 'writes to the environment' do 33 | subject.update('settable.configuration', 'test') 34 | ENV['SETTABLE_CONFIGURATION'].must_equal('test') 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/configuration/providers/git_old_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/configuration/providers/git_old' 3 | 4 | describe PreCommit::Configuration::Providers::GitOld do 5 | subject do 6 | PreCommit::Configuration::Providers::GitOld 7 | end 8 | 9 | it "has priority" do 10 | subject.priority.must_equal(11) 11 | end 12 | 13 | describe :filesystem do 14 | before do 15 | create_temp_dir 16 | start_git 17 | sh "git config pre-commit.checks jshint,local" 18 | sh "git config pre-commit.other jshint,local" 19 | end 20 | after(&:destroy_temp_dir) 21 | 22 | it "reads values" do 23 | example = subject.new 24 | example[:test1].must_be_nil 25 | example[:checks].must_equal([:jshint, :local]) 26 | example[:other].must_equal("jshint,local") 27 | end 28 | 29 | it "saves values" do 30 | example = subject.new 31 | example[:checks] 32 | example[:other] 33 | sh("git config pre-commit.checks").strip.must_equal("[:jshint, :local]") 34 | sh("git config pre-commit.other").strip.must_equal("jshint,local") 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/configuration/providers/git_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/configuration/providers/git' 3 | 4 | describe PreCommit::Configuration::Providers::Git do 5 | subject do 6 | PreCommit::Configuration::Providers::Git 7 | end 8 | 9 | it "has priority" do 10 | subject.priority.must_equal(10) 11 | end 12 | 13 | describe :filesystem do 14 | before do 15 | create_temp_dir 16 | start_git 17 | sh "git config pre-commit.test2 2" 18 | sh "git config pre-commit.test3 3" 19 | end 20 | after(&:destroy_temp_dir) 21 | 22 | it "reads values" do 23 | example = subject.new 24 | example[:test1].must_be_nil 25 | example[:test2].must_equal("2") 26 | example[:test3].must_equal("3") 27 | end 28 | 29 | it "saves values" do 30 | example = subject.new 31 | example.update(:test2, "4") 32 | sh("git config pre-commit.test2").strip.must_equal("4") 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/unit/plugins/pre_commit/configuration/providers/yaml_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'plugins/pre_commit/configuration/providers/yaml' 3 | 4 | describe PreCommit::Configuration::Providers::Yaml do 5 | subject do 6 | PreCommit::Configuration::Providers::Yaml 7 | end 8 | 9 | it "has priority" do 10 | subject.priority.must_equal(20) 11 | end 12 | 13 | describe :filesystem do 14 | before do 15 | create_temp_dir 16 | start_git 17 | @system_file = "etc_pre_commit.yml" 18 | @global_file = ".pre_commit.yml" 19 | preprare_file(@system_file, SYSTEM_DATA) 20 | end 21 | after(&:destroy_temp_dir) 22 | 23 | it "reads system values" do 24 | example = subject.new 25 | example.instance_variable_set(:@system_file, @system_file) 26 | example.instance_variable_set(:@global_file, @global_file) 27 | example[:test1].must_equal(1) 28 | example[:test2].must_equal(2) 29 | example[:test3].must_equal(3) 30 | example[:test4].must_be_nil 31 | end 32 | 33 | it "reads system and global values" do 34 | preprare_file(@global_file, GLOBAL_DATA) 35 | example = subject.new 36 | example.instance_variable_set(:@system_file, @system_file) 37 | example.instance_variable_set(:@global_file, @global_file) 38 | example[:test1].must_equal(4) 39 | example[:test2].must_equal(5) 40 | example[:test3].must_equal(3) 41 | example[:test4].must_be_nil 42 | end 43 | 44 | it "reads system and local values" do 45 | Dir.mkdir("config") 46 | preprare_file("config/pre_commit.yml", LOCAL_DATA) 47 | example = subject.new 48 | example.instance_variable_set(:@system_file, @system_file) 49 | example.instance_variable_set(:@global_file, @global_file) 50 | example[:test1].must_equal(6) 51 | example[:test2].must_equal(2) 52 | example[:test3].must_equal(3) 53 | example[:test4].must_be_nil 54 | end 55 | 56 | it "reads system and local values" do 57 | preprare_file(@global_file, GLOBAL_DATA) 58 | Dir.mkdir("config") 59 | preprare_file("config/pre_commit.yml", LOCAL_DATA) 60 | example = subject.new 61 | example.instance_variable_set(:@system_file, @system_file) 62 | example.instance_variable_set(:@global_file, @global_file) 63 | example[:test1].must_equal(6) 64 | example[:test2].must_equal(5) 65 | example[:test3].must_equal(3) 66 | example[:test4].must_be_nil 67 | end 68 | 69 | it "saves values" do 70 | Dir.mkdir("config") 71 | preprare_file("config/pre_commit.yml", LOCAL_DATA) 72 | example = subject.new 73 | example.instance_variable_set(:@system_file, @system_file) 74 | example.instance_variable_set(:@global_file, @global_file) 75 | example.update(:test2, 7) 76 | File.open("config/pre_commit.yml") do |file| 77 | file.read.must_equal(<<-EXPECTED) 78 | --- 79 | :test1: 6 80 | :test2: 7 81 | EXPECTED 82 | end 83 | end 84 | end 85 | 86 | def preprare_file(name, content) 87 | File.open(name, "w") {|file| file.write(content) } 88 | end 89 | 90 | SYSTEM_DATA = <<-DATA 91 | --- 92 | :test1: 1 93 | :test2: 2 94 | :test3: 3 95 | DATA 96 | 97 | GLOBAL_DATA = <<-DATA 98 | --- 99 | :test1: 4 100 | :test2: 5 101 | DATA 102 | 103 | LOCAL_DATA = <<-DATA 104 | --- 105 | :test1: 6 106 | DATA 107 | 108 | end 109 | -------------------------------------------------------------------------------- /test/unit/pre-commit/checks/grep_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/checks/grep' 3 | 4 | describe PreCommit::Checks::Grep do 5 | subject do 6 | PreCommit::Checks::Grep.new(nil, nil, []) 7 | end 8 | 9 | it "succeeds if nothing changed" do 10 | subject.call([]).must_be_nil 11 | end 12 | 13 | it "succeeds if nothing changed" do 14 | ->{ subject.call([fixture_file('file_with_nb_space.rb')]) }.must_raise PreCommit::Checks::Grep::PaternNotSet 15 | end 16 | 17 | it "succeeds if file has no pattern" do 18 | subject.instance_variable_set(:@pattern, "other") 19 | subject.call([fixture_file('file_with_nb_space.rb')]).must_be_nil 20 | end 21 | 22 | it "fails if file has pattern" do 23 | subject.instance_variable_set(:@pattern, "test") 24 | subject.call([fixture_file('file_with_nb_space.rb')]).to_s.must_equal( 25 | "test/files/file_with_nb_space.rb:1:test" 26 | ) 27 | end 28 | 29 | it "fails if file has pattern, even if filename has spaces" do 30 | subject.instance_variable_set(:@pattern, "test") 31 | subject.call([fixture_file('filename with spaces.rb')]).to_s.must_equal( 32 | "test/files/filename with spaces.rb:1:test" 33 | ) 34 | end 35 | 36 | it "adds message to output" do 37 | subject.instance_variable_set(:@pattern, "test") 38 | subject.instance_variable_set(:@message, "extra message:") 39 | subject.call([fixture_file('file_with_nb_space.rb')]).to_s.must_equal("\ 40 | extra message: 41 | test/files/file_with_nb_space.rb:1:test") 42 | end 43 | 44 | it "respects extra_grep" do 45 | subject.instance_variable_set(:@pattern, "test") 46 | subject.instance_variable_set(:@extra_grep, %w{-v test}) 47 | subject.call([fixture_file('file_with_nb_space.rb')]).must_be_nil 48 | end 49 | 50 | it "finds grep for FreeBSD" do 51 | subject.send(:grep, 'FreeBSD').must_equal(%w{grep -EnIH}) 52 | end 53 | 54 | it "finds grep for other systems" do 55 | subject.send(:grep, 'other systems').must_equal(%w{grep -PnIH}) 56 | end 57 | 58 | it "detects grep version" do 59 | subject.send(:detect_grep_version).must_match(/grep/) 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/unit/pre-commit/checks/js_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/checks/js' 3 | 4 | describe PreCommit::Checks::Js do 5 | let(:check){ PreCommit::Checks::Js.new(nil, nil, []) } 6 | 7 | it "succeeds if nothing changed" do 8 | check.call([]).must_be_nil 9 | end 10 | 11 | it "succeeds if non-js changed" do 12 | check.call(["bar.foo"]).must_be_nil 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/unit/pre-commit/checks/plugin/config_file_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'tempfile' 3 | require 'stringio' 4 | require 'pre-commit/checks/plugin/config_file' 5 | 6 | describe PreCommit::Checks::Plugin::ConfigFile do 7 | let(:temp_config_file){ Tempfile.new('config.file') } 8 | let(:config){ MiniTest::Mock.new } 9 | let(:alternate_location){temp_config_file.path} 10 | subject do 11 | PreCommit::Checks::Plugin::ConfigFile.new('my_plugin', config, alternate_location) 12 | end 13 | 14 | describe 'when configuration is specified by a provider' do 15 | describe 'when the file exists' do 16 | it 'returns the provider specified configuration when it exists' do 17 | config.expect(:get, temp_config_file.path, ['my_plugin.config']) 18 | 19 | subject.location.must_equal(temp_config_file.path) 20 | config.verify 21 | end 22 | end 23 | 24 | describe 'when file does not exist' do 25 | it 'displays a usage message when the file does not exist' do 26 | config.expect(:get, 'missing.config', ['my_plugin.config']) 27 | 28 | stderr, $stderr = $stderr, StringIO.new 29 | subject.location 30 | stderr, $stderr = $stderr, stderr 31 | 32 | stderr.string.must_equal <<-USAGE 33 | Warning: my_plugin config file 'missing.config' does not exist 34 | Set the path to the config file using: 35 | \tgit config pre-commit.my_plugin.config 'path/relative/to/git/dir/my_plugin.config' 36 | Or in 'config/pre_commit.yml': 37 | \tmy.plugin.config: path/relative/to/git/dir/my_plugin.config 38 | Or set the environment variable: 39 | \texport MY_PLUGIN_CONFIG='path/relative/to/git/dir/my_plugin.config' 40 | my_plugin will look for a configuration file in the project root or use its default behavior. 41 | 42 | USAGE 43 | config.verify 44 | end 45 | end 46 | end 47 | 48 | describe 'when configuration is not specified by a provider and alternate location is specified' do 49 | before{ config.expect(:get, nil, ['my_plugin.config']) } 50 | 51 | describe 'when the file exists' do 52 | it 'returns the alternate location' do 53 | subject.location.must_equal(temp_config_file.path) 54 | end 55 | end 56 | 57 | describe 'when the file does not exist' do 58 | let(:alternate_location){ 'missing.config' } 59 | 60 | it 'returns nil' do 61 | subject.location.must_be_nil 62 | end 63 | end 64 | end 65 | 66 | describe 'when no configuration is specified' do 67 | before{ config.expect(:get, nil, ['my_plugin.config']) } 68 | let(:alternate_location){ nil } 69 | 70 | it 'returns nil' do 71 | subject.location.must_be_nil 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /test/unit/pre-commit/checks/plugin_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/checks/plugin' 3 | 4 | describe PreCommit::Checks::Plugin do 5 | 6 | subject do 7 | PreCommit::Checks::Plugin.new(nil, nil, []) 8 | end 9 | 10 | it "succeeds if nothing changed" do 11 | subject.send(:in_groups, [1,2,3,4], 2).to_a.must_equal [[1,2], [3,4]] 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/unit/pre-commit/checks/shell_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/checks/shell' 3 | 4 | describe PreCommit::Checks::Shell do 5 | subject do 6 | PreCommit::Checks::Shell.new(nil, nil, []) 7 | end 8 | 9 | it "nil for success" do 10 | subject.send(:execute_raw, "true").must_be_nil 11 | end 12 | 13 | it "error for fail" do 14 | subject.send(:execute_raw, "echo test && false").must_equal "test\n" 15 | end 16 | 17 | it "nil for fail on expected false" do 18 | subject.send(:execute_raw, "echo test", success_status: false).must_equal "test\n" 19 | end 20 | 21 | it "nil for fail on expected false" do 22 | subject.send(:execute_raw, "false", success_status: false).must_be_nil 23 | end 24 | 25 | it "builds_command" do 26 | subject.send(:build_command, ["echo", "test more"]).must_equal "echo test\\ more" 27 | end 28 | 29 | it "does not escape speciall shell things" do 30 | subject.send(:build_command, ["grep", "|", "grep"]).must_equal "grep | grep" 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /test/unit/pre-commit/cli_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/cli' 3 | require 'stringio' 4 | 5 | describe PreCommit::Cli do 6 | 7 | before do 8 | create_temp_dir 9 | start_git 10 | FileUtils.mkdir_p(".git/hooks") 11 | $stderr = StringIO.new 12 | $stdout = StringIO.new 13 | end 14 | after do 15 | destroy_temp_dir 16 | $stderr = STDERR 17 | $stdout = STDOUT 18 | end 19 | 20 | subject { PreCommit::Cli } 21 | 22 | it "shows help" do 23 | cli = subject.new('help') 24 | cli.execute.must_equal(true) 25 | $stderr.string.must_match(/Usage:/) 26 | $stdout.string.must_equal('') 27 | end 28 | 29 | it "shows fallbacks to help" do 30 | cli = subject.new('unknown') 31 | cli.execute.must_equal(false) 32 | $stderr.string.must_match(/Usage:/) 33 | $stdout.string.must_equal('') 34 | end 35 | 36 | it "intalls the hook" do 37 | cli = subject.new('install') 38 | cli.execute.must_equal(true) 39 | $stderr.string.must_equal('') 40 | $stdout.string.must_match(/Installed .*\/templates\/hooks\/automatic to .*\n/) 41 | end 42 | 43 | it "lists configuration" do 44 | cli = subject.new('list') 45 | cli.execute.must_equal(true) 46 | $stderr.string.must_equal('') 47 | $stdout.string.gsub(/\s+\n/,"\n").must_equal(<<-EXPECTED) 48 | Available providers: default(0) git(10) git_old(11) yaml(20) env(30) 49 | Available checks : #{available_checks} 50 | Default checks : common rails 51 | Enabled checks : common rails 52 | Evaluated checks : tabs nb_space whitespace merge_conflict debugger pry local jshint console_log migration 53 | Default warnings : 54 | Enabled warnings : 55 | Evaluated warnings : 56 | EXPECTED 57 | end 58 | 59 | it "lists plugins" do 60 | cli = subject.new('plugins') 61 | cli.execute.must_equal(true) 62 | $stderr.string.must_equal('') 63 | cli.list_evaluator.send(:plugin_names).each {|name| 64 | $stdout.string.must_match("#{name} : ") 65 | } 66 | end 67 | 68 | it "disable checks" do 69 | cli = subject.new('disable', 'git', 'checks', 'whitespace') 70 | status = cli.execute 71 | $stderr.string.must_equal('') 72 | $stdout.string.must_equal('') 73 | status.must_equal(true) 74 | sh("git config pre-commit.checks.remove").strip.must_equal("[:whitespace]") 75 | cli = subject.new('list') 76 | cli.execute.must_equal(true) 77 | $stderr.string.must_equal('') 78 | $stdout.string.gsub(/\s+\n/,"\n").must_equal(<<-EXPECTED) 79 | Available providers: default(0) git(10) git_old(11) yaml(20) env(30) 80 | Available checks : #{available_checks} 81 | Default checks : common rails 82 | Enabled checks : common rails 83 | Evaluated checks : tabs nb_space merge_conflict debugger pry local jshint console_log migration 84 | Default warnings : 85 | Enabled warnings : 86 | Evaluated warnings : 87 | EXPECTED 88 | end 89 | 90 | it "disable checks by alias" do 91 | cli = subject.new('disable', 'git', 'checks', 'whitespace') 92 | status = cli.execute 93 | $stderr.string.must_equal('') 94 | $stdout.string.must_equal('') 95 | status.must_equal(true) 96 | sh("git config pre-commit.checks.remove").strip.must_equal("[:whitespace]") 97 | cli = subject.new('list') 98 | cli.execute.must_equal(true) 99 | $stderr.string.must_equal('') 100 | $stdout.string.gsub(/\s+\n/,"\n").must_equal(<<-EXPECTED) 101 | Available providers: default(0) git(10) git_old(11) yaml(20) env(30) 102 | Available checks : #{available_checks} 103 | Default checks : common rails 104 | Enabled checks : common rails 105 | Evaluated checks : tabs nb_space merge_conflict debugger pry local jshint console_log migration 106 | Default warnings : 107 | Enabled warnings : 108 | Evaluated warnings : 109 | EXPECTED 110 | end 111 | 112 | it "enables warnings" do 113 | cli = subject.new('enable', 'git', 'warnings', 'gemfile_path') 114 | status = cli.execute 115 | $stderr.string.must_equal('') 116 | $stdout.string.must_equal('') 117 | status.must_equal(true) 118 | sh("git config pre-commit.warnings.add").strip.must_equal("[:gemfile_path]") 119 | cli = subject.new('list') 120 | cli.execute.must_equal(true) 121 | $stderr.string.must_equal('') 122 | $stdout.string.gsub(/\s+\n/,"\n").must_equal(<<-EXPECTED) 123 | Available providers: default(0) git(10) git_old(11) yaml(20) env(30) 124 | Available checks : #{available_checks} 125 | Default checks : common rails 126 | Enabled checks : common rails 127 | Evaluated checks : tabs nb_space whitespace merge_conflict debugger pry local jshint console_log migration 128 | Default warnings : 129 | Enabled warnings : gemfile_path 130 | Evaluated warnings : gemfile_path 131 | EXPECTED 132 | end 133 | 134 | it "does fail on unknown provider" do 135 | cli = subject.new('enable', 'unknown', 'warnings', 'gemfile_path') 136 | status = cli.execute 137 | $stderr.string.must_equal(<<-EXPECTED) 138 | Plugin not found for unknown. 139 | EXPECTED 140 | $stdout.string.must_equal('') 141 | status.must_equal(false) 142 | end 143 | 144 | it "shows help on missing enable params" do 145 | cli = subject.new('enable') 146 | status = cli.execute 147 | $stderr.string.must_equal(<<-EXPECTED) 148 | Unknown parameters: "enable" 149 | Usage: pre-commit install 150 | Usage: pre-commit list 151 | Usage: pre-commit plugins 152 | Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin' 153 | Usage: pre-commit check1 [check2...] 154 | EXPECTED 155 | $stdout.string.must_equal('') 156 | status.must_equal(false) 157 | end 158 | 159 | it "shows help on missing enable git params" do 160 | cli = subject.new('enable', 'git') 161 | status = cli.execute 162 | $stderr.string.must_equal(<<-EXPECTED) 163 | Unknown parameters: "enable" "git" 164 | Usage: pre-commit install 165 | Usage: pre-commit list 166 | Usage: pre-commit plugins 167 | Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin' 168 | Usage: pre-commit check1 [check2...] 169 | EXPECTED 170 | $stdout.string.must_equal('') 171 | status.must_equal(false) 172 | end 173 | 174 | it "shows help on missing enable git checks params" do 175 | cli = subject.new('enable', 'git', 'checks') 176 | status = cli.execute 177 | $stderr.string.must_equal(<<-EXPECTED) 178 | Unknown parameters: "enable" "git" "checks" 179 | Usage: pre-commit install 180 | Usage: pre-commit list 181 | Usage: pre-commit plugins 182 | Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin' 183 | Usage: pre-commit check1 [check2...] 184 | EXPECTED 185 | $stdout.string.must_equal('') 186 | status.must_equal(false) 187 | end 188 | 189 | it "shows help on missing disable params" do 190 | cli = subject.new('disable') 191 | status = cli.execute 192 | $stderr.string.must_equal(<<-EXPECTED) 193 | Unknown parameters: "disable" 194 | Usage: pre-commit install 195 | Usage: pre-commit list 196 | Usage: pre-commit plugins 197 | Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin' 198 | Usage: pre-commit check1 [check2...] 199 | EXPECTED 200 | $stdout.string.must_equal('') 201 | status.must_equal(false) 202 | end 203 | 204 | end 205 | -------------------------------------------------------------------------------- /test/unit/pre-commit/configuration/providers_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/configuration/providers' 3 | require 'plugins/pre_commit/configuration/providers/default' 4 | require 'plugins/pre_commit/configuration/providers/git' 5 | require 'pluginator' 6 | 7 | describe PreCommit::Configuration::Providers do 8 | subject do 9 | PreCommit::Configuration::Providers 10 | end 11 | 12 | it "detects plugins" do 13 | provider = subject.new(Pluginator.find("pre_commit")) 14 | provider.send(:plugins)[0].must_be_instance_of(PreCommit::Configuration::Providers::Default) 15 | provider.send(:plugins).size.must_be(:>=, 3) 16 | end 17 | 18 | describe "filesystem" do 19 | before do 20 | create_temp_dir 21 | start_git 22 | @defaults = {:test1 => 1, :test2 => 2, :test3 => 3} 23 | @plugins = [ 24 | PreCommit::Configuration::Providers::Default.new(@defaults), 25 | PreCommit::Configuration::Providers::Git.new, 26 | PreCommit::Configuration::Providers::Yaml.new, 27 | ] 28 | 29 | sh "git config pre-commit.test1 [:a]" 30 | sh "git config pre-commit.test2 5" 31 | Dir.mkdir("config") 32 | File.open("config/pre_commit.yml", "w") do |file| 33 | file.write <<-DATA 34 | --- 35 | :test1: 36 | - :c 37 | DATA 38 | end 39 | end 40 | after(&:destroy_temp_dir) 41 | 42 | it "reads configurations" do 43 | provider = subject.new(nil, @plugins) 44 | provider[:test1].must_equal([:c]) 45 | provider[:test2].must_equal("5") 46 | provider[:test3].must_equal(3) 47 | provider[:test4].must_be_nil 48 | end 49 | 50 | it "updates git configurations" do 51 | provider = subject.new(nil, @plugins) 52 | provider.update('git', 'test1', :+, [:b]) 53 | sh("git config pre-commit.test1").strip.must_equal("[:a, :b]") 54 | end 55 | 56 | it "updates yaml configurations" do 57 | provider = subject.new(nil, @plugins) 58 | provider.update('yaml', 'test2', :+, [:d]) 59 | File.open("config/pre_commit.yml", "r") do |file| 60 | file.read.must_equal(<<-EXPECTED) 61 | --- 62 | :test1: 63 | - :c 64 | :test2: 65 | - :d 66 | EXPECTED 67 | end 68 | end 69 | 70 | it "update add configurations" do 71 | sh "git config pre-commit.test.array '[:one, :two]'" 72 | provider = subject.new(nil, @plugins) 73 | provider.update('git', 'test.array', :+, [:three]) 74 | sh("git config pre-commit.test.array").strip.must_equal("[:one, :two, :three]") 75 | end 76 | 77 | it "update remore configurations" do 78 | sh "git config pre-commit.test.array '[:one, :two]'" 79 | provider = subject.new(nil, @plugins) 80 | provider.update('git', 'test.array', :-, [:one]) 81 | sh("git config pre-commit.test.array").strip.must_equal("[:two]") 82 | end 83 | 84 | end 85 | 86 | end 87 | -------------------------------------------------------------------------------- /test/unit/pre-commit/configuration_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/configuration' 3 | require 'plugins/pre_commit/configuration/providers/default' 4 | require 'plugins/pre_commit/configuration/providers/git' 5 | require 'plugins/pre_commit/checks/before_all' 6 | require 'plugins/pre_commit/checks/console_log' 7 | 8 | describe PreCommit::Configuration do 9 | describe "fake" do 10 | subject do 11 | PreCommit::Configuration.new(nil, { 12 | :value => 'simple', 13 | :test => %w{5 6 7}, 14 | :test_add => %w{8}, 15 | :test_remove => %w{6}, 16 | }) 17 | end 18 | 19 | it "reads values" do 20 | subject.get(:value).must_equal('simple') 21 | end 22 | 23 | it "reads using string" do 24 | subject.get("value").must_equal('simple') 25 | end 26 | 27 | it "fails to read string as array" do 28 | ->(){ subject.get_arr(:value) }.must_raise(PreCommit::NotAnArray) 29 | end 30 | 31 | it "calculates checks" do 32 | subject.get_combined(:test).must_equal(%w{5 6 7 8}) 33 | end 34 | end 35 | 36 | describe "filesystem" do 37 | before do 38 | create_temp_dir 39 | start_git 40 | sh "git config pre-commit.test.add '[:one, :two]'" 41 | sh "git config pre-commit.test.remove '[:three, :four]'" 42 | $stderr = StringIO.new 43 | $stdout = StringIO.new 44 | end 45 | after do 46 | destroy_temp_dir 47 | $stderr = STDERR 48 | $stdout = STDOUT 49 | end 50 | subject do 51 | PreCommit::Configuration.new( 52 | {'checks' => [ 53 | PreCommit::Checks::BeforeAll, 54 | PreCommit::Checks::ConsoleLog 55 | ]}, 56 | PreCommit::Configuration::Providers.new(nil, [ 57 | PreCommit::Configuration::Providers::Default.new({}), 58 | PreCommit::Configuration::Providers::Git.new, 59 | ]) 60 | ) 61 | end 62 | 63 | it "enables list configuration" do 64 | subject.enable('git', 'test', 'three').must_equal(true) 65 | sh("git config pre-commit.test.add").strip.must_equal("[:one, :two, :three]") 66 | sh("git config pre-commit.test.remove").strip.must_equal("[:four]") 67 | end 68 | 69 | it "disables list configuration" do 70 | subject.disable('git', 'test', 'two').must_equal(true) 71 | sh("git config pre-commit.test.add").strip.must_equal("[:one]") 72 | sh("git config pre-commit.test.remove").strip.must_equal("[:three, :four, :two]") 73 | end 74 | 75 | it "handles missing enable plugin" do 76 | subject.enable('unknown', 'test', 'two').must_equal(false) 77 | $stderr.string.strip.must_match('Plugin not found for unknown.') 78 | $stdout.string.must_equal('') 79 | end 80 | 81 | it "handles missing disable plugin" do 82 | subject.disable('another', 'test', 'two').must_equal(false) 83 | $stderr.string.strip.must_match('Plugin not found for another.') 84 | $stdout.string.must_equal('') 85 | end 86 | 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /test/unit/pre-commit/error_list_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/error_list' 3 | 4 | describe PreCommit::ErrorList do 5 | 6 | it :starts_empty do 7 | result = PreCommit::ErrorList.new() 8 | result.errors.must_equal [] 9 | end 10 | 11 | it :creates_array_from_string do 12 | result = PreCommit::ErrorList.new("message1") 13 | result.errors.must_equal [PreCommit::Line.new("message1")] 14 | end 15 | 16 | it :uses_array_for_errors do 17 | result = PreCommit::ErrorList.new([1,2]) 18 | result.errors.must_equal [1,2] 19 | end 20 | 21 | it :converts_errors_to_strings_in_to_a do 22 | result = PreCommit::ErrorList.new([1,2]) 23 | result.to_a.must_equal ["1","2"] 24 | end 25 | 26 | it :converts_errors_to_string_in_to_s do 27 | result = PreCommit::ErrorList.new([1,2]) 28 | result.to_s.must_equal "1\n2" 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /test/unit/pre-commit/installer_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/installer' 3 | require 'stringio' 4 | 5 | describe PreCommit::Installer do 6 | 7 | before do 8 | create_temp_dir 9 | start_git 10 | FileUtils.mkdir_p(".git/hooks") 11 | $stderr = StringIO.new 12 | $stdout = StringIO.new 13 | end 14 | after do 15 | destroy_temp_dir 16 | $stderr = STDERR 17 | $stdout = STDOUT 18 | end 19 | 20 | subject { PreCommit::Installer } 21 | let(:automatic_hook_contents) { File.read(subject.new.templates["automatic"]) } 22 | 23 | it "intalls the hook" do 24 | installer = subject.new 25 | File.exist?(installer.target).must_equal false 26 | 27 | installer.install.must_equal(true) 28 | File.exist?(installer.target).must_equal true 29 | File.read(installer.target).must_equal automatic_hook_contents 30 | 31 | $stderr.string.must_equal('') 32 | $stdout.string.must_match(/Installed .*\/templates\/hooks\/automatic to #{installer.target}\n/) 33 | end 34 | 35 | it "installs other hook templates" do 36 | installer = subject.new('--manual') 37 | File.exist?(installer.target).must_equal false 38 | installer.install.must_equal(true) 39 | File.exist?(installer.target).must_equal true 40 | File.read(installer.target).must_equal File.read(installer.send(:templates)["manual"]) 41 | $stderr.string.must_equal('') 42 | $stdout.string.must_match(/Installed .*\/templates\/hooks\/manual to #{installer.target}\n/) 43 | end 44 | 45 | it "installs the automatic hook when passed --automatic" do 46 | installer = subject.new('--automatic') 47 | File.exist?(installer.target).must_equal false 48 | 49 | installer.install.must_equal(true) 50 | File.exist?(installer.target).must_equal true 51 | File.read(installer.target).must_equal automatic_hook_contents 52 | 53 | $stderr.string.must_equal('') 54 | $stdout.string.must_match(/Installed .*\/templates\/hooks\/automatic to #{installer.target}\n/) 55 | end 56 | 57 | it "handles missing templates" do 58 | installer = subject.new('--not-found') 59 | File.exist?(installer.target).must_equal false 60 | installer.install.must_equal(false) 61 | $stderr.string.must_match(/Could not find template/) 62 | $stdout.string.must_equal('') 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /test/unit/pre-commit/line_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/line' 3 | 4 | describe PreCommit::Line do 5 | describe :to_s do 6 | 7 | subject do 8 | PreCommit::Line.new("message1") 9 | end 10 | 11 | it :has_message do 12 | subject.to_s.must_equal("message1") 13 | end 14 | 15 | it :has_file do 16 | subject.file = "path/to/file" 17 | subject.to_s.must_equal("message1\npath/to/file") 18 | end 19 | 20 | it :has_file_without_message do 21 | subject.message = nil 22 | subject.file = "path/to/file" 23 | subject.to_s.must_equal("path/to/file") 24 | end 25 | 26 | it :has_file_and_line do 27 | subject.file = "path/to/file" 28 | subject.line = "4" 29 | subject.to_s.must_equal("message1\npath/to/file:4") 30 | end 31 | 32 | it :has_no_line_or_code_without_file do 33 | subject.line = "4" 34 | subject.code = " some code" 35 | subject.to_s.must_equal("message1") 36 | end 37 | 38 | it :has_file_and_line_and_code do 39 | subject.file = "path/to/file" 40 | subject.line = "4" 41 | subject.code = " some code" 42 | subject.to_s.must_equal("message1\npath/to/file:4: some code") 43 | end 44 | 45 | it :has_file_and_code do 46 | subject.file = "path/to/file" 47 | subject.code = " some code" 48 | subject.to_s.must_equal("message1\npath/to/file: some code") 49 | end 50 | 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/unit/pre-commit/list_evaluator_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/list_evaluator' 3 | require 'plugins/pre_commit/configuration/providers/default' 4 | require 'plugins/pre_commit/configuration/providers/git' 5 | require 'plugins/pre_commit/checks/before_all' 6 | require 'plugins/pre_commit/checks/console_log' 7 | 8 | describe PreCommit::ListEvaluator do 9 | let :configuration do 10 | PreCommit::Configuration.new( 11 | PreCommit.pluginator, 12 | PreCommit::Configuration::Providers.new(nil, [ 13 | PreCommit::Configuration::Providers::Default.new({}), 14 | ]) 15 | ) 16 | end 17 | 18 | subject do 19 | PreCommit::ListEvaluator.new(configuration) 20 | end 21 | 22 | it :list do 23 | subject.list.gsub(/\s+\n/,"\n").must_equal(<<-EXPECTED) 24 | Available providers: default(0) 25 | Available checks : #{available_checks} 26 | Default checks : 27 | Enabled checks : 28 | Evaluated checks : 29 | Default warnings : 30 | Enabled warnings : 31 | Evaluated warnings : 32 | EXPECTED 33 | end 34 | 35 | it "plugins have includes" do 36 | subject.send(:format_plugin, "ruby", "6", configuration.pluginator.find_check(:ruby)).must_equal([ 37 | " ruby : Plugins common for ruby.", 38 | " - includes: pry local", 39 | ]) 40 | end 41 | it "plugins have excludes" do 42 | subject.send(:format_plugin, "rubocop", "7", configuration.pluginator.find_check(:rubocop)).must_equal([ 43 | "rubocop : Runs rubocop to detect errors.", 44 | " - excludes: ruby_symbol_hashrocket", 45 | ]) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/unit/pre-commit/plugins_list_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/plugins_list' 3 | 4 | class ClassBase 5 | def call(*args) 6 | end 7 | end 8 | class Class1 < ClassBase; end 9 | class Class3 < ClassBase 10 | def self.includes 11 | [:class4] 12 | end 13 | end 14 | class Class4 < ClassBase; end 15 | class Class5 < ClassBase 16 | def self.excludes 17 | [:class4] 18 | end 19 | end 20 | class Class6 < ClassBase 21 | def self.excludes 22 | [:class3] 23 | end 24 | end 25 | class Class7 < ClassBase 26 | def self.includes 27 | [:class6] 28 | end 29 | end 30 | 31 | describe PreCommit::PluginsList do 32 | describe :find_class do 33 | subject do 34 | PreCommit::PluginsList.new([], []) do |name| 35 | { :class1 => Class1 }[name] 36 | end 37 | end 38 | 39 | it "finds class" do 40 | subject.send(:find_class, :class1).must_equal(Class1) 41 | end 42 | 43 | it "does not find class" do 44 | subject.send(:find_class, :class2).must_be_nil 45 | end 46 | 47 | end 48 | 49 | describe :configured_names do 50 | subject do 51 | PreCommit::PluginsList.new([:a, :b], []) do |name| 52 | end 53 | end 54 | 55 | it "has configured_names" do 56 | subject.configured_names.must_equal([:a, :b]) 57 | end 58 | end 59 | 60 | describe :finds_classes do 61 | subject do 62 | PreCommit::PluginsList.new([], []) do |name| 63 | { :class1 => Class1 }[name] 64 | end 65 | end 66 | 67 | it "finds existing class" do 68 | subject.send(:find_classes, [:class1]).must_equal([[:class1, Class1]]) 69 | end 70 | 71 | it "does not find missing class" do 72 | subject.send(:find_classes, [:class2]).must_equal([]) 73 | end 74 | 75 | it "finds only existing class" do 76 | subject.send(:find_classes, [:class1, :class2]).must_equal([[:class1, Class1]]) 77 | end 78 | end 79 | 80 | describe :class_and_includes do 81 | subject do 82 | PreCommit::PluginsList.new([], []) do |name| 83 | { :class1 => Class1, :class3 => Class3, :class4 => Class4 }[name] 84 | end 85 | end 86 | 87 | it "finds no included class" do 88 | subject.send(:class_and_includes, :class1, Class1).must_equal([:class1, Class1, []]) 89 | end 90 | 91 | it "finds included class" do 92 | subject.send(:class_and_includes, :class3, Class3).must_equal([:class3, Class3, [[:class4, Class4,[]]]]) 93 | end 94 | end 95 | 96 | describe :find_classes_and_includes do 97 | subject do 98 | PreCommit::PluginsList.new([], []) do |name| 99 | { :class1 => Class1, :class3 => Class3, :class4 => Class4 }[name] 100 | end 101 | end 102 | 103 | it "finds existing class" do 104 | subject.send(:find_classes_and_includes, [:class1]).must_equal([[:class1, Class1, []]]) 105 | end 106 | 107 | it "does not find missing class" do 108 | subject.send(:find_classes_and_includes, [:class2]).must_equal([]) 109 | end 110 | 111 | it "finds nested existing class" do 112 | subject.send(:find_classes_and_includes, [:class1, :class2, :class3]).must_equal([[:class1, Class1, []], [:class3, Class3, [[:class4, Class4,[]]]]]) 113 | end 114 | end 115 | 116 | describe :excludes do 117 | subject do 118 | PreCommit::PluginsList.new([], []) do |name| 119 | end 120 | end 121 | 122 | it "does not exclude classes" do 123 | subject.send(:excludes, [[:class1, Class1, []]]).must_equal([[]]) 124 | end 125 | 126 | it "does exclude classes" do 127 | subject.send(:excludes, [[:class5, Class5, []]]).must_equal([[:class4]]) 128 | end 129 | 130 | it "does exclude nested classes" do 131 | subject.send(:excludes, [[:class7, Class7, [[:class6, Class6, []]]]]).must_equal([[[:class3]]]) 132 | end 133 | end 134 | 135 | describe :filter_excludes do 136 | subject do 137 | PreCommit::PluginsList.new([], []) do |name| 138 | end 139 | end 140 | 141 | it "does not filter classes" do 142 | subject.send(:filter_excludes, [[:class1, Class1, []]], []).must_equal([[:class1, Class1, []]]) 143 | end 144 | 145 | it "does filter classes" do 146 | subject.send(:filter_excludes, [[:class1, Class1, []]], [:class1]).must_equal([]) 147 | end 148 | 149 | it "does filter nested classes" do 150 | subject.send(:filter_excludes, [[:class3, Class3, [[:class4, Class4, []]]]], [:class4]).must_equal([[:class3, Class3, []]]) 151 | end 152 | 153 | it "does filter including classes" do 154 | subject.send(:filter_excludes, [[:class3, Class3, [[:class4, Class4, []]]]], [:class3]).must_equal([]) 155 | end 156 | end 157 | 158 | describe :evaluated_names_pairs do 159 | subject do 160 | PreCommit::PluginsList.new([:class1, :class2, :class3, :class7], []) do |name| 161 | { :class1 => Class1, :class3 => Class3, :class4 => Class4, :class6 => Class6, :class7 => Class7 }[name] 162 | end 163 | end 164 | 165 | it "evaluates list" do 166 | subject.send(:evaluated_names_pairs).must_equal([[:class1, Class1, []], [:class7, Class7, [[:class6, Class6, []]]]]) 167 | end 168 | 169 | it "evaluated_names" do 170 | subject.evaluated_names.must_equal([:class1, :class7, :class6]) 171 | end 172 | 173 | it "list_to_run" do 174 | subject.list_to_run.must_equal([Class1, Class7, Class6]) 175 | end 176 | end 177 | 178 | describe "evaluated_names_pairs config remove" do 179 | subject do 180 | PreCommit::PluginsList.new([:class7], [:class6]) do |name| 181 | { :class1 => Class1, :class3 => Class3, :class4 => Class4, :class6 => Class6, :class7 => Class7 }[name] 182 | end 183 | end 184 | 185 | it "evaluates list" do 186 | subject.send(:evaluated_names_pairs).must_equal([[:class7, Class7, []]]) 187 | end 188 | 189 | end 190 | 191 | 192 | end 193 | -------------------------------------------------------------------------------- /test/unit/pre-commit/runner_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/runner' 3 | require 'stringio' 4 | 5 | class FakeAll 6 | def initialize(value) 7 | @value = value 8 | end 9 | # Pluginator 10 | def find_check(name) 11 | @value[name] 12 | end 13 | # Plugin 14 | def new(pluginator, config, list) 15 | @pluginator = pluginator 16 | self 17 | end 18 | def instance_methods 19 | methods 20 | end 21 | def call(list) 22 | @value 23 | end 24 | def checks_evaluated(type = :evaluated_names) 25 | @value[:checks] 26 | end 27 | def warnings_evaluated(type = :evaluated_names) 28 | @value[:warnings] 29 | end 30 | end 31 | 32 | class EmptyListEvaluator 33 | def warnings_evaluated(*); []; end 34 | def checks_evaluated(*); []; end 35 | end 36 | 37 | describe PreCommit::Runner do 38 | 39 | describe "file selection" do 40 | 41 | let :list_evaluator do 42 | EmptyListEvaluator.new 43 | end 44 | 45 | subject do 46 | PreCommit::Runner.new(StringIO.new).tap do |runner| 47 | runner.instance_variable_set(:@list_evaluator, list_evaluator) 48 | end 49 | end 50 | 51 | it "uses files passed to #run" do 52 | subject.run("some_file", "another_file") 53 | subject.staged_files.must_equal(["some_file", "another_file"]) 54 | end 55 | 56 | end 57 | 58 | describe "stubbed - failing" do 59 | before do 60 | @output = StringIO.new 61 | end 62 | let :pluginator do 63 | FakeAll.new( 64 | :plugin1 => FakeAll.new("result 1"), 65 | :plugin2 => FakeAll.new("result 2"), 66 | :plugin3 => FakeAll.new("result 3"), 67 | ) 68 | end 69 | let :configuration do 70 | PreCommit::Configuration.new(pluginator) 71 | end 72 | let :list_evaluator do 73 | list_evaluator = PreCommit::ListEvaluator.new(configuration) 74 | list_evaluator.instance_variable_set(:@get_combined_warnings, [:plugin1]) 75 | list_evaluator.instance_variable_set(:@get_arr_warnings_remove, []) 76 | list_evaluator.instance_variable_set(:@get_combined_checks, [:plugin2, :plugin3]) 77 | list_evaluator.instance_variable_set(:@get_arr_checks_remove, []) 78 | list_evaluator 79 | end 80 | subject do 81 | runner = PreCommit::Runner.new( @output, [], configuration, pluginator ) 82 | runner.instance_variable_set(:@list_evaluator, list_evaluator) 83 | runner 84 | end 85 | 86 | it "has warning template" do 87 | result = subject.warnings(["warn 1", "warn 2"]) 88 | result.must_match(/^pre-commit:/) 89 | result.must_match(/^warn 1$/) 90 | result.must_match(/^warn 2$/) 91 | result.wont_match(/^error 3$/) 92 | end 93 | 94 | it "has errors template" do 95 | result = subject.checks(["error 3", "error 4"]) 96 | result.must_match(/^pre-commit:/) 97 | result.must_match(/^error 3$/) 98 | result.must_match(/^error 4$/) 99 | result.wont_match(/^warn 1$/) 100 | end 101 | 102 | it "finds plugins" do 103 | result = subject.list_to_run(:checks) 104 | result.must_equal([pluginator.find_check(:plugin2), pluginator.find_check(:plugin3)]) 105 | end 106 | 107 | it "executes checks" do 108 | result = subject.execute([FakeAll.new("result 1"), FakeAll.new("result 2")]) 109 | result.size.must_equal(2) 110 | result.must_include("result 1") 111 | result.must_include("result 2") 112 | result.wont_include("result 3") 113 | end 114 | 115 | describe :show_output do 116 | it "has no output" do 117 | subject.show_output(:checks, []).must_equal(true) 118 | @output.string.must_equal('') 119 | end 120 | it "shows output" do 121 | subject.show_output(:checks, ["result 1", "result 2"]).must_equal(false) 122 | @output.string.must_match(/^result 1$/) 123 | @output.string.must_match(/^result 2$/) 124 | end 125 | end 126 | 127 | describe :run do 128 | it "has no errors" do 129 | subject.run.must_equal(false) 130 | @output.string.must_equal(<<-EXPECTED) 131 | pre-commit: Some warnings were raised. These will not stop commit: 132 | result 1 133 | pre-commit: Stopping commit because of errors. 134 | result 2 135 | result 3 136 | pre-commit: You can bypass this check using `git commit -n` 137 | 138 | EXPECTED 139 | end 140 | end 141 | 142 | end 143 | 144 | describe "stubbed - success" do 145 | before do 146 | @output = StringIO.new 147 | end 148 | let :pluginator do 149 | FakeAll.new( 150 | :plugin1 => FakeAll.new(nil), 151 | :plugin2 => FakeAll.new(nil), 152 | :plugin3 => FakeAll.new(nil), 153 | ) 154 | end 155 | let :configuration do 156 | PreCommit::Configuration.new(pluginator) 157 | end 158 | let :list_evaluator do 159 | list_evaluator = PreCommit::ListEvaluator.new(configuration) 160 | list_evaluator.instance_variable_set(:@get_combined_warnings, [:plugin1]) 161 | list_evaluator.instance_variable_set(:@get_arr_warnings_remove, []) 162 | list_evaluator.instance_variable_set(:@get_combined_checks, [:plugin2, :plugin3]) 163 | list_evaluator.instance_variable_set(:@get_arr_checks_remove, []) 164 | list_evaluator 165 | end 166 | subject do 167 | runner = PreCommit::Runner.new( @output, [], configuration, pluginator ) 168 | runner.instance_variable_set(:@list_evaluator, list_evaluator) 169 | runner 170 | end 171 | 172 | it "has no errors" do 173 | subject.run.must_equal(true) 174 | @output.string.must_equal('') 175 | end 176 | end 177 | 178 | 179 | describe "real run" do 180 | before do 181 | @output = StringIO.new 182 | create_temp_dir 183 | start_git 184 | end 185 | after(&:destroy_temp_dir) 186 | 187 | it "detects tabs" do 188 | write("test.rb", "\t\t Muahaha\n\n\n") 189 | sh "git add -A" 190 | status = PreCommit::Runner.new(@output, ["test.rb"]).run 191 | @output.string.must_equal(<<-EXPECTED) 192 | pre-commit: Stopping commit because of errors. 193 | detected tab before initial space: 194 | test.rb:1:\t\t Muahaha 195 | test.rb:2: new blank line at EOF. 196 | 197 | pre-commit: You can bypass this check using `git commit -n` 198 | 199 | EXPECTED 200 | status.must_equal(false) # more interested in output first 201 | end 202 | 203 | it "allows commit" do 204 | status = PreCommit::Runner.new(@output).run 205 | @output.string.must_equal('') 206 | status.must_equal(true) # more interested in output first 207 | end 208 | 209 | end 210 | 211 | end 212 | -------------------------------------------------------------------------------- /test/unit/pre-commit/template_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/template' 3 | 4 | describe PreCommit::Template do 5 | 6 | subject do 7 | PreCommit::Template.new("plugin-name", 8 | "Author Name", 9 | "author.email@example.com", 10 | "Plugin description.") 11 | end 12 | 13 | it "gathers a list of files" do 14 | assert_equal(12, subject.all_files.length) 15 | end 16 | 17 | it "substitutes in the plugin name in the file list" do 18 | processed_filenames = subject.all_files.map do |file| 19 | subject.target_path(file) 20 | end.sort 21 | 22 | [ 23 | "pre-commit-plugin-name/.gitignore", 24 | "pre-commit-plugin-name/.travis.yml", 25 | "pre-commit-plugin-name/pre-commit-plugin-name.gemspec", 26 | "pre-commit-plugin-name/Gemfile", 27 | "pre-commit-plugin-name/lib/plugins/pre_commit/checks/plugin-name.rb", 28 | "pre-commit-plugin-name/lib/pre-commit/plugin-name/version.rb", 29 | "pre-commit-plugin-name/LICENSE", 30 | "pre-commit-plugin-name/Rakefile", 31 | "pre-commit-plugin-name/README.md", 32 | "pre-commit-plugin-name/test/files/.keep", 33 | "pre-commit-plugin-name/test/minitest_helper.rb", 34 | "pre-commit-plugin-name/test/plugins/pre_commit/checks/plugin-name_test.rb" 35 | ].sort.each_with_index do |expected, index| 36 | assert_equal(expected, processed_filenames[index]) 37 | end 38 | 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /test/unit/pre-commit/utils/git_conversions_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/utils/git_conversions' 3 | 4 | describe PreCommit::Utils::GitConversions do 5 | subject do 6 | Object.new.send(:extend, PreCommit::Utils::GitConversions) 7 | end 8 | 9 | describe :str_symbolize do 10 | it "converts strings to symbols" do 11 | subject.str_symbolize(':simple').must_equal(:simple) 12 | end 13 | 14 | it "leaves symbols in place" do 15 | subject.str_symbolize(:simple).must_equal(:simple) 16 | end 17 | 18 | it "leaves strings in place" do 19 | subject.str_symbolize('simple').must_equal('simple') 20 | end 21 | end 22 | 23 | describe :str2arr do 24 | it "converts strings to arrays" do 25 | subject.str2arr("test, some,string").must_equal(%w{test some string}) 26 | end 27 | 28 | it "str_symbolizes array values" do 29 | subject.str2arr(":test,:some, string").must_equal([:test, :some, "string"]) 30 | end 31 | end 32 | 33 | describe :git_to_ruby do 34 | it "transforms arrays" do 35 | subject.git_to_ruby("[1,2,3]").must_equal(%w{1 2 3}) 36 | end 37 | 38 | it "transforms empty string to nil" do 39 | subject.git_to_ruby("").must_be_nil 40 | end 41 | 42 | it "transforms string to symbols" do 43 | subject.git_to_ruby(":value").must_equal(:value) 44 | end 45 | 46 | it "transforms does not change strings" do 47 | subject.git_to_ruby("value").must_equal("value") 48 | end 49 | 50 | it "passes on unknowns" do 51 | subject.git_to_ruby({}).must_equal({}) 52 | end 53 | end 54 | 55 | describe :sym_symbolize do 56 | it "converts symbols to strings" do 57 | subject.sym_symbolize(:something).must_equal(":something") 58 | end 59 | 60 | it "leaves strings in place" do 61 | subject.sym_symbolize("something").must_equal("something") 62 | end 63 | end 64 | 65 | describe :arr2str do 66 | it "converts arrays to strings" do 67 | subject.arr2str([:symbols, "strings"]).must_equal("[:symbols, strings]") 68 | end 69 | end 70 | 71 | describe :ruby_to_git do 72 | it "converts strings" do 73 | subject.ruby_to_git("something").must_equal("something") 74 | end 75 | it "converts symbols" do 76 | subject.ruby_to_git(:something).must_equal(":something") 77 | end 78 | it "converts arrays" do 79 | subject.ruby_to_git([:symbols, "strings"]).must_equal("[:symbols, strings]") 80 | end 81 | end 82 | 83 | end 84 | -------------------------------------------------------------------------------- /test/unit/pre-commit/utils/staged_files_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'pre-commit/utils/staged_files' 3 | 4 | describe PreCommit::Utils::StagedFiles do 5 | before do 6 | create_temp_dir 7 | start_git 8 | end 9 | after(&:destroy_temp_dir) 10 | subject do 11 | Object.new.send(:extend, PreCommit::Utils::StagedFiles) 12 | end 13 | 14 | describe :staged_files do 15 | 16 | it "finds staged files" do 17 | write("test.rb", "\t\t Muahaha\n\n\n") 18 | sh "git add -A" 19 | subject.staged_files.must_equal(['test.rb']) 20 | end 21 | 22 | it "filters out binary files" do 23 | write("test", (1..50).map(&:chr).join) 24 | sh "git add -A" 25 | subject.staged_files.must_equal([]) 26 | end 27 | 28 | it "quick-filters out images based on extension" do 29 | write("foo.jpg", "not an image") 30 | sh "git add -A" 31 | subject.staged_files.must_equal([]) 32 | end 33 | 34 | it "does not blow up on zero size files" do 35 | write("no_contents", "") 36 | sh "git add -A" 37 | subject.staged_files.must_equal(["no_contents"]) 38 | end 39 | 40 | it "has empty list for no changes" do 41 | subject.staged_files.must_equal([]) 42 | end 43 | 44 | it "does not include links to nowhere" do 45 | write("something.rb", "class Something; end") 46 | write("nowhere.rb", "") 47 | FileUtils.ln_s("nowhere.rb", "link_to_nowhere.rb") 48 | FileUtils.rm("nowhere.rb") 49 | system("git", "add", "-A") 50 | subject.staged_files.must_equal ["something.rb"] 51 | end 52 | 53 | end # :staged_files 54 | 55 | describe "source file" do 56 | it "always treats Ruby files as source files" do 57 | subject.source_file?("foo.rb").must_equal(true) 58 | end 59 | end 60 | 61 | describe "ignore extension" do 62 | it "ignores images" do 63 | subject.ignore_extension?("foo.jpg").must_equal(true) 64 | end 65 | 66 | it "treats ordinary source code extensions as source files" do 67 | subject.ignore_extension?("foo.rb").must_equal(false) 68 | end 69 | 70 | it "treats disk images as non-source files" do 71 | subject.ignore_extension?("foo.dmg").must_equal(true) 72 | end 73 | end 74 | 75 | describe :repo_ignores do 76 | 77 | it "builds ignores file path" do 78 | subject.send(:repo_ignores_file).must_match(%r{.*/.pre_commit.ignore$}) 79 | end 80 | 81 | it "does not fail without ignore file" do 82 | subject.send(:repo_ignores).must_equal [] 83 | end 84 | 85 | it "does read ignores file" do 86 | write(".pre_commit.ignore", "file.rb\n") 87 | subject.send(:repo_ignores).must_equal ["file.rb"] 88 | end 89 | 90 | it "it does exclude links from ignore file" do 91 | write(".pre_commit.ignore", "file.rb\n") 92 | write("something.rb", "") 93 | write("file.rb", "") 94 | system("git", "add", "-A") 95 | subject.staged_files.sort.must_equal [".pre_commit.ignore", "something.rb"].sort 96 | end 97 | 98 | end # :repo_ignores 99 | 100 | describe :staged_files_all do 101 | 102 | it "lists all files" do 103 | write("something.rb", "") 104 | write("file.rb", "") 105 | subject.staged_files_all.sort.must_equal ["file.rb", "something.rb"].sort 106 | end 107 | 108 | end # :staged_files_all 109 | 110 | describe :staged_files_git_all do 111 | 112 | it "lists all files" do 113 | write("something.rb", "") 114 | write("file.rb", "") 115 | system("git", "add", "-A") 116 | write("not_git.rb", "") 117 | subject.staged_files_git_all.sort.must_equal ["file.rb", "something.rb"].sort 118 | end 119 | 120 | end # :staged_files_git_all 121 | 122 | describe :set_staged_files do 123 | 124 | it "sets staged files from list" do 125 | subject.staged_files.must_equal([]) 126 | subject.set_staged_files("some_file", "another_file") 127 | subject.staged_files.sort.must_equal(["some_file", "another_file"].sort) 128 | end 129 | 130 | it "sets staged files - all" do 131 | subject.staged_files.must_equal([]) 132 | write("something.rb", "") 133 | write("file.rb", "") 134 | subject.set_staged_files(:all) 135 | subject.staged_files.sort.must_equal(["file.rb", "something.rb"].sort) 136 | end 137 | 138 | it "sets staged files - git" do 139 | subject.staged_files.must_equal([]) 140 | write("something.rb", "") 141 | write("file.rb", "") 142 | system("git", "add", "-A") 143 | subject.staged_files = nil 144 | subject.set_staged_files() 145 | write("not_git.rb", "") 146 | subject.staged_files.sort.must_equal(["file.rb", "something.rb"].sort) 147 | end 148 | 149 | it "sets staged files - git all" do 150 | subject.staged_files.must_equal([]) 151 | write("something.rb", "") 152 | write("file.rb", "") 153 | system("git", "add", "-A") 154 | subject.set_staged_files(:git) 155 | write("not_git.rb", "") 156 | subject.staged_files.sort.must_equal(["file.rb", "something.rb"].sort) 157 | end 158 | 159 | end 160 | 161 | end 162 | --------------------------------------------------------------------------------