diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..8cf8b4a0
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,30 @@
+name: Lint
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ overcommit:
+ timeout-minutes: 10
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: 3.3
+ bundler-cache: true
+
+ - name: Prepare environment
+ run: |
+ git config --global user.email "gh-actions@example.com"
+ git config --global user.name "GitHub Actions"
+ bundle exec overcommit --sign
+ bundle exec overcommit --sign pre-commit
+
+ - name: Run pre-commit checks
+ run: bundle exec overcommit --run
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..0e2aac20
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,61 @@
+name: Tests
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ rspec:
+ timeout-minutes: 15
+ runs-on: ${{ matrix.os }}-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ ruby-version:
+ - "2.6"
+ - "2.7"
+ - "3.0"
+ - "3.1"
+ - "3.2"
+ - "3.3"
+ os:
+ - ubuntu
+ # At the moment of this commit various specs fail on Windows.
+ # Any contributor is welcome to fix them and enable the Windows build.
+ # Please see Issue #836 for more details.
+ # - windows
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Ruby ${{ matrix.ruby-version }}
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby-version }}
+ bundler-cache: true
+
+ - name: Run tests
+ run: |
+ git config --global user.email "gh-actions@example.com"
+ git config --global user.name "GitHub Actions"
+ bundle exec rspec
+
+ - name: Code coverage reporting
+ uses: coverallsapp/github-action@v2
+ with:
+ github-token: ${{ secrets.github_token }}
+ flag-name: ruby${{ matrix.ruby-version }}-${{ matrix.os }}
+ parallel: true
+
+ finish:
+ needs: rspec
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Finalize code coverage report
+ uses: coverallsapp/github-action@v2
+ with:
+ github-token: ${{ secrets.github_token }}
+ parallel-finished: true
diff --git a/.projections.json b/.projections.json
deleted file mode 100644
index 24f5ce34..00000000
--- a/.projections.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "lib/overcommit/*.rb": {
- "alternate": "spec/overcommit/{}_spec.rb",
- "type": "source"
- },
- "spec/overcommit/*_spec.rb": {
- "alternate": "lib/overcommit/{}.rb",
- "type": "test"
- }
-}
diff --git a/.rubocop.yml b/.rubocop.yml
index 56197f7d..1b4e7ad7 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,5 +1,9 @@
+inherit_from: .rubocop_todo.yml
+
AllCops:
- TargetRubyVersion: 2.4
+ TargetRubyVersion: 2.6
+ NewCops: disable
+ SuggestExtensions: false
Layout/ClosingParenthesisIndentation:
Enabled: false
@@ -14,12 +18,15 @@ Layout/EndOfLine:
Layout/FirstParameterIndentation:
Enabled: false
-Layout/IndentArray:
+Layout/FirstArrayElementIndentation:
Enabled: false
-Layout/IndentHeredoc:
+Layout/HeredocIndentation:
Enabled: false
+Layout/LineLength:
+ Max: 100
+
Layout/MultilineMethodCallIndentation:
Enabled: false
@@ -48,9 +55,6 @@ Metrics/AbcSize:
Metrics/BlockLength:
Enabled: false
-Metrics/LineLength:
- Max: 100
-
Metrics/MethodLength:
Max: 20
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 00000000..53df9159
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,137 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit`
+# on 2024-01-10 14:09:00 UTC using RuboCop version 1.59.0.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowAliasSyntax, AllowedMethods.
+# AllowedMethods: alias_method, public, protected, private
+Layout/EmptyLinesAroundAttributeAccessor:
+ Exclude:
+ - 'lib/overcommit/hook_context/post_merge.rb'
+
+# Offense count: 6
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: enums
+Lint/ConstantDefinitionInBlock:
+ Exclude:
+ - 'spec/overcommit/message_processor_spec.rb'
+
+# Offense count: 4
+Lint/MixedRegexpCaptureTypes:
+ Exclude:
+ - 'lib/overcommit/hook/pre_commit/dart_analyzer.rb'
+ - 'lib/overcommit/hook/pre_commit/java_checkstyle.rb'
+ - 'lib/overcommit/hook/pre_commit/kt_lint.rb'
+ - 'lib/overcommit/hook/pre_commit/scalastyle.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Lint/RedundantCopDisableDirective:
+ Exclude:
+ - 'lib/overcommit/hook_runner.rb'
+ - 'lib/overcommit/printer.rb'
+
+# Offense count: 1
+# Configuration parameters: CountComments, Max, CountAsOne.
+Metrics/ClassLength:
+ Exclude:
+ - 'lib/overcommit/utils.rb'
+
+# Offense count: 2
+# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
+Metrics/CyclomaticComplexity:
+ Exclude:
+ - 'lib/overcommit/configuration.rb'
+ - 'lib/overcommit/hook_runner.rb'
+
+# Offense count: 3
+# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
+Metrics/PerceivedComplexity:
+ Exclude:
+ - 'lib/overcommit/configuration.rb'
+ - 'lib/overcommit/configuration_validator.rb'
+ - 'lib/overcommit/hook_runner.rb'
+
+# Offense count: 23
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/GlobalStdStream:
+ Exclude:
+ - 'bin/overcommit'
+ - 'lib/overcommit/hook/post_commit/git_guilt.rb'
+ - 'template-dir/hooks/commit-msg'
+ - 'template-dir/hooks/overcommit-hook'
+ - 'template-dir/hooks/post-checkout'
+ - 'template-dir/hooks/post-commit'
+ - 'template-dir/hooks/post-merge'
+ - 'template-dir/hooks/post-rewrite'
+ - 'template-dir/hooks/pre-commit'
+ - 'template-dir/hooks/pre-push'
+ - 'template-dir/hooks/pre-rebase'
+ - 'template-dir/hooks/prepare-commit-msg'
+
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/HashTransformValues:
+ Exclude:
+ - 'lib/overcommit/configuration.rb'
+ - 'lib/overcommit/configuration_validator.rb'
+
+# Offense count: 1
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: respond_to_missing?
+Style/OptionalBooleanParameter:
+ Exclude:
+ - 'lib/overcommit/logger.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantBegin:
+ Exclude:
+ - 'lib/overcommit/hook/prepare_commit_msg/replace_branch.rb'
+ - 'lib/overcommit/utils.rb'
+
+# Offense count: 10
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: SafeForConstants.
+Style/RedundantFetchBlock:
+ Exclude:
+ - 'lib/overcommit/configuration.rb'
+ - 'lib/overcommit/configuration_validator.rb'
+ - 'lib/overcommit/hook/base.rb'
+ - 'lib/overcommit/hook/pre_commit/chamber_verification.rb'
+ - 'lib/overcommit/logger.rb'
+ - 'spec/support/shell_helpers.rb'
+
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantRegexpEscape:
+ Exclude:
+ - 'lib/overcommit/configuration.rb'
+ - 'lib/overcommit/hook/pre_commit/php_cs.rb'
+ - 'lib/overcommit/hook/pre_commit/php_lint.rb'
+ - 'lib/overcommit/hook/pre_commit/php_stan.rb'
+
+# Offense count: 15
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Mode.
+Style/StringConcatenation:
+ Exclude:
+ - 'lib/overcommit/hook/pre_commit/bundle_check.rb'
+ - 'lib/overcommit/hook_runner.rb'
+ - 'lib/overcommit/message_processor.rb'
+ - 'spec/integration/gemfile_option_spec.rb'
+ - 'spec/overcommit/hook/commit_msg/text_width_spec.rb'
+ - 'spec/overcommit/hook/prepare_commit_msg/base_spec.rb'
+ - 'spec/overcommit/message_processor_spec.rb'
+ - 'spec/spec_helper.rb'
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/ZeroLengthPredicate:
+ Exclude:
+ - 'lib/overcommit/hook/pre_commit/ruby_syntax.rb'
diff --git a/.simplecov b/.simplecov
deleted file mode 100644
index 3cc40052..00000000
--- a/.simplecov
+++ /dev/null
@@ -1,6 +0,0 @@
-SimpleCov.start do
- add_filter 'bin/'
- add_filter 'libexec/'
- add_filter 'spec/'
- add_filter 'template-dir/'
-end
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f12366a7..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: ruby
-
-cache: bundler
-
-rvm:
- - 2.4
- - 2.5
- - 2.6
-
-before_script:
- - git config --global user.email "travis@travis.ci"
- - git config --global user.name "Travis CI"
-
-before_install:
- - gem update --system
- - gem install bundler
-
-script:
- - bundle exec rspec
- - bundle exec overcommit --sign
- - bundle exec overcommit --sign pre-commit
- - bundle exec overcommit --run
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1711cb54..ad1cb477 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,107 @@
# Overcommit Changelog
+## 0.67.1
+
+* Fix `set` gem dependency error when running with `--diff` flag
+
+## 0.67.0
+
+* Fix bug introduced in 0.65.0 that prevented `gemfile: false` from working correctly
+
+## 0.66.0
+
+* Add `--diff` CLI option for running pre-commit hooks against only changed files
+
+## 0.65.0
+
+* Load bundled gems on expected version
+
+## 0.64.1
+
+* Update minimum version of rexml to address [CVE-2024-49761](https://www.ruby-lang.org/en/news/2024/10/28/redos-rexml-cve-2024-49761/)
+
+## 0.64.0
+
+* Add support for `stylelint` 16+
+* Add `changelog_uri` to gemspec
+
+## 0.63.0
+
+* Add `Sorbet` pre-commit hook
+* Add `RSpec` pre-commit hook
+
+## 0.62.0
+
+* Allow version 5 of `childprocess` gem dependency
+
+## 0.61.0
+
+* Allow `ReplaceBranch` to use `skip_if`
+* Fix local Overcommit file merges with existing `.overcommit.yml`
+
+## 0.60.0
+
+* Allow overriding `Gemfile.lock` location for `BundleCheck` pre-commit hook
+* Fix `ReplaceBranch` prepare-commit-msg hook to allow trailing spaces
+* Add `MixFormat` pre-commit hook
+* Add `MixTest` pre-push hook
+* Allow loading custom local configuration from `.local-overcommit.yml`
+* Handle `Psych::DisallowedClass` when running `YamlSyntax` pre-commit hook
+* Add support for specifying custom `encoding` in `RailsSchemaUpToDate` pre-commit hook
+
+## 0.59.1
+
+* Remove `--disable-pending-cops` as default flag to `RuboCop` pre-commit hook.
+* Remove special handling of process output on Windows since it broke on Linux.
+
+## 0.59.0
+
+* Add `--disable-pending-cops` as default flag to `RuboCop` pre-commit hook to ignore non-existent cops. Requires RuboCop `0.82.0` or newer.
+* Fix deprecation warning for `Bundler.with_clean_env`.
+* Fix handling of some kinds of pronto errors in the `Pronto` hook.
+* Fix encoding of process output on Windows.
+* Add support for specifying hook type to `--run` flag.
+* Fix message regex parser for Stylelint.
+* Fix configuration loading on Ruby 3.1.
+* Fix `YamlSyntax` to support aliases when parsing.
+* Fix run output to explicitly flush partial logs.
+
+## 0.58.0
+
+* Add `rexml` dependency explicitly to support Ruby 3.0.
+* Add `DartAnalyzer` pre-commit hook to analyze Dart files.
+* Add `PubTest` and `FlutterTest` pre-push hooks to run `pub test` and `flutter test` for Dart projects, respectively.
+* Update `index-tags` script to support scanning only files tracked by Git.
+* Fix `EsLint` pre-commit hook to not report certain false positives.
+* Update `YamlLint` to `fail` the run instead of `warn` when errors are detected.
+* Update `YamlLint` parse the line number of output so it is line aware.
+* Gracefully handle breaking behavior in upstream Psych gem to support YAML aliases.
+* Fix case where `git` would delete all tracked files when popping stash.
+
+## 0.57.0
+
+* Fix `CommitMsg` hooks to be able to call `modified_lines_in_file`.
+* Add `ErbLint` pre-commit hook to lint ERB files.
+
+## 0.56.0
+
+* Update `ReplaceBranch` prepare-commit-msg hook to avoid running on `--amend` by default.
+* Add support for `modified_files` and `modified_lines_in_file` in `CommitMsg` hooks.
+
+## 0.55.0
+
+* Fix `GoFmt` to not be enabled by default. This was enabled by mistake when introduced in Overcommit `0.52.0`.
+
+## 0.54.1
+
+* Fix `Overcommit::GitRepo.list_files` helper to work with arbitrarily large lists of files.
+* Fix `AuthorName` to allow mononyms to be more inclusive of names.
+
+## 0.54.0
+
+* Fix `YamlLint` pre-commit hook
+* Relax `childprocess` gem version constraint to allow version 4.x
+
## 0.53.0
* Improve performance in `PhpCs` pre-commit hook
diff --git a/Gemfile b/Gemfile
index 961e5d5a..49dc7923 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,10 +8,14 @@ gemspec
gem 'rspec', '~> 3.0'
-# Generate coverage information in Travis builds
-gem 'coveralls', '~> 0.8'
+gem 'simplecov', '~> 0.21.0'
+gem 'simplecov-lcov', '~> 0.8.0'
-# Pin RuboCop for Travis builds.
-gem 'rubocop', '0.54.0'
+# Pin RuboCop for CI builds
+if RUBY_VERSION < '2.7.0'
+ gem 'rubocop', '1.50.0'
+else
+ gem 'rubocop', '1.59.0'
+end
gem 'ffi' if Gem.win_platform?
diff --git a/README.md b/README.md
index af718032..a0d46726 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,8 @@
[](https://badge.fury.io/rb/overcommit)
-[](https://travis-ci.org/sds/overcommit)
-[](https://ci.appveyor.com/project/sds/overcommit)
-[](https://coveralls.io/github/sds/overcommit?branch=master)
+[](https://github.com/sds/overcommit/actions/workflows/tests.yml/badge.svg?branch=main)
+[](https://coveralls.io/github/sds/overcommit?branch=main)
[](https://codeclimate.com/github/sds/overcommit/maintainability)
-[](http://inch-ci.org/github/sds/overcommit)
+[](http://inch-ci.org/github/sds/overcommit)
@@ -12,59 +11,60 @@
`overcommit` is a tool to manage and configure
[Git hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks).
-
-
In addition to supporting a wide variety of hooks that can be used across
multiple repositories, you can also define hooks specific to a repository which
are stored in source control. You can also easily
[add your existing hook scripts](#adding-existing-git-hooks) without writing
any Ruby code.
-* [Requirements](#requirements)
- * [Dependencies](#dependencies)
-* [Installation](#installation)
- * [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
-* [Usage](#usage)
-* [Continuous Integration](#continuous-integration)
-* [Configuration](#configuration)
- * [Hook Options](#hook-options)
- * [Hook Categories](#hook-categories)
- * [Gemfile](#gemfile)
- * [Plugin Directory](#plugin-directory)
- * [Signature Verification](#signature-verification)
-* [Built-In Hooks](#built-in-hooks)
- * [CommitMsg](#commitmsg)
- * [PostCheckout](#postcheckout)
- * [PostCommit](#postcommit)
- * [PostMerge](#postmerge)
- * [PostRewrite](#postrewrite)
- * [PreCommit](#precommit)
- * [PrePush](#prepush)
- * [PreRebase](#prerebase)
-* [Repo-Specific Hooks](#repo-specific-hooks)
- * [Adding Existing Git Hooks](#adding-existing-git-hooks)
-* [Security](#security)
-* [Contributing](#contributing)
-* [Community](#community)
-* [Changelog](#changelog)
-* [License](#license)
+- [Requirements](#requirements)
+ - [Windows](#windows)
+ - [Dependencies](#dependencies)
+- [Installation](#installation)
+ - [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
+- [Usage](#usage)
+ - [Skipping Hooks](#skipping-hooks)
+ - [Disabling Overcommit](#disabling-overcommit)
+ - [Disabling Colorized Output](#disabling-colorized-output)
+- [Continuous Integration](#continuous-integration)
+- [Configuration](#configuration)
+ - [Hook Options](#hook-options)
+ - [Hook Categories](#hook-categories)
+ - [The `ALL` Hook](#the-all-hook)
+ - [Gemfile](#gemfile)
+ - [Plugin Directory](#plugin-directory)
+ - [Quiet Hook Runs](#quiet-hook-runs)
+ - [Concurrency](#concurrency)
+ - [Signature Verification](#signature-verification)
+- [Built-In Hooks](#built-in-hooks)
+ - [CommitMsg](#commitmsg)
+ - [PostCheckout](#postcheckout)
+ - [PostCommit](#postcommit)
+ - [PostMerge](#postmerge)
+ - [PostRewrite](#postrewrite)
+ - [PreCommit](#precommit)
+ - [WARNING: pre-commit hooks cannot have side effects](#warning-pre-commit-hooks-cannot-have-side-effects)
+ - [PrePush](#prepush)
+ - [PreRebase](#prerebase)
+- [Repo-Specific hooks](#repo-specific-hooks)
+ - [Adding Existing Git Hooks](#adding-existing-git-hooks)
+- [Security](#security)
+ - [Disabling Signature Checking](#disabling-signature-checking)
+- [Contributing](#contributing)
+- [Community](#community)
+- [Changelog](#changelog)
+- [License](#license)
## Requirements
-This project aims to support the following Ruby runtimes on both \*nix and Windows:
-
-* Ruby 2.4+
+This project aims to support the following Ruby runtimes on \*nix (and best effort on Windows):
-### Windows
-
-If you are using Overcommit on **Windows**, make sure you include the `ffi` gem in your
-list of dependencies. Overcommit does not include the `ffi` gem by default since it
-significantly increases the install time for non-Windows platforms.
+* Ruby 2.6+
### Dependencies
-Some of the hooks have third-party dependencies. For example, to lint your
-[SCSS](http://sass-lang.com/) files, you're going to need our
+Some hooks have third-party dependencies. For example, to lint your
+[SCSS](http://sass-lang.com/) files, you're going to need the
[scss_lint gem](https://github.com/sds/scss-lint).
Depending on the hooks you enable/disable for your repository, you'll need to
@@ -79,9 +79,9 @@ available during your hook runs.
`overcommit` is installed via [RubyGems](https://rubygems.org/). It is strongly
recommended that your environment support running `gem install` without
-requiring `sudo` privileges. Using a Ruby version manager like
-[`rbenv`](https://github.com/rbenv/rbenv/) or [`rvm`](https://rvm.io/) can help
-here.
+requiring root user privileges via `sudo` or otherwise. Using a Ruby version
+manager like [`rbenv`](https://github.com/rbenv/rbenv/) or
+[`rvm`](https://rvm.io/) is recommended.
Once you have an environment that allows you to install gems without `sudo`,
run:
@@ -131,6 +131,7 @@ Command Line Flag | Description
`-f`/`--force` | Don't bail on install if other hooks already exist--overwrite them
`-l`/`--list-hooks` | Display all available hooks in the current repository
`-r`/`--run` | Run pre-commit hook against all tracked files in repository
+`--diff [` | Run pre-commit hook against all changed files relative to `][`
`-t`/`--template-dir` | Print location of template directory
`-h`/`--help` | Show command-line flag documentation
`-v`/`--version` | Show version
@@ -211,6 +212,10 @@ PreCommit:
command: ['bundle', 'exec', 'rubocop'] # Invoke within Bundler context
```
+Additionally, you may wish to have repo-specific configurations that are local to your computer that are not part of the shared repo config.
+Adding a `.local-overcommit.yml` file in the top-level directory of the repository adds another configuration file. This file works the same as `.overcommit.yml`.
+Adding this to ignored files in a git repo will allow you to have a local configuration per repo.
+
### Hook Options
Individual hooks expose both built-in configuration options as well as their
@@ -500,7 +505,9 @@ issue](https://github.com/sds/overcommit/issues/238) for more details.
* [CoffeeLint](lib/overcommit/hook/pre_commit/coffee_lint.rb)
* [Credo](lib/overcommit/hook/pre_commit/credo.rb)
* [CssLint](lib/overcommit/hook/pre_commit/css_lint.rb)
+* [DartAnalyzer](lib/overcommit/hook/pre_commit/dart_analyzer.rb)
* [Dogma](lib/overcommit/hook/pre_commit/dogma.rb)
+* [ErbLint](lib/overcommit/hook/pre_commit/erb_lint.rb)
* [EsLint](lib/overcommit/hook/pre_commit/es_lint.rb)
* [ExecutePermissions](lib/overcommit/hook/pre_commit/execute_permissions.rb)
* [Fasterer](lib/overcommit/hook/pre_commit/fasterer.rb)
@@ -558,6 +565,7 @@ issue](https://github.com/sds/overcommit/issues/238) for more details.
* [SemiStandard](lib/overcommit/hook/pre_commit/semi_standard.rb)
* [ShellCheck](lib/overcommit/hook/pre_commit/shell_check.rb)
* [SlimLint](lib/overcommit/hook/pre_commit/slim_lint.rb)
+* [Sorbet](lib/overcommit/hook/pre_commit/sorbet.rb)
* [Sqlint](lib/overcommit/hook/pre_commit/sqlint.rb)
* [Standard](lib/overcommit/hook/pre_commit/standard.rb)
* [Stylelint](lib/overcommit/hook/pre_commit/stylelint.rb)
@@ -581,10 +589,12 @@ but before any objects have been transferred. If a hook fails, the push is
aborted.
* [Brakeman](lib/overcommit/hook/pre_push/brakeman.rb)
+* [FlutterTest](lib/overcommit/hook/pre_push/flutter_test.rb)
* [Minitest](lib/overcommit/hook/pre_push/minitest.rb)
* [PhpUnit](lib/overcommit/hook/pre_push/php_unit.rb)
* [Pronto](lib/overcommit/hook/pre_push/pronto.rb)
* [ProtectedBranches](lib/overcommit/hook/pre_push/protected_branches.rb)
+* [PubTest](lib/overcommit/hook/pre_push/pub_test.rb)
* [Pytest](lib/overcommit/hook/pre_push/pytest.rb)
* [PythonNose](lib/overcommit/hook/pre_push/python_nose.rb)
* [RakeTarget](lib/overcommit/hook/pre_push/rake_target.rb)
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index fcb90b95..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-version: "{build}"
-
-clone_depth: 1
-
-environment:
- matrix:
- - RUBY_FOLDER_VERSION: "24-x64"
- - RUBY_FOLDER_VERSION: "25-x64"
- - RUBY_FOLDER_VERSION: "26-x64"
-matrix:
- fast_finish: true
-
-cache:
- # Cache installed gems unless dependencies change
- - vendor\bundle -> Gemfile,overcommit.gemspec
-
-install:
- # Add ruby executables to PATH
- - set PATH=C:\Ruby%RUBY_FOLDER_VERSION%\bin;%PATH%
- - echo %PATH%
-
- # Print version and location for pre-installed grep
- - grep --version
- - where grep
-
- # Print version and location for pre-installed git
- - git --version
- - where git
-
- # Print version and location for pre-installed ruby
- - ruby --version
- - where ruby
-
- # Install latest version of RubyGems
- - gem update --system --no-document
- - gem --version
- - where gem
-
- # Install latest Bundler to work around version issues
- - gem install bundler:'< 2' --force
- - where bundler
-
- # Install ruby dependencies
- - bundle install --retry 3 --path vendor\bundle
-
-build: off
-
-before_test:
- # Necessary for AuthorName and AuthorEmail pre-commit hooks to pass
- - git config --global user.email "appveyor@appveyor.ci"
- - git config --global user.name "Appveyor CI"
-
- # Ignore CRLF conversion warnings
- - git config --global core.autocrlf true
- - git config --global core.safecrlf false
-
-test_script:
- - bundle exec rspec --tty --backtrace --color
- - bundle exec overcommit --sign
- - bundle exec overcommit --sign pre-commit
- - bundle exec overcommit --run
diff --git a/bin/overcommit b/bin/overcommit
index ebcf56a4..d54d179f 100755
--- a/bin/overcommit
+++ b/bin/overcommit
@@ -18,15 +18,16 @@ if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
ensure
$stderr = old_stderr
end
- rescue Bundler::BundlerError => ex
- puts "Problem loading '#{gemfile}': #{ex.message}"
- puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound)
+ rescue Bundler::BundlerError => e
+ puts "Problem loading '#{gemfile}': #{e.message}"
+ puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound)
exit 78 # EX_CONFIG
- rescue Gem::LoadError => ex
+ rescue Gem::LoadError => e
# Handle case where user is executing overcommit without `bundle exec` and
# whose local Gemfile has a gem requirement that does not match a gem
# requirement of the installed version of Overcommit.
- raise unless ex.message =~ /already activated/i
+ raise unless e.message =~ /already activated/i
+
exec('bundle', 'exec', $0, *ARGV)
end
end
diff --git a/config/default.yml b/config/default.yml
index 74503e8e..ba8af533 100644
--- a/config/default.yml
+++ b/config/default.yml
@@ -32,6 +32,10 @@
# your repository, and then set the `gemfile` option below to the name you gave
# the file.
# (Generate lock file by running `bundle install --gemfile=.overcommit_gems.rb`)
+#
+# NOTE: the following line will be parsed by a regexp rather than a proper YAML
+# parser, so avoid any values other than false or a string, and don't use inline
+# comments
gemfile: false
# Where to store hook plugins specific to a repository. These are loaded in
@@ -266,6 +270,14 @@ PreCommit:
install_command: 'npm install -g csslint'
include: '**/*.css'
+ DartAnalyzer:
+ enabled: false
+ description: 'Analyze with dartanalyzer'
+ required_executable: 'dartanalyzer'
+ flags: []
+ include:
+ - '**/*.dart'
+
Dogma:
enabled: false
description: 'Analyze with dogma'
@@ -275,6 +287,13 @@ PreCommit:
- '**/*.ex'
- '**/*.exs'
+ ErbLint:
+ enabled: false
+ description: 'Analyze with ERB Lint'
+ required_executable: 'erblint'
+ install_command: 'bundle install erb_lint'
+ include: '**/*.html.erb'
+
EsLint:
enabled: false
description: 'Analyze with ESLint'
@@ -338,7 +357,7 @@ PreCommit:
keywords: ['FContext','FDescribe','FIt','FMeasure','FSpecify','FWhen']
GoFmt:
- enabled: true
+ enabled: false
description: 'Fix with go fmt'
required_executable: 'go'
command: ['go', 'fmt']
@@ -521,6 +540,16 @@ PreCommit:
required_executable: 'grep'
flags: ['-IHn', "^<<<<<<<[ \t]"]
+ MixFormat:
+ enabled: false
+ description: 'Check formatting with mix format'
+ required_executable: 'mix'
+ flags: ['format', '--check-formatted']
+ include:
+ - '**/*.ex'
+ - '**/*.heex'
+ - '**/*.exs'
+
PuppetMetadataJsonLint:
enabled: false
description: 'Checking module metadata'
@@ -684,6 +713,11 @@ PreCommit:
install_command: 'pip install restructuredtext_lint'
include: '**/*.rst'
+ RSpec:
+ enabled: false
+ description: 'Run tests with Rspec'
+ required_executable: 'rspec'
+
RuboCop:
enabled: false
description: 'Analyze with RuboCop'
@@ -765,6 +799,14 @@ PreCommit:
install_command: 'gem install slim_lint'
include: '**/*.slim'
+ Sorbet:
+ enabled: false
+ description: 'Analyze with Sorbet'
+ required_executable: 'srb'
+ install_command: 'gem install sorbet'
+ command: ['srb', 'tc']
+ include: '**/*.rb'
+
Sqlint:
enabled: false
description: 'Analyze with sqlint'
@@ -885,7 +927,7 @@ PreCommit:
enabled: false
description: 'Analyze with YAMLlint'
required_executable: 'yamllint'
- flags: ['--format=parsable']
+ flags: ['--format=parsable', '--strict']
install_command: 'pip install yamllint'
include:
- '**/*.yaml'
@@ -1237,9 +1279,14 @@ PrepareCommitMsg:
ReplaceBranch:
enabled: false
description: 'Prepends the commit message with text based on the branch name'
- branch_pattern: '\A.*\w+[-_](\d+).*\z'
+ branch_pattern: '\A(\d+)-(\w+).*\z'
replacement_text: '[#\1]'
- skipped_commit_types: ['message', 'template', 'merge', 'squash']
+ skipped_commit_types:
+ - 'message' # if message is given via `-m`, `-F`
+ - 'template' # if `-t` is given or `commit.template` is set
+ - 'commit' # if `-c`, `-C`, or `--amend` is given
+ - 'merge' # if merging
+ - 'squash' # if squashing
on_fail: warn
# Hooks that run during `git push`, after remote refs have been updated but
@@ -1264,6 +1311,12 @@ PrePush:
flags: ['test']
include: 'src/**/*.rs'
+ FlutterTest:
+ enabled: false
+ description: 'Run flutter test suite'
+ required_executable: 'flutter'
+ flags: ['test']
+
GitLfs:
enabled: false
description: 'Upload files tracked by Git LFS'
@@ -1290,6 +1343,12 @@ PrePush:
command: ['ruby', '-Ilib:test', '-rminitest', "-e 'exit! Minitest.run'"]
include: 'test/**/*_test.rb'
+ MixTest:
+ enabled: false
+ description: 'Run mix test suite'
+ required_executable: 'mix'
+ flags: ['test']
+
PhpUnit:
enabled: false
description: 'Run PhpUnit test suite'
@@ -1310,6 +1369,12 @@ PrePush:
destructive_only: true
branches: ['master']
+ PubTest:
+ enabled: false
+ description: 'Run pub test suite'
+ required_executable: 'pub'
+ flags: ['run', 'test']
+
Pytest:
enabled: false
description: 'Run pytest test suite'
diff --git a/lib/overcommit/cli.rb b/lib/overcommit/cli.rb
index 54a3f9b7..dafc545a 100644
--- a/lib/overcommit/cli.rb
+++ b/lib/overcommit/cli.rb
@@ -6,9 +6,10 @@
module Overcommit
# Responsible for parsing command-line options and executing appropriate
# application logic based on those options.
- class CLI # rubocop:disable ClassLength
+ class CLI # rubocop:disable Metrics/ClassLength
def initialize(arguments, input, logger)
@arguments = arguments
+ @cli_options = {}
@input = input
@log = logger
@options = {}
@@ -28,12 +29,14 @@ def run
sign
when :run_all
run_all
+ when :diff
+ diff
end
- rescue Overcommit::Exceptions::ConfigurationSignatureChanged => ex
- puts ex
+ rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e
+ puts e
exit 78 # EX_CONFIG
- rescue Overcommit::Exceptions::HookContextLoadError => ex
- puts ex
+ rescue Overcommit::Exceptions::HookContextLoadError => e
+ puts e
exit 64 # EX_USAGE
end
@@ -45,15 +48,15 @@ def parse_arguments
@parser = create_option_parser
begin
- @parser.parse!(@arguments)
+ @parser.parse!(@arguments, into: @cli_options)
# Default action is to install
@options[:action] ||= :install
# Unconsumed arguments are our targets
@options[:targets] = @arguments
- rescue OptionParser::InvalidOption => ex
- print_help @parser.help, ex
+ rescue OptionParser::InvalidOption => e
+ print_help @parser.help, e
end
end
@@ -94,8 +97,14 @@ def add_installation_options(opts)
@options[:force] = true
end
- opts.on('-r', '--run', 'Run pre-commit hook against all git tracked files') do
+ opts.on('-r [hook]', '--run [hook]', 'Run specified hook against all git tracked files. Defaults to `pre_commit`.') do |arg| # rubocop:disable Layout/LineLength
@options[:action] = :run_all
+ @options[:hook_to_run] = arg ? arg.to_s : 'run-all'
+ end
+
+ opts.on('--diff [ref]', 'Run pre_commit hooks against the diff between a given ref. Defaults to `main`.') do |arg| # rubocop:disable Layout/LineLength
+ @options[:action] = :diff
+ arg
end
end
@@ -123,15 +132,13 @@ def install_or_uninstall
end
@options[:targets].each do |target|
- begin
- Installer.new(log).run(target, @options)
- rescue Overcommit::Exceptions::InvalidGitRepo => error
- log.warning "Invalid repo #{target}: #{error}"
- halt 69 # EX_UNAVAILABLE
- rescue Overcommit::Exceptions::PreExistingHooks => error
- log.warning "Unable to install into #{target}: #{error}"
- halt 73 # EX_CANTCREAT
- end
+ Installer.new(log).run(target, @options)
+ rescue Overcommit::Exceptions::InvalidGitRepo => e
+ log.warning "Invalid repo #{target}: #{e}"
+ halt 69 # EX_UNAVAILABLE
+ rescue Overcommit::Exceptions::PreExistingHooks => e
+ log.warning "Unable to install into #{target}: #{e}"
+ halt 73 # EX_CANTCREAT
end
end
@@ -199,7 +206,20 @@ def sign
def run_all
empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input
- context = Overcommit::HookContext.create('run-all', config, @arguments, empty_stdin)
+ context = Overcommit::HookContext.create(@options[:hook_to_run], config, @arguments, empty_stdin) # rubocop:disable Layout/LineLength
+ config.apply_environment!(context, ENV)
+
+ printer = Overcommit::Printer.new(config, log, context)
+ runner = Overcommit::HookRunner.new(config, log, context, printer)
+
+ status = runner.run
+
+ halt(status ? 0 : 65)
+ end
+
+ def diff
+ empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input
+ context = Overcommit::HookContext.create('diff', config, @arguments, empty_stdin, **@cli_options) # rubocop:disable Layout/LineLength
config.apply_environment!(context, ENV)
printer = Overcommit::Printer.new(config, log, context)
diff --git a/lib/overcommit/command_splitter.rb b/lib/overcommit/command_splitter.rb
index 32c5299c..c334b6f9 100644
--- a/lib/overcommit/command_splitter.rb
+++ b/lib/overcommit/command_splitter.rb
@@ -107,8 +107,10 @@ def arguments_under_limit(splittable_args, start_index, byte_limit)
loop do
break if index > splittable_args.length - 1
+
total_bytes += splittable_args[index].bytesize
break if total_bytes > byte_limit # Not enough room
+
index += 1
end
diff --git a/lib/overcommit/configuration.rb b/lib/overcommit/configuration.rb
index ae3efa50..72086457 100644
--- a/lib/overcommit/configuration.rb
+++ b/lib/overcommit/configuration.rb
@@ -5,7 +5,7 @@
module Overcommit
# Stores configuration for Overcommit and the hooks it runs.
- class Configuration # rubocop:disable ClassLength
+ class Configuration # rubocop:disable Metrics/ClassLength
# Creates a configuration from the given hash.
#
# @param hash [Hash] loaded YAML config file as a hash
diff --git a/lib/overcommit/configuration_loader.rb b/lib/overcommit/configuration_loader.rb
index f884c41d..8fd68a01 100644
--- a/lib/overcommit/configuration_loader.rb
+++ b/lib/overcommit/configuration_loader.rb
@@ -24,13 +24,16 @@ def default_configuration
# @option logger [Overcommit::Logger]
# @return [Overcommit::Configuration]
def load_from_file(file, options = {})
- hash =
- if yaml = YAML.load_file(file)
- yaml.to_hash
- else
- {}
+ # Psych 4 introduced breaking behavior that doesn't support aliases by
+ # default. Explicitly enable aliases if the option is available.
+ yaml =
+ begin
+ YAML.load_file(file, aliases: true)
+ rescue ArgumentError
+ YAML.load_file(file)
end
+ hash = yaml ? yaml.to_hash : {}
Overcommit::Configuration.new(hash, options)
end
end
@@ -50,10 +53,14 @@ def initialize(logger, options = {})
#
# @return [Overcommit::Configuration]
def load_repo_config
+ overcommit_local_yml = File.join(Overcommit::Utils.repo_root,
+ Overcommit::LOCAL_CONFIG_FILE_NAME)
overcommit_yml = File.join(Overcommit::Utils.repo_root,
Overcommit::CONFIG_FILE_NAME)
- if File.exist?(overcommit_yml)
+ if File.exist?(overcommit_local_yml) && File.exist?(overcommit_yml)
+ load_file(overcommit_yml, overcommit_local_yml)
+ elsif File.exist?(overcommit_yml)
load_file(overcommit_yml)
else
self.class.default_configuration
@@ -61,9 +68,11 @@ def load_repo_config
end
# Loads a configuration, ensuring it extends the default configuration.
- def load_file(file)
- config = self.class.load_from_file(file, default: false, logger: @log)
- config = self.class.default_configuration.merge(config)
+ def load_file(file, local_file = nil)
+ overcommit_config = self.class.load_from_file(file, default: false, logger: @log)
+ l_config = self.class.load_from_file(local_file, default: false, logger: @log) if local_file
+ config = self.class.default_configuration.merge(overcommit_config)
+ config = config.merge(l_config) if l_config
if @options.fetch(:verify) { config.verify_signatures? }
verify_signatures(config)
@@ -72,10 +81,10 @@ def load_file(file)
config
rescue Overcommit::Exceptions::ConfigurationSignatureChanged
raise
- rescue StandardError => error
+ rescue StandardError => e
raise Overcommit::Exceptions::ConfigurationError,
- "Unable to load configuration from '#{file}': #{error}",
- error.backtrace
+ "Unable to load configuration from '#{file}': #{e}",
+ e.backtrace
end
private
diff --git a/lib/overcommit/constants.rb b/lib/overcommit/constants.rb
index dd027932..e7c19728 100644
--- a/lib/overcommit/constants.rb
+++ b/lib/overcommit/constants.rb
@@ -4,6 +4,7 @@
module Overcommit
HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
CONFIG_FILE_NAME = '.overcommit.yml'
+ LOCAL_CONFIG_FILE_NAME = '.local-overcommit.yml'
HOOK_DIRECTORY = File.join(HOME, 'lib', 'overcommit', 'hook').freeze
diff --git a/lib/overcommit/exceptions.rb b/lib/overcommit/exceptions.rb
index 3246c567..050982fb 100644
--- a/lib/overcommit/exceptions.rb
+++ b/lib/overcommit/exceptions.rb
@@ -1,52 +1,55 @@
# frozen_string_literal: true
module Overcommit::Exceptions
+ # Base error class.
+ class Error < StandardError; end
+
# Raised when a {Configuration} could not be loaded from a file.
- class ConfigurationError < StandardError; end
+ class ConfigurationError < Error; end
# Raised when the Overcommit configuration file signature has changed.
- class ConfigurationSignatureChanged < StandardError; end
+ class ConfigurationSignatureChanged < Error; end
# Raised when trying to read/write to/from the local repo git config fails.
- class GitConfigError < StandardError; end
+ class GitConfigError < Error; end
# Raised when there was a problem reading submodule information for a repo.
- class GitSubmoduleError < StandardError; end
+ class GitSubmoduleError < Error; end
# Raised when there was a problem reading git revision information with `rev-list`.
- class GitRevListError < StandardError; end
+ class GitRevListError < Error; end
# Raised when a {HookContext} is unable to setup the environment before a run.
- class HookSetupFailed < StandardError; end
+ class HookSetupFailed < Error; end
# Raised when a {HookContext} is unable to clean the environment after a run.
- class HookCleanupFailed < StandardError; end
+ class HookCleanupFailed < Error; end
# Raised when a hook run was cancelled by the user.
- class HookCancelled < StandardError; end
+ class HookCancelled < Error; end
# Raised when a hook could not be loaded by a {HookRunner}.
- class HookLoadError < StandardError; end
+ class HookLoadError < Error; end
# Raised when a {HookRunner} could not be loaded.
- class HookContextLoadError < StandardError; end
+ class HookContextLoadError < Error; end
# Raised when a pipe character is used in the `execute` helper, as this was
# likely used in error.
- class InvalidCommandArgs < StandardError; end
+ class InvalidCommandArgs < Error; end
# Raised when an installation target is not a valid git repository.
- class InvalidGitRepo < StandardError; end
+ class InvalidGitRepo < Error; end
# Raised when a hook was defined incorrectly.
- class InvalidHookDefinition < StandardError; end
+ class InvalidHookDefinition < Error; end
# Raised when one or more hook plugin signatures have changed.
- class InvalidHookSignature < StandardError; end
+ class InvalidHookSignature < Error; end
# Raised when there is a problem processing output into {Hook::Messages}s.
- class MessageProcessingError < StandardError; end
+ class MessageProcessingError < Error; end
# Raised when an installation target already contains non-Overcommit hooks.
- class PreExistingHooks < StandardError; end
+ class PreExistingHooks < Error; end
end
diff --git a/lib/overcommit/git_config.rb b/lib/overcommit/git_config.rb
index d2ad2ad6..c1243861 100644
--- a/lib/overcommit/git_config.rb
+++ b/lib/overcommit/git_config.rb
@@ -16,7 +16,8 @@ def comment_character
def hooks_path
path = `git config --get core.hooksPath`.chomp
return File.join(Overcommit::Utils.git_dir, 'hooks') if path.empty?
- File.absolute_path(path, Dir.pwd)
+
+ File.expand_path(path, Dir.pwd)
end
end
end
diff --git a/lib/overcommit/git_repo.rb b/lib/overcommit/git_repo.rb
index bd659eb7..1af73e81 100644
--- a/lib/overcommit/git_repo.rb
+++ b/lib/overcommit/git_repo.rb
@@ -14,7 +14,7 @@ module GitRepo
[^\s]+\s # Ignore old file range
\+(\d+)(?:,(\d+))? # Extract range of hunk containing start line and number of lines
\s@@.*$
- /x
+ /x.freeze
# Regular expression used to extract information from lines of
# `git submodule status` output
@@ -22,7 +22,7 @@ module GitRepo
^\s*(?[-+U]?)(?\w+)
\s(?[^\s]+?)
(?:\s\((?.+)\))?$
- /x
+ /x.freeze
# Struct encapsulating submodule information extracted from the
# output of `git submodule status`
@@ -109,15 +109,16 @@ def modified_files(options)
# @return [Array] list of absolute file paths
def list_files(paths = [], options = {})
ref = options[:ref] || 'HEAD'
- path_list =
- if OS.windows?
- paths = paths.map { |path| path.gsub('"', '""') }
- paths.empty? ? '' : "\"#{paths.join('" "')}\""
- else
- paths.shelljoin
- end
- `git ls-tree --name-only #{ref} #{path_list}`.
- split(/\n/).
+
+ result = Overcommit::Utils.execute(%W[git ls-tree --name-only #{ref}], args: paths)
+ unless result.success?
+ raise Overcommit::Exceptions::Error,
+ "Error listing files. EXIT STATUS(es): #{result.statuses}.\n" \
+ "STDOUT(s): #{result.stdouts}.\n" \
+ "STDERR(s): #{result.stderrs}."
+ end
+
+ result.stdout.split(/\n/).
map { |relative_file| File.expand_path(relative_file) }.
reject { |file| File.directory?(file) } # Exclude submodule directories
end
@@ -261,9 +262,9 @@ def submodules(options = {})
end
modules
- rescue IniParse::IniParseError => ex
+ rescue IniParse::IniParseError => e
raise Overcommit::Exceptions::GitSubmoduleError,
- "Unable to read submodule information from #{ref}:.gitmodules file: #{ex.message}"
+ "Unable to read submodule information from #{ref}:.gitmodules file: #{e.message}"
end
# Returns the names of all branches containing the given commit.
diff --git a/lib/overcommit/hook/base.rb b/lib/overcommit/hook/base.rb
index 37696d08..3c2eaeb3 100644
--- a/lib/overcommit/hook/base.rb
+++ b/lib/overcommit/hook/base.rb
@@ -228,14 +228,12 @@ def check_for_libraries
output = []
required_libraries.each do |library|
- begin
- require library
- rescue LoadError
- install_command = @config['install_command']
- install_command = " -- install via #{install_command}" if install_command
-
- output << "Unable to load '#{library}'#{install_command}"
- end
+ require library
+ rescue LoadError
+ install_command = @config['install_command']
+ install_command = " -- install via #{install_command}" if install_command
+
+ output << "Unable to load '#{library}'#{install_command}"
end
return if output.empty?
diff --git a/lib/overcommit/hook/commit_msg/base.rb b/lib/overcommit/hook/commit_msg/base.rb
index 2b46a66e..f99f53fc 100644
--- a/lib/overcommit/hook/commit_msg/base.rb
+++ b/lib/overcommit/hook/commit_msg/base.rb
@@ -9,6 +9,6 @@ class Base < Overcommit::Hook::Base
def_delegators :@context, :empty_message?, :commit_message,
:update_commit_message, :commit_message_lines,
- :commit_message_file
+ :commit_message_file, :modified_lines_in_file
end
end
diff --git a/lib/overcommit/hook/commit_msg/spell_check.rb b/lib/overcommit/hook/commit_msg/spell_check.rb
index 241df378..ec6c4d4a 100644
--- a/lib/overcommit/hook/commit_msg/spell_check.rb
+++ b/lib/overcommit/hook/commit_msg/spell_check.rb
@@ -9,7 +9,7 @@ module Overcommit::Hook::CommitMsg
class SpellCheck < Base
Misspelling = Struct.new(:word, :suggestions)
- MISSPELLING_REGEX = /^[]\s(?\w+)(?:.+?:\s(?.*))?/
+ MISSPELLING_REGEX = /^[]\s(?\w+)(?:.+?:\s(?.*))?/.freeze
def run
result = execute(command + [uncommented_commit_msg_file])
diff --git a/lib/overcommit/hook/commit_msg/text_width.rb b/lib/overcommit/hook/commit_msg/text_width.rb
index 011e7e2a..52de3bd7 100644
--- a/lib/overcommit/hook/commit_msg/text_width.rb
+++ b/lib/overcommit/hook/commit_msg/text_width.rb
@@ -32,7 +32,7 @@ def find_errors_in_subject(subject)
min_subject_width = config['min_subject_width']
if subject.length < min_subject_width
@errors << "Commit message subject must be >= #{min_subject_width} characters"
- return
+ nil
end
end
@@ -41,7 +41,7 @@ def find_errors_in_body(lines)
max_body_width = config['max_body_width']
- lines[2..-1].each_with_index do |line, index|
+ lines[2..].each_with_index do |line, index|
if line.chomp.size > max_body_width
@errors << "Line #{index + 3} of commit message has > " \
"#{max_body_width} characters"
diff --git a/lib/overcommit/hook/post_checkout/base.rb b/lib/overcommit/hook/post_checkout/base.rb
index 842e3154..78798b9e 100644
--- a/lib/overcommit/hook/post_checkout/base.rb
+++ b/lib/overcommit/hook/post_checkout/base.rb
@@ -16,6 +16,7 @@ def skip_file_checkout?
def enabled?
return false if file_checkout? && skip_file_checkout?
+
super
end
end
diff --git a/lib/overcommit/hook/post_commit/git_guilt.rb b/lib/overcommit/hook/post_commit/git_guilt.rb
index 41980425..b4f1ead8 100644
--- a/lib/overcommit/hook/post_commit/git_guilt.rb
+++ b/lib/overcommit/hook/post_commit/git_guilt.rb
@@ -5,12 +5,13 @@ module Overcommit::Hook::PostCommit
#
# @see https://www.npmjs.com/package/git-guilt
class GitGuilt < Base
- PLUS_MINUS_REGEX = /^(.*?)(?:(\++)|(-+))$/
+ PLUS_MINUS_REGEX = /^(.*?)(?:(\++)|(-+))$/.freeze
GREEN = 32
RED = 31
def run
return :pass if initial_commit?
+
result = execute(command)
return :fail, result.stderr unless result.success?
diff --git a/lib/overcommit/hook/pre_commit/author_name.rb b/lib/overcommit/hook/pre_commit/author_name.rb
index 63d4243a..fc89792a 100644
--- a/lib/overcommit/hook/pre_commit/author_name.rb
+++ b/lib/overcommit/hook/pre_commit/author_name.rb
@@ -12,9 +12,9 @@ def run
result.stdout.chomp
end
- unless name.split(' ').count >= 2
+ if name.empty?
return :fail,
- "Author must have at least first and last name, but was: #{name}.\n" \
+ "Author name must be non-0 in length.\n" \
'Set your name with `git config --global user.name "Your Name"` ' \
'or via the GIT_AUTHOR_NAME environment variable'
end
diff --git a/lib/overcommit/hook/pre_commit/bundle_audit.rb b/lib/overcommit/hook/pre_commit/bundle_audit.rb
index 465cc3c8..1f9f24c7 100644
--- a/lib/overcommit/hook/pre_commit/bundle_audit.rb
+++ b/lib/overcommit/hook/pre_commit/bundle_audit.rb
@@ -17,7 +17,7 @@ def run
if result.success?
:pass
else
- return [:warn, result.stdout]
+ [:warn, result.stdout]
end
end
end
diff --git a/lib/overcommit/hook/pre_commit/bundle_check.rb b/lib/overcommit/hook/pre_commit/bundle_check.rb
index 10d30d2c..70f5fa2e 100644
--- a/lib/overcommit/hook/pre_commit/bundle_check.rb
+++ b/lib/overcommit/hook/pre_commit/bundle_check.rb
@@ -6,7 +6,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://bundler.io/
class BundleCheck < Base
- LOCK_FILE = 'Gemfile.lock'
+ LOCK_FILE = File.basename(ENV['BUNDLE_GEMFILE'] || 'Gemfile') + '.lock'
def run
# Ignore if Gemfile.lock is not tracked by git
diff --git a/lib/overcommit/hook/pre_commit/chamber_compare.rb b/lib/overcommit/hook/pre_commit/chamber_compare.rb
index f0c4f146..b3e0366c 100644
--- a/lib/overcommit/hook/pre_commit/chamber_compare.rb
+++ b/lib/overcommit/hook/pre_commit/chamber_compare.rb
@@ -14,11 +14,11 @@ def run
next unless second
result = execute(
- command,
- args: [
- "--first=#{first.join(' ')}",
- "--second=#{second.join(' ')}",
- ],
+ command,
+ args: [
+ "--first=#{first.join(' ')}",
+ "--second=#{second.join(' ')}",
+ ],
)
unless result.stdout.empty?
diff --git a/lib/overcommit/hook/pre_commit/chamber_security.rb b/lib/overcommit/hook/pre_commit/chamber_security.rb
index b47672df..c639fbf8 100644
--- a/lib/overcommit/hook/pre_commit/chamber_security.rb
+++ b/lib/overcommit/hook/pre_commit/chamber_security.rb
@@ -9,6 +9,7 @@ def run
result = execute(command, args: applicable_files)
return :pass if result.stdout.empty?
+
[:fail, "These settings appear to need to be secured but were not: #{result.stdout}"]
end
end
diff --git a/lib/overcommit/hook/pre_commit/coffee_lint.rb b/lib/overcommit/hook/pre_commit/coffee_lint.rb
index 7e68b40d..57c80a58 100644
--- a/lib/overcommit/hook/pre_commit/coffee_lint.rb
+++ b/lib/overcommit/hook/pre_commit/coffee_lint.rb
@@ -10,7 +10,7 @@ class CoffeeLint < Base
,(?\d*),\d*
,(?\w+)
,(?.+)$
- /x
+ /x.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type.include?('w') ? :warning : :error
diff --git a/lib/overcommit/hook/pre_commit/css_lint.rb b/lib/overcommit/hook/pre_commit/css_lint.rb
index 7ca82283..b110d074 100644
--- a/lib/overcommit/hook/pre_commit/css_lint.rb
+++ b/lib/overcommit/hook/pre_commit/css_lint.rb
@@ -9,7 +9,7 @@ class CssLint < Base
^(?(?:\w:)?[^:]+):\s
(?:line\s(?\d+)[^EW]+)?
(?Error|Warning)
- /x
+ /x.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/dart_analyzer.rb b/lib/overcommit/hook/pre_commit/dart_analyzer.rb
new file mode 100644
index 00000000..38002f22
--- /dev/null
+++ b/lib/overcommit/hook/pre_commit/dart_analyzer.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PreCommit
+ # Runs `dartanalyzer` against modified Dart files.
+ # @see https://dart.dev/tools/dartanalyzer
+ class DartAnalyzer < Base
+ MESSAGE_REGEX = /(?.*)•\ (?[^•]+)•\ (?[^:]+):(?\d+):(\d+)\.*/.freeze
+
+ def run
+ result = execute(command, args: applicable_files)
+ return :pass if result.success?
+
+ extract_messages(
+ result.stdout.split("\n").grep(MESSAGE_REGEX),
+ MESSAGE_REGEX,
+ lambda do |type|
+ type.include?('error') ? :error : :warning
+ end
+ )
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_commit/erb_lint.rb b/lib/overcommit/hook/pre_commit/erb_lint.rb
new file mode 100644
index 00000000..ae5af164
--- /dev/null
+++ b/lib/overcommit/hook/pre_commit/erb_lint.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PreCommit
+ # Runs `erblint` against any modified ERB files.
+ #
+ # @see https://github.com/Shopify/erb-lint
+ class ErbLint < Base
+ MESSAGE_REGEX = /(?.+)\nIn file: (?.+):(?\d+)/.freeze
+
+ def run
+ result = execute(command, args: applicable_files)
+ return :pass if result.success?
+
+ extract_messages(
+ result.stdout.split("\n\n")[1..],
+ MESSAGE_REGEX
+ )
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_commit/es_lint.rb b/lib/overcommit/hook/pre_commit/es_lint.rb
index ebbdd112..240d749d 100644
--- a/lib/overcommit/hook/pre_commit/es_lint.rb
+++ b/lib/overcommit/hook/pre_commit/es_lint.rb
@@ -19,20 +19,17 @@ module Overcommit::Hook::PreCommit
# @see http://eslint.org/
class EsLint < Base
def run
+ eslint_regex = /^(?[^\s](?:\w:)?[^:]+):[^\d]+(?\d+).*?(?Error|Warning)/
result = execute(command, args: applicable_files)
output = result.stdout.chomp
- messages = output.split("\n").grep(/Warning|Error/)
+ messages = output.split("\n").grep(eslint_regex)
return [:fail, result.stderr] if messages.empty? && !result.success?
return :pass if result.success? && output.empty?
# example message:
# path/to/file.js: line 1, col 0, Error - Error message (ruleName)
- extract_messages(
- messages,
- /^(?(?:\w:)?[^:]+):[^\d]+(?\d+).*?(?Error|Warning)/,
- lambda { |type| type.downcase.to_sym }
- )
+ extract_messages(messages, eslint_regex, lambda { |type| type.downcase.to_sym })
end
end
end
diff --git a/lib/overcommit/hook/pre_commit/fasterer.rb b/lib/overcommit/hook/pre_commit/fasterer.rb
index dc584368..871be80f 100644
--- a/lib/overcommit/hook/pre_commit/fasterer.rb
+++ b/lib/overcommit/hook/pre_commit/fasterer.rb
@@ -12,7 +12,7 @@ def run
if extract_offense_num(output) == 0
:pass
else
- return [:warn, output]
+ [:warn, output]
end
end
diff --git a/lib/overcommit/hook/pre_commit/foodcritic.rb b/lib/overcommit/hook/pre_commit/foodcritic.rb
index 98ac9873..b40adfbe 100644
--- a/lib/overcommit/hook/pre_commit/foodcritic.rb
+++ b/lib/overcommit/hook/pre_commit/foodcritic.rb
@@ -102,7 +102,7 @@ def run
if result.success?
:pass
else
- return [:warn, result.stderr + result.stdout]
+ [:warn, result.stderr + result.stdout]
end
end
@@ -137,12 +137,14 @@ def modified_cookbooks_args
def modified(type)
return [] if !config["#{type}_directory"] || config["#{type}_directory"].empty?
+
@modified ||= {}
@modified[type] ||= directories_changed(full_directory_path("#{type}_directory"))
end
def full_directory_path(config_option)
return config[config_option] if config[config_option].start_with?(File::SEPARATOR)
+
File.absolute_path(File.join(Overcommit::Utils.repo_root, config[config_option]))
end
end
diff --git a/lib/overcommit/hook/pre_commit/hlint.rb b/lib/overcommit/hook/pre_commit/hlint.rb
index 1ac2775c..a52d1b62 100644
--- a/lib/overcommit/hook/pre_commit/hlint.rb
+++ b/lib/overcommit/hook/pre_commit/hlint.rb
@@ -10,7 +10,7 @@ class Hlint < Base
:(?\d+)
:\d+
:\s*(?\w+)
- /x
+ /x.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type.include?('W') ? :warning : :error
diff --git a/lib/overcommit/hook/pre_commit/html_hint.rb b/lib/overcommit/hook/pre_commit/html_hint.rb
index e8cb4a68..ddbe37d1 100644
--- a/lib/overcommit/hook/pre_commit/html_hint.rb
+++ b/lib/overcommit/hook/pre_commit/html_hint.rb
@@ -14,7 +14,7 @@ def run
lines = group.split("\n").map(&:strip)
file = lines[0][/(.+):/, 1]
extract_messages(
- lines[1..-1].map { |msg| "#{file}: #{msg}" },
+ lines[1..].map { |msg| "#{file}: #{msg}" },
/^(?(?:\w:)?[^:]+): line (?\d+)/
)
end.flatten
diff --git a/lib/overcommit/hook/pre_commit/html_tidy.rb b/lib/overcommit/hook/pre_commit/html_tidy.rb
index b4a2d89d..2667e2c1 100644
--- a/lib/overcommit/hook/pre_commit/html_tidy.rb
+++ b/lib/overcommit/hook/pre_commit/html_tidy.rb
@@ -10,7 +10,7 @@ class HtmlTidy < Base
line\s(?\d+)\s
column\s(?\d+)\s-\s
(?Error|Warning):\s(?.+)$
- /x
+ /x.freeze
def run
# example message:
diff --git a/lib/overcommit/hook/pre_commit/java_checkstyle.rb b/lib/overcommit/hook/pre_commit/java_checkstyle.rb
index 4961eb12..5d627a64 100644
--- a/lib/overcommit/hook/pre_commit/java_checkstyle.rb
+++ b/lib/overcommit/hook/pre_commit/java_checkstyle.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://checkstyle.sourceforge.net/
class JavaCheckstyle < Base
- MESSAGE_REGEX = /^(\[(?[^\]]+)\]\s+)?(?(?:\w:)?[^:]+):(?\d+)/
+ MESSAGE_REGEX = /^(\[(?[^\]]+)\]\s+)?(?(?:\w:)?[^:]+):(?\d+)/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
%w[WARN INFO].include?(type.to_s) ? :warning : :error
diff --git a/lib/overcommit/hook/pre_commit/js_lint.rb b/lib/overcommit/hook/pre_commit/js_lint.rb
index 3b6b8b87..956dfd2c 100644
--- a/lib/overcommit/hook/pre_commit/js_lint.rb
+++ b/lib/overcommit/hook/pre_commit/js_lint.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://www.jslint.com/
class JsLint < Base
- MESSAGE_REGEX = /(?(?:\w:)?[^:]+):(?\d+)/
+ MESSAGE_REGEX = /(?(?:\w:)?[^:]+):(?\d+)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/jsl.rb b/lib/overcommit/hook/pre_commit/jsl.rb
index 38f8a0cf..98eb7175 100644
--- a/lib/overcommit/hook/pre_commit/jsl.rb
+++ b/lib/overcommit/hook/pre_commit/jsl.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://www.javascriptlint.com/
class Jsl < Base
- MESSAGE_REGEX = /(?(?:\w:)?.+)\((?\d+)\):(?[^:]+)/
+ MESSAGE_REGEX = /(?(?:\w:)?.+)\((?\d+)\):(?[^:]+)/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type.match?(/warning/) ? :warning : :error
diff --git a/lib/overcommit/hook/pre_commit/json_syntax.rb b/lib/overcommit/hook/pre_commit/json_syntax.rb
index 04972b91..bd162f7d 100644
--- a/lib/overcommit/hook/pre_commit/json_syntax.rb
+++ b/lib/overcommit/hook/pre_commit/json_syntax.rb
@@ -7,12 +7,10 @@ def run
messages = []
applicable_files.each do |file|
- begin
- JSON.parse(IO.read(file))
- rescue JSON::ParserError => e
- error = "#{e.message} parsing #{file}"
- messages << Overcommit::Hook::Message.new(:error, file, nil, error)
- end
+ JSON.parse(IO.read(file))
+ rescue JSON::ParserError => e
+ error = "#{e.message} parsing #{file}"
+ messages << Overcommit::Hook::Message.new(:error, file, nil, error)
end
messages
diff --git a/lib/overcommit/hook/pre_commit/kt_lint.rb b/lib/overcommit/hook/pre_commit/kt_lint.rb
index 8e80a581..4b81ed21 100644
--- a/lib/overcommit/hook/pre_commit/kt_lint.rb
+++ b/lib/overcommit/hook/pre_commit/kt_lint.rb
@@ -4,7 +4,7 @@ module Overcommit::Hook::PreCommit
# Runs `ktlint` against modified Kotlin files.
# @see https://github.com/shyiko/ktlint
class KtLint < Base
- MESSAGE_REGEX = /((?[^:]+):(?\d+):(\d+):(?.+))/
+ MESSAGE_REGEX = /((?[^:]+):(?\d+):(\d+):(?.+))/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/license_finder.rb b/lib/overcommit/hook/pre_commit/license_finder.rb
index fc3739fe..e2b8611b 100644
--- a/lib/overcommit/hook/pre_commit/license_finder.rb
+++ b/lib/overcommit/hook/pre_commit/license_finder.rb
@@ -7,6 +7,7 @@ class LicenseFinder < Base
def run
result = execute(command)
return :pass if result.success?
+
output = result.stdout + result.stderr
[:fail, output]
end
diff --git a/lib/overcommit/hook/pre_commit/line_endings.rb b/lib/overcommit/hook/pre_commit/line_endings.rb
index 8f8eb9c7..ab66001b 100644
--- a/lib/overcommit/hook/pre_commit/line_endings.rb
+++ b/lib/overcommit/hook/pre_commit/line_endings.rb
@@ -12,14 +12,14 @@ def run
file = File.open(file_name)
begin
messages += check_file(file, file_name)
- rescue ArgumentError => ex
+ rescue ArgumentError => e
# File is likely a binary file which this check should ignore, but
# print a warning just in case
messages << Overcommit::Hook::Message.new(
:warning,
file_name,
file.lineno,
- "#{file_name}:#{file.lineno}:#{ex.message}"
+ "#{file_name}:#{file.lineno}:#{e.message}"
)
end
end
@@ -59,6 +59,7 @@ def offending_files
i = info.split.first
next if i == 'l/-text' # ignore binary files
next if i == "l/#{eol}"
+
path
end.compact
end
diff --git a/lib/overcommit/hook/pre_commit/mix_format.rb b/lib/overcommit/hook/pre_commit/mix_format.rb
new file mode 100644
index 00000000..2fa07551
--- /dev/null
+++ b/lib/overcommit/hook/pre_commit/mix_format.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PreCommit
+ # Runs `mix format --check-formatted` against any modified ex/heex/exs files.
+ #
+ # @see https://hexdocs.pm/mix/main/Mix.Tasks.Format.html
+ class MixFormat < Base
+ # example message:
+ # ** (Mix) mix format failed due to --check-formatted.
+ # The following files are not formatted:
+ #
+ # * lib/file1.ex
+ # * lib/file2.ex
+ FILES_REGEX = /^\s+\*\s+(?.+)$/.freeze
+
+ def run
+ result = execute(command, args: applicable_files)
+ return :pass if result.success?
+
+ result.stderr.scan(FILES_REGEX).flatten.
+ map { |file| message(file) }
+ end
+
+ private
+
+ def message(file)
+ Overcommit::Hook::Message.new(:error, file, nil, file)
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_commit/nginx_test.rb b/lib/overcommit/hook/pre_commit/nginx_test.rb
index cff43bbe..b681ded0 100644
--- a/lib/overcommit/hook/pre_commit/nginx_test.rb
+++ b/lib/overcommit/hook/pre_commit/nginx_test.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/
class NginxTest < Base
- MESSAGE_REGEX = /^nginx: .+ in (?.+):(?\d+)$/
+ MESSAGE_REGEX = /^nginx: .+ in (?.+):(?\d+)$/.freeze
def run
messages = []
diff --git a/lib/overcommit/hook/pre_commit/php_cs.rb b/lib/overcommit/hook/pre_commit/php_cs.rb
index 8e837406..41d2f2b4 100644
--- a/lib/overcommit/hook/pre_commit/php_cs.rb
+++ b/lib/overcommit/hook/pre_commit/php_cs.rb
@@ -4,7 +4,7 @@ module Overcommit::Hook::PreCommit
# Runs `phpcs` against any modified PHP files.
class PhpCs < Base
# Parse `phpcs` csv mode output
- MESSAGE_REGEX = /^\"(?.+)\",(?\d+),\d+,(?.+),\"(?.+)\"/
+ MESSAGE_REGEX = /^\"(?.+)\",(?\d+),\d+,(?.+),\"(?.+)\"/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
'error'.include?(type) ? :error : :warning
end
diff --git a/lib/overcommit/hook/pre_commit/php_cs_fixer.rb b/lib/overcommit/hook/pre_commit/php_cs_fixer.rb
index 02f8932e..87e63744 100644
--- a/lib/overcommit/hook/pre_commit/php_cs_fixer.rb
+++ b/lib/overcommit/hook/pre_commit/php_cs_fixer.rb
@@ -3,7 +3,7 @@
module Overcommit::Hook::PreCommit
# Runs `php-cs-fixer` against any modified PHP files.
class PhpCsFixer < Base
- MESSAGE_REGEX = /\s+\d+\)\s+(?.*\.php)(?\s+\(\w+(?:,\s+)?\))?/
+ MESSAGE_REGEX = /\s+\d+\)\s+(?.*\.php)(?\s+\(\w+(?:,\s+)?\))?/.freeze
def run
messages = []
diff --git a/lib/overcommit/hook/pre_commit/php_lint.rb b/lib/overcommit/hook/pre_commit/php_lint.rb
index 98582c3c..f2ba2f24 100644
--- a/lib/overcommit/hook/pre_commit/php_lint.rb
+++ b/lib/overcommit/hook/pre_commit/php_lint.rb
@@ -4,10 +4,10 @@ module Overcommit::Hook::PreCommit
# Runs `php -l` against any modified PHP files.
class PhpLint < Base
# Sample String
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
# PHP Parse error: syntax error, unexpected 'require_once' (T_REQUIRE_ONCE) in site/sumo.php on line 12
- # rubocop:enable Metrics/LineLength
- MESSAGE_REGEX = /^(?.+)\:\s+(?.+) in (?.+) on line (?\d+)/
+ # rubocop:enable Layout/LineLength
+ MESSAGE_REGEX = /^(?.+)\:\s+(?.+) in (?.+) on line (?\d+)/.freeze
def run
# A list of error messages
diff --git a/lib/overcommit/hook/pre_commit/php_stan.rb b/lib/overcommit/hook/pre_commit/php_stan.rb
index d9971ff0..9b8550f7 100644
--- a/lib/overcommit/hook/pre_commit/php_stan.rb
+++ b/lib/overcommit/hook/pre_commit/php_stan.rb
@@ -8,7 +8,7 @@ module Overcommit::Hook::PreCommit
# https://github.com/phpstan/phpstan/issues/239
# https://gist.github.com/edmondscommerce/89695c9cd2584fefdf540fb1c528d2c2
class PhpStan < Base
- MESSAGE_REGEX = /^(?.+)\:(?\d+)\:(?.+)/
+ MESSAGE_REGEX = /^(?.+)\:(?\d+)\:(?.+)/.freeze
def run
messages = []
diff --git a/lib/overcommit/hook/pre_commit/puppet_lint.rb b/lib/overcommit/hook/pre_commit/puppet_lint.rb
index c67c248a..2ddf3a10 100644
--- a/lib/overcommit/hook/pre_commit/puppet_lint.rb
+++ b/lib/overcommit/hook/pre_commit/puppet_lint.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://puppet-lint.com/
class PuppetLint < Base
- MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/
+ MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type == 'ERROR' ? :error : :warning
diff --git a/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb b/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb
index b020b79c..311a09c3 100644
--- a/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb
+++ b/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb
@@ -8,7 +8,7 @@ module Overcommit::Hook::PreCommit
# @see https://voxpupuli.org/blog/2014/11/06/linting-metadata-json/
#
class PuppetMetadataJsonLint < Base
- MESSAGE_REGEX = /\((?.*)\).*/
+ MESSAGE_REGEX = /\((?.*)\).*/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type == 'WARN' ? :warning : :error
diff --git a/lib/overcommit/hook/pre_commit/pyflakes.rb b/lib/overcommit/hook/pre_commit/pyflakes.rb
index 871f0594..ff7824c4 100644
--- a/lib/overcommit/hook/pre_commit/pyflakes.rb
+++ b/lib/overcommit/hook/pre_commit/pyflakes.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://pypi.python.org/pypi/pyflakes
class Pyflakes < Base
- MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/
+ MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/pylint.rb b/lib/overcommit/hook/pre_commit/pylint.rb
index 1ee92c9a..526bee2d 100644
--- a/lib/overcommit/hook/pre_commit/pylint.rb
+++ b/lib/overcommit/hook/pre_commit/pylint.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://www.pylint.org/
class Pylint < Base
- MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):(?[CEFRW])/
+ MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):(?[CEFRW])/.freeze
# Classify 'E' and 'F' message codes as errors,
# everything else as warnings.
diff --git a/lib/overcommit/hook/pre_commit/python_flake8.rb b/lib/overcommit/hook/pre_commit/python_flake8.rb
index 0e03bdf9..d506cf62 100644
--- a/lib/overcommit/hook/pre_commit/python_flake8.rb
+++ b/lib/overcommit/hook/pre_commit/python_flake8.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://pypi.python.org/pypi/flake8
class PythonFlake8 < Base
- MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):\d+:\s(?\w\d+)/
+ MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):\d+:\s(?\w\d+)/.freeze
# Classify 'Exxx' and 'Fxxx' message codes as errors,
# everything else as warnings.
diff --git a/lib/overcommit/hook/pre_commit/r_spec.rb b/lib/overcommit/hook/pre_commit/r_spec.rb
new file mode 100644
index 00000000..26bbc0a8
--- /dev/null
+++ b/lib/overcommit/hook/pre_commit/r_spec.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'overcommit/hook/shared/r_spec'
+
+module Overcommit::Hook::PreCommit
+ # Runs `rspec` test suite
+ #
+ # @see http://rspec.info/
+ class RSpec < Base
+ include Overcommit::Hook::Shared::RSpec
+ end
+end
diff --git a/lib/overcommit/hook/pre_commit/rails_best_practices.rb b/lib/overcommit/hook/pre_commit/rails_best_practices.rb
index bda0101f..7c3ba79a 100644
--- a/lib/overcommit/hook/pre_commit/rails_best_practices.rb
+++ b/lib/overcommit/hook/pre_commit/rails_best_practices.rb
@@ -7,7 +7,7 @@ module PreCommit
#
# @see https://github.com/railsbp/rails_best_practices
class RailsBestPractices < Base
- ERROR_REGEXP = /^(?(?:\w:)?[^:]+):(?\d+)\s-\s(?.+)/
+ ERROR_REGEXP = /^(?(?:\w:)?[^:]+):(?\d+)\s-\s(?.+)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb b/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb
index da0951bf..61725074 100644
--- a/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb
+++ b/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb
@@ -6,7 +6,7 @@ module Overcommit::Hook::PreCommit
# failure. The exception is if the schema is at version 0 (i.e before any
# migrations have been run). In this case it is OK if there are no migrations.
class RailsSchemaUpToDate < Base
- def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity
+ def run # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
if migration_files.any? && schema_files.none?
return :fail, "It looks like you're adding a migration, but did not update the schema file"
elsif migration_files.none? && schema_files.any? && non_zero_schema_version?
@@ -18,7 +18,7 @@ def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity
# their username.
latest_version = migration_files.map do |file|
File.basename(file)[/\d+/]
- end.sort.last
+ end.max
up_to_date = schema.include?(latest_version)
@@ -34,6 +34,12 @@ def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity
private
+ def encoding
+ return unless @config.key?('encoding')
+
+ { encoding: @config['encoding'] }.compact
+ end
+
def migration_files
@migration_files ||= applicable_files.select do |file|
file.match %r{db/migrate/.*\.rb}
@@ -47,7 +53,7 @@ def schema_files
end
def schema
- @schema ||= schema_files.map { |file| File.read(file) }.join
+ @schema ||= schema_files.map { |file| File.read(file, **(encoding || {})) }.join
@schema.tr('_', '')
end
diff --git a/lib/overcommit/hook/pre_commit/rst_lint.rb b/lib/overcommit/hook/pre_commit/rst_lint.rb
index 15f93584..5647508d 100644
--- a/lib/overcommit/hook/pre_commit/rst_lint.rb
+++ b/lib/overcommit/hook/pre_commit/rst_lint.rb
@@ -7,7 +7,7 @@ module Overcommit::Hook::PreCommit
class RstLint < Base
MESSAGE_REGEX = /
^(?INFO|WARNING|ERROR|SEVERE)(?(?:\w:)?[^:]+):(?\d+)\s(?.+)
- /x
+ /x.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/scalariform.rb b/lib/overcommit/hook/pre_commit/scalariform.rb
index 6b766dd0..63bf5e6c 100644
--- a/lib/overcommit/hook/pre_commit/scalariform.rb
+++ b/lib/overcommit/hook/pre_commit/scalariform.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://github.com/mdr/scalariform
class Scalariform < Base
- MESSAGE_REGEX = /^\[(?FAILED|ERROR)\]\s+(?(?:\w:)?.+)/
+ MESSAGE_REGEX = /^\[(?FAILED|ERROR)\]\s+(?(?:\w:)?.+)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/scalastyle.rb b/lib/overcommit/hook/pre_commit/scalastyle.rb
index 55c6dc4f..6494521d 100644
--- a/lib/overcommit/hook/pre_commit/scalastyle.rb
+++ b/lib/overcommit/hook/pre_commit/scalastyle.rb
@@ -10,7 +10,7 @@ class Scalastyle < Base
file=(?(?:\w:)?.+)\s
message=.+\s*
(line=(?\d+))?
- /x
+ /x.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/scss_lint.rb b/lib/overcommit/hook/pre_commit/scss_lint.rb
index 6d279cec..1bbaba93 100644
--- a/lib/overcommit/hook/pre_commit/scss_lint.rb
+++ b/lib/overcommit/hook/pre_commit/scss_lint.rb
@@ -18,9 +18,9 @@ def run
begin
collect_lint_messages(JSON.parse(result.stdout))
- rescue JSON::ParserError => ex
- return :fail, "Unable to parse JSON returned by SCSS-Lint: #{ex.message}\n" \
- "STDOUT: #{result.stdout}\nSTDERR: #{result.stderr}"
+ rescue JSON::ParserError => e
+ [:fail, "Unable to parse JSON returned by SCSS-Lint: #{e.message}\n" \
+ "STDOUT: #{result.stdout}\nSTDERR: #{result.stderr}"]
end
end
diff --git a/lib/overcommit/hook/pre_commit/semi_standard.rb b/lib/overcommit/hook/pre_commit/semi_standard.rb
index ce40119b..f6746d2f 100644
--- a/lib/overcommit/hook/pre_commit/semi_standard.rb
+++ b/lib/overcommit/hook/pre_commit/semi_standard.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://github.com/Flet/semistandard
class SemiStandard < Base
- MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/
+ MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/sorbet.rb b/lib/overcommit/hook/pre_commit/sorbet.rb
new file mode 100644
index 00000000..57988a6e
--- /dev/null
+++ b/lib/overcommit/hook/pre_commit/sorbet.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PreCommit
+ # Runs 'srb tc' against any modified files.
+ #
+ # @see https://github.com/sorbet/sorbet
+ class Sorbet < Base
+ # example of output:
+ # sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
+ MESSAGE_REGEX = /^(?[^:]+):(?\d+): (?.*)$/.freeze
+
+ def run
+ result = execute(command, args: applicable_files)
+ return :pass if result.success?
+
+ output = result.stderr.split("\n").grep(MESSAGE_REGEX)
+
+ extract_messages(
+ output,
+ MESSAGE_REGEX
+ )
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_commit/sqlint.rb b/lib/overcommit/hook/pre_commit/sqlint.rb
index 6568a987..2e5c5a88 100644
--- a/lib/overcommit/hook/pre_commit/sqlint.rb
+++ b/lib/overcommit/hook/pre_commit/sqlint.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://github.com/purcell/sqlint
class Sqlint < Base
- MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/
+ MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type == 'ERROR' ? :error : :warning
diff --git a/lib/overcommit/hook/pre_commit/standard.rb b/lib/overcommit/hook/pre_commit/standard.rb
index 4d2ee277..7b70ab2c 100644
--- a/lib/overcommit/hook/pre_commit/standard.rb
+++ b/lib/overcommit/hook/pre_commit/standard.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see https://github.com/feross/standard
class Standard < Base
- MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/
+ MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/stylelint.rb b/lib/overcommit/hook/pre_commit/stylelint.rb
index 403c5e27..aae26f08 100644
--- a/lib/overcommit/hook/pre_commit/stylelint.rb
+++ b/lib/overcommit/hook/pre_commit/stylelint.rb
@@ -8,12 +8,13 @@ class Stylelint < Base
# example of output:
# index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)
- MESSAGE_REGEX = /^(?.+):\D*(?\d+).*$/
+ MESSAGE_REGEX = /^(?[^:]+):\D*(?\d+).*$/.freeze
def run
result = execute(command, args: applicable_files)
- output = result.stdout.chomp
+ output = result.stdout + result.stderr.chomp
return :pass if result.success? && output.empty?
+
extract_messages(
output.split("\n"),
MESSAGE_REGEX
diff --git a/lib/overcommit/hook/pre_commit/swift_lint.rb b/lib/overcommit/hook/pre_commit/swift_lint.rb
index 40e4a5eb..a1d8ef09 100644
--- a/lib/overcommit/hook/pre_commit/swift_lint.rb
+++ b/lib/overcommit/hook/pre_commit/swift_lint.rb
@@ -4,7 +4,7 @@ module Overcommit::Hook::PreCommit
# Runs `swiftlint lint` against modified Swift files.
# @see https://github.com/realm/SwiftLint
class SwiftLint < Base
- MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+):(?.*)/
+ MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+):(?.*)/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/ts_lint.rb b/lib/overcommit/hook/pre_commit/ts_lint.rb
index 7f8cc503..1340cbcf 100644
--- a/lib/overcommit/hook/pre_commit/ts_lint.rb
+++ b/lib/overcommit/hook/pre_commit/ts_lint.rb
@@ -8,7 +8,7 @@ class TsLint < Base
# "src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140"
# or
# "ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef"
- MESSAGE_REGEX = /^(?.+: )?(?.+?(?=\[))[^\d]+(?\d+).*?/
+ MESSAGE_REGEX = /^(?.+: )?(?.+?(?=\[))[^\d]+(?\d+).*?/.freeze
def run
result = execute(command, args: applicable_files)
@@ -19,9 +19,9 @@ def run
type_categorizer = ->(type) { type.nil? || type.include?('ERROR') ? :error : :warning }
extract_messages(
- output_lines,
- MESSAGE_REGEX,
- type_categorizer
+ output_lines,
+ MESSAGE_REGEX,
+ type_categorizer
)
end
end
diff --git a/lib/overcommit/hook/pre_commit/w3c_css.rb b/lib/overcommit/hook/pre_commit/w3c_css.rb
index f36230f6..cb32c109 100644
--- a/lib/overcommit/hook/pre_commit/w3c_css.rb
+++ b/lib/overcommit/hook/pre_commit/w3c_css.rb
@@ -42,10 +42,10 @@ def validator
def opts
@opts ||= {
validator_uri: config['validator_uri'],
- proxy_server: config['proxy_server'],
- proxy_port: config['proxy_port'],
- proxy_user: config['proxy_user'],
- proxy_pass: config['proxy_pass']
+ proxy_server: config['proxy_server'],
+ proxy_port: config['proxy_port'],
+ proxy_user: config['proxy_user'],
+ proxy_pass: config['proxy_pass']
}
end
diff --git a/lib/overcommit/hook/pre_commit/w3c_html.rb b/lib/overcommit/hook/pre_commit/w3c_html.rb
index 5330ef4e..945e7997 100644
--- a/lib/overcommit/hook/pre_commit/w3c_html.rb
+++ b/lib/overcommit/hook/pre_commit/w3c_html.rb
@@ -42,10 +42,10 @@ def validator
def opts
@opts ||= {
validator_uri: config['validator_uri'],
- proxy_server: config['proxy_server'],
- proxy_port: config['proxy_port'],
- proxy_user: config['proxy_user'],
- proxy_pass: config['proxy_pass']
+ proxy_server: config['proxy_server'],
+ proxy_port: config['proxy_port'],
+ proxy_user: config['proxy_user'],
+ proxy_pass: config['proxy_pass']
}
end
diff --git a/lib/overcommit/hook/pre_commit/xml_lint.rb b/lib/overcommit/hook/pre_commit/xml_lint.rb
index 618f3e74..71cb403f 100644
--- a/lib/overcommit/hook/pre_commit/xml_lint.rb
+++ b/lib/overcommit/hook/pre_commit/xml_lint.rb
@@ -5,7 +5,7 @@ module Overcommit::Hook::PreCommit
#
# @see http://xmlsoft.org/xmllint.html
class XmlLint < Base
- MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/
+ MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze
def run
result = execute(command, args: applicable_files)
diff --git a/lib/overcommit/hook/pre_commit/xml_syntax.rb b/lib/overcommit/hook/pre_commit/xml_syntax.rb
index 7ac05360..99465182 100644
--- a/lib/overcommit/hook/pre_commit/xml_syntax.rb
+++ b/lib/overcommit/hook/pre_commit/xml_syntax.rb
@@ -7,12 +7,10 @@ def run
messages = []
applicable_files.each do |file|
- begin
- REXML::Document.new(IO.read(file))
- rescue REXML::ParseException => e
- error = "Error parsing #{file}: #{e.message}"
- messages << Overcommit::Hook::Message.new(:error, file, nil, error)
- end
+ REXML::Document.new(IO.read(file))
+ rescue REXML::ParseException => e
+ error = "Error parsing #{file}: #{e.message}"
+ messages << Overcommit::Hook::Message.new(:error, file, nil, error)
end
messages
diff --git a/lib/overcommit/hook/pre_commit/yaml_lint.rb b/lib/overcommit/hook/pre_commit/yaml_lint.rb
index 2b24a932..f780725f 100644
--- a/lib/overcommit/hook/pre_commit/yaml_lint.rb
+++ b/lib/overcommit/hook/pre_commit/yaml_lint.rb
@@ -5,13 +5,34 @@ module Overcommit::Hook::PreCommit
#
# @see https://github.com/adrienverge/yamllint
class YamlLint < Base
+ MESSAGE_REGEX = /
+ ^(?.+)
+ :(?\d+)
+ :(?\d+)
+ :\s\[(?\w+)\]
+ \s(?.+)$
+ /x.freeze
+
def run
result = execute(command, args: applicable_files)
+ parse_messages(result.stdout)
+ end
+
+ private
+
+ def parse_messages(output)
+ repo_root = Overcommit::Utils.repo_root
+
+ output.scan(MESSAGE_REGEX).map do |file, line, col, type, msg|
+ line = line.to_i
+ type = type.to_sym
+ # Obtain the path relative to the root of the repository
+ # for nicer output:
+ relpath = file.dup
+ relpath.slice!("#{repo_root}/")
- if result.success?
- :pass
- else
- return [:warn, result.stdout]
+ text = "#{relpath}:#{line}:#{col}:#{type} #{msg}"
+ Overcommit::Hook::Message.new(type, file, line, text)
end
end
end
diff --git a/lib/overcommit/hook/pre_commit/yaml_syntax.rb b/lib/overcommit/hook/pre_commit/yaml_syntax.rb
index fa0c4e66..83ff6789 100644
--- a/lib/overcommit/hook/pre_commit/yaml_syntax.rb
+++ b/lib/overcommit/hook/pre_commit/yaml_syntax.rb
@@ -7,14 +7,25 @@ def run
messages = []
applicable_files.each do |file|
+ YAML.load_file(file, aliases: true)
+ rescue ArgumentError
begin
YAML.load_file(file)
rescue ArgumentError, Psych::SyntaxError => e
messages << Overcommit::Hook::Message.new(:error, file, nil, e.message)
end
+ rescue Psych::DisallowedClass => e
+ messages << error_message(file, e)
end
messages
end
+
+ private
+
+ def error_message(file, error)
+ text = "#{file}: #{error.message}"
+ Overcommit::Hook::Message.new(:error, file, nil, text)
+ end
end
end
diff --git a/lib/overcommit/hook/pre_commit/yard_coverage.rb b/lib/overcommit/hook/pre_commit/yard_coverage.rb
index b9336f0f..c9188edb 100644
--- a/lib/overcommit/hook/pre_commit/yard_coverage.rb
+++ b/lib/overcommit/hook/pre_commit/yard_coverage.rb
@@ -1,4 +1,3 @@
-
# frozen_string_literal: true
module Overcommit::Hook::PreCommit
diff --git a/lib/overcommit/hook/pre_push/cargo_test.rb b/lib/overcommit/hook/pre_push/cargo_test.rb
index 33e5076b..ab782e0c 100644
--- a/lib/overcommit/hook/pre_push/cargo_test.rb
+++ b/lib/overcommit/hook/pre_push/cargo_test.rb
@@ -6,6 +6,7 @@ class CargoTest < Base
def run
result = execute(command)
return :pass if result.success?
+
[:fail, result.stdout]
end
end
diff --git a/lib/overcommit/hook/pre_push/flutter_test.rb b/lib/overcommit/hook/pre_push/flutter_test.rb
new file mode 100644
index 00000000..26c987cf
--- /dev/null
+++ b/lib/overcommit/hook/pre_push/flutter_test.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PrePush
+ # Runs Flutter test suite (`flutter test`) before push
+ #
+ # @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html
+ class FlutterTest < Base
+ def run
+ result = execute(command)
+ return :pass if result.success?
+
+ output = result.stdout + result.stderr
+ [:fail, output]
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_push/mix_test.rb b/lib/overcommit/hook/pre_push/mix_test.rb
new file mode 100644
index 00000000..3ce005e9
--- /dev/null
+++ b/lib/overcommit/hook/pre_push/mix_test.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PrePush
+ # Runs `mix test` test suite before push
+ #
+ # @see https://hexdocs.pm/mix/Mix.Tasks.Test.html
+ class MixTest < Base
+ def run
+ result = execute(command)
+ return :pass if result.success?
+
+ output = result.stdout + result.stderr
+ [:fail, output]
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_push/pub_test.rb b/lib/overcommit/hook/pre_push/pub_test.rb
new file mode 100644
index 00000000..0dbfb0bf
--- /dev/null
+++ b/lib/overcommit/hook/pre_push/pub_test.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::PrePush
+ # Runs Dart test suite (`pub run test`) before push
+ #
+ # @see https://pub.dev/packages/test#running-tests
+ class PubTest < Base
+ def run
+ result = execute(command)
+ return :pass if result.success?
+
+ output = result.stdout + result.stderr
+ [:fail, output]
+ end
+ end
+end
diff --git a/lib/overcommit/hook/pre_push/r_spec.rb b/lib/overcommit/hook/pre_push/r_spec.rb
index a3b4474c..7ce3df29 100644
--- a/lib/overcommit/hook/pre_push/r_spec.rb
+++ b/lib/overcommit/hook/pre_push/r_spec.rb
@@ -1,16 +1,12 @@
# frozen_string_literal: true
+require 'overcommit/hook/shared/r_spec'
+
module Overcommit::Hook::PrePush
- # Runs `rspec` test suite before push
+ # Runs `rspec` test suite
#
# @see http://rspec.info/
class RSpec < Base
- def run
- result = execute(command)
- return :pass if result.success?
-
- output = result.stdout + result.stderr
- [:fail, output]
- end
+ include Overcommit::Hook::Shared::RSpec
end
end
diff --git a/lib/overcommit/hook/prepare_commit_msg/base.rb b/lib/overcommit/hook/prepare_commit_msg/base.rb
index 5c0c68d4..a4fbf31e 100644
--- a/lib/overcommit/hook/prepare_commit_msg/base.rb
+++ b/lib/overcommit/hook/prepare_commit_msg/base.rb
@@ -12,6 +12,7 @@ class Base < Overcommit::Hook::Base
def modify_commit_message
raise 'This expects a block!' unless block_given?
+
# NOTE: this assumes all the hooks of the same type share the context's
# memory. If that's not the case, this won't work.
lock.synchronize do
diff --git a/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
index c64f4c22..8852b0b5 100644
--- a/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
+++ b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
@@ -2,11 +2,37 @@
module Overcommit::Hook::PrepareCommitMsg
# Prepends the commit message with a message based on the branch name.
+ #
+ # === What to prepend
+ #
# It's possible to reference parts of the branch name through the captures in
# the `branch_pattern` regex.
+ #
+ # For instance, if your current branch is `123-topic` then this config
+ #
+ # branch_pattern: '(\d+)-(\w+)'
+ # replacement_text: '[#\1] '
+ #
+ # would make this hook prepend commit messages with `[#123] `.
+ #
+ # Similarly, a replacement text of `[\1][\2]` would result in `[123][topic]`.
+ #
+ # == When to run this hook
+ #
+ # You can configure this to run only for specific types of commits by setting
+ # the `skipped_commit_types`. The allowed types are
+ #
+ # - 'message' - if message is given via `-m`, `-F`
+ # - 'template' - if `-t` is given or `commit.template` is set
+ # - 'commit' - if `-c`, `-C`, or `--amend` is given
+ # - 'merge' - if merging
+ # - 'squash' - if squashing
+ #
class ReplaceBranch < Base
+ DEFAULT_BRANCH_PATTERN = /\A(\d+)-(\w+).*\z/.freeze
+
def run
- return :pass if skipped_commit_types.include? commit_message_source
+ return :pass if skip?
Overcommit::Utils.log.debug(
"Checking if '#{Overcommit::GitRepo.current_branch}' matches #{branch_pattern}"
@@ -17,21 +43,25 @@ def run
Overcommit::Utils.log.debug("Writing #{commit_message_filename} with #{new_template}")
modify_commit_message do |old_contents|
- "#{new_template} #{old_contents}"
+ "#{new_template}#{old_contents}"
end
:pass
end
def new_template
- @new_template ||= Overcommit::GitRepo.current_branch.gsub(branch_pattern, replacement_text)
+ @new_template ||=
+ begin
+ curr_branch = Overcommit::GitRepo.current_branch
+ curr_branch.gsub(branch_pattern, replacement_text)
+ end
end
def branch_pattern
@branch_pattern ||=
begin
pattern = config['branch_pattern']
- Regexp.new((pattern || '').empty? ? '\A.*\w+[-_](\d+).*\z' : pattern)
+ Regexp.new((pattern || '').empty? ? DEFAULT_BRANCH_PATTERN : pattern)
end
end
@@ -39,7 +69,7 @@ def replacement_text
@replacement_text ||=
begin
if File.exist?(replacement_text_config)
- File.read(replacement_text_config)
+ File.read(replacement_text_config).chomp
else
replacement_text_config
end
@@ -53,5 +83,9 @@ def replacement_text_config
def skipped_commit_types
@skipped_commit_types ||= config['skipped_commit_types'].map(&:to_sym)
end
+
+ def skip?
+ super || skipped_commit_types.include?(commit_message_source)
+ end
end
end
diff --git a/lib/overcommit/hook/shared/bower_install.rb b/lib/overcommit/hook/shared/bower_install.rb
index b39d4ada..cfcde92f 100644
--- a/lib/overcommit/hook/shared/bower_install.rb
+++ b/lib/overcommit/hook/shared/bower_install.rb
@@ -9,6 +9,7 @@ module BowerInstall
def run
result = execute(command)
return :fail, result.stderr unless result.success?
+
:pass
end
end
diff --git a/lib/overcommit/hook/shared/bundle_install.rb b/lib/overcommit/hook/shared/bundle_install.rb
index 060e30c5..01acaa32 100644
--- a/lib/overcommit/hook/shared/bundle_install.rb
+++ b/lib/overcommit/hook/shared/bundle_install.rb
@@ -9,6 +9,7 @@ module BundleInstall
def run
result = execute(command)
return :fail, result.stdout unless result.success?
+
:pass
end
end
diff --git a/lib/overcommit/hook/shared/composer_install.rb b/lib/overcommit/hook/shared/composer_install.rb
index 839dd65b..2d2ff01d 100644
--- a/lib/overcommit/hook/shared/composer_install.rb
+++ b/lib/overcommit/hook/shared/composer_install.rb
@@ -9,6 +9,7 @@ module ComposerInstall
def run
result = execute(command)
return :fail, result.stdout unless result.success?
+
:pass
end
end
diff --git a/lib/overcommit/hook/shared/npm_install.rb b/lib/overcommit/hook/shared/npm_install.rb
index 7fa06772..f487c8e7 100644
--- a/lib/overcommit/hook/shared/npm_install.rb
+++ b/lib/overcommit/hook/shared/npm_install.rb
@@ -9,6 +9,7 @@ module NpmInstall
def run
result = execute(command)
return :fail, result.stderr unless result.success?
+
:pass
end
end
diff --git a/lib/overcommit/hook/shared/pronto.rb b/lib/overcommit/hook/shared/pronto.rb
index b1d71442..cf8a3139 100644
--- a/lib/overcommit/hook/shared/pronto.rb
+++ b/lib/overcommit/hook/shared/pronto.rb
@@ -1,21 +1,33 @@
# frozen_string_literal: true
module Overcommit::Hook::Shared
- # Shared code used by all Pronto hooks. Runs pronto linter.
+ # Shared code used by all Pronto hooks. Runs pronto linters.
+
+ # @see https://github.com/prontolabs/pronto
module Pronto
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
type.include?('E') ? :error : :warning
end
+ MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+) (?[^ ]+)/.freeze
+
def run
result = execute(command)
return :pass if result.success?
- extract_messages(
- result.stdout.split("\n"),
- /^(?(?:\w:)?[^:]+):(?\d+) (?[^ ]+)/,
+ # e.g. runtime errors
+ generic_errors = extract_messages(
+ result.stderr.split("\n"),
+ /^(?[a-z]+)/i
+ )
+
+ pronto_infractions = extract_messages(
+ result.stdout.split("\n").select { |line| line.match?(MESSAGE_REGEX) },
+ MESSAGE_REGEX,
MESSAGE_TYPE_CATEGORIZER,
)
+
+ generic_errors + pronto_infractions
end
end
end
diff --git a/lib/overcommit/hook/shared/r_spec.rb b/lib/overcommit/hook/shared/r_spec.rb
new file mode 100644
index 00000000..9b41e8e1
--- /dev/null
+++ b/lib/overcommit/hook/shared/r_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Overcommit::Hook::Shared
+ # Runs `rspec` test suite before push
+ #
+ # @see http://rspec.info/
+ module RSpec
+ def run
+ result = if @config['include']
+ execute(command, args: applicable_files)
+ else
+ execute(command)
+ end
+
+ return :pass if result.success?
+
+ output = result.stdout + result.stderr
+ [:fail, output]
+ end
+ end
+end
diff --git a/lib/overcommit/hook/shared/yarn_install.rb b/lib/overcommit/hook/shared/yarn_install.rb
index bc3b7869..fd548f07 100644
--- a/lib/overcommit/hook/shared/yarn_install.rb
+++ b/lib/overcommit/hook/shared/yarn_install.rb
@@ -9,6 +9,7 @@ module YarnInstall
def run
result = execute(command)
return :fail, result.stderr unless result.success?
+
:pass
end
end
diff --git a/lib/overcommit/hook_context.rb b/lib/overcommit/hook_context.rb
index df12b86c..5863ed94 100644
--- a/lib/overcommit/hook_context.rb
+++ b/lib/overcommit/hook_context.rb
@@ -2,18 +2,18 @@
# Utility module which manages the creation of {HookContext}s.
module Overcommit::HookContext
- def self.create(hook_type, config, args, input)
+ def self.create(hook_type, config, args, input, **cli_options)
hook_type_class = Overcommit::Utils.camel_case(hook_type)
underscored_hook_type = Overcommit::Utils.snake_case(hook_type)
require "overcommit/hook_context/#{underscored_hook_type}"
- Overcommit::HookContext.const_get(hook_type_class).new(config, args, input)
- rescue LoadError, NameError => error
+ Overcommit::HookContext.const_get(hook_type_class).new(config, args, input, **cli_options)
+ rescue LoadError, NameError => e
# Could happen when a symlink was created for a hook type Overcommit does
# not yet support.
raise Overcommit::Exceptions::HookContextLoadError,
- "Unable to load '#{hook_type}' hook context: '#{error}'",
- error.backtrace
+ "Unable to load '#{hook_type}' hook context: '#{e}'",
+ e.backtrace
end
end
diff --git a/lib/overcommit/hook_context/base.rb b/lib/overcommit/hook_context/base.rb
index 077394fb..b50698c9 100644
--- a/lib/overcommit/hook_context/base.rb
+++ b/lib/overcommit/hook_context/base.rb
@@ -18,10 +18,12 @@ class Base
# @param config [Overcommit::Configuration]
# @param args [Array]
# @param input [IO] standard input stream
- def initialize(config, args, input)
+ # @param options [Hash] cli options
+ def initialize(config, args, input, **options)
@config = config
@args = args
@input = input
+ @options = options
end
# Executes a command as if it were a regular git hook, passing all
diff --git a/lib/overcommit/hook_context/commit_msg.rb b/lib/overcommit/hook_context/commit_msg.rb
index e73aa3ff..ae32e55d 100644
--- a/lib/overcommit/hook_context/commit_msg.rb
+++ b/lib/overcommit/hook_context/commit_msg.rb
@@ -1,8 +1,15 @@
# frozen_string_literal: true
+require_relative 'pre_commit'
+require_relative 'helpers/stash_unstaged_changes'
+require_relative 'helpers/file_modifications'
+
module Overcommit::HookContext
# Contains helpers related to contextual information used by commit-msg hooks.
class CommitMsg < Base
+ include Overcommit::HookContext::Helpers::StashUnstagedChanges
+ include Overcommit::HookContext::Helpers::FileModifications
+
def empty_message?
commit_message.strip.empty?
end
diff --git a/lib/overcommit/hook_context/diff.rb b/lib/overcommit/hook_context/diff.rb
new file mode 100644
index 00000000..3e9aa568
--- /dev/null
+++ b/lib/overcommit/hook_context/diff.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'overcommit/git_repo'
+
+require 'set'
+
+module Overcommit::HookContext
+ # Simulates a pre-commit context based on the diff with another git ref.
+ #
+ # This results in pre-commit hooks running against the changes between the current
+ # and another ref, which is useful for automated CI scripts.
+ class Diff < Base
+ def modified_files
+ @modified_files ||= Overcommit::GitRepo.modified_files(refs: @options[:diff])
+ end
+
+ def modified_lines_in_file(file)
+ @modified_lines ||= {}
+ @modified_lines[file] ||= Overcommit::GitRepo.extract_modified_lines(file,
+ refs: @options[:diff])
+ end
+
+ def hook_class_name
+ 'PreCommit'
+ end
+
+ def hook_type_name
+ 'pre_commit'
+ end
+
+ def hook_script_name
+ 'pre-commit'
+ end
+
+ def initial_commit?
+ @initial_commit ||= Overcommit::GitRepo.initial_commit?
+ end
+ end
+end
diff --git a/lib/overcommit/hook_context/helpers/file_modifications.rb b/lib/overcommit/hook_context/helpers/file_modifications.rb
new file mode 100644
index 00000000..00aaccc8
--- /dev/null
+++ b/lib/overcommit/hook_context/helpers/file_modifications.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+module Overcommit::HookContext
+ module Helpers
+ # This module contains methods for determining what files were changed and on what unique line
+ # numbers did the change occur.
+ module FileModifications
+ # Returns whether this hook run was triggered by `git commit --amend`
+ def amendment?
+ return @amendment unless @amendment.nil?
+
+ cmd = Overcommit::Utils.parent_command
+ return unless cmd
+
+ amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'
+
+ # Since the ps command can return invalid byte sequences for commands
+ # containing unicode characters, we replace the offending characters,
+ # since the pattern we're looking for will consist of ASCII characters
+ unless cmd.valid_encoding?
+ cmd = Overcommit::Utils.
+ parent_command.
+ encode('UTF-16be', invalid: :replace, replace: '?').
+ encode('UTF-8')
+ end
+
+ return @amendment if
+ # True if the command is a commit with the --amend flag
+ @amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?
+
+ # Check for git aliases that call `commit --amend`
+ `git config --get-regexp "^alias\\." "#{amend_pattern}"`.
+ scan(/alias\.([-\w]+)/). # Extract the alias
+ each do |match|
+ return @amendment if
+ # True if the command uses a git alias for `commit --amend`
+ @amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
+ end
+
+ @amendment
+ end
+
+ # Get a list of added, copied, or modified files that have been staged.
+ # Renames and deletions are ignored, since there should be nothing to check.
+ def modified_files
+ unless @modified_files
+ currently_staged = Overcommit::GitRepo.modified_files(staged: true)
+ @modified_files = currently_staged
+
+ # Include files modified in last commit if amending
+ if amendment?
+ subcmd = 'show --format=%n'
+ previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
+ @modified_files |= filter_modified_files(previously_modified)
+ end
+ end
+ @modified_files
+ end
+
+ # Returns the set of line numbers corresponding to the lines that were
+ # changed in a specified file.
+ def modified_lines_in_file(file)
+ @modified_lines ||= {}
+ unless @modified_lines[file]
+ @modified_lines[file] =
+ Overcommit::GitRepo.extract_modified_lines(file, staged: true)
+
+ # Include lines modified in last commit if amending
+ if amendment?
+ subcmd = 'show --format=%n'
+ @modified_lines[file] +=
+ Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
+ end
+ end
+ @modified_lines[file]
+ end
+ end
+ end
+end
diff --git a/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb b/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb
new file mode 100644
index 00000000..17400539
--- /dev/null
+++ b/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+module Overcommit::HookContext
+ module Helpers
+ # This module contains behavior for stashing unstaged changes before hooks are ran and restoring
+ # them afterwards
+ module StashUnstagedChanges
+ # Stash unstaged contents of files so hooks don't see changes that aren't
+ # about to be committed.
+ def setup_environment
+ store_modified_times
+ Overcommit::GitRepo.store_merge_state
+ Overcommit::GitRepo.store_cherry_pick_state
+
+ # Don't attempt to stash changes if all changes are staged, as this
+ # prevents us from modifying files at all, which plays better with
+ # editors/tools which watch for file changes.
+ if !initial_commit? && unstaged_changes?
+ stash_changes
+
+ # While running hooks make it appear as if nothing changed
+ restore_modified_times
+ end
+ end
+
+ # Returns whether the current git branch is empty (has no commits).
+ def initial_commit?
+ return @initial_commit unless @initial_commit.nil?
+
+ @initial_commit = Overcommit::GitRepo.initial_commit?
+ end
+
+ # Restore unstaged changes and reset file modification times so it appears
+ # as if nothing ever changed.
+ #
+ # We want to restore the modification times for each of the files after
+ # every step to ensure as little time as possible has passed while the
+ # modification time on the file was newer. This helps us play more nicely
+ # with file watchers.
+ def cleanup_environment
+ if @changes_stashed
+ clear_working_tree
+ restore_working_tree
+ restore_modified_times
+ end
+
+ Overcommit::GitRepo.restore_merge_state
+ Overcommit::GitRepo.restore_cherry_pick_state
+ end
+
+ private
+
+ # Stores the modification times for all modified files to make it appear like
+ # they never changed.
+ #
+ # This prevents (some) editors from complaining about files changing when we
+ # stash changes before running the hooks.
+ def store_modified_times
+ @modified_times = {}
+
+ staged_files = modified_files
+ unstaged_files = Overcommit::GitRepo.modified_files(staged: false)
+
+ (staged_files + unstaged_files).each do |file|
+ next if Overcommit::Utils.broken_symlink?(file)
+ next unless File.exist?(file) # Ignore renamed files (old file no longer exists)
+
+ @modified_times[file] = File.mtime(file)
+ end
+ end
+
+ # Returns whether there are any changes to tracked files which have not yet
+ # been staged.
+ def unstaged_changes?
+ result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])
+ !result.success?
+ end
+
+ def stash_changes
+ @stash_attempted = true
+
+ stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}"
+ result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do
+ Overcommit::Utils.execute(
+ %w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]
+ )
+ end
+
+ unless result.success?
+ # Failure to stash in this case is likely due to a configuration
+ # issue (e.g. author/email not set or GPG signing key incorrect)
+ raise Overcommit::Exceptions::HookSetupFailed,
+ "Unable to setup environment for #{hook_script_name} hook run:" \
+ "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
+ end
+
+ @changes_stashed = `git stash list -1`.include?(stash_message)
+ end
+
+ # Restores the file modification times for all modified files to make it
+ # appear like they never changed.
+ def restore_modified_times
+ @modified_times.each do |file, time|
+ next if Overcommit::Utils.broken_symlink?(file)
+ next unless File.exist?(file)
+
+ File.utime(time, time, file)
+ end
+ end
+
+ # Clears the working tree so that the stash can be applied.
+ def clear_working_tree
+ removed_submodules = Overcommit::GitRepo.staged_submodule_removals
+
+ result = Overcommit::Utils.execute(%w[git reset --hard])
+ unless result.success?
+ raise Overcommit::Exceptions::HookCleanupFailed,
+ "Unable to cleanup working tree after #{hook_script_name} hooks run:" \
+ "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
+ end
+
+ # Hard-resetting a staged submodule removal results in the index being
+ # reset but the submodule being restored as an empty directory. This empty
+ # directory prevents us from stashing on a subsequent run if a hook fails.
+ #
+ # Work around this by removing these empty submodule directories as there
+ # doesn't appear any reason to keep them around.
+ removed_submodules.each do |submodule|
+ FileUtils.rmdir(submodule.path)
+ end
+ end
+
+ # Applies the stash to the working tree to restore the user's state.
+ def restore_working_tree
+ result = Overcommit::Utils.execute(%w[git stash pop --index])
+ unless result.success?
+ raise Overcommit::Exceptions::HookCleanupFailed,
+ "Unable to restore working tree after #{hook_script_name} hooks run:" \
+ "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/overcommit/hook_context/post_commit.rb b/lib/overcommit/hook_context/post_commit.rb
index 0e780077..8327b0d9 100644
--- a/lib/overcommit/hook_context/post_commit.rb
+++ b/lib/overcommit/hook_context/post_commit.rb
@@ -27,6 +27,7 @@ def modified_lines_in_file(file)
# @return [true,false]
def initial_commit?
return @initial_commit unless @initial_commit.nil?
+
@initial_commit = !Overcommit::Utils.execute(%w[git rev-parse HEAD~]).success?
end
end
diff --git a/lib/overcommit/hook_context/pre_commit.rb b/lib/overcommit/hook_context/pre_commit.rb
index 6c73cb3b..cd510605 100644
--- a/lib/overcommit/hook_context/pre_commit.rb
+++ b/lib/overcommit/hook_context/pre_commit.rb
@@ -2,6 +2,8 @@
require 'fileutils'
require 'set'
+require_relative 'helpers/stash_unstaged_changes'
+require_relative 'helpers/file_modifications'
module Overcommit::HookContext
# Contains helpers related to contextual information used by pre-commit hooks.
@@ -9,204 +11,8 @@ module Overcommit::HookContext
# This includes staged files, which lines of those files have been modified,
# etc. It is also responsible for saving/restoring the state of the repo so
# hooks only inspect staged changes.
- class PreCommit < Base # rubocop:disable ClassLength
- # Returns whether this hook run was triggered by `git commit --amend`
- def amendment?
- return @amendment unless @amendment.nil?
-
- cmd = Overcommit::Utils.parent_command
- return unless cmd
- amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'
-
- # Since the ps command can return invalid byte sequences for commands
- # containing unicode characters, we replace the offending characters,
- # since the pattern we're looking for will consist of ASCII characters
- unless cmd.valid_encoding?
- cmd = Overcommit::Utils.parent_command.encode('UTF-16be', invalid: :replace, replace: '?').
- encode('UTF-8')
- end
-
- return @amendment if
- # True if the command is a commit with the --amend flag
- @amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?
-
- # Check for git aliases that call `commit --amend`
- `git config --get-regexp "^alias\\." "#{amend_pattern}"`.
- scan(/alias\.([-\w]+)/). # Extract the alias
- each do |match|
- return @amendment if
- # True if the command uses a git alias for `commit --amend`
- @amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
- end
-
- @amendment
- end
-
- # Stash unstaged contents of files so hooks don't see changes that aren't
- # about to be committed.
- def setup_environment
- store_modified_times
- Overcommit::GitRepo.store_merge_state
- Overcommit::GitRepo.store_cherry_pick_state
-
- # Don't attempt to stash changes if all changes are staged, as this
- # prevents us from modifying files at all, which plays better with
- # editors/tools which watch for file changes.
- if !initial_commit? && unstaged_changes?
- stash_changes
-
- # While running hooks make it appear as if nothing changed
- restore_modified_times
- end
- end
-
- # Restore unstaged changes and reset file modification times so it appears
- # as if nothing ever changed.
- #
- # We want to restore the modification times for each of the files after
- # every step to ensure as little time as possible has passed while the
- # modification time on the file was newer. This helps us play more nicely
- # with file watchers.
- def cleanup_environment
- if @changes_stashed
- clear_working_tree
- restore_working_tree
- restore_modified_times
- end
-
- Overcommit::GitRepo.restore_merge_state
- Overcommit::GitRepo.restore_cherry_pick_state
- end
-
- # Get a list of added, copied, or modified files that have been staged.
- # Renames and deletions are ignored, since there should be nothing to check.
- def modified_files
- unless @modified_files
- currently_staged = Overcommit::GitRepo.modified_files(staged: true)
- @modified_files = currently_staged
-
- # Include files modified in last commit if amending
- if amendment?
- subcmd = 'show --format=%n'
- previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
- @modified_files |= filter_modified_files(previously_modified)
- end
- end
- @modified_files
- end
-
- # Returns the set of line numbers corresponding to the lines that were
- # changed in a specified file.
- def modified_lines_in_file(file)
- @modified_lines ||= {}
- unless @modified_lines[file]
- @modified_lines[file] =
- Overcommit::GitRepo.extract_modified_lines(file, staged: true)
-
- # Include lines modified in last commit if amending
- if amendment?
- subcmd = 'show --format=%n'
- @modified_lines[file] +=
- Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
- end
- end
- @modified_lines[file]
- end
-
- # Returns whether the current git branch is empty (has no commits).
- def initial_commit?
- return @initial_commit unless @initial_commit.nil?
- @initial_commit = Overcommit::GitRepo.initial_commit?
- end
-
- private
-
- def stash_changes
- @stash_attempted = true
-
- stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}"
- result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do
- Overcommit::Utils.execute(
- %w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]
- )
- end
-
- unless result.success?
- # Failure to stash in this case is likely due to a configuration
- # issue (e.g. author/email not set or GPG signing key incorrect)
- raise Overcommit::Exceptions::HookSetupFailed,
- "Unable to setup environment for #{hook_script_name} hook run:" \
- "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
- end
-
- @changes_stashed = `git stash list -1`.include?(stash_message)
- end
-
- # Clears the working tree so that the stash can be applied.
- def clear_working_tree
- removed_submodules = Overcommit::GitRepo.staged_submodule_removals
-
- result = Overcommit::Utils.execute(%w[git reset --hard])
- unless result.success?
- raise Overcommit::Exceptions::HookCleanupFailed,
- "Unable to cleanup working tree after #{hook_script_name} hooks run:" \
- "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
- end
-
- # Hard-resetting a staged submodule removal results in the index being
- # reset but the submodule being restored as an empty directory. This empty
- # directory prevents us from stashing on a subsequent run if a hook fails.
- #
- # Work around this by removing these empty submodule directories as there
- # doesn't appear any reason to keep them around.
- removed_submodules.each do |submodule|
- FileUtils.rmdir(submodule.path)
- end
- end
-
- # Applies the stash to the working tree to restore the user's state.
- def restore_working_tree
- result = Overcommit::Utils.execute(%w[git stash pop --index --quiet])
- unless result.success?
- raise Overcommit::Exceptions::HookCleanupFailed,
- "Unable to restore working tree after #{hook_script_name} hooks run:" \
- "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
- end
- end
-
- # Returns whether there are any changes to tracked files which have not yet
- # been staged.
- def unstaged_changes?
- result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])
- !result.success?
- end
-
- # Stores the modification times for all modified files to make it appear like
- # they never changed.
- #
- # This prevents (some) editors from complaining about files changing when we
- # stash changes before running the hooks.
- def store_modified_times
- @modified_times = {}
-
- staged_files = modified_files
- unstaged_files = Overcommit::GitRepo.modified_files(staged: false)
-
- (staged_files + unstaged_files).each do |file|
- next if Overcommit::Utils.broken_symlink?(file)
- next unless File.exist?(file) # Ignore renamed files (old file no longer exists)
- @modified_times[file] = File.mtime(file)
- end
- end
-
- # Restores the file modification times for all modified files to make it
- # appear like they never changed.
- def restore_modified_times
- @modified_times.each do |file, time|
- next if Overcommit::Utils.broken_symlink?(file)
- next unless File.exist?(file)
- File.utime(time, time, file)
- end
- end
+ class PreCommit < Base
+ include Overcommit::HookContext::Helpers::StashUnstagedChanges
+ include Overcommit::HookContext::Helpers::FileModifications
end
end
diff --git a/lib/overcommit/hook_context/pre_push.rb b/lib/overcommit/hook_context/pre_push.rb
index 0b2e72a1..39ec19ba 100644
--- a/lib/overcommit/hook_context/pre_push.rb
+++ b/lib/overcommit/hook_context/pre_push.rb
@@ -76,6 +76,7 @@ def ref_range
def overwritten_commits
return @overwritten_commits if defined? @overwritten_commits
+
result = Overcommit::Subprocess.spawn(%W[git rev-list #{remote_sha1} ^#{local_sha1}])
if result.success?
result.stdout.split("\n")
diff --git a/lib/overcommit/hook_context/run_all.rb b/lib/overcommit/hook_context/run_all.rb
index e7acee57..5ed7efa0 100644
--- a/lib/overcommit/hook_context/run_all.rb
+++ b/lib/overcommit/hook_context/run_all.rb
@@ -36,6 +36,7 @@ def hook_script_name
def initial_commit?
return @initial_commit unless @initial_commit.nil?
+
@initial_commit = Overcommit::GitRepo.initial_commit?
end
diff --git a/lib/overcommit/hook_loader/base.rb b/lib/overcommit/hook_loader/base.rb
index 93e83ccc..9173e9f1 100644
--- a/lib/overcommit/hook_loader/base.rb
+++ b/lib/overcommit/hook_loader/base.rb
@@ -38,10 +38,10 @@ def create_hook(hook_name)
Overcommit::Hook.const_get(@context.hook_class_name).
const_get(hook_name).
new(@config, @context)
- rescue LoadError, NameError => error
+ rescue LoadError, NameError => e
raise Overcommit::Exceptions::HookLoadError,
- "Unable to load hook '#{hook_name}': #{error}",
- error.backtrace
+ "Unable to load hook '#{hook_name}': #{e}",
+ e.backtrace
end
end
end
diff --git a/lib/overcommit/hook_loader/plugin_hook_loader.rb b/lib/overcommit/hook_loader/plugin_hook_loader.rb
index b57d7095..d168a0de 100644
--- a/lib/overcommit/hook_loader/plugin_hook_loader.rb
+++ b/lib/overcommit/hook_loader/plugin_hook_loader.rb
@@ -93,10 +93,10 @@ def run
end
hook_module.const_set(hook_name, hook_class).new(@config, @context)
- rescue LoadError, NameError => error
+ rescue LoadError, NameError => e
raise Overcommit::Exceptions::HookLoadError,
- "Unable to load hook '#{hook_name}': #{error}",
- error.backtrace
+ "Unable to load hook '#{hook_name}': #{e}",
+ e.backtrace
end
end
end
diff --git a/lib/overcommit/hook_runner.rb b/lib/overcommit/hook_runner.rb
index 8ac01e99..c7c313ab 100644
--- a/lib/overcommit/hook_runner.rb
+++ b/lib/overcommit/hook_runner.rb
@@ -94,6 +94,7 @@ def consume
loop do
hook = @lock.synchronize { @hooks_left.pop }
break unless hook
+
run_hook(hook)
end
end
@@ -159,12 +160,12 @@ def run_hook(hook) # rubocop:disable Metrics/CyclomaticComplexity
return if should_skip?(hook)
status, output = hook.run_and_transform
- rescue Overcommit::Exceptions::MessageProcessingError => ex
+ rescue Overcommit::Exceptions::MessageProcessingError => e
status = :fail
- output = ex.message
- rescue StandardError => ex
+ output = e.message
+ rescue StandardError => e
status = :fail
- output = "Hook raised unexpected error\n#{ex.message}\n#{ex.backtrace.join("\n")}"
+ output = "Hook raised unexpected error\n#{e.message}\n#{e.backtrace.join("\n")}"
end
@failed = true if status == :fail
@@ -202,7 +203,7 @@ def load_hooks
# Load plugin hooks after so they can subclass existing hooks
@hooks += HookLoader::PluginHookLoader.new(@config, @context, @log).load_hooks
- rescue LoadError => ex
+ rescue LoadError => e
# Include a more helpful message that will probably save some confusion
message = 'A load error occurred. ' +
if @config['gemfile']
@@ -212,8 +213,8 @@ def load_hooks
end
raise Overcommit::Exceptions::HookLoadError,
- "#{message}\n#{ex.message}",
- ex.backtrace
+ "#{message}\n#{e.message}",
+ e.backtrace
end
end
end
diff --git a/lib/overcommit/hook_signer.rb b/lib/overcommit/hook_signer.rb
index f432ca48..fab99c37 100644
--- a/lib/overcommit/hook_signer.rb
+++ b/lib/overcommit/hook_signer.rb
@@ -52,6 +52,7 @@ def hook_path
def signable_file?(file)
return unless file
+
sep = Overcommit::OS.windows? ? '\\' : File::SEPARATOR
file.start_with?(".#{sep}") ||
file.start_with?(Overcommit::Utils.repo_root)
diff --git a/lib/overcommit/installer.rb b/lib/overcommit/installer.rb
index 11d598e8..79287b30 100644
--- a/lib/overcommit/installer.rb
+++ b/lib/overcommit/installer.rb
@@ -4,7 +4,7 @@
module Overcommit
# Manages the installation of Overcommit hooks in a git repository.
- class Installer # rubocop:disable ClassLength
+ class Installer # rubocop:disable Metrics/ClassLength
TEMPLATE_DIRECTORY = File.join(Overcommit::HOME, 'template-dir')
MASTER_HOOK = File.join(TEMPLATE_DIRECTORY, 'hooks', 'overcommit-hook')
@@ -174,6 +174,7 @@ def install_starter_config
repo_config_file = File.join(@target, Overcommit::CONFIG_FILE_NAME)
return if File.exist?(repo_config_file)
+
FileUtils.cp(File.join(Overcommit::HOME, 'config', 'starter.yml'), repo_config_file)
end
diff --git a/lib/overcommit/logger.rb b/lib/overcommit/logger.rb
index a9ff793c..9f5d4248 100644
--- a/lib/overcommit/logger.rb
+++ b/lib/overcommit/logger.rb
@@ -31,6 +31,11 @@ def newline
log
end
+ # Flushes the [IO] object for partial lines
+ def flush
+ @out.flush if @out.respond_to? :flush
+ end
+
# Write a line of output.
#
# A newline character will always be appended.
diff --git a/lib/overcommit/printer.rb b/lib/overcommit/printer.rb
index 02d0995d..fcb69653 100644
--- a/lib/overcommit/printer.rb
+++ b/lib/overcommit/printer.rb
@@ -43,16 +43,14 @@ def end_hook(hook, status, output)
end
def interrupt_triggered
- log.newline
- log.error 'Interrupt signal received. Stopping hooks...'
+ log.error "\nInterrupt signal received. Stopping hooks..."
end
# Executed when a hook run was interrupted/cancelled by user.
def run_interrupted
log.newline
log.warning '⚠ Hook run interrupted by user'
- log.warning '⚠ If files appear modified/missing, check your stash to recover them'
- log.newline
+ log.warning "⚠ If files appear modified/missing, check your stash to recover them\n"
end
# Executed when one or more hooks by the end of the run.
@@ -91,6 +89,7 @@ def print_header(hook)
log.partial hook.description
log.partial '.' * [70 - hook.description.length - hook_name.length, 0].max
log.partial hook_name
+ log.flush
end
def print_result(hook, status, output) # rubocop:disable Metrics/CyclomaticComplexity
diff --git a/lib/overcommit/subprocess.rb b/lib/overcommit/subprocess.rb
index 47713ff2..41175fb9 100644
--- a/lib/overcommit/subprocess.rb
+++ b/lib/overcommit/subprocess.rb
@@ -2,6 +2,7 @@
require 'childprocess'
require 'tempfile'
+require 'overcommit/os'
module Overcommit
# Manages execution of a child process, collecting the exit status and
@@ -38,7 +39,7 @@ def spawn(args, options = {})
if options[:input]
begin
process.io.stdin.puts(options[:input])
- rescue StandardError # rubocop:disable Lint/HandleExceptions
+ rescue StandardError
# Silently ignore if the standard input stream of the spawned
# process is closed before we get a chance to write to it. This
# happens on JRuby a lot.
diff --git a/lib/overcommit/utils/messages_utils.rb b/lib/overcommit/utils/messages_utils.rb
index 642e3e22..31c0f8db 100644
--- a/lib/overcommit/utils/messages_utils.rb
+++ b/lib/overcommit/utils/messages_utils.rb
@@ -26,7 +26,7 @@ def extract_messages(output_messages, regex, type_categorizer = nil)
raise Overcommit::Exceptions::MessageProcessingError,
'Unexpected output: unable to determine line number or type ' \
"of error/warning for output:\n" \
- "#{output_messages[index..-1].join("\n")}"
+ "#{output_messages[index..].join("\n")}"
end
file = extract_file(match, message)
@@ -52,6 +52,7 @@ def extract_file(match, message)
def extract_line(match, message)
return unless match.names.include?('line')
+
Integer(match[:line])
rescue ArgumentError, TypeError
raise Overcommit::Exceptions::MessageProcessingError,
diff --git a/lib/overcommit/version.rb b/lib/overcommit/version.rb
index d4659a8b..872c5327 100644
--- a/lib/overcommit/version.rb
+++ b/lib/overcommit/version.rb
@@ -2,5 +2,5 @@
# Defines the gem version.
module Overcommit
- VERSION = '0.53.0'
+ VERSION = '0.67.1'
end
diff --git a/libexec/index-tags b/libexec/index-tags
index 3c4478e2..51a6d516 100755
--- a/libexec/index-tags
+++ b/libexec/index-tags
@@ -6,10 +6,12 @@
set -e
-trap "rm -f $GIT_DIR/tags.$$" EXIT
-err_file=$GIT_DIR/ctags.err
-if ctags --tag-relative -Rf$GIT_DIR/tags.$$ --exclude=.git "$@" 2>${err_file}; then
- mv $GIT_DIR/tags.$$ $GIT_DIR/tags
+dir="`git rev-parse --git-dir`"
+
+trap "rm -f $dir/tags.$$" EXIT
+err_file=$dir/ctags.err
+if ctags --tag-relative -Rf$dir/tags.$$ --exclude=.git "$@" 2>${err_file}; then
+ mv $dir/tags.$$ $dir/tags
[ -e ${err_file} ] && rm -f ${err_file}
else
# Ignore STDERR unless `ctags` returned a non-zero exit code
diff --git a/overcommit.gemspec b/overcommit.gemspec
index 4e06bfc8..caaa8499 100644
--- a/overcommit.gemspec
+++ b/overcommit.gemspec
@@ -15,6 +15,10 @@ Gem::Specification.new do |s|
s.post_install_message =
'Install hooks by running `overcommit --install` in your Git repository'
+ s.metadata = {
+ 'changelog_uri' => 'https://github.com/sds/overcommit/blob/main/CHANGELOG.md'
+ }
+
s.require_paths = %w[lib]
s.executables = ['overcommit']
@@ -25,8 +29,9 @@ Gem::Specification.new do |s|
Dir['libexec/**/*'] +
Dir['template-dir/**/*']
- s.required_ruby_version = '>= 2.4'
+ s.required_ruby_version = '>= 2.6'
- s.add_dependency 'childprocess', '>= 0.6.3', '< 4'
+ s.add_dependency 'childprocess', '>= 0.6.3', '< 6'
s.add_dependency 'iniparse', '~> 1.4'
+ s.add_dependency 'rexml', '>= 3.3.9'
end
diff --git a/spec/integration/committing_spec.rb b/spec/integration/committing_spec.rb
index 6bdd0f08..1e1f65a6 100644
--- a/spec/integration/committing_spec.rb
+++ b/spec/integration/committing_spec.rb
@@ -26,7 +26,7 @@
context 'when a hook fails' do
before do
- `git config --local user.name "John"`
+ `git config --local user.name ""`
end
it 'exits with a non-zero status' do
@@ -34,6 +34,16 @@
end
end
+ context 'when no hooks fail on single author name' do
+ before do
+ `git config --local user.name "John"`
+ end
+
+ it 'exits successfully' do
+ subject.status.should == 0
+ end
+ end
+
context 'when no hooks fail' do
before do
`git config --local user.name "John Doe"`
diff --git a/spec/integration/diff_flag_spec.rb b/spec/integration/diff_flag_spec.rb
new file mode 100644
index 00000000..dcaa1bd3
--- /dev/null
+++ b/spec/integration/diff_flag_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'overcommit --diff' do
+ subject { shell(%w[overcommit --diff main]) }
+
+ context 'when using an existing pre-commit hook script' do
+ let(:script_name) { 'test-script' }
+ let(:script_contents) { "#!/bin/bash\nexit 0" }
+ let(:script_path) { ".#{Overcommit::OS::SEPARATOR}#{script_name}" }
+
+ let(:config) do
+ {
+ 'PreCommit' => {
+ 'MyHook' => {
+ 'enabled' => true,
+ 'required_executable' => script_path,
+ }
+ }
+ }
+ end
+
+ around do |example|
+ repo do
+ File.open('.overcommit.yml', 'w') { |f| f.puts(config.to_yaml) }
+ echo(script_contents, script_path)
+ `git add #{script_path}`
+ FileUtils.chmod(0o755, script_path)
+ example.run
+ end
+ end
+
+ it 'completes successfully without blocking' do
+ wait_until(timeout: 10) { subject } # Need to wait long time for JRuby startup
+ subject.status.should == 0
+ end
+ end
+end
diff --git a/spec/integration/gemfile_option_spec.rb b/spec/integration/gemfile_option_spec.rb
index 9cf25877..a6a6a4ba 100644
--- a/spec/integration/gemfile_option_spec.rb
+++ b/spec/integration/gemfile_option_spec.rb
@@ -3,97 +3,156 @@
require 'spec_helper'
describe 'specifying `gemfile` option in Overcommit configuration' do
- let(:repo_root) { File.expand_path(File.join('..', '..'), File.dirname(__FILE__)) }
- let(:fake_gem_path) { File.join('lib', 'my_fake_gem') }
-
- # We point the overcommit gem back to this repo since we can't assume the gem
- # has already been installed in a test environment
- let(:gemfile) { normalize_indent(<<-RUBY) }
- source 'https://rubygems.org'
-
- gem 'overcommit', path: '#{repo_root}'
- gem 'my_fake_gem', path: '#{fake_gem_path}'
- gem 'ffi' if Gem.win_platform? # Necessary for test to pass on Windows
- RUBY
-
- let(:gemspec) { normalize_indent(<<-RUBY) }
- Gem::Specification.new do |s|
- s.name = 'my_fake_gem'
- s.version = '1.0.0'
- s.author = 'John Doe'
- s.email = 'john.doe@example.com'
- s.summary = 'A fake gem'
- s.files = [File.join('lib', 'my_fake_gem.rb')]
- end
- RUBY
-
- # Specify a hook that depends on an external gem to test Gemfile loading
- let(:hook) { normalize_indent(<<-RUBY) }
- module Overcommit::Hook::PreCommit
- class FakeHook < Base
- def run
- require 'my_fake_gem'
- :pass
+ context 'given a project that uses a Gemfile' do
+ let(:repo_root) { File.expand_path(File.join('..', '..'), File.dirname(__FILE__)) }
+ let(:fake_gem_path) { File.join('lib', 'my_fake_gem') }
+
+ # We point the overcommit gem back to this repo since we can't assume the gem
+ # has already been installed in a test environment
+ let(:gemfile) { normalize_indent(<<-RUBY) }
+ source 'https://rubygems.org'
+
+ gem 'overcommit', path: '#{repo_root}'
+ gem 'my_fake_gem', path: '#{fake_gem_path}'
+ gem 'ffi' if Gem.win_platform? # Necessary for test to pass on Windows
+ RUBY
+
+ let(:gemspec) { normalize_indent(<<-RUBY) }
+ Gem::Specification.new do |s|
+ s.name = 'my_fake_gem'
+ s.version = '1.0.0'
+ s.author = 'John Doe'
+ s.license = 'MIT'
+ s.homepage = 'https://example.com'
+ s.email = 'john.doe@example.com'
+ s.summary = 'A fake gem'
+ s.files = [File.join('lib', 'my_fake_gem.rb')]
+ end
+ RUBY
+
+ # Specify a hook that depends on an external gem to test Gemfile loading
+ let(:hook) { normalize_indent(<<-RUBY) }
+ module Overcommit::Hook::PreCommit
+ class FakeHook < Base
+ def run
+ require 'my_fake_gem'
+ :pass
+ end
+ end
+ end
+ RUBY
+
+ let(:config) { normalize_indent(<<-YAML) }
+ verify_signatures: false
+
+ CommitMsg:
+ ALL:
+ enabled: false
+
+ PreCommit:
+ ALL:
+ enabled: false
+ FakeHook:
+ enabled: true
+ requires_files: false
+ YAML
+
+ around do |example|
+ repo do
+ # Since RSpec is being run within a Bundler context we need to clear it
+ # in order to not taint the test
+ Bundler.with_unbundled_env do
+ FileUtils.mkdir_p(File.join(fake_gem_path, 'lib'))
+ echo(gemspec, File.join(fake_gem_path, 'my_fake_gem.gemspec'))
+ touch(File.join(fake_gem_path, 'lib', 'my_fake_gem.rb'))
+
+ echo(gemfile, '.overcommit_gems.rb')
+ `bundle install --gemfile=.overcommit_gems.rb`
+
+ echo(config, '.overcommit.yml')
+
+ # Set BUNDLE_GEMFILE so we load Overcommit from the current repo
+ ENV['BUNDLE_GEMFILE'] = '.overcommit_gems.rb'
+ `bundle exec overcommit --install > #{File::NULL}`
+ FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit'))
+ echo(hook, File.join('.git-hooks', 'pre_commit', 'fake_hook.rb'))
+
+ Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do
+ example.run
+ end
end
end
end
- RUBY
-
- let(:config) { normalize_indent(<<-YAML) }
- verify_signatures: false
-
- CommitMsg:
- ALL:
- enabled: false
-
- PreCommit:
- ALL:
- enabled: false
- FakeHook:
- enabled: true
- requires_files: false
- YAML
-
- around do |example|
- repo do
- # Since RSpec is being run within a Bundler context we need to clear it
- # in order to not taint the test
- Bundler.with_clean_env do
- FileUtils.mkdir_p(File.join(fake_gem_path, 'lib'))
- echo(gemspec, File.join(fake_gem_path, 'my_fake_gem.gemspec'))
- touch(File.join(fake_gem_path, 'lib', 'my_fake_gem.rb'))
-
- echo(gemfile, '.overcommit_gems.rb')
- `bundle install --gemfile=.overcommit_gems.rb`
+ subject { shell(%w[git commit --allow-empty -m Test]) }
+
+ context 'when configuration specifies the gemfile' do
+ let(:config) { "gemfile: .overcommit_gems.rb\n" + super() }
+
+ it 'runs the hook successfully' do
+ subject.status.should == 0
+ end
+ end
+
+ context 'when configuration does not specify the gemfile' do
+ it 'fails to run the hook' do
+ subject.status.should_not == 0
+ end
+ end
+ end
+
+ context 'given a project that does not use a Gemfile' do
+ let(:hook) { normalize_indent(<<-RUBY) }
+ module Overcommit::Hook::PreCommit
+ class NoInvalidGemfileHook < Base
+ def run
+ if (gemfile = ENV["BUNDLE_GEMFILE"])
+ raise unless File.exist?(gemfile)
+ end
+
+ :pass
+ end
+ end
+ end
+ RUBY
+
+ let(:config) { normalize_indent(<<-YAML) }
+ verify_signatures: false
+
+ CommitMsg:
+ ALL:
+ enabled: false
+
+ PreCommit:
+ ALL:
+ enabled: false
+ NoInvalidGemfileHook:
+ enabled: true
+ requires_files: false
+ YAML
+
+ around do |example|
+ repo do
echo(config, '.overcommit.yml')
- # Set BUNDLE_GEMFILE so we load Overcommit from the current repo
- ENV['BUNDLE_GEMFILE'] = '.overcommit_gems.rb'
- `bundle exec overcommit --install > #{File::NULL}`
+ `overcommit --install > #{File::NULL}`
FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit'))
- echo(hook, File.join('.git-hooks', 'pre_commit', 'fake_hook.rb'))
+ echo(hook, File.join('.git-hooks', 'pre_commit', 'no_invalid_gemfile_hook.rb'))
Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do
example.run
end
end
end
- end
-
- subject { shell(%w[git commit --allow-empty -m Test]) }
- context 'when configuration specifies the gemfile' do
- let(:config) { "gemfile: .overcommit_gems.rb\n" + super() }
+ subject { shell(%w[git commit --allow-empty -m Test]) }
- it 'runs the hook successfully' do
- subject.status.should == 0
- end
- end
+ context 'when configuration explicitly sets the gemfile to false' do
+ let(:config) { "gemfile: false\n" + super() }
- context 'when configuration does not specify the gemfile' do
- it 'fails to run the hook' do
- subject.status.should_not == 0
+ it 'runs the hook successfully' do
+ subject.status.should == 0
+ end
end
end
end
diff --git a/spec/integration/parallelize_spec.rb b/spec/integration/parallelize_spec.rb
index 20a86be3..471aa7b3 100644
--- a/spec/integration/parallelize_spec.rb
+++ b/spec/integration/parallelize_spec.rb
@@ -27,8 +27,14 @@
end
end
- it 'does not hang' do
- result = Timeout.timeout(5) { subject }
- result.stderr.should_not include 'No live threads left. Deadlock?'
+ # Test fails on Ruby 3.0 on Windows but nothing else. Would glady accept a pull
+ # request that resolves.
+ unless Overcommit::OS.windows? &&
+ Overcommit::Utils::Version.new(RUBY_VERSION) >= '3' &&
+ Overcommit::Utils::Version.new(RUBY_VERSION) < '3.1'
+ it 'does not hang' do
+ result = Timeout.timeout(5) { subject }
+ result.stderr.should_not include 'No live threads left. Deadlock?'
+ end
end
end
diff --git a/spec/integration/resolving_cherry_pick_conflict_spec.rb b/spec/integration/resolving_cherry_pick_conflict_spec.rb
index 2ea05d7b..2a7f053c 100644
--- a/spec/integration/resolving_cherry_pick_conflict_spec.rb
+++ b/spec/integration/resolving_cherry_pick_conflict_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe 'resolving cherry-pick conflicts' do
- subject { shell(%w[git commit -m "Resolve conflicts" -i some-file]) }
+ subject { shell(%w[git commit -m Test -i some-file]) }
let(:config) { <<-YML }
PreCommit:
@@ -39,15 +39,18 @@
end
it 'exits with a non-zero status' do
+ skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']
subject.status.should_not == 0
end
it 'does not remove the CHERRY_PICK_HEAD file' do
+ skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']
subject
Dir['.git/*'].should include '.git/CHERRY_PICK_HEAD'
end
it 'keeps the commit message from the cherry-picked commit' do
+ skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']
subject
File.read(File.join('.git', 'MERGE_MSG')).should include 'Add Branch 2 addition'
end
diff --git a/spec/integration/resolving_merge_conflict_spec.rb b/spec/integration/resolving_merge_conflict_spec.rb
index 679ba136..997dcb09 100644
--- a/spec/integration/resolving_merge_conflict_spec.rb
+++ b/spec/integration/resolving_merge_conflict_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe 'resolving merge conflicts' do
- subject { shell(%w[git commit -m "Resolve conflicts" -i some-file]) }
+ subject { shell(%w[git commit -m Test -i some-file]) }
around do |example|
repo do
diff --git a/spec/overcommit/cli_spec.rb b/spec/overcommit/cli_spec.rb
index 40923ad3..3d95b09d 100644
--- a/spec/overcommit/cli_spec.rb
+++ b/spec/overcommit/cli_spec.rb
@@ -2,6 +2,7 @@
require 'spec_helper'
require 'overcommit/cli'
+require 'overcommit/hook_context/diff'
require 'overcommit/hook_context/run_all'
describe Overcommit::CLI do
@@ -125,5 +126,30 @@
subject
end
end
+
+ context 'with the diff switch specified' do
+ let(:arguments) { ['--diff=some-ref'] }
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+
+ before do
+ cli.stub(:halt)
+ Overcommit::HookRunner.any_instance.stub(:run)
+ end
+
+ it 'creates a HookRunner with the diff context' do
+ Overcommit::HookRunner.should_receive(:new).
+ with(config,
+ logger,
+ instance_of(Overcommit::HookContext::Diff),
+ instance_of(Overcommit::Printer)).
+ and_call_original
+ subject
+ end
+
+ it 'runs the HookRunner' do
+ Overcommit::HookRunner.any_instance.should_receive(:run)
+ subject
+ end
+ end
end
end
diff --git a/spec/overcommit/configuration_loader_spec.rb b/spec/overcommit/configuration_loader_spec.rb
index a99608d4..90497201 100644
--- a/spec/overcommit/configuration_loader_spec.rb
+++ b/spec/overcommit/configuration_loader_spec.rb
@@ -57,5 +57,71 @@
end
end
end
+
+ context 'when repo only contains a repo level configuration file' do
+ let(:config_contents) { <<-CFG }
+ PreCommit:
+ Rubocop:
+ enabled: true
+ CFG
+
+ around do |example|
+ repo do
+ File.open('.overcommit.yml', 'w') { |f| f.write(config_contents) }
+ example.run
+ end
+ end
+
+ it 'includes default settings' do
+ subject
+ subject.for_hook('CapitalizedSubject', 'CommitMsg').should include('enabled' => true)
+ end
+
+ it 'includes .overwrite.yml configs' do
+ subject
+ subject.for_hook('Rubocop', 'PreCommit').should include('enabled' => true)
+ end
+ end
+
+ context 'when repo also contains a local configuration file' do
+ let(:local_config_contents) { <<-CFG }
+ plugin_directory: 'some-different-directory'
+ CFG
+
+ around do |example|
+ repo do
+ File.open('.overcommit.yml', 'w') { |f| f.write(config_contents) }
+ File.open('.local-overcommit.yml', 'w') { |f| f.write(local_config_contents) }
+ example.run
+ end
+ end
+
+ let(:config_contents) { <<-CFG }
+ PreCommit:
+ ScssLint:
+ enabled: true
+ CFG
+
+ let(:local_config_contents) { <<-CFG }
+ PreCommit:
+ Rubocop:
+ enabled: true
+ CFG
+
+ it 'includes default settings' do
+ subject
+ subject.for_hook('CapitalizedSubject', 'CommitMsg').should include('enabled' => true)
+ end
+
+ it 'includes .overwrite.yml configs' do
+ subject
+ subject.for_hook('ScssLint', 'PreCommit').should include('enabled' => true)
+ end
+
+ it 'includes .local-overwrite.yml configs' do
+ subject
+ subject.for_hook('Rubocop', 'PreCommit').should include('enabled' => true)
+ end
+ end
end
end
diff --git a/spec/overcommit/default_configuration_spec.rb b/spec/overcommit/default_configuration_spec.rb
index a86b1fe3..706af872 100644
--- a/spec/overcommit/default_configuration_spec.rb
+++ b/spec/overcommit/default_configuration_spec.rb
@@ -4,7 +4,11 @@
describe 'default configuration' do
default_config =
- YAML.load_file(Overcommit::ConfigurationLoader::DEFAULT_CONFIG_PATH).to_hash
+ begin
+ YAML.load_file(Overcommit::ConfigurationLoader::DEFAULT_CONFIG_PATH, aliases: true).to_hash
+ rescue ArgumentError
+ YAML.load_file(Overcommit::ConfigurationLoader::DEFAULT_CONFIG_PATH).to_hash
+ end
Overcommit::Utils.supported_hook_types.each do |hook_type|
hook_class = Overcommit::Utils.camel_case(hook_type)
diff --git a/spec/overcommit/git_config_spec.rb b/spec/overcommit/git_config_spec.rb
index 22ad7c02..9cc51862 100644
--- a/spec/overcommit/git_config_spec.rb
+++ b/spec/overcommit/git_config_spec.rb
@@ -78,5 +78,19 @@
expect(subject).to eq File.expand_path('my-hooks')
end
end
+
+ context 'when explicitly set to a path starting with a tilde' do
+ around do |example|
+ repo do
+ `git config --local core.hooksPath ~/my-hooks`
+ example.run
+ end
+ end
+
+ it 'returns the absolute path to the folder in the users home path' do
+ expect(subject).to eq File.expand_path('~/my-hooks')
+ expect(subject).not_to include('~')
+ end
+ end
end
end
diff --git a/spec/overcommit/git_repo_spec.rb b/spec/overcommit/git_repo_spec.rb
index b5376212..b887415b 100644
--- a/spec/overcommit/git_repo_spec.rb
+++ b/spec/overcommit/git_repo_spec.rb
@@ -24,12 +24,13 @@
end
submodule = repo do
- `git submodule add #{nested_submodule} nested-sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add \
+ #{nested_submodule} nested-sub 2>&1 > #{File::NULL}`
`git commit -m "Add nested submodule"`
end
repo do
- `git submodule add #{submodule} sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`
example.run
end
end
@@ -150,7 +151,7 @@
end
before do
- `git submodule add #{submodule} sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`
end
it { should_not include File.expand_path('sub') }
@@ -178,7 +179,8 @@
`git commit --allow-empty -m "Submodule commit"`
end
- `git submodule add #{submodule} #{submodule_dir} 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add \
+ #{submodule} #{submodule_dir} 2>&1 > #{File::NULL}`
`git commit -m "Add submodule"`
end
@@ -217,6 +219,20 @@
end
end
end
+
+ context 'when the git ls-tree command fails for whatever reason' do
+ before do
+ result = double('result', success?: false, statuses: [1], stdouts: '', stderrs: '')
+ allow(Overcommit::Utils).
+ to receive(:execute).
+ with(%w[git ls-tree --name-only HEAD], args: []).
+ and_return(result)
+ end
+
+ it 'raises' do
+ expect { subject }.to raise_error Overcommit::Exceptions::Error
+ end
+ end
end
describe '.tracked?' do
@@ -268,7 +284,7 @@
touch 'tracked'
`git add tracked`
`git commit -m "Initial commit"`
- `git submodule add #{submodule} sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`
touch 'staged'
`git add staged`
example.run
@@ -313,7 +329,7 @@
end
repo do
- `git submodule add #{submodule} sub-repo 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub-repo 2>&1 > #{File::NULL}`
`git commit -m "Initial commit"`
example.run
end
@@ -329,7 +345,8 @@
`git commit --allow-empty -m "Another submodule"`
end
- `git submodule add #{another_submodule} another-sub-repo 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add \
+ #{another_submodule} another-sub-repo 2>&1 > #{File::NULL}`
end
it { should be_empty }
@@ -355,7 +372,8 @@
`git commit --allow-empty -m "Another submodule"`
end
- `git submodule add #{another_submodule} yet-another-sub-repo 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add \
+ #{another_submodule} yet-another-sub-repo 2>&1 > #{File::NULL}`
`git commit -m "Add yet another submodule"`
`git rm sub-repo`
`git rm yet-another-sub-repo`
@@ -363,7 +381,7 @@
it 'returns all submodules that were removed' do
subject.size.should == 2
- subject.map(&:path).sort.should == ['sub-repo', 'yet-another-sub-repo']
+ subject.map(&:path).sort.should == %w[sub-repo yet-another-sub-repo]
end
end
end
diff --git a/spec/overcommit/hook/commit_msg/message_format_spec.rb b/spec/overcommit/hook/commit_msg/message_format_spec.rb
index d4aaab62..c9dde29b 100644
--- a/spec/overcommit/hook/commit_msg/message_format_spec.rb
+++ b/spec/overcommit/hook/commit_msg/message_format_spec.rb
@@ -15,11 +15,11 @@
context 'when pattern is empty' do
let(:config) do
super().merge(Overcommit::Configuration.new(
- 'CommitMsg' => {
- 'MessageFormat' => {
- 'pattern' => nil
- }
- }
+ 'CommitMsg' => {
+ 'MessageFormat' => {
+ 'pattern' => nil
+ }
+ }
))
end
@@ -43,11 +43,11 @@
context 'when multiline message matches the pattern' do
let(:config) do
super().merge(Overcommit::Configuration.new(
- 'CommitMsg' => {
- 'MessageFormat' => {
- 'pattern' => '^Some .* Message$'
- }
- }
+ 'CommitMsg' => {
+ 'MessageFormat' => {
+ 'pattern' => '^Some .* Message$'
+ }
+ }
))
end
diff --git a/spec/overcommit/hook/commit_msg/text_width_spec.rb b/spec/overcommit/hook/commit_msg/text_width_spec.rb
index e07d777e..8829668e 100644
--- a/spec/overcommit/hook/commit_msg/text_width_spec.rb
+++ b/spec/overcommit/hook/commit_msg/text_width_spec.rb
@@ -98,13 +98,13 @@
context 'when custom lengths are specified' do
let(:config) do
super().merge(Overcommit::Configuration.new(
- 'CommitMsg' => {
- 'TextWidth' => {
- 'max_subject_width' => 70,
- 'min_subject_width' => 4,
- 'max_body_width' => 80
- }
- }
+ 'CommitMsg' => {
+ 'TextWidth' => {
+ 'max_subject_width' => 70,
+ 'min_subject_width' => 4,
+ 'max_body_width' => 80
+ }
+ }
))
end
diff --git a/spec/overcommit/hook/pre_commit/author_email_spec.rb b/spec/overcommit/hook/pre_commit/author_email_spec.rb
index 613f114e..8e0dd768 100644
--- a/spec/overcommit/hook/pre_commit/author_email_spec.rb
+++ b/spec/overcommit/hook/pre_commit/author_email_spec.rb
@@ -30,11 +30,11 @@
context 'when a custom pattern is specified' do
let(:config) do
super().merge(Overcommit::Configuration.new(
- 'PreCommit' => {
- 'AuthorEmail' => {
- 'pattern' => '^[^@]+@brigade\.com$'
- }
- }
+ 'PreCommit' => {
+ 'AuthorEmail' => {
+ 'pattern' => '^[^@]+@brigade\.com$'
+ }
+ }
))
end
diff --git a/spec/overcommit/hook/pre_commit/author_name_spec.rb b/spec/overcommit/hook/pre_commit/author_name_spec.rb
index 7cf230d5..1ed0502f 100644
--- a/spec/overcommit/hook/pre_commit/author_name_spec.rb
+++ b/spec/overcommit/hook/pre_commit/author_name_spec.rb
@@ -18,7 +18,7 @@
context 'when user has only a first name' do
let(:name) { 'John' }
- it { should fail_hook }
+ it { should pass }
end
context 'when user has first and last name' do
diff --git a/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb b/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb
index 633e14fe..b8360e2e 100644
--- a/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb
+++ b/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb
@@ -54,7 +54,7 @@
Title: Rest-Client Gem for Ruby logs password information in plaintext
Solution: upgrade to >= 1.7.3
Vulnerabilities found!
-MSG
+ MSG
)
end
@@ -67,7 +67,7 @@
stdout: <<-MSG
Insecure Source URI found: git://github.com/xxx/overcommit.git
Vulnerabilities found!
-MSG
+ MSG
)
end
diff --git a/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb b/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb
index c61e4873..a734b3dc 100644
--- a/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb
+++ b/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb
@@ -49,7 +49,7 @@
aws-sdk-resources (newest 2.3.3, installed 2.3.1)
config (newest 1.1.1, installed 1.1.0)
ruby_parser (newest 3.8.2, installed 3.8.1)
-MSG
+ MSG
)
end
@@ -60,7 +60,7 @@
let(:result) do
double(stdout: <<-MSG
Warning: the running version of Bundler is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
-MSG
+ MSG
)
end
diff --git a/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb b/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb
new file mode 100644
index 00000000..1013c503
--- /dev/null
+++ b/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PreCommit::DartAnalyzer do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ before do
+ subject.stub(:applicable_files).and_return(%w[file1.dart file2.dart])
+ end
+
+ context 'when dartanalyzer exits successfully' do
+ before do
+ result = double('result')
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+ end
+
+ context 'when dartanalyzer exits unsucessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'and it reports an error' do
+ before do
+ result.stub(:stdout).and_return([
+ 'Analyzing file1.dart...',
+ 'error • message_ommitted • lib/file1.dart:35:3 • rule',
+ 'Analyzing file2.dart...',
+ 'hint • message_ommitted • lib/file2.dart:100:13 • rule',
+ 'info • message_ommitted • lib/file2.dart:113:16 • rule',
+ '3 lints found.'
+ ].join("\n"))
+ end
+
+ it { should fail_hook }
+ end
+ end
+end
diff --git a/spec/overcommit/hook/pre_commit/erb_lint_spec.rb b/spec/overcommit/hook/pre_commit/erb_lint_spec.rb
new file mode 100644
index 00000000..e9272bfe
--- /dev/null
+++ b/spec/overcommit/hook/pre_commit/erb_lint_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PreCommit::ErbLint do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ before do
+ subject.stub(:applicable_files).and_return(%w[file1.html.erb file2.html.erb])
+ end
+
+ context 'when erblint exits successfully' do
+ before do
+ result = double('result')
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+ end
+
+ context 'when erblint exits unsucessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'and it reports an error' do
+ before do
+ result.stub(:stdout).and_return(<<-MSG)
+Linting 1 files with 14 linters...
+
+erb interpolation with '<%= (...).html_safe %>' in this context is never safe
+In file: app/views/posts/show.html.erb:10
+ MSG
+ end
+
+ it { should fail_hook }
+ end
+ end
+end
diff --git a/spec/overcommit/hook/pre_commit/es_lint_spec.rb b/spec/overcommit/hook/pre_commit/es_lint_spec.rb
index bcb2cfdc..39de7d27 100644
--- a/spec/overcommit/hook/pre_commit/es_lint_spec.rb
+++ b/spec/overcommit/hook/pre_commit/es_lint_spec.rb
@@ -52,6 +52,18 @@
it { should warn }
end
+
+ context 'and it doesnt count false positives error messages' do
+ before do
+ result.stub(:stdout).and_return([
+ '$ yarn eslint --quiet --format=compact /app/project/Error.ts',
+ '$ /app/project/node_modules/.bin/eslint --quiet --format=compact /app/project/Error.ts',
+ '',
+ ].join("\n"))
+ end
+
+ it { should pass }
+ end
end
context 'when eslint exits unsucessfully' do
diff --git a/spec/overcommit/hook/pre_commit/fasterer_spec.rb b/spec/overcommit/hook/pre_commit/fasterer_spec.rb
index 405888ab..afb08791 100644
--- a/spec/overcommit/hook/pre_commit/fasterer_spec.rb
+++ b/spec/overcommit/hook/pre_commit/fasterer_spec.rb
@@ -36,7 +36,7 @@
spec/models/blog_spec.rb
Using each_with_index is slower than while loop. Occurred at lines: 12.
2 files inspected, 0 offense detected
- MSG
+ MSG
)
end
@@ -51,7 +51,7 @@
spec/models/product_spec.rb
Using each_with_index is slower than while loop. Occurred at lines: 52.
1 files inspected, 1 offense detected
- MSG
+ MSG
)
end
diff --git a/spec/overcommit/hook/pre_commit/flay_spec.rb b/spec/overcommit/hook/pre_commit/flay_spec.rb
index a64cd2c3..2a5d8140 100644
--- a/spec/overcommit/hook/pre_commit/flay_spec.rb
+++ b/spec/overcommit/hook/pre_commit/flay_spec.rb
@@ -38,7 +38,7 @@
app/whatever21.rb:105
app/whatever22.rb:76
-MSG
+ MSG
)
end
@@ -51,7 +51,7 @@
success?: false,
stdout: <<-MSG
Total score (lower is better) = 0
-MSG
+ MSG
)
end
diff --git a/spec/overcommit/hook/pre_commit/hadolint_spec.rb b/spec/overcommit/hook/pre_commit/hadolint_spec.rb
index 4091ca0b..ad756f30 100644
--- a/spec/overcommit/hook/pre_commit/hadolint_spec.rb
+++ b/spec/overcommit/hook/pre_commit/hadolint_spec.rb
@@ -31,7 +31,7 @@
success?: false,
stdout: <<-MSG
Dockerfile:5 DL3015 Avoid additional packages by specifying `--no-install-recommends`
- MSG
+ MSG
)
end
let(:result_dockerfile_web) do
@@ -39,7 +39,7 @@
success?: false,
stdout: <<-MSG
Dockerfile.web:13 DL3020 Use COPY instead of ADD for files and folders
- MSG
+ MSG
)
end
@@ -52,7 +52,7 @@
success?: false,
stdout: <<-MSG
Dockerfile:11 SC2086 Double quote to prevent globbing and word splitting.
- MSG
+ MSG
)
end
let(:result_dockerfile_web) do
diff --git a/spec/overcommit/hook/pre_commit/mix_format_spec.rb b/spec/overcommit/hook/pre_commit/mix_format_spec.rb
new file mode 100644
index 00000000..9acc50ed
--- /dev/null
+++ b/spec/overcommit/hook/pre_commit/mix_format_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PreCommit::MixFormat do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ before do
+ subject.stub(:applicable_files).and_return(%w[file1.ex file2.exs])
+ end
+
+ context 'when mix format exits successfully' do
+ before do
+ result = double('result')
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+ end
+
+ context 'when mix format exits unsucessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'and it reports an error' do
+ before do
+ result.stub(:stdout).and_return('')
+ result.stub(:stderr).and_return([
+ '** (Mix) mix format failed due to --check-formatted.',
+ 'The following files are not formatted:',
+ '',
+ ' * lib/file1.ex',
+ ' * lib/file2.ex'
+ ].join("\n"))
+ end
+
+ it { should fail_hook }
+ end
+ end
+end
diff --git a/spec/overcommit/hook/pre_commit/php_lint_spec.rb b/spec/overcommit/hook/pre_commit/php_lint_spec.rb
index aff0b97e..ec755345 100644
--- a/spec/overcommit/hook/pre_commit/php_lint_spec.rb
+++ b/spec/overcommit/hook/pre_commit/php_lint_spec.rb
@@ -26,13 +26,13 @@
context 'when php lint exits unsuccessfully' do
before do
# php -l prints the same to both stdout and stderr
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'',
"Parse error: syntax error, unexpected '0' (T_LNUMBER), expecting variable (T_VARIABLE) or '{' or '$' in sample.php on line 3 ",
'Errors parsing invalid.php',
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result = double('result')
result.stub(:status).and_return(255)
diff --git a/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb b/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb
index 33b0d7c1..a4b1e187 100644
--- a/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb
+++ b/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb
@@ -13,7 +13,7 @@
context 'when phpcs fixer exits successfully with fixed file' do
before do
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'Loaded config default.',
'Using cache file ".php_cs.cache".',
@@ -24,7 +24,7 @@
'Fixed all files in 0.001 seconds, 10.000 MB memory used',
'',
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result = double('result')
result.stub(:status).and_return(0)
@@ -38,7 +38,7 @@
context 'when phpcs fixer exits successfully with no file to fix' do
before do
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'Loaded config default.',
'Using cache file ".php_cs.cache".',
@@ -46,7 +46,7 @@
'Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error',
'',
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result = double('result')
result.stub(:status).and_return(0)
@@ -60,7 +60,7 @@
context 'when phpcs exits unsuccessfully' do
before do
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'Loaded config default.',
'Using cache file ".php_cs.cache".',
@@ -72,7 +72,7 @@
' 1) /home/damien/Code/Rezdy/php/foo/broken.php',
'',
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result = double('result')
result.stub(:status).and_return(1)
diff --git a/spec/overcommit/hook/pre_commit/phpcs_spec.rb b/spec/overcommit/hook/pre_commit/phpcs_spec.rb
index 3df6cfb5..6982a297 100644
--- a/spec/overcommit/hook/pre_commit/phpcs_spec.rb
+++ b/spec/overcommit/hook/pre_commit/phpcs_spec.rb
@@ -39,12 +39,12 @@
context 'and it reports a warning' do
before do
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'File,Line,Column,Type,Message,Source,Severity,Fixable',
'"/Users/craig/HelpScout/overcommit-testing/invalid.php",5,1,warning,"Possible parse error: FOREACH has no AS statement",Squiz.ControlStructures.ForEachLoopDeclaration.MissingAs,5,0'
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result.stub(:stdout).and_return(sample_output)
end
@@ -53,12 +53,12 @@
context 'and it reports an error' do
before do
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
sample_output = [
'File,Line,Column,Type,Message,Source,Severity,Fixable',
'"/Users/craig/HelpScout/overcommit-testing/invalid.php",5,1,error,"Inline control structures are not allowed",Generic.ControlStructures.InlineControlStructure.NotAllowed,5,1'
].join("\n")
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
result.stub(:stdout).and_return(sample_output)
end
diff --git a/spec/overcommit/hook/pre_commit/pronto_spec.rb b/spec/overcommit/hook/pre_commit/pronto_spec.rb
index ed099ace..c9d56abe 100644
--- a/spec/overcommit/hook/pre_commit/pronto_spec.rb
+++ b/spec/overcommit/hook/pre_commit/pronto_spec.rb
@@ -31,6 +31,7 @@
context 'and it reports an error' do
before do
+ result.stub(:stderr).and_return('')
result.stub(:stdout).and_return([
'file2.rb:10 E: IDENTICAL code found in :iter.',
].join("\n"))
@@ -41,13 +42,30 @@
context 'and it reports a warning' do
before do
- result.stub(:stdout).and_return([
- 'file1.rb:12 W: Line is too long. [107/80]',
- 'file2.rb:14 I: Prefer single-quoted strings'
- ].join("\n"))
+ result.stub(:stderr).and_return('')
+ result.stub(:stdout).and_return <<~MESSAGE
+ Running Pronto::Rubocop
+ file1.rb:12 W: Line is too long. [107/80]
+ file2.rb:14 I: Prefer single-quoted strings
+
+ ```suggestion
+ x = 'x'
+ ```
+ MESSAGE
end
it { should warn }
end
+
+ context 'and it has a generic error message written to stderr' do
+ before do
+ result.stub(:stdout).and_return('')
+ result.stub(:stderr).and_return([
+ 'Could not find pronto in any of the sources'
+ ].join("\n"))
+ end
+
+ it { should fail_hook }
+ end
end
end
diff --git a/spec/overcommit/hook/pre_commit/r_spec_spec.rb b/spec/overcommit/hook/pre_commit/r_spec_spec.rb
new file mode 100644
index 00000000..7fdff3b3
--- /dev/null
+++ b/spec/overcommit/hook/pre_commit/r_spec_spec.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PreCommit::RSpec do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ context 'when rspec exits successfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+
+ it {
+ expect(subject).to receive(:execute).with(['rspec']).and_return(result)
+
+ subject.run
+ }
+ end
+
+ context 'with included files set' do
+ let(:result) { double('result') }
+ let(:config) do
+ super().merge(Overcommit::Configuration.new(
+ 'PreCommit' => {
+ 'RSpec' => {
+ 'include' => ['**/*_spec.rb'],
+ }
+ }
+ ))
+ end
+
+ let(:context) { double('context') }
+
+ before do
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ subject.stub(:applicable_files).and_return('spec/test_spec.rb')
+ end
+
+ it { should pass }
+
+ it {
+ expect(subject).to receive(:execute).with(['rspec'],
+ args: 'spec/test_spec.rb').and_return(result)
+
+ subject.run
+ }
+ end
+
+ context 'when rspec exits unsuccessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'with a runtime error' do
+ before do
+ result.stub(stdout: '', stderr: <<-MSG)
+ /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `load': /home/user/dev/github/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:49: can't find string "EOS" anywhere before EOF (SyntaxError)
+ /home/user/dev/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:29: syntax error, unexpected end-of-input
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `block in load_spec_files'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `each'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `load_spec_files'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:97:in `setup'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:85:in `run'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:70:in `run'
+ from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:38:in `invoke'
+ from /home/user/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.2/exe/rspec:4:in `'
+ from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `load'
+ from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `'
+ MSG
+ end
+
+ it { should fail_hook }
+ end
+
+ context 'with a test failure' do
+ before do
+ result.stub(stderr: '', stdout: <<-MSG)
+ .FF
+
+ Failures:
+
+ 1) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail
+ Failure/Error: it { should fail_hook }
+ expected that the hook would fail
+ # ./spec/overcommit/hook/pre_push/rspec_spec.rb:45:in `block (4 levels) in '
+
+ 2) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail
+ Failure/Error: it { should fail_hook }
+ expected that the hook would fail
+ # ./spec/overcommit/hook/pre_push/rspec_spec.rb:57:in `block (4 levels) in '
+
+ Finished in 0.00505 seconds (files took 0.27437 seconds to load)
+ 3 examples, 2 failures
+
+ Failed examples:
+
+ rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:45 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail
+ rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:57 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail
+ MSG
+ end
+
+ it { should fail_hook }
+ end
+ end
+end
diff --git a/spec/overcommit/hook/pre_commit/rails_schema_up_to_date_spec.rb b/spec/overcommit/hook/pre_commit/rails_schema_up_to_date_spec.rb
index ad53485e..9ee704b7 100644
--- a/spec/overcommit/hook/pre_commit/rails_schema_up_to_date_spec.rb
+++ b/spec/overcommit/hook/pre_commit/rails_schema_up_to_date_spec.rb
@@ -70,6 +70,33 @@
end
it { should fail_hook }
+
+ context 'when non ASCII encoding is required' do
+ let!(:config) do
+ super().merge(Overcommit::Configuration.new(
+ 'PreCommit' => {
+ 'RailsSchemaUpToDate' => {
+ 'encoding' => 'utf-8'
+ }
+ }
+ ))
+ end
+
+ before do
+ subject.stub(:applicable_files).and_return([sql_schema_file])
+ end
+
+ around do |example|
+ repo do
+ FileUtils.mkdir_p('db/migrate')
+ File.open(sql_schema_file, 'w') { |f| f.write("version: 12345678901234\nVALUES ('字')") }
+ `git add #{sql_schema_file}`
+ example.run
+ end
+ end
+
+ it { should fail_hook }
+ end
end
context 'when a Ruby schema file with the latest version and migrations are added' do
diff --git a/spec/overcommit/hook/pre_commit/rake_target_spec.rb b/spec/overcommit/hook/pre_commit/rake_target_spec.rb
index 4a0cf400..e295e61f 100644
--- a/spec/overcommit/hook/pre_commit/rake_target_spec.rb
+++ b/spec/overcommit/hook/pre_commit/rake_target_spec.rb
@@ -19,11 +19,11 @@
context 'with targets parameter set' do
let(:config) do
super().merge(Overcommit::Configuration.new(
- 'PreCommit' => {
- 'RakeTarget' => {
- 'targets' => ['test'],
- }
- }
+ 'PreCommit' => {
+ 'RakeTarget' => {
+ 'targets' => ['test'],
+ }
+ }
))
end
let(:result) { double('result') }
diff --git a/spec/overcommit/hook/pre_commit/sorbet_spec.rb b/spec/overcommit/hook/pre_commit/sorbet_spec.rb
new file mode 100644
index 00000000..60f811cb
--- /dev/null
+++ b/spec/overcommit/hook/pre_commit/sorbet_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PreCommit::Sorbet do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ before do
+ subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])
+ end
+
+ context 'when Sorbet exits successfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+
+ context 'and it printed a message to stderr' do
+ before do
+ result.stub(:stderr).and_return("No errors! Great job.\n")
+ end
+
+ it { should pass }
+ end
+ end
+
+ context 'when Sorbet exits unsucessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'and it reports an error' do
+ before do
+ result.stub(:stderr).and_return(normalize_indent(<<-MSG))
+ sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
+ 5 | foo 'bar'
+ ^^^
+ Errors: 1
+ MSG
+ end
+
+ it { should fail_hook }
+ end
+ end
+end
diff --git a/spec/overcommit/hook/pre_commit/stylelint_spec.rb b/spec/overcommit/hook/pre_commit/stylelint_spec.rb
index d31ba948..68e83f65 100644
--- a/spec/overcommit/hook/pre_commit/stylelint_spec.rb
+++ b/spec/overcommit/hook/pre_commit/stylelint_spec.rb
@@ -15,6 +15,7 @@
before do
result = double('result')
result.stub(:success?).and_return(true)
+ result.stub(:stderr).and_return('')
result.stub(:stdout).and_return('')
subject.stub(:execute).and_return(result)
end
@@ -22,7 +23,7 @@
it { should pass }
end
- context 'when stylelint exits unsucessfully' do
+ context 'when stylelint exits unsucessfully with messages on stdout (stylelint < 16)' do
let(:result) { double('result') }
before do
@@ -32,6 +33,7 @@
context 'and it reports an error' do
before do
result.stub(:success?).and_return(false)
+ result.stub(:stderr).and_return('')
result.stub(:stdout).and_return([
'index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)',
'form.css: line 10, col 6, error - Expected indentation of 4 spaces (indentation)',
@@ -45,4 +47,29 @@
end
end
end
+
+ context 'when stylelint exits unsucessfully with messages on stderr (stylelint >= 16)' do
+ let(:result) { double('result') }
+
+ before do
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'and it reports an error' do
+ before do
+ result.stub(:success?).and_return(false)
+ result.stub(:stdout).and_return('')
+ result.stub(:stderr).and_return([
+ 'index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)',
+ 'form.css: line 10, col 6, error - Expected indentation of 4 spaces (indentation)',
+ ].join("\n"))
+ end
+
+ it { should fail_hook }
+
+ it 'extracts lines numbers correctly from output' do
+ expect(subject.run.map(&:line)).to eq([4, 10])
+ end
+ end
+ end
end
diff --git a/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb b/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb
index 72ebc6e8..e5aea475 100644
--- a/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb
+++ b/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb
@@ -19,7 +19,7 @@
end
before do
- subject.stub(:execute).with(%w[yamllint --format=parsable], args: applicable_files).
+ subject.stub(:execute).with(%w[yamllint --format=parsable --strict], args: applicable_files).
and_return(result)
end
@@ -30,20 +30,33 @@
stdout: <<-MSG
file1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length)
file2.yml:41:81: [error] line too long (261 > 80 characters) (line-length)
- MSG
+ MSG
)
end
- it { should warn }
+ it { should fail_hook }
end
+ context 'and has 1 error and 1 warning' do
+ let(:result) do
+ double(
+ success?: false,
+ stdout: <<-MSG
+file1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length)
+file2.yml:41:81: [warning] missing document start "---" (document-start)
+ MSG
+ )
+ end
+
+ it { should fail_hook }
+ end
context 'and has single suggestion for missing file header' do
let(:result) do
double(
success?: false,
stdout: <<-MSG
file1.yaml:1:1: [warning] missing document start "---" (document-start)
- MSG
+ MSG
)
end
diff --git a/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb b/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb
index 29cb7b02..a6f61f2c 100644
--- a/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb
+++ b/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb
@@ -22,6 +22,7 @@
context 'when YAML file has errors' do
before do
+ YAML.stub(:load_file).with(staged_file, { aliases: true }).and_raise(ArgumentError)
YAML.stub(:load_file).with(staged_file).and_raise(ArgumentError)
end
diff --git a/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb b/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb
index d59779c9..d463d25d 100644
--- a/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb
+++ b/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb
@@ -76,7 +76,7 @@
context 'and it reports an error' do
before do
result.stub(:stdout).and_return(
- <<-HEREDOC
+ <<-HEREDOC
Files: 72
Modules: 12 ( 3 undocumented)
Classes: 63 ( 15 undocumented)
diff --git a/spec/overcommit/hook/pre_push/flutter_test_spec.rb b/spec/overcommit/hook/pre_push/flutter_test_spec.rb
new file mode 100644
index 00000000..a5bce582
--- /dev/null
+++ b/spec/overcommit/hook/pre_push/flutter_test_spec.rb
@@ -0,0 +1,167 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PrePush::FlutterTest do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ context 'when flutter test exits successfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(true)
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should pass }
+ end
+
+ context 'when flutter test exits unsuccessfully' do
+ let(:result) { double('result') }
+
+ before do
+ result.stub(:success?).and_return(false)
+ subject.stub(:execute).and_return(result)
+ end
+
+ context 'with a runtime error' do
+ before do
+ result.stub(stdout: '', stderr: <<-MSG)
+ 0:03 +0: Counter increments smoke test
+ ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
+ The following _Exception was thrown running a test:
+ Exception
+
+ When the exception was thrown, this was the stack:
+ #0 main. (file:///Users/user/project/test/widget_test.dart:18:5)
+
+ #1 main. (file:///Users/user/project/test/widget_test.dart)
+ #2 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29)
+
+ #3 testWidgets.. (package:flutter_test/src/widget_tester.dart)
+ #4 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
+
+ #7 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
+ #8 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24)
+ #9 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54)
+ #14 withClock (package:clock/src/default.dart:48:10)
+ #15 FakeAsync.run. (package:fake_async/fake_async.dart:178:22)
+ #20 FakeAsync.run (package:fake_async/fake_async.dart:178:7)
+ #21 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
+ #22 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24)
+ #23 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19)
+
+ #24 Declarer.test.. (package:test_api/src/backend/declarer.dart)
+ #29 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13)
+ #30 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15)
+ #35 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
+ #36 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17)
+
+ #37 Invoker._onRun... (package:test_api/src/backend/invoker.dart)
+ #42 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9)
+ #43 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
+ #44 Invoker._onRun. (package:test_api/src/backend/invoker.dart:369:7)
+ #51 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)
+ #52 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)
+ #53 RemoteListener._runLiveTest. (package:test_api/src/remote_listener.dart:256:16)
+ #58 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)
+ #59 RemoteListener._serializeTest. (package:test_api/src/remote_listener.dart:208:7)
+ #77 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)
+ #78 new _MultiChannel. (package:stream_channel/src/multi_channel.dart:159:31)
+ #82 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
+ #116 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1145:21)
+ #124 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)
+ #125 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)
+ #135 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)
+ #144 new _RawSocket. (dart:io-patch/socket_patch.dart:1580:33)
+ #145 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)
+ (elided 111 frames from dart:async and package:stack_trace)
+
+ The test description was:
+ Counter increments smoke test
+ ════════════════════════════════════════════════════════════════════════════════════════════════════
+ 00:03 +0 -1: Counter increments smoke test [E]
+ Test failed. See exception logs above.
+ The test description was: Counter increments smoke test
+
+ 00:03 +0 -1: Some tests failed.
+ MSG
+ end
+
+ it { should fail_hook }
+ end
+
+ context 'with a test failure' do
+ before do
+ result.stub(stderr: '', stdout: <<-MSG)
+ 00:02 +0: Counter increments smoke test
+ ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
+ The following TestFailure object was thrown running a test:
+ Expected: exactly one matching node in the widget tree
+ Actual: _TextFinder:
+ Which: means none were found but one was expected
+
+ When the exception was thrown, this was the stack:
+ #4 main. (file:///Users/user/project/test/widget_test.dart:19:5)
+
+ #5 main. (file:///Users/user/project/test/widget_test.dart)
+ #6 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29)
+
+ #7 testWidgets.. (package:flutter_test/src/widget_tester.dart)
+ #8 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
+
+ #11 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
+ #12 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24)
+ #13 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54)
+ #18 withClock (package:clock/src/default.dart:48:10)
+ #19 FakeAsync.run. (package:fake_async/fake_async.dart:178:22)
+ #24 FakeAsync.run (package:fake_async/fake_async.dart:178:7)
+ #25 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
+ #26 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24)
+ #27 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19)
+
+ #28 Declarer.test.. (package:test_api/src/backend/declarer.dart)
+ #33 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13)
+ #34 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15)
+ #39 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
+ #40 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17)
+
+ #41 Invoker._onRun... (package:test_api/src/backend/invoker.dart)
+ #46 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9)
+ #47 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
+ #48 Invoker._onRun.]