├── .editorconfig ├── .git-hooks └── pre_commit │ └── master_hooks_match.rb ├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── .overcommit.yml ├── .rubocop.yml ├── .rubocop_todo.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── MIT-LICENSE ├── README.md ├── bin └── overcommit ├── config ├── default.yml └── starter.yml ├── lib ├── overcommit.rb └── overcommit │ ├── cli.rb │ ├── command_splitter.rb │ ├── configuration.rb │ ├── configuration_loader.rb │ ├── configuration_validator.rb │ ├── constants.rb │ ├── exceptions.rb │ ├── git_config.rb │ ├── git_repo.rb │ ├── git_version.rb │ ├── hook │ ├── base.rb │ ├── commit_msg │ │ ├── base.rb │ │ ├── capitalized_subject.rb │ │ ├── empty_message.rb │ │ ├── gerrit_change_id.rb │ │ ├── hard_tabs.rb │ │ ├── message_format.rb │ │ ├── russian_novel.rb │ │ ├── single_line_subject.rb │ │ ├── spell_check.rb │ │ ├── text_width.rb │ │ └── trailing_period.rb │ ├── post_checkout │ │ ├── base.rb │ │ ├── bower_install.rb │ │ ├── bundle_install.rb │ │ ├── composer_install.rb │ │ ├── index_tags.rb │ │ ├── npm_install.rb │ │ ├── submodule_status.rb │ │ └── yarn_install.rb │ ├── post_commit │ │ ├── base.rb │ │ ├── bower_install.rb │ │ ├── bundle_install.rb │ │ ├── commitplease.rb │ │ ├── composer_install.rb │ │ ├── git_guilt.rb │ │ ├── index_tags.rb │ │ ├── npm_install.rb │ │ ├── submodule_status.rb │ │ └── yarn_install.rb │ ├── post_merge │ │ ├── base.rb │ │ ├── bower_install.rb │ │ ├── bundle_install.rb │ │ ├── composer_install.rb │ │ ├── index_tags.rb │ │ ├── npm_install.rb │ │ ├── submodule_status.rb │ │ └── yarn_install.rb │ ├── post_rewrite │ │ ├── base.rb │ │ ├── bower_install.rb │ │ ├── bundle_install.rb │ │ ├── composer_install.rb │ │ ├── index_tags.rb │ │ ├── npm_install.rb │ │ ├── submodule_status.rb │ │ └── yarn_install.rb │ ├── pre_commit │ │ ├── author_email.rb │ │ ├── author_name.rb │ │ ├── base.rb │ │ ├── berksfile_check.rb │ │ ├── broken_symlinks.rb │ │ ├── bundle_audit.rb │ │ ├── bundle_check.rb │ │ ├── bundle_outdated.rb │ │ ├── case_conflicts.rb │ │ ├── chamber_compare.rb │ │ ├── chamber_security.rb │ │ ├── chamber_verification.rb │ │ ├── code_spell_check.rb │ │ ├── coffee_lint.rb │ │ ├── cook_style.rb │ │ ├── credo.rb │ │ ├── css_lint.rb │ │ ├── dart_analyzer.rb │ │ ├── dogma.rb │ │ ├── erb_lint.rb │ │ ├── es_lint.rb │ │ ├── execute_permissions.rb │ │ ├── fasterer.rb │ │ ├── file_size.rb │ │ ├── fix_me.rb │ │ ├── flay.rb │ │ ├── foodcritic.rb │ │ ├── forbidden_branches.rb │ │ ├── ginkgo_focus.rb │ │ ├── go_fmt.rb │ │ ├── go_lint.rb │ │ ├── go_vet.rb │ │ ├── golangci_lint.rb │ │ ├── hadolint.rb │ │ ├── haml_lint.rb │ │ ├── hard_tabs.rb │ │ ├── hlint.rb │ │ ├── html_hint.rb │ │ ├── html_tidy.rb │ │ ├── image_optim.rb │ │ ├── java_checkstyle.rb │ │ ├── js_hint.rb │ │ ├── js_lint.rb │ │ ├── jscs.rb │ │ ├── jsl.rb │ │ ├── json_syntax.rb │ │ ├── kt_lint.rb │ │ ├── license_finder.rb │ │ ├── license_header.rb │ │ ├── line_endings.rb │ │ ├── local_paths_in_gemfile.rb │ │ ├── mdl.rb │ │ ├── merge_conflicts.rb │ │ ├── mix_format.rb │ │ ├── nginx_test.rb │ │ ├── pep257.rb │ │ ├── pep8.rb │ │ ├── php_cs.rb │ │ ├── php_cs_fixer.rb │ │ ├── php_lint.rb │ │ ├── php_stan.rb │ │ ├── pronto.rb │ │ ├── puppet_lint.rb │ │ ├── puppet_metadata_json_lint.rb │ │ ├── pycodestyle.rb │ │ ├── pydocstyle.rb │ │ ├── pyflakes.rb │ │ ├── pylint.rb │ │ ├── python_flake8.rb │ │ ├── r_spec.rb │ │ ├── rails_best_practices.rb │ │ ├── rails_schema_up_to_date.rb │ │ ├── rake_target.rb │ │ ├── reek.rb │ │ ├── rst_lint.rb │ │ ├── rubo_cop.rb │ │ ├── ruby_lint.rb │ │ ├── ruby_syntax.rb │ │ ├── scalariform.rb │ │ ├── scalastyle.rb │ │ ├── scss_lint.rb │ │ ├── semi_standard.rb │ │ ├── shell_check.rb │ │ ├── slim_lint.rb │ │ ├── sorbet.rb │ │ ├── sqlint.rb │ │ ├── standard.rb │ │ ├── stylelint.rb │ │ ├── swift_lint.rb │ │ ├── terraform_format.rb │ │ ├── trailing_whitespace.rb │ │ ├── travis_lint.rb │ │ ├── ts_lint.rb │ │ ├── vint.rb │ │ ├── w3c_css.rb │ │ ├── w3c_html.rb │ │ ├── xml_lint.rb │ │ ├── xml_syntax.rb │ │ ├── yaml_lint.rb │ │ ├── yaml_syntax.rb │ │ ├── yard_coverage.rb │ │ └── yarn_check.rb │ ├── pre_push │ │ ├── base.rb │ │ ├── brakeman.rb │ │ ├── cargo_test.rb │ │ ├── flutter_test.rb │ │ ├── go_test.rb │ │ ├── golangci_lint.rb │ │ ├── minitest.rb │ │ ├── mix_test.rb │ │ ├── php_unit.rb │ │ ├── pronto.rb │ │ ├── protected_branches.rb │ │ ├── pub_test.rb │ │ ├── pytest.rb │ │ ├── python_nose.rb │ │ ├── r_spec.rb │ │ ├── rake_target.rb │ │ └── test_unit.rb │ ├── pre_rebase │ │ ├── base.rb │ │ └── merged_commits.rb │ ├── prepare_commit_msg │ │ ├── base.rb │ │ └── replace_branch.rb │ └── shared │ │ ├── bower_install.rb │ │ ├── bundle_install.rb │ │ ├── composer_install.rb │ │ ├── index_tags.rb │ │ ├── npm_install.rb │ │ ├── pronto.rb │ │ ├── r_spec.rb │ │ ├── rake_target.rb │ │ ├── submodule_status.rb │ │ └── yarn_install.rb │ ├── hook_context.rb │ ├── hook_context │ ├── base.rb │ ├── commit_msg.rb │ ├── diff.rb │ ├── helpers │ │ ├── file_modifications.rb │ │ └── stash_unstaged_changes.rb │ ├── post_checkout.rb │ ├── post_commit.rb │ ├── post_merge.rb │ ├── post_rewrite.rb │ ├── pre_commit.rb │ ├── pre_push.rb │ ├── pre_rebase.rb │ ├── prepare_commit_msg.rb │ └── run_all.rb │ ├── hook_loader │ ├── base.rb │ ├── built_in_hook_loader.rb │ └── plugin_hook_loader.rb │ ├── hook_runner.rb │ ├── hook_signer.rb │ ├── installer.rb │ ├── interrupt_handler.rb │ ├── logger.rb │ ├── message_processor.rb │ ├── os.rb │ ├── printer.rb │ ├── subprocess.rb │ ├── utils.rb │ ├── utils │ ├── file_utils.rb │ └── messages_utils.rb │ └── version.rb ├── libexec ├── gerrit-change-id └── index-tags ├── logo ├── horizontal.png └── square.png ├── overcommit.gemspec ├── spec ├── integration │ ├── committing_spec.rb │ ├── configuration_signing_spec.rb │ ├── diff_flag_spec.rb │ ├── disable_overcommit_spec.rb │ ├── gemfile_option_spec.rb │ ├── hook_signing_spec.rb │ ├── installing_overcommit_spec.rb │ ├── parallelize_spec.rb │ ├── protected_branches_spec.rb │ ├── resolving_cherry_pick_conflict_spec.rb │ ├── resolving_merge_conflict_spec.rb │ ├── run_flag_spec.rb │ └── template_dir_spec.rb ├── overcommit │ ├── cli_spec.rb │ ├── command_splitter_spec.rb │ ├── configuration_loader_spec.rb │ ├── configuration_spec.rb │ ├── configuration_validator_spec.rb │ ├── default_configuration_spec.rb │ ├── git_config_spec.rb │ ├── git_repo_spec.rb │ ├── hook │ │ ├── base_spec.rb │ │ ├── commit_msg │ │ │ ├── capitalized_subject_spec.rb │ │ │ ├── empty_message_spec.rb │ │ │ ├── gerrit_change_id_spec.rb │ │ │ ├── hard_tabs_spec.rb │ │ │ ├── message_format_spec.rb │ │ │ ├── russian_novel_spec.rb │ │ │ ├── single_line_subject_spec.rb │ │ │ ├── spell_check_spec.rb │ │ │ ├── text_width_spec.rb │ │ │ └── trailing_period_spec.rb │ │ ├── post_checkout │ │ │ ├── base_spec.rb │ │ │ ├── bower_install_spec.rb │ │ │ ├── bundle_install_spec.rb │ │ │ ├── composer_install_spec.rb │ │ │ ├── index_tags_spec.rb │ │ │ ├── npm_install_spec.rb │ │ │ ├── submodule_status_spec.rb │ │ │ └── yarn_install_spec.rb │ │ ├── post_commit │ │ │ ├── bower_install_spec.rb │ │ │ ├── bundle_install_spec.rb │ │ │ ├── commitplease_spec.rb │ │ │ ├── composer_install_spec.rb │ │ │ ├── git_guilt_spec.rb │ │ │ ├── index_tags_spec.rb │ │ │ ├── npm_install_spec.rb │ │ │ ├── submodule_status_spec.rb │ │ │ └── yarn_install_spec.rb │ │ ├── post_merge │ │ │ ├── bower_install_spec.rb │ │ │ ├── bundle_install_spec.rb │ │ │ ├── composer_install_spec.rb │ │ │ ├── index_tags_spec.rb │ │ │ ├── npm_install_spec.rb │ │ │ ├── submodule_status_spec.rb │ │ │ └── yarn_install_spec.rb │ │ ├── post_rewrite │ │ │ ├── bower_install_spec.rb │ │ │ ├── bundle_install_spec.rb │ │ │ ├── composer_install_spec.rb │ │ │ ├── index_tags_spec.rb │ │ │ ├── npm_install_spec.rb │ │ │ ├── submodule_status_spec.rb │ │ │ └── yarn_install_spec.rb │ │ ├── pre_commit │ │ │ ├── author_email_spec.rb │ │ │ ├── author_name_spec.rb │ │ │ ├── berksfile_check_spec.rb │ │ │ ├── broken_symlinks_spec.rb │ │ │ ├── bundle_audit_spec.rb │ │ │ ├── bundle_check_spec.rb │ │ │ ├── bundle_outdated_spec.rb │ │ │ ├── case_conflicts_spec.rb │ │ │ ├── chamber_compare_spec.rb │ │ │ ├── chamber_security_spec.rb │ │ │ ├── chamber_verification_spec.rb │ │ │ ├── code_spell_check_spec.rb │ │ │ ├── coffee_lint_spec.rb │ │ │ ├── cook_style_spec.rb │ │ │ ├── credo_spec.rb │ │ │ ├── css_lint_spec.rb │ │ │ ├── dart_analyzer_spec.rb │ │ │ ├── dogma_spec.rb │ │ │ ├── erb_lint_spec.rb │ │ │ ├── es_lint_spec.rb │ │ │ ├── execute_permissions_spec.rb │ │ │ ├── fasterer_spec.rb │ │ │ ├── file_size_spec.rb │ │ │ ├── fix_me_spec.rb │ │ │ ├── flay_spec.rb │ │ │ ├── foodcritic_spec.rb │ │ │ ├── forbidden_branches_spec.rb │ │ │ ├── go_fmt_spec.rb │ │ │ ├── go_lint_spec.rb │ │ │ ├── go_vet_spec.rb │ │ │ ├── golangci_lint_spec.rb │ │ │ ├── hadolint_spec.rb │ │ │ ├── haml_lint_spec.rb │ │ │ ├── hard_tabs_spec.rb │ │ │ ├── hlint_spec.rb │ │ │ ├── html_hint_spec.rb │ │ │ ├── html_tidy_spec.rb │ │ │ ├── image_optim_spec.rb │ │ │ ├── java_checkstyle_spec.rb │ │ │ ├── js_hint_spec.rb │ │ │ ├── js_lint_spec.rb │ │ │ ├── jscs_spec.rb │ │ │ ├── jsl_spec.rb │ │ │ ├── json_syntax_spec.rb │ │ │ ├── kt_lint_spec.rb │ │ │ ├── license_finder_spec.rb │ │ │ ├── license_header_spec.rb │ │ │ ├── line_endings_spec.rb │ │ │ ├── local_paths_in_gemfile_spec.rb │ │ │ ├── mdl_spec.rb │ │ │ ├── merge_conflicts_spec.rb │ │ │ ├── mix_format_spec.rb │ │ │ ├── nginx_test_spec.rb │ │ │ ├── pep257_spec.rb │ │ │ ├── pep8_spec.rb │ │ │ ├── php_lint_spec.rb │ │ │ ├── php_stan_spec.rb │ │ │ ├── phpcs_fixer_spec.rb │ │ │ ├── phpcs_spec.rb │ │ │ ├── pronto_spec.rb │ │ │ ├── puppet_lint_spec.rb │ │ │ ├── puppet_metadata_json_lint_spec.rb │ │ │ ├── pycodestyle_spec.rb │ │ │ ├── pydocstyle_spec.rb │ │ │ ├── pyflakes_spec.rb │ │ │ ├── pylint_spec.rb │ │ │ ├── python_flake8_spec.rb │ │ │ ├── r_spec_spec.rb │ │ │ ├── rails_best_practices_spec.rb │ │ │ ├── rails_schema_up_to_date_spec.rb │ │ │ ├── rake_target_spec.rb │ │ │ ├── reek_spec.rb │ │ │ ├── rst_lint_spec.rb │ │ │ ├── rubo_cop_spec.rb │ │ │ ├── ruby_lint_spec.rb │ │ │ ├── ruby_syntax_spec.rb │ │ │ ├── scalariform_spec.rb │ │ │ ├── scalastyle_spec.rb │ │ │ ├── scss_lint_spec.rb │ │ │ ├── semi_standard_spec.rb │ │ │ ├── shell_check_spec.rb │ │ │ ├── slim_lint_spec.rb │ │ │ ├── sorbet_spec.rb │ │ │ ├── sqlint_spec.rb │ │ │ ├── standard_spec.rb │ │ │ ├── stylelint_spec.rb │ │ │ ├── swift_lint_spec.rb │ │ │ ├── terraform_format_spec.rb │ │ │ ├── trailing_whitespace_spec.rb │ │ │ ├── travis_lint_spec.rb │ │ │ ├── ts_lint_spec.rb │ │ │ ├── vint_spec.rb │ │ │ ├── w3c_css_spec.rb │ │ │ ├── w3c_html_spec.rb │ │ │ ├── xml_lint_spec.rb │ │ │ ├── xml_syntax_spec.rb │ │ │ ├── yaml_lint_spec.rb │ │ │ ├── yaml_syntax_spec.rb │ │ │ ├── yard_coverage_spec.rb │ │ │ └── yarn_check_spec.rb │ │ ├── pre_push │ │ │ ├── base_spec.rb │ │ │ ├── brakeman_spec.rb │ │ │ ├── cargo_test_spec.rb │ │ │ ├── flutter_test_spec.rb │ │ │ ├── go_test_spec.rb │ │ │ ├── golangci_lint_spec.rb │ │ │ ├── minitest_spec.rb │ │ │ ├── mix_test_spec.rb │ │ │ ├── php_unit_spec.rb │ │ │ ├── pronto_spec.rb │ │ │ ├── protected_branches_spec.rb │ │ │ ├── pub_test_spec.rb │ │ │ ├── pytest_spec.rb │ │ │ ├── python_nose_spec.rb │ │ │ ├── r_spec_spec.rb │ │ │ ├── rake_target_spec.rb │ │ │ └── test_unit_spec.rb │ │ ├── pre_rebase │ │ │ └── merged_commits_spec.rb │ │ └── prepare_commit_msg │ │ │ ├── base_spec.rb │ │ │ └── replace_branch_spec.rb │ ├── hook_context │ │ ├── base_spec.rb │ │ ├── commit_msg_spec.rb │ │ ├── diff_spec.rb │ │ ├── post_checkout_spec.rb │ │ ├── post_commit_spec.rb │ │ ├── post_merge_spec.rb │ │ ├── post_rewrite_spec.rb │ │ ├── pre_commit_spec.rb │ │ ├── pre_push_spec.rb │ │ ├── pre_rebase_spec.rb │ │ ├── prepare_commit_msg_spec.rb │ │ └── run_all_spec.rb │ ├── hook_signer_spec.rb │ ├── installer_spec.rb │ ├── logger_spec.rb │ ├── message_processor_spec.rb │ └── utils_spec.rb ├── spec_helper.rb └── support │ ├── git_spec_helpers.rb │ ├── matchers │ └── hook.rb │ ├── normalize_indent.rb │ ├── output_helpers.rb │ └── shell_helpers.rb └── template-dir └── hooks ├── commit-msg ├── overcommit-hook ├── post-checkout ├── post-commit ├── post-merge ├── post-rewrite ├── pre-commit ├── pre-push ├── pre-rebase └── prepare-commit-msg /.editorconfig: -------------------------------------------------------------------------------- 1 | # Defines the coding style for different editors and IDEs. 2 | # http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.git-hooks/pre_commit/master_hooks_match.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'fileutils' 4 | 5 | module Overcommit::Hook::PreCommit 6 | # Ensures all master hooks have the same content. 7 | # 8 | # This is necessary because we can't use symlinks to link all the hooks in the 9 | # template directory to the master `overcommit-hook` file, since symlinks are 10 | # not supported on Windows. 11 | class MasterHooksMatch < Base 12 | def run 13 | hooks_dir = File.join('template-dir', 'hooks') 14 | master_hook = File.join(hooks_dir, 'overcommit-hook') 15 | Dir.glob(File.join(hooks_dir, '*')).each do |hook_path| 16 | unless FileUtils.compare_file(master_hook, hook_path) 17 | return [ 18 | :fail, 19 | "Template directory hook '#{hook_path}' does not match '#{master_hook}'!\n" \ 20 | "Run `cp #{master_hook} #{hook_path}`" 21 | ] 22 | end 23 | end 24 | 25 | :pass 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | jobs: 9 | overcommit: 10 | timeout-minutes: 10 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: 3.3 20 | bundler-cache: true 21 | 22 | - name: Prepare environment 23 | run: | 24 | git config --global user.email "gh-actions@example.com" 25 | git config --global user.name "GitHub Actions" 26 | bundle exec overcommit --sign 27 | bundle exec overcommit --sign pre-commit 28 | 29 | - name: Run pre-commit checks 30 | run: bundle exec overcommit --run 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | coverage/ 3 | pkg/ 4 | .bundle 5 | .idea 6 | .history/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | gemfile: Gemfile 2 | 3 | PreCommit: 4 | # Disabled since this causes spurious failures on AppVeyor builds 5 | BrokenSymlinks: 6 | enabled: false 7 | 8 | BundleCheck: 9 | enabled: true 10 | 11 | ExecutePermissions: 12 | enabled: true 13 | exclude: 14 | - 'bin/overcommit' 15 | - 'libexec/**/*' 16 | - 'template-dir/hooks/**/*' 17 | 18 | HardTabs: 19 | enabled: true 20 | 21 | MasterHooksMatch: 22 | enabled: true 23 | quiet: true 24 | 25 | RuboCop: 26 | enabled: true 27 | include: 28 | - '**/*.gemspec' 29 | - '**/*.rb' 30 | - '**/Gemfile' 31 | - template-dir/hooks/overcommit-hook 32 | 33 | TrailingWhitespace: 34 | enabled: true 35 | 36 | YamlSyntax: 37 | enabled: true 38 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | 7 | # Development dependencies are listed below 8 | 9 | gem 'rspec', '~> 3.0' 10 | 11 | gem 'simplecov', '~> 0.21.0' 12 | gem 'simplecov-lcov', '~> 0.8.0' 13 | 14 | # Pin RuboCop for CI builds 15 | if RUBY_VERSION < '2.7.0' 16 | gem 'rubocop', '1.50.0' 17 | else 18 | gem 'rubocop', '1.59.0' 19 | end 20 | 21 | gem 'ffi' if Gem.win_platform? 22 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2019 Shane da Silva, Aiden Scandella 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/overcommit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/os' 4 | require 'overcommit/constants' 5 | require 'overcommit/exceptions' 6 | require 'overcommit/utils/file_utils' 7 | require 'overcommit/utils' 8 | require 'overcommit/git_version' 9 | require 'overcommit/configuration_validator' 10 | require 'overcommit/configuration' 11 | require 'overcommit/configuration_loader' 12 | require 'overcommit/hook/base' 13 | require 'overcommit/hook_context/base' 14 | require 'overcommit/hook_context' 15 | require 'overcommit/git_config' 16 | require 'overcommit/git_repo' 17 | require 'overcommit/hook_signer' 18 | require 'overcommit/hook_loader/base' 19 | require 'overcommit/hook_loader/built_in_hook_loader' 20 | require 'overcommit/hook_loader/plugin_hook_loader' 21 | require 'overcommit/interrupt_handler' 22 | require 'overcommit/printer' 23 | require 'overcommit/hook_runner' 24 | require 'overcommit/installer' 25 | require 'overcommit/logger' 26 | require 'overcommit/version' 27 | -------------------------------------------------------------------------------- /lib/overcommit/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Global application constants. 4 | module Overcommit 5 | HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze 6 | CONFIG_FILE_NAME = '.overcommit.yml' 7 | LOCAL_CONFIG_FILE_NAME = '.local-overcommit.yml' 8 | 9 | HOOK_DIRECTORY = File.join(HOME, 'lib', 'overcommit', 'hook').freeze 10 | 11 | REPO_URL = 'https://github.com/sds/overcommit' 12 | BUG_REPORT_URL = "#{REPO_URL}/issues" 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/git_config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/utils' 4 | 5 | module Overcommit 6 | # Get configuration options from git 7 | module GitConfig 8 | module_function 9 | 10 | def comment_character 11 | char = `git config --get core.commentchar`.chomp 12 | char = '#' if char == '' 13 | char 14 | end 15 | 16 | def hooks_path 17 | path = `git config --get core.hooksPath`.chomp 18 | return File.join(Overcommit::Utils.git_dir, 'hooks') if path.empty? 19 | 20 | File.expand_path(path, Dir.pwd) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/git_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Returns the version of the available git binary. 4 | # 5 | # This is intended to be used to conveniently execute code based on a specific 6 | # git version. Simply compare to a version string: 7 | # 8 | # @example 9 | # if GIT_VERSION <= '1.8.5' 10 | # ... 11 | # end 12 | module Overcommit 13 | GIT_VERSION = begin 14 | version = `git --version`.chomp[/\d+(\.\d+)+/, 0] 15 | Overcommit::Utils::Version.new(version) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::CommitMsg 6 | # Functionality common to all commit-msg hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, :empty_message?, :commit_message, 11 | :update_commit_message, :commit_message_lines, 12 | :commit_message_file, :modified_lines_in_file 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/capitalized_subject.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Ensures commit message subject lines start with a capital letter. 5 | class CapitalizedSubject < Base 6 | def run 7 | return :pass if empty_message? 8 | 9 | # Git treats the first non-empty line as the subject 10 | subject = commit_message_lines.find { |line| !line.strip.empty? }.to_s 11 | first_letter = subject.match(/^[[:punct:]]*(.)/)[1] 12 | unless special_prefix?(subject) || first_letter =~ /[[:upper:]]/ 13 | return :warn, 'Subject should start with a capital letter' 14 | end 15 | 16 | :pass 17 | end 18 | 19 | private 20 | 21 | def special_prefix?(subject) 22 | subject =~ /^(fixup|squash)!/ 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/empty_message.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Checks that the commit message is not empty 5 | class EmptyMessage < Base 6 | def run 7 | return :pass unless empty_message? 8 | 9 | [:fail, 'Commit message should not be empty'] 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/gerrit_change_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Ensures a Gerrit Change-Id line is included in the commit message. 5 | # 6 | # It may seem odd to do this here instead of in a prepare-commit-msg hook, but 7 | # the reality is that if you want to _ensure_ the Change-Id is included then 8 | # you need to do it in a commit-msg hook. This is because the user could still 9 | # edit the message after a prepare-commit-msg hook was run. 10 | # 11 | # @see https://code.google.com/p/gerrit/ 12 | class GerritChangeId < Base 13 | SCRIPT_LOCATION = Overcommit::Utils.script_path('gerrit-change-id') 14 | 15 | def run 16 | result = execute(['sh', SCRIPT_LOCATION, commit_message_file]) 17 | return :pass if result.success? 18 | 19 | [:fail, result.stdout] 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/hard_tabs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Checks for hard tabs in commit messages. 5 | class HardTabs < Base 6 | def run 7 | return :pass if empty_message? 8 | 9 | # Catches hard tabs entered by the user (not auto-generated) 10 | if commit_message.index(/\t/) 11 | return :warn, "Don't use hard tabs in commit messages" 12 | end 13 | 14 | :pass 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/message_format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Ensures the commit message follows a specific format. 5 | class MessageFormat < Base 6 | def run 7 | error_msg = validate_pattern(commit_message_lines.join("\n")) 8 | return :fail, error_msg if error_msg 9 | 10 | :pass 11 | end 12 | 13 | private 14 | 15 | def validate_pattern(message) 16 | pattern = config['pattern'] 17 | return if pattern.empty? 18 | 19 | expected_pattern_message = config['expected_pattern_message'] 20 | sample_message = config['sample_message'] 21 | 22 | unless message.match?(/#{pattern}/m) 23 | [ 24 | 'Commit message pattern mismatch.', 25 | "Expected : #{expected_pattern_message}", 26 | "Sample : #{sample_message}" 27 | ].join("\n") 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/russian_novel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Checks for long commit messages (not good or bad--just fun to point out) 5 | class RussianNovel < Base 6 | RUSSIAN_NOVEL_LENGTH = 30 7 | 8 | def run 9 | if commit_message_lines.length >= RUSSIAN_NOVEL_LENGTH 10 | return :warn, 'You seem to have authored a Russian novel; congratulations!' 11 | end 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/single_line_subject.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Ensures commit message subject lines are followed by a blank line. 5 | class SingleLineSubject < Base 6 | def run 7 | return :pass if empty_message? 8 | 9 | unless commit_message_lines[1].to_s.strip.empty? 10 | return :warn, 'Subject should be one line and followed by a blank line' 11 | end 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/commit_msg/trailing_period.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::CommitMsg 4 | # Ensures commit message subject lines do not have a trailing period 5 | class TrailingPeriod < Base 6 | def run 7 | return :pass if empty_message? 8 | 9 | if commit_message_lines.first.rstrip.end_with?('.') 10 | return :warn, 'Please omit trailing period from commit message subject' 11 | end 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Functionality common to all post-checkout hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, 11 | :previous_head, :new_head, :branch_checkout?, :file_checkout? 12 | 13 | def skip_file_checkout? 14 | @config['skip_file_checkout'] != false 15 | end 16 | 17 | def enabled? 18 | return false if file_checkout? && skip_file_checkout? 19 | 20 | super 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/bower_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bower_install' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Runs `bower install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BowerInstall 10 | class BowerInstall < Base 11 | include Overcommit::Hook::Shared::BowerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/bundle_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bundle_install' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Runs `bundle install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BundleInstall 10 | class BundleInstall < Base 11 | include Overcommit::Hook::Shared::BundleInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/composer_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/composer_install' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Runs `composer install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::ComposerInstall 10 | class ComposerInstall < Base 11 | include Overcommit::Hook::Shared::ComposerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/index_tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/index_tags' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Updates ctags index for all source code in the repository. 7 | # 8 | # @see Overcommit::Hook::Shared::IndexTags 9 | class IndexTags < Base 10 | include Overcommit::Hook::Shared::IndexTags 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/npm_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/npm_install' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Runs `npm install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::NpmInstall 10 | class NpmInstall < Base 11 | include Overcommit::Hook::Shared::NpmInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/submodule_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/submodule_status' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Checks the status of submodules in the current repository and 7 | # notifies the user if any are uninitialized, out of date with 8 | # the current index, or contain merge conflicts. 9 | class SubmoduleStatus < Base 10 | include Overcommit::Hook::Shared::SubmoduleStatus 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_checkout/yarn_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/yarn_install' 4 | 5 | module Overcommit::Hook::PostCheckout 6 | # Runs `yarn install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::YarnInstall 10 | class YarnInstall < Base 11 | include Overcommit::Hook::Shared::YarnInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Functionality common to all post-commit hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, :modified_lines_in_file, :initial_commit? 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/bower_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bower_install' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Runs `bower install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BowerInstall 10 | class BowerInstall < Base 11 | include Overcommit::Hook::Shared::BowerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/bundle_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bundle_install' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Runs `bundle install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BundleInstall 10 | class BundleInstall < Base 11 | include Overcommit::Hook::Shared::BundleInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/commitplease.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PostCommit 4 | # Check that a commit message conforms to a certain style 5 | # 6 | # @see https://www.npmjs.com/package/commitplease 7 | class Commitplease < Base 8 | def run 9 | result = execute(command) 10 | output = result.stderr 11 | return :pass if result.success? && output.empty? 12 | 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/composer_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/composer_install' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Runs `composer install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::ComposerInstall 10 | class ComposerInstall < Base 11 | include Overcommit::Hook::Shared::ComposerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/index_tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/index_tags' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Updates ctags index for all source code in the repository. 7 | # 8 | # @see Overcommit::Hook::Shared::IndexTags 9 | class IndexTags < Base 10 | include Overcommit::Hook::Shared::IndexTags 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/npm_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/npm_install' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Runs `npm install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::NpmInstall 10 | class NpmInstall < Base 11 | include Overcommit::Hook::Shared::NpmInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/submodule_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/submodule_status' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Checks the status of submodules in the current repository and 7 | # notifies the user if any are uninitialized, out of date with 8 | # the current index, or contain merge conflicts. 9 | class SubmoduleStatus < Base 10 | include Overcommit::Hook::Shared::SubmoduleStatus 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_commit/yarn_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/yarn_install' 4 | 5 | module Overcommit::Hook::PostCommit 6 | # Runs `yarn install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::YarnInstall 10 | class YarnInstall < Base 11 | include Overcommit::Hook::Shared::YarnInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Functionality common to all post-merge hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, :modified_lines_in_file, :squash?, :merge_commit? 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/bower_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bower_install' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Runs `bower install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BowerInstall 10 | class BowerInstall < Base 11 | include Overcommit::Hook::Shared::BowerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/bundle_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bundle_install' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Runs `bundle install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BundleInstall 10 | class BundleInstall < Base 11 | include Overcommit::Hook::Shared::BundleInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/composer_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/composer_install' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Runs `composer install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::ComposerInstall 10 | class ComposerInstall < Base 11 | include Overcommit::Hook::Shared::ComposerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/index_tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/index_tags' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Updates ctags index for all source code in the repository. 7 | # 8 | # @see Overcommit::Hook::Shared::IndexTags 9 | class IndexTags < Base 10 | include Overcommit::Hook::Shared::IndexTags 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/npm_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/npm_install' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Runs `npm install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::NpmInstall 10 | class NpmInstall < Base 11 | include Overcommit::Hook::Shared::NpmInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/submodule_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/submodule_status' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Checks the status of submodules in the current repository and 7 | # notifies the user if any are uninitialized, out of date with 8 | # the current index, or contain merge conflicts. 9 | class SubmoduleStatus < Base 10 | include Overcommit::Hook::Shared::SubmoduleStatus 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_merge/yarn_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/yarn_install' 4 | 5 | module Overcommit::Hook::PostMerge 6 | # Runs `yarn install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::YarnInstall 10 | class YarnInstall < Base 11 | include Overcommit::Hook::Shared::YarnInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Functionality common to all post-rewrite hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, :amend?, :rebase?, :rewritten_commits 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/bower_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bower_install' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Runs `bower install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BowerInstall 10 | class BowerInstall < Base 11 | include Overcommit::Hook::Shared::BowerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/bundle_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/bundle_install' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Runs `bundle install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::BundleInstall 10 | class BundleInstall < Base 11 | include Overcommit::Hook::Shared::BundleInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/composer_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/composer_install' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Runs `composer install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::ComposerInstall 10 | class ComposerInstall < Base 11 | include Overcommit::Hook::Shared::ComposerInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/index_tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/index_tags' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Updates ctags index for all source code in the repository. 7 | # 8 | # @see Overcommit::Hook::Shared::IndexTags 9 | class IndexTags < Base 10 | include Overcommit::Hook::Shared::IndexTags 11 | 12 | def run 13 | # Ignore unless this is a rebase (amends are covered by post-commit hook) 14 | return :pass unless rebase? 15 | 16 | super 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/npm_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/npm_install' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Runs `npm install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::NpmInstall 10 | class NpmInstall < Base 11 | include Overcommit::Hook::Shared::NpmInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/submodule_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/submodule_status' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Checks the status of submodules in the current repository and 7 | # notifies the user if any are uninitialized, out of date with 8 | # the current index, or contain merge conflicts. 9 | class SubmoduleStatus < Base 10 | include Overcommit::Hook::Shared::SubmoduleStatus 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/post_rewrite/yarn_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/yarn_install' 4 | 5 | module Overcommit::Hook::PostRewrite 6 | # Runs `yarn install` when a change is detected in the repository's 7 | # dependencies. 8 | # 9 | # @see Overcommit::Hook::Shared::YarnInstall 10 | class YarnInstall < Base 11 | include Overcommit::Hook::Shared::YarnInstall 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/author_email.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks the format of an author's email address. 5 | class AuthorEmail < Base 6 | def run 7 | email = 8 | if ENV.key?('GIT_AUTHOR_EMAIL') 9 | ENV['GIT_AUTHOR_EMAIL'] 10 | else 11 | result = execute(%w[git config --get user.email]) 12 | result.stdout.chomp 13 | end 14 | 15 | unless email.match?(/#{config['pattern']}/) 16 | return :fail, 17 | "Author has an invalid email address: '#{email}'\n" \ 18 | 'Set your email with ' \ 19 | '`git config --global user.email your_email@example.com` ' \ 20 | 'or via the GIT_AUTHOR_EMAIL environment variable' 21 | end 22 | 23 | :pass 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/author_name.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Ensures that a commit author has a name with at least first and last names. 5 | class AuthorName < Base 6 | def run 7 | name = 8 | if ENV.key?('GIT_AUTHOR_NAME') 9 | ENV['GIT_AUTHOR_NAME'] 10 | else 11 | result = execute(%w[git config --get user.name]) 12 | result.stdout.chomp 13 | end 14 | 15 | if name.empty? 16 | return :fail, 17 | "Author name must be non-0 in length.\n" \ 18 | 'Set your name with `git config --global user.name "Your Name"` ' \ 19 | 'or via the GIT_AUTHOR_NAME environment variable' 20 | end 21 | 22 | :pass 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | require 'overcommit/utils/messages_utils' 5 | 6 | module Overcommit::Hook::PreCommit 7 | # Functionality common to all pre-commit hooks. 8 | class Base < Overcommit::Hook::Base 9 | extend Forwardable 10 | 11 | def_delegators :@context, :modified_lines_in_file, :amendment?, :initial_commit? 12 | 13 | private 14 | 15 | def extract_messages(*args) 16 | Overcommit::Utils::MessagesUtils.extract_messages(*args) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/berksfile_check.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Check if local Berksfile.lock matches Berksfile when either changes, unless 5 | # Berksfile.lock is ignored by git. 6 | # 7 | # @see http://berkshelf.com/ 8 | class BerksfileCheck < Base 9 | LOCK_FILE = 'Berksfile.lock' 10 | 11 | def run 12 | # Ignore if Berksfile.lock is not tracked by git 13 | ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split("\n") 14 | return :pass if ignored_files.include?(LOCK_FILE) 15 | 16 | result = execute(command) 17 | unless result.success? 18 | return :fail, result.stderr 19 | end 20 | 21 | :pass 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/broken_symlinks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for broken symlinks. 5 | class BrokenSymlinks < Base 6 | def run 7 | broken_symlinks = applicable_files. 8 | select { |file| Overcommit::Utils.broken_symlink?(file) } 9 | 10 | if broken_symlinks.any? 11 | return :fail, "Broken symlinks detected:\n#{broken_symlinks.join("\n")}" 12 | end 13 | 14 | :pass 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/bundle_audit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for vulnerable versions of gems in Gemfile.lock. 5 | # 6 | # @see https://github.com/rubysec/bundler-audit 7 | class BundleAudit < Base 8 | LOCK_FILE = 'Gemfile.lock' 9 | 10 | def run 11 | # Ignore if Gemfile.lock is not tracked by git 12 | ignored_files = execute(%W[git ls-files -o -i --exclude-standard -- #{LOCK_FILE}]). 13 | stdout.split("\n") 14 | return :pass if ignored_files.include?(LOCK_FILE) 15 | 16 | result = execute(command) 17 | if result.success? 18 | :pass 19 | else 20 | [:warn, result.stdout] 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/bundle_check.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Check if local Gemfile.lock matches Gemfile when either changes, unless 5 | # Gemfile.lock is ignored by git. 6 | # 7 | # @see http://bundler.io/ 8 | class BundleCheck < Base 9 | LOCK_FILE = File.basename(ENV['BUNDLE_GEMFILE'] || 'Gemfile') + '.lock' 10 | 11 | def run 12 | # Ignore if Gemfile.lock is not tracked by git 13 | ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split("\n") 14 | return :pass if ignored_files.include?(LOCK_FILE) 15 | 16 | previous_lockfile = File.read(LOCK_FILE) if File.exist?(LOCK_FILE) 17 | 18 | result = execute(command) 19 | unless result.success? 20 | return :fail, result.stdout 21 | end 22 | 23 | new_lockfile = File.read(LOCK_FILE) if File.exist?(LOCK_FILE) 24 | if previous_lockfile != new_lockfile 25 | return :fail, "#{LOCK_FILE} is not up-to-date -- run \ 26 | `#{command.join(' ')}` or add the Gemfile and/or Gemfile.lock".squeeze 27 | end 28 | 29 | :pass 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/bundle_outdated.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Check if any gems in Gemfile.lock have newer versions, unless the 5 | # Gemfile.lock is ignored by Git. 6 | # 7 | # @see http://bundler.io/bundle_outdated.html 8 | class BundleOutdated < Base 9 | LOCK_FILE = 'Gemfile.lock' 10 | 11 | def run 12 | # Ignore if Gemfile.lock is not tracked by git 13 | ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split("\n") 14 | return :pass if ignored_files.include?(LOCK_FILE) 15 | 16 | result = execute(command) 17 | warn_msgs = result.stdout.split("\n"). 18 | reject { |str| str.strip.empty? }. 19 | reject { |str| (str.strip =~ /^(\[|\()?warning|deprecation/i) } 20 | warnings = warn_msgs.map { |msg| Overcommit::Hook::Message.new(:warning, nil, nil, msg) } 21 | 22 | warnings.empty? ? :pass : warnings 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/case_conflicts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for files that would conflict in case-insensitive filesystems 5 | # Adapted from https://github.com/pre-commit/pre-commit-hooks 6 | class CaseConflicts < Base 7 | def run 8 | repo_files = Set.new(applicable_files) 9 | 10 | unless Overcommit::GitRepo.initial_commit? 11 | paths = repo_files.map { |file| File.dirname(file) + File::SEPARATOR }.uniq 12 | repo_files += Overcommit::GitRepo.list_files(paths) 13 | end 14 | 15 | conflict_hash = repo_files.classify(&:downcase). 16 | select { |_, files| files.size > 1 } 17 | conflict_files = applicable_files. 18 | select { |file| conflict_hash.include?(file.downcase) } 19 | 20 | conflict_files.map do |file| 21 | conflicts = conflict_hash[file.downcase].map { |f| File.basename(f) } 22 | msg = "Conflict detected for case-insensitive file systems: #{conflicts.join(', ')}" 23 | Overcommit::Hook::Message.new(:error, file, nil, msg) 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/chamber_security.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `chamber secure` against any modified Chamber settings files. 5 | # 6 | # @see https://github.com/thekompanee/chamber 7 | class ChamberSecurity < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | 11 | return :pass if result.stdout.empty? 12 | 13 | [:fail, "These settings appear to need to be secured but were not: #{result.stdout}"] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/code_spell_check.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `alfonsox` spell-checking tool against any modified code file. 5 | # 6 | # @see https://github.com/diegojromerolopez/alfonsox 7 | class CodeSpellCheck < Base 8 | def run 9 | # Create default file config if it does not exist 10 | 11 | # Run spell-check 12 | result = execute(command, args: applicable_files) 13 | return :pass if result.success? 14 | 15 | spellchecking_errors = result.stderr.split("\n") 16 | spellchecking_errors.pop 17 | 18 | error_messages(spellchecking_errors) 19 | end 20 | 21 | private 22 | 23 | # Create the error messages 24 | def error_messages(spellchecking_errors) 25 | messages = [] 26 | spellchecking_errors.each do |spellchecking_error_i| 27 | error_location, word = spellchecking_error_i.split(' ') 28 | error_file_path, line = error_location.split(':') 29 | messages << Overcommit::Hook::Message.new( 30 | :error, error_file_path, line, "#{error_location}: #{word}" 31 | ) 32 | end 33 | messages 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/coffee_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `coffeelint` against any modified CoffeeScript files. 5 | # 6 | # @see http://www.coffeelint.org/ 7 | class CoffeeLint < Base 8 | MESSAGE_REGEX = / 9 | ^(?.+) 10 | ,(?\d*),\d* 11 | ,(?\w+) 12 | ,(?.+)$ 13 | /x.freeze 14 | 15 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 16 | type.include?('w') ? :warning : :error 17 | end 18 | 19 | def run 20 | result = execute(command, args: applicable_files) 21 | parse_messages(result.stdout) 22 | end 23 | 24 | private 25 | 26 | def parse_messages(output) 27 | output.scan(MESSAGE_REGEX).map do |file, line, type, msg| 28 | line = line.to_i 29 | type = MESSAGE_TYPE_CATEGORIZER.call(type) 30 | text = "#{file}:#{line}:#{type} #{msg}" 31 | Overcommit::Hook::Message.new(type, file, line, text) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/cook_style.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `cookstyle` against any modified Chef Ruby files. 5 | # 6 | # @see https://docs.chef.io/cookstyle.html 7 | class CookStyle < Base 8 | GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.match?(/^warn/) ? :warning : :error 10 | end 11 | 12 | COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type| 13 | type.include?('W') ? :warning : :error 14 | end 15 | 16 | def run 17 | result = execute(command, args: applicable_files) 18 | return :pass if result.success? 19 | 20 | generic_messages = extract_messages( 21 | result.stderr.split("\n"), 22 | /^(?[a-z]+)/i, 23 | GENERIC_MESSAGE_TYPE_CATEGORIZER, 24 | ) 25 | 26 | cop_messages = extract_messages( 27 | result.stdout.split("\n"), 28 | /^(?(?:\w:)?[^:]+):(?\d+):[^ ]+ (?[^ ]+)/, 29 | COP_MESSAGE_TYPE_CATEGORIZER, 30 | ) 31 | 32 | generic_messages + cop_messages 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/credo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `credo` against any modified ex files. 5 | # 6 | # @see https://github.com/rrrene/credo 7 | class Credo < Base 8 | # example message: 9 | # lib/file1.ex:1:11: R: Modules should have a @moduledoc tag. 10 | # lib/file2.ex:12:81: R: Line is too long (max is 80, was 81). 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | result.stdout.split("\n").map(&:strip).reject(&:empty?). 17 | map { |error| message(error) } 18 | end 19 | 20 | private 21 | 22 | def message(error) 23 | file, line = error.split(':') 24 | Overcommit::Hook::Message.new(:error, file, Integer(line), error) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/css_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `csslint` against any modified CSS files. 5 | # 6 | # @see https://github.com/CSSLint/csslint 7 | class CssLint < Base 8 | MESSAGE_REGEX = / 9 | ^(?(?:\w:)?[^:]+):\s 10 | (?:line\s(?\d+)[^EW]+)? 11 | (?Error|Warning) 12 | /x.freeze 13 | 14 | def run 15 | result = execute(command, args: applicable_files) 16 | output = result.stdout.chomp 17 | return :pass if result.success? && output.empty? 18 | 19 | extract_messages( 20 | output.split("\n").reject(&:empty?), 21 | MESSAGE_REGEX, 22 | lambda { |type| type.downcase.to_sym } 23 | ) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/dart_analyzer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `dartanalyzer` against modified Dart files. 5 | # @see https://dart.dev/tools/dartanalyzer 6 | class DartAnalyzer < Base 7 | MESSAGE_REGEX = /(?.*)•\ (?[^•]+)•\ (?[^:]+):(?\d+):(\d+)\.*/.freeze 8 | 9 | def run 10 | result = execute(command, args: applicable_files) 11 | return :pass if result.success? 12 | 13 | extract_messages( 14 | result.stdout.split("\n").grep(MESSAGE_REGEX), 15 | MESSAGE_REGEX, 16 | lambda do |type| 17 | type.include?('error') ? :error : :warning 18 | end 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/dogma.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `dogma` against any modified ex files. 5 | # 6 | # @see https://github.com/lpil/dogma 7 | class Dogma < Base 8 | def run 9 | result = execute command 10 | return :pass if result.success? 11 | 12 | messages = [] 13 | # example message: 14 | # == web/channels/user_socket.ex == 15 | # 26: LineLength: Line length should not exceed 80 chars (was 83). 16 | # 1: ModuleDoc: Module Sample.UserSocket is missing a @moduledoc. 17 | output = result.stdout.chomp.match(/(==.+)/m) 18 | 19 | if output 20 | output.captures.first.split(/\n\n/).each do |error_group| 21 | errors = error_group.split /\n/ 22 | file = errors.shift.gsub /[ =]/, '' 23 | errors.each do |error| 24 | line = error.split(': ').first 25 | messages << Overcommit::Hook::Message.new(:error, file, line, "#{file}: #{error}") 26 | end 27 | end 28 | end 29 | 30 | messages 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/erb_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `erblint` against any modified ERB files. 5 | # 6 | # @see https://github.com/Shopify/erb-lint 7 | class ErbLint < Base 8 | MESSAGE_REGEX = /(?.+)\nIn file: (?.+):(?\d+)/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | return :pass if result.success? 13 | 14 | extract_messages( 15 | result.stdout.split("\n\n")[1..], 16 | MESSAGE_REGEX 17 | ) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/fasterer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `fasterer` against any modified Ruby files. 5 | # 6 | # @see https://github.com/DamirSvrtan/fasterer 7 | class Fasterer < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | output = result.stdout 11 | 12 | if extract_offense_num(output) == 0 13 | :pass 14 | else 15 | [:warn, output] 16 | end 17 | end 18 | 19 | private 20 | 21 | def extract_offense_num(raw_output) 22 | raw_output.scan(/(\d+) offense detected/).flatten.map(&:to_i).inject(0, :+) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/file_size.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for oversized files before committing. 5 | class FileSize < Base 6 | def run 7 | return :pass if oversized_files.empty? 8 | 9 | oversized_files.map do |file| 10 | error_message_for(file) 11 | end 12 | end 13 | 14 | def description 15 | "Check for files over #{size_limit_bytes} bytes" 16 | end 17 | 18 | private 19 | 20 | def oversized_files 21 | @oversized_files ||= build_oversized_file_list 22 | end 23 | 24 | def build_oversized_file_list 25 | applicable_files.select do |file| 26 | File.exist?(file) && file_size(file) > size_limit_bytes 27 | end 28 | end 29 | 30 | def size_limit_bytes 31 | config.fetch('size_limit_bytes') 32 | end 33 | 34 | def error_message_for(file) 35 | Overcommit::Hook::Message.new( 36 | :error, 37 | file, 38 | nil, 39 | "#{file} is #{file_size(file)} bytes" 40 | ) 41 | end 42 | 43 | def file_size(file) 44 | File.size(file) 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/fix_me.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Check for "token" strings 5 | class FixMe < Base 6 | def run 7 | keywords = config['keywords'] 8 | result = execute(command, args: [keywords.join('|')] + applicable_files) 9 | 10 | extract_messages( 11 | result.stdout.split("\n"), 12 | /^(?(?:\w:)?[^:]+):(?\d+)/, 13 | lambda { |_type| :warning } 14 | ) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/flay.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `flay` against any modified files. 5 | # 6 | # @see https://github.com/seattlerb/flay 7 | class Flay < Base 8 | # Flay prints two kinds of messages: 9 | # 10 | # 1) IDENTICAL code found in :defn (mass*2 = MASS) 11 | # file_path_1.rb:LINE_1 12 | # file_path_2.rb:LINE_2 13 | # 14 | # 2) Similar code found in :defn (mass = MASS) 15 | # file_path_1.rb:LINE_1 16 | # file_path_2.rb:LINE_2 17 | # 18 | 19 | def run 20 | command = ['flay', '--mass', @config['mass_threshold'].to_s, '--fuzzy', @config['fuzzy'].to_s] 21 | # Use a more liberal detection method 22 | command += ['--liberal'] if @config['liberal'] 23 | messages = [] 24 | # Run the command for each file 25 | applicable_files.each do |file| 26 | result = execute(command, args: [file]) 27 | results = result.stdout.split("\n\n") 28 | results.shift 29 | unless results.empty? 30 | error_message = results.join("\n").gsub(/^\d+\)\s*/, '') 31 | message = Overcommit::Hook::Message.new(:error, nil, nil, error_message) 32 | messages << message 33 | end 34 | end 35 | messages 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/forbidden_branches.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Prevents commits to branches matching one of the configured patterns. 5 | class ForbiddenBranches < Base 6 | def run 7 | return :pass unless forbidden_commit? 8 | 9 | [:fail, "Committing to #{current_branch} is forbidden"] 10 | end 11 | 12 | private 13 | 14 | def forbidden_commit? 15 | forbidden_branch_patterns.any? { |p| File.fnmatch(p, current_branch) } 16 | end 17 | 18 | def forbidden_branch_patterns 19 | @forbidden_branch_patterns ||= Array(config['branch_patterns']) 20 | end 21 | 22 | def current_branch 23 | @current_branch ||= Overcommit::GitRepo.current_branch 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/ginkgo_focus.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Check for "focused" tests 5 | class GinkgoFocus < Base 6 | def run 7 | keywords = config['keywords'] 8 | result = execute(command, args: [keywords.join('|')] + applicable_files) 9 | 10 | extract_messages( 11 | result.stdout.split("\n"), 12 | /^(?(?:\w:)?[^:]+):(?\d+)/, 13 | lambda { |_type| :warning } 14 | ) 15 | end 16 | 17 | def applicable_test_files 18 | applicable_files.select do |f| 19 | f if f =~ /_test\.go/ 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/go_fmt.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs go fmt for all modified Go files 5 | class GoFmt < Base 6 | def run 7 | errors = [] 8 | applicable_files.each do |file| 9 | result = execute(command, args: [file]) 10 | errors << (result.stdout + result.stderr) unless result.success? 11 | end 12 | return :pass if errors.empty? 13 | 14 | [:fail, errors.join("\n")] 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/go_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `golint` against any modified Golang files. 5 | # 6 | # @see https://github.com/golang/lint 7 | class GoLint < Base 8 | def run 9 | output = '' 10 | 11 | # golint doesn't accept multiple file arguments if 12 | # they belong to different packages 13 | applicable_files.each do |gofile| 14 | result = execute(command, args: Array(gofile)) 15 | output += result.stdout + result.stderr 16 | end 17 | 18 | # Unfortunately the exit code is always 0 19 | return :pass if output.empty? 20 | 21 | # example message: 22 | # path/to/file.go:1:1: Error message 23 | extract_messages( 24 | output.split("\n"), 25 | /^(?(?:\w:)?[^:]+):(?\d+)/ 26 | ) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/go_vet.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `go vet` against any modified Golang files. 5 | # 6 | # @see https://godoc.org/code.google.com/p/go-zh.tools/cmd/vet 7 | class GoVet < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | if result.stderr.match?(/no such tool "vet"/) 13 | return :fail, "`go tool vet` is not installed#{install_command_prompt}" 14 | end 15 | 16 | # example message: 17 | # path/to/file.go:7: Error message 18 | extract_messages( 19 | result.stderr.split("\n"), 20 | /^(?(?:\w:)?[^:]+):(?\d+)/ 21 | ) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/golangci_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `golangci-lint run` against any modified packages 5 | # 6 | # @see https://github.com/golangci/golangci-lint 7 | class GolangciLint < Base 8 | def run 9 | packages = applicable_files.map { |f| File.dirname(f) }.uniq 10 | result = execute(command, args: packages) 11 | return :pass if result.success? 12 | return [:fail, result.stderr] unless result.stderr.empty? 13 | 14 | extract_messages( 15 | result.stdout.split("\n"), 16 | /^(?(?:\w:)?[^:]+):(?\d+)/, 17 | nil 18 | ) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/hadolint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `hadolint` against any modified Dockefile files. 5 | # 6 | # @see http://hadolint.lukasmartinelli.ch/ 7 | class Hadolint < Base 8 | def run 9 | output = '' 10 | success = true 11 | 12 | # hadolint doesn't accept multiple arguments 13 | applicable_files.each do |dockerfile| 14 | result = execute(command, args: Array(dockerfile)) 15 | output += result.stdout 16 | success &&= result.success? 17 | end 18 | 19 | return :pass if success 20 | 21 | extract_messages( 22 | output.split("\n"), 23 | /^(?[^:]+):(?\d+)/, 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/haml_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `haml-lint` against any modified HAML files. 5 | # 6 | # @see https://github.com/brigade/haml-lint/ 7 | class HamlLint < Base 8 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.include?('W') ? :warning : :error 10 | end 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | extract_messages( 17 | result.stdout.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+)/, 19 | MESSAGE_TYPE_CATEGORIZER, 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/hard_tabs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for hard tabs in files. 5 | class HardTabs < Base 6 | def run 7 | result = execute(command, args: applicable_files) 8 | 9 | extract_messages( 10 | result.stdout.split("\n"), 11 | /^(?(?:\w:)?[^:]+):(?\d+)/, 12 | ) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/hlint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `hlint` against any modified Haskell files. 5 | # 6 | # @see https://github.com/ndmitchell/hlint 7 | class Hlint < Base 8 | MESSAGE_REGEX = / 9 | ^(?(?:\w:)?[^:]+) 10 | :(?\d+) 11 | :\d+ 12 | :\s*(?\w+) 13 | /x.freeze 14 | 15 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 16 | type.include?('W') ? :warning : :error 17 | end 18 | 19 | def run 20 | result = execute(command, args: applicable_files) 21 | return :pass if result.success? 22 | 23 | raw_messages = result.stdout.split("\n").grep(MESSAGE_REGEX) 24 | 25 | # example message: 26 | # path/to/file.hs:1:0: Error: message 27 | extract_messages( 28 | raw_messages, 29 | MESSAGE_REGEX, 30 | MESSAGE_TYPE_CATEGORIZER 31 | ) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/html_hint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `htmlhint` against any modified HTML files. 5 | # 6 | # @see http://htmlhint.com/ 7 | class HtmlHint < Base 8 | def run 9 | result = execute(command + applicable_files) 10 | output = Overcommit::Utils.strip_color_codes(result.stdout.chomp) 11 | 12 | message_groups = output.split("\n\n")[0..-2] 13 | message_groups.map do |group| 14 | lines = group.split("\n").map(&:strip) 15 | file = lines[0][/(.+):/, 1] 16 | extract_messages( 17 | lines[1..].map { |msg| "#{file}: #{msg}" }, 18 | /^(?(?:\w:)?[^:]+): line (?\d+)/ 19 | ) 20 | end.flatten 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/html_tidy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `tidy` against any modified HTML files. 5 | # 6 | # @see http://www.html-tidy.org/ 7 | class HtmlTidy < Base 8 | MESSAGE_REGEX = / 9 | ^(?(?:\w:)?[^:]+):\s 10 | line\s(?\d+)\s 11 | column\s(?\d+)\s-\s 12 | (?Error|Warning):\s(?.+)$ 13 | /x.freeze 14 | 15 | def run 16 | # example message: 17 | # line 4 column 24 - Warning: proprietary attribute "class" 18 | applicable_files.collect do |file| 19 | result = execute(command + [file]) 20 | output = result.stderr.chomp 21 | 22 | extract_messages( 23 | output.split("\n").collect { |msg| "#{file}: #{msg}" }, 24 | MESSAGE_REGEX, 25 | lambda { |type| type.downcase.to_sym } 26 | ) 27 | end.flatten 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/image_optim.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for images that can be optimized with `image_optim`. 5 | # 6 | # @see https://github.com/toy/image_optim 7 | class ImageOptim < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return [:fail, result.stdout + result.stderr] unless result.success? 11 | 12 | optimized_files = extract_optimized_files(result.stdout) 13 | return :pass if optimized_files.empty? 14 | 15 | output = "The following images are optimizable:\n#{optimized_files.join("\n")}" 16 | output += "\n\nOptimize them by running `#{command.join(' ')} #{optimized_files.join(' ')}`" 17 | [:fail, output] 18 | end 19 | 20 | private 21 | 22 | def extract_optimized_files(output) 23 | output.split("\n"). 24 | select { |line| line =~ /^\d+/ }. 25 | map { |line| line.split(/\s+/).last } 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/java_checkstyle.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `checkstyle` against any modified Java files. 5 | # 6 | # @see http://checkstyle.sourceforge.net/ 7 | class JavaCheckstyle < Base 8 | MESSAGE_REGEX = /^(\[(?[^\]]+)\]\s+)?(?(?:\w:)?[^:]+):(?\d+)/.freeze 9 | 10 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 11 | %w[WARN INFO].include?(type.to_s) ? :warning : :error 12 | end 13 | 14 | def run 15 | result = execute(command, args: applicable_files) 16 | output = result.stdout.chomp 17 | 18 | # example message: 19 | # path/to/file.java:3:5: Error message 20 | extract_messages( 21 | output.split("\n").grep(MESSAGE_REGEX), 22 | MESSAGE_REGEX, 23 | MESSAGE_TYPE_CATEGORIZER 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/js_hint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `jshint` against any modified JavaScript files. 5 | # 6 | # @see http://jshint.com/ 7 | class JsHint < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | output = result.stdout.chomp 11 | 12 | return :pass if result.success? && output.empty? 13 | 14 | # example message: 15 | # path/to/file.js: line 1, col 0, Error message (E001) 16 | extract_messages( 17 | output.split("\n").grep(/E|W/), 18 | /^(?(?:\w:)?[^:]+):[^\d]+(?\d+).+\((?E|W)\d+\)/, 19 | lambda { |type| type.include?('W') ? :warning : :error } 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/js_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `jslint` against any modified JavaScript files. 5 | # 6 | # @see http://www.jslint.com/ 7 | class JsLint < Base 8 | MESSAGE_REGEX = /(?(?:\w:)?[^:]+):(?\d+)/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | return :pass if result.success? 13 | 14 | # example message: 15 | # path/to/file.js:1:1: Error message 16 | extract_messages( 17 | result.stdout.split("\n").grep(MESSAGE_REGEX), 18 | MESSAGE_REGEX 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/jscs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `jscs` (JavaScript Code Style Checker) against any modified JavaScript 5 | # files. 6 | # 7 | # @see http://jscs.info/ 8 | class Jscs < Base 9 | def run 10 | result = execute(command, args: applicable_files) 11 | return :pass if result.success? 12 | 13 | # Exit status 2 = Code style errors; everything else we don't know how to 14 | # parse. https://github.com/jscs-dev/node-jscs/wiki/Exit-codes 15 | unless result.status == 2 16 | return :fail, result.stdout + result.stderr.chomp 17 | end 18 | 19 | # example message: 20 | # path/to/file.js: line 7, col 0, ruleName: Error message 21 | extract_messages( 22 | result.stdout.split("\n"), 23 | /^(?(?:\w:)?[^:]+):[^\d]+(?\d+)/, 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/jsl.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `jsl` against any modified JavaScript files. 5 | # 6 | # @see http://www.javascriptlint.com/ 7 | class Jsl < Base 8 | MESSAGE_REGEX = /(?(?:\w:)?.+)\((?\d+)\):(?[^:]+)/.freeze 9 | 10 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 11 | type.match?(/warning/) ? :warning : :error 12 | end 13 | 14 | def run 15 | file_flags = applicable_files.map { |file| ['-process', file] } 16 | result = execute(command + file_flags.flatten) 17 | return :pass if result.success? 18 | 19 | # example message: 20 | # path/to/file.js(1): lint warning: Error message 21 | extract_messages( 22 | result.stdout.split("\n").grep(MESSAGE_REGEX), 23 | MESSAGE_REGEX, 24 | MESSAGE_TYPE_CATEGORIZER 25 | ) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/json_syntax.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks the syntax of any modified JSON files. 5 | class JsonSyntax < Base 6 | def run 7 | messages = [] 8 | 9 | applicable_files.each do |file| 10 | JSON.parse(IO.read(file)) 11 | rescue JSON::ParserError => e 12 | error = "#{e.message} parsing #{file}" 13 | messages << Overcommit::Hook::Message.new(:error, file, nil, error) 14 | end 15 | 16 | messages 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/kt_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `ktlint` against modified Kotlin files. 5 | # @see https://github.com/shyiko/ktlint 6 | class KtLint < Base 7 | MESSAGE_REGEX = /((?[^:]+):(?\d+):(\d+):(?.+))/.freeze 8 | 9 | def run 10 | result = execute(command, args: applicable_files) 11 | return :pass if result.success? 12 | 13 | extract_messages( 14 | result.stdout.split("\n").grep(MESSAGE_REGEX), 15 | MESSAGE_REGEX 16 | ) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/license_finder.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs LicenseFinder if any of your package manager declaration files have changed 5 | # See more about LicenseFinder at https://github.com/pivotal/LicenseFinder 6 | class LicenseFinder < Base 7 | def run 8 | result = execute(command) 9 | return :pass if result.success? 10 | 11 | output = result.stdout + result.stderr 12 | [:fail, output] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for local paths in files and issues a warning 5 | class LocalPathsInGemfile < Base 6 | def run 7 | result = execute(command, args: applicable_files) 8 | 9 | unless result.stdout.empty? 10 | return :warn, "Avoid pointing to local paths in Gemfiles:\n#{result.stdout}" 11 | end 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/mdl.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `mdl` against any modified Markdown files 5 | # 6 | # @see https://github.com/mivok/markdownlint 7 | class Mdl < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | output = result.stdout.chomp 11 | 12 | return :pass if result.success? 13 | return [:fail, result.stderr] unless result.stderr.empty? 14 | 15 | # example message: 16 | # [{"filename":"file1.md","line":1,"rule":"MD013","aliases":["line-length"], 17 | # "description":"Line length"}] 18 | json_messages = JSON.parse(output) 19 | json_messages.map do |message| 20 | Overcommit::Hook::Message.new( 21 | :error, 22 | message['filename'], 23 | message['line'], 24 | "#{message['filename']}:#{message['line']} #{message['rule']} #{message['description']}" 25 | ) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/merge_conflicts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for unresolved merge conflicts 5 | class MergeConflicts < Base 6 | def run 7 | result = execute(command, args: applicable_files) 8 | 9 | unless result.stdout.empty? 10 | return :fail, "Merge conflict markers detected:\n#{result.stdout}" 11 | end 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/mix_format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `mix format --check-formatted` against any modified ex/heex/exs files. 5 | # 6 | # @see https://hexdocs.pm/mix/main/Mix.Tasks.Format.html 7 | class MixFormat < Base 8 | # example message: 9 | # ** (Mix) mix format failed due to --check-formatted. 10 | # The following files are not formatted: 11 | # 12 | # * lib/file1.ex 13 | # * lib/file2.ex 14 | FILES_REGEX = /^\s+\*\s+(?.+)$/.freeze 15 | 16 | def run 17 | result = execute(command, args: applicable_files) 18 | return :pass if result.success? 19 | 20 | result.stderr.scan(FILES_REGEX).flatten. 21 | map { |file| message(file) } 22 | end 23 | 24 | private 25 | 26 | def message(file) 27 | Overcommit::Hook::Message.new(:error, file, nil, file) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/nginx_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `nginx -t` against any modified Nginx config files. 5 | # 6 | # @see https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/ 7 | class NginxTest < Base 8 | MESSAGE_REGEX = /^nginx: .+ in (?.+):(?\d+)$/.freeze 9 | 10 | def run 11 | messages = [] 12 | 13 | applicable_files.each do |file| 14 | result = execute(command + ['-c', file]) 15 | next if result.success? 16 | 17 | messages += extract_messages( 18 | result.stderr.split("\n").grep(MESSAGE_REGEX), 19 | MESSAGE_REGEX 20 | ) 21 | end 22 | 23 | messages 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pep257.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pep257` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/pep257 7 | class Pep257 < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | output = result.stderr.chomp 13 | 14 | # example message: 15 | # path/to/file.py:1 in public method `foo`: 16 | # D102: Docstring missing 17 | extract_messages( 18 | output.gsub(/:\s+/, ': ').split("\n"), 19 | /^(?(?:\w:)?[^:]+):(?\d+)/ 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pep8.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pep8` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/pep8 7 | class Pep8 < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | output = result.stdout.chomp 11 | 12 | return :pass if result.success? && output.empty? 13 | 14 | # example message: 15 | # path/to/file.py:88:5: E301 expected 1 blank line, found 0 16 | extract_messages( 17 | output.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+):\d+:\s(?E|W)/, 19 | lambda { |type| type.include?('W') ? :warning : :error } 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/php_stan.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `phpstan` against any modified PHP files. 5 | # For running `phpstan` with Laravel, it requires setup with `ide_helper`. 6 | # 7 | # References: 8 | # https://github.com/phpstan/phpstan/issues/239 9 | # https://gist.github.com/edmondscommerce/89695c9cd2584fefdf540fb1c528d2c2 10 | class PhpStan < Base 11 | MESSAGE_REGEX = /^(?.+)\:(?\d+)\:(?.+)/.freeze 12 | 13 | def run 14 | messages = [] 15 | 16 | result = execute(command, args: applicable_files) 17 | 18 | unless result.success? 19 | messages += result.stdout.lstrip.split("\n") 20 | end 21 | 22 | return :pass if messages.empty? 23 | 24 | extract_messages( 25 | messages, 26 | MESSAGE_REGEX 27 | ) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pronto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/pronto' 4 | 5 | module Overcommit::Hook::PreCommit 6 | # Runs `pronto` 7 | # 8 | # @see https://github.com/mmozuras/pronto 9 | class Pronto < Base 10 | include Overcommit::Hook::Shared::Pronto 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/puppet_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs 'puppet-lint' against any modified Puppet files. 5 | # 6 | # @see http://puppet-lint.com/ 7 | class PuppetLint < Base 8 | MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze 9 | 10 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 11 | type == 'ERROR' ? :error : :warning 12 | end 13 | 14 | def run 15 | result = execute(command, args: applicable_files) 16 | output = result.stdout.chomp.gsub(/^"|"$/, '') 17 | return :pass if result.success? && output.empty? 18 | 19 | extract_messages( 20 | output.split("\n"), 21 | MESSAGE_REGEX, 22 | MESSAGE_TYPE_CATEGORIZER 23 | ) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # 5 | # Run's the Puppet metadata linter. It has support for adding options 6 | # in the .overcommit.yaml 7 | # 8 | # @see https://voxpupuli.org/blog/2014/11/06/linting-metadata-json/ 9 | # 10 | class PuppetMetadataJsonLint < Base 11 | MESSAGE_REGEX = /\((?.*)\).*/.freeze 12 | 13 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 14 | type == 'WARN' ? :warning : :error 15 | end 16 | 17 | def run 18 | result = execute(command, args: applicable_files) 19 | output = result.stdout.chomp.gsub(/^"|"$/, '') 20 | return :pass if result.success? && output.empty? 21 | 22 | extract_messages( 23 | output.split("\n"), 24 | MESSAGE_REGEX, 25 | MESSAGE_TYPE_CATEGORIZER 26 | ) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pycodestyle.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pycodestyle` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/pycodestyle 7 | class Pycodestyle < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | output = result.stdout.chomp 11 | 12 | return :pass if result.success? && output.empty? 13 | 14 | # example message: 15 | # path/to/file.py:88:5: E301 expected 1 blank line, found 0 16 | extract_messages( 17 | output.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+):\d+:\s(?E|W)/, 19 | lambda { |type| type.include?('W') ? :warning : :error } 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pydocstyle.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pydocstyle` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/pydocstyle 7 | class Pydocstyle < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | output = result.stderr.chomp 13 | 14 | # example message: 15 | # path/to/file.py:1 in public method `foo`: 16 | # D102: Docstring missing 17 | extract_messages( 18 | output.gsub(/:\s+/, ': ').split("\n"), 19 | /^(?(?:\w:)?[^:]+):(?\d+)/ 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pyflakes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pyflakes` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/pyflakes 7 | class Pyflakes < Base 8 | MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | return :pass if result.success? 13 | 14 | errors = get_messages(result.stderr, :error) 15 | warnings = get_messages(result.stdout, :warning) 16 | 17 | errors + warnings 18 | end 19 | 20 | private 21 | 22 | def get_messages(output, type) 23 | # example message: 24 | # path/to/file.py:57: local variable 'x' is assigned to but never used 25 | extract_messages( 26 | output.split("\n").grep(MESSAGE_REGEX), 27 | MESSAGE_REGEX, 28 | proc { type } 29 | ) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/pylint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `pylint` against any modified Python files. 5 | # 6 | # @see http://www.pylint.org/ 7 | class Pylint < Base 8 | MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):(?[CEFRW])/.freeze 9 | 10 | # Classify 'E' and 'F' message codes as errors, 11 | # everything else as warnings. 12 | # http://pylint.readthedocs.org/en/latest/tutorial.html#getting-started 13 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 14 | 'EF'.include?(type) ? :error : :warning 15 | end 16 | 17 | def run 18 | result = execute(command, args: applicable_files) 19 | return :pass if result.success? 20 | 21 | output = result.stdout.chomp 22 | 23 | # example message: 24 | # path/to/file.py:64:C: Missing function docstring (missing-docstring) 25 | extract_messages( 26 | output.split("\n").grep(MESSAGE_REGEX), 27 | MESSAGE_REGEX, 28 | MESSAGE_TYPE_CATEGORIZER 29 | ) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/python_flake8.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `flake8` against any modified Python files. 5 | # 6 | # @see https://pypi.python.org/pypi/flake8 7 | class PythonFlake8 < Base 8 | MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):\d+:\s(?\w\d+)/.freeze 9 | 10 | # Classify 'Exxx' and 'Fxxx' message codes as errors, 11 | # everything else as warnings. 12 | # http://flake8.readthedocs.org/en/latest/warnings.html 13 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 14 | 'EF'.include?(type[0]) ? :error : :warning 15 | end 16 | 17 | def run 18 | result = execute(command, args: applicable_files) 19 | return :pass if result.success? 20 | 21 | output = result.stdout.chomp 22 | 23 | # example message: 24 | # path/to/file.py:2:13: F812 list comprehension redefines name from line 1 25 | extract_messages( 26 | output.split("\n").grep(MESSAGE_REGEX), 27 | MESSAGE_REGEX, 28 | MESSAGE_TYPE_CATEGORIZER 29 | ) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/r_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/r_spec' 4 | 5 | module Overcommit::Hook::PreCommit 6 | # Runs `rspec` test suite 7 | # 8 | # @see http://rspec.info/ 9 | class RSpec < Base 10 | include Overcommit::Hook::Shared::RSpec 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/rails_best_practices.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit 4 | module Hook 5 | module PreCommit 6 | # Runs `rails_best_practices` against Ruby files 7 | # 8 | # @see https://github.com/railsbp/rails_best_practices 9 | class RailsBestPractices < Base 10 | ERROR_REGEXP = /^(?(?:\w:)?[^:]+):(?\d+)\s-\s(?.+)/.freeze 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | 15 | return :pass if result.success? 16 | return [:fail, result.stderr] unless result.stderr.empty? 17 | 18 | extract_messages( 19 | filter_output(result.stdout), 20 | ERROR_REGEXP 21 | ) 22 | end 23 | 24 | private 25 | 26 | def filter_output(stdout) 27 | stdout.split("\n").select do |message| 28 | message.match ERROR_REGEXP 29 | end 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/rake_target.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/rake_target' 4 | 5 | module Overcommit::Hook::PreCommit 6 | # Runs rake targets 7 | # 8 | # @see Overcommit::Hook::Shared::RakeTarget 9 | class RakeTarget < Base 10 | include Overcommit::Hook::Shared::RakeTarget 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/reek.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `reek` against any modified Ruby files. 5 | # 6 | # @see https://github.com/troessner/reek 7 | class Reek < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | output = scrub_output(result.stdout + result.stderr) 13 | 14 | extract_messages( 15 | output, 16 | /^\s*(?(?:\w:)?[^:]+):(?\d+):/, 17 | ) 18 | end 19 | 20 | private 21 | 22 | def scrub_output(raw_output) 23 | raw_output.split("\n").grep(/^(.(?!warning))*$/) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/rst_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `rst-lint` against any modified reStructuredText files 5 | # 6 | # @see https://github.com/twolfson/restructuredtext-lint 7 | class RstLint < Base 8 | MESSAGE_REGEX = / 9 | ^(?INFO|WARNING|ERROR|SEVERE)(?(?:\w:)?[^:]+):(?\d+)\s(?.+) 10 | /x.freeze 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | output = result.stdout.chomp 15 | 16 | return :pass if result.success? 17 | return [:fail, result.stderr] unless result.stderr.empty? 18 | 19 | # example message: 20 | # WARNING README.rst:7 Title underline too short. 21 | extract_messages( 22 | output.split("\n"), 23 | MESSAGE_REGEX 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/rubo_cop.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `rubocop` against any modified Ruby files. 5 | # 6 | # @see http://batsov.com/rubocop/ 7 | class RuboCop < Base 8 | GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.match?(/^warn/) ? :warning : :error 10 | end 11 | 12 | COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type| 13 | type.include?('W') ? :warning : :error 14 | end 15 | 16 | def run 17 | result = execute(command, args: applicable_files) 18 | return :pass if result.success? 19 | 20 | generic_messages = extract_messages( 21 | result.stderr.split("\n"), 22 | /^(?[a-z]+)/i, 23 | GENERIC_MESSAGE_TYPE_CATEGORIZER, 24 | ) 25 | 26 | cop_messages = extract_messages( 27 | result.stdout.split("\n"), 28 | /^(?(?:\w:)?[^:]+):(?\d+):[^ ]+ (?[^ ]+)/, 29 | COP_MESSAGE_TYPE_CATEGORIZER, 30 | ) 31 | 32 | generic_messages + cop_messages 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/ruby_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `ruby-lint` against any modified Ruby files. 5 | # 6 | # @see https://github.com/YorickPeterse/ruby-lint 7 | class RubyLint < Base 8 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.include?('W') ? :warning : :error 10 | end 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | extract_messages( 17 | result.stdout.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?[^:]+):(?\d+)/, 19 | MESSAGE_TYPE_CATEGORIZER 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/ruby_syntax.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `ruby -c` against all Ruby files. 5 | # 6 | class RubySyntax < Base 7 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 8 | type.match?(/^(syntax)?\s*error/) ? :error : :warning 9 | end 10 | 11 | def run 12 | result = execute(command, args: applicable_files) 13 | 14 | result_lines = result.stderr.split("\n") 15 | 16 | return :pass if result_lines.length.zero? 17 | 18 | # Example message: 19 | # path/to/file.rb:1: syntax error, unexpected '^' 20 | extract_messages( 21 | result_lines, 22 | /^(?[^:]+):(?\d+):\s*(?[^,]+),\s*(?.+)/, 23 | MESSAGE_TYPE_CATEGORIZER 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/scalariform.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `scalariform` against any modified Scala files. 5 | # 6 | # @see https://github.com/mdr/scalariform 7 | class Scalariform < Base 8 | MESSAGE_REGEX = /^\[(?FAILED|ERROR)\]\s+(?(?:\w:)?.+)/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | 13 | # example message: 14 | # [FAILED] path/to/file.scala 15 | extract_messages( 16 | result.stdout.split("\n").grep(MESSAGE_REGEX), 17 | MESSAGE_REGEX, 18 | lambda { |type| type == 'ERROR' ? :error : :warning } 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/scalastyle.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `scalastyle` against any modified Scala files. 5 | # 6 | # @see http://www.scalastyle.org/ 7 | class Scalastyle < Base 8 | MESSAGE_REGEX = / 9 | ^(?error|warning)\s 10 | file=(?(?:\w:)?.+)\s 11 | message=.+\s* 12 | (line=(?\d+))? 13 | /x.freeze 14 | 15 | def run 16 | result = execute(command, args: applicable_files) 17 | output = result.stdout.chomp + result.stderr.chomp 18 | messages = output.split("\n").grep(MESSAGE_REGEX) 19 | 20 | return [:fail, output] unless result.success? || messages.any? 21 | 22 | # example message: 23 | # error file=/path/to/file.scala message=Error message line=1 column=1 24 | extract_messages( 25 | messages, 26 | MESSAGE_REGEX, 27 | lambda { |type| type.to_sym } 28 | ) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/semi_standard.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `semistandard` against any modified JavaScript files. 5 | # 6 | # @see https://github.com/Flet/semistandard 7 | class SemiStandard < Base 8 | MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | output = result.stdout.chomp 13 | return :pass if result.success? && output.empty? 14 | 15 | # example message: 16 | # path/to/file.js:1:1: Error message (ruleName) 17 | extract_messages( 18 | output.split("\n").grep(MESSAGE_REGEX), # ignore header line 19 | MESSAGE_REGEX 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/shell_check.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `shellcheck` against any modified shell script files. 5 | # 6 | # @see http://www.shellcheck.net/ 7 | class ShellCheck < Base 8 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.include?('note') ? :warning : :error 10 | end 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | extract_messages( 17 | result.stdout.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+):[^ ]+ (?[^ ]+)/, 19 | MESSAGE_TYPE_CATEGORIZER, 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/slim_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `slim-lint` against any modified Slim templates. 5 | # 6 | # @see https://github.com/sds/slim-lint 7 | class SlimLint < Base 8 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.include?('W') ? :warning : :error 10 | end 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | extract_messages( 17 | result.stdout.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+)/, 19 | MESSAGE_TYPE_CATEGORIZER, 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/sorbet.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs 'srb tc' against any modified files. 5 | # 6 | # @see https://github.com/sorbet/sorbet 7 | class Sorbet < Base 8 | # example of output: 9 | # sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003 10 | MESSAGE_REGEX = /^(?[^:]+):(?\d+): (?.*)$/.freeze 11 | 12 | def run 13 | result = execute(command, args: applicable_files) 14 | return :pass if result.success? 15 | 16 | output = result.stderr.split("\n").grep(MESSAGE_REGEX) 17 | 18 | extract_messages( 19 | output, 20 | MESSAGE_REGEX 21 | ) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/sqlint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs 'sqlint' against any modified SQL files. 5 | # 6 | # @see https://github.com/purcell/sqlint 7 | class Sqlint < Base 8 | MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze 9 | 10 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 11 | type == 'ERROR' ? :error : :warning 12 | end 13 | 14 | def run 15 | result = execute(command, args: applicable_files) 16 | output = result.stdout.chomp 17 | return :pass if result.success? && output.empty? 18 | 19 | extract_messages( 20 | output.split("\n"), 21 | MESSAGE_REGEX, 22 | MESSAGE_TYPE_CATEGORIZER 23 | ) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/standard.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `standard` against any modified JavaScript files. 5 | # 6 | # @see https://github.com/feross/standard 7 | class Standard < Base 8 | MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | output = result.stdout.chomp 13 | return :pass if result.success? && output.empty? 14 | 15 | # example message: 16 | # path/to/file.js:1:1: Error message (ruleName) 17 | extract_messages( 18 | output.split("\n").grep(MESSAGE_REGEX), # ignore header line 19 | MESSAGE_REGEX 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/stylelint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `stylelint` against any modified CSS file. 5 | # 6 | # @see https://github.com/stylelint/stylelint 7 | class Stylelint < Base 8 | # example of output: 9 | # index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation) 10 | 11 | MESSAGE_REGEX = /^(?[^:]+):\D*(?\d+).*$/.freeze 12 | 13 | def run 14 | result = execute(command, args: applicable_files) 15 | output = result.stdout + result.stderr.chomp 16 | return :pass if result.success? && output.empty? 17 | 18 | extract_messages( 19 | output.split("\n"), 20 | MESSAGE_REGEX 21 | ) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/swift_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `swiftlint lint` against modified Swift files. 5 | # @see https://github.com/realm/SwiftLint 6 | class SwiftLint < Base 7 | MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+):(?.*)/.freeze 8 | 9 | def run 10 | result = execute(command, args: applicable_files) 11 | return :pass if result.success? 12 | 13 | extract_messages( 14 | result.stdout.split("\n").grep(MESSAGE_REGEX), 15 | MESSAGE_REGEX 16 | ) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/terraform_format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs 'terraform fmt' against any modified *.tf files. 5 | # 6 | # @see https://www.terraform.io/docs/commands/fmt.html 7 | class TerraformFormat < Base 8 | def run 9 | messages = [] 10 | applicable_files.each do |f| 11 | result = execute(command, args: [f]) 12 | unless result.success? 13 | messages << Overcommit::Hook::Message.new(:error, f, nil, "violation found in #{f}") 14 | end 15 | end 16 | messages 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/trailing_whitespace.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks for trailing whitespace in files. 5 | class TrailingWhitespace < Base 6 | def run 7 | result = execute(command, args: applicable_files) 8 | 9 | extract_messages( 10 | result.stdout.split("\n"), 11 | /^(?(?:\w:)?[^:]+):(?\d+)/, 12 | ) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/travis_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `travis-lint` against any modified Travis CI files. 5 | # 6 | # @see https://github.com/travis-ci/travis.rb 7 | class TravisLint < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | [:fail, (result.stdout + result.stderr).strip] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/ts_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `tslint` against modified TypeScript files. 5 | # @see http://palantir.github.io/tslint/ 6 | class TsLint < Base 7 | # example message: 8 | # "src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140" 9 | # or 10 | # "ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef" 11 | MESSAGE_REGEX = /^(?.+: )?(?.+?(?=\[))[^\d]+(?\d+).*?/.freeze 12 | 13 | def run 14 | result = execute(command, args: applicable_files) 15 | output = result.stdout.chomp 16 | return :pass if result.success? && output.empty? 17 | 18 | output_lines = output.split("\n").map(&:strip).reject(&:empty?) 19 | type_categorizer = ->(type) { type.nil? || type.include?('ERROR') ? :error : :warning } 20 | 21 | extract_messages( 22 | output_lines, 23 | MESSAGE_REGEX, 24 | type_categorizer 25 | ) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/vint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `vint` against any modified Vim script files. 5 | # 6 | # @see https://github.com/Kuniwak/vint 7 | class Vint < Base 8 | def run 9 | result = execute(command, args: applicable_files) 10 | return :pass if result.success? 11 | 12 | return [:fail, result.stderr] unless result.stderr.empty? 13 | 14 | # example message: 15 | # path/to/file.vim:1:1: Error message 16 | extract_messages( 17 | result.stdout.split("\n"), 18 | /^(?(?:\w:)?[^:]+):(?\d+)/ 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/xml_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `xmllint` against any modified XML files. 5 | # 6 | # @see http://xmlsoft.org/xmllint.html 7 | class XmlLint < Base 8 | MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze 9 | 10 | def run 11 | result = execute(command, args: applicable_files) 12 | output = result.stderr.chomp 13 | 14 | return :pass if result.success? && output.empty? 15 | 16 | # example message: 17 | # path/to/file.xml:1: parser error : Error message 18 | extract_messages( 19 | output.split("\n").grep(MESSAGE_REGEX), 20 | MESSAGE_REGEX 21 | ) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/xml_syntax.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks the syntax of any modified XML files. 5 | class XmlSyntax < Base 6 | def run 7 | messages = [] 8 | 9 | applicable_files.each do |file| 10 | REXML::Document.new(IO.read(file)) 11 | rescue REXML::ParseException => e 12 | error = "Error parsing #{file}: #{e.message}" 13 | messages << Overcommit::Hook::Message.new(:error, file, nil, error) 14 | end 15 | 16 | messages 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/yaml_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Runs `YAMLLint` against any modified YAML files. 5 | # 6 | # @see https://github.com/adrienverge/yamllint 7 | class YamlLint < Base 8 | MESSAGE_REGEX = / 9 | ^(?.+) 10 | :(?\d+) 11 | :(?\d+) 12 | :\s\[(?\w+)\] 13 | \s(?.+)$ 14 | /x.freeze 15 | 16 | def run 17 | result = execute(command, args: applicable_files) 18 | parse_messages(result.stdout) 19 | end 20 | 21 | private 22 | 23 | def parse_messages(output) 24 | repo_root = Overcommit::Utils.repo_root 25 | 26 | output.scan(MESSAGE_REGEX).map do |file, line, col, type, msg| 27 | line = line.to_i 28 | type = type.to_sym 29 | # Obtain the path relative to the root of the repository 30 | # for nicer output: 31 | relpath = file.dup 32 | relpath.slice!("#{repo_root}/") 33 | 34 | text = "#{relpath}:#{line}:#{col}:#{type} #{msg}" 35 | Overcommit::Hook::Message.new(type, file, line, text) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_commit/yaml_syntax.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreCommit 4 | # Checks the syntax of any modified YAML files. 5 | class YamlSyntax < Base 6 | def run 7 | messages = [] 8 | 9 | applicable_files.each do |file| 10 | YAML.load_file(file, aliases: true) 11 | rescue ArgumentError 12 | begin 13 | YAML.load_file(file) 14 | rescue ArgumentError, Psych::SyntaxError => e 15 | messages << Overcommit::Hook::Message.new(:error, file, nil, e.message) 16 | end 17 | rescue Psych::DisallowedClass => e 18 | messages << error_message(file, e) 19 | end 20 | 21 | messages 22 | end 23 | 24 | private 25 | 26 | def error_message(file, error) 27 | text = "#{file}: #{error.message}" 28 | Overcommit::Hook::Message.new(:error, file, nil, text) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | require 'overcommit/utils/messages_utils' 5 | 6 | module Overcommit::Hook::PrePush 7 | # Functionality common to all pre-push hooks. 8 | class Base < Overcommit::Hook::Base 9 | extend Forwardable 10 | 11 | def_delegators :@context, :remote_name, :remote_url, :pushed_refs 12 | 13 | def run? 14 | super && 15 | !exclude_remotes.include?(remote_name) && 16 | (include_remote_ref_deletions? || !@context.remote_ref_deletion?) 17 | end 18 | 19 | private 20 | 21 | def extract_messages(*args) 22 | Overcommit::Utils::MessagesUtils.extract_messages(*args) 23 | end 24 | 25 | def exclude_remotes 26 | @config['exclude_remotes'] || [] 27 | end 28 | 29 | def include_remote_ref_deletions? 30 | @config['include_remote_ref_deletions'] 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/brakeman.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `brakeman` whenever Ruby/Rails files change. 5 | # 6 | # @see http://brakemanscanner.org/ 7 | class Brakeman < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | [:fail, result.stdout] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/cargo_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `cargo test` before push if Rust files changed 5 | class CargoTest < Base 6 | def run 7 | result = execute(command) 8 | return :pass if result.success? 9 | 10 | [:fail, result.stdout] 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/flutter_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs Flutter test suite (`flutter test`) before push 5 | # 6 | # @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html 7 | class FlutterTest < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/go_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `go test ./...` command on prepush 5 | class GoTest < Base 6 | def run 7 | result = execute(command) 8 | return :pass if result.success? 9 | 10 | output = result.stdout + result.stderr 11 | [:fail, output] 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/golangci_lint.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs golangci-lint 5 | # 6 | # @see https://github.com/golangci/golangci-lint 7 | class GolangciLint < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/minitest.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `minitest` test suite before push 5 | # 6 | # @see https://github.com/seattlerb/minitest 7 | class Minitest < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | 16 | def command 17 | super + included_files.map { |file| "-r#{file}" } 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/mix_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `mix test` test suite before push 5 | # 6 | # @see https://hexdocs.pm/mix/Mix.Tasks.Test.html 7 | class MixTest < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/php_unit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `phpunit` test suite before push 5 | # 6 | # @see https://phpunit.de/ 7 | class PhpUnit < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/pronto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/pronto' 4 | 5 | module Overcommit::Hook::PrePush 6 | # Runs `pronto` 7 | # 8 | # @see https://github.com/mmozuras/pronto 9 | class Pronto < Base 10 | include Overcommit::Hook::Shared::Pronto 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/pub_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs Dart test suite (`pub run test`) before push 5 | # 6 | # @see https://pub.dev/packages/test#running-tests 7 | class PubTest < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/pytest.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `pytest` test suite before push 5 | # 6 | # @see https://github.com/pytest-dev/pytest 7 | class Pytest < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/python_nose.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `nose` test suite before push 5 | # 6 | # @see https://nose.readthedocs.io/en/latest/ 7 | class PythonNose < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/r_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/r_spec' 4 | 5 | module Overcommit::Hook::PrePush 6 | # Runs `rspec` test suite 7 | # 8 | # @see http://rspec.info/ 9 | class RSpec < Base 10 | include Overcommit::Hook::Shared::RSpec 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/rake_target.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/hook/shared/rake_target' 4 | 5 | module Overcommit::Hook::PrePush 6 | # Runs rake targets 7 | # 8 | # @see Overcommit::Hook::Shared::RakeTarget 9 | class RakeTarget < Base 10 | include Overcommit::Hook::Shared::RakeTarget 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_push/test_unit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PrePush 4 | # Runs `test-unit` test suite before push 5 | # 6 | # @see https://github.com/test-unit/test-unit 7 | class TestUnit < Base 8 | def run 9 | result = execute(command) 10 | return :pass if result.success? 11 | 12 | output = result.stdout + result.stderr 13 | [:fail, output] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_rebase/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PreRebase 6 | # Functionality common to all pre-rebase hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, 11 | :upstream_branch, :rebased_branch, :detached_head?, 12 | :fast_forward?, :rebased_commits 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/overcommit/hook/pre_rebase/merged_commits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::PreRebase 4 | # Prevents rebasing commits that have already been merged into one of 5 | # a specified set of branches. 6 | class MergedCommits < Base 7 | def run 8 | # Allow rebasing a detached HEAD since no refs are changed. 9 | return :pass if detached_head? || illegal_commits.empty? 10 | 11 | message = 'Cannot rebase commits that have already been merged into ' \ 12 | "one of #{branches.join(', ')}" 13 | 14 | [:fail, message] 15 | end 16 | 17 | private 18 | 19 | def branches 20 | @branches ||= config['branches'] 21 | end 22 | 23 | def illegal_commits 24 | @illegal_commits ||= rebased_commits.select do |commit_sha1| 25 | branches_containing_commit = 26 | Overcommit::GitRepo.branches_containing_commit(commit_sha1) 27 | (branches_containing_commit & branches).any? 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/overcommit/hook/prepare_commit_msg/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | module Overcommit::Hook::PrepareCommitMsg 6 | # Functionality common to all prepare-commit-msg hooks. 7 | class Base < Overcommit::Hook::Base 8 | extend Forwardable 9 | 10 | def_delegators :@context, 11 | :commit_message_filename, :commit_message_source, :commit, :lock 12 | 13 | def modify_commit_message 14 | raise 'This expects a block!' unless block_given? 15 | 16 | # NOTE: this assumes all the hooks of the same type share the context's 17 | # memory. If that's not the case, this won't work. 18 | lock.synchronize do 19 | contents = File.read(commit_message_filename) 20 | File.open(commit_message_filename, 'w') do |f| 21 | f << (yield contents) 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/bower_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all BowerInstall hooks. Runs `bower install` when a 5 | # change is detected in the repository's dependencies. 6 | # 7 | # @see http://bower.io/ 8 | module BowerInstall 9 | def run 10 | result = execute(command) 11 | return :fail, result.stderr unless result.success? 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/bundle_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all BundleInstall hooks. Runs `bundle install` when a 5 | # change is detected in the repository's dependencies. 6 | # 7 | # @see http://bundler.io/ 8 | module BundleInstall 9 | def run 10 | result = execute(command) 11 | return :fail, result.stdout unless result.success? 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/composer_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all ComposerInstall hooks. Runs `composer install` when 5 | # a change is detected in the repository's dependencies. 6 | # 7 | # @see https://getcomposer.org/ 8 | module ComposerInstall 9 | def run 10 | result = execute(command) 11 | return :fail, result.stdout unless result.success? 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/index_tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all IndexTags hooks. It runs ctags in the background so 5 | # your tag definitions are up-to-date. 6 | # 7 | # @see http://ctags.sourceforge.net/ 8 | module IndexTags 9 | def run 10 | execute_in_background([Overcommit::Utils.script_path('index-tags')]) 11 | :pass 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/npm_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all NpmInstall hooks. Runs `npm install` when a change 5 | # is detected in the repository's dependencies. 6 | # 7 | # @see https://www.npmjs.com/ 8 | module NpmInstall 9 | def run 10 | result = execute(command) 11 | return :fail, result.stderr unless result.success? 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/pronto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all Pronto hooks. Runs pronto linters. 5 | 6 | # @see https://github.com/prontolabs/pronto 7 | module Pronto 8 | MESSAGE_TYPE_CATEGORIZER = lambda do |type| 9 | type.include?('E') ? :error : :warning 10 | end 11 | 12 | MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+) (?[^ ]+)/.freeze 13 | 14 | def run 15 | result = execute(command) 16 | return :pass if result.success? 17 | 18 | # e.g. runtime errors 19 | generic_errors = extract_messages( 20 | result.stderr.split("\n"), 21 | /^(?[a-z]+)/i 22 | ) 23 | 24 | pronto_infractions = extract_messages( 25 | result.stdout.split("\n").select { |line| line.match?(MESSAGE_REGEX) }, 26 | MESSAGE_REGEX, 27 | MESSAGE_TYPE_CATEGORIZER, 28 | ) 29 | 30 | generic_errors + pronto_infractions 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/r_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Runs `rspec` test suite before push 5 | # 6 | # @see http://rspec.info/ 7 | module RSpec 8 | def run 9 | result = if @config['include'] 10 | execute(command, args: applicable_files) 11 | else 12 | execute(command) 13 | end 14 | 15 | return :pass if result.success? 16 | 17 | output = result.stdout + result.stderr 18 | [:fail, output] 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/rake_target.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # runs specified rake targets. It fails on the first non- 5 | # successful exit. 6 | # 7 | module RakeTarget 8 | def run 9 | targets = config['targets'] 10 | 11 | if Array(targets).empty? 12 | raise 'RakeTarget: targets parameter is empty. Add at least one task to ' \ 13 | 'the targets parameter. Valid: Array of target names or String of ' \ 14 | 'target names' 15 | end 16 | 17 | targets.each do |task| 18 | result = execute(command + [task]) 19 | unless result.success? 20 | return :fail, "Rake target #{task}:\n#{result.stdout}" 21 | end 22 | end 23 | :pass 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/submodule_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all `SubmoduleStatus` hooks to notify the user if any 5 | # submodules are uninitialized, out of date with the current index, or contain 6 | # merge conflicts. 7 | module SubmoduleStatus 8 | def run 9 | messages = [] 10 | submodule_statuses.each do |submodule_status| 11 | path = submodule_status.path 12 | if submodule_status.uninitialized? 13 | messages << "Submodule #{path} is uninitialized." 14 | elsif submodule_status.outdated? 15 | messages << "Submodule #{path} is out of date with the current index." 16 | elsif submodule_status.merge_conflict? 17 | messages << "Submodule #{path} has merge conflicts." 18 | end 19 | end 20 | 21 | return :pass if messages.empty? 22 | 23 | [:warn, messages.join("\n")] 24 | end 25 | 26 | private 27 | 28 | def submodule_statuses 29 | Overcommit::GitRepo.submodule_statuses(recursive: config['recursive']) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/overcommit/hook/shared/yarn_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::Hook::Shared 4 | # Shared code used by all YarnInstall hooks. Runs `yarn install` when a change 5 | # is detected in the repository's dependencies. 6 | # 7 | # @see https://yarnpkg.com/ 8 | module YarnInstall 9 | def run 10 | result = execute(command) 11 | return :fail, result.stderr unless result.success? 12 | 13 | :pass 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Utility module which manages the creation of {HookContext}s. 4 | module Overcommit::HookContext 5 | def self.create(hook_type, config, args, input, **cli_options) 6 | hook_type_class = Overcommit::Utils.camel_case(hook_type) 7 | underscored_hook_type = Overcommit::Utils.snake_case(hook_type) 8 | 9 | require "overcommit/hook_context/#{underscored_hook_type}" 10 | 11 | Overcommit::HookContext.const_get(hook_type_class).new(config, args, input, **cli_options) 12 | rescue LoadError, NameError => e 13 | # Could happen when a symlink was created for a hook type Overcommit does 14 | # not yet support. 15 | raise Overcommit::Exceptions::HookContextLoadError, 16 | "Unable to load '#{hook_type}' hook context: '#{e}'", 17 | e.backtrace 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/diff.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'overcommit/git_repo' 4 | 5 | require 'set' 6 | 7 | module Overcommit::HookContext 8 | # Simulates a pre-commit context based on the diff with another git ref. 9 | # 10 | # This results in pre-commit hooks running against the changes between the current 11 | # and another ref, which is useful for automated CI scripts. 12 | class Diff < Base 13 | def modified_files 14 | @modified_files ||= Overcommit::GitRepo.modified_files(refs: @options[:diff]) 15 | end 16 | 17 | def modified_lines_in_file(file) 18 | @modified_lines ||= {} 19 | @modified_lines[file] ||= Overcommit::GitRepo.extract_modified_lines(file, 20 | refs: @options[:diff]) 21 | end 22 | 23 | def hook_class_name 24 | 'PreCommit' 25 | end 26 | 27 | def hook_type_name 28 | 'pre_commit' 29 | end 30 | 31 | def hook_script_name 32 | 'pre-commit' 33 | end 34 | 35 | def initial_commit? 36 | @initial_commit ||= Overcommit::GitRepo.initial_commit? 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/post_checkout.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookContext 4 | # Contains helpers related to contextual information used by post-checkout 5 | # hooks. 6 | class PostCheckout < Base 7 | # Returns the ref of the HEAD that we transitioned from. 8 | def previous_head 9 | @args[0] 10 | end 11 | 12 | # Returns the ref of the new current HEAD. 13 | def new_head 14 | @args[1] 15 | end 16 | 17 | # Returns whether this checkout was the result of changing/updating a 18 | # branch. 19 | def branch_checkout? 20 | @args[2].to_i == 1 21 | end 22 | 23 | # Returns whether this checkout was for a single file. 24 | def file_checkout? 25 | !branch_checkout? 26 | end 27 | 28 | # Get a list of files that have been added or modified between 29 | # `previous_head` and `new_head`. Renames and deletions are ignored, since 30 | # there should be nothing to check. 31 | def modified_files 32 | @modified_files ||= 33 | Overcommit::GitRepo.modified_files(refs: "#{previous_head} #{new_head}") 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/post_commit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookContext 4 | # Contains helpers related to contextual information used by post-commit 5 | # hooks. 6 | class PostCommit < Base 7 | # Get a list of files that were added, copied, or modified in the last 8 | # commit. Renames and deletions are ignored, since there should be nothing 9 | # to check. 10 | def modified_files 11 | subcmd = 'show --format=%n' 12 | @modified_files ||= Overcommit::GitRepo.modified_files(subcmd: subcmd) 13 | end 14 | 15 | # Returns the set of line numbers corresponding to the lines that were 16 | # changed in a specified file. 17 | def modified_lines_in_file(file) 18 | subcmd = 'show --format=%n' 19 | @modified_lines ||= {} 20 | @modified_lines[file] ||= 21 | Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd) 22 | end 23 | 24 | # Returns whether the commit that triggered this hook is the first commit on 25 | # the branch. 26 | # 27 | # @return [true,false] 28 | def initial_commit? 29 | return @initial_commit unless @initial_commit.nil? 30 | 31 | @initial_commit = !Overcommit::Utils.execute(%w[git rev-parse HEAD~]).success? 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/post_merge.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookContext 4 | # Contains helpers related to contextual information used by post-merge 5 | # hooks. 6 | class PostMerge < Base 7 | attr_accessor :args 8 | # Get a list of files that were added, copied, or modified in the merge 9 | # commit. Renames and deletions are ignored, since there should be nothing 10 | # to check. 11 | def modified_files 12 | staged = squash? 13 | refs = 'HEAD^ HEAD' if merge_commit? 14 | @modified_files ||= Overcommit::GitRepo.modified_files(staged: staged, refs: refs) 15 | end 16 | 17 | # Returns the set of line numbers corresponding to the lines that were 18 | # changed in a specified file. 19 | def modified_lines_in_file(file) 20 | staged = squash? 21 | refs = 'HEAD^ HEAD' if merge_commit? 22 | @modified_lines ||= {} 23 | @modified_lines[file] ||= 24 | Overcommit::GitRepo.extract_modified_lines(file, staged: staged, refs: refs) 25 | end 26 | 27 | # Returns whether this merge was made using --squash 28 | def squash? 29 | @args[0].to_i == 1 30 | end 31 | 32 | # Returns whether this merge was made without --squash 33 | def merge_commit? 34 | !squash? 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/pre_commit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'fileutils' 4 | require 'set' 5 | require_relative 'helpers/stash_unstaged_changes' 6 | require_relative 'helpers/file_modifications' 7 | 8 | module Overcommit::HookContext 9 | # Contains helpers related to contextual information used by pre-commit hooks. 10 | # 11 | # This includes staged files, which lines of those files have been modified, 12 | # etc. It is also responsible for saving/restoring the state of the repo so 13 | # hooks only inspect staged changes. 14 | class PreCommit < Base 15 | include Overcommit::HookContext::Helpers::StashUnstagedChanges 16 | include Overcommit::HookContext::Helpers::FileModifications 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/pre_rebase.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookContext 4 | # Contains helpers related to contextual information used by pre-rebase 5 | # hooks. 6 | class PreRebase < Base 7 | # Returns the name of the branch we are rebasing onto. 8 | def upstream_branch 9 | @args[0] 10 | end 11 | 12 | # Returns the name of the branch being rebased. Empty if rebasing a 13 | # detached HEAD. 14 | def rebased_branch 15 | @rebased_branch ||= 16 | @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp 17 | end 18 | 19 | # Returns whether we are rebasing a detached HEAD rather than a branch 20 | def detached_head? 21 | rebased_branch.empty? 22 | end 23 | 24 | # Returns whether this rebase is a fast-forward 25 | def fast_forward? 26 | rebased_commits.empty? 27 | end 28 | 29 | # Returns the SHA1-sums of the series of commits to be rebased 30 | # in reverse topological order. 31 | def rebased_commits 32 | rebased_ref = detached_head? ? 'HEAD' : rebased_branch 33 | @rebased_commits ||= 34 | `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_ref}`. 35 | split("\n") 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/prepare_commit_msg.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookContext 4 | # Contains helpers related to contextual information used by prepare-commit-msg 5 | # hooks. 6 | class PrepareCommitMsg < Base 7 | # Returns the name of the file that contains the commit log message 8 | def commit_message_filename 9 | @args[0] 10 | end 11 | 12 | # Returns the source of the commit message, and can be: message (if a -m or 13 | # -F option was given); template (if a -t option was given or the 14 | # configuration option commit.template is set); merge (if the commit is a 15 | # merge or a .git/MERGE_MSG file exists); squash (if a .git/SQUASH_MSG file 16 | # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend 17 | # option was given) 18 | def commit_message_source 19 | @args[1]&.to_sym 20 | end 21 | 22 | # Returns the commit's SHA-1. 23 | # If commit_message_source is :commit, it's passed through the command-line. 24 | def commit_message_source_ref 25 | @args[2] || `git rev-parse HEAD` 26 | end 27 | 28 | # Lock for the pre_commit_message file. Should be shared by all 29 | # prepare-commit-message hooks 30 | def lock 31 | @lock ||= Monitor.new 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/overcommit/hook_context/run_all.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'set' 4 | 5 | module Overcommit::HookContext 6 | # Simulates a pre-commit context pretending that all files have been changed. 7 | # 8 | # This results in pre-commit hooks running against the entire repository, 9 | # which is useful for automated CI scripts. 10 | class RunAll < Base 11 | def modified_files 12 | @modified_files ||= all_files 13 | end 14 | 15 | # Returns all lines in the file since in this context the entire repo is 16 | # being scrutinized. 17 | # 18 | # @param file [String] 19 | # @return [Set] 20 | def modified_lines_in_file(file) 21 | @modified_lines_in_file ||= {} 22 | @modified_lines_in_file[file] ||= Set.new(1..count_lines(file)) 23 | end 24 | 25 | def hook_class_name 26 | 'PreCommit' 27 | end 28 | 29 | def hook_type_name 30 | 'pre_commit' 31 | end 32 | 33 | def hook_script_name 34 | 'pre-commit' 35 | end 36 | 37 | def initial_commit? 38 | return @initial_commit unless @initial_commit.nil? 39 | 40 | @initial_commit = Overcommit::GitRepo.initial_commit? 41 | end 42 | 43 | private 44 | 45 | def count_lines(file) 46 | File.foreach(file).count 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/overcommit/hook_loader/built_in_hook_loader.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Overcommit::HookLoader 4 | # Responsible for loading hooks that ship with Overcommit. 5 | class BuiltInHookLoader < Base 6 | def load_hooks 7 | @config.enabled_builtin_hooks(@context).map do |hook_name| 8 | underscored_hook_name = Overcommit::Utils.snake_case(hook_name) 9 | require "overcommit/hook/#{@context.hook_type_name}/#{underscored_hook_name}" 10 | create_hook(hook_name) 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/overcommit/os.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rbconfig' 4 | 5 | module Overcommit 6 | # Methods relating to the current operating system 7 | module OS 8 | class << self 9 | def windows? 10 | !(/mswin|msys|mingw|bccwin|wince|emc/ =~ host_os).nil? 11 | end 12 | 13 | def cygwin? 14 | !(/cygwin/ =~ host_os).nil? 15 | end 16 | 17 | def mac? 18 | !(/darwin|mac os/ =~ host_os).nil? 19 | end 20 | 21 | def unix? 22 | !windows? 23 | end 24 | 25 | def linux? 26 | unix? && !mac? && !cygwin? 27 | end 28 | 29 | private 30 | 31 | def host_os 32 | @host_os ||= ::RbConfig::CONFIG['host_os'].freeze 33 | end 34 | end 35 | 36 | SEPARATOR = (windows? ? '\\' : File::SEPARATOR).freeze 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/overcommit/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Defines the gem version. 4 | module Overcommit 5 | VERSION = '0.67.1' 6 | end 7 | -------------------------------------------------------------------------------- /libexec/index-tags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Indexes all tags for this repository for easy navigation with Vim, storing the 3 | # file in /.git/tags. If you're using Fugitive.vim, you'll automatically 4 | # have access to the tags file; otherwise you'll have to modify your `tags` 5 | # option in your vimrc to search the generated file. 6 | 7 | set -e 8 | 9 | dir="`git rev-parse --git-dir`" 10 | 11 | trap "rm -f $dir/tags.$$" EXIT 12 | err_file=$dir/ctags.err 13 | if ctags --tag-relative -Rf$dir/tags.$$ --exclude=.git "$@" 2>${err_file}; then 14 | mv $dir/tags.$$ $dir/tags 15 | [ -e ${err_file} ] && rm -f ${err_file} 16 | else 17 | # Ignore STDERR unless `ctags` returned a non-zero exit code 18 | cat ${err_file} 19 | fi 20 | -------------------------------------------------------------------------------- /logo/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sds/overcommit/46c303377c495b1455531218f195c61243a61655/logo/horizontal.png -------------------------------------------------------------------------------- /logo/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sds/overcommit/46c303377c495b1455531218f195c61243a61655/logo/square.png -------------------------------------------------------------------------------- /spec/integration/diff_flag_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'overcommit --diff' do 6 | subject { shell(%w[overcommit --diff main]) } 7 | 8 | context 'when using an existing pre-commit hook script' do 9 | let(:script_name) { 'test-script' } 10 | let(:script_contents) { "#!/bin/bash\nexit 0" } 11 | let(:script_path) { ".#{Overcommit::OS::SEPARATOR}#{script_name}" } 12 | 13 | let(:config) do 14 | { 15 | 'PreCommit' => { 16 | 'MyHook' => { 17 | 'enabled' => true, 18 | 'required_executable' => script_path, 19 | } 20 | } 21 | } 22 | end 23 | 24 | around do |example| 25 | repo do 26 | File.open('.overcommit.yml', 'w') { |f| f.puts(config.to_yaml) } 27 | echo(script_contents, script_path) 28 | `git add #{script_path}` 29 | FileUtils.chmod(0o755, script_path) 30 | example.run 31 | end 32 | end 33 | 34 | it 'completes successfully without blocking' do 35 | wait_until(timeout: 10) { subject } # Need to wait long time for JRuby startup 36 | subject.status.should == 0 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/integration/parallelize_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'timeout' 5 | 6 | describe 'running a hook with parallelism disabled' do 7 | subject { shell(%w[git commit --allow-empty -m Test]) } 8 | 9 | let(:config) { <<-YML } 10 | concurrency: 20 11 | CommitMsg: 12 | TrailingPeriod: 13 | enabled: true 14 | parallelize: false 15 | command: ['ruby', '-e', 'sleep 1'] 16 | TextWidth: 17 | enabled: true 18 | parallelize: true 19 | processors: 1 20 | YML 21 | 22 | around do |example| 23 | repo do 24 | File.open('.overcommit.yml', 'w') { |f| f.write(config) } 25 | `overcommit --install > #{File::NULL}` 26 | example.run 27 | end 28 | end 29 | 30 | # Test fails on Ruby 3.0 on Windows but nothing else. Would glady accept a pull 31 | # request that resolves. 32 | unless Overcommit::OS.windows? && 33 | Overcommit::Utils::Version.new(RUBY_VERSION) >= '3' && 34 | Overcommit::Utils::Version.new(RUBY_VERSION) < '3.1' 35 | it 'does not hang' do 36 | result = Timeout.timeout(5) { subject } 37 | result.stderr.should_not include 'No live threads left. Deadlock?' 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/integration/resolving_merge_conflict_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'resolving merge conflicts' do 6 | subject { shell(%w[git commit -m Test -i some-file]) } 7 | 8 | around do |example| 9 | repo do 10 | echo('Master', 'some-file') 11 | `git add some-file` 12 | `git commit -m "Add some-file"` 13 | `git checkout -q -b branch1` 14 | echo('Branch 1 Addition', 'some-file') 15 | `git add some-file` 16 | `git commit -m "Add Branch 1 addition"` 17 | `git checkout -q master` 18 | `git checkout -q -b branch2` 19 | echo('Branch 2 Addition', 'some-file') 20 | `git add some-file` 21 | `git commit -m "Add Branch 2 addition"` 22 | `git checkout -q master` 23 | `git merge branch1` 24 | `git merge branch2` # Results in merge conflict 25 | `overcommit --install > #{File::NULL}` 26 | echo('Conflicts Resolved', 'some-file') 27 | `git add some-file` 28 | example.run 29 | end 30 | end 31 | 32 | it 'exits successfully' do 33 | subject.status.should == 0 34 | end 35 | 36 | it 'does not display an error about MERGE_HEAD missing' do 37 | subject.stderr.should_not include 'MERGE_HEAD' 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/integration/run_flag_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'overcommit --run' do 6 | subject { shell(%w[overcommit --run]) } 7 | 8 | context 'when using an existing pre-commit hook script' do 9 | if Overcommit::OS.windows? 10 | let(:script_name) { 'test-script.bat' } 11 | let(:script_contents) { 'exit 0' } 12 | else 13 | let(:script_name) { 'test-script' } 14 | let(:script_contents) { "#!/bin/bash\nexit 0" } 15 | end 16 | let(:script_path) { ".#{Overcommit::OS::SEPARATOR}#{script_name}" } 17 | 18 | let(:config) do 19 | { 20 | 'PreCommit' => { 21 | 'MyHook' => { 22 | 'enabled' => true, 23 | 'required_executable' => script_path, 24 | } 25 | } 26 | } 27 | end 28 | 29 | around do |example| 30 | repo do 31 | File.open('.overcommit.yml', 'w') { |f| f.puts(config.to_yaml) } 32 | echo(script_contents, script_path) 33 | `git add #{script_path}` 34 | FileUtils.chmod(0o755, script_path) 35 | example.run 36 | end 37 | end 38 | 39 | it 'completes successfully without blocking' do 40 | wait_until(timeout: 10) { subject } # Need to wait long time for JRuby startup 41 | subject.status.should == 0 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/integration/template_dir_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'fileutils' 5 | 6 | describe 'template directory' do 7 | let(:template_dir) { File.join(Overcommit::HOME, 'template-dir') } 8 | let(:hooks_dir) { File.join(template_dir, 'hooks') } 9 | 10 | it 'contains a hooks directory' do 11 | File.directory?(hooks_dir).should == true 12 | end 13 | 14 | describe 'the hooks directory' do 15 | it 'contains the master hook as an actual file with content' do 16 | master_hook = File.join(hooks_dir, 'overcommit-hook') 17 | File.exist?(master_hook).should == true 18 | File.size?(master_hook).should > 0 19 | Overcommit::Utils::FileUtils.symlink?(master_hook).should == false 20 | end 21 | 22 | it 'contains all other hooks as copies of the master hook' do 23 | Overcommit::Utils.supported_hook_types.each do |hook_type| 24 | FileUtils.compare_file(File.join(hooks_dir, hook_type), 25 | File.join(hooks_dir, 'overcommit-hook')).should == true 26 | end 27 | end 28 | 29 | it 'contains no symlinks' do 30 | Overcommit::Utils.supported_hook_types.each do |hook_type| 31 | Overcommit::Utils::FileUtils.symlink?(File.join(hooks_dir, hook_type)).should == false 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/overcommit/hook/commit_msg/empty_message_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::CommitMsg::EmptyMessage do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | context.stub(:empty_message?).and_return(commit_msg.strip.empty?) 12 | end 13 | 14 | context 'when commit message is empty' do 15 | let(:commit_msg) { '' } 16 | 17 | it { should fail_hook } 18 | end 19 | 20 | context 'when commit message contains only whitespace' do 21 | let(:commit_msg) { ' ' } 22 | 23 | it { should fail_hook } 24 | end 25 | 26 | context 'when commit message is not empty' do 27 | let(:commit_msg) { 'Some commit message' } 28 | 29 | it { should pass } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/commit_msg/hard_tabs_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::CommitMsg::HardTabs do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | context.stub(:commit_message).and_return(commit_msg) 12 | context.stub(:empty_message?).and_return(commit_msg.empty?) 13 | end 14 | 15 | context 'when commit message is empty' do 16 | let(:commit_msg) { '' } 17 | 18 | it { should pass } 19 | end 20 | 21 | context 'when message contains hard tabs' do 22 | let(:commit_msg) { "This is a hard-tab\tcommit message" } 23 | 24 | it { should warn } 25 | end 26 | 27 | context 'when message does not contain hard tabs' do 28 | let(:commit_msg) { 'No hard tabs to be found' } 29 | 30 | it { should pass } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/overcommit/hook/commit_msg/russian_novel_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::CommitMsg::RussianNovel do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | context.stub(:commit_message_lines).and_return(commit_msg) 12 | end 13 | 14 | context 'when message contains fewer than 30 lines' do 15 | let(:commit_msg) { ['A single line'] * 10 } 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when message contains at least 30 lines' do 21 | let(:commit_msg) { ['A single line'] * 30 } 22 | 23 | it { should warn } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/overcommit/hook/commit_msg/single_line_subject_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::CommitMsg::SingleLineSubject do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | context.stub(:commit_message_lines).and_return(commit_msg.split("\n")) 12 | context.stub(:empty_message?).and_return(commit_msg.empty?) 13 | end 14 | 15 | context 'when commit message is empty' do 16 | let(:commit_msg) { '' } 17 | 18 | it { should pass } 19 | end 20 | 21 | context 'when subject is separated from body by a blank line' do 22 | let(:commit_msg) { <<-MSG } 23 | Initial commit 24 | 25 | Mostly cats so far. 26 | MSG 27 | 28 | it { should pass } 29 | end 30 | 31 | context 'when subject is not kept to one line' do 32 | let(:commit_msg) { <<-MSG } 33 | Initial commit where I forget about commit message 34 | standards and decide to hard-wrap my subject 35 | 36 | Still mostly cats so far. 37 | MSG 38 | 39 | it { should warn } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/overcommit/hook/commit_msg/trailing_period_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::CommitMsg::TrailingPeriod do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | context.stub(:commit_message_lines).and_return(commit_msg.split("\n")) 12 | context.stub(:empty_message?).and_return(commit_msg.empty?) 13 | end 14 | 15 | context 'when commit message is empty' do 16 | let(:commit_msg) { '' } 17 | 18 | it { should pass } 19 | end 20 | 21 | context 'when subject contains a trailing period' do 22 | let(:commit_msg) { 'This subject has a period.' } 23 | 24 | it { should warn } 25 | end 26 | 27 | context 'when subject does not contain a trailing period' do 28 | let(:commit_msg) { 'This subject has no period' } 29 | 30 | it { should pass } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/bower_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::BowerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bower install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bower install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 27 | bower EMALFORMED Failed to read bower.json 28 | OUT 29 | end 30 | 31 | it { should fail_hook } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/bundle_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::BundleInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bundle install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bundle install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/composer_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::ComposerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when composer install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when composer install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Composer could not find a composer.json file') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/index_tags_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::IndexTags do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:execute_in_background) 12 | end 13 | 14 | it { should pass } 15 | end 16 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/npm_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::NpmInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when npm install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when npm install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: "npm ERR! install Couldn't read dependencies") 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_checkout/yarn_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCheckout::YarnInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when yarn install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when yarn install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: %{error An unexpected error occurred: ...}) 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/bower_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::BowerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bower install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bower install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 27 | bower EMALFORMED Failed to read bower.json 28 | OUT 29 | end 30 | 31 | it { should fail_hook } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/bundle_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::BundleInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bundle install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bundle install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/commitplease_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::Commitplease do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when commitplease exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | result.stub(:stderr).and_return('') 20 | end 21 | 22 | it { should pass } 23 | end 24 | 25 | context 'when commitplease exits unsuccessfully' do 26 | before do 27 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 28 | - First line must be (): 29 | Need an opening parenthesis: ( 30 | OUT 31 | end 32 | 33 | it { should fail_hook } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/composer_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::ComposerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when composer install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when composer install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Composer could not find a composer.json file') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/index_tags_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::IndexTags do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:execute_in_background) 12 | end 13 | 14 | it { should pass } 15 | end 16 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/npm_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::NpmInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when npm install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when npm install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: "npm ERR! install Couldn't read dependencies") 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_commit/yarn_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostCommit::YarnInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when yarn install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when yarn install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: %{error An unexpected error occurred: ...}) 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/bower_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::BowerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bower install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bower install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 27 | bower EMALFORMED Failed to read bower.json 28 | OUT 29 | end 30 | 31 | it { should fail_hook } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/bundle_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::BundleInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bundle install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bundle install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/composer_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::ComposerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when composer install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when composer install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Composer could not find a composer.json file') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/index_tags_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::IndexTags do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:execute_in_background) 12 | end 13 | 14 | it { should pass } 15 | end 16 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/npm_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::NpmInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when npm install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when npm install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: "npm ERR! install Couldn't read dependencies") 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_merge/yarn_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::YarnInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when yarn install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when yarn install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: %{error An unexpected error occurred: ...}) 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/bower_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostRewrite::BowerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bower install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bower install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 27 | bower EMALFORMED Failed to read bower.json 28 | OUT 29 | end 30 | 31 | it { should fail_hook } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/bundle_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostRewrite::BundleInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when bundle install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when bundle install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/composer_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostRewrite::ComposerInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when composer install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when composer install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stdout: 'Composer could not find a composer.json file') 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/index_tags_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostMerge::IndexTags do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:execute_in_background) 12 | end 13 | 14 | it { should pass } 15 | end 16 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/npm_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostRewrite::NpmInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when npm install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when npm install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: "npm ERR! install Couldn't read dependencies") 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/post_rewrite/yarn_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PostRewrite::YarnInstall do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:execute).and_return(result) 14 | end 15 | 16 | context 'when yarn install exits successfully' do 17 | before do 18 | result.stub(:success?).and_return(true) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when yarn install exits unsuccessfully' do 25 | before do 26 | result.stub(success?: false, stderr: %{error An unexpected error occurred: ...}) 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::BrokenSymlinks do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | let(:staged_file) { 'staged-file.txt' } 9 | subject { described_class.new(config, context) } 10 | 11 | before do 12 | subject.stub(:applicable_files).and_return([staged_file]) 13 | end 14 | 15 | before do 16 | Overcommit::Utils.stub(:broken_symlink?).with(staged_file).and_return(broken) 17 | end 18 | 19 | context 'when the symlink is broken' do 20 | let(:broken) { true } 21 | 22 | it { should fail_hook } 23 | end 24 | 25 | context 'when the symlink is not broken' do 26 | let(:broken) { false } 27 | 28 | it { should pass } 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/case_conflicts_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::CaseConflicts do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | Overcommit::GitRepo.stub(:initial_commit?).and_return(false) 12 | Overcommit::GitRepo.stub(:list_files).and_return(%w[foo]) 13 | end 14 | 15 | context 'when a new file conflicts with an existing file' do 16 | before do 17 | subject.stub(:applicable_files).and_return(%w[Foo]) 18 | end 19 | 20 | it { should fail_hook } 21 | end 22 | 23 | context 'when a new file conflicts with another new file' do 24 | before do 25 | subject.stub(:applicable_files).and_return(%w[bar Bar]) 26 | end 27 | 28 | it { should fail_hook } 29 | end 30 | 31 | context 'when there are no conflicts' do 32 | before do 33 | subject.stub(:applicable_files).and_return(%w[bar baz]) 34 | end 35 | 36 | it { should pass } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/chamber_compare_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::ChamberCompare do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(['my_settings.yml']) 12 | end 13 | 14 | context 'when chamber exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:stdout).and_return('') 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when chamber exits unsucessfully' do 25 | before do 26 | result = double('result') 27 | result.stub(:stdout).and_return('Some error message') 28 | subject.stub(:execute).and_return(result) 29 | end 30 | 31 | it { should warn } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/chamber_security_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::ChamberSecurity do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(['my_settings.yml']) 12 | end 13 | 14 | context 'when chamber exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:stdout).and_return('') 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when chamber exits unsucessfully' do 25 | before do 26 | result = double('result') 27 | result.stub(:stdout).and_return('Some error message') 28 | subject.stub(:execute).and_return(result) 29 | end 30 | 31 | it { should fail_hook } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/code_spell_check_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::CodeSpellCheck do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb]) 12 | end 13 | 14 | context 'when code-spell-check exists successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | result.stub(:stdout).and_return('') 19 | result.stub(:stderr).and_return('') 20 | subject.stub(:execute).and_return(result) 21 | end 22 | 23 | it { should pass } 24 | end 25 | 26 | context 'when code-spell-check exists unsuccessfully via standard error' do 27 | before do 28 | result = double('result') 29 | result.stub(:success?).and_return(false) 30 | result.stub(:stdout).and_return('') 31 | result.stub(:stderr).and_return( 32 | "file1.rb:35: inkorrectspelling\n✗ Errors in code spellchecking" 33 | ) 34 | subject.stub(:execute).and_return(result) 35 | end 36 | 37 | it { should fail_hook } 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/fix_me_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::FixMe do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | let(:staged_file) { 'filename.txt' } 10 | 11 | before do 12 | subject.stub(:applicable_files).and_return([staged_file]) 13 | end 14 | 15 | around do |example| 16 | repo do 17 | File.open(staged_file, 'w') { |f| f.write(contents) } 18 | `git add #{staged_file}` 19 | example.run 20 | end 21 | end 22 | 23 | context 'when file contains FIXME' do 24 | let(:contents) { 'eval(params[:q]) # FIXME maybe this is a bad idea?' } 25 | 26 | it { should warn } 27 | end 28 | 29 | context 'when file contains TODO with special chars around it' do 30 | let(:contents) { 'users = (1..1000).map { |i| User.find(1) } #TODO: make it better' } 31 | 32 | it { should warn } 33 | end 34 | 35 | context 'when file does not contain any FixMe words' do 36 | let(:contents) { 'if HACKY_CONSTANT.blank?' } 37 | 38 | it { should pass } 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/hard_tabs_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::HardTabs do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | let(:staged_file) { 'filename.txt' } 10 | 11 | before do 12 | subject.stub(:applicable_files).and_return([staged_file]) 13 | end 14 | 15 | around do |example| 16 | repo do 17 | File.open(staged_file, 'w') { |f| f.write(contents) } 18 | `git add #{staged_file}` 19 | example.run 20 | end 21 | end 22 | 23 | context 'when file contains hard tabs' do 24 | let(:contents) { "Some\thard\ttabs" } 25 | 26 | it { should fail_hook } 27 | end 28 | 29 | context 'when file has no hard tabs' do 30 | let(:contents) { 'Just some text' } 31 | 32 | it { should pass } 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/html_hint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::HtmlHint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.html file2.html]) 12 | end 13 | 14 | context 'when htmlhint exits successfully' do 15 | let(:result) { double('result') } 16 | 17 | before do 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | context 'with no errors' do 22 | before do 23 | result.stub(:stdout).and_return('') 24 | end 25 | 26 | it { should pass } 27 | end 28 | 29 | context 'and it reports an error' do 30 | before do 31 | result.stub(:stdout).and_return([ 32 | 'file1.html:', 33 | "\tline 355, col 520: \e[31mId redefinition of [ stats ].\e[39m", 34 | '', 35 | '', 36 | '1 problem.' 37 | ].join("\n")) 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/js_lint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::JsLint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.js file2.js]) 12 | end 13 | 14 | context 'when jslint exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when jslint exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stdout).and_return([ 35 | "file1.js:1:3: Expected ']' at column 9, not column 3." 36 | ].join("\n")) 37 | end 38 | 39 | it { should fail_hook } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/json_syntax_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'json' 5 | 6 | describe Overcommit::Hook::PreCommit::JsonSyntax do 7 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 8 | let(:context) { double('context') } 9 | let(:staged_file) { 'my_file.json' } 10 | 11 | subject { described_class.new(config, context) } 12 | 13 | before do 14 | subject.stub(:applicable_files).and_return([staged_file]) 15 | end 16 | 17 | around do |example| 18 | repo do 19 | touch staged_file 20 | `git add #{staged_file}` 21 | example.run 22 | end 23 | end 24 | 25 | context 'when JSON files have no errors' do 26 | before do 27 | JSON.stub(:parse) 28 | end 29 | 30 | it { should pass } 31 | end 32 | 33 | context 'when JSON file has errors' do 34 | before do 35 | JSON.stub(:parse).and_raise(JSON::ParserError) 36 | end 37 | 38 | it { should fail_hook } 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/kt_lint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::KtLint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.kt file2.kt]) 12 | end 13 | 14 | context 'when KtLint exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when KtLint exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stdout).and_return([ 35 | 'file1.kt:12:10: error message' 36 | ].join("\n")) 37 | end 38 | 39 | it { should fail_hook } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/license_finder_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::LicenseFinder do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when license_finder exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when license_finder runs unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | result.stub(:stderr).and_return('') 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | it { should fail_hook 'Some error message' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::MergeConflicts do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | let(:staged_file) { 'filename.txt' } 9 | 10 | subject { described_class.new(config, context) } 11 | 12 | before do 13 | subject.stub(:applicable_files).and_return([staged_file]) 14 | end 15 | 16 | around do |example| 17 | repo do 18 | File.open(staged_file, 'w') { |f| f.write(contents) } 19 | `git add #{staged_file}` 20 | example.run 21 | end 22 | end 23 | 24 | context 'when file contains a merge conflict marker' do 25 | let(:contents) { "Just\n<<<<<<< HEAD:filename.txt\nconflicting text" } 26 | 27 | it { should fail_hook } 28 | end 29 | 30 | context 'when file does not have any merge conflict markers' do 31 | let(:contents) { 'Just some text' } 32 | 33 | it { should pass } 34 | end 35 | 36 | context "when file contains characters that aren't conflict markers" do 37 | let(:contents) { 'Just some <<<<<<< arrows' } 38 | 39 | it { should pass } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/nginx_test_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::NginxTest do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | subject.stub(:applicable_files).and_return(%w[nginx.conf]) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | context 'when nginx -t exits successfully' do 18 | before do 19 | result.stub(:success?).and_return(true) 20 | end 21 | 22 | it { should pass } 23 | end 24 | 25 | context 'when nginx -t exits unsuccessfully' do 26 | let(:result) { double('result') } 27 | 28 | before do 29 | result.stub(success?: false, stderr: normalize_indent(<<-OUT)) 30 | nginx: [emerg] unknown directive "erver" in nginx.conf:2 31 | nginx: configuration file nginx.conf test failed 32 | OUT 33 | end 34 | 35 | it { should fail_hook } 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/pep257_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::Pep257 do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.py file2.py]) 12 | end 13 | 14 | context 'when pep257 exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when pep257 exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stderr).and_return([ 35 | 'file1.py:1 in public method `foo`:', 36 | ' D102: Docstring missing' 37 | ].join("\n")) 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/pydocstyle_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::Pydocstyle do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.py file2.py]) 12 | end 13 | 14 | context 'when pydocstyle exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when pydocstyle exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stderr).and_return([ 35 | 'file1.py:1 in public method `foo`:', 36 | ' D102: Docstring missing' 37 | ].join("\n")) 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/rst_lint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::RstLint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | let(:result) { double('result') } 11 | 12 | before do 13 | result.stub(success?: success, stdout: stdout, stderr: stderr) 14 | subject.stub(:applicable_files).and_return(%w[file1.rst file2.rst]) 15 | subject.stub(:execute).and_return(result) 16 | end 17 | 18 | context 'when rst-lint exits successfully' do 19 | let(:success) { true } 20 | let(:stdout) { '' } 21 | let(:stderr) { '' } 22 | 23 | it { should pass } 24 | end 25 | 26 | context 'when rst-lint exits unsuccessfully' do 27 | let(:success) { false } 28 | 29 | context 'and it reports an error' do 30 | let(:stdout) { 'WARNING file1.rst:7 Title underline too short.' } 31 | let(:stderr) { '' } 32 | 33 | it { should fail_hook } 34 | end 35 | 36 | context 'when there is an error running rst-lint' do 37 | let(:stdout) { '' } 38 | let(:stderr) { 'Some runtime error' } 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/semi_standard_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::SemiStandard do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.js file2.js]) 12 | end 13 | 14 | context 'when semistandard exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(success?: true, stdout: '') 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when semistandard exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stdout).and_return([ 35 | 'semistandard: Use Semicolons For All! (https://github.com/Flet/semistandard)', 36 | ' file1.js:1:1: Extra semicolon. (eslint/semi)' 37 | ].join("\n")) 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/standard_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::Standard do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.js file2.js]) 12 | end 13 | 14 | context 'when standard exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(success?: true, stdout: '') 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when standard exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stdout).and_return([ 35 | 'standard: Use JavaScript Standard Style (https://github.com/feross/standard)', 36 | ' file1.js:1:1: Extra semicolon. (eslint/semi)' 37 | ].join("\n")) 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/swift_lint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::SwiftLint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.swift file2.swift]) 12 | end 13 | 14 | context 'when SwiftLint exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when SwiftLint exits unsucessfully' do 25 | let(:result) { double('result') } 26 | 27 | before do 28 | result.stub(:success?).and_return(false) 29 | subject.stub(:execute).and_return(result) 30 | end 31 | 32 | context 'and it reports an error' do 33 | before do 34 | result.stub(:stdout).and_return([ 35 | 'file1.swift:12: warning: message: details' 36 | ].join("\n")) 37 | end 38 | 39 | it { should fail_hook } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/terraform_format_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::TerraformFormat do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(%w[file1.tf file2.tf]) 12 | end 13 | 14 | context 'when Terraform exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when Terraform exits unsucessfully' do 25 | let(:result_ok) { double('result') } 26 | let(:result_bad) { double('result') } 27 | let(:cmdline) { %w[terraform fmt -check=true -diff=false] } 28 | 29 | before do 30 | result_ok.stub(:success?).and_return(true) 31 | result_bad.stub(:success?).and_return(false) 32 | subject.stub(:execute).with(cmdline, args: ['file1.tf']).and_return(result_ok) 33 | subject.stub(:execute).with(cmdline, args: ['file2.tf']).and_return(result_bad) 34 | end 35 | 36 | it { should fail_hook } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::TrailingWhitespace do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | let(:staged_file) { 'filename.txt' } 10 | 11 | before do 12 | subject.stub(:applicable_files).and_return([staged_file]) 13 | end 14 | 15 | around do |example| 16 | repo do 17 | File.open(staged_file, 'w') { |f| f.write(contents) } 18 | `git add #{staged_file}` 19 | example.run 20 | end 21 | end 22 | 23 | context 'when file contains trailing whitespace' do 24 | let(:contents) { 'Some trailing whitespace ' } 25 | 26 | it { should fail_hook } 27 | end 28 | 29 | context 'when file contains trailing tabs' do 30 | let(:contents) { "Some trailing tabs\t\t" } 31 | 32 | it { should fail_hook } 33 | end 34 | 35 | context 'when file has no invalid whitespace' do 36 | let(:contents) { 'Just some text' } 37 | 38 | it { should pass } 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/travis_lint_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::TravisLint do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | before do 11 | subject.stub(:applicable_files).and_return(['.travis.yml']) 12 | end 13 | 14 | context 'when travis-lint exits successfully' do 15 | before do 16 | result = double('result') 17 | result.stub(:success?).and_return(true) 18 | subject.stub(:execute).and_return(result) 19 | end 20 | 21 | it { should pass } 22 | end 23 | 24 | context 'when travis-lint exits unsucessfully' do 25 | before do 26 | result = double('result') 27 | result.stub(:success?).and_return(false) 28 | result.stub(:stdout).and_return('Some error message') 29 | result.stub(:stderr).and_return('') 30 | subject.stub(:execute).and_return(result) 31 | end 32 | 33 | it { should fail_hook 'Some error message' } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/xml_syntax_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'rexml/document' 5 | 6 | describe Overcommit::Hook::PreCommit::XmlSyntax do 7 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 8 | let(:context) { double('context') } 9 | subject { described_class.new(config, context) } 10 | let(:staged_file) { 'file1.xml' } 11 | 12 | before do 13 | IO.stub(:read).with(staged_file) 14 | subject.stub(:applicable_files).and_return([staged_file]) 15 | end 16 | 17 | context 'when XML files have no errors' do 18 | before do 19 | REXML::Document.stub(:new) 20 | end 21 | 22 | it { should pass } 23 | end 24 | 25 | context 'when XML file has errors' do 26 | before do 27 | REXML::Document.stub(:new).and_raise(REXML::ParseException.new('')) 28 | end 29 | 30 | it { should fail_hook } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PreCommit::YamlSyntax do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | let(:staged_file) { 'file1.yml' } 10 | 11 | before do 12 | subject.stub(:applicable_files).and_return([staged_file]) 13 | end 14 | 15 | context 'when YAML files have no errors' do 16 | before do 17 | YAML.stub(:load_file) 18 | end 19 | 20 | it { should pass } 21 | end 22 | 23 | context 'when YAML file has errors' do 24 | before do 25 | YAML.stub(:load_file).with(staged_file, { aliases: true }).and_raise(ArgumentError) 26 | YAML.stub(:load_file).with(staged_file).and_raise(ArgumentError) 27 | end 28 | 29 | it { should fail_hook } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/brakeman_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::Brakeman do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when brakeman exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when brakeman exits unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | subject.stub(:execute).and_return(result) 26 | end 27 | 28 | it { should fail_hook 'Some error message' } 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/minitest_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::Minitest do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context', all_files: ['test/test_foo.rb']) } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when minitest exits successfully' do 11 | let(:result) { double('result') } 12 | 13 | before do 14 | result.stub(:success?).and_return(true) 15 | subject.stub(:execute).and_return(result) 16 | end 17 | 18 | it { should pass } 19 | end 20 | 21 | context 'when minitest exits unsuccessfully' do 22 | let(:result) { double('result') } 23 | 24 | before do 25 | result.stub(:success?).and_return(false) 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | context 'with a runtime error' do 30 | before do 31 | result.stub(stdout: '', stderr: <<-MSG) 32 | 1) Error: 33 | FooTest#test_: foo should bar. : 34 | RuntimeError: 35 | test/model/foo_test.rb:1:in `block (2 levels) in ' 36 | MSG 37 | end 38 | 39 | it { should fail_hook } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/mix_test_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::MixTest do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when mix test exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when mix test exits unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | result.stub(:stderr).and_return('') 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | it { should fail_hook 'Some error message' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/php_unit_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::PhpUnit do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when phpunit exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when phpunit exits unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | result.stub(:stderr).and_return('') 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | it { should fail_hook 'Some error message' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/pytest_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::Pytest do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when pytest exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when pytest exits unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | result.stub(:stderr).and_return('') 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | it { should fail_hook 'Some error message' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/python_nose_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::PythonNose do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context') } 8 | subject { described_class.new(config, context) } 9 | 10 | context 'when nose exits successfully' do 11 | before do 12 | result = double('result') 13 | result.stub(:success?).and_return(true) 14 | subject.stub(:execute).and_return(result) 15 | end 16 | 17 | it { should pass } 18 | end 19 | 20 | context 'when nose exits unsucessfully' do 21 | before do 22 | result = double('result') 23 | result.stub(:success?).and_return(false) 24 | result.stub(:stdout).and_return('Some error message') 25 | result.stub(:stderr).and_return('') 26 | subject.stub(:execute).and_return(result) 27 | end 28 | 29 | it { should fail_hook 'Some error message' } 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/overcommit/hook/pre_push/test_unit_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::Hook::PrePush::TestUnit do 6 | let(:config) { Overcommit::ConfigurationLoader.default_configuration } 7 | let(:context) { double('context', all_files: ['test/foo_test.rb']) } 8 | 9 | subject { described_class.new(config, context) } 10 | 11 | context 'when test-unit exits successfully' do 12 | let(:result) { double('result') } 13 | 14 | before do 15 | result.stub(:success?).and_return(true) 16 | subject.stub(:execute).and_return(result) 17 | end 18 | 19 | it { should pass } 20 | end 21 | 22 | context 'when test-unit exits unsuccessfully' do 23 | let(:result) { double('result') } 24 | 25 | before do 26 | result.stub(:success?).and_return(false) 27 | subject.stub(:execute).and_return(result) 28 | end 29 | 30 | context 'with a runtime error' do 31 | before do 32 | result.stub(stdout: '', stderr: <<-MSG) 33 | 1) Error: 34 | FooTest#test_: foo should bar. : 35 | RuntimeError: 36 | test/model/foo_test.rb:1:in `block (2 levels) in ' 37 | MSG 38 | end 39 | 40 | it { should fail_hook } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/overcommit/hook_context/base_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Overcommit::HookContext::Base do 6 | let(:config) { double('config') } 7 | let(:args) { [] } 8 | let(:input) { double('input') } 9 | let(:context) { described_class.new(config, args, input) } 10 | 11 | describe '#hook_class_name' do 12 | subject { context.hook_class_name } 13 | 14 | it 'returns the short class name of the context' do 15 | subject.should == 'Base' 16 | end 17 | end 18 | 19 | describe '#input_lines' do 20 | subject { context.input_lines } 21 | 22 | before do 23 | input.stub(:read).and_return("line 1\nline 2\n") 24 | end 25 | 26 | it { should == ['line 1', 'line 2'] } 27 | end 28 | 29 | describe '#post_fail_message' do 30 | subject { context.post_fail_message } 31 | 32 | it { should be_nil } 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/overcommit/hook_context/prepare_commit_msg_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'overcommit/hook_context/prepare_commit_msg' 5 | 6 | describe Overcommit::HookContext::PrepareCommitMsg do 7 | let(:config) { double('config') } 8 | let(:args) { [commit_message_filename, commit_message_source] } 9 | let(:commit_message_filename) { 'message-template.txt' } 10 | let(:commit_message_source) { :file } 11 | let(:commit) { 'SHA-1 here' } 12 | let(:input) { double('input') } 13 | let(:context) { described_class.new(config, args, input) } 14 | 15 | describe '#commit_message_filename' do 16 | subject { context.commit_message_filename } 17 | 18 | it { should == commit_message_filename } 19 | end 20 | 21 | describe '#commit_message_source' do 22 | subject { context.commit_message_source } 23 | 24 | it { should == commit_message_source } 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/support/normalize_indent.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Strips off excess leading indentation from each line so we can use Heredocs 4 | # for writing code without having the leading indentation count. 5 | module IndentNormalizer 6 | def normalize_indent(code) 7 | leading_indent = code[/^(\s*)/, 1] 8 | code.lstrip.gsub(/\n#{leading_indent}/, "\n") 9 | end 10 | end 11 | 12 | RSpec.configure do |_config| 13 | include IndentNormalizer 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/output_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Helpers for capturing output streams in tests. 4 | module OutputHelpers 5 | module_function 6 | 7 | def capture_stdout 8 | original = $stdout 9 | $stdout = output = StringIO.new 10 | 11 | yield 12 | 13 | output.string 14 | ensure 15 | $stdout = original 16 | end 17 | end 18 | --------------------------------------------------------------------------------