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 35002594..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
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 7b489462..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: ruby
-
-cache: bundler
-
-rvm:
- - 2.4
- - 2.5
- - 2.6
- - 2.7
- - 3.0
-
-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 f27e6208..ad1cb477 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,70 @@
# Overcommit Changelog
-## master (unreleased)
+## 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
diff --git a/Gemfile b/Gemfile
index d1cd3c37..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.82.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 36126a2c..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)
@@ -18,46 +17,49 @@ 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:
+This project aims to support the following Ruby runtimes on \*nix (and best effort on Windows):
-* Ruby 2.4+
-
-### 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
@@ -129,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
@@ -209,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
@@ -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)
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/config/default.yml b/config/default.yml
index 51189348..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
@@ -536,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'
@@ -699,11 +713,16 @@ 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'
required_executable: 'rubocop'
- flags: ['--format=emacs', '--force-exclusion', '--display-cop-names', '--disable-pending-cops']
+ flags: ['--format=emacs', '--force-exclusion', '--display-cop-names']
install_command: 'gem install rubocop'
include:
- '**/*.gemspec'
@@ -780,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'
@@ -1316,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'
diff --git a/lib/overcommit/cli.rb b/lib/overcommit/cli.rb
index d8b743a3..dafc545a 100644
--- a/lib/overcommit/cli.rb
+++ b/lib/overcommit/cli.rb
@@ -9,6 +9,7 @@ module Overcommit
class CLI # rubocop:disable Metrics/ClassLength
def initialize(arguments, input, logger)
@arguments = arguments
+ @cli_options = {}
@input = input
@log = logger
@options = {}
@@ -28,6 +29,8 @@ def run
sign
when :run_all
run_all
+ when :diff
+ diff
end
rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e
puts e
@@ -45,7 +48,7 @@ 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
@@ -94,10 +97,15 @@ def add_installation_options(opts)
@options[:force] = true
end
- opts.on('-r [hook]', '--run [hook]', 'Run specified hook against all git tracked files. Defaults to `pre_commit`.') do |arg|
+ 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
def add_other_options(opts)
@@ -124,15 +132,13 @@ def install_or_uninstall
end
@options[:targets].each do |target|
- begin
- 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
+ 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
@@ -200,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(@options[:hook_to_run], 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/configuration_loader.rb b/lib/overcommit/configuration_loader.rb
index 77a08549..8fd68a01 100644
--- a/lib/overcommit/configuration_loader.rb
+++ b/lib/overcommit/configuration_loader.rb
@@ -53,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
@@ -64,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)
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/git_config.rb b/lib/overcommit/git_config.rb
index 392dd9da..c1243861 100644
--- a/lib/overcommit/git_config.rb
+++ b/lib/overcommit/git_config.rb
@@ -17,7 +17,7 @@ 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/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/text_width.rb b/lib/overcommit/hook/commit_msg/text_width.rb
index 63addbcf..52de3bd7 100644
--- a/lib/overcommit/hook/commit_msg/text_width.rb
+++ b/lib/overcommit/hook/commit_msg/text_width.rb
@@ -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/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/erb_lint.rb b/lib/overcommit/hook/pre_commit/erb_lint.rb
index a903b10b..ae5af164 100644
--- a/lib/overcommit/hook/pre_commit/erb_lint.rb
+++ b/lib/overcommit/hook/pre_commit/erb_lint.rb
@@ -12,7 +12,7 @@ def run
return :pass if result.success?
extract_messages(
- result.stdout.split("\n\n")[1..-1],
+ result.stdout.split("\n\n")[1..],
MESSAGE_REGEX
)
end
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/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/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/php_lint.rb b/lib/overcommit/hook/pre_commit/php_lint.rb
index 3f70bfb2..f2ba2f24 100644
--- a/lib/overcommit/hook/pre_commit/php_lint.rb
+++ b/lib/overcommit/hook/pre_commit/php_lint.rb
@@ -4,9 +4,9 @@ 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
+ # rubocop:enable Layout/LineLength
MESSAGE_REGEX = /^(?.+)\:\s+(?.+) in (?.+) on line (?\d+)/.freeze
def run
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_schema_up_to_date.rb b/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb
index 13d7932f..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
@@ -34,6 +34,12 @@ def run # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplex
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/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/stylelint.rb b/lib/overcommit/hook/pre_commit/stylelint.rb
index b129cbd2..aae26f08 100644
--- a/lib/overcommit/hook/pre_commit/stylelint.rb
+++ b/lib/overcommit/hook/pre_commit/stylelint.rb
@@ -12,7 +12,7 @@ class Stylelint < Base
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(
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_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_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/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/replace_branch.rb b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
index 7a40ed69..8852b0b5 100644
--- a/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
+++ b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb
@@ -11,9 +11,9 @@ module Overcommit::Hook::PrepareCommitMsg
# For instance, if your current branch is `123-topic` then this config
#
# branch_pattern: '(\d+)-(\w+)'
- # replacement_text: '[#\1]'
+ # replacement_text: '[#\1] '
#
- # would make this hook prepend commit messages with `[#123]`.
+ # would make this hook prepend commit messages with `[#123] `.
#
# Similarly, a replacement text of `[\1][\2]` would result in `[123][topic]`.
#
@@ -53,7 +53,7 @@ def new_template
@new_template ||=
begin
curr_branch = Overcommit::GitRepo.current_branch
- curr_branch.gsub(branch_pattern, replacement_text).strip
+ curr_branch.gsub(branch_pattern, replacement_text)
end
end
@@ -69,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
@@ -85,7 +85,7 @@ def skipped_commit_types
end
def skip?
- skipped_commit_types.include?(commit_message_source)
+ super || skipped_commit_types.include?(commit_message_source)
end
end
end
diff --git a/lib/overcommit/hook/shared/pronto.rb b/lib/overcommit/hook/shared/pronto.rb
index 9975fbc6..cf8a3139 100644
--- a/lib/overcommit/hook/shared/pronto.rb
+++ b/lib/overcommit/hook/shared/pronto.rb
@@ -15,11 +15,19 @@ def run
result = execute(command)
return :pass if result.success?
- extract_messages(
+ # 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_context.rb b/lib/overcommit/hook_context.rb
index acdff1bb..5863ed94 100644
--- a/lib/overcommit/hook_context.rb
+++ b/lib/overcommit/hook_context.rb
@@ -2,13 +2,13 @@
# 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)
+ 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.
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/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/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 3be06388..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
@@ -51,7 +52,7 @@ def spawn(args, options = {})
err.rewind
out.rewind
- Result.new(process.exit_code, to_utf8(out.read), to_utf8(err.read))
+ Result.new(process.exit_code, out.read, err.read)
end
# Spawns a new process in the background using the given array of
@@ -83,23 +84,6 @@ def win32_prepare_args(args)
%w[cmd.exe /c] + [args.join(' ')]
end
- # Convert string from current locale to utf-8
- #
- # When running commands under windows the command output is using
- # current system locale (depends on system lanuage) not UTF-8
- #
- # @param process [String]
- # @return [String]
- def to_utf8(string)
- if Encoding.locale_charmap == 'UTF-8'
- return string
- end
-
- ec = Encoding::Converter.new(Encoding.locale_charmap, 'UTF-8')
- # Convert encoding, alternatively simple: string.scrub will suffice
- ec.convert(string)
- end
-
# @param process [ChildProcess]
# @return [Array]
def assign_output_streams(process)
diff --git a/lib/overcommit/utils/messages_utils.rb b/lib/overcommit/utils/messages_utils.rb
index c92a6010..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)
diff --git a/lib/overcommit/version.rb b/lib/overcommit/version.rb
index ec9b0cca..872c5327 100644
--- a/lib/overcommit/version.rb
+++ b/lib/overcommit/version.rb
@@ -2,5 +2,5 @@
# Defines the gem version.
module Overcommit
- VERSION = '0.58.0'
+ VERSION = '0.67.1'
end
diff --git a/overcommit.gemspec b/overcommit.gemspec
index ef9f24fc..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,9 +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', '< 5'
+ s.add_dependency 'childprocess', '>= 0.6.3', '< 6'
s.add_dependency 'iniparse', '~> 1.4'
- s.add_dependency 'rexml', '~> 3.2'
+ s.add_dependency 'rexml', '>= 3.3.9'
end
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 7a0175da..a6a6a4ba 100644
--- a/spec/integration/gemfile_option_spec.rb
+++ b/spec/integration/gemfile_option_spec.rb
@@ -3,99 +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.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
+ 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_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`
+ 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 58e22e65..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:
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 128737c3..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
@@ -282,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
@@ -327,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
@@ -343,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 }
@@ -369,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`
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 7a050d10..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,6 +42,7 @@
context 'and it reports a warning' do
before do
+ result.stub(:stderr).and_return('')
result.stub(:stdout).and_return <<~MESSAGE
Running Pronto::Rubocop
file1.rb:12 W: Line is too long. [107/80]
@@ -54,5 +56,16 @@
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/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_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_push/mix_test_spec.rb b/spec/overcommit/hook/pre_push/mix_test_spec.rb
new file mode 100644
index 00000000..798da7f7
--- /dev/null
+++ b/spec/overcommit/hook/pre_push/mix_test_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Overcommit::Hook::PrePush::MixTest do
+ let(:config) { Overcommit::ConfigurationLoader.default_configuration }
+ let(:context) { double('context') }
+ subject { described_class.new(config, context) }
+
+ context 'when mix test 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 test exits unsucessfully' do
+ before do
+ result = double('result')
+ result.stub(:success?).and_return(false)
+ result.stub(:stdout).and_return('Some error message')
+ result.stub(:stderr).and_return('')
+ subject.stub(:execute).and_return(result)
+ end
+
+ it { should fail_hook 'Some error message' }
+ end
+end
diff --git a/spec/overcommit/hook/pre_push/pronto_spec.rb b/spec/overcommit/hook/pre_push/pronto_spec.rb
index 3ca660e5..5f48b7d7 100644
--- a/spec/overcommit/hook/pre_push/pronto_spec.rb
+++ b/spec/overcommit/hook/pre_push/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,6 +42,7 @@
context 'and it reports a warning' do
before do
+ result.stub(:stderr).and_return('')
result.stub(:stdout).and_return <<~MESSAGE
Running Pronto::Rubocop
file1.rb:12 W: Line is too long. [107/80]
@@ -54,5 +56,16 @@
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_push/r_spec_spec.rb b/spec/overcommit/hook/pre_push/r_spec_spec.rb
index 26f6ea0f..1efc56d7 100644
--- a/spec/overcommit/hook/pre_push/r_spec_spec.rb
+++ b/spec/overcommit/hook/pre_push/r_spec_spec.rb
@@ -16,6 +16,42 @@
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(
+ 'PrePush' => {
+ '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
diff --git a/spec/overcommit/hook/prepare_commit_msg/base_spec.rb b/spec/overcommit/hook/prepare_commit_msg/base_spec.rb
index 616d6b8e..873ed637 100644
--- a/spec/overcommit/hook/prepare_commit_msg/base_spec.rb
+++ b/spec/overcommit/hook/prepare_commit_msg/base_spec.rb
@@ -38,9 +38,9 @@
contents + "bravo\n"
end
end
- Thread.new { hook_1.run }
- Thread.new { hook_2.run }
- Thread.list.each { |t| t.join unless t == Thread.current }
+ t1 = Thread.new { hook_1.run }
+ t2 = Thread.new { hook_2.run }
+ [t1, t2].each(&:join)
expect(File.read(tempfile)).to match(/alpha\n#{initial_content}bravo\n/m)
end
end
diff --git a/spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb b/spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb
index 3844f77a..e355517f 100644
--- a/spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb
+++ b/spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb
@@ -62,6 +62,34 @@ def remove_file(name)
it 'prepends the replacement text' do
expect(File.read('COMMIT_EDITMSG')).to eq("[#123]\n")
end
+
+ context 'when the replacement text contains a space' do
+ let(:config) { new_config('replacement_text' => '[\1] ') }
+
+ it 'prepends the replacement text, including the space' do
+ expect(File.read('COMMIT_EDITMSG')).to eq("[123] \n")
+ end
+ end
+
+ context 'when skip_if exits with a zero status' do
+ let(:config) { new_config('skip_if' => ['bash', '-c', 'exit 0']) }
+
+ it { is_expected.to pass }
+
+ it 'does not change the commit message' do
+ expect(File.read('COMMIT_EDITMSG')).to eq("\n")
+ end
+ end
+
+ context 'when skip_if exits with a non-zero status' do
+ let(:config) { new_config('skip_if' => ['bash', '-c', 'exit 1']) }
+
+ it { is_expected.to pass }
+
+ it 'does change the commit message' do
+ expect(File.read('COMMIT_EDITMSG')).to eq("[#123]\n")
+ end
+ end
end
context "when the checked out branch doesn't matches the pattern" do
diff --git a/spec/overcommit/hook_context/commit_msg_spec.rb b/spec/overcommit/hook_context/commit_msg_spec.rb
index 2aa68516..058f1508 100644
--- a/spec/overcommit/hook_context/commit_msg_spec.rb
+++ b/spec/overcommit/hook_context/commit_msg_spec.rb
@@ -298,7 +298,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -474,7 +474,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -500,7 +500,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -532,7 +532,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
`git rm sub`
example.run
@@ -561,7 +561,7 @@
end
repo do
- `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`
expect(subject).to_not include File.expand_path('test-sub')
end
end
diff --git a/spec/overcommit/hook_context/diff_spec.rb b/spec/overcommit/hook_context/diff_spec.rb
new file mode 100644
index 00000000..0c712fbc
--- /dev/null
+++ b/spec/overcommit/hook_context/diff_spec.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'overcommit/hook_context/diff'
+
+describe Overcommit::HookContext::Diff do
+ let(:config) { double('config') }
+ let(:args) { [] }
+ let(:input) { double('input') }
+ let(:context) { described_class.new(config, args, input, diff: 'master') }
+
+ describe '#modified_files' do
+ subject { context.modified_files }
+
+ context 'when repo contains no files' do
+ around do |example|
+ repo do
+ `git commit --allow-empty -m "Initial commit"`
+ `git checkout -b other-branch 2>&1`
+ example.run
+ end
+ end
+
+ it { should be_empty }
+ end
+
+ context 'when the repo contains files that are unchanged from the ref' do
+ around do |example|
+ repo do
+ touch('some-file')
+ `git add some-file`
+ touch('some-other-file')
+ `git add some-other-file`
+ `git commit -m "Add files"`
+ `git checkout -b other-branch 2>&1`
+ example.run
+ end
+ end
+
+ it { should be_empty }
+ end
+
+ context 'when repo contains files that have been changed from the ref' do
+ around do |example|
+ repo do
+ touch('some-file')
+ `git add some-file`
+ touch('some-other-file')
+ `git add some-other-file`
+ `git commit -m "Add files"`
+ `git checkout -b other-branch 2>&1`
+ File.open('some-file', 'w') { |f| f.write("hello\n") }
+ `git add some-file`
+ `git commit -m "Edit file"`
+ example.run
+ end
+ end
+
+ it { should == %w[some-file].map { |file| File.expand_path(file) } }
+ end
+
+ context 'when repo contains submodules' do
+ around do |example|
+ submodule = repo do
+ touch 'foo'
+ `git add foo`
+ `git commit -m "Initial commit"`
+ end
+
+ repo do
+ `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`
+ `git commit --allow-empty -m "Initial commit"`
+ `git checkout -b other-branch 2>&1`
+ example.run
+ end
+ end
+
+ it { should_not include File.expand_path('test-sub') }
+ end
+ end
+
+ describe '#modified_lines_in_file' do
+ let(:modified_file) { 'some-file' }
+ subject { context.modified_lines_in_file(modified_file) }
+
+ context 'when file contains a trailing newline' do
+ around do |example|
+ repo do
+ touch(modified_file)
+ `git add #{modified_file}`
+ `git commit -m "Add file"`
+ `git checkout -b other-branch 2>&1`
+ File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write("#{i}\n") } }
+ `git add #{modified_file}`
+ `git commit -m "Edit file"`
+ example.run
+ end
+ end
+
+ it { should == Set.new(1..3) }
+ end
+
+ context 'when file does not contain a trailing newline' do
+ around do |example|
+ repo do
+ touch(modified_file)
+ `git add #{modified_file}`
+ `git commit -m "Add file"`
+ `git checkout -b other-branch 2>&1`
+ File.open(modified_file, 'w') do |f|
+ (1..2).each { |i| f.write("#{i}\n") }
+ f.write(3)
+ end
+ `git add #{modified_file}`
+ `git commit -m "Edit file"`
+ example.run
+ end
+ end
+
+ it { should == Set.new(1..3) }
+ end
+ end
+
+ describe '#hook_type_name' do
+ subject { context.hook_type_name }
+
+ it { should == 'pre_commit' }
+ end
+
+ describe '#hook_script_name' do
+ subject { context.hook_script_name }
+
+ it { should == 'pre-commit' }
+ end
+
+ describe '#initial_commit?' do
+ subject { context.initial_commit? }
+
+ before { Overcommit::GitRepo.stub(:initial_commit?).and_return(true) }
+
+ it { should == true }
+ end
+end
diff --git a/spec/overcommit/hook_context/pre_commit_spec.rb b/spec/overcommit/hook_context/pre_commit_spec.rb
index dae42e66..719982a1 100644
--- a/spec/overcommit/hook_context/pre_commit_spec.rb
+++ b/spec/overcommit/hook_context/pre_commit_spec.rb
@@ -207,7 +207,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -383,7 +383,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -409,7 +409,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
echo('Hello World', 'sub/submodule-file')
`git submodule foreach "git add submodule-file" < #{File::NULL}`
@@ -441,7 +441,7 @@
end
repo do
- `git submodule add #{submodule} sub > #{File::NULL} 2>&1`
+ `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`
`git commit -m "Add submodule"`
`git rm sub`
example.run
@@ -470,7 +470,7 @@
end
repo do
- `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`
+ `git -c protocol.file.allow=always submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`
expect(subject).to_not include File.expand_path('test-sub')
end
end
diff --git a/spec/overcommit/installer_spec.rb b/spec/overcommit/installer_spec.rb
index c7cfb05e..5283b2b6 100644
--- a/spec/overcommit/installer_spec.rb
+++ b/spec/overcommit/installer_spec.rb
@@ -231,8 +231,8 @@ def hook_files_installed?(hooks_dir)
context 'which has an external git dir' do
let(:submodule) { File.join(target, 'submodule') }
before do
- system 'git', 'submodule', 'add', target, 'submodule',
- chdir: target, out: :close, err: :close
+ system 'git', '-c', 'protocol.file.allow=always', 'submodule', 'add', target,
+ 'submodule', chdir: target, out: :close, err: :close
end
let(:submodule_git_file) { File.join(submodule, '.git') }
let(:submodule_git_dir) do
diff --git a/spec/overcommit/utils_spec.rb b/spec/overcommit/utils_spec.rb
index 3be592ff..acaf3eba 100644
--- a/spec/overcommit/utils_spec.rb
+++ b/spec/overcommit/utils_spec.rb
@@ -119,17 +119,17 @@
describe '.supported_hook_types' do
subject { described_class.supported_hook_types }
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
it { should =~ %w[commit-msg pre-commit post-checkout post-commit post-merge post-rewrite pre-push pre-rebase prepare-commit-msg] }
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
end
describe '.supported_hook_type_classes' do
subject { described_class.supported_hook_type_classes }
- # rubocop:disable Metrics/LineLength
+ # rubocop:disable Layout/LineLength
it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush PreRebase PrepareCommitMsg] }
- # rubocop:enable Metrics/LineLength
+ # rubocop:enable Layout/LineLength
end
describe '.parent_command' do
@@ -190,7 +190,7 @@
it 'invokes CommandSplitter.execute' do
Overcommit::CommandSplitter.
should_receive(:execute).
- with(arguments, args: splittable_args).
+ with(arguments, { args: splittable_args }).
and_return(double(status: 0, stdout: '', stderr: ''))
subject
end
@@ -215,7 +215,7 @@
end
it 'executes the command' do
- wait_until { subject.exited? } # Make sure process terminated before checking
+ wait_until(timeout: 5) { subject.exited? } # Make sure process terminated before checking
File.exist?('some-file').should == true
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index fe6f82da..b6a24d82 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,13 +1,23 @@
# frozen_string_literal: true
-if ENV['TRAVIS']
- # When running in Travis, report coverage stats to Coveralls.
- require 'coveralls'
- Coveralls.wear!
-else
- # Otherwise render coverage information in coverage/index.html and display
- # coverage percentage in the console.
- require 'simplecov'
+require 'bundler'
+require 'simplecov'
+SimpleCov.start do
+ add_filter 'bin/'
+ add_filter 'libexec/'
+ add_filter 'spec/'
+ add_filter 'template-dir/'
+
+ if ENV['CI']
+ require 'simplecov-lcov'
+
+ SimpleCov::Formatter::LcovFormatter.config do |c|
+ c.report_with_single_file = true
+ c.single_report_path = 'coverage/lcov.info'
+ end
+
+ formatter SimpleCov::Formatter::LcovFormatter
+ end
end
require 'overcommit'
diff --git a/spec/support/git_spec_helpers.rb b/spec/support/git_spec_helpers.rb
index 23e46c51..28eb94da 100644
--- a/spec/support/git_spec_helpers.rb
+++ b/spec/support/git_spec_helpers.rb
@@ -13,7 +13,7 @@ module GitSpecHelpers
# @return [String] path of the repository
def repo(options = {})
directory('some-repo') do
- create_cmd = %w[git init]
+ create_cmd = %w[git init --initial-branch=master]
create_cmd += ['--template', options[:template_dir]] if options[:template_dir]
create_cmd += ['--separate-git-dir', options[:git_dir]] if options[:git_dir]
diff --git a/template-dir/hooks/commit-msg b/template-dir/hooks/commit-msg
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/commit-msg
+++ b/template-dir/hooks/commit-msg
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/overcommit-hook b/template-dir/hooks/overcommit-hook
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/overcommit-hook
+++ b/template-dir/hooks/overcommit-hook
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/post-checkout b/template-dir/hooks/post-checkout
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/post-checkout
+++ b/template-dir/hooks/post-checkout
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/post-commit b/template-dir/hooks/post-commit
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/post-commit
+++ b/template-dir/hooks/post-commit
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/post-merge b/template-dir/hooks/post-merge
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/post-merge
+++ b/template-dir/hooks/post-merge
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/post-rewrite b/template-dir/hooks/post-rewrite
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/post-rewrite
+++ b/template-dir/hooks/post-rewrite
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/pre-commit b/template-dir/hooks/pre-commit
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/pre-commit
+++ b/template-dir/hooks/pre-commit
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/pre-push b/template-dir/hooks/pre-push
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/pre-push
+++ b/template-dir/hooks/pre-push
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/pre-rebase b/template-dir/hooks/pre-rebase
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/pre-rebase
+++ b/template-dir/hooks/pre-rebase
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
diff --git a/template-dir/hooks/prepare-commit-msg b/template-dir/hooks/prepare-commit-msg
index 197dc198..87ffeb58 100755
--- a/template-dir/hooks/prepare-commit-msg
+++ b/template-dir/hooks/prepare-commit-msg
@@ -27,9 +27,10 @@ if hook_type == 'overcommit-hook'
end
# Check if Overcommit should invoke a Bundler context for loading gems
-require 'yaml'
-# rubocop:disable Style/RescueModifier
-if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
+File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/
+gemfile = Regexp.last_match(1)
+
+if gemfile
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
]