diff --git a/.git-hooks/pre_commit/master_hooks_match.rb b/.git-hooks/pre_commit/master_hooks_match.rb index f2b845a9..09a0e34b 100644 --- a/.git-hooks/pre_commit/master_hooks_match.rb +++ b/.git-hooks/pre_commit/master_hooks_match.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fileutils' module Overcommit::Hook::PreCommit 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/.gitignore b/.gitignore index 2bb7f841..7ea8f1f7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ coverage/ pkg/ .bundle .idea +.history/ +.vscode/ diff --git a/.overcommit.yml b/.overcommit.yml index 501a51c5..adb9505d 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -1,6 +1,10 @@ gemfile: Gemfile PreCommit: + # Disabled since this causes spurious failures on AppVeyor builds + BrokenSymlinks: + enabled: false + BundleCheck: enabled: 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 1d95f9d4..1b4e7ad7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,9 @@ +inherit_from: .rubocop_todo.yml + AllCops: - TargetRubyVersion: 2.2 + TargetRubyVersion: 2.6 + NewCops: disable + SuggestExtensions: false Layout/ClosingParenthesisIndentation: Enabled: false @@ -14,12 +18,15 @@ Layout/EndOfLine: Layout/FirstParameterIndentation: Enabled: false -Layout/IndentArray: +Layout/FirstArrayElementIndentation: Enabled: false -Layout/IndentHeredoc: +Layout/HeredocIndentation: Enabled: false +Layout/LineLength: + Max: 100 + Layout/MultilineMethodCallIndentation: Enabled: false @@ -48,9 +55,6 @@ Metrics/AbcSize: Metrics/BlockLength: Enabled: false -Metrics/LineLength: - Max: 100 - Metrics/MethodLength: Max: 20 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..53df9159 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,137 @@ +# This configuration was generated by +# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit` +# on 2024-01-10 14:09:00 UTC using RuboCop version 1.59.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowAliasSyntax, AllowedMethods. +# AllowedMethods: alias_method, public, protected, private +Layout/EmptyLinesAroundAttributeAccessor: + Exclude: + - 'lib/overcommit/hook_context/post_merge.rb' + +# Offense count: 6 +# Configuration parameters: AllowedMethods. +# AllowedMethods: enums +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/overcommit/message_processor_spec.rb' + +# Offense count: 4 +Lint/MixedRegexpCaptureTypes: + Exclude: + - 'lib/overcommit/hook/pre_commit/dart_analyzer.rb' + - 'lib/overcommit/hook/pre_commit/java_checkstyle.rb' + - 'lib/overcommit/hook/pre_commit/kt_lint.rb' + - 'lib/overcommit/hook/pre_commit/scalastyle.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Lint/RedundantCopDisableDirective: + Exclude: + - 'lib/overcommit/hook_runner.rb' + - 'lib/overcommit/printer.rb' + +# Offense count: 1 +# Configuration parameters: CountComments, Max, CountAsOne. +Metrics/ClassLength: + Exclude: + - 'lib/overcommit/utils.rb' + +# Offense count: 2 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/CyclomaticComplexity: + Exclude: + - 'lib/overcommit/configuration.rb' + - 'lib/overcommit/hook_runner.rb' + +# Offense count: 3 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/PerceivedComplexity: + Exclude: + - 'lib/overcommit/configuration.rb' + - 'lib/overcommit/configuration_validator.rb' + - 'lib/overcommit/hook_runner.rb' + +# Offense count: 23 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/GlobalStdStream: + Exclude: + - 'bin/overcommit' + - 'lib/overcommit/hook/post_commit/git_guilt.rb' + - 'template-dir/hooks/commit-msg' + - 'template-dir/hooks/overcommit-hook' + - 'template-dir/hooks/post-checkout' + - 'template-dir/hooks/post-commit' + - 'template-dir/hooks/post-merge' + - 'template-dir/hooks/post-rewrite' + - 'template-dir/hooks/pre-commit' + - 'template-dir/hooks/pre-push' + - 'template-dir/hooks/pre-rebase' + - 'template-dir/hooks/prepare-commit-msg' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/HashTransformValues: + Exclude: + - 'lib/overcommit/configuration.rb' + - 'lib/overcommit/configuration_validator.rb' + +# Offense count: 1 +# Configuration parameters: AllowedMethods. +# AllowedMethods: respond_to_missing? +Style/OptionalBooleanParameter: + Exclude: + - 'lib/overcommit/logger.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantBegin: + Exclude: + - 'lib/overcommit/hook/prepare_commit_msg/replace_branch.rb' + - 'lib/overcommit/utils.rb' + +# Offense count: 10 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SafeForConstants. +Style/RedundantFetchBlock: + Exclude: + - 'lib/overcommit/configuration.rb' + - 'lib/overcommit/configuration_validator.rb' + - 'lib/overcommit/hook/base.rb' + - 'lib/overcommit/hook/pre_commit/chamber_verification.rb' + - 'lib/overcommit/logger.rb' + - 'spec/support/shell_helpers.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantRegexpEscape: + Exclude: + - 'lib/overcommit/configuration.rb' + - 'lib/overcommit/hook/pre_commit/php_cs.rb' + - 'lib/overcommit/hook/pre_commit/php_lint.rb' + - 'lib/overcommit/hook/pre_commit/php_stan.rb' + +# Offense count: 15 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. +Style/StringConcatenation: + Exclude: + - 'lib/overcommit/hook/pre_commit/bundle_check.rb' + - 'lib/overcommit/hook_runner.rb' + - 'lib/overcommit/message_processor.rb' + - 'spec/integration/gemfile_option_spec.rb' + - 'spec/overcommit/hook/commit_msg/text_width_spec.rb' + - 'spec/overcommit/hook/prepare_commit_msg/base_spec.rb' + - 'spec/overcommit/message_processor_spec.rb' + - 'spec/spec_helper.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ZeroLengthPredicate: + Exclude: + - 'lib/overcommit/hook/pre_commit/ruby_syntax.rb' diff --git a/.simplecov b/.simplecov deleted file mode 100644 index 3cc40052..00000000 --- a/.simplecov +++ /dev/null @@ -1,6 +0,0 @@ -SimpleCov.start do - add_filter 'bin/' - add_filter 'libexec/' - add_filter 'spec/' - add_filter 'template-dir/' -end diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 785041a8..00000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: ruby - -sudo: false - -cache: bundler - -rvm: - - 2.2 - - 2.3.7 - - 2.4.4 - - 2.5.1 - -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 f7d2e114..ad1cb477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,209 @@ # 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 + +* Add `rexml` dependency explicitly to support Ruby 3.0. +* Add `DartAnalyzer` pre-commit hook to analyze Dart files. +* Add `PubTest` and `FlutterTest` pre-push hooks to run `pub test` and `flutter test` for Dart projects, respectively. +* Update `index-tags` script to support scanning only files tracked by Git. +* Fix `EsLint` pre-commit hook to not report certain false positives. +* Update `YamlLint` to `fail` the run instead of `warn` when errors are detected. +* Update `YamlLint` parse the line number of output so it is line aware. +* Gracefully handle breaking behavior in upstream Psych gem to support YAML aliases. +* Fix case where `git` would delete all tracked files when popping stash. + +## 0.57.0 + +* Fix `CommitMsg` hooks to be able to call `modified_lines_in_file`. +* Add `ErbLint` pre-commit hook to lint ERB files. + +## 0.56.0 + +* Update `ReplaceBranch` prepare-commit-msg hook to avoid running on `--amend` by default. +* Add support for `modified_files` and `modified_lines_in_file` in `CommitMsg` hooks. + +## 0.55.0 + +* Fix `GoFmt` to not be enabled by default. This was enabled by mistake when introduced in Overcommit `0.52.0`. + +## 0.54.1 + +* Fix `Overcommit::GitRepo.list_files` helper to work with arbitrarily large lists of files. +* Fix `AuthorName` to allow mononyms to be more inclusive of names. + +## 0.54.0 + +* Fix `YamlLint` pre-commit hook +* Relax `childprocess` gem version constraint to allow version 4.x + +## 0.53.0 + +* Improve performance in `PhpCs` pre-commit hook +* Add `Pronto` pre-push hook +* Remove erroneous extra newline in replacement string for `ReplaceBranch` prepare-commit-msg hook +* Add note about potentially checking your stash when hook is interrupted +* Add support for skipping hooks based on command result using the `skip_if` option + +## 0.52.1 + +* Fix case where no standard input is provided to `pre-push` hooks + +## 0.52.0 + +* Fix `Mdl` to properly parse JSON output from `mdl` +* Add `GolangciLint` pre-commit and pre-push hooks +* Add `GoTest` pre-push hook +* Add `GoFmt` pre-commit hook +* Add `exclude_branches` hook option to disable hooks running on specific branches +* Add `exclude_remotes` pre-push hook option to disable pre-push hooks running against specific remotes +* Change default behavior of pre-push hooks to **not** run against deleted remote refs +* Add `include_remote_ref_deletions` pre-push hook option to allow running for a remote branch deletion +* Rename `remote_branch_deletion?` pre-push hook helper to `remote_ref_deletion?` +* Add per-branch `destructive_only` setting to `ProtectedBranches` pre-push hook + +## 0.51.0 + +* Stop stashing in pre-commit hooks when all changes are already staged, + avoiding unnecessary file modification +* Improve instructions for recovering commit message when a `commit-msg` hook + fails + +## 0.50.0 + +* Fix Overcommit to display helpful error message when a hook does not inherit + from the base class +* Relax `childprocess` gem constraint to allow up to version 3.x +* Display a helpful message if hooks do not inherit from the correct base class +* Fix Overcommit to work with emacs/magit by [disabling literal pathspecs](https://magit.vc/manual/magit/My-Git-hooks-work-on-the-command_002dline-but-not-inside-Magit.html) + +## 0.49.1 + +* Fix Overcommit to run when executed with no parent process +* Fix `Stylelint` pre-commit hook `required_executable` + +## 0.49.0 + +### New Features + +* Add `skipped_commit_types` option to `ReplaceBranch` prepare-commit-msg hook +* Add `RubySyntax` pre-commit hook +* Add `CodeSpellCheck` pre-commit hook + +### Changes + +* Relax `childprocess` dependency to allow version 1.x + +### Bug Fixes +* Fix deadlock which was more likely to occur when setting `parallelize` on a hook to `false` +* Fix `Mdl` hook to use JSON output and not fail on unexpected output + +## 0.48.1 + +* Fix `Stylelint` hook regex to extract line numbers with more than one digit +* Fix `CaseConflicts` hook to work with file paths containing double quotes + +## 0.48.0 + +* Drop support for Ruby 2.3 or older +* Support multi-line matches in `MessageFormat` `commit-msg` hook +* Add `FileSize` pre-commit hook + +## 0.47.0 + +### New Features + +* Add support for `prepare-commit-message` hooks +* Add [`SwiftLint`](https://github.com/realm/SwiftLint) pre-commit hook +* Add [`KtLint`](https://github.com/shyiko/ktlint) pre-commit hook +* Add `TerraformFormat` pre-commit hook +* Add [`CookStyle`](https://docs.chef.io/cookstyle.html) pre-commit hook + +### Changes + +* Update `validator_uri` for `W3cHtml` pre-commit hook +* Update `TsLint` pre-commit hook to support new output format +* Update `BundleCheck` error message with additional instructions + +### Bug Fixes + +* Add `--force-exclusion` flag to `Reek` pre-commit hook configuration to + ensure excluded files are excluded + +## 0.46.0 * Fix `Credo` pre-commit hook to lint applicable files only rather than all files * Add `PhpCsFixer` pre-commit hook * Add `YardCoverage` pre-commit hook * Add `Flay` pre-commit hook +* Add `Stylelint` pre-commit hook +* Fix `TsLint` default flags to work with `tslint` 5.11+ ## 0.45.0 @@ -475,7 +672,7 @@ * Disable almost all hooks by default. You will now need to explicitly enable almost all hooks yourself in your `.overcommit.yml`. If you are migrating from `overcommit` 0.23.0 and want to use the default configuration that shipped - with that version, copy the [default configuration from 0.23.0](https://github.com/brigade/overcommit/blob/9f03e9c82b385d375a836ca7146b117dbde5c822/config/default.yml) + with that version, copy the [default configuration from 0.23.0](https://github.com/sds/overcommit/blob/9f03e9c82b385d375a836ca7146b117dbde5c822/config/default.yml) * Update `ScssLint` pre-commit hook to properly handle special exit code that signals all files were filtered by exclusions (new as of `scss-lint` 0.36.0) * Update `childprocess` dependency to minimum 0.5.6 @@ -833,7 +1030,7 @@ ## 0.2.6 * Added check for linting HAML files with - [haml-lint](https://github.com/brigade/haml-lint) + [haml-lint](https://github.com/sds/haml-lint) ## 0.2.5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edca7acb..102b4032 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ The reasoning for this perhaps odd naming scheme is to strike a balance between consistency, familiarity for those who already know the tool, and Overcommit's ability to deduce the name of a hook from its filename and vice versa. -[1]: https://github.com/brigade/overcommit/issues +[1]: https://github.com/sds/overcommit/issues [2]: https://medium.com/brigade-engineering/the-secrets-to-great-commit-messages-106fc0a92a25 [3]: https://travis-ci.org/ @@ -82,4 +82,4 @@ ability to deduce the name of a hook from its filename and vice versa. This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. -[code-of-conduct]: https://github.com/brigade/code-of-conduct +[code-of-conduct]: https://github.com/civiccc/code-of-conduct diff --git a/Gemfile b/Gemfile index 57103c0d..49dc7923 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec @@ -6,8 +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 CI builds +if RUBY_VERSION < '2.7.0' + gem 'rubocop', '1.50.0' +else + gem 'rubocop', '1.59.0' +end -# Pin RuboCop for Travis builds. -gem 'rubocop', '0.54.0' +gem 'ffi' if Gem.win_platform? diff --git a/MIT-LICENSE b/MIT-LICENSE index 5d9e2dcb..d5312cfb 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,5 +1,4 @@ -Copyright (c) 2013-2015 Brigade Engineering, Aiden Scandella, Shane da Silva -https://www.brigade.com/ +Copyright (c) 2013-2019 Shane da Silva, Aiden Scandella Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 0127cedc..a0d46726 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,71 @@ [![Gem Version](https://badge.fury.io/rb/overcommit.svg)](https://badge.fury.io/rb/overcommit) -[![Build Status](https://travis-ci.org/brigade/overcommit.svg?branch=master)](https://travis-ci.org/brigade/overcommit) -[![Windows Build Status](https://ci.appveyor.com/api/projects/status/iqsr6jtrq6y0jhqq/branch/master?svg=true)](https://ci.appveyor.com/project/brigade/overcommit/branch/master) -[![Coverage Status](https://coveralls.io/repos/github/brigade/overcommit/badge.svg?branch=master)](https://coveralls.io/github/brigade/overcommit?branch=master) -[![Code Climate](https://codeclimate.com/github/brigade/overcommit.svg)](https://codeclimate.com/github/brigade/overcommit) -[![Inline docs](http://inch-ci.org/github/brigade/overcommit.svg?branch=master)](http://inch-ci.org/github/brigade/overcommit) +[![Build Status](https://github.com/sds/overcommit/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/sds/overcommit/actions/workflows/tests.yml/badge.svg?branch=main) +[![Coverage Status](https://coveralls.io/repos/github/sds/overcommit/badge.svg?branch=main)](https://coveralls.io/github/sds/overcommit?branch=main) +[![Maintainability](https://api.codeclimate.com/v1/badges/5da42f7f365e5fef6b4c/maintainability)](https://codeclimate.com/github/sds/overcommit/maintainability) +[![Inline docs](http://inch-ci.org/github/sds/overcommit.svg?branch=main)](http://inch-ci.org/github/sds/overcommit)

- Overcommit Logo + Overcommit Logo

`overcommit` is a tool to manage and configure [Git hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks). -![Demonstration](https://brigade.github.io/overcommit/overcommit.gif) - In addition to supporting a wide variety of hooks that can be used across multiple repositories, you can also define hooks specific to a repository which are stored in source control. You can also easily [add your existing hook scripts](#adding-existing-git-hooks) without writing any Ruby code. -* [Requirements](#requirements) - * [Dependencies](#dependencies) -* [Limitations](#limitations) -* [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): -* MRI 2.2+ +* Ruby 2.6+ ### Dependencies -Some of the hooks have third-party dependencies. For example, to lint your -[SCSS](http://sass-lang.com/) files, you're going to need our -[scss_lint gem](https://github.com/brigade/scss-lint). +Some hooks have third-party dependencies. For example, to lint your +[SCSS](http://sass-lang.com/) files, you're going to need the +[scss_lint gem](https://github.com/sds/scss-lint). Depending on the hooks you enable/disable for your repository, you'll need to ensure your development environment already has those dependencies installed. @@ -74,9 +79,9 @@ available during your hook runs. `overcommit` is installed via [RubyGems](https://rubygems.org/). It is strongly recommended that your environment support running `gem install` without -requiring `sudo` privileges. Using a Ruby version manager like -[`rbenv`](https://github.com/rbenv/rbenv/) or [`rvm`](https://rvm.io/) can help -here. +requiring root user privileges via `sudo` or otherwise. Using a Ruby version +manager like [`rbenv`](https://github.com/rbenv/rbenv/) or +[`rvm`](https://rvm.io/) is recommended. Once you have an environment that allows you to install gems without `sudo`, run: @@ -126,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 @@ -206,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 @@ -221,6 +231,9 @@ Option | Description `requires_files` | If `true`, this hook runs only if files that are applicable to it have been modified. See `include` and `exclude` for how to specify applicable files. `include` | File paths or glob patterns of files that apply to this hook. The hook will only run on the applicable files when they have been modified. Note that the concept of modified varies for different types of hooks. By default, `include` matches every file until you specify a list of patterns. `exclude` | File paths or glob patterns of files that do not apply to this hook. This is used to exclude any files that would have been matched by `include`. +`exclude_branches` | List of branch names or glob patterns of branches that this hook should not run against. +`exclude_remotes` | *`PrePush` hooks only.* List of remote names that the hook should not run against. +`include_remote_ref_deletions` | *`PrePush` hooks only.* By default, `PrePush` hooks will **not** run for pushes that delete a remote ref (i.e. branches or tags). Set to `true` to have the hook run even for deleted remote ref. `problem_on_unmodified_line` | How to treat errors reported on lines that weren't modified during the action captured by this hook (e.g. for pre-commit hooks, warnings/errors reported on lines that were not staged with `git add` may not be warnings/errors you care about). Valid values are `report`: report errors/warnings as-is regardless of line location (default); `warn`: report errors as warnings if they are on lines you didn't modify; and `ignore`: don't display errors/warnings at all if they are on lines you didn't modify (`ignore` is _not_ recommended). `on_fail` | Change the status of a failed hook to `warn` or `pass`. This allows you to treat failures as warnings or potentially ignore them entirely, but you should use caution when doing so as you might be hiding important information. `on_warn` | Similar to `on_fail`, change the status of a hook that returns a warning status to either `pass` (you wish to silence warnings entirely) or `fail` (you wish to treat all warnings as errors). @@ -233,6 +246,7 @@ Option | Description `processors` | The number of processing units to reserve for this hook. This does not reserve CPUs, but indicates that out of the total number of possible concurrent hooks allowed by the global `concurrency` option, this hook requires the specified number. Thus in the typical case where `concurrency` is set to the number of available cores (default), and you have a hook that executes an application which itself creates 2 threads (or is otherwise scheduled on 2 cores), you can indicate that Overcommit should allocate 2 `processors` to the hook. Ideally this means your hooks won't put undue load on your available cores. `install_command` | Command the user can run to install the `required_executable` (or alternately the specified `required_libraries`). This is intended for documentation purposes, as Overcommit does not install software on your behalf since there are too many edge cases where such behavior would result in incorrectly configured installations (e.g. installing a Python package in the global package space instead of in a virtual environment). `skip_file_checkout` | Whether to skip this hook for file checkouts (e.g. `git checkout some-ref -- file`). Only applicable to `PostCheckout` hooks. +`skip_if` | Array of arguments to be executed to determine whether or not the hook should run. For example, setting this to a value of `['bash', '-c', '! which my-executable']` would allow you to skip running this hook if `my-executable` was not in the bin path. In addition to the built-in configuration options, each hook can expose its own unique configuration options. The `AuthorEmail` hook, for example, allows @@ -476,7 +490,7 @@ instead of whatever contents are in your working tree (as you don't want unstaged changes to taint your results). Overcommit takes care of this for you, but to do it in a generalized way introduces this limitation. See the [thread tracking this -issue](https://github.com/brigade/overcommit/issues/238) for more details. +issue](https://github.com/sds/overcommit/issues/238) for more details. * [`*`AuthorEmail](lib/overcommit/hook/pre_commit/author_email.rb) * [`*`AuthorName](lib/overcommit/hook/pre_commit/author_name.rb) @@ -487,13 +501,17 @@ issue](https://github.com/brigade/overcommit/issues/238) for more details. * [BundleOutdated](lib/overcommit/hook/pre_commit/bundle_outdated.rb) * [`*`CaseConflicts](lib/overcommit/hook/pre_commit/case_conflicts.rb) * [ChamberSecurity](lib/overcommit/hook/pre_commit/chamber_security.rb) +* [CodeSpellCheck](lib/overcommit/hook/pre_commit/code_spell_check.rb) * [CoffeeLint](lib/overcommit/hook/pre_commit/coffee_lint.rb) * [Credo](lib/overcommit/hook/pre_commit/credo.rb) * [CssLint](lib/overcommit/hook/pre_commit/css_lint.rb) +* [DartAnalyzer](lib/overcommit/hook/pre_commit/dart_analyzer.rb) * [Dogma](lib/overcommit/hook/pre_commit/dogma.rb) +* [ErbLint](lib/overcommit/hook/pre_commit/erb_lint.rb) * [EsLint](lib/overcommit/hook/pre_commit/es_lint.rb) * [ExecutePermissions](lib/overcommit/hook/pre_commit/execute_permissions.rb) * [Fasterer](lib/overcommit/hook/pre_commit/fasterer.rb) +* [FileSize](lib/overcommit/hook/pre_commit/file_size.rb) * [FixMe](lib/overcommit/hook/pre_commit/fix_me.rb) * [Flay](lib/overcommit/hook/pre_commit/flay.rb) * [Foodcritic](lib/overcommit/hook/pre_commit/foodcritic.rb) @@ -514,6 +532,7 @@ issue](https://github.com/brigade/overcommit/issues/238) for more details. * [JsLint](lib/overcommit/hook/pre_commit/js_lint.rb) * [Jsl](lib/overcommit/hook/pre_commit/jsl.rb) * [JsonSyntax](lib/overcommit/hook/pre_commit/json_syntax.rb) +* [KtLint](lib/overcommit/hook/pre_commit/kt_lint.rb) * [LicenseHeader](lib/overcommit/hook/pre_commit/license_header.rb) * [LineEndings](lib/overcommit/hook/pre_commit/line_endings.rb) * [LocalPathsInGemfile](lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb) @@ -538,15 +557,18 @@ issue](https://github.com/brigade/overcommit/issues/238) for more details. * [Reek](lib/overcommit/hook/pre_commit/reek.rb) * [RuboCop](lib/overcommit/hook/pre_commit/rubo_cop.rb) * [RubyLint](lib/overcommit/hook/pre_commit/ruby_lint.rb) +* [RubySyntax](lib/overcommit/hook/pre_commit/ruby_syntax.rb) +* [SwiftLint](lib/overcommit/hook/pre_commit/swift_lint.rb) * [Scalariform](lib/overcommit/hook/pre_commit/scalariform.rb) * [Scalastyle](lib/overcommit/hook/pre_commit/scalastyle.rb) * [ScssLint](lib/overcommit/hook/pre_commit/scss_lint.rb) * [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) +* [Stylelint](lib/overcommit/hook/pre_commit/stylelint.rb) * [TrailingWhitespace](lib/overcommit/hook/pre_commit/trailing_whitespace.rb) * [TravisLint](lib/overcommit/hook/pre_commit/travis_lint.rb) * [TsLint](lib/overcommit/hook/pre_commit/ts_lint.rb) @@ -567,9 +589,12 @@ but before any objects have been transferred. If a hook fails, the push is aborted. * [Brakeman](lib/overcommit/hook/pre_push/brakeman.rb) +* [FlutterTest](lib/overcommit/hook/pre_push/flutter_test.rb) * [Minitest](lib/overcommit/hook/pre_push/minitest.rb) * [PhpUnit](lib/overcommit/hook/pre_push/php_unit.rb) +* [Pronto](lib/overcommit/hook/pre_push/pronto.rb) * [ProtectedBranches](lib/overcommit/hook/pre_push/protected_branches.rb) +* [PubTest](lib/overcommit/hook/pre_push/pub_test.rb) * [Pytest](lib/overcommit/hook/pre_push/pytest.rb) * [PythonNose](lib/overcommit/hook/pre_push/python_nose.rb) * [RakeTarget](lib/overcommit/hook/pre_push/rake_target.rb) @@ -589,9 +614,9 @@ Out of the box, `overcommit` comes with a set of hooks that enforce a variety of styles and lints. However, some hooks only make sense in the context of a specific repository. -At Brigade, for example, we have a number of simple checks that we run -against our code to catch common errors. For example, since we use -[RSpec](http://rspec.info/), we want to make sure all spec files contain the +For example, you can have a number of simple checks that run +against your code to catch common errors. For example, if you use +[RSpec](http://rspec.info/), you can make sure all spec files contain the line `require 'spec_helper'`. Inside our repository, we can add the file @@ -648,6 +673,12 @@ executes the command and appends any arguments and standard input stream that would have been passed to the regular hook. The hook passes or fails based on the exit status of the command. +The script is executed as if Git were calling the hook directly. If you want +to understand which arguments are passed to the script depending on the type +of hook, see the [git-hooks documentation][GHD]. + +[GHD]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks + ## Security While Overcommit can make managing Git hooks easier and more convenient, @@ -701,9 +732,7 @@ ensure your thoughts, ideas, or code get merged. ## Community All major discussion surrounding Overcommit happens on the -[GitHub issues list](https://github.com/brigade/overcommit/issues). - -You can also follow [@git_overcommit on Twitter](https://twitter.com/git_overcommit). +[GitHub issues list](https://github.com/sds/overcommit/issues). ## Changelog diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index e1873591..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,62 +0,0 @@ -version: "{build}" - -clone_depth: 1 - -environment: - matrix: - - RUBY_FOLDER_VERSION: "22" - - RUBY_FOLDER_VERSION: "22-x64" - - RUBY_FOLDER_VERSION: "23" - - RUBY_FOLDER_VERSION: "23-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-ri --no-rdoc - - gem --version - - where gem - - # Install latest Bundler to work around version issues - - gem install bundler --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.safecrlf false - -test_script: - - bundle exec rspec --tty --backtrace --color - - bundle exec overcommit --sign - - bundle exec overcommit --sign pre-commit - - bundle exec overcommit --run diff --git a/bin/overcommit b/bin/overcommit index f933787a..d54d179f 100755 --- a/bin/overcommit +++ b/bin/overcommit @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Check if Overcommit should invoke a Bundler context for loading gems require 'yaml' @@ -17,15 +18,16 @@ if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil ensure $stderr = old_stderr end - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG - rescue Gem::LoadError => ex + rescue Gem::LoadError => e # Handle case where user is executing overcommit without `bundle exec` and # whose local Gemfile has a gem requirement that does not match a gem # requirement of the installed version of Overcommit. - raise unless ex.message =~ /already activated/i + raise unless e.message =~ /already activated/i + exec('bundle', 'exec', $0, *ARGV) end end diff --git a/config/default.yml b/config/default.yml index ce602d84..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 @@ -222,6 +226,15 @@ PreCommit: install_command: 'gem install chamber' include: *chamber_settings_files + CodeSpellCheck: + enabled: false + description: 'Check if all your code is spell-checked correctly' + command: 'alfonsox' + install_command: 'gem install alfonsox' + include: + - '**/*.rb' + - '**/*.erb' + CoffeeLint: enabled: false description: 'Analyze with coffeelint' @@ -230,6 +243,16 @@ PreCommit: install_command: 'npm install -g coffeelint' include: '**/*.coffee' + CookStyle: + enabled: false + description: 'Analyze with CookStyle' + required_executable: 'cookstyle' + flags: ['--format=emacs', '--force-exclusion', '--display-cop-names'] + install_command: 'gem install cookstyle' + include: + - '**/*.rb' + - '**/*.erb' + Credo: enabled: false description: 'Analyze with credo' @@ -247,6 +270,14 @@ PreCommit: install_command: 'npm install -g csslint' include: '**/*.css' + DartAnalyzer: + enabled: false + description: 'Analyze with dartanalyzer' + required_executable: 'dartanalyzer' + flags: [] + include: + - '**/*.dart' + Dogma: enabled: false description: 'Analyze with dogma' @@ -256,6 +287,13 @@ PreCommit: - '**/*.ex' - '**/*.exs' + ErbLint: + enabled: false + description: 'Analyze with ERB Lint' + required_executable: 'erblint' + install_command: 'bundle install erb_lint' + include: '**/*.html.erb' + EsLint: enabled: false description: 'Analyze with ESLint' @@ -283,6 +321,11 @@ PreCommit: flags: ['-IEHnw'] keywords: ['BROKEN', 'BUG', 'ERROR', 'FIXME', 'HACK', 'NOTE', 'OPTIMIZE', 'REVIEW', 'TODO', 'WTF', 'XXX'] + FileSize: + enabled: false + description: 'Check for oversized files' + size_limit_bytes: 1_000_000 + Flay: enabled: false description: 'Analyze ruby code for structural similarities with Flay' @@ -306,6 +349,30 @@ PreCommit: quiet: true branch_patterns: ['master'] + GinkgoFocus: + enabled: false + description: 'Check for "focused" tests' + required_executable: 'grep' + flags: ['-IEHnw'] + keywords: ['FContext','FDescribe','FIt','FMeasure','FSpecify','FWhen'] + + GoFmt: + enabled: false + description: 'Fix with go fmt' + required_executable: 'go' + command: ['go', 'fmt'] + parallelize: false + include: '**/*.go' + + GolangciLint: + enabled: false + description: 'Analyze with golangci-lint' + required_executable: 'golangci-lint' + install_command: 'go get github.com/golangci/golangci-lint/cmd/golangci-lint' + flags: ['--out-format=line-number', '--print-issued-lines=false'] + command: ['golangci-lint', 'run'] + include: '**/*.go' + GoLint: enabled: false description: 'Analyze with golint' @@ -424,6 +491,13 @@ PreCommit: install_command: 'gem install json' include: '**/*.json' + KtLint: + enabled: false + description: 'Analyze with KtLint' + required_executable: 'ktlint' + flags: [] + include: '**/*.kt' + LicenseFinder: enabled: false description: 'Analyze with LicenseFinder' @@ -455,6 +529,7 @@ PreCommit: enabled: false description: 'Analyze markdown files with mdl' required_executable: 'mdl' + flags: ['--json'] install_command: 'gem install mdl' include: '**/*.md' @@ -465,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' @@ -612,7 +697,7 @@ PreCommit: enabled: false description: 'Analyze with Reek' required_executable: 'reek' - flags: ['--single-line', '--no-color'] + flags: ['--single-line', '--no-color', '--force-exclusion'] install_command: 'gem install reek' include: - '**/*.gemspec' @@ -628,6 +713,11 @@ PreCommit: install_command: 'pip install restructuredtext_lint' include: '**/*.rst' + RSpec: + enabled: false + description: 'Run tests with Rspec' + required_executable: 'rspec' + RuboCop: enabled: false description: 'Analyze with RuboCop' @@ -652,6 +742,19 @@ PreCommit: - '**/*.gemspec' - '**/*.rb' + RubySyntax: + enabled: false + description: 'Check ruby syntax' + required_executable: 'ruby' + command: [ + 'ruby', + '-e', + 'ARGV.each { |applicable_file| ruby_c_output = `ruby -c #{applicable_file}`; puts ruby_c_output unless $?.success? }' + ] + include: + - '**/*.gemspec' + - '**/*.rb' + Scalariform: enabled: false description: 'Check formatting with Scalariform' @@ -696,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' @@ -714,7 +825,7 @@ PreCommit: Stylelint: enabled: false description: 'Check styles with Stylelint' - require_executable: 'stylelint' + required_executable: 'stylelint' flags: ['-f', 'compact'] install_command: 'npm install -g stylelint' include: @@ -722,13 +833,27 @@ PreCommit: - '**/*.css' - '**/*.less' + SwiftLint: + enabled: false + description: 'Analyze with SwiftLint' + required_executable: 'swiftlint' + flags: ['lint', '--strict'] + install_command: 'brew install swiftlint' + include: '**/*.swift' + + TerraformFormat: + enabled: false + description: 'Analyze with Terraform' + required_executable: 'terraform' + flags: ['fmt', '-check=true', '-diff=false'] + include: '**/*.tf' + TsLint: - enabled: false - description: 'Analyze with TSLint' - required_executable: 'tslint' - install_command: 'npm install -g tslint typescript' - flags: ['--t=prose'] - include: '**/*.ts' + enabled: false + description: 'Analyze with TSLint' + required_executable: 'tslint' + install_command: 'npm install -g tslint typescript' + include: '**/*.ts' TrailingWhitespace: enabled: false @@ -770,7 +895,7 @@ PreCommit: description: 'Analyze with W3C HTML validation service' required_library: 'w3c_validators' install_command: 'gem install w3c_validators' - validator_uri: 'http://validator.w3.org/check' + validator_uri: 'https://validator.w3.org/nu' charset: 'utf-8' doctype: 'HTML5' include: @@ -802,7 +927,7 @@ PreCommit: enabled: false description: 'Analyze with YAMLlint' required_executable: 'yamllint' - flags: ['--format=parsable'] + flags: ['--format=parsable', '--strict'] install_command: 'pip install yamllint' include: - '**/*.yaml' @@ -1144,6 +1269,26 @@ PostRewrite: - 'package.json' - 'yarn.lock' +# Hooks that run during the `prepare-commit-msg` hook. +PrepareCommitMsg: + ALL: + requires_files: false + required: false + quiet: false + + ReplaceBranch: + enabled: false + description: 'Prepends the commit message with text based on the branch name' + branch_pattern: '\A(\d+)-(\w+).*\z' + replacement_text: '[#\1]' + skipped_commit_types: + - 'message' # if message is given via `-m`, `-F` + - 'template' # if `-t` is given or `commit.template` is set + - 'commit' # if `-c`, `-C`, or `--amend` is given + - 'merge' # if merging + - 'squash' # if squashing + on_fail: warn + # Hooks that run during `git push`, after remote refs have been updated but # before any objects have been transferred. PrePush: @@ -1166,18 +1311,44 @@ PrePush: flags: ['test'] include: 'src/**/*.rs' + FlutterTest: + enabled: false + description: 'Run flutter test suite' + required_executable: 'flutter' + flags: ['test'] + GitLfs: enabled: false description: 'Upload files tracked by Git LFS' required_executable: 'git-lfs' install_command: 'brew install git-lfs' + GolangciLint: + enabled: false + description: 'Analyze with golangci-lint' + required_executable: 'golangci-lint' + install_command: 'go get github.com/golangci/golangci-lint/cmd/golangci-lint' + flags: ['--out-format=line-number', '--print-issued-lines=false'] + command: ['golangci-lint', 'run'] + + GoTest: + enabled: false + description: 'Run go test suite' + required_executable: 'go' + command: ['go', 'test', './...'] + Minitest: enabled: false description: 'Run Minitest test suite' 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' @@ -1185,12 +1356,25 @@ PrePush: flags: ['--bootstrap', 'vendor/autoload.php', 'tests'] install_command: 'composer require --dev phpunit/phpunit' + Pronto: + enabled: false + description: 'Analyzing with pronto' + required_executable: 'pronto' + install_command: 'gem install pronto' + flags: ['run', '--exit-code'] + ProtectedBranches: enabled: false description: 'Check for illegal pushes to protected branches' destructive_only: true branches: ['master'] + PubTest: + enabled: false + description: 'Run pub test suite' + required_executable: 'pub' + flags: ['run', 'test'] + Pytest: enabled: false description: 'Run pytest test suite' diff --git a/config/starter.yml b/config/starter.yml index 3037a427..c2bc6865 100644 --- a/config/starter.yml +++ b/config/starter.yml @@ -1,6 +1,6 @@ # Use this file to configure the Overcommit hooks you wish to use. This will # extend the default configuration defined in: -# https://github.com/brigade/overcommit/blob/master/config/default.yml +# https://github.com/sds/overcommit/blob/master/config/default.yml # # At the topmost level of this YAML file is a key representing type of hook # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can @@ -8,10 +8,10 @@ # `include`), whether to only display output if it fails (via `quiet`), etc. # # For a complete list of hooks, see: -# https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook +# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook # # For a complete list of options that you can use to customize hooks, see: -# https://github.com/brigade/overcommit#configuration +# https://github.com/sds/overcommit#configuration # # Uncomment the following lines to make the configuration take effect. diff --git a/lib/overcommit.rb b/lib/overcommit.rb index b97d257d..15fef94f 100644 --- a/lib/overcommit.rb +++ b/lib/overcommit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/os' require 'overcommit/constants' require 'overcommit/exceptions' diff --git a/lib/overcommit/cli.rb b/lib/overcommit/cli.rb index 54a3f9b7..dafc545a 100644 --- a/lib/overcommit/cli.rb +++ b/lib/overcommit/cli.rb @@ -6,9 +6,10 @@ module Overcommit # Responsible for parsing command-line options and executing appropriate # application logic based on those options. - class CLI # rubocop:disable ClassLength + class CLI # rubocop:disable Metrics/ClassLength def initialize(arguments, input, logger) @arguments = arguments + @cli_options = {} @input = input @log = logger @options = {} @@ -28,12 +29,14 @@ def run sign when :run_all run_all + when :diff + diff end - rescue Overcommit::Exceptions::ConfigurationSignatureChanged => ex - puts ex + rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e exit 78 # EX_CONFIG - rescue Overcommit::Exceptions::HookContextLoadError => ex - puts ex + rescue Overcommit::Exceptions::HookContextLoadError => e + puts e exit 64 # EX_USAGE end @@ -45,15 +48,15 @@ def parse_arguments @parser = create_option_parser begin - @parser.parse!(@arguments) + @parser.parse!(@arguments, into: @cli_options) # Default action is to install @options[:action] ||= :install # Unconsumed arguments are our targets @options[:targets] = @arguments - rescue OptionParser::InvalidOption => ex - print_help @parser.help, ex + rescue OptionParser::InvalidOption => e + print_help @parser.help, e end end @@ -94,8 +97,14 @@ def add_installation_options(opts) @options[:force] = true end - opts.on('-r', '--run', 'Run pre-commit hook against all git tracked files') do + opts.on('-r [hook]', '--run [hook]', 'Run specified hook against all git tracked files. Defaults to `pre_commit`.') do |arg| # rubocop:disable Layout/LineLength @options[:action] = :run_all + @options[:hook_to_run] = arg ? arg.to_s : 'run-all' + end + + opts.on('--diff [ref]', 'Run pre_commit hooks against the diff between a given ref. Defaults to `main`.') do |arg| # rubocop:disable Layout/LineLength + @options[:action] = :diff + arg end end @@ -123,15 +132,13 @@ def install_or_uninstall end @options[:targets].each do |target| - begin - Installer.new(log).run(target, @options) - rescue Overcommit::Exceptions::InvalidGitRepo => error - log.warning "Invalid repo #{target}: #{error}" - halt 69 # EX_UNAVAILABLE - rescue Overcommit::Exceptions::PreExistingHooks => error - log.warning "Unable to install into #{target}: #{error}" - halt 73 # EX_CANTCREAT - end + Installer.new(log).run(target, @options) + rescue Overcommit::Exceptions::InvalidGitRepo => e + log.warning "Invalid repo #{target}: #{e}" + halt 69 # EX_UNAVAILABLE + rescue Overcommit::Exceptions::PreExistingHooks => e + log.warning "Unable to install into #{target}: #{e}" + halt 73 # EX_CANTCREAT end end @@ -199,7 +206,20 @@ def sign def run_all empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input - context = Overcommit::HookContext.create('run-all', config, @arguments, empty_stdin) + context = Overcommit::HookContext.create(@options[:hook_to_run], config, @arguments, empty_stdin) # rubocop:disable Layout/LineLength + config.apply_environment!(context, ENV) + + printer = Overcommit::Printer.new(config, log, context) + runner = Overcommit::HookRunner.new(config, log, context, printer) + + status = runner.run + + halt(status ? 0 : 65) + end + + def diff + empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input + context = Overcommit::HookContext.create('diff', config, @arguments, empty_stdin, **@cli_options) # rubocop:disable Layout/LineLength config.apply_environment!(context, ENV) printer = Overcommit::Printer.new(config, log, context) diff --git a/lib/overcommit/command_splitter.rb b/lib/overcommit/command_splitter.rb index 3b40ff34..c334b6f9 100644 --- a/lib/overcommit/command_splitter.rb +++ b/lib/overcommit/command_splitter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit # Distributes a list of arguments over multiple invocations of a command. # @@ -105,8 +107,10 @@ def arguments_under_limit(splittable_args, start_index, byte_limit) loop do break if index > splittable_args.length - 1 + total_bytes += splittable_args[index].bytesize break if total_bytes > byte_limit # Not enough room + index += 1 end diff --git a/lib/overcommit/configuration.rb b/lib/overcommit/configuration.rb index 25dba1cf..72086457 100644 --- a/lib/overcommit/configuration.rb +++ b/lib/overcommit/configuration.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'digest' require 'json' module Overcommit # Stores configuration for Overcommit and the hooks it runs. - class Configuration # rubocop:disable ClassLength + class Configuration # rubocop:disable Metrics/ClassLength # Creates a configuration from the given hash. # # @param hash [Hash] loaded YAML config file as a hash @@ -41,7 +43,7 @@ def concurrency @concurrency ||= begin cores = Overcommit::Utils.processor_count - content = @hash.fetch('concurrency', '%d') + content = @hash.fetch('concurrency') { '%d' } if content.is_a?(String) concurrency_expr = content % { processors: cores } @@ -154,7 +156,7 @@ def merge(config) # environment variables. def apply_environment!(hook_context, env) skipped_hooks = "#{env['SKIP']} #{env['SKIP_CHECKS']} #{env['SKIP_HOOKS']}".split(/[:, ]/) - only_hooks = env.fetch('ONLY', '').split(/[:, ]/) + only_hooks = env.fetch('ONLY') { '' }.split(/[:, ]/) hook_type = hook_context.hook_class_name if only_hooks.any? || skipped_hooks.include?('all') || skipped_hooks.include?('ALL') @@ -252,7 +254,7 @@ def update_signature! private def ad_hoc_hook?(hook_context, hook_name) - ad_hoc_conf = @hash.fetch(hook_context.hook_class_name, {}).fetch(hook_name, {}) + ad_hoc_conf = @hash.fetch(hook_context.hook_class_name) { {} }.fetch(hook_name) { {} } # Ad hoc hooks are neither built-in nor have a plugin file written but # still have a `command` specified to be run @@ -282,7 +284,7 @@ def hook_enabled?(hook_context_or_type, hook_name) hook_context_or_type.hook_class_name end - individual_enabled = @hash[hook_type].fetch(hook_name, {})['enabled'] + individual_enabled = @hash[hook_type].fetch(hook_name) { {} }['enabled'] return individual_enabled unless individual_enabled.nil? all_enabled = @hash[hook_type]['ALL']['enabled'] diff --git a/lib/overcommit/configuration_loader.rb b/lib/overcommit/configuration_loader.rb index f4946320..8fd68a01 100644 --- a/lib/overcommit/configuration_loader.rb +++ b/lib/overcommit/configuration_loader.rb @@ -24,13 +24,16 @@ def default_configuration # @option logger [Overcommit::Logger] # @return [Overcommit::Configuration] def load_from_file(file, options = {}) - hash = - if yaml = YAML.load_file(file) - yaml.to_hash - else - {} + # Psych 4 introduced breaking behavior that doesn't support aliases by + # default. Explicitly enable aliases if the option is available. + yaml = + begin + YAML.load_file(file, aliases: true) + rescue ArgumentError + YAML.load_file(file) end + hash = yaml ? yaml.to_hash : {} Overcommit::Configuration.new(hash, options) end end @@ -50,10 +53,14 @@ def initialize(logger, options = {}) # # @return [Overcommit::Configuration] def load_repo_config + overcommit_local_yml = File.join(Overcommit::Utils.repo_root, + Overcommit::LOCAL_CONFIG_FILE_NAME) overcommit_yml = File.join(Overcommit::Utils.repo_root, Overcommit::CONFIG_FILE_NAME) - if File.exist?(overcommit_yml) + if File.exist?(overcommit_local_yml) && File.exist?(overcommit_yml) + load_file(overcommit_yml, overcommit_local_yml) + elsif File.exist?(overcommit_yml) load_file(overcommit_yml) else self.class.default_configuration @@ -61,21 +68,23 @@ 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?) + if @options.fetch(:verify) { config.verify_signatures? } verify_signatures(config) end config rescue Overcommit::Exceptions::ConfigurationSignatureChanged raise - rescue StandardError => error + rescue StandardError => e raise Overcommit::Exceptions::ConfigurationError, - "Unable to load configuration from '#{file}': #{error}", - error.backtrace + "Unable to load configuration from '#{file}': #{e}", + e.backtrace end private diff --git a/lib/overcommit/configuration_validator.rb b/lib/overcommit/configuration_validator.rb index 800893e2..ce1eb317 100644 --- a/lib/overcommit/configuration_validator.rb +++ b/lib/overcommit/configuration_validator.rb @@ -1,4 +1,6 @@ -# rubocop:disable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/LineLength +# frozen_string_literal: true + +# rubocop:disable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength module Overcommit # Validates and normalizes a configuration. class ConfigurationValidator @@ -57,8 +59,8 @@ def check_hook_env(hash) errors = [] Overcommit::Utils.supported_hook_type_classes.each do |hook_type| - hash.fetch(hook_type, {}).each do |hook_name, hook_config| - hook_env = hook_config.fetch('env', {}) + hash.fetch(hook_type) { {} }.each do |hook_name, hook_config| + hook_env = hook_config.fetch('env') { {} } unless hook_env.is_a?(Hash) errors << "#{hook_type}::#{hook_name} has an invalid `env` specified: " \ @@ -82,8 +84,10 @@ def check_hook_env(hash) end if errors.any? - @log.error errors.join("\n") if @log - @log.newline if @log + if @log + @log.error errors.join("\n") + @log.newline + end raise Overcommit::Exceptions::ConfigurationError, 'One or more hooks had an invalid `env` configuration option' end @@ -95,10 +99,10 @@ def check_hook_name_format(hash) errors = [] Overcommit::Utils.supported_hook_type_classes.each do |hook_type| - hash.fetch(hook_type, {}).each_key do |hook_name| + hash.fetch(hook_type) { {} }.each_key do |hook_name| next if hook_name == 'ALL' - unless hook_name =~ /\A[A-Za-z0-9]+\z/ + unless hook_name.match?(/\A[A-Za-z0-9]+\z/) errors << "#{hook_type}::#{hook_name} has an invalid name " \ "#{hook_name}. It must contain only alphanumeric " \ 'characters (no underscores or dashes, etc.)' @@ -107,8 +111,10 @@ def check_hook_name_format(hash) end if errors.any? - @log.error errors.join("\n") if @log - @log.newline if @log + if @log + @log.error errors.join("\n") + @log.newline + end raise Overcommit::Exceptions::ConfigurationError, 'One or more hooks had invalid names' end @@ -122,7 +128,7 @@ def check_for_missing_enabled_option(hash) any_warnings = false Overcommit::Utils.supported_hook_type_classes.each do |hook_type| - hash.fetch(hook_type, {}).each do |hook_name, hook_config| + hash.fetch(hook_type) { {} }.each do |hook_name, hook_config| next if hook_name == 'ALL' if hook_config['enabled'].nil? @@ -143,8 +149,8 @@ def check_for_too_many_processors(config, hash) errors = [] Overcommit::Utils.supported_hook_type_classes.each do |hook_type| - hash.fetch(hook_type, {}).each do |hook_name, hook_config| - processors = hook_config.fetch('processors', 1) + hash.fetch(hook_type) { {} }.each do |hook_name, hook_config| + processors = hook_config.fetch('processors') { 1 } if processors > concurrency errors << "#{hook_type}::#{hook_name} `processors` value " \ "(#{processors}) is larger than the global `concurrency` " \ @@ -154,8 +160,10 @@ def check_for_too_many_processors(config, hash) end if errors.any? - @log.error errors.join("\n") if @log - @log.newline if @log + if @log + @log.error errors.join("\n") + @log.newline + end raise Overcommit::Exceptions::ConfigurationError, 'One or more hooks had invalid `processor` value configured' end @@ -175,4 +183,4 @@ def check_for_verify_plugin_signatures_option(hash) end end end -# rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/LineLength +# rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength diff --git a/lib/overcommit/constants.rb b/lib/overcommit/constants.rb index ff2c9311..e7c19728 100644 --- a/lib/overcommit/constants.rb +++ b/lib/overcommit/constants.rb @@ -3,10 +3,11 @@ # Global application constants. module Overcommit HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze - CONFIG_FILE_NAME = '.overcommit.yml'.freeze + CONFIG_FILE_NAME = '.overcommit.yml' + LOCAL_CONFIG_FILE_NAME = '.local-overcommit.yml' HOOK_DIRECTORY = File.join(HOME, 'lib', 'overcommit', 'hook').freeze - REPO_URL = 'https://github.com/brigade/overcommit'.freeze - BUG_REPORT_URL = "#{REPO_URL}/issues".freeze + REPO_URL = 'https://github.com/sds/overcommit' + BUG_REPORT_URL = "#{REPO_URL}/issues" end diff --git a/lib/overcommit/exceptions.rb b/lib/overcommit/exceptions.rb index 14d4622d..050982fb 100644 --- a/lib/overcommit/exceptions.rb +++ b/lib/overcommit/exceptions.rb @@ -1,50 +1,55 @@ +# frozen_string_literal: true + module Overcommit::Exceptions + # Base error class. + class Error < StandardError; end + # Raised when a {Configuration} could not be loaded from a file. - class ConfigurationError < StandardError; end + class ConfigurationError < Error; end # Raised when the Overcommit configuration file signature has changed. - class ConfigurationSignatureChanged < StandardError; end + class ConfigurationSignatureChanged < Error; end # Raised when trying to read/write to/from the local repo git config fails. - class GitConfigError < StandardError; end + class GitConfigError < Error; end # Raised when there was a problem reading submodule information for a repo. - class GitSubmoduleError < StandardError; end + class GitSubmoduleError < Error; end # Raised when there was a problem reading git revision information with `rev-list`. - class GitRevListError < StandardError; end + class GitRevListError < Error; end # Raised when a {HookContext} is unable to setup the environment before a run. - class HookSetupFailed < StandardError; end + class HookSetupFailed < Error; end # Raised when a {HookContext} is unable to clean the environment after a run. - class HookCleanupFailed < StandardError; end + class HookCleanupFailed < Error; end # Raised when a hook run was cancelled by the user. - class HookCancelled < StandardError; end + class HookCancelled < Error; end # Raised when a hook could not be loaded by a {HookRunner}. - class HookLoadError < StandardError; end + class HookLoadError < Error; end # Raised when a {HookRunner} could not be loaded. - class HookContextLoadError < StandardError; end + class HookContextLoadError < Error; end # Raised when a pipe character is used in the `execute` helper, as this was # likely used in error. - class InvalidCommandArgs < StandardError; end + class InvalidCommandArgs < Error; end # Raised when an installation target is not a valid git repository. - class InvalidGitRepo < StandardError; end + class InvalidGitRepo < Error; end # Raised when a hook was defined incorrectly. - class InvalidHookDefinition < StandardError; end + class InvalidHookDefinition < Error; end # Raised when one or more hook plugin signatures have changed. - class InvalidHookSignature < StandardError; end + class InvalidHookSignature < Error; end # Raised when there is a problem processing output into {Hook::Messages}s. - class MessageProcessingError < StandardError; end + class MessageProcessingError < Error; end # Raised when an installation target already contains non-Overcommit hooks. - class PreExistingHooks < StandardError; end + class PreExistingHooks < Error; end end diff --git a/lib/overcommit/git_config.rb b/lib/overcommit/git_config.rb index 71be91dc..c1243861 100644 --- a/lib/overcommit/git_config.rb +++ b/lib/overcommit/git_config.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/utils' module Overcommit @@ -14,7 +16,8 @@ def comment_character def hooks_path path = `git config --get core.hooksPath`.chomp return File.join(Overcommit::Utils.git_dir, 'hooks') if path.empty? - File.absolute_path(path, Dir.pwd) + + File.expand_path(path, Dir.pwd) end end end diff --git a/lib/overcommit/git_repo.rb b/lib/overcommit/git_repo.rb index 0ed6d3de..1af73e81 100644 --- a/lib/overcommit/git_repo.rb +++ b/lib/overcommit/git_repo.rb @@ -1,4 +1,7 @@ +# frozen_string_literal: true + require 'iniparse' +require 'shellwords' module Overcommit # Provide a set of utilities for certain interactions with `git`. @@ -11,7 +14,7 @@ module GitRepo [^\s]+\s # Ignore old file range \+(\d+)(?:,(\d+))? # Extract range of hunk containing start line and number of lines \s@@.*$ - /x + /x.freeze # Regular expression used to extract information from lines of # `git submodule status` output @@ -19,7 +22,7 @@ module GitRepo ^\s*(?[-+U]?)(?\w+) \s(?[^\s]+?) (?:\s\((?.+)\))?$ - /x + /x.freeze # Struct encapsulating submodule information extracted from the # output of `git submodule status` @@ -106,9 +109,16 @@ def modified_files(options) # @return [Array] list of absolute file paths def list_files(paths = [], options = {}) ref = options[:ref] || 'HEAD' - path_list = paths.empty? ? '' : "\"#{paths.join('" "')}\"" - `git ls-tree --name-only #{ref} #{path_list}`. - split(/\n/). + + result = Overcommit::Utils.execute(%W[git ls-tree --name-only #{ref}], args: paths) + unless result.success? + raise Overcommit::Exceptions::Error, + "Error listing files. EXIT STATUS(es): #{result.statuses}.\n" \ + "STDOUT(s): #{result.stdouts}.\n" \ + "STDERR(s): #{result.stderrs}." + end + + result.stdout.split(/\n/). map { |relative_file| File.expand_path(relative_file) }. reject { |file| File.directory?(file) } # Exclude submodule directories end @@ -252,9 +262,9 @@ def submodules(options = {}) end modules - rescue IniParse::IniParseError => ex + rescue IniParse::IniParseError => e raise Overcommit::Exceptions::GitSubmoduleError, - "Unable to read submodule information from #{ref}:.gitmodules file: #{ex.message}" + "Unable to read submodule information from #{ref}:.gitmodules file: #{e.message}" end # Returns the names of all branches containing the given commit. diff --git a/lib/overcommit/git_version.rb b/lib/overcommit/git_version.rb index ab5021ac..ac4044c3 100644 --- a/lib/overcommit/git_version.rb +++ b/lib/overcommit/git_version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Returns the version of the available git binary. # # This is intended to be used to conveniently execute code based on a specific diff --git a/lib/overcommit/hook/base.rb b/lib/overcommit/hook/base.rb index b1607c08..3c2eaeb3 100644 --- a/lib/overcommit/hook/base.rb +++ b/lib/overcommit/hook/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' require 'overcommit/message_processor' @@ -42,7 +44,7 @@ def run_and_transform if output = check_for_requirements status = :fail else - result = Overcommit::Utils.with_environment(@config.fetch('env', {})) { run } + result = Overcommit::Utils.with_environment(@config.fetch('env') { {} }) { run } status, output = process_hook_return_value(result) end @@ -66,7 +68,7 @@ def parallelize? end def processors - @config.fetch('processors', 1) + @config.fetch('processors') { 1 } end def quiet? @@ -77,12 +79,18 @@ def enabled? @config['enabled'] != false end + def excluded? + exclude_branches.any? { |p| File.fnmatch(p, current_branch) } + end + def skip? - @config['skip'] + @config['skip'] || + (@config['skip_if'] ? execute(@config['skip_if']).success? : false) end def run? enabled? && + !excluded? && !(@config['requires_files'] && applicable_files.empty?) end @@ -202,11 +210,8 @@ def check_for_requirements def check_for_executable return unless required_executable && !in_path?(required_executable) - output = "'#{required_executable}' is not installed, not in your PATH, " \ - 'or does not have execute permissions' - output << install_command_prompt - - output + "'#{required_executable}' is not installed, not in your PATH, " \ + "or does not have execute permissions#{install_command_prompt}" end def install_command_prompt @@ -223,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? @@ -270,12 +273,20 @@ def process_hook_return_value(hook_return_value) def transform_status(status) case status when :fail - @config.fetch('on_fail', :fail).to_sym + @config.fetch('on_fail') { :fail }.to_sym when :warn - @config.fetch('on_warn', :warn).to_sym + @config.fetch('on_warn') { :warn }.to_sym else status end end + + def exclude_branches + @config['exclude_branches'] || [] + end + + def current_branch + @current_branch ||= Overcommit::GitRepo.current_branch + end end end diff --git a/lib/overcommit/hook/commit_msg/base.rb b/lib/overcommit/hook/commit_msg/base.rb index 24241640..f99f53fc 100644 --- a/lib/overcommit/hook/commit_msg/base.rb +++ b/lib/overcommit/hook/commit_msg/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::CommitMsg @@ -7,6 +9,6 @@ class Base < Overcommit::Hook::Base def_delegators :@context, :empty_message?, :commit_message, :update_commit_message, :commit_message_lines, - :commit_message_file + :commit_message_file, :modified_lines_in_file end end diff --git a/lib/overcommit/hook/commit_msg/capitalized_subject.rb b/lib/overcommit/hook/commit_msg/capitalized_subject.rb index 081bafcc..1b783bc1 100644 --- a/lib/overcommit/hook/commit_msg/capitalized_subject.rb +++ b/lib/overcommit/hook/commit_msg/capitalized_subject.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures commit message subject lines start with a capital letter. class CapitalizedSubject < Base diff --git a/lib/overcommit/hook/commit_msg/empty_message.rb b/lib/overcommit/hook/commit_msg/empty_message.rb index b0cd0310..a0f3bfc7 100644 --- a/lib/overcommit/hook/commit_msg/empty_message.rb +++ b/lib/overcommit/hook/commit_msg/empty_message.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Checks that the commit message is not empty class EmptyMessage < Base diff --git a/lib/overcommit/hook/commit_msg/gerrit_change_id.rb b/lib/overcommit/hook/commit_msg/gerrit_change_id.rb index 567b6c0b..f8514849 100644 --- a/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +++ b/lib/overcommit/hook/commit_msg/gerrit_change_id.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures a Gerrit Change-Id line is included in the commit message. # diff --git a/lib/overcommit/hook/commit_msg/hard_tabs.rb b/lib/overcommit/hook/commit_msg/hard_tabs.rb index dcd0356e..e8ff5aeb 100644 --- a/lib/overcommit/hook/commit_msg/hard_tabs.rb +++ b/lib/overcommit/hook/commit_msg/hard_tabs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Checks for hard tabs in commit messages. class HardTabs < Base diff --git a/lib/overcommit/hook/commit_msg/message_format.rb b/lib/overcommit/hook/commit_msg/message_format.rb index dc70e25f..f5093c24 100644 --- a/lib/overcommit/hook/commit_msg/message_format.rb +++ b/lib/overcommit/hook/commit_msg/message_format.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures the commit message follows a specific format. class MessageFormat < Base @@ -17,7 +19,7 @@ def validate_pattern(message) expected_pattern_message = config['expected_pattern_message'] sample_message = config['sample_message'] - unless message =~ /#{pattern}/ + unless message.match?(/#{pattern}/m) [ 'Commit message pattern mismatch.', "Expected : #{expected_pattern_message}", diff --git a/lib/overcommit/hook/commit_msg/russian_novel.rb b/lib/overcommit/hook/commit_msg/russian_novel.rb index 35c89402..26ec7ff5 100644 --- a/lib/overcommit/hook/commit_msg/russian_novel.rb +++ b/lib/overcommit/hook/commit_msg/russian_novel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Checks for long commit messages (not good or bad--just fun to point out) class RussianNovel < Base diff --git a/lib/overcommit/hook/commit_msg/single_line_subject.rb b/lib/overcommit/hook/commit_msg/single_line_subject.rb index 8e939536..e374d541 100644 --- a/lib/overcommit/hook/commit_msg/single_line_subject.rb +++ b/lib/overcommit/hook/commit_msg/single_line_subject.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures commit message subject lines are followed by a blank line. class SingleLineSubject < Base diff --git a/lib/overcommit/hook/commit_msg/spell_check.rb b/lib/overcommit/hook/commit_msg/spell_check.rb index e021c766..ec6c4d4a 100644 --- a/lib/overcommit/hook/commit_msg/spell_check.rb +++ b/lib/overcommit/hook/commit_msg/spell_check.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tempfile' module Overcommit::Hook::CommitMsg @@ -7,7 +9,7 @@ module Overcommit::Hook::CommitMsg class SpellCheck < Base Misspelling = Struct.new(:word, :suggestions) - MISSPELLING_REGEX = /^[&#]\s(?\w+)(?:.+?:\s(?.*))?/ + MISSPELLING_REGEX = /^[&#]\s(?\w+)(?:.+?:\s(?.*))?/.freeze def run result = execute(command + [uncommented_commit_msg_file]) diff --git a/lib/overcommit/hook/commit_msg/text_width.rb b/lib/overcommit/hook/commit_msg/text_width.rb index d057c8cc..52de3bd7 100644 --- a/lib/overcommit/hook/commit_msg/text_width.rb +++ b/lib/overcommit/hook/commit_msg/text_width.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures the number of columns the subject and commit message lines occupy is # under the preferred limits. @@ -30,7 +32,7 @@ def find_errors_in_subject(subject) min_subject_width = config['min_subject_width'] if subject.length < min_subject_width @errors << "Commit message subject must be >= #{min_subject_width} characters" - return + nil end end @@ -39,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/commit_msg/trailing_period.rb b/lib/overcommit/hook/commit_msg/trailing_period.rb index ea8725b8..d3c8b276 100644 --- a/lib/overcommit/hook/commit_msg/trailing_period.rb +++ b/lib/overcommit/hook/commit_msg/trailing_period.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::CommitMsg # Ensures commit message subject lines do not have a trailing period class TrailingPeriod < Base diff --git a/lib/overcommit/hook/post_checkout/base.rb b/lib/overcommit/hook/post_checkout/base.rb index d83bb357..78798b9e 100644 --- a/lib/overcommit/hook/post_checkout/base.rb +++ b/lib/overcommit/hook/post_checkout/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::PostCheckout @@ -14,6 +16,7 @@ def skip_file_checkout? def enabled? return false if file_checkout? && skip_file_checkout? + super end end diff --git a/lib/overcommit/hook/post_checkout/bower_install.rb b/lib/overcommit/hook/post_checkout/bower_install.rb index cc93e074..9280baea 100644 --- a/lib/overcommit/hook/post_checkout/bower_install.rb +++ b/lib/overcommit/hook/post_checkout/bower_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bower_install' module Overcommit::Hook::PostCheckout # Runs `bower install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BowerInstall} + # @see Overcommit::Hook::Shared::BowerInstall class BowerInstall < Base include Overcommit::Hook::Shared::BowerInstall end diff --git a/lib/overcommit/hook/post_checkout/bundle_install.rb b/lib/overcommit/hook/post_checkout/bundle_install.rb index f6cfde78..2641ded6 100644 --- a/lib/overcommit/hook/post_checkout/bundle_install.rb +++ b/lib/overcommit/hook/post_checkout/bundle_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bundle_install' module Overcommit::Hook::PostCheckout # Runs `bundle install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BundleInstall} + # @see Overcommit::Hook::Shared::BundleInstall class BundleInstall < Base include Overcommit::Hook::Shared::BundleInstall end diff --git a/lib/overcommit/hook/post_checkout/composer_install.rb b/lib/overcommit/hook/post_checkout/composer_install.rb index 28573343..d5b9abed 100644 --- a/lib/overcommit/hook/post_checkout/composer_install.rb +++ b/lib/overcommit/hook/post_checkout/composer_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/composer_install' module Overcommit::Hook::PostCheckout # Runs `composer install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::ComposerInstall} + # @see Overcommit::Hook::Shared::ComposerInstall class ComposerInstall < Base include Overcommit::Hook::Shared::ComposerInstall end diff --git a/lib/overcommit/hook/post_checkout/index_tags.rb b/lib/overcommit/hook/post_checkout/index_tags.rb index 5d7784b7..eb3456ba 100644 --- a/lib/overcommit/hook/post_checkout/index_tags.rb +++ b/lib/overcommit/hook/post_checkout/index_tags.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/index_tags' module Overcommit::Hook::PostCheckout # Updates ctags index for all source code in the repository. # - # @see {Overcommit::Hook::Shared::IndexTags} + # @see Overcommit::Hook::Shared::IndexTags class IndexTags < Base include Overcommit::Hook::Shared::IndexTags end diff --git a/lib/overcommit/hook/post_checkout/npm_install.rb b/lib/overcommit/hook/post_checkout/npm_install.rb index e726469f..66b1d425 100644 --- a/lib/overcommit/hook/post_checkout/npm_install.rb +++ b/lib/overcommit/hook/post_checkout/npm_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/npm_install' module Overcommit::Hook::PostCheckout # Runs `npm install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::NpmInstall} + # @see Overcommit::Hook::Shared::NpmInstall class NpmInstall < Base include Overcommit::Hook::Shared::NpmInstall end diff --git a/lib/overcommit/hook/post_checkout/submodule_status.rb b/lib/overcommit/hook/post_checkout/submodule_status.rb index 59f16a73..139301fb 100644 --- a/lib/overcommit/hook/post_checkout/submodule_status.rb +++ b/lib/overcommit/hook/post_checkout/submodule_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/submodule_status' module Overcommit::Hook::PostCheckout diff --git a/lib/overcommit/hook/post_checkout/yarn_install.rb b/lib/overcommit/hook/post_checkout/yarn_install.rb index 48087612..94a59e63 100644 --- a/lib/overcommit/hook/post_checkout/yarn_install.rb +++ b/lib/overcommit/hook/post_checkout/yarn_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/yarn_install' module Overcommit::Hook::PostCheckout # Runs `yarn install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::YarnInstall} + # @see Overcommit::Hook::Shared::YarnInstall class YarnInstall < Base include Overcommit::Hook::Shared::YarnInstall end diff --git a/lib/overcommit/hook/post_commit/base.rb b/lib/overcommit/hook/post_commit/base.rb index 3d22c91c..9acd77d3 100644 --- a/lib/overcommit/hook/post_commit/base.rb +++ b/lib/overcommit/hook/post_commit/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::PostCommit diff --git a/lib/overcommit/hook/post_commit/bower_install.rb b/lib/overcommit/hook/post_commit/bower_install.rb index 72cc1cbc..e35ae231 100644 --- a/lib/overcommit/hook/post_commit/bower_install.rb +++ b/lib/overcommit/hook/post_commit/bower_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bower_install' module Overcommit::Hook::PostCommit # Runs `bower install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BowerInstall} + # @see Overcommit::Hook::Shared::BowerInstall class BowerInstall < Base include Overcommit::Hook::Shared::BowerInstall end diff --git a/lib/overcommit/hook/post_commit/bundle_install.rb b/lib/overcommit/hook/post_commit/bundle_install.rb index 3505023e..1c4abb32 100644 --- a/lib/overcommit/hook/post_commit/bundle_install.rb +++ b/lib/overcommit/hook/post_commit/bundle_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bundle_install' module Overcommit::Hook::PostCommit # Runs `bundle install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BundleInstall} + # @see Overcommit::Hook::Shared::BundleInstall class BundleInstall < Base include Overcommit::Hook::Shared::BundleInstall end diff --git a/lib/overcommit/hook/post_commit/commitplease.rb b/lib/overcommit/hook/post_commit/commitplease.rb index ccc404f6..3674dc12 100644 --- a/lib/overcommit/hook/post_commit/commitplease.rb +++ b/lib/overcommit/hook/post_commit/commitplease.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PostCommit # Check that a commit message conforms to a certain style # diff --git a/lib/overcommit/hook/post_commit/composer_install.rb b/lib/overcommit/hook/post_commit/composer_install.rb index 218c8140..adbdc33a 100644 --- a/lib/overcommit/hook/post_commit/composer_install.rb +++ b/lib/overcommit/hook/post_commit/composer_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/composer_install' module Overcommit::Hook::PostCommit # Runs `composer install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::ComposerInstall} + # @see Overcommit::Hook::Shared::ComposerInstall class ComposerInstall < Base include Overcommit::Hook::Shared::ComposerInstall end diff --git a/lib/overcommit/hook/post_commit/git_guilt.rb b/lib/overcommit/hook/post_commit/git_guilt.rb index 874f925c..b4f1ead8 100644 --- a/lib/overcommit/hook/post_commit/git_guilt.rb +++ b/lib/overcommit/hook/post_commit/git_guilt.rb @@ -1,14 +1,17 @@ +# frozen_string_literal: true + module Overcommit::Hook::PostCommit # Calculates the change in blame since the last revision. # # @see https://www.npmjs.com/package/git-guilt class GitGuilt < Base - PLUS_MINUS_REGEX = /^(.*?)(?:(\++)|(-+))$/ + PLUS_MINUS_REGEX = /^(.*?)(?:(\++)|(-+))$/.freeze GREEN = 32 RED = 31 def run return :pass if initial_commit? + result = execute(command) return :fail, result.stderr unless result.success? diff --git a/lib/overcommit/hook/post_commit/index_tags.rb b/lib/overcommit/hook/post_commit/index_tags.rb index 802411cd..175bbf6f 100644 --- a/lib/overcommit/hook/post_commit/index_tags.rb +++ b/lib/overcommit/hook/post_commit/index_tags.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/index_tags' module Overcommit::Hook::PostCommit # Updates ctags index for all source code in the repository. # - # @see {Overcommit::Hook::Shared::IndexTags} + # @see Overcommit::Hook::Shared::IndexTags class IndexTags < Base include Overcommit::Hook::Shared::IndexTags end diff --git a/lib/overcommit/hook/post_commit/npm_install.rb b/lib/overcommit/hook/post_commit/npm_install.rb index 9fd61461..df2f5af3 100644 --- a/lib/overcommit/hook/post_commit/npm_install.rb +++ b/lib/overcommit/hook/post_commit/npm_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/npm_install' module Overcommit::Hook::PostCommit # Runs `npm install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::NpmInstall} + # @see Overcommit::Hook::Shared::NpmInstall class NpmInstall < Base include Overcommit::Hook::Shared::NpmInstall end diff --git a/lib/overcommit/hook/post_commit/submodule_status.rb b/lib/overcommit/hook/post_commit/submodule_status.rb index 5921a19f..7cb313dd 100644 --- a/lib/overcommit/hook/post_commit/submodule_status.rb +++ b/lib/overcommit/hook/post_commit/submodule_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/submodule_status' module Overcommit::Hook::PostCommit diff --git a/lib/overcommit/hook/post_commit/yarn_install.rb b/lib/overcommit/hook/post_commit/yarn_install.rb index 184277f5..69ae7670 100644 --- a/lib/overcommit/hook/post_commit/yarn_install.rb +++ b/lib/overcommit/hook/post_commit/yarn_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/yarn_install' module Overcommit::Hook::PostCommit # Runs `yarn install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::YarnInstall} + # @see Overcommit::Hook::Shared::YarnInstall class YarnInstall < Base include Overcommit::Hook::Shared::YarnInstall end diff --git a/lib/overcommit/hook/post_merge/base.rb b/lib/overcommit/hook/post_merge/base.rb index 32cc2638..e4d3620f 100644 --- a/lib/overcommit/hook/post_merge/base.rb +++ b/lib/overcommit/hook/post_merge/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::PostMerge diff --git a/lib/overcommit/hook/post_merge/bower_install.rb b/lib/overcommit/hook/post_merge/bower_install.rb index 07d9b601..982e8152 100644 --- a/lib/overcommit/hook/post_merge/bower_install.rb +++ b/lib/overcommit/hook/post_merge/bower_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bower_install' module Overcommit::Hook::PostMerge # Runs `bower install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BowerInstall} + # @see Overcommit::Hook::Shared::BowerInstall class BowerInstall < Base include Overcommit::Hook::Shared::BowerInstall end diff --git a/lib/overcommit/hook/post_merge/bundle_install.rb b/lib/overcommit/hook/post_merge/bundle_install.rb index 5557428b..b9b147bd 100644 --- a/lib/overcommit/hook/post_merge/bundle_install.rb +++ b/lib/overcommit/hook/post_merge/bundle_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bundle_install' module Overcommit::Hook::PostMerge # Runs `bundle install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BundleInstall} + # @see Overcommit::Hook::Shared::BundleInstall class BundleInstall < Base include Overcommit::Hook::Shared::BundleInstall end diff --git a/lib/overcommit/hook/post_merge/composer_install.rb b/lib/overcommit/hook/post_merge/composer_install.rb index ec17a5a8..80d8fb94 100644 --- a/lib/overcommit/hook/post_merge/composer_install.rb +++ b/lib/overcommit/hook/post_merge/composer_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/composer_install' module Overcommit::Hook::PostMerge # Runs `composer install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::ComposerInstall} + # @see Overcommit::Hook::Shared::ComposerInstall class ComposerInstall < Base include Overcommit::Hook::Shared::ComposerInstall end diff --git a/lib/overcommit/hook/post_merge/index_tags.rb b/lib/overcommit/hook/post_merge/index_tags.rb index 19133336..a7da71a1 100644 --- a/lib/overcommit/hook/post_merge/index_tags.rb +++ b/lib/overcommit/hook/post_merge/index_tags.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/index_tags' module Overcommit::Hook::PostMerge # Updates ctags index for all source code in the repository. # - # @see {Overcommit::Hook::Shared::IndexTags} + # @see Overcommit::Hook::Shared::IndexTags class IndexTags < Base include Overcommit::Hook::Shared::IndexTags end diff --git a/lib/overcommit/hook/post_merge/npm_install.rb b/lib/overcommit/hook/post_merge/npm_install.rb index 72420ec5..099e838c 100644 --- a/lib/overcommit/hook/post_merge/npm_install.rb +++ b/lib/overcommit/hook/post_merge/npm_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/npm_install' module Overcommit::Hook::PostMerge # Runs `npm install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::NpmInstall} + # @see Overcommit::Hook::Shared::NpmInstall class NpmInstall < Base include Overcommit::Hook::Shared::NpmInstall end diff --git a/lib/overcommit/hook/post_merge/submodule_status.rb b/lib/overcommit/hook/post_merge/submodule_status.rb index d33bef71..beced8f1 100644 --- a/lib/overcommit/hook/post_merge/submodule_status.rb +++ b/lib/overcommit/hook/post_merge/submodule_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/submodule_status' module Overcommit::Hook::PostMerge diff --git a/lib/overcommit/hook/post_merge/yarn_install.rb b/lib/overcommit/hook/post_merge/yarn_install.rb index 39d5c33b..d47199db 100644 --- a/lib/overcommit/hook/post_merge/yarn_install.rb +++ b/lib/overcommit/hook/post_merge/yarn_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/yarn_install' module Overcommit::Hook::PostMerge # Runs `yarn install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::YarnInstall} + # @see Overcommit::Hook::Shared::YarnInstall class YarnInstall < Base include Overcommit::Hook::Shared::YarnInstall end diff --git a/lib/overcommit/hook/post_rewrite/base.rb b/lib/overcommit/hook/post_rewrite/base.rb index 9dfcaf27..39f4fb6f 100644 --- a/lib/overcommit/hook/post_rewrite/base.rb +++ b/lib/overcommit/hook/post_rewrite/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::PostRewrite diff --git a/lib/overcommit/hook/post_rewrite/bower_install.rb b/lib/overcommit/hook/post_rewrite/bower_install.rb index c755aae1..9974e629 100644 --- a/lib/overcommit/hook/post_rewrite/bower_install.rb +++ b/lib/overcommit/hook/post_rewrite/bower_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bower_install' module Overcommit::Hook::PostRewrite # Runs `bower install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BowerInstall} + # @see Overcommit::Hook::Shared::BowerInstall class BowerInstall < Base include Overcommit::Hook::Shared::BowerInstall end diff --git a/lib/overcommit/hook/post_rewrite/bundle_install.rb b/lib/overcommit/hook/post_rewrite/bundle_install.rb index d3ae92e1..b6409338 100644 --- a/lib/overcommit/hook/post_rewrite/bundle_install.rb +++ b/lib/overcommit/hook/post_rewrite/bundle_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/bundle_install' module Overcommit::Hook::PostRewrite # Runs `bundle install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::BundleInstall} + # @see Overcommit::Hook::Shared::BundleInstall class BundleInstall < Base include Overcommit::Hook::Shared::BundleInstall end diff --git a/lib/overcommit/hook/post_rewrite/composer_install.rb b/lib/overcommit/hook/post_rewrite/composer_install.rb index 354d6e50..40a23f54 100644 --- a/lib/overcommit/hook/post_rewrite/composer_install.rb +++ b/lib/overcommit/hook/post_rewrite/composer_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/composer_install' module Overcommit::Hook::PostRewrite # Runs `composer install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::ComposerInstall} + # @see Overcommit::Hook::Shared::ComposerInstall class ComposerInstall < Base include Overcommit::Hook::Shared::ComposerInstall end diff --git a/lib/overcommit/hook/post_rewrite/index_tags.rb b/lib/overcommit/hook/post_rewrite/index_tags.rb index 51c4a558..a3a54550 100644 --- a/lib/overcommit/hook/post_rewrite/index_tags.rb +++ b/lib/overcommit/hook/post_rewrite/index_tags.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/index_tags' module Overcommit::Hook::PostRewrite # Updates ctags index for all source code in the repository. # - # @see {Overcommit::Hook::Shared::IndexTags} + # @see Overcommit::Hook::Shared::IndexTags class IndexTags < Base include Overcommit::Hook::Shared::IndexTags diff --git a/lib/overcommit/hook/post_rewrite/npm_install.rb b/lib/overcommit/hook/post_rewrite/npm_install.rb index 382ace9d..3c94d365 100644 --- a/lib/overcommit/hook/post_rewrite/npm_install.rb +++ b/lib/overcommit/hook/post_rewrite/npm_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/npm_install' module Overcommit::Hook::PostRewrite # Runs `npm install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::NpmInstall} + # @see Overcommit::Hook::Shared::NpmInstall class NpmInstall < Base include Overcommit::Hook::Shared::NpmInstall end diff --git a/lib/overcommit/hook/post_rewrite/submodule_status.rb b/lib/overcommit/hook/post_rewrite/submodule_status.rb index 06c77b24..7fd25b7a 100644 --- a/lib/overcommit/hook/post_rewrite/submodule_status.rb +++ b/lib/overcommit/hook/post_rewrite/submodule_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/submodule_status' module Overcommit::Hook::PostRewrite diff --git a/lib/overcommit/hook/post_rewrite/yarn_install.rb b/lib/overcommit/hook/post_rewrite/yarn_install.rb index 37c61ded..fdb64d3e 100644 --- a/lib/overcommit/hook/post_rewrite/yarn_install.rb +++ b/lib/overcommit/hook/post_rewrite/yarn_install.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/yarn_install' module Overcommit::Hook::PostRewrite # Runs `yarn install` when a change is detected in the repository's # dependencies. # - # @see {Overcommit::Hook::Shared::YarnInstall} + # @see Overcommit::Hook::Shared::YarnInstall class YarnInstall < Base include Overcommit::Hook::Shared::YarnInstall end diff --git a/lib/overcommit/hook/pre_commit/author_email.rb b/lib/overcommit/hook/pre_commit/author_email.rb index e5aca1b1..37fbb478 100644 --- a/lib/overcommit/hook/pre_commit/author_email.rb +++ b/lib/overcommit/hook/pre_commit/author_email.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks the format of an author's email address. class AuthorEmail < Base @@ -10,7 +12,7 @@ def run result.stdout.chomp end - unless email =~ /#{config['pattern']}/ + unless email.match?(/#{config['pattern']}/) return :fail, "Author has an invalid email address: '#{email}'\n" \ 'Set your email with ' \ diff --git a/lib/overcommit/hook/pre_commit/author_name.rb b/lib/overcommit/hook/pre_commit/author_name.rb index 79f899f5..fc89792a 100644 --- a/lib/overcommit/hook/pre_commit/author_name.rb +++ b/lib/overcommit/hook/pre_commit/author_name.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Ensures that a commit author has a name with at least first and last names. class AuthorName < Base @@ -10,9 +12,9 @@ def run result.stdout.chomp end - unless name.split(' ').count >= 2 + if name.empty? return :fail, - "Author must have at least first and last name, but was: #{name}.\n" \ + "Author name must be non-0 in length.\n" \ 'Set your name with `git config --global user.name "Your Name"` ' \ 'or via the GIT_AUTHOR_NAME environment variable' end diff --git a/lib/overcommit/hook/pre_commit/base.rb b/lib/overcommit/hook/pre_commit/base.rb index e1a7b3d5..4e944db5 100644 --- a/lib/overcommit/hook/pre_commit/base.rb +++ b/lib/overcommit/hook/pre_commit/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' require 'overcommit/utils/messages_utils' diff --git a/lib/overcommit/hook/pre_commit/berksfile_check.rb b/lib/overcommit/hook/pre_commit/berksfile_check.rb index 80049e2d..a8040976 100644 --- a/lib/overcommit/hook/pre_commit/berksfile_check.rb +++ b/lib/overcommit/hook/pre_commit/berksfile_check.rb @@ -6,7 +6,7 @@ module Overcommit::Hook::PreCommit # # @see http://berkshelf.com/ class BerksfileCheck < Base - LOCK_FILE = 'Berksfile.lock'.freeze + LOCK_FILE = 'Berksfile.lock' def run # Ignore if Berksfile.lock is not tracked by git diff --git a/lib/overcommit/hook/pre_commit/broken_symlinks.rb b/lib/overcommit/hook/pre_commit/broken_symlinks.rb index c2a62c8e..50dbbee2 100644 --- a/lib/overcommit/hook/pre_commit/broken_symlinks.rb +++ b/lib/overcommit/hook/pre_commit/broken_symlinks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for broken symlinks. class BrokenSymlinks < Base diff --git a/lib/overcommit/hook/pre_commit/bundle_audit.rb b/lib/overcommit/hook/pre_commit/bundle_audit.rb index 2840a536..1f9f24c7 100644 --- a/lib/overcommit/hook/pre_commit/bundle_audit.rb +++ b/lib/overcommit/hook/pre_commit/bundle_audit.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for vulnerable versions of gems in Gemfile.lock. # # @see https://github.com/rubysec/bundler-audit class BundleAudit < Base - LOCK_FILE = 'Gemfile.lock'.freeze + LOCK_FILE = 'Gemfile.lock' def run # Ignore if Gemfile.lock is not tracked by git @@ -15,7 +17,7 @@ def run if result.success? :pass else - return [:warn, result.stdout] + [:warn, result.stdout] end end end diff --git a/lib/overcommit/hook/pre_commit/bundle_check.rb b/lib/overcommit/hook/pre_commit/bundle_check.rb index d2ddd406..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'.freeze + LOCK_FILE = File.basename(ENV['BUNDLE_GEMFILE'] || 'Gemfile') + '.lock' def run # Ignore if Gemfile.lock is not tracked by git @@ -22,7 +22,8 @@ def run new_lockfile = File.read(LOCK_FILE) if File.exist?(LOCK_FILE) if previous_lockfile != new_lockfile - return :fail, "#{LOCK_FILE} is not up-to-date -- run `#{command.join(' ')}`" + return :fail, "#{LOCK_FILE} is not up-to-date -- run \ + `#{command.join(' ')}` or add the Gemfile and/or Gemfile.lock".squeeze end :pass diff --git a/lib/overcommit/hook/pre_commit/bundle_outdated.rb b/lib/overcommit/hook/pre_commit/bundle_outdated.rb index 576430ce..afa36c23 100644 --- a/lib/overcommit/hook/pre_commit/bundle_outdated.rb +++ b/lib/overcommit/hook/pre_commit/bundle_outdated.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Check if any gems in Gemfile.lock have newer versions, unless the # Gemfile.lock is ignored by Git. # # @see http://bundler.io/bundle_outdated.html class BundleOutdated < Base - LOCK_FILE = 'Gemfile.lock'.freeze + LOCK_FILE = 'Gemfile.lock' def run # Ignore if Gemfile.lock is not tracked by git diff --git a/lib/overcommit/hook/pre_commit/case_conflicts.rb b/lib/overcommit/hook/pre_commit/case_conflicts.rb index 84e7af95..ee6e4161 100644 --- a/lib/overcommit/hook/pre_commit/case_conflicts.rb +++ b/lib/overcommit/hook/pre_commit/case_conflicts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for files that would conflict in case-insensitive filesystems # Adapted from https://github.com/pre-commit/pre-commit-hooks diff --git a/lib/overcommit/hook/pre_commit/chamber_compare.rb b/lib/overcommit/hook/pre_commit/chamber_compare.rb index b5e5791e..b3e0366c 100644 --- a/lib/overcommit/hook/pre_commit/chamber_compare.rb +++ b/lib/overcommit/hook/pre_commit/chamber_compare.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `chamber compare` against a configurable set of namespaces. # @@ -12,11 +14,11 @@ def run next unless second result = execute( - command, - args: [ - "--first=#{first.join(' ')}", - "--second=#{second.join(' ')}", - ], + command, + args: [ + "--first=#{first.join(' ')}", + "--second=#{second.join(' ')}", + ], ) unless result.stdout.empty? diff --git a/lib/overcommit/hook/pre_commit/chamber_security.rb b/lib/overcommit/hook/pre_commit/chamber_security.rb index b70148b3..c639fbf8 100644 --- a/lib/overcommit/hook/pre_commit/chamber_security.rb +++ b/lib/overcommit/hook/pre_commit/chamber_security.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `chamber secure` against any modified Chamber settings files. # @@ -7,6 +9,7 @@ def run result = execute(command, args: applicable_files) return :pass if result.stdout.empty? + [:fail, "These settings appear to need to be secured but were not: #{result.stdout}"] end end diff --git a/lib/overcommit/hook/pre_commit/chamber_verification.rb b/lib/overcommit/hook/pre_commit/chamber_verification.rb index 457439ad..8581c063 100644 --- a/lib/overcommit/hook/pre_commit/chamber_verification.rb +++ b/lib/overcommit/hook/pre_commit/chamber_verification.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `chamber sign --verify`. # @@ -5,7 +7,7 @@ module Overcommit::Hook::PreCommit # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity class ChamberVerification < Base def run - approver_name = config.fetch('approver_name', 'your approver') + approver_name = config.fetch('approver_name') { 'your approver' } approver_email = config['approver_email'] ? " (#{config['approver_email']})" : nil result = execute(command) diff --git a/lib/overcommit/hook/pre_commit/code_spell_check.rb b/lib/overcommit/hook/pre_commit/code_spell_check.rb new file mode 100644 index 00000000..a1132797 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/code_spell_check.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `alfonsox` spell-checking tool against any modified code file. + # + # @see https://github.com/diegojromerolopez/alfonsox + class CodeSpellCheck < Base + def run + # Create default file config if it does not exist + + # Run spell-check + result = execute(command, args: applicable_files) + return :pass if result.success? + + spellchecking_errors = result.stderr.split("\n") + spellchecking_errors.pop + + error_messages(spellchecking_errors) + end + + private + + # Create the error messages + def error_messages(spellchecking_errors) + messages = [] + spellchecking_errors.each do |spellchecking_error_i| + error_location, word = spellchecking_error_i.split(' ') + error_file_path, line = error_location.split(':') + messages << Overcommit::Hook::Message.new( + :error, error_file_path, line, "#{error_location}: #{word}" + ) + end + messages + end + end +end diff --git a/lib/overcommit/hook/pre_commit/coffee_lint.rb b/lib/overcommit/hook/pre_commit/coffee_lint.rb index d8057ff6..57c80a58 100644 --- a/lib/overcommit/hook/pre_commit/coffee_lint.rb +++ b/lib/overcommit/hook/pre_commit/coffee_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `coffeelint` against any modified CoffeeScript files. # @@ -8,7 +10,7 @@ class CoffeeLint < Base ,(?\d*),\d* ,(?\w+) ,(?.+)$ - /x + /x.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| type.include?('w') ? :warning : :error diff --git a/lib/overcommit/hook/pre_commit/cook_style.rb b/lib/overcommit/hook/pre_commit/cook_style.rb new file mode 100644 index 00000000..e3242645 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/cook_style.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `cookstyle` against any modified Chef Ruby files. + # + # @see https://docs.chef.io/cookstyle.html + class CookStyle < Base + GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type| + type.match?(/^warn/) ? :warning : :error + end + + COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type| + type.include?('W') ? :warning : :error + end + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + generic_messages = extract_messages( + result.stderr.split("\n"), + /^(?[a-z]+)/i, + GENERIC_MESSAGE_TYPE_CATEGORIZER, + ) + + cop_messages = extract_messages( + result.stdout.split("\n"), + /^(?(?:\w:)?[^:]+):(?\d+):[^ ]+ (?[^ ]+)/, + COP_MESSAGE_TYPE_CATEGORIZER, + ) + + generic_messages + cop_messages + end + end +end diff --git a/lib/overcommit/hook/pre_commit/credo.rb b/lib/overcommit/hook/pre_commit/credo.rb index 62192fda..b6d4143f 100644 --- a/lib/overcommit/hook/pre_commit/credo.rb +++ b/lib/overcommit/hook/pre_commit/credo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `credo` against any modified ex files. # diff --git a/lib/overcommit/hook/pre_commit/css_lint.rb b/lib/overcommit/hook/pre_commit/css_lint.rb index 27ffd7eb..b110d074 100644 --- a/lib/overcommit/hook/pre_commit/css_lint.rb +++ b/lib/overcommit/hook/pre_commit/css_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `csslint` against any modified CSS files. # @@ -7,7 +9,7 @@ class CssLint < Base ^(?(?:\w:)?[^:]+):\s (?:line\s(?\d+)[^EW]+)? (?Error|Warning) - /x + /x.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/dart_analyzer.rb b/lib/overcommit/hook/pre_commit/dart_analyzer.rb new file mode 100644 index 00000000..38002f22 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/dart_analyzer.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `dartanalyzer` against modified Dart files. + # @see https://dart.dev/tools/dartanalyzer + class DartAnalyzer < Base + MESSAGE_REGEX = /(?.*)•\ (?[^•]+)•\ (?[^:]+):(?\d+):(\d+)\.*/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + extract_messages( + result.stdout.split("\n").grep(MESSAGE_REGEX), + MESSAGE_REGEX, + lambda do |type| + type.include?('error') ? :error : :warning + end + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/dogma.rb b/lib/overcommit/hook/pre_commit/dogma.rb index e9c217fb..744553a2 100644 --- a/lib/overcommit/hook/pre_commit/dogma.rb +++ b/lib/overcommit/hook/pre_commit/dogma.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `dogma` against any modified ex files. # diff --git a/lib/overcommit/hook/pre_commit/erb_lint.rb b/lib/overcommit/hook/pre_commit/erb_lint.rb new file mode 100644 index 00000000..ae5af164 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/erb_lint.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `erblint` against any modified ERB files. + # + # @see https://github.com/Shopify/erb-lint + class ErbLint < Base + MESSAGE_REGEX = /(?.+)\nIn file: (?.+):(?\d+)/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + extract_messages( + result.stdout.split("\n\n")[1..], + MESSAGE_REGEX + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/es_lint.rb b/lib/overcommit/hook/pre_commit/es_lint.rb index 71c71b9f..240d749d 100644 --- a/lib/overcommit/hook/pre_commit/es_lint.rb +++ b/lib/overcommit/hook/pre_commit/es_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `eslint` against any modified JavaScript files. # @@ -17,20 +19,17 @@ module Overcommit::Hook::PreCommit # @see http://eslint.org/ class EsLint < Base def run + eslint_regex = /^(?[^\s](?:\w:)?[^:]+):[^\d]+(?\d+).*?(?Error|Warning)/ result = execute(command, args: applicable_files) output = result.stdout.chomp - messages = output.split("\n").grep(/Warning|Error/) + messages = output.split("\n").grep(eslint_regex) return [:fail, result.stderr] if messages.empty? && !result.success? return :pass if result.success? && output.empty? # example message: # path/to/file.js: line 1, col 0, Error - Error message (ruleName) - extract_messages( - messages, - /^(?(?:\w:)?[^:]+):[^\d]+(?\d+).*?(?Error|Warning)/, - lambda { |type| type.downcase.to_sym } - ) + extract_messages(messages, eslint_regex, lambda { |type| type.downcase.to_sym }) end end end diff --git a/lib/overcommit/hook/pre_commit/execute_permissions.rb b/lib/overcommit/hook/pre_commit/execute_permissions.rb index b94d4392..ae4cabd9 100644 --- a/lib/overcommit/hook/pre_commit/execute_permissions.rb +++ b/lib/overcommit/hook/pre_commit/execute_permissions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for files with execute permissions, which are usually not necessary # in source code files (and are typically caused by a misconfigured editor diff --git a/lib/overcommit/hook/pre_commit/fasterer.rb b/lib/overcommit/hook/pre_commit/fasterer.rb index bdc7981a..871be80f 100644 --- a/lib/overcommit/hook/pre_commit/fasterer.rb +++ b/lib/overcommit/hook/pre_commit/fasterer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `fasterer` against any modified Ruby files. # @@ -10,7 +12,7 @@ def run if extract_offense_num(output) == 0 :pass else - return [:warn, output] + [:warn, output] end end diff --git a/lib/overcommit/hook/pre_commit/file_size.rb b/lib/overcommit/hook/pre_commit/file_size.rb new file mode 100644 index 00000000..f81154ef --- /dev/null +++ b/lib/overcommit/hook/pre_commit/file_size.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Checks for oversized files before committing. + class FileSize < Base + def run + return :pass if oversized_files.empty? + + oversized_files.map do |file| + error_message_for(file) + end + end + + def description + "Check for files over #{size_limit_bytes} bytes" + end + + private + + def oversized_files + @oversized_files ||= build_oversized_file_list + end + + def build_oversized_file_list + applicable_files.select do |file| + File.exist?(file) && file_size(file) > size_limit_bytes + end + end + + def size_limit_bytes + config.fetch('size_limit_bytes') + end + + def error_message_for(file) + Overcommit::Hook::Message.new( + :error, + file, + nil, + "#{file} is #{file_size(file)} bytes" + ) + end + + def file_size(file) + File.size(file) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/fix_me.rb b/lib/overcommit/hook/pre_commit/fix_me.rb index 311afd10..81dad3f9 100644 --- a/lib/overcommit/hook/pre_commit/fix_me.rb +++ b/lib/overcommit/hook/pre_commit/fix_me.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Check for "token" strings class FixMe < Base diff --git a/lib/overcommit/hook/pre_commit/flay.rb b/lib/overcommit/hook/pre_commit/flay.rb index c30ee76b..9876739a 100644 --- a/lib/overcommit/hook/pre_commit/flay.rb +++ b/lib/overcommit/hook/pre_commit/flay.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `flay` against any modified files. # diff --git a/lib/overcommit/hook/pre_commit/foodcritic.rb b/lib/overcommit/hook/pre_commit/foodcritic.rb index a9091a26..b40adfbe 100644 --- a/lib/overcommit/hook/pre_commit/foodcritic.rb +++ b/lib/overcommit/hook/pre_commit/foodcritic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `foodcritic` against any modified Ruby files from Chef directory structure. # @@ -100,7 +102,7 @@ def run if result.success? :pass else - return [:warn, result.stderr + result.stdout] + [:warn, result.stderr + result.stdout] end end @@ -135,12 +137,14 @@ def modified_cookbooks_args def modified(type) return [] if !config["#{type}_directory"] || config["#{type}_directory"].empty? + @modified ||= {} @modified[type] ||= directories_changed(full_directory_path("#{type}_directory")) end def full_directory_path(config_option) return config[config_option] if config[config_option].start_with?(File::SEPARATOR) + File.absolute_path(File.join(Overcommit::Utils.repo_root, config[config_option])) end end diff --git a/lib/overcommit/hook/pre_commit/forbidden_branches.rb b/lib/overcommit/hook/pre_commit/forbidden_branches.rb index cb907da1..6969742f 100644 --- a/lib/overcommit/hook/pre_commit/forbidden_branches.rb +++ b/lib/overcommit/hook/pre_commit/forbidden_branches.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Prevents commits to branches matching one of the configured patterns. class ForbiddenBranches < Base diff --git a/lib/overcommit/hook/pre_commit/ginkgo_focus.rb b/lib/overcommit/hook/pre_commit/ginkgo_focus.rb new file mode 100644 index 00000000..8528926e --- /dev/null +++ b/lib/overcommit/hook/pre_commit/ginkgo_focus.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Check for "focused" tests + class GinkgoFocus < Base + def run + keywords = config['keywords'] + result = execute(command, args: [keywords.join('|')] + applicable_files) + + extract_messages( + result.stdout.split("\n"), + /^(?(?:\w:)?[^:]+):(?\d+)/, + lambda { |_type| :warning } + ) + end + + def applicable_test_files + applicable_files.select do |f| + f if f =~ /_test\.go/ + end + end + end +end diff --git a/lib/overcommit/hook/pre_commit/go_fmt.rb b/lib/overcommit/hook/pre_commit/go_fmt.rb new file mode 100644 index 00000000..0f46a18e --- /dev/null +++ b/lib/overcommit/hook/pre_commit/go_fmt.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs go fmt for all modified Go files + class GoFmt < Base + def run + errors = [] + applicable_files.each do |file| + result = execute(command, args: [file]) + errors << (result.stdout + result.stderr) unless result.success? + end + return :pass if errors.empty? + + [:fail, errors.join("\n")] + end + end +end diff --git a/lib/overcommit/hook/pre_commit/go_lint.rb b/lib/overcommit/hook/pre_commit/go_lint.rb index 3e0dfc3c..c610edb4 100644 --- a/lib/overcommit/hook/pre_commit/go_lint.rb +++ b/lib/overcommit/hook/pre_commit/go_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `golint` against any modified Golang files. # diff --git a/lib/overcommit/hook/pre_commit/go_vet.rb b/lib/overcommit/hook/pre_commit/go_vet.rb index 7ddfa840..92814648 100644 --- a/lib/overcommit/hook/pre_commit/go_vet.rb +++ b/lib/overcommit/hook/pre_commit/go_vet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `go vet` against any modified Golang files. # @@ -7,7 +9,7 @@ def run result = execute(command, args: applicable_files) return :pass if result.success? - if result.stderr =~ /no such tool "vet"/ + if result.stderr.match?(/no such tool "vet"/) return :fail, "`go tool vet` is not installed#{install_command_prompt}" end diff --git a/lib/overcommit/hook/pre_commit/golangci_lint.rb b/lib/overcommit/hook/pre_commit/golangci_lint.rb new file mode 100644 index 00000000..6dffe659 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/golangci_lint.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `golangci-lint run` against any modified packages + # + # @see https://github.com/golangci/golangci-lint + class GolangciLint < Base + def run + packages = applicable_files.map { |f| File.dirname(f) }.uniq + result = execute(command, args: packages) + return :pass if result.success? + return [:fail, result.stderr] unless result.stderr.empty? + + extract_messages( + result.stdout.split("\n"), + /^(?(?:\w:)?[^:]+):(?\d+)/, + nil + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/hadolint.rb b/lib/overcommit/hook/pre_commit/hadolint.rb index 513259c8..dd26b2bb 100644 --- a/lib/overcommit/hook/pre_commit/hadolint.rb +++ b/lib/overcommit/hook/pre_commit/hadolint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `hadolint` against any modified Dockefile files. # diff --git a/lib/overcommit/hook/pre_commit/haml_lint.rb b/lib/overcommit/hook/pre_commit/haml_lint.rb index f91dc303..af8d801a 100644 --- a/lib/overcommit/hook/pre_commit/haml_lint.rb +++ b/lib/overcommit/hook/pre_commit/haml_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `haml-lint` against any modified HAML files. # diff --git a/lib/overcommit/hook/pre_commit/hard_tabs.rb b/lib/overcommit/hook/pre_commit/hard_tabs.rb index 854337ee..0c94d4eb 100644 --- a/lib/overcommit/hook/pre_commit/hard_tabs.rb +++ b/lib/overcommit/hook/pre_commit/hard_tabs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for hard tabs in files. class HardTabs < Base diff --git a/lib/overcommit/hook/pre_commit/hlint.rb b/lib/overcommit/hook/pre_commit/hlint.rb index fcbfa122..a52d1b62 100644 --- a/lib/overcommit/hook/pre_commit/hlint.rb +++ b/lib/overcommit/hook/pre_commit/hlint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `hlint` against any modified Haskell files. # @@ -8,7 +10,7 @@ class Hlint < Base :(?\d+) :\d+ :\s*(?\w+) - /x + /x.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| type.include?('W') ? :warning : :error diff --git a/lib/overcommit/hook/pre_commit/html_hint.rb b/lib/overcommit/hook/pre_commit/html_hint.rb index 600e2c52..ddbe37d1 100644 --- a/lib/overcommit/hook/pre_commit/html_hint.rb +++ b/lib/overcommit/hook/pre_commit/html_hint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `htmlhint` against any modified HTML files. # @@ -12,7 +14,7 @@ def run lines = group.split("\n").map(&:strip) file = lines[0][/(.+):/, 1] extract_messages( - lines[1..-1].map { |msg| "#{file}: #{msg}" }, + lines[1..].map { |msg| "#{file}: #{msg}" }, /^(?(?:\w:)?[^:]+): line (?\d+)/ ) end.flatten diff --git a/lib/overcommit/hook/pre_commit/html_tidy.rb b/lib/overcommit/hook/pre_commit/html_tidy.rb index e8c172e2..2667e2c1 100644 --- a/lib/overcommit/hook/pre_commit/html_tidy.rb +++ b/lib/overcommit/hook/pre_commit/html_tidy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `tidy` against any modified HTML files. # @@ -8,7 +10,7 @@ class HtmlTidy < Base line\s(?\d+)\s column\s(?\d+)\s-\s (?Error|Warning):\s(?.+)$ - /x + /x.freeze def run # example message: diff --git a/lib/overcommit/hook/pre_commit/image_optim.rb b/lib/overcommit/hook/pre_commit/image_optim.rb index a4cd75af..200a83f7 100644 --- a/lib/overcommit/hook/pre_commit/image_optim.rb +++ b/lib/overcommit/hook/pre_commit/image_optim.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for images that can be optimized with `image_optim`. # diff --git a/lib/overcommit/hook/pre_commit/java_checkstyle.rb b/lib/overcommit/hook/pre_commit/java_checkstyle.rb index a8607860..5d627a64 100644 --- a/lib/overcommit/hook/pre_commit/java_checkstyle.rb +++ b/lib/overcommit/hook/pre_commit/java_checkstyle.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `checkstyle` against any modified Java files. # # @see http://checkstyle.sourceforge.net/ class JavaCheckstyle < Base - MESSAGE_REGEX = /^(\[(?[^\]]+)\]\s+)?(?(?:\w:)?[^:]+):(?\d+)/ + MESSAGE_REGEX = /^(\[(?[^\]]+)\]\s+)?(?(?:\w:)?[^:]+):(?\d+)/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| %w[WARN INFO].include?(type.to_s) ? :warning : :error diff --git a/lib/overcommit/hook/pre_commit/js_hint.rb b/lib/overcommit/hook/pre_commit/js_hint.rb index 943e64ca..6c22c538 100644 --- a/lib/overcommit/hook/pre_commit/js_hint.rb +++ b/lib/overcommit/hook/pre_commit/js_hint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `jshint` against any modified JavaScript files. # diff --git a/lib/overcommit/hook/pre_commit/js_lint.rb b/lib/overcommit/hook/pre_commit/js_lint.rb index 4e2e6bab..956dfd2c 100644 --- a/lib/overcommit/hook/pre_commit/js_lint.rb +++ b/lib/overcommit/hook/pre_commit/js_lint.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `jslint` against any modified JavaScript files. # # @see http://www.jslint.com/ class JsLint < Base - MESSAGE_REGEX = /(?(?:\w:)?[^:]+):(?\d+)/ + MESSAGE_REGEX = /(?(?:\w:)?[^:]+):(?\d+)/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/jscs.rb b/lib/overcommit/hook/pre_commit/jscs.rb index 23a63f1c..db3ee57b 100644 --- a/lib/overcommit/hook/pre_commit/jscs.rb +++ b/lib/overcommit/hook/pre_commit/jscs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `jscs` (JavaScript Code Style Checker) against any modified JavaScript # files. diff --git a/lib/overcommit/hook/pre_commit/jsl.rb b/lib/overcommit/hook/pre_commit/jsl.rb index 97a00057..98eb7175 100644 --- a/lib/overcommit/hook/pre_commit/jsl.rb +++ b/lib/overcommit/hook/pre_commit/jsl.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `jsl` against any modified JavaScript files. # # @see http://www.javascriptlint.com/ class Jsl < Base - MESSAGE_REGEX = /(?(?:\w:)?.+)\((?\d+)\):(?[^:]+)/ + MESSAGE_REGEX = /(?(?:\w:)?.+)\((?\d+)\):(?[^:]+)/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| - type =~ /warning/ ? :warning : :error + type.match?(/warning/) ? :warning : :error end def run diff --git a/lib/overcommit/hook/pre_commit/json_syntax.rb b/lib/overcommit/hook/pre_commit/json_syntax.rb index bb6f785f..bd162f7d 100644 --- a/lib/overcommit/hook/pre_commit/json_syntax.rb +++ b/lib/overcommit/hook/pre_commit/json_syntax.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks the syntax of any modified JSON files. class JsonSyntax < Base @@ -5,12 +7,10 @@ def run messages = [] applicable_files.each do |file| - begin - JSON.parse(IO.read(file)) - rescue JSON::ParserError => e - error = "#{e.message} parsing #{file}" - messages << Overcommit::Hook::Message.new(:error, file, nil, error) - end + JSON.parse(IO.read(file)) + rescue JSON::ParserError => e + error = "#{e.message} parsing #{file}" + messages << Overcommit::Hook::Message.new(:error, file, nil, error) end messages diff --git a/lib/overcommit/hook/pre_commit/kt_lint.rb b/lib/overcommit/hook/pre_commit/kt_lint.rb new file mode 100644 index 00000000..4b81ed21 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/kt_lint.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `ktlint` against modified Kotlin files. + # @see https://github.com/shyiko/ktlint + class KtLint < Base + MESSAGE_REGEX = /((?[^:]+):(?\d+):(\d+):(?.+))/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + extract_messages( + result.stdout.split("\n").grep(MESSAGE_REGEX), + MESSAGE_REGEX + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/license_finder.rb b/lib/overcommit/hook/pre_commit/license_finder.rb index 45383318..e2b8611b 100644 --- a/lib/overcommit/hook/pre_commit/license_finder.rb +++ b/lib/overcommit/hook/pre_commit/license_finder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs LicenseFinder if any of your package manager declaration files have changed # See more about LicenseFinder at https://github.com/pivotal/LicenseFinder @@ -5,6 +7,7 @@ class LicenseFinder < Base def run result = execute(command) return :pass if result.success? + output = result.stdout + result.stderr [:fail, output] end diff --git a/lib/overcommit/hook/pre_commit/license_header.rb b/lib/overcommit/hook/pre_commit/license_header.rb index c13109c1..f129caf8 100644 --- a/lib/overcommit/hook/pre_commit/license_header.rb +++ b/lib/overcommit/hook/pre_commit/license_header.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for license headers in source files class LicenseHeader < Base diff --git a/lib/overcommit/hook/pre_commit/line_endings.rb b/lib/overcommit/hook/pre_commit/line_endings.rb index 70b2e549..ab66001b 100644 --- a/lib/overcommit/hook/pre_commit/line_endings.rb +++ b/lib/overcommit/hook/pre_commit/line_endings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for line endings in files. # @@ -10,14 +12,14 @@ def run file = File.open(file_name) begin messages += check_file(file, file_name) - rescue ArgumentError => ex + rescue ArgumentError => e # File is likely a binary file which this check should ignore, but # print a warning just in case messages << Overcommit::Hook::Message.new( :warning, file_name, file.lineno, - "#{file_name}:#{file.lineno}:#{ex.message}" + "#{file_name}:#{file.lineno}:#{e.message}" ) end end @@ -57,6 +59,7 @@ def offending_files i = info.split.first next if i == 'l/-text' # ignore binary files next if i == "l/#{eol}" + path end.compact end diff --git a/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb b/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb index 8ca6694e..bf3db4ed 100644 --- a/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb +++ b/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for local paths in files and issues a warning class LocalPathsInGemfile < Base diff --git a/lib/overcommit/hook/pre_commit/mdl.rb b/lib/overcommit/hook/pre_commit/mdl.rb index 305d0c20..56349d72 100644 --- a/lib/overcommit/hook/pre_commit/mdl.rb +++ b/lib/overcommit/hook/pre_commit/mdl.rb @@ -1,10 +1,10 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `mdl` against any modified Markdown files # # @see https://github.com/mivok/markdownlint class Mdl < Base - MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):\s(?.+)/ - def run result = execute(command, args: applicable_files) output = result.stdout.chomp @@ -13,11 +13,17 @@ def run return [:fail, result.stderr] unless result.stderr.empty? # example message: - # path/to/file.md:1: MD001 Error message - extract_messages( - output.split("\n"), - MESSAGE_REGEX - ) + # [{"filename":"file1.md","line":1,"rule":"MD013","aliases":["line-length"], + # "description":"Line length"}] + json_messages = JSON.parse(output) + json_messages.map do |message| + Overcommit::Hook::Message.new( + :error, + message['filename'], + message['line'], + "#{message['filename']}:#{message['line']} #{message['rule']} #{message['description']}" + ) + end end end end diff --git a/lib/overcommit/hook/pre_commit/merge_conflicts.rb b/lib/overcommit/hook/pre_commit/merge_conflicts.rb index 33965bd8..1619d3c6 100644 --- a/lib/overcommit/hook/pre_commit/merge_conflicts.rb +++ b/lib/overcommit/hook/pre_commit/merge_conflicts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for unresolved merge conflicts class MergeConflicts < Base diff --git a/lib/overcommit/hook/pre_commit/mix_format.rb b/lib/overcommit/hook/pre_commit/mix_format.rb new file mode 100644 index 00000000..2fa07551 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/mix_format.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `mix format --check-formatted` against any modified ex/heex/exs files. + # + # @see https://hexdocs.pm/mix/main/Mix.Tasks.Format.html + class MixFormat < Base + # example message: + # ** (Mix) mix format failed due to --check-formatted. + # The following files are not formatted: + # + # * lib/file1.ex + # * lib/file2.ex + FILES_REGEX = /^\s+\*\s+(?.+)$/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + result.stderr.scan(FILES_REGEX).flatten. + map { |file| message(file) } + end + + private + + def message(file) + Overcommit::Hook::Message.new(:error, file, nil, file) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/nginx_test.rb b/lib/overcommit/hook/pre_commit/nginx_test.rb index 553ef0f5..b681ded0 100644 --- a/lib/overcommit/hook/pre_commit/nginx_test.rb +++ b/lib/overcommit/hook/pre_commit/nginx_test.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `nginx -t` against any modified Nginx config files. # # @see https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/ class NginxTest < Base - MESSAGE_REGEX = /^nginx: .+ in (?.+):(?\d+)$/ + MESSAGE_REGEX = /^nginx: .+ in (?.+):(?\d+)$/.freeze def run messages = [] diff --git a/lib/overcommit/hook/pre_commit/pep257.rb b/lib/overcommit/hook/pre_commit/pep257.rb index 339de9ea..01562039 100644 --- a/lib/overcommit/hook/pre_commit/pep257.rb +++ b/lib/overcommit/hook/pre_commit/pep257.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pep257` against any modified Python files. # diff --git a/lib/overcommit/hook/pre_commit/pep8.rb b/lib/overcommit/hook/pre_commit/pep8.rb index 954b2f91..5c42a6d5 100644 --- a/lib/overcommit/hook/pre_commit/pep8.rb +++ b/lib/overcommit/hook/pre_commit/pep8.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pep8` against any modified Python files. # diff --git a/lib/overcommit/hook/pre_commit/php_cs.rb b/lib/overcommit/hook/pre_commit/php_cs.rb index 86c400d7..41d2f2b4 100644 --- a/lib/overcommit/hook/pre_commit/php_cs.rb +++ b/lib/overcommit/hook/pre_commit/php_cs.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `phpcs` against any modified PHP files. class PhpCs < Base # Parse `phpcs` csv mode output - MESSAGE_REGEX = /^\"(?.+)\",(?\d+),\d+,(?.+),\"(?.+)\"/ + MESSAGE_REGEX = /^\"(?.+)\",(?\d+),\d+,(?.+),\"(?.+)\"/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| 'error'.include?(type) ? :error : :warning end @@ -10,21 +12,14 @@ class PhpCs < Base def run messages = [] - applicable_files.each do |file| - result = execute(command, args: [file]) - if result.status - rows = result.stdout.split("\n") - - # Discard the csv header - rows.shift - - # Push each of the errors in the particular file into the array - rows.map do |row| - messages << row - end - end + result = execute(command, args: applicable_files) + if result.status + messages = result.stdout.split("\n") + # Discard the csv header + messages.shift end + return :fail if messages.empty? && !result.success? return :pass if messages.empty? parse_messages(messages) diff --git a/lib/overcommit/hook/pre_commit/php_cs_fixer.rb b/lib/overcommit/hook/pre_commit/php_cs_fixer.rb index a06caf09..87e63744 100644 --- a/lib/overcommit/hook/pre_commit/php_cs_fixer.rb +++ b/lib/overcommit/hook/pre_commit/php_cs_fixer.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `php-cs-fixer` against any modified PHP files. class PhpCsFixer < Base - MESSAGE_REGEX = /\s+\d+\)\s+(?.*\.php)(?\s+\(\w+(?:,\s+)?\))?/ + MESSAGE_REGEX = /\s+\d+\)\s+(?.*\.php)(?\s+\(\w+(?:,\s+)?\))?/.freeze def run messages = [] diff --git a/lib/overcommit/hook/pre_commit/php_lint.rb b/lib/overcommit/hook/pre_commit/php_lint.rb index f34fc433..f2ba2f24 100644 --- a/lib/overcommit/hook/pre_commit/php_lint.rb +++ b/lib/overcommit/hook/pre_commit/php_lint.rb @@ -1,11 +1,13 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `php -l` against any modified PHP files. class PhpLint < Base # Sample String - # rubocop:disable Metrics/LineLength + # rubocop:disable Layout/LineLength # PHP Parse error: syntax error, unexpected 'require_once' (T_REQUIRE_ONCE) in site/sumo.php on line 12 - # rubocop:enable Metrics/LineLength - MESSAGE_REGEX = /^(?.+)\:\s+(?.+) in (?.+) on line (?\d+)/ + # rubocop:enable Layout/LineLength + MESSAGE_REGEX = /^(?.+)\:\s+(?.+) in (?.+) on line (?\d+)/.freeze def run # A list of error messages diff --git a/lib/overcommit/hook/pre_commit/php_stan.rb b/lib/overcommit/hook/pre_commit/php_stan.rb index 7b2c821b..9b8550f7 100644 --- a/lib/overcommit/hook/pre_commit/php_stan.rb +++ b/lib/overcommit/hook/pre_commit/php_stan.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `phpstan` against any modified PHP files. # For running `phpstan` with Laravel, it requires setup with `ide_helper`. @@ -6,7 +8,7 @@ module Overcommit::Hook::PreCommit # https://github.com/phpstan/phpstan/issues/239 # https://gist.github.com/edmondscommerce/89695c9cd2584fefdf540fb1c528d2c2 class PhpStan < Base - MESSAGE_REGEX = /^(?.+)\:(?\d+)\:(?.+)/ + MESSAGE_REGEX = /^(?.+)\:(?\d+)\:(?.+)/.freeze def run messages = [] diff --git a/lib/overcommit/hook/pre_commit/pronto.rb b/lib/overcommit/hook/pre_commit/pronto.rb index 29b588cd..664b4ef8 100644 --- a/lib/overcommit/hook/pre_commit/pronto.rb +++ b/lib/overcommit/hook/pre_commit/pronto.rb @@ -1,21 +1,12 @@ +# frozen_string_literal: true + +require 'overcommit/hook/shared/pronto' + module Overcommit::Hook::PreCommit # Runs `pronto` # # @see https://github.com/mmozuras/pronto class Pronto < Base - MESSAGE_TYPE_CATEGORIZER = lambda do |type| - type.include?('E') ? :error : :warning - end - - def run - result = execute(command) - return :pass if result.success? - - extract_messages( - result.stdout.split("\n"), - /^(?(?:\w:)?[^:]+):(?\d+) (?[^ ]+)/, - MESSAGE_TYPE_CATEGORIZER, - ) - end + include Overcommit::Hook::Shared::Pronto end end diff --git a/lib/overcommit/hook/pre_commit/puppet_lint.rb b/lib/overcommit/hook/pre_commit/puppet_lint.rb index 14d01d9a..2ddf3a10 100644 --- a/lib/overcommit/hook/pre_commit/puppet_lint.rb +++ b/lib/overcommit/hook/pre_commit/puppet_lint.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs 'puppet-lint' against any modified Puppet files. # # @see http://puppet-lint.com/ class PuppetLint < Base - MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/ + MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| type == 'ERROR' ? :error : :warning diff --git a/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb b/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb index 60fb3d52..311a09c3 100644 --- a/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb +++ b/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # # Run's the Puppet metadata linter. It has support for adding options @@ -6,7 +8,7 @@ module Overcommit::Hook::PreCommit # @see https://voxpupuli.org/blog/2014/11/06/linting-metadata-json/ # class PuppetMetadataJsonLint < Base - MESSAGE_REGEX = /\((?.*)\).*/ + MESSAGE_REGEX = /\((?.*)\).*/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| type == 'WARN' ? :warning : :error diff --git a/lib/overcommit/hook/pre_commit/pycodestyle.rb b/lib/overcommit/hook/pre_commit/pycodestyle.rb index fa779afe..a15e7fb7 100644 --- a/lib/overcommit/hook/pre_commit/pycodestyle.rb +++ b/lib/overcommit/hook/pre_commit/pycodestyle.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pycodestyle` against any modified Python files. # diff --git a/lib/overcommit/hook/pre_commit/pydocstyle.rb b/lib/overcommit/hook/pre_commit/pydocstyle.rb index 379302f0..3556f2bf 100644 --- a/lib/overcommit/hook/pre_commit/pydocstyle.rb +++ b/lib/overcommit/hook/pre_commit/pydocstyle.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pydocstyle` against any modified Python files. # diff --git a/lib/overcommit/hook/pre_commit/pyflakes.rb b/lib/overcommit/hook/pre_commit/pyflakes.rb index 8c56ef1d..ff7824c4 100644 --- a/lib/overcommit/hook/pre_commit/pyflakes.rb +++ b/lib/overcommit/hook/pre_commit/pyflakes.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pyflakes` against any modified Python files. # # @see https://pypi.python.org/pypi/pyflakes class Pyflakes < Base - MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/ + MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/pylint.rb b/lib/overcommit/hook/pre_commit/pylint.rb index 7ef6d57c..526bee2d 100644 --- a/lib/overcommit/hook/pre_commit/pylint.rb +++ b/lib/overcommit/hook/pre_commit/pylint.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `pylint` against any modified Python files. # # @see http://www.pylint.org/ class Pylint < Base - MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):(?[CEFRW])/ + MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):(?[CEFRW])/.freeze # Classify 'E' and 'F' message codes as errors, # everything else as warnings. diff --git a/lib/overcommit/hook/pre_commit/python_flake8.rb b/lib/overcommit/hook/pre_commit/python_flake8.rb index 8ae893af..d506cf62 100644 --- a/lib/overcommit/hook/pre_commit/python_flake8.rb +++ b/lib/overcommit/hook/pre_commit/python_flake8.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `flake8` against any modified Python files. # # @see https://pypi.python.org/pypi/flake8 class PythonFlake8 < Base - MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):\d+:\s(?\w\d+)/ + MESSAGE_REGEX = /^(?(?:\w:)?.+):(?\d+):\d+:\s(?\w\d+)/.freeze # Classify 'Exxx' and 'Fxxx' message codes as errors, # everything else as warnings. diff --git a/lib/overcommit/hook/pre_commit/r_spec.rb b/lib/overcommit/hook/pre_commit/r_spec.rb new file mode 100644 index 00000000..26bbc0a8 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/r_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'overcommit/hook/shared/r_spec' + +module Overcommit::Hook::PreCommit + # Runs `rspec` test suite + # + # @see http://rspec.info/ + class RSpec < Base + include Overcommit::Hook::Shared::RSpec + end +end diff --git a/lib/overcommit/hook/pre_commit/rails_best_practices.rb b/lib/overcommit/hook/pre_commit/rails_best_practices.rb index 12dd8ffb..7c3ba79a 100644 --- a/lib/overcommit/hook/pre_commit/rails_best_practices.rb +++ b/lib/overcommit/hook/pre_commit/rails_best_practices.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit module Hook module PreCommit @@ -5,7 +7,7 @@ module PreCommit # # @see https://github.com/railsbp/rails_best_practices class RailsBestPractices < Base - ERROR_REGEXP = /^(?(?:\w:)?[^:]+):(?\d+)\s-\s(?.+)/ + ERROR_REGEXP = /^(?(?:\w:)?[^:]+):(?\d+)\s-\s(?.+)/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb b/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb index 59a45989..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 @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Check to see whether the schema file is in line with the migrations. When a # schema file is present but a migration file is not, this is usually a # failure. The exception is if the schema is at version 0 (i.e before any # migrations have been run). In this case it is OK if there are no migrations. class RailsSchemaUpToDate < Base - def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity + def run # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity if migration_files.any? && schema_files.none? return :fail, "It looks like you're adding a migration, but did not update the schema file" elsif migration_files.none? && schema_files.any? && non_zero_schema_version? @@ -16,7 +18,7 @@ def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity # their username. latest_version = migration_files.map do |file| File.basename(file)[/\d+/] - end.sort.last + end.max up_to_date = schema.include?(latest_version) @@ -32,6 +34,12 @@ def run # rubocop:disable CyclomaticComplexity, PerceivedComplexity private + def encoding + return unless @config.key?('encoding') + + { encoding: @config['encoding'] }.compact + end + def migration_files @migration_files ||= applicable_files.select do |file| file.match %r{db/migrate/.*\.rb} @@ -45,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/rake_target.rb b/lib/overcommit/hook/pre_commit/rake_target.rb index 52f14c99..8ee36ba6 100644 --- a/lib/overcommit/hook/pre_commit/rake_target.rb +++ b/lib/overcommit/hook/pre_commit/rake_target.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/rake_target' module Overcommit::Hook::PreCommit # Runs rake targets # - # @see {Overcommit::Hook::Shared::RakeTarget} + # @see Overcommit::Hook::Shared::RakeTarget class RakeTarget < Base include Overcommit::Hook::Shared::RakeTarget end diff --git a/lib/overcommit/hook/pre_commit/reek.rb b/lib/overcommit/hook/pre_commit/reek.rb index 4574a297..163d7206 100644 --- a/lib/overcommit/hook/pre_commit/reek.rb +++ b/lib/overcommit/hook/pre_commit/reek.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `reek` against any modified Ruby files. # diff --git a/lib/overcommit/hook/pre_commit/rst_lint.rb b/lib/overcommit/hook/pre_commit/rst_lint.rb index d85be659..5647508d 100644 --- a/lib/overcommit/hook/pre_commit/rst_lint.rb +++ b/lib/overcommit/hook/pre_commit/rst_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `rst-lint` against any modified reStructuredText files # @@ -5,7 +7,7 @@ module Overcommit::Hook::PreCommit class RstLint < Base MESSAGE_REGEX = / ^(?INFO|WARNING|ERROR|SEVERE)(?(?:\w:)?[^:]+):(?\d+)\s(?.+) - /x + /x.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/rubo_cop.rb b/lib/overcommit/hook/pre_commit/rubo_cop.rb index 2fe17207..4e3b3353 100644 --- a/lib/overcommit/hook/pre_commit/rubo_cop.rb +++ b/lib/overcommit/hook/pre_commit/rubo_cop.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `rubocop` against any modified Ruby files. # # @see http://batsov.com/rubocop/ class RuboCop < Base GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type| - type =~ /^warn/ ? :warning : :error + type.match?(/^warn/) ? :warning : :error end COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type| diff --git a/lib/overcommit/hook/pre_commit/ruby_lint.rb b/lib/overcommit/hook/pre_commit/ruby_lint.rb index 6e0c23da..63403b22 100644 --- a/lib/overcommit/hook/pre_commit/ruby_lint.rb +++ b/lib/overcommit/hook/pre_commit/ruby_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `ruby-lint` against any modified Ruby files. # diff --git a/lib/overcommit/hook/pre_commit/ruby_syntax.rb b/lib/overcommit/hook/pre_commit/ruby_syntax.rb new file mode 100644 index 00000000..ac31e5ed --- /dev/null +++ b/lib/overcommit/hook/pre_commit/ruby_syntax.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `ruby -c` against all Ruby files. + # + class RubySyntax < Base + MESSAGE_TYPE_CATEGORIZER = lambda do |type| + type.match?(/^(syntax)?\s*error/) ? :error : :warning + end + + def run + result = execute(command, args: applicable_files) + + result_lines = result.stderr.split("\n") + + return :pass if result_lines.length.zero? + + # Example message: + # path/to/file.rb:1: syntax error, unexpected '^' + extract_messages( + result_lines, + /^(?[^:]+):(?\d+):\s*(?[^,]+),\s*(?.+)/, + MESSAGE_TYPE_CATEGORIZER + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/scalariform.rb b/lib/overcommit/hook/pre_commit/scalariform.rb index 3822a80e..63bf5e6c 100644 --- a/lib/overcommit/hook/pre_commit/scalariform.rb +++ b/lib/overcommit/hook/pre_commit/scalariform.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `scalariform` against any modified Scala files. # # @see https://github.com/mdr/scalariform class Scalariform < Base - MESSAGE_REGEX = /^\[(?FAILED|ERROR)\]\s+(?(?:\w:)?.+)/ + MESSAGE_REGEX = /^\[(?FAILED|ERROR)\]\s+(?(?:\w:)?.+)/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/scalastyle.rb b/lib/overcommit/hook/pre_commit/scalastyle.rb index 6034195a..6494521d 100644 --- a/lib/overcommit/hook/pre_commit/scalastyle.rb +++ b/lib/overcommit/hook/pre_commit/scalastyle.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `scalastyle` against any modified Scala files. # @@ -8,7 +10,7 @@ class Scalastyle < Base file=(?(?:\w:)?.+)\s message=.+\s* (line=(?\d+))? - /x + /x.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/scss_lint.rb b/lib/overcommit/hook/pre_commit/scss_lint.rb index 741c810b..1bbaba93 100644 --- a/lib/overcommit/hook/pre_commit/scss_lint.rb +++ b/lib/overcommit/hook/pre_commit/scss_lint.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `scss-lint` against any modified SCSS files. # - # @see https://github.com/brigade/scss-lint + # @see https://github.com/sds/scss-lint class ScssLint < Base def run result = execute(command, args: applicable_files) @@ -16,9 +18,9 @@ def run begin collect_lint_messages(JSON.parse(result.stdout)) - rescue JSON::ParserError => ex - return :fail, "Unable to parse JSON returned by SCSS-Lint: #{ex.message}\n" \ - "STDOUT: #{result.stdout}\nSTDERR: #{result.stderr}" + rescue JSON::ParserError => e + [:fail, "Unable to parse JSON returned by SCSS-Lint: #{e.message}\n" \ + "STDOUT: #{result.stdout}\nSTDERR: #{result.stderr}"] end end diff --git a/lib/overcommit/hook/pre_commit/semi_standard.rb b/lib/overcommit/hook/pre_commit/semi_standard.rb index d88afb28..f6746d2f 100644 --- a/lib/overcommit/hook/pre_commit/semi_standard.rb +++ b/lib/overcommit/hook/pre_commit/semi_standard.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `semistandard` against any modified JavaScript files. # # @see https://github.com/Flet/semistandard class SemiStandard < Base - MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/ + MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/shell_check.rb b/lib/overcommit/hook/pre_commit/shell_check.rb index d918c95c..03c7a64f 100644 --- a/lib/overcommit/hook/pre_commit/shell_check.rb +++ b/lib/overcommit/hook/pre_commit/shell_check.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `shellcheck` against any modified shell script files. # diff --git a/lib/overcommit/hook/pre_commit/slim_lint.rb b/lib/overcommit/hook/pre_commit/slim_lint.rb index e373bcb5..8e1d1ce0 100644 --- a/lib/overcommit/hook/pre_commit/slim_lint.rb +++ b/lib/overcommit/hook/pre_commit/slim_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `slim-lint` against any modified Slim templates. # diff --git a/lib/overcommit/hook/pre_commit/sorbet.rb b/lib/overcommit/hook/pre_commit/sorbet.rb new file mode 100644 index 00000000..57988a6e --- /dev/null +++ b/lib/overcommit/hook/pre_commit/sorbet.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs 'srb tc' against any modified files. + # + # @see https://github.com/sorbet/sorbet + class Sorbet < Base + # example of output: + # sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003 + MESSAGE_REGEX = /^(?[^:]+):(?\d+): (?.*)$/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + output = result.stderr.split("\n").grep(MESSAGE_REGEX) + + extract_messages( + output, + MESSAGE_REGEX + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/sqlint.rb b/lib/overcommit/hook/pre_commit/sqlint.rb index b5cae3c6..2e5c5a88 100644 --- a/lib/overcommit/hook/pre_commit/sqlint.rb +++ b/lib/overcommit/hook/pre_commit/sqlint.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs 'sqlint' against any modified SQL files. # # @see https://github.com/purcell/sqlint class Sqlint < Base - MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/ + MESSAGE_REGEX = /(?(?:\w:)?.+):(?\d+):\d+:(?\w+)/.freeze MESSAGE_TYPE_CATEGORIZER = lambda do |type| type == 'ERROR' ? :error : :warning diff --git a/lib/overcommit/hook/pre_commit/standard.rb b/lib/overcommit/hook/pre_commit/standard.rb index ae8dfa5d..7b70ab2c 100644 --- a/lib/overcommit/hook/pre_commit/standard.rb +++ b/lib/overcommit/hook/pre_commit/standard.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `standard` against any modified JavaScript files. # # @see https://github.com/feross/standard class Standard < Base - MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/ + MESSAGE_REGEX = /^\s*(?(?:\w:)?[^:]+):(?\d+)/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/stylelint.rb b/lib/overcommit/hook/pre_commit/stylelint.rb index d0120eda..aae26f08 100644 --- a/lib/overcommit/hook/pre_commit/stylelint.rb +++ b/lib/overcommit/hook/pre_commit/stylelint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `stylelint` against any modified CSS file. # @@ -6,12 +8,13 @@ class Stylelint < Base # example of output: # index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation) - MESSAGE_REGEX = /^(?.+):\D*(?\d).*$/ + MESSAGE_REGEX = /^(?[^:]+):\D*(?\d+).*$/.freeze def run result = execute(command, args: applicable_files) - output = result.stdout.chomp + output = result.stdout + result.stderr.chomp return :pass if result.success? && output.empty? + extract_messages( output.split("\n"), MESSAGE_REGEX diff --git a/lib/overcommit/hook/pre_commit/swift_lint.rb b/lib/overcommit/hook/pre_commit/swift_lint.rb new file mode 100644 index 00000000..a1d8ef09 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/swift_lint.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs `swiftlint lint` against modified Swift files. + # @see https://github.com/realm/SwiftLint + class SwiftLint < Base + MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+)[^ ]* (?[^ ]+):(?.*)/.freeze + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + extract_messages( + result.stdout.split("\n").grep(MESSAGE_REGEX), + MESSAGE_REGEX + ) + end + end +end diff --git a/lib/overcommit/hook/pre_commit/terraform_format.rb b/lib/overcommit/hook/pre_commit/terraform_format.rb new file mode 100644 index 00000000..905a55eb --- /dev/null +++ b/lib/overcommit/hook/pre_commit/terraform_format.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PreCommit + # Runs 'terraform fmt' against any modified *.tf files. + # + # @see https://www.terraform.io/docs/commands/fmt.html + class TerraformFormat < Base + def run + messages = [] + applicable_files.each do |f| + result = execute(command, args: [f]) + unless result.success? + messages << Overcommit::Hook::Message.new(:error, f, nil, "violation found in #{f}") + end + end + messages + end + end +end diff --git a/lib/overcommit/hook/pre_commit/trailing_whitespace.rb b/lib/overcommit/hook/pre_commit/trailing_whitespace.rb index b17e2871..62431cad 100644 --- a/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +++ b/lib/overcommit/hook/pre_commit/trailing_whitespace.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks for trailing whitespace in files. class TrailingWhitespace < Base diff --git a/lib/overcommit/hook/pre_commit/travis_lint.rb b/lib/overcommit/hook/pre_commit/travis_lint.rb index 9909a072..9fb4c681 100644 --- a/lib/overcommit/hook/pre_commit/travis_lint.rb +++ b/lib/overcommit/hook/pre_commit/travis_lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `travis-lint` against any modified Travis CI files. # diff --git a/lib/overcommit/hook/pre_commit/ts_lint.rb b/lib/overcommit/hook/pre_commit/ts_lint.rb index 2a29865c..1340cbcf 100644 --- a/lib/overcommit/hook/pre_commit/ts_lint.rb +++ b/lib/overcommit/hook/pre_commit/ts_lint.rb @@ -1,17 +1,27 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `tslint` against modified TypeScript files. # @see http://palantir.github.io/tslint/ class TsLint < Base + # example message: + # "src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140" + # or + # "ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef" + MESSAGE_REGEX = /^(?.+: )?(?.+?(?=\[))[^\d]+(?\d+).*?/.freeze + def run result = execute(command, args: applicable_files) output = result.stdout.chomp return :pass if result.success? && output.empty? - # example message: - # src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140 + output_lines = output.split("\n").map(&:strip).reject(&:empty?) + type_categorizer = ->(type) { type.nil? || type.include?('ERROR') ? :error : :warning } + extract_messages( - output.split("\n"), - /^(?.+?(?=\[))[^\d]+(?\d+).*?/ + output_lines, + MESSAGE_REGEX, + type_categorizer ) end end diff --git a/lib/overcommit/hook/pre_commit/vint.rb b/lib/overcommit/hook/pre_commit/vint.rb index 91169e82..17d8f9a2 100644 --- a/lib/overcommit/hook/pre_commit/vint.rb +++ b/lib/overcommit/hook/pre_commit/vint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `vint` against any modified Vim script files. # diff --git a/lib/overcommit/hook/pre_commit/w3c_css.rb b/lib/overcommit/hook/pre_commit/w3c_css.rb index 0193ea3e..cb32c109 100644 --- a/lib/overcommit/hook/pre_commit/w3c_css.rb +++ b/lib/overcommit/hook/pre_commit/w3c_css.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `w3c_validators` against any modified CSS files. # @@ -40,10 +42,10 @@ def validator def opts @opts ||= { validator_uri: config['validator_uri'], - proxy_server: config['proxy_server'], - proxy_port: config['proxy_port'], - proxy_user: config['proxy_user'], - proxy_pass: config['proxy_pass'] + proxy_server: config['proxy_server'], + proxy_port: config['proxy_port'], + proxy_user: config['proxy_user'], + proxy_pass: config['proxy_pass'] } end diff --git a/lib/overcommit/hook/pre_commit/w3c_html.rb b/lib/overcommit/hook/pre_commit/w3c_html.rb index ae6a8a7c..945e7997 100644 --- a/lib/overcommit/hook/pre_commit/w3c_html.rb +++ b/lib/overcommit/hook/pre_commit/w3c_html.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `w3c_validators` against any modified HTML files. # @@ -40,10 +42,10 @@ def validator def opts @opts ||= { validator_uri: config['validator_uri'], - proxy_server: config['proxy_server'], - proxy_port: config['proxy_port'], - proxy_user: config['proxy_user'], - proxy_pass: config['proxy_pass'] + proxy_server: config['proxy_server'], + proxy_port: config['proxy_port'], + proxy_user: config['proxy_user'], + proxy_pass: config['proxy_pass'] } end diff --git a/lib/overcommit/hook/pre_commit/xml_lint.rb b/lib/overcommit/hook/pre_commit/xml_lint.rb index 0be13e82..71cb403f 100644 --- a/lib/overcommit/hook/pre_commit/xml_lint.rb +++ b/lib/overcommit/hook/pre_commit/xml_lint.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `xmllint` against any modified XML files. # # @see http://xmlsoft.org/xmllint.html class XmlLint < Base - MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/ + MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+):/.freeze def run result = execute(command, args: applicable_files) diff --git a/lib/overcommit/hook/pre_commit/xml_syntax.rb b/lib/overcommit/hook/pre_commit/xml_syntax.rb index a84ac145..99465182 100644 --- a/lib/overcommit/hook/pre_commit/xml_syntax.rb +++ b/lib/overcommit/hook/pre_commit/xml_syntax.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks the syntax of any modified XML files. class XmlSyntax < Base @@ -5,12 +7,10 @@ def run messages = [] applicable_files.each do |file| - begin - REXML::Document.new(IO.read(file)) - rescue REXML::ParseException => e - error = "Error parsing #{file}: #{e.message}" - messages << Overcommit::Hook::Message.new(:error, file, nil, error) - end + REXML::Document.new(IO.read(file)) + rescue REXML::ParseException => e + error = "Error parsing #{file}: #{e.message}" + messages << Overcommit::Hook::Message.new(:error, file, nil, error) end messages diff --git a/lib/overcommit/hook/pre_commit/yaml_lint.rb b/lib/overcommit/hook/pre_commit/yaml_lint.rb index 6c6898a2..f780725f 100644 --- a/lib/overcommit/hook/pre_commit/yaml_lint.rb +++ b/lib/overcommit/hook/pre_commit/yaml_lint.rb @@ -1,15 +1,38 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Runs `YAMLLint` against any modified YAML files. # # @see https://github.com/adrienverge/yamllint class YamlLint < Base + MESSAGE_REGEX = / + ^(?.+) + :(?\d+) + :(?\d+) + :\s\[(?\w+)\] + \s(?.+)$ + /x.freeze + def run result = execute(command, args: applicable_files) + parse_messages(result.stdout) + end + + private + + def parse_messages(output) + repo_root = Overcommit::Utils.repo_root + + output.scan(MESSAGE_REGEX).map do |file, line, col, type, msg| + line = line.to_i + type = type.to_sym + # Obtain the path relative to the root of the repository + # for nicer output: + relpath = file.dup + relpath.slice!("#{repo_root}/") - if result.success? - :pass - else - return [:warn, result.stdout] + text = "#{relpath}:#{line}:#{col}:#{type} #{msg}" + Overcommit::Hook::Message.new(type, file, line, text) end end end diff --git a/lib/overcommit/hook/pre_commit/yaml_syntax.rb b/lib/overcommit/hook/pre_commit/yaml_syntax.rb index 9562087e..83ff6789 100644 --- a/lib/overcommit/hook/pre_commit/yaml_syntax.rb +++ b/lib/overcommit/hook/pre_commit/yaml_syntax.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreCommit # Checks the syntax of any modified YAML files. class YamlSyntax < Base @@ -5,14 +7,25 @@ def run messages = [] applicable_files.each do |file| + YAML.load_file(file, aliases: true) + rescue ArgumentError begin YAML.load_file(file) rescue ArgumentError, Psych::SyntaxError => e messages << Overcommit::Hook::Message.new(:error, file, nil, e.message) end + rescue Psych::DisallowedClass => e + messages << error_message(file, e) end messages end + + private + + def error_message(file, error) + text = "#{file}: #{error.message}" + Overcommit::Hook::Message.new(:error, file, nil, text) + end end end diff --git a/lib/overcommit/hook/pre_commit/yard_coverage.rb b/lib/overcommit/hook/pre_commit/yard_coverage.rb index fdb6c26a..c9188edb 100644 --- a/lib/overcommit/hook/pre_commit/yard_coverage.rb +++ b/lib/overcommit/hook/pre_commit/yard_coverage.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module Overcommit::Hook::PreCommit # Class to check yard documentation coverage. diff --git a/lib/overcommit/hook/pre_commit/yarn_check.rb b/lib/overcommit/hook/pre_commit/yarn_check.rb index 78a29cdc..f51402d3 100644 --- a/lib/overcommit/hook/pre_commit/yarn_check.rb +++ b/lib/overcommit/hook/pre_commit/yarn_check.rb @@ -6,13 +6,13 @@ module Overcommit::Hook::PreCommit # # @see https://yarnpkg.com/en/docs/cli/check class YarnCheck < Base - LOCK_FILE = 'yarn.lock'.freeze + LOCK_FILE = 'yarn.lock' # A lot of the errors returned by `yarn check` are outside the developer's control # (are caused by bad package specification, in the hands of the upstream maintainer) # So limit reporting to errors the developer can do something about ACTIONABLE_ERRORS = [ - 'Lockfile does not contain pattern'.freeze, + 'Lockfile does not contain pattern', ].freeze def run diff --git a/lib/overcommit/hook/pre_push/base.rb b/lib/overcommit/hook/pre_push/base.rb index 7f07a9ac..35e2b3a8 100644 --- a/lib/overcommit/hook/pre_push/base.rb +++ b/lib/overcommit/hook/pre_push/base.rb @@ -1,4 +1,7 @@ +# frozen_string_literal: true + require 'forwardable' +require 'overcommit/utils/messages_utils' module Overcommit::Hook::PrePush # Functionality common to all pre-push hooks. @@ -6,5 +9,25 @@ class Base < Overcommit::Hook::Base extend Forwardable def_delegators :@context, :remote_name, :remote_url, :pushed_refs + + def run? + super && + !exclude_remotes.include?(remote_name) && + (include_remote_ref_deletions? || !@context.remote_ref_deletion?) + end + + private + + def extract_messages(*args) + Overcommit::Utils::MessagesUtils.extract_messages(*args) + end + + def exclude_remotes + @config['exclude_remotes'] || [] + end + + def include_remote_ref_deletions? + @config['include_remote_ref_deletions'] + end end end diff --git a/lib/overcommit/hook/pre_push/brakeman.rb b/lib/overcommit/hook/pre_push/brakeman.rb index 9fe76e4e..c4fbf482 100644 --- a/lib/overcommit/hook/pre_push/brakeman.rb +++ b/lib/overcommit/hook/pre_push/brakeman.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `brakeman` whenever Ruby/Rails files change. # diff --git a/lib/overcommit/hook/pre_push/cargo_test.rb b/lib/overcommit/hook/pre_push/cargo_test.rb index 70be95f7..ab782e0c 100644 --- a/lib/overcommit/hook/pre_push/cargo_test.rb +++ b/lib/overcommit/hook/pre_push/cargo_test.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `cargo test` before push if Rust files changed class CargoTest < Base def run result = execute(command) return :pass if result.success? + [:fail, result.stdout] end end diff --git a/lib/overcommit/hook/pre_push/flutter_test.rb b/lib/overcommit/hook/pre_push/flutter_test.rb new file mode 100644 index 00000000..26c987cf --- /dev/null +++ b/lib/overcommit/hook/pre_push/flutter_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs Flutter test suite (`flutter test`) before push + # + # @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html + class FlutterTest < Base + def run + result = execute(command) + return :pass if result.success? + + output = result.stdout + result.stderr + [:fail, output] + end + end +end diff --git a/lib/overcommit/hook/pre_push/go_test.rb b/lib/overcommit/hook/pre_push/go_test.rb new file mode 100644 index 00000000..b6b732a6 --- /dev/null +++ b/lib/overcommit/hook/pre_push/go_test.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs `go test ./...` command on prepush + class GoTest < 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/golangci_lint.rb b/lib/overcommit/hook/pre_push/golangci_lint.rb new file mode 100644 index 00000000..42df41b0 --- /dev/null +++ b/lib/overcommit/hook/pre_push/golangci_lint.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs golangci-lint + # + # @see https://github.com/golangci/golangci-lint + class GolangciLint < 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/minitest.rb b/lib/overcommit/hook/pre_push/minitest.rb index 450a8098..4743665e 100644 --- a/lib/overcommit/hook/pre_push/minitest.rb +++ b/lib/overcommit/hook/pre_push/minitest.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `minitest` test suite before push # 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/php_unit.rb b/lib/overcommit/hook/pre_push/php_unit.rb index 758718d5..ec500597 100644 --- a/lib/overcommit/hook/pre_push/php_unit.rb +++ b/lib/overcommit/hook/pre_push/php_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `phpunit` test suite before push # diff --git a/lib/overcommit/hook/pre_push/pronto.rb b/lib/overcommit/hook/pre_push/pronto.rb new file mode 100644 index 00000000..93a0724e --- /dev/null +++ b/lib/overcommit/hook/pre_push/pronto.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'overcommit/hook/shared/pronto' + +module Overcommit::Hook::PrePush + # Runs `pronto` + # + # @see https://github.com/mmozuras/pronto + class Pronto < Base + include Overcommit::Hook::Shared::Pronto + end +end diff --git a/lib/overcommit/hook/pre_push/protected_branches.rb b/lib/overcommit/hook/pre_push/protected_branches.rb index e83ad8a1..aca3f2ec 100644 --- a/lib/overcommit/hook/pre_push/protected_branches.rb +++ b/lib/overcommit/hook/pre_push/protected_branches.rb @@ -1,5 +1,9 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush - # Prevents destructive updates to specified branches. + # Prevents updates to specified branches. + # Accepts a 'destructive_only' option globally or per branch + # to only prevent destructive updates. class ProtectedBranches < Base def run return :pass unless illegal_pushes.any? @@ -15,32 +19,55 @@ def run def illegal_pushes @illegal_pushes ||= pushed_refs.select do |pushed_ref| - protected?(pushed_ref.remote_ref) && allow_non_destructive?(pushed_ref) + protected?(pushed_ref) end end - def protected?(remote_ref) + def protected?(ref) + find_pattern(ref.remote_ref)&.destructive?(ref) + end + + def find_pattern(remote_ref) ref_name = remote_ref[%r{refs/heads/(.*)}, 1] - return false if ref_name.nil? - protected_branch_patterns.any? do |pattern| - File.fnmatch(pattern, ref_name) + return if ref_name.nil? + + patterns.find do |pattern| + File.fnmatch(pattern.to_s, ref_name) + end + end + + def patterns + @patterns ||= fetch_patterns + end + + def fetch_patterns + branch_configurations.map do |pattern| + if pattern.is_a?(Hash) + Pattern.new(pattern.keys.first, pattern['destructive_only']) + else + Pattern.new(pattern, global_destructive_only?) + end end end - def protected_branch_patterns - @protected_branch_patterns ||= Array(config['branches']). - concat(Array(config['branch_patterns'])) + def branch_configurations + config['branches'].to_a + config['branch_patterns'].to_a end - def destructive_only? + def global_destructive_only? config['destructive_only'].nil? || config['destructive_only'] end - def allow_non_destructive?(ref) - if destructive_only? - ref.destructive? - else - true + Pattern = Struct.new('Pattern', :name, :destructive_only) do + alias_method :to_s, :name + alias_method :destructive_only?, :destructive_only + + def destructive?(ref) + if destructive_only? + ref.destructive? + else + true + end end end end diff --git a/lib/overcommit/hook/pre_push/pub_test.rb b/lib/overcommit/hook/pre_push/pub_test.rb new file mode 100644 index 00000000..0dbfb0bf --- /dev/null +++ b/lib/overcommit/hook/pre_push/pub_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs Dart test suite (`pub run test`) before push + # + # @see https://pub.dev/packages/test#running-tests + class PubTest < Base + def run + result = execute(command) + return :pass if result.success? + + output = result.stdout + result.stderr + [:fail, output] + end + end +end diff --git a/lib/overcommit/hook/pre_push/pytest.rb b/lib/overcommit/hook/pre_push/pytest.rb index 109bf216..35c11561 100644 --- a/lib/overcommit/hook/pre_push/pytest.rb +++ b/lib/overcommit/hook/pre_push/pytest.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `pytest` test suite before push # diff --git a/lib/overcommit/hook/pre_push/python_nose.rb b/lib/overcommit/hook/pre_push/python_nose.rb index da805773..94476e0e 100644 --- a/lib/overcommit/hook/pre_push/python_nose.rb +++ b/lib/overcommit/hook/pre_push/python_nose.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `nose` test suite before push # diff --git a/lib/overcommit/hook/pre_push/r_spec.rb b/lib/overcommit/hook/pre_push/r_spec.rb index e9f4ebad..7ce3df29 100644 --- a/lib/overcommit/hook/pre_push/r_spec.rb +++ b/lib/overcommit/hook/pre_push/r_spec.rb @@ -1,14 +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/pre_push/rake_target.rb b/lib/overcommit/hook/pre_push/rake_target.rb index 304bbba5..f44897de 100644 --- a/lib/overcommit/hook/pre_push/rake_target.rb +++ b/lib/overcommit/hook/pre_push/rake_target.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'overcommit/hook/shared/rake_target' module Overcommit::Hook::PrePush # Runs rake targets # - # @see {Overcommit::Hook::Shared::RakeTarget} + # @see Overcommit::Hook::Shared::RakeTarget class RakeTarget < Base include Overcommit::Hook::Shared::RakeTarget end diff --git a/lib/overcommit/hook/pre_push/test_unit.rb b/lib/overcommit/hook/pre_push/test_unit.rb index 930a0f19..44ae9d71 100644 --- a/lib/overcommit/hook/pre_push/test_unit.rb +++ b/lib/overcommit/hook/pre_push/test_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PrePush # Runs `test-unit` test suite before push # diff --git a/lib/overcommit/hook/pre_rebase/base.rb b/lib/overcommit/hook/pre_rebase/base.rb index 49ac6511..1dc72f1c 100644 --- a/lib/overcommit/hook/pre_rebase/base.rb +++ b/lib/overcommit/hook/pre_rebase/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module Overcommit::Hook::PreRebase diff --git a/lib/overcommit/hook/pre_rebase/merged_commits.rb b/lib/overcommit/hook/pre_rebase/merged_commits.rb index fd003942..7c522e8c 100644 --- a/lib/overcommit/hook/pre_rebase/merged_commits.rb +++ b/lib/overcommit/hook/pre_rebase/merged_commits.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::PreRebase # Prevents rebasing commits that have already been merged into one of # a specified set of branches. diff --git a/lib/overcommit/hook/prepare_commit_msg/base.rb b/lib/overcommit/hook/prepare_commit_msg/base.rb new file mode 100644 index 00000000..a4fbf31e --- /dev/null +++ b/lib/overcommit/hook/prepare_commit_msg/base.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'forwardable' + +module Overcommit::Hook::PrepareCommitMsg + # Functionality common to all prepare-commit-msg hooks. + class Base < Overcommit::Hook::Base + extend Forwardable + + def_delegators :@context, + :commit_message_filename, :commit_message_source, :commit, :lock + + def modify_commit_message + raise 'This expects a block!' unless block_given? + + # NOTE: this assumes all the hooks of the same type share the context's + # memory. If that's not the case, this won't work. + lock.synchronize do + contents = File.read(commit_message_filename) + File.open(commit_message_filename, 'w') do |f| + f << (yield contents) + end + end + end + end +end diff --git a/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb new file mode 100644 index 00000000..8852b0b5 --- /dev/null +++ b/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrepareCommitMsg + # Prepends the commit message with a message based on the branch name. + # + # === What to prepend + # + # It's possible to reference parts of the branch name through the captures in + # the `branch_pattern` regex. + # + # For instance, if your current branch is `123-topic` then this config + # + # branch_pattern: '(\d+)-(\w+)' + # replacement_text: '[#\1] ' + # + # would make this hook prepend commit messages with `[#123] `. + # + # Similarly, a replacement text of `[\1][\2]` would result in `[123][topic]`. + # + # == When to run this hook + # + # You can configure this to run only for specific types of commits by setting + # the `skipped_commit_types`. The allowed types are + # + # - 'message' - if message is given via `-m`, `-F` + # - 'template' - if `-t` is given or `commit.template` is set + # - 'commit' - if `-c`, `-C`, or `--amend` is given + # - 'merge' - if merging + # - 'squash' - if squashing + # + class ReplaceBranch < Base + DEFAULT_BRANCH_PATTERN = /\A(\d+)-(\w+).*\z/.freeze + + def run + return :pass if skip? + + Overcommit::Utils.log.debug( + "Checking if '#{Overcommit::GitRepo.current_branch}' matches #{branch_pattern}" + ) + + return :warn unless branch_pattern.match?(Overcommit::GitRepo.current_branch) + + Overcommit::Utils.log.debug("Writing #{commit_message_filename} with #{new_template}") + + modify_commit_message do |old_contents| + "#{new_template}#{old_contents}" + end + + :pass + end + + def new_template + @new_template ||= + begin + curr_branch = Overcommit::GitRepo.current_branch + curr_branch.gsub(branch_pattern, replacement_text) + end + end + + def branch_pattern + @branch_pattern ||= + begin + pattern = config['branch_pattern'] + Regexp.new((pattern || '').empty? ? DEFAULT_BRANCH_PATTERN : pattern) + end + end + + def replacement_text + @replacement_text ||= + begin + if File.exist?(replacement_text_config) + File.read(replacement_text_config).chomp + else + replacement_text_config + end + end + end + + def replacement_text_config + @replacement_text_config ||= config['replacement_text'] + end + + def skipped_commit_types + @skipped_commit_types ||= config['skipped_commit_types'].map(&:to_sym) + end + + def skip? + super || skipped_commit_types.include?(commit_message_source) + end + end +end diff --git a/lib/overcommit/hook/shared/bower_install.rb b/lib/overcommit/hook/shared/bower_install.rb index a0ecab63..cfcde92f 100644 --- a/lib/overcommit/hook/shared/bower_install.rb +++ b/lib/overcommit/hook/shared/bower_install.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all BowerInstall hooks. Runs `bower install` when a # change is detected in the repository's dependencies. @@ -7,6 +9,7 @@ module BowerInstall def run result = execute(command) return :fail, result.stderr unless result.success? + :pass end end diff --git a/lib/overcommit/hook/shared/bundle_install.rb b/lib/overcommit/hook/shared/bundle_install.rb index bfb2d560..01acaa32 100644 --- a/lib/overcommit/hook/shared/bundle_install.rb +++ b/lib/overcommit/hook/shared/bundle_install.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all BundleInstall hooks. Runs `bundle install` when a # change is detected in the repository's dependencies. @@ -7,6 +9,7 @@ module BundleInstall def run result = execute(command) return :fail, result.stdout unless result.success? + :pass end end diff --git a/lib/overcommit/hook/shared/composer_install.rb b/lib/overcommit/hook/shared/composer_install.rb index 828484e6..2d2ff01d 100644 --- a/lib/overcommit/hook/shared/composer_install.rb +++ b/lib/overcommit/hook/shared/composer_install.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all ComposerInstall hooks. Runs `composer install` when # a change is detected in the repository's dependencies. @@ -7,6 +9,7 @@ module ComposerInstall def run result = execute(command) return :fail, result.stdout unless result.success? + :pass end end diff --git a/lib/overcommit/hook/shared/index_tags.rb b/lib/overcommit/hook/shared/index_tags.rb index ffc4a67e..99f7efde 100644 --- a/lib/overcommit/hook/shared/index_tags.rb +++ b/lib/overcommit/hook/shared/index_tags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all IndexTags hooks. It runs ctags in the background so # your tag definitions are up-to-date. diff --git a/lib/overcommit/hook/shared/npm_install.rb b/lib/overcommit/hook/shared/npm_install.rb index 93dd0696..f487c8e7 100644 --- a/lib/overcommit/hook/shared/npm_install.rb +++ b/lib/overcommit/hook/shared/npm_install.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all NpmInstall hooks. Runs `npm install` when a change # is detected in the repository's dependencies. @@ -7,6 +9,7 @@ module NpmInstall def run result = execute(command) return :fail, result.stderr unless result.success? + :pass end end diff --git a/lib/overcommit/hook/shared/pronto.rb b/lib/overcommit/hook/shared/pronto.rb new file mode 100644 index 00000000..cf8a3139 --- /dev/null +++ b/lib/overcommit/hook/shared/pronto.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Overcommit::Hook::Shared + # Shared code used by all Pronto hooks. Runs pronto linters. + + # @see https://github.com/prontolabs/pronto + module Pronto + MESSAGE_TYPE_CATEGORIZER = lambda do |type| + type.include?('E') ? :error : :warning + end + + MESSAGE_REGEX = /^(?(?:\w:)?[^:]+):(?\d+) (?[^ ]+)/.freeze + + def run + result = execute(command) + return :pass if result.success? + + # e.g. runtime errors + generic_errors = extract_messages( + result.stderr.split("\n"), + /^(?[a-z]+)/i + ) + + pronto_infractions = extract_messages( + result.stdout.split("\n").select { |line| line.match?(MESSAGE_REGEX) }, + MESSAGE_REGEX, + MESSAGE_TYPE_CATEGORIZER, + ) + + generic_errors + pronto_infractions + end + end +end diff --git a/lib/overcommit/hook/shared/r_spec.rb b/lib/overcommit/hook/shared/r_spec.rb new file mode 100644 index 00000000..9b41e8e1 --- /dev/null +++ b/lib/overcommit/hook/shared/r_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Overcommit::Hook::Shared + # Runs `rspec` test suite before push + # + # @see http://rspec.info/ + module RSpec + def run + result = if @config['include'] + execute(command, args: applicable_files) + else + execute(command) + end + + return :pass if result.success? + + output = result.stdout + result.stderr + [:fail, output] + end + end +end diff --git a/lib/overcommit/hook/shared/rake_target.rb b/lib/overcommit/hook/shared/rake_target.rb index 82da8ec9..6ef3873a 100644 --- a/lib/overcommit/hook/shared/rake_target.rb +++ b/lib/overcommit/hook/shared/rake_target.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # runs specified rake targets. It fails on the first non- - # successfull exit. + # successful exit. # module RakeTarget def run diff --git a/lib/overcommit/hook/shared/submodule_status.rb b/lib/overcommit/hook/shared/submodule_status.rb index fc35c32d..41e24aed 100644 --- a/lib/overcommit/hook/shared/submodule_status.rb +++ b/lib/overcommit/hook/shared/submodule_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all `SubmoduleStatus` hooks to notify the user if any # submodules are uninitialized, out of date with the current index, or contain diff --git a/lib/overcommit/hook/shared/yarn_install.rb b/lib/overcommit/hook/shared/yarn_install.rb index 5cc9b236..fd548f07 100644 --- a/lib/overcommit/hook/shared/yarn_install.rb +++ b/lib/overcommit/hook/shared/yarn_install.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Hook::Shared # Shared code used by all YarnInstall hooks. Runs `yarn install` when a change # is detected in the repository's dependencies. @@ -7,6 +9,7 @@ module YarnInstall def run result = execute(command) return :fail, result.stderr unless result.success? + :pass end end diff --git a/lib/overcommit/hook_context.rb b/lib/overcommit/hook_context.rb index 567ac153..5863ed94 100644 --- a/lib/overcommit/hook_context.rb +++ b/lib/overcommit/hook_context.rb @@ -1,17 +1,19 @@ +# frozen_string_literal: true + # Utility module which manages the creation of {HookContext}s. module Overcommit::HookContext - def self.create(hook_type, config, args, input) + def self.create(hook_type, config, args, input, **cli_options) hook_type_class = Overcommit::Utils.camel_case(hook_type) underscored_hook_type = Overcommit::Utils.snake_case(hook_type) require "overcommit/hook_context/#{underscored_hook_type}" - Overcommit::HookContext.const_get(hook_type_class).new(config, args, input) - rescue LoadError, NameError => error + Overcommit::HookContext.const_get(hook_type_class).new(config, args, input, **cli_options) + rescue LoadError, NameError => e # Could happen when a symlink was created for a hook type Overcommit does # not yet support. raise Overcommit::Exceptions::HookContextLoadError, - "Unable to load '#{hook_type}' hook context: '#{error}'", - error.backtrace + "Unable to load '#{hook_type}' hook context: '#{e}'", + e.backtrace end end diff --git a/lib/overcommit/hook_context/base.rb b/lib/overcommit/hook_context/base.rb index 9d01ff2f..b50698c9 100644 --- a/lib/overcommit/hook_context/base.rb +++ b/lib/overcommit/hook_context/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to the context with which a hook is being run. # @@ -16,10 +18,12 @@ class Base # @param config [Overcommit::Configuration] # @param args [Array] # @param input [IO] standard input stream - def initialize(config, args, input) + # @param options [Hash] cli options + def initialize(config, args, input, **options) @config = config @args = args @input = input + @options = options end # Executes a command as if it were a regular git hook, passing all diff --git a/lib/overcommit/hook_context/commit_msg.rb b/lib/overcommit/hook_context/commit_msg.rb index ed97bbdc..ae32e55d 100644 --- a/lib/overcommit/hook_context/commit_msg.rb +++ b/lib/overcommit/hook_context/commit_msg.rb @@ -1,6 +1,15 @@ +# frozen_string_literal: true + +require_relative 'pre_commit' +require_relative 'helpers/stash_unstaged_changes' +require_relative 'helpers/file_modifications' + module Overcommit::HookContext # Contains helpers related to contextual information used by commit-msg hooks. class CommitMsg < Base + include Overcommit::HookContext::Helpers::StashUnstagedChanges + include Overcommit::HookContext::Helpers::FileModifications + def empty_message? commit_message.strip.empty? end @@ -32,7 +41,9 @@ def commit_message_file end def post_fail_message - "Failed commit message:\n" + commit_message_lines.join + "Failed commit message:\n#{commit_message_lines.join.chomp}\n\n" \ + "Try again with your existing commit message by running:\n" \ + "git commit --edit --file=#{commit_message_file}" end private diff --git a/lib/overcommit/hook_context/diff.rb b/lib/overcommit/hook_context/diff.rb new file mode 100644 index 00000000..3e9aa568 --- /dev/null +++ b/lib/overcommit/hook_context/diff.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'overcommit/git_repo' + +require 'set' + +module Overcommit::HookContext + # Simulates a pre-commit context based on the diff with another git ref. + # + # This results in pre-commit hooks running against the changes between the current + # and another ref, which is useful for automated CI scripts. + class Diff < Base + def modified_files + @modified_files ||= Overcommit::GitRepo.modified_files(refs: @options[:diff]) + end + + def modified_lines_in_file(file) + @modified_lines ||= {} + @modified_lines[file] ||= Overcommit::GitRepo.extract_modified_lines(file, + refs: @options[:diff]) + end + + def hook_class_name + 'PreCommit' + end + + def hook_type_name + 'pre_commit' + end + + def hook_script_name + 'pre-commit' + end + + def initial_commit? + @initial_commit ||= Overcommit::GitRepo.initial_commit? + end + end +end diff --git a/lib/overcommit/hook_context/helpers/file_modifications.rb b/lib/overcommit/hook_context/helpers/file_modifications.rb new file mode 100644 index 00000000..00aaccc8 --- /dev/null +++ b/lib/overcommit/hook_context/helpers/file_modifications.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Overcommit::HookContext + module Helpers + # This module contains methods for determining what files were changed and on what unique line + # numbers did the change occur. + module FileModifications + # Returns whether this hook run was triggered by `git commit --amend` + def amendment? + return @amendment unless @amendment.nil? + + cmd = Overcommit::Utils.parent_command + return unless cmd + + amend_pattern = 'commit(\s.*)?\s--amend(\s|$)' + + # Since the ps command can return invalid byte sequences for commands + # containing unicode characters, we replace the offending characters, + # since the pattern we're looking for will consist of ASCII characters + unless cmd.valid_encoding? + cmd = Overcommit::Utils. + parent_command. + encode('UTF-16be', invalid: :replace, replace: '?'). + encode('UTF-8') + end + + return @amendment if + # True if the command is a commit with the --amend flag + @amendment = !(/\s#{amend_pattern}/ =~ cmd).nil? + + # Check for git aliases that call `commit --amend` + `git config --get-regexp "^alias\\." "#{amend_pattern}"`. + scan(/alias\.([-\w]+)/). # Extract the alias + each do |match| + return @amendment if + # True if the command uses a git alias for `commit --amend` + @amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil? + end + + @amendment + end + + # Get a list of added, copied, or modified files that have been staged. + # Renames and deletions are ignored, since there should be nothing to check. + def modified_files + unless @modified_files + currently_staged = Overcommit::GitRepo.modified_files(staged: true) + @modified_files = currently_staged + + # Include files modified in last commit if amending + if amendment? + subcmd = 'show --format=%n' + previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd) + @modified_files |= filter_modified_files(previously_modified) + end + end + @modified_files + end + + # Returns the set of line numbers corresponding to the lines that were + # changed in a specified file. + def modified_lines_in_file(file) + @modified_lines ||= {} + unless @modified_lines[file] + @modified_lines[file] = + Overcommit::GitRepo.extract_modified_lines(file, staged: true) + + # Include lines modified in last commit if amending + if amendment? + subcmd = 'show --format=%n' + @modified_lines[file] += + Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd) + end + end + @modified_lines[file] + end + end + end +end diff --git a/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb b/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb new file mode 100644 index 00000000..17400539 --- /dev/null +++ b/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +module Overcommit::HookContext + module Helpers + # This module contains behavior for stashing unstaged changes before hooks are ran and restoring + # them afterwards + module StashUnstagedChanges + # Stash unstaged contents of files so hooks don't see changes that aren't + # about to be committed. + def setup_environment + store_modified_times + Overcommit::GitRepo.store_merge_state + Overcommit::GitRepo.store_cherry_pick_state + + # Don't attempt to stash changes if all changes are staged, as this + # prevents us from modifying files at all, which plays better with + # editors/tools which watch for file changes. + if !initial_commit? && unstaged_changes? + stash_changes + + # While running hooks make it appear as if nothing changed + restore_modified_times + end + end + + # Returns whether the current git branch is empty (has no commits). + def initial_commit? + return @initial_commit unless @initial_commit.nil? + + @initial_commit = Overcommit::GitRepo.initial_commit? + end + + # Restore unstaged changes and reset file modification times so it appears + # as if nothing ever changed. + # + # We want to restore the modification times for each of the files after + # every step to ensure as little time as possible has passed while the + # modification time on the file was newer. This helps us play more nicely + # with file watchers. + def cleanup_environment + if @changes_stashed + clear_working_tree + restore_working_tree + restore_modified_times + end + + Overcommit::GitRepo.restore_merge_state + Overcommit::GitRepo.restore_cherry_pick_state + end + + private + + # Stores the modification times for all modified files to make it appear like + # they never changed. + # + # This prevents (some) editors from complaining about files changing when we + # stash changes before running the hooks. + def store_modified_times + @modified_times = {} + + staged_files = modified_files + unstaged_files = Overcommit::GitRepo.modified_files(staged: false) + + (staged_files + unstaged_files).each do |file| + next if Overcommit::Utils.broken_symlink?(file) + next unless File.exist?(file) # Ignore renamed files (old file no longer exists) + + @modified_times[file] = File.mtime(file) + end + end + + # Returns whether there are any changes to tracked files which have not yet + # been staged. + def unstaged_changes? + result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet]) + !result.success? + end + + def stash_changes + @stash_attempted = true + + stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}" + result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do + Overcommit::Utils.execute( + %w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message] + ) + end + + unless result.success? + # Failure to stash in this case is likely due to a configuration + # issue (e.g. author/email not set or GPG signing key incorrect) + raise Overcommit::Exceptions::HookSetupFailed, + "Unable to setup environment for #{hook_script_name} hook run:" \ + "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" + end + + @changes_stashed = `git stash list -1`.include?(stash_message) + end + + # Restores the file modification times for all modified files to make it + # appear like they never changed. + def restore_modified_times + @modified_times.each do |file, time| + next if Overcommit::Utils.broken_symlink?(file) + next unless File.exist?(file) + + File.utime(time, time, file) + end + end + + # Clears the working tree so that the stash can be applied. + def clear_working_tree + removed_submodules = Overcommit::GitRepo.staged_submodule_removals + + result = Overcommit::Utils.execute(%w[git reset --hard]) + unless result.success? + raise Overcommit::Exceptions::HookCleanupFailed, + "Unable to cleanup working tree after #{hook_script_name} hooks run:" \ + "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" + end + + # Hard-resetting a staged submodule removal results in the index being + # reset but the submodule being restored as an empty directory. This empty + # directory prevents us from stashing on a subsequent run if a hook fails. + # + # Work around this by removing these empty submodule directories as there + # doesn't appear any reason to keep them around. + removed_submodules.each do |submodule| + FileUtils.rmdir(submodule.path) + end + end + + # Applies the stash to the working tree to restore the user's state. + def restore_working_tree + result = Overcommit::Utils.execute(%w[git stash pop --index]) + unless result.success? + raise Overcommit::Exceptions::HookCleanupFailed, + "Unable to restore working tree after #{hook_script_name} hooks run:" \ + "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" + end + end + end + end +end diff --git a/lib/overcommit/hook_context/post_checkout.rb b/lib/overcommit/hook_context/post_checkout.rb index a9310974..a8d38649 100644 --- a/lib/overcommit/hook_context/post_checkout.rb +++ b/lib/overcommit/hook_context/post_checkout.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to contextual information used by post-checkout # hooks. diff --git a/lib/overcommit/hook_context/post_commit.rb b/lib/overcommit/hook_context/post_commit.rb index e5ec6da6..8327b0d9 100644 --- a/lib/overcommit/hook_context/post_commit.rb +++ b/lib/overcommit/hook_context/post_commit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to contextual information used by post-commit # hooks. @@ -25,6 +27,7 @@ def modified_lines_in_file(file) # @return [true,false] def initial_commit? return @initial_commit unless @initial_commit.nil? + @initial_commit = !Overcommit::Utils.execute(%w[git rev-parse HEAD~]).success? end end diff --git a/lib/overcommit/hook_context/post_merge.rb b/lib/overcommit/hook_context/post_merge.rb index d8547998..14f91826 100644 --- a/lib/overcommit/hook_context/post_merge.rb +++ b/lib/overcommit/hook_context/post_merge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to contextual information used by post-merge # hooks. diff --git a/lib/overcommit/hook_context/post_rewrite.rb b/lib/overcommit/hook_context/post_rewrite.rb index 249071dc..5310ef8e 100644 --- a/lib/overcommit/hook_context/post_rewrite.rb +++ b/lib/overcommit/hook_context/post_rewrite.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers for contextual information used by post-rewrite hooks. class PostRewrite < Base diff --git a/lib/overcommit/hook_context/pre_commit.rb b/lib/overcommit/hook_context/pre_commit.rb index 32ee752d..cd510605 100644 --- a/lib/overcommit/hook_context/pre_commit.rb +++ b/lib/overcommit/hook_context/pre_commit.rb @@ -1,5 +1,9 @@ +# frozen_string_literal: true + require 'fileutils' require 'set' +require_relative 'helpers/stash_unstaged_changes' +require_relative 'helpers/file_modifications' module Overcommit::HookContext # Contains helpers related to contextual information used by pre-commit hooks. @@ -7,201 +11,8 @@ module Overcommit::HookContext # This includes staged files, which lines of those files have been modified, # etc. It is also responsible for saving/restoring the state of the repo so # hooks only inspect staged changes. - class PreCommit < Base # rubocop:disable ClassLength - # Returns whether this hook run was triggered by `git commit --amend` - def amendment? - return @amendment unless @amendment.nil? - - cmd = Overcommit::Utils.parent_command - amend_pattern = 'commit(\s.*)?\s--amend(\s|$)' - - # Since the ps command can return invalid byte sequences for commands - # containing unicode characters, we replace the offending characters, - # since the pattern we're looking for will consist of ASCII characters - unless cmd.valid_encoding? - cmd.encode!('UTF-16be', invalid: :replace, replace: '?').encode!('UTF-8') - end - - return @amendment if - # True if the command is a commit with the --amend flag - @amendment = !(/\s#{amend_pattern}/ =~ cmd).nil? - - # Check for git aliases that call `commit --amend` - `git config --get-regexp "^alias\\." "#{amend_pattern}"`. - scan(/alias\.([-\w]+)/). # Extract the alias - each do |match| - return @amendment if - # True if the command uses a git alias for `commit --amend` - @amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil? - end - - @amendment - end - - # Stash unstaged contents of files so hooks don't see changes that aren't - # about to be committed. - def setup_environment - store_modified_times - Overcommit::GitRepo.store_merge_state - Overcommit::GitRepo.store_cherry_pick_state - - if !initial_commit? && any_changes? - @stash_attempted = true - - stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}" - result = Overcommit::Utils.execute( - %w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message] - ) - - unless result.success? - # Failure to stash in this case is likely due to a configuration - # issue (e.g. author/email not set or GPG signing key incorrect) - raise Overcommit::Exceptions::HookSetupFailed, - "Unable to setup environment for #{hook_script_name} hook run:" \ - "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" - end - - @changes_stashed = `git stash list -1`.include?(stash_message) - end - - # While running the hooks make it appear as if nothing changed - restore_modified_times - end - - # Restore unstaged changes and reset file modification times so it appears - # as if nothing ever changed. - # - # We want to restore the modification times for each of the files after - # every step to ensure as little time as possible has passed while the - # modification time on the file was newer. This helps us play more nicely - # with file watchers. - def cleanup_environment - unless initial_commit? || (@stash_attempted && !@changes_stashed) - clear_working_tree # Ensure working tree is clean before restoring it - restore_modified_times - end - - if @changes_stashed - restore_working_tree - restore_modified_times - end - - Overcommit::GitRepo.restore_merge_state - Overcommit::GitRepo.restore_cherry_pick_state - restore_modified_times - end - - # Get a list of added, copied, or modified files that have been staged. - # Renames and deletions are ignored, since there should be nothing to check. - def modified_files - unless @modified_files - currently_staged = Overcommit::GitRepo.modified_files(staged: true) - @modified_files = currently_staged - - # Include files modified in last commit if amending - if amendment? - subcmd = 'show --format=%n' - previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd) - @modified_files |= filter_modified_files(previously_modified) - end - end - @modified_files - end - - # Returns the set of line numbers corresponding to the lines that were - # changed in a specified file. - def modified_lines_in_file(file) - @modified_lines ||= {} - unless @modified_lines[file] - @modified_lines[file] = - Overcommit::GitRepo.extract_modified_lines(file, staged: true) - - # Include lines modified in last commit if amending - if amendment? - subcmd = 'show --format=%n' - @modified_lines[file] += - Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd) - end - end - @modified_lines[file] - end - - # Returns whether the current git branch is empty (has no commits). - def initial_commit? - return @initial_commit unless @initial_commit.nil? - @initial_commit = Overcommit::GitRepo.initial_commit? - end - - private - - # Clears the working tree so that the stash can be applied. - def clear_working_tree - removed_submodules = Overcommit::GitRepo.staged_submodule_removals - - result = Overcommit::Utils.execute(%w[git reset --hard]) - unless result.success? - raise Overcommit::Exceptions::HookCleanupFailed, - "Unable to cleanup working tree after #{hook_script_name} hooks run:" \ - "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" - end - - # Hard-resetting a staged submodule removal results in the index being - # reset but the submodule being restored as an empty directory. This empty - # directory prevents us from stashing on a subsequent run if a hook fails. - # - # Work around this by removing these empty submodule directories as there - # doesn't appear any reason to keep them around. - removed_submodules.each do |submodule| - FileUtils.rmdir(submodule.path) - end - end - - # Applies the stash to the working tree to restore the user's state. - def restore_working_tree - result = Overcommit::Utils.execute(%w[git stash pop --index --quiet]) - unless result.success? - raise Overcommit::Exceptions::HookCleanupFailed, - "Unable to restore working tree after #{hook_script_name} hooks run:" \ - "\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}" - end - end - - # Returns whether there are any changes to the working tree, staged or - # otherwise. - def any_changes? - modified_files = `git status -z --untracked-files=no`. - split("\0"). - map { |line| line.gsub(/[^\s]+\s+(.+)/, '\\1') } - - modified_files.any? - end - - # Stores the modification times for all modified files to make it appear like - # they never changed. - # - # This prevents (some) editors from complaining about files changing when we - # stash changes before running the hooks. - def store_modified_times - @modified_times = {} - - staged_files = modified_files - unstaged_files = Overcommit::GitRepo.modified_files(staged: false) - - (staged_files + unstaged_files).each do |file| - next if Overcommit::Utils.broken_symlink?(file) - next unless File.exist?(file) # Ignore renamed files (old file no longer exists) - @modified_times[file] = File.mtime(file) - end - end - - # Restores the file modification times for all modified files to make it - # appear like they never changed. - def restore_modified_times - @modified_times.each do |file, time| - next if Overcommit::Utils.broken_symlink?(file) - next unless File.exist?(file) - File.utime(time, time, file) - end - end + class PreCommit < Base + include Overcommit::HookContext::Helpers::StashUnstagedChanges + include Overcommit::HookContext::Helpers::FileModifications end end diff --git a/lib/overcommit/hook_context/pre_push.rb b/lib/overcommit/hook_context/pre_push.rb index 66fa0c91..39ec19ba 100644 --- a/lib/overcommit/hook_context/pre_push.rb +++ b/lib/overcommit/hook_context/pre_push.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to contextual information used by pre-push hooks. class PrePush < Base @@ -11,6 +13,15 @@ def remote_url @args[1] end + def remote_ref_deletion? + return @remote_ref_deletion if defined?(@remote_ref_deletion) + + @remote_ref_deletion ||= input_lines. + first&. + split(' ')&. + first == '(deleted)' + end + def pushed_refs input_lines.map do |line| PushedRef.new(*line.split(' ')) @@ -65,6 +76,7 @@ def ref_range def overwritten_commits return @overwritten_commits if defined? @overwritten_commits + result = Overcommit::Subprocess.spawn(%W[git rev-list #{remote_sha1} ^#{local_sha1}]) if result.success? result.stdout.split("\n") diff --git a/lib/overcommit/hook_context/pre_rebase.rb b/lib/overcommit/hook_context/pre_rebase.rb index 5a2ca149..f681e067 100644 --- a/lib/overcommit/hook_context/pre_rebase.rb +++ b/lib/overcommit/hook_context/pre_rebase.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookContext # Contains helpers related to contextual information used by pre-rebase # hooks. diff --git a/lib/overcommit/hook_context/prepare_commit_msg.rb b/lib/overcommit/hook_context/prepare_commit_msg.rb new file mode 100644 index 00000000..781fab73 --- /dev/null +++ b/lib/overcommit/hook_context/prepare_commit_msg.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Overcommit::HookContext + # Contains helpers related to contextual information used by prepare-commit-msg + # hooks. + class PrepareCommitMsg < Base + # Returns the name of the file that contains the commit log message + def commit_message_filename + @args[0] + end + + # Returns the source of the commit message, and can be: message (if a -m or + # -F option was given); template (if a -t option was given or the + # configuration option commit.template is set); merge (if the commit is a + # merge or a .git/MERGE_MSG file exists); squash (if a .git/SQUASH_MSG file + # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend + # option was given) + def commit_message_source + @args[1]&.to_sym + end + + # Returns the commit's SHA-1. + # If commit_message_source is :commit, it's passed through the command-line. + def commit_message_source_ref + @args[2] || `git rev-parse HEAD` + end + + # Lock for the pre_commit_message file. Should be shared by all + # prepare-commit-message hooks + def lock + @lock ||= Monitor.new + end + end +end diff --git a/lib/overcommit/hook_context/run_all.rb b/lib/overcommit/hook_context/run_all.rb index 537bff11..5ed7efa0 100644 --- a/lib/overcommit/hook_context/run_all.rb +++ b/lib/overcommit/hook_context/run_all.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'set' module Overcommit::HookContext @@ -34,6 +36,7 @@ def hook_script_name def initial_commit? return @initial_commit unless @initial_commit.nil? + @initial_commit = Overcommit::GitRepo.initial_commit? end diff --git a/lib/overcommit/hook_loader/base.rb b/lib/overcommit/hook_loader/base.rb index 020bcf5b..9173e9f1 100644 --- a/lib/overcommit/hook_loader/base.rb +++ b/lib/overcommit/hook_loader/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookLoader # Responsible for loading hooks from a file. class Base @@ -24,13 +26,23 @@ def load_hooks # Load and return a {Hook} from a CamelCase hook name. def create_hook(hook_name) - Overcommit::Hook.const_get(@context.hook_class_name). - const_get(hook_name). - new(@config, @context) - rescue LoadError, NameError => error - raise Overcommit::Exceptions::HookLoadError, - "Unable to load hook '#{hook_name}': #{error}", - error.backtrace + hook_type_class = Overcommit::Hook.const_get(@context.hook_class_name) + hook_base_class = hook_type_class.const_get(:Base) + hook_class = hook_type_class.const_get(hook_name) + unless hook_class < hook_base_class + raise Overcommit::Exceptions::HookLoadError, + "Class #{hook_name} is not a subclass of #{hook_base_class}." + end + + begin + Overcommit::Hook.const_get(@context.hook_class_name). + const_get(hook_name). + new(@config, @context) + rescue LoadError, NameError => e + raise Overcommit::Exceptions::HookLoadError, + "Unable to load hook '#{hook_name}': #{e}", + e.backtrace + end end end end diff --git a/lib/overcommit/hook_loader/built_in_hook_loader.rb b/lib/overcommit/hook_loader/built_in_hook_loader.rb index 5d3be7e5..1b7d3f87 100644 --- a/lib/overcommit/hook_loader/built_in_hook_loader.rb +++ b/lib/overcommit/hook_loader/built_in_hook_loader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::HookLoader # Responsible for loading hooks that ship with Overcommit. class BuiltInHookLoader < Base diff --git a/lib/overcommit/hook_loader/plugin_hook_loader.rb b/lib/overcommit/hook_loader/plugin_hook_loader.rb index df7e06e2..d168a0de 100644 --- a/lib/overcommit/hook_loader/plugin_hook_loader.rb +++ b/lib/overcommit/hook_loader/plugin_hook_loader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'digest' module Overcommit::HookLoader @@ -91,10 +93,10 @@ def run end hook_module.const_set(hook_name, hook_class).new(@config, @context) - rescue LoadError, NameError => error + rescue LoadError, NameError => e raise Overcommit::Exceptions::HookLoadError, - "Unable to load hook '#{hook_name}': #{error}", - error.backtrace + "Unable to load hook '#{hook_name}': #{e}", + e.backtrace end end end diff --git a/lib/overcommit/hook_runner.rb b/lib/overcommit/hook_runner.rb index 505674e5..c7c313ab 100644 --- a/lib/overcommit/hook_runner.rb +++ b/lib/overcommit/hook_runner.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit # Responsible for loading the hooks the repository has configured and running # them, collecting and displaying the results. @@ -92,6 +94,7 @@ def consume loop do hook = @lock.synchronize { @hooks_left.pop } break unless hook + run_hook(hook) end end @@ -127,12 +130,9 @@ def release_slot(hook) slots_released = processors_for_hook(hook) @slots_available += slots_released - if @hooks_left.any? - # Signal once. `wait_for_slot` will perform additional signals if - # there are still slots available. This prevents us from sending out - # useless signals - @resource.signal - end + # Signal every time in case there are threads that are already waiting for + # these slots to be released + @resource.signal end end @@ -160,12 +160,12 @@ def run_hook(hook) # rubocop:disable Metrics/CyclomaticComplexity return if should_skip?(hook) status, output = hook.run_and_transform - rescue Overcommit::Exceptions::MessageProcessingError => ex + rescue Overcommit::Exceptions::MessageProcessingError => e status = :fail - output = ex.message - rescue StandardError => ex + output = e.message + rescue StandardError => e status = :fail - output = "Hook raised unexpected error\n#{ex.message}\n#{ex.backtrace.join("\n")}" + output = "Hook raised unexpected error\n#{e.message}\n#{e.backtrace.join("\n")}" end @failed = true if status == :fail @@ -203,7 +203,7 @@ def load_hooks # Load plugin hooks after so they can subclass existing hooks @hooks += HookLoader::PluginHookLoader.new(@config, @context, @log).load_hooks - rescue LoadError => ex + rescue LoadError => e # Include a more helpful message that will probably save some confusion message = 'A load error occurred. ' + if @config['gemfile'] @@ -213,8 +213,8 @@ def load_hooks end raise Overcommit::Exceptions::HookLoadError, - "#{message}\n#{ex.message}", - ex.backtrace + "#{message}\n#{e.message}", + e.backtrace end end end diff --git a/lib/overcommit/hook_signer.rb b/lib/overcommit/hook_signer.rb index 796585bc..fab99c37 100644 --- a/lib/overcommit/hook_signer.rb +++ b/lib/overcommit/hook_signer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit # Calculates, stores, and retrieves stored signatures of hook plugins. class HookSigner @@ -50,6 +52,7 @@ def hook_path def signable_file?(file) return unless file + sep = Overcommit::OS.windows? ? '\\' : File::SEPARATOR file.start_with?(".#{sep}") || file.start_with?(Overcommit::Utils.repo_root) diff --git a/lib/overcommit/installer.rb b/lib/overcommit/installer.rb index 1cc0b058..79287b30 100644 --- a/lib/overcommit/installer.rb +++ b/lib/overcommit/installer.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'fileutils' module Overcommit # Manages the installation of Overcommit hooks in a git repository. - class Installer # rubocop:disable ClassLength + class Installer # rubocop:disable Metrics/ClassLength TEMPLATE_DIRECTORY = File.join(Overcommit::HOME, 'template-dir') MASTER_HOOK = File.join(TEMPLATE_DIRECTORY, 'hooks', 'overcommit-hook') @@ -172,6 +174,7 @@ def install_starter_config repo_config_file = File.join(@target, Overcommit::CONFIG_FILE_NAME) return if File.exist?(repo_config_file) + FileUtils.cp(File.join(Overcommit::HOME, 'config', 'starter.yml'), repo_config_file) end diff --git a/lib/overcommit/interrupt_handler.rb b/lib/overcommit/interrupt_handler.rb index 96ee2400..4dc085ab 100644 --- a/lib/overcommit/interrupt_handler.rb +++ b/lib/overcommit/interrupt_handler.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'singleton' # Provides a handler for interrupt signals (SIGINT), allowing the application to diff --git a/lib/overcommit/logger.rb b/lib/overcommit/logger.rb index 87cb4e7e..9f5d4248 100644 --- a/lib/overcommit/logger.rb +++ b/lib/overcommit/logger.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit # Encapsulates all communication to an output source. class Logger @@ -29,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. @@ -38,7 +45,7 @@ def log(*args) # Write a line of output if debug mode is enabled. def debug(*args) - color('35', *args) unless ENV.fetch('OVERCOMMIT_DEBUG', '').empty? + color('35', *args) unless ENV.fetch('OVERCOMMIT_DEBUG') { '' }.empty? end # Write a line of output that is intended to be emphasized. diff --git a/lib/overcommit/message_processor.rb b/lib/overcommit/message_processor.rb index eac9fb00..c1bedf96 100644 --- a/lib/overcommit/message_processor.rb +++ b/lib/overcommit/message_processor.rb @@ -8,12 +8,12 @@ module Overcommit # output tuple from an array of {Overcommit::Hook::Message}s, respecting the # configuration settings for the given hook. class MessageProcessor - ERRORS_MODIFIED_HEADER = 'Errors on modified lines:'.freeze - WARNINGS_MODIFIED_HEADER = 'Warnings on modified lines:'.freeze - ERRORS_UNMODIFIED_HEADER = "Errors on lines you didn't modify:".freeze - WARNINGS_UNMODIFIED_HEADER = "Warnings on lines you didn't modify:".freeze - ERRORS_GENERIC_HEADER = 'Errors:'.freeze - WARNINGS_GENERIC_HEADER = 'Warnings:'.freeze + ERRORS_MODIFIED_HEADER = 'Errors on modified lines:' + WARNINGS_MODIFIED_HEADER = 'Warnings on modified lines:' + ERRORS_UNMODIFIED_HEADER = "Errors on lines you didn't modify:" + WARNINGS_UNMODIFIED_HEADER = "Warnings on lines you didn't modify:" + ERRORS_GENERIC_HEADER = 'Errors:' + WARNINGS_GENERIC_HEADER = 'Warnings:' # @param hook [Overcommit::Hook::Base] # @param unmodified_lines_setting [String] how to treat messages on diff --git a/lib/overcommit/os.rb b/lib/overcommit/os.rb index d6b0ce18..7b525a1a 100644 --- a/lib/overcommit/os.rb +++ b/lib/overcommit/os.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rbconfig' module Overcommit diff --git a/lib/overcommit/printer.rb b/lib/overcommit/printer.rb index 4208a4b6..fcb69653 100644 --- a/lib/overcommit/printer.rb +++ b/lib/overcommit/printer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'monitor' module Overcommit @@ -41,15 +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.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. @@ -88,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 386fc6fb..41175fb9 100644 --- a/lib/overcommit/subprocess.rb +++ b/lib/overcommit/subprocess.rb @@ -1,5 +1,8 @@ +# frozen_string_literal: true + require 'childprocess' require 'tempfile' +require 'overcommit/os' module Overcommit # Manages execution of a child process, collecting the exit status and @@ -36,7 +39,7 @@ def spawn(args, options = {}) if options[:input] begin process.io.stdin.puts(options[:input]) - rescue StandardError # rubocop:disable Lint/HandleExceptions + rescue StandardError # Silently ignore if the standard input stream of the spawned # process is closed before we get a chance to write to it. This # happens on JRuby a lot. diff --git a/lib/overcommit/utils.rb b/lib/overcommit/utils.rb index 2961f52e..130046c3 100644 --- a/lib/overcommit/utils.rb +++ b/lib/overcommit/utils.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'pathname' require 'overcommit/os' require 'overcommit/subprocess' @@ -131,7 +133,12 @@ def in_path?(cmd) end # Return the parent command that triggered this hook run + # + # @return [String,nil] the command as a string, if a parent exists. def parent_command + # When run in Docker containers, there may be no parent process. + return if Process.ppid.zero? + if OS.windows? `wmic process where ProcessId=#{Process.ppid} get CommandLine /FORMAT:VALUE`. strip. @@ -172,7 +179,7 @@ def execute(initial_args, options = {}) end result = - if (splittable_args = options.fetch(:args, [])).any? + if (splittable_args = options.fetch(:args) { [] }).any? debug(initial_args.join(' ') + " ... (#{splittable_args.length} splittable args)") Overcommit::CommandSplitter.execute(initial_args, options) else @@ -295,7 +302,7 @@ def matches_path?(pattern, path) # # @param args [Array] def debug(*args) - log.debug(*args) if log + log&.debug(*args) end end end diff --git a/lib/overcommit/utils/file_utils.rb b/lib/overcommit/utils/file_utils.rb index f96cc8ad..cff2ce0b 100644 --- a/lib/overcommit/utils/file_utils.rb +++ b/lib/overcommit/utils/file_utils.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'overcommit/os' require 'overcommit/subprocess' diff --git a/lib/overcommit/utils/messages_utils.rb b/lib/overcommit/utils/messages_utils.rb index caca39d2..31c0f8db 100644 --- a/lib/overcommit/utils/messages_utils.rb +++ b/lib/overcommit/utils/messages_utils.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Overcommit::Utils # Utility to process messages module MessagesUtils @@ -24,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) @@ -50,6 +52,7 @@ def extract_file(match, message) def extract_line(match, message) return unless match.names.include?('line') + Integer(match[:line]) rescue ArgumentError, TypeError raise Overcommit::Exceptions::MessageProcessingError, diff --git a/lib/overcommit/version.rb b/lib/overcommit/version.rb index a9ecf0ad..872c5327 100644 --- a/lib/overcommit/version.rb +++ b/lib/overcommit/version.rb @@ -2,5 +2,5 @@ # Defines the gem version. module Overcommit - VERSION = '0.45.0'.freeze + VERSION = '0.67.1' end diff --git a/libexec/index-tags b/libexec/index-tags index 3c4478e2..51a6d516 100755 --- a/libexec/index-tags +++ b/libexec/index-tags @@ -6,10 +6,12 @@ set -e -trap "rm -f $GIT_DIR/tags.$$" EXIT -err_file=$GIT_DIR/ctags.err -if ctags --tag-relative -Rf$GIT_DIR/tags.$$ --exclude=.git "$@" 2>${err_file}; then - mv $GIT_DIR/tags.$$ $GIT_DIR/tags +dir="`git rev-parse --git-dir`" + +trap "rm -f $dir/tags.$$" EXIT +err_file=$dir/ctags.err +if ctags --tag-relative -Rf$dir/tags.$$ --exclude=.git "$@" 2>${err_file}; then + mv $dir/tags.$$ $dir/tags [ -e ${err_file} ] && rm -f ${err_file} else # Ignore STDERR unless `ctags` returned a non-zero exit code diff --git a/overcommit.gemspec b/overcommit.gemspec index 4c3dd07e..caaa8499 100644 --- a/overcommit.gemspec +++ b/overcommit.gemspec @@ -9,12 +9,16 @@ Gem::Specification.new do |s| s.license = 'MIT' s.summary = 'Git hook manager' s.description = 'Utility to install, configure, and extend Git hooks' - s.authors = ['Brigade Engineering', 'Shane da Silva'] - s.email = ['eng@brigade.com', 'shane.dasilva@brigade.com'] + s.authors = ['Shane da Silva'] + s.email = ['shane@dasilva.io'] s.homepage = Overcommit::REPO_URL s.post_install_message = 'Install hooks by running `overcommit --install` in your Git repository' + s.metadata = { + 'changelog_uri' => 'https://github.com/sds/overcommit/blob/main/CHANGELOG.md' + } + s.require_paths = %w[lib] s.executables = ['overcommit'] @@ -25,8 +29,9 @@ Gem::Specification.new do |s| Dir['libexec/**/*'] + Dir['template-dir/**/*'] - s.required_ruby_version = '>= 2.2' + s.required_ruby_version = '>= 2.6' - s.add_dependency 'childprocess', '~> 0.6', '>= 0.6.3' + s.add_dependency 'childprocess', '>= 0.6.3', '< 6' s.add_dependency 'iniparse', '~> 1.4' + s.add_dependency 'rexml', '>= 3.3.9' end diff --git a/spec/integration/committing_spec.rb b/spec/integration/committing_spec.rb index ca5afcfe..1e1f65a6 100644 --- a/spec/integration/committing_spec.rb +++ b/spec/integration/committing_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'commiting' do @@ -24,7 +26,7 @@ context 'when a hook fails' do before do - `git config --local user.name "John"` + `git config --local user.name ""` end it 'exits with a non-zero status' do @@ -32,6 +34,16 @@ end end + context 'when no hooks fail on single author name' do + before do + `git config --local user.name "John"` + end + + it 'exits successfully' do + subject.status.should == 0 + end + end + context 'when no hooks fail' do before do `git config --local user.name "John Doe"` diff --git a/spec/integration/configuration_signing_spec.rb b/spec/integration/configuration_signing_spec.rb index e7ccb7b7..0b1e0b38 100644 --- a/spec/integration/configuration_signing_spec.rb +++ b/spec/integration/configuration_signing_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'yaml' 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/disable_overcommit_spec.rb b/spec/integration/disable_overcommit_spec.rb index 2bcdaebf..04243cd5 100644 --- a/spec/integration/disable_overcommit_spec.rb +++ b/spec/integration/disable_overcommit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'disabling Overcommit' do diff --git a/spec/integration/gemfile_option_spec.rb b/spec/integration/gemfile_option_spec.rb index 5d29c7c8..a6a6a4ba 100644 --- a/spec/integration/gemfile_option_spec.rb +++ b/spec/integration/gemfile_option_spec.rb @@ -1,96 +1,158 @@ +# frozen_string_literal: true + 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}' - RUBY - - let(:gemspec) { normalize_indent(<<-RUBY) } - Gem::Specification.new do |s| - s.name = 'my_fake_gem' - s.version = '1.0.0' - s.author = 'John Doe' - s.email = 'john.doe@example.com' - s.summary = 'A fake gem' - s.files = [File.join('lib', 'my_fake_gem.rb')] - end - RUBY - - # Specify a hook that depends on an external gem to test Gemfile loading - let(:hook) { normalize_indent(<<-RUBY) } - module Overcommit::Hook::PreCommit - class FakeHook < Base - def run - require 'my_fake_gem' - :pass + context 'given a project that uses a Gemfile' do + let(:repo_root) { File.expand_path(File.join('..', '..'), File.dirname(__FILE__)) } + let(:fake_gem_path) { File.join('lib', 'my_fake_gem') } + + # We point the overcommit gem back to this repo since we can't assume the gem + # has already been installed in a test environment + let(:gemfile) { normalize_indent(<<-RUBY) } + source 'https://rubygems.org' + + gem 'overcommit', path: '#{repo_root}' + gem 'my_fake_gem', path: '#{fake_gem_path}' + gem 'ffi' if Gem.win_platform? # Necessary for test to pass on Windows + RUBY + + let(:gemspec) { normalize_indent(<<-RUBY) } + Gem::Specification.new do |s| + s.name = 'my_fake_gem' + s.version = '1.0.0' + s.author = 'John Doe' + s.license = 'MIT' + s.homepage = 'https://example.com' + s.email = 'john.doe@example.com' + s.summary = 'A fake gem' + s.files = [File.join('lib', 'my_fake_gem.rb')] + end + RUBY + + # Specify a hook that depends on an external gem to test Gemfile loading + let(:hook) { normalize_indent(<<-RUBY) } + module Overcommit::Hook::PreCommit + class FakeHook < Base + def run + require 'my_fake_gem' + :pass + end + end + end + RUBY + + let(:config) { normalize_indent(<<-YAML) } + verify_signatures: false + + CommitMsg: + ALL: + enabled: false + + PreCommit: + ALL: + enabled: false + FakeHook: + enabled: true + requires_files: false + YAML + + around do |example| + repo do + # Since RSpec is being run within a Bundler context we need to clear it + # in order to not taint the test + Bundler.with_unbundled_env do + FileUtils.mkdir_p(File.join(fake_gem_path, 'lib')) + echo(gemspec, File.join(fake_gem_path, 'my_fake_gem.gemspec')) + touch(File.join(fake_gem_path, 'lib', 'my_fake_gem.rb')) + + echo(gemfile, '.overcommit_gems.rb') + `bundle install --gemfile=.overcommit_gems.rb` + + echo(config, '.overcommit.yml') + + # Set BUNDLE_GEMFILE so we load Overcommit from the current repo + ENV['BUNDLE_GEMFILE'] = '.overcommit_gems.rb' + `bundle exec overcommit --install > #{File::NULL}` + FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit')) + echo(hook, File.join('.git-hooks', 'pre_commit', 'fake_hook.rb')) + + Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do + example.run + end end end end - RUBY - - let(:config) { normalize_indent(<<-YAML) } - verify_signatures: false - - CommitMsg: - ALL: - enabled: false - - PreCommit: - ALL: - enabled: false - FakeHook: - enabled: true - requires_files: false - YAML - - around do |example| - repo do - # Since RSpec is being run within a Bundler context we need to clear it - # in order to not taint the test - Bundler.with_clean_env do - FileUtils.mkdir_p(File.join(fake_gem_path, 'lib')) - echo(gemspec, File.join(fake_gem_path, 'my_fake_gem.gemspec')) - touch(File.join(fake_gem_path, 'lib', 'my_fake_gem.rb')) - - echo(gemfile, '.overcommit_gems.rb') - `bundle install --gemfile=.overcommit_gems.rb` + subject { shell(%w[git commit --allow-empty -m Test]) } + + context 'when configuration specifies the gemfile' do + let(:config) { "gemfile: .overcommit_gems.rb\n" + super() } + + it 'runs the hook successfully' do + subject.status.should == 0 + end + end + + context 'when configuration does not specify the gemfile' do + it 'fails to run the hook' do + subject.status.should_not == 0 + end + end + end + + context 'given a project that does not use a Gemfile' do + let(:hook) { normalize_indent(<<-RUBY) } + module Overcommit::Hook::PreCommit + class NoInvalidGemfileHook < Base + def run + if (gemfile = ENV["BUNDLE_GEMFILE"]) + raise unless File.exist?(gemfile) + end + + :pass + end + end + end + RUBY + + let(:config) { normalize_indent(<<-YAML) } + verify_signatures: false + + CommitMsg: + ALL: + enabled: false + + PreCommit: + ALL: + enabled: false + NoInvalidGemfileHook: + enabled: true + requires_files: false + YAML + + around do |example| + repo do echo(config, '.overcommit.yml') - # Set BUNDLE_GEMFILE so we load Overcommit from the current repo - ENV['BUNDLE_GEMFILE'] = '.overcommit_gems.rb' - `bundle exec overcommit --install > #{File::NULL}` + `overcommit --install > #{File::NULL}` FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit')) - echo(hook, File.join('.git-hooks', 'pre_commit', 'fake_hook.rb')) + echo(hook, File.join('.git-hooks', 'pre_commit', 'no_invalid_gemfile_hook.rb')) Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do example.run end end end - end - - subject { shell(%w[git commit --allow-empty -m Test]) } - context 'when configuration specifies the gemfile' do - let(:config) { "gemfile: .overcommit_gems.rb\n" + super() } + subject { shell(%w[git commit --allow-empty -m Test]) } - it 'runs the hook successfully' do - subject.status.should == 0 - end - end + context 'when configuration explicitly sets the gemfile to false' do + let(:config) { "gemfile: false\n" + super() } - context 'when configuration does not specify the gemfile' do - it 'fails to run the hook' do - subject.status.should_not == 0 + it 'runs the hook successfully' do + subject.status.should == 0 + end end end end diff --git a/spec/integration/hook_signing_spec.rb b/spec/integration/hook_signing_spec.rb index 6992fb06..6392a64e 100644 --- a/spec/integration/hook_signing_spec.rb +++ b/spec/integration/hook_signing_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'yaml' diff --git a/spec/integration/installing_overcommit_spec.rb b/spec/integration/installing_overcommit_spec.rb index 965f970d..5c8ebe35 100644 --- a/spec/integration/installing_overcommit_spec.rb +++ b/spec/integration/installing_overcommit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'installing Overcommit' do diff --git a/spec/integration/parallelize_spec.rb b/spec/integration/parallelize_spec.rb index 8c2b8205..471aa7b3 100644 --- a/spec/integration/parallelize_spec.rb +++ b/spec/integration/parallelize_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'timeout' @@ -5,10 +7,16 @@ subject { shell(%w[git commit --allow-empty -m Test]) } let(:config) { <<-YML } + concurrency: 20 CommitMsg: TrailingPeriod: enabled: true parallelize: false + command: ['ruby', '-e', 'sleep 1'] + TextWidth: + enabled: true + parallelize: true + processors: 1 YML around do |example| @@ -19,7 +27,14 @@ end end - it 'does not hang' do - Timeout.timeout(5) { subject } + # 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/protected_branches_spec.rb b/spec/integration/protected_branches_spec.rb index 402be380..115be29d 100644 --- a/spec/integration/protected_branches_spec.rb +++ b/spec/integration/protected_branches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::ProtectedBranches, @@ -22,6 +24,8 @@ enabled: true branches: - protected + - protected_for_destructive_only: + destructive_only: true YML around do |example| diff --git a/spec/integration/resolving_cherry_pick_conflict_spec.rb b/spec/integration/resolving_cherry_pick_conflict_spec.rb index cb9f91f5..2a7f053c 100644 --- a/spec/integration/resolving_cherry_pick_conflict_spec.rb +++ b/spec/integration/resolving_cherry_pick_conflict_spec.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + 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: @@ -37,15 +39,18 @@ end it 'exits with a non-zero status' do + skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR'] subject.status.should_not == 0 end it 'does not remove the CHERRY_PICK_HEAD file' do + skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR'] subject Dir['.git/*'].should include '.git/CHERRY_PICK_HEAD' end it 'keeps the commit message from the cherry-picked commit' do + skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR'] subject File.read(File.join('.git', 'MERGE_MSG')).should include 'Add Branch 2 addition' end diff --git a/spec/integration/resolving_merge_conflict_spec.rb b/spec/integration/resolving_merge_conflict_spec.rb index de1e5301..997dcb09 100644 --- a/spec/integration/resolving_merge_conflict_spec.rb +++ b/spec/integration/resolving_merge_conflict_spec.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + 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/integration/run_flag_spec.rb b/spec/integration/run_flag_spec.rb index 107db518..65284f29 100644 --- a/spec/integration/run_flag_spec.rb +++ b/spec/integration/run_flag_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'overcommit --run' do diff --git a/spec/integration/template_dir_spec.rb b/spec/integration/template_dir_spec.rb index 76e4c1b4..ebcff83c 100644 --- a/spec/integration/template_dir_spec.rb +++ b/spec/integration/template_dir_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'fileutils' diff --git a/spec/overcommit/cli_spec.rb b/spec/overcommit/cli_spec.rb index fa12d359..3d95b09d 100644 --- a/spec/overcommit/cli_spec.rb +++ b/spec/overcommit/cli_spec.rb @@ -1,5 +1,8 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/cli' +require 'overcommit/hook_context/diff' require 'overcommit/hook_context/run_all' describe Overcommit::CLI do @@ -123,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/command_splitter_spec.rb b/spec/overcommit/command_splitter_spec.rb index 2da34ba0..5db2e82f 100644 --- a/spec/overcommit/command_splitter_spec.rb +++ b/spec/overcommit/command_splitter_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::CommandSplitter do diff --git a/spec/overcommit/configuration_loader_spec.rb b/spec/overcommit/configuration_loader_spec.rb index a454f10e..90497201 100644 --- a/spec/overcommit/configuration_loader_spec.rb +++ b/spec/overcommit/configuration_loader_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::ConfigurationLoader do @@ -55,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/configuration_spec.rb b/spec/overcommit/configuration_spec.rb index 4d43188c..d9860400 100644 --- a/spec/overcommit/configuration_spec.rb +++ b/spec/overcommit/configuration_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Configuration do diff --git a/spec/overcommit/configuration_validator_spec.rb b/spec/overcommit/configuration_validator_spec.rb index ed5f0a01..52320633 100644 --- a/spec/overcommit/configuration_validator_spec.rb +++ b/spec/overcommit/configuration_validator_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::ConfigurationValidator do diff --git a/spec/overcommit/default_configuration_spec.rb b/spec/overcommit/default_configuration_spec.rb index 271d37d6..706af872 100644 --- a/spec/overcommit/default_configuration_spec.rb +++ b/spec/overcommit/default_configuration_spec.rb @@ -1,8 +1,14 @@ +# frozen_string_literal: true + require 'spec_helper' 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 acb4619b..9cc51862 100644 --- a/spec/overcommit/git_config_spec.rb +++ b/spec/overcommit/git_config_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::GitConfig do @@ -76,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 10bfbf88..b887415b 100644 --- a/spec/overcommit/git_repo_spec.rb +++ b/spec/overcommit/git_repo_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::GitRepo do @@ -22,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 @@ -148,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') } @@ -176,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 @@ -215,6 +219,20 @@ end end end + + context 'when the git ls-tree command fails for whatever reason' do + before do + result = double('result', success?: false, statuses: [1], stdouts: '', stderrs: '') + allow(Overcommit::Utils). + to receive(:execute). + with(%w[git ls-tree --name-only HEAD], args: []). + and_return(result) + end + + it 'raises' do + expect { subject }.to raise_error Overcommit::Exceptions::Error + end + end end describe '.tracked?' do @@ -266,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 @@ -311,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 @@ -327,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 } @@ -353,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` @@ -361,7 +381,7 @@ it 'returns all submodules that were removed' do subject.size.should == 2 - subject.map(&:path).sort.should == ['sub-repo', 'yet-another-sub-repo'] + subject.map(&:path).sort.should == %w[sub-repo yet-another-sub-repo] end end end diff --git a/spec/overcommit/hook/base_spec.rb b/spec/overcommit/hook/base_spec.rb index c3caf2a2..0c8c7af7 100644 --- a/spec/overcommit/hook/base_spec.rb +++ b/spec/overcommit/hook/base_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::Base do @@ -32,4 +34,157 @@ end end end + + describe '#run?' do + let(:modified_files) { [] } + let(:hook_config) do + { + 'enabled' => enabled, + 'requires_files' => requires_files, + } + end + + before do + config.stub(:for_hook).and_return(hook_config) + context.stub(:modified_files).and_return(modified_files) + end + + subject { hook.run? } + + context 'enabled is true, requires_files is false, modified_files empty' do + let(:enabled) { true } + let(:requires_files) { false } + + it { subject.should == true } + end + + context 'enabled is false, requires_files is false, modified_files empty' do + let(:enabled) { false } + let(:requires_files) { false } + + it { subject.should == false } + end + + context 'enabled is true, requires_files is true, modified_files is not empty' do + let(:enabled) { true } + let(:requires_files) { true } + let(:modified_files) { ['file1'] } + + it { subject.should == true } + end + + context 'enabled is true, requires_files is false, modified_files is not empty' do + let(:enabled) { true } + let(:requires_files) { false } + let(:modified_files) { ['file1'] } + + it { subject.should == true } + end + + context 'with exclude_branches specified' do + let(:current_branch) { 'test-branch' } + let(:hook_config) do + { + 'enabled' => true, + 'requires_files' => false, + 'exclude_branches' => exclude_branches + } + end + + before do + allow(Overcommit::GitRepo). + to receive(:current_branch). + and_return(current_branch) + end + + context 'exclude_branches is nil' do + let(:exclude_branches) { nil } + + it { subject.should == true } + end + + context 'exact match between exclude_branches and current_branch' do + let(:exclude_branches) { ['test-branch'] } + + it { subject.should == false } + end + + context 'partial match between exclude_branches and current_branch' do + let(:exclude_branches) { ['test-*'] } + + it { subject.should == false } + end + + context 'non-match between exclude_branches and current_branch' do + let(:exclude_branches) { ['no-test-*'] } + + it { subject.should == true } + end + end + end + + context '#skip?' do + before do + config.stub(:for_hook).and_return(hook_config) + end + + subject { hook.skip? } + + context 'with skip_if not specified' do + let(:hook_config) do + { 'skip' => skip } + end + + context 'with skip true' do + let(:skip) { true } + + it { subject.should == true } + end + + context 'with skip false' do + let(:skip) { false } + + it { subject.should == false } + end + end + + context 'with skip_if specified' do + before do + result = Overcommit::Subprocess::Result.new(success ? 0 : 1, '', '') + allow(Overcommit::Utils).to receive(:execute).and_return(result) + end + + let(:hook_config) do + { 'skip' => skip, 'skip_if' => ['bash', '-c', '! which my-executable'] } + end + + context 'with skip true and skip_if returning true' do + let(:skip) { true } + let(:success) { true } + + it { subject.should == true } + end + + context 'with skip true and skip_if returning false' do + let(:skip) { true } + let(:success) { false } + + it { subject.should == true } + end + + context 'with skip false and skip_if returning true' do + let(:skip) { false } + let(:success) { true } + + it { subject.should == true } + end + + context 'with skip false and skip_if returning false' do + let(:skip) { false } + let(:success) { false } + + it { subject.should == false } + end + end + end end diff --git a/spec/overcommit/hook/commit_msg/capitalized_subject_spec.rb b/spec/overcommit/hook/commit_msg/capitalized_subject_spec.rb index 0b047401..77adaf69 100644 --- a/spec/overcommit/hook/commit_msg/capitalized_subject_spec.rb +++ b/spec/overcommit/hook/commit_msg/capitalized_subject_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::CapitalizedSubject do diff --git a/spec/overcommit/hook/commit_msg/empty_message_spec.rb b/spec/overcommit/hook/commit_msg/empty_message_spec.rb index 5ccfcb23..94eeb468 100644 --- a/spec/overcommit/hook/commit_msg/empty_message_spec.rb +++ b/spec/overcommit/hook/commit_msg/empty_message_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::EmptyMessage do diff --git a/spec/overcommit/hook/commit_msg/gerrit_change_id_spec.rb b/spec/overcommit/hook/commit_msg/gerrit_change_id_spec.rb index 49d13c07..e9b0577e 100644 --- a/spec/overcommit/hook/commit_msg/gerrit_change_id_spec.rb +++ b/spec/overcommit/hook/commit_msg/gerrit_change_id_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::GerritChangeId do diff --git a/spec/overcommit/hook/commit_msg/hard_tabs_spec.rb b/spec/overcommit/hook/commit_msg/hard_tabs_spec.rb index 68900dd1..2898ff0a 100644 --- a/spec/overcommit/hook/commit_msg/hard_tabs_spec.rb +++ b/spec/overcommit/hook/commit_msg/hard_tabs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::HardTabs do diff --git a/spec/overcommit/hook/commit_msg/message_format_spec.rb b/spec/overcommit/hook/commit_msg/message_format_spec.rb index 21952479..c9dde29b 100644 --- a/spec/overcommit/hook/commit_msg/message_format_spec.rb +++ b/spec/overcommit/hook/commit_msg/message_format_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::MessageFormat do @@ -13,11 +15,11 @@ context 'when pattern is empty' do let(:config) do super().merge(Overcommit::Configuration.new( - 'CommitMsg' => { - 'MessageFormat' => { - 'pattern' => nil - } - } + 'CommitMsg' => { + 'MessageFormat' => { + 'pattern' => nil + } + } )) end @@ -37,4 +39,20 @@ it { should fail_hook expected_message } end + + context 'when multiline message matches the pattern' do + let(:config) do + super().merge(Overcommit::Configuration.new( + 'CommitMsg' => { + 'MessageFormat' => { + 'pattern' => '^Some .* Message$' + } + } + )) + end + + let(:commit_msg) { "Some \n multiline \n Message" } + + it { should pass } + end end diff --git a/spec/overcommit/hook/commit_msg/russian_novel_spec.rb b/spec/overcommit/hook/commit_msg/russian_novel_spec.rb index 599a34a3..6f531a04 100644 --- a/spec/overcommit/hook/commit_msg/russian_novel_spec.rb +++ b/spec/overcommit/hook/commit_msg/russian_novel_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::RussianNovel do diff --git a/spec/overcommit/hook/commit_msg/single_line_subject_spec.rb b/spec/overcommit/hook/commit_msg/single_line_subject_spec.rb index 2d4c5ef5..96ed6e32 100644 --- a/spec/overcommit/hook/commit_msg/single_line_subject_spec.rb +++ b/spec/overcommit/hook/commit_msg/single_line_subject_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::SingleLineSubject do diff --git a/spec/overcommit/hook/commit_msg/spell_check_spec.rb b/spec/overcommit/hook/commit_msg/spell_check_spec.rb index 6479b316..6eae073f 100644 --- a/spec/overcommit/hook/commit_msg/spell_check_spec.rb +++ b/spec/overcommit/hook/commit_msg/spell_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::SpellCheck do diff --git a/spec/overcommit/hook/commit_msg/text_width_spec.rb b/spec/overcommit/hook/commit_msg/text_width_spec.rb index 2c11002b..8829668e 100644 --- a/spec/overcommit/hook/commit_msg/text_width_spec.rb +++ b/spec/overcommit/hook/commit_msg/text_width_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::TextWidth do @@ -96,13 +98,13 @@ context 'when custom lengths are specified' do let(:config) do super().merge(Overcommit::Configuration.new( - 'CommitMsg' => { - 'TextWidth' => { - 'max_subject_width' => 70, - 'min_subject_width' => 4, - 'max_body_width' => 80 - } - } + 'CommitMsg' => { + 'TextWidth' => { + 'max_subject_width' => 70, + 'min_subject_width' => 4, + 'max_body_width' => 80 + } + } )) end diff --git a/spec/overcommit/hook/commit_msg/trailing_period_spec.rb b/spec/overcommit/hook/commit_msg/trailing_period_spec.rb index ea5f91c6..cee57981 100644 --- a/spec/overcommit/hook/commit_msg/trailing_period_spec.rb +++ b/spec/overcommit/hook/commit_msg/trailing_period_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::CommitMsg::TrailingPeriod do diff --git a/spec/overcommit/hook/post_checkout/base_spec.rb b/spec/overcommit/hook/post_checkout/base_spec.rb index ecdb7dbd..ee3a9b2d 100644 --- a/spec/overcommit/hook/post_checkout/base_spec.rb +++ b/spec/overcommit/hook/post_checkout/base_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::Base do diff --git a/spec/overcommit/hook/post_checkout/bower_install_spec.rb b/spec/overcommit/hook/post_checkout/bower_install_spec.rb index 6720a244..1dd0bc2e 100644 --- a/spec/overcommit/hook/post_checkout/bower_install_spec.rb +++ b/spec/overcommit/hook/post_checkout/bower_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::BowerInstall do diff --git a/spec/overcommit/hook/post_checkout/bundle_install_spec.rb b/spec/overcommit/hook/post_checkout/bundle_install_spec.rb index 3c9f46b5..0a375d54 100644 --- a/spec/overcommit/hook/post_checkout/bundle_install_spec.rb +++ b/spec/overcommit/hook/post_checkout/bundle_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::BundleInstall do diff --git a/spec/overcommit/hook/post_checkout/composer_install_spec.rb b/spec/overcommit/hook/post_checkout/composer_install_spec.rb index bf041e6f..1b43b54b 100644 --- a/spec/overcommit/hook/post_checkout/composer_install_spec.rb +++ b/spec/overcommit/hook/post_checkout/composer_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::ComposerInstall do diff --git a/spec/overcommit/hook/post_checkout/index_tags_spec.rb b/spec/overcommit/hook/post_checkout/index_tags_spec.rb index 1476eb6c..93d79db0 100644 --- a/spec/overcommit/hook/post_checkout/index_tags_spec.rb +++ b/spec/overcommit/hook/post_checkout/index_tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::IndexTags do diff --git a/spec/overcommit/hook/post_checkout/npm_install_spec.rb b/spec/overcommit/hook/post_checkout/npm_install_spec.rb index 41842735..00d324ce 100644 --- a/spec/overcommit/hook/post_checkout/npm_install_spec.rb +++ b/spec/overcommit/hook/post_checkout/npm_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::NpmInstall do diff --git a/spec/overcommit/hook/post_checkout/submodule_status_spec.rb b/spec/overcommit/hook/post_checkout/submodule_status_spec.rb index 45078f46..799256b3 100644 --- a/spec/overcommit/hook/post_checkout/submodule_status_spec.rb +++ b/spec/overcommit/hook/post_checkout/submodule_status_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::SubmoduleStatus do diff --git a/spec/overcommit/hook/post_checkout/yarn_install_spec.rb b/spec/overcommit/hook/post_checkout/yarn_install_spec.rb index f243dfc0..95028e77 100644 --- a/spec/overcommit/hook/post_checkout/yarn_install_spec.rb +++ b/spec/overcommit/hook/post_checkout/yarn_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCheckout::YarnInstall do diff --git a/spec/overcommit/hook/post_commit/bower_install_spec.rb b/spec/overcommit/hook/post_commit/bower_install_spec.rb index 7d5417f8..e42b4038 100644 --- a/spec/overcommit/hook/post_commit/bower_install_spec.rb +++ b/spec/overcommit/hook/post_commit/bower_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::BowerInstall do diff --git a/spec/overcommit/hook/post_commit/bundle_install_spec.rb b/spec/overcommit/hook/post_commit/bundle_install_spec.rb index 1445436e..7c4f85db 100644 --- a/spec/overcommit/hook/post_commit/bundle_install_spec.rb +++ b/spec/overcommit/hook/post_commit/bundle_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::BundleInstall do diff --git a/spec/overcommit/hook/post_commit/commitplease_spec.rb b/spec/overcommit/hook/post_commit/commitplease_spec.rb index 25dbfbcf..e6b38bc8 100644 --- a/spec/overcommit/hook/post_commit/commitplease_spec.rb +++ b/spec/overcommit/hook/post_commit/commitplease_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::Commitplease do diff --git a/spec/overcommit/hook/post_commit/composer_install_spec.rb b/spec/overcommit/hook/post_commit/composer_install_spec.rb index 499ec8ea..13abe5e0 100644 --- a/spec/overcommit/hook/post_commit/composer_install_spec.rb +++ b/spec/overcommit/hook/post_commit/composer_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::ComposerInstall do diff --git a/spec/overcommit/hook/post_commit/git_guilt_spec.rb b/spec/overcommit/hook/post_commit/git_guilt_spec.rb index 28c8c9f6..d099b507 100644 --- a/spec/overcommit/hook/post_commit/git_guilt_spec.rb +++ b/spec/overcommit/hook/post_commit/git_guilt_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::GitGuilt do diff --git a/spec/overcommit/hook/post_commit/index_tags_spec.rb b/spec/overcommit/hook/post_commit/index_tags_spec.rb index d5ebcb65..eb3a5e10 100644 --- a/spec/overcommit/hook/post_commit/index_tags_spec.rb +++ b/spec/overcommit/hook/post_commit/index_tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::IndexTags do diff --git a/spec/overcommit/hook/post_commit/npm_install_spec.rb b/spec/overcommit/hook/post_commit/npm_install_spec.rb index 81bbd325..d3405cd4 100644 --- a/spec/overcommit/hook/post_commit/npm_install_spec.rb +++ b/spec/overcommit/hook/post_commit/npm_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::NpmInstall do diff --git a/spec/overcommit/hook/post_commit/submodule_status_spec.rb b/spec/overcommit/hook/post_commit/submodule_status_spec.rb index 2c12d5fe..6d46a4e1 100644 --- a/spec/overcommit/hook/post_commit/submodule_status_spec.rb +++ b/spec/overcommit/hook/post_commit/submodule_status_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::SubmoduleStatus do diff --git a/spec/overcommit/hook/post_commit/yarn_install_spec.rb b/spec/overcommit/hook/post_commit/yarn_install_spec.rb index abdaeae0..dfc77908 100644 --- a/spec/overcommit/hook/post_commit/yarn_install_spec.rb +++ b/spec/overcommit/hook/post_commit/yarn_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostCommit::YarnInstall do diff --git a/spec/overcommit/hook/post_merge/bower_install_spec.rb b/spec/overcommit/hook/post_merge/bower_install_spec.rb index 63f5cbaf..163b5d12 100644 --- a/spec/overcommit/hook/post_merge/bower_install_spec.rb +++ b/spec/overcommit/hook/post_merge/bower_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::BowerInstall do diff --git a/spec/overcommit/hook/post_merge/bundle_install_spec.rb b/spec/overcommit/hook/post_merge/bundle_install_spec.rb index ed463dac..1c2ad3b2 100644 --- a/spec/overcommit/hook/post_merge/bundle_install_spec.rb +++ b/spec/overcommit/hook/post_merge/bundle_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::BundleInstall do diff --git a/spec/overcommit/hook/post_merge/composer_install_spec.rb b/spec/overcommit/hook/post_merge/composer_install_spec.rb index fb092bc2..94d00083 100644 --- a/spec/overcommit/hook/post_merge/composer_install_spec.rb +++ b/spec/overcommit/hook/post_merge/composer_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::ComposerInstall do diff --git a/spec/overcommit/hook/post_merge/index_tags_spec.rb b/spec/overcommit/hook/post_merge/index_tags_spec.rb index e897ddf4..2ac6fad1 100644 --- a/spec/overcommit/hook/post_merge/index_tags_spec.rb +++ b/spec/overcommit/hook/post_merge/index_tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::IndexTags do diff --git a/spec/overcommit/hook/post_merge/npm_install_spec.rb b/spec/overcommit/hook/post_merge/npm_install_spec.rb index 9f63d717..017d7a36 100644 --- a/spec/overcommit/hook/post_merge/npm_install_spec.rb +++ b/spec/overcommit/hook/post_merge/npm_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::NpmInstall do diff --git a/spec/overcommit/hook/post_merge/submodule_status_spec.rb b/spec/overcommit/hook/post_merge/submodule_status_spec.rb index 245fe6c1..c0dd2efc 100644 --- a/spec/overcommit/hook/post_merge/submodule_status_spec.rb +++ b/spec/overcommit/hook/post_merge/submodule_status_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::SubmoduleStatus do diff --git a/spec/overcommit/hook/post_merge/yarn_install_spec.rb b/spec/overcommit/hook/post_merge/yarn_install_spec.rb index d40fa95a..b9648712 100644 --- a/spec/overcommit/hook/post_merge/yarn_install_spec.rb +++ b/spec/overcommit/hook/post_merge/yarn_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::YarnInstall do diff --git a/spec/overcommit/hook/post_rewrite/bower_install_spec.rb b/spec/overcommit/hook/post_rewrite/bower_install_spec.rb index a3a1a1b1..fe2ebd35 100644 --- a/spec/overcommit/hook/post_rewrite/bower_install_spec.rb +++ b/spec/overcommit/hook/post_rewrite/bower_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::BowerInstall do diff --git a/spec/overcommit/hook/post_rewrite/bundle_install_spec.rb b/spec/overcommit/hook/post_rewrite/bundle_install_spec.rb index b3703ccb..93d05773 100644 --- a/spec/overcommit/hook/post_rewrite/bundle_install_spec.rb +++ b/spec/overcommit/hook/post_rewrite/bundle_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::BundleInstall do diff --git a/spec/overcommit/hook/post_rewrite/composer_install_spec.rb b/spec/overcommit/hook/post_rewrite/composer_install_spec.rb index fd44d43c..30c2091f 100644 --- a/spec/overcommit/hook/post_rewrite/composer_install_spec.rb +++ b/spec/overcommit/hook/post_rewrite/composer_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::ComposerInstall do diff --git a/spec/overcommit/hook/post_rewrite/index_tags_spec.rb b/spec/overcommit/hook/post_rewrite/index_tags_spec.rb index e897ddf4..2ac6fad1 100644 --- a/spec/overcommit/hook/post_rewrite/index_tags_spec.rb +++ b/spec/overcommit/hook/post_rewrite/index_tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostMerge::IndexTags do diff --git a/spec/overcommit/hook/post_rewrite/npm_install_spec.rb b/spec/overcommit/hook/post_rewrite/npm_install_spec.rb index 2af2291f..24b72a5a 100644 --- a/spec/overcommit/hook/post_rewrite/npm_install_spec.rb +++ b/spec/overcommit/hook/post_rewrite/npm_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::NpmInstall do diff --git a/spec/overcommit/hook/post_rewrite/submodule_status_spec.rb b/spec/overcommit/hook/post_rewrite/submodule_status_spec.rb index c783c029..a5770745 100644 --- a/spec/overcommit/hook/post_rewrite/submodule_status_spec.rb +++ b/spec/overcommit/hook/post_rewrite/submodule_status_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::SubmoduleStatus do diff --git a/spec/overcommit/hook/post_rewrite/yarn_install_spec.rb b/spec/overcommit/hook/post_rewrite/yarn_install_spec.rb index d2c57e0d..9774d1ab 100644 --- a/spec/overcommit/hook/post_rewrite/yarn_install_spec.rb +++ b/spec/overcommit/hook/post_rewrite/yarn_install_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PostRewrite::YarnInstall do diff --git a/spec/overcommit/hook/pre_commit/author_email_spec.rb b/spec/overcommit/hook/pre_commit/author_email_spec.rb index 97d24397..8e0dd768 100644 --- a/spec/overcommit/hook/pre_commit/author_email_spec.rb +++ b/spec/overcommit/hook/pre_commit/author_email_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::AuthorEmail do @@ -28,11 +30,11 @@ context 'when a custom pattern is specified' do let(:config) do super().merge(Overcommit::Configuration.new( - 'PreCommit' => { - 'AuthorEmail' => { - 'pattern' => '^[^@]+@brigade\.com$' - } - } + 'PreCommit' => { + 'AuthorEmail' => { + 'pattern' => '^[^@]+@brigade\.com$' + } + } )) end diff --git a/spec/overcommit/hook/pre_commit/author_name_spec.rb b/spec/overcommit/hook/pre_commit/author_name_spec.rb index 0981f1be..1ed0502f 100644 --- a/spec/overcommit/hook/pre_commit/author_name_spec.rb +++ b/spec/overcommit/hook/pre_commit/author_name_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::AuthorName do @@ -16,7 +18,7 @@ context 'when user has only a first name' do let(:name) { 'John' } - it { should fail_hook } + it { should pass } end context 'when user has first and last name' do diff --git a/spec/overcommit/hook/pre_commit/berksfile_check_spec.rb b/spec/overcommit/hook/pre_commit/berksfile_check_spec.rb index a6c24355..58d82517 100644 --- a/spec/overcommit/hook/pre_commit/berksfile_check_spec.rb +++ b/spec/overcommit/hook/pre_commit/berksfile_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::BerksfileCheck do diff --git a/spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb b/spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb index b3058aae..84ce890e 100644 --- a/spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb +++ b/spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::BrokenSymlinks do diff --git a/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb b/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb index 7fa2fc89..b8360e2e 100644 --- a/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb +++ b/spec/overcommit/hook/pre_commit/bundle_audit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::BundleAudit do @@ -52,7 +54,7 @@ Title: Rest-Client Gem for Ruby logs password information in plaintext Solution: upgrade to >= 1.7.3 Vulnerabilities found! -MSG + MSG ) end @@ -65,7 +67,7 @@ stdout: <<-MSG Insecure Source URI found: git://github.com/xxx/overcommit.git Vulnerabilities found! -MSG + MSG ) end diff --git a/spec/overcommit/hook/pre_commit/bundle_check_spec.rb b/spec/overcommit/hook/pre_commit/bundle_check_spec.rb index bc11a065..b8a471a1 100644 --- a/spec/overcommit/hook/pre_commit/bundle_check_spec.rb +++ b/spec/overcommit/hook/pre_commit/bundle_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::BundleCheck do diff --git a/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb b/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb index 3ae32a4c..a734b3dc 100644 --- a/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb +++ b/spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::BundleOutdated do @@ -47,7 +49,7 @@ aws-sdk-resources (newest 2.3.3, installed 2.3.1) config (newest 1.1.1, installed 1.1.0) ruby_parser (newest 3.8.2, installed 3.8.1) -MSG + MSG ) end @@ -58,7 +60,7 @@ let(:result) do double(stdout: <<-MSG Warning: the running version of Bundler is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bundler by running `gem install bundler`. -MSG + MSG ) end diff --git a/spec/overcommit/hook/pre_commit/case_conflicts_spec.rb b/spec/overcommit/hook/pre_commit/case_conflicts_spec.rb index 0d7281d0..1ce157d5 100644 --- a/spec/overcommit/hook/pre_commit/case_conflicts_spec.rb +++ b/spec/overcommit/hook/pre_commit/case_conflicts_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::CaseConflicts do diff --git a/spec/overcommit/hook/pre_commit/chamber_compare_spec.rb b/spec/overcommit/hook/pre_commit/chamber_compare_spec.rb index a9d76712..de9fffa2 100644 --- a/spec/overcommit/hook/pre_commit/chamber_compare_spec.rb +++ b/spec/overcommit/hook/pre_commit/chamber_compare_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ChamberCompare do diff --git a/spec/overcommit/hook/pre_commit/chamber_security_spec.rb b/spec/overcommit/hook/pre_commit/chamber_security_spec.rb index ca4c9290..7badd189 100644 --- a/spec/overcommit/hook/pre_commit/chamber_security_spec.rb +++ b/spec/overcommit/hook/pre_commit/chamber_security_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ChamberSecurity do diff --git a/spec/overcommit/hook/pre_commit/chamber_verification_spec.rb b/spec/overcommit/hook/pre_commit/chamber_verification_spec.rb index 1dee1de9..a49e2a98 100644 --- a/spec/overcommit/hook/pre_commit/chamber_verification_spec.rb +++ b/spec/overcommit/hook/pre_commit/chamber_verification_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ChamberVerification do @@ -31,7 +33,7 @@ it { should pass } end - context 'when chamber exits unsucessfully via standard out' do + context 'when chamber exits unsuccessfully via standard out' do before do result = double('result') result.stub(:stdout).and_return('Some error message') @@ -42,7 +44,7 @@ it { should warn } end - context 'when chamber exits unsucessfully via standard error' do + context 'when chamber exits unsuccessfully via standard error' do before do result = double('result') result.stub(:stdout).and_return('') diff --git a/spec/overcommit/hook/pre_commit/code_spell_check_spec.rb b/spec/overcommit/hook/pre_commit/code_spell_check_spec.rb new file mode 100644 index 00000000..85a54b38 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/code_spell_check_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::CodeSpellCheck 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 code-spell-check exists successfully' do + before do + result = double('result') + result.stub(:success?).and_return(true) + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return('') + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when code-spell-check exists unsuccessfully via standard error' do + before do + result = double('result') + result.stub(:success?).and_return(false) + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return( + "file1.rb:35: inkorrectspelling\n✗ Errors in code spellchecking" + ) + subject.stub(:execute).and_return(result) + end + + it { should fail_hook } + end +end diff --git a/spec/overcommit/hook/pre_commit/coffee_lint_spec.rb b/spec/overcommit/hook/pre_commit/coffee_lint_spec.rb index 44e00aca..5ece05f9 100644 --- a/spec/overcommit/hook/pre_commit/coffee_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/coffee_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::CoffeeLint do diff --git a/spec/overcommit/hook/pre_commit/cook_style_spec.rb b/spec/overcommit/hook/pre_commit/cook_style_spec.rb new file mode 100644 index 00000000..a7dece87 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/cook_style_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::CookStyle 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 cookstyle exits successfully' do + let(:result) { double('result') } + + before do + result.stub(success?: true, stderr: '', stdout: '') + subject.stub(:execute).and_return(result) + end + + it { should pass } + + context 'and it printed warnings to stderr' do + before do + result.stub(:stderr).and_return(normalize_indent(<<-MSG)) + warning: parser/current is loading parser/ruby21, which recognizes + warning: 2.1.8-compliant syntax, but you are running 2.1.1. + warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. + MSG + end + + it { should pass } + end + end + + context 'when cookstyle 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 a warning' do + before do + result.stub(:stdout).and_return([ + 'file1.rb:1:1: W: Useless assignment to variable - my_var.', + ].join("\n")) + result.stub(:stderr).and_return('') + end + + it { should warn } + + context 'and it printed warnings to stderr' do + before do + result.stub(:stderr).and_return(normalize_indent(<<-MSG)) + warning: parser/current is loading parser/ruby21, which recognizes + warning: 2.1.8-compliant syntax, but you are running 2.1.1. + warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. + MSG + end + + it { should warn } + end + end + + context 'and it reports an error' do + before do + result.stub(:stdout).and_return([ + 'file1.rb:1:1: C: Missing top-level class documentation', + ].join("\n")) + result.stub(:stderr).and_return('') + end + + it { should fail_hook } + + context 'and it printed warnings to stderr' do + before do + result.stub(:stderr).and_return(normalize_indent(<<-MSG)) + warning: parser/current is loading parser/ruby21, which recognizes + warning: 2.1.8-compliant syntax, but you are running 2.1.1. + warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. + MSG + end + + it { should fail_hook } + end + end + + context 'when a generic error message is written to stderr' do + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return([ + 'Could not find cookstyle in any of the sources' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/credo_spec.rb b/spec/overcommit/hook/pre_commit/credo_spec.rb index c6de2514..0a97a0d6 100644 --- a/spec/overcommit/hook/pre_commit/credo_spec.rb +++ b/spec/overcommit/hook/pre_commit/credo_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Credo do diff --git a/spec/overcommit/hook/pre_commit/css_lint_spec.rb b/spec/overcommit/hook/pre_commit/css_lint_spec.rb index 1f7133ed..4d2afc4d 100644 --- a/spec/overcommit/hook/pre_commit/css_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/css_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::CssLint do diff --git a/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb b/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb new file mode 100644 index 00000000..1013c503 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::DartAnalyzer do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + before do + subject.stub(:applicable_files).and_return(%w[file1.dart file2.dart]) + end + + context 'when dartanalyzer exits successfully' do + before do + result = double('result') + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when dartanalyzer exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'and it reports an error' do + before do + result.stub(:stdout).and_return([ + 'Analyzing file1.dart...', + 'error • message_ommitted • lib/file1.dart:35:3 • rule', + 'Analyzing file2.dart...', + 'hint • message_ommitted • lib/file2.dart:100:13 • rule', + 'info • message_ommitted • lib/file2.dart:113:16 • rule', + '3 lints found.' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/dogma_spec.rb b/spec/overcommit/hook/pre_commit/dogma_spec.rb index 0e162ad3..53b5cd62 100644 --- a/spec/overcommit/hook/pre_commit/dogma_spec.rb +++ b/spec/overcommit/hook/pre_commit/dogma_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Dogma do diff --git a/spec/overcommit/hook/pre_commit/erb_lint_spec.rb b/spec/overcommit/hook/pre_commit/erb_lint_spec.rb new file mode 100644 index 00000000..e9272bfe --- /dev/null +++ b/spec/overcommit/hook/pre_commit/erb_lint_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::ErbLint do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + before do + subject.stub(:applicable_files).and_return(%w[file1.html.erb file2.html.erb]) + end + + context 'when erblint exits successfully' do + before do + result = double('result') + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when erblint exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'and it reports an error' do + before do + result.stub(:stdout).and_return(<<-MSG) +Linting 1 files with 14 linters... + +erb interpolation with '<%= (...).html_safe %>' in this context is never safe +In file: app/views/posts/show.html.erb:10 + MSG + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/es_lint_spec.rb b/spec/overcommit/hook/pre_commit/es_lint_spec.rb index e6cfd3fb..39de7d27 100644 --- a/spec/overcommit/hook/pre_commit/es_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/es_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::EsLint do @@ -50,6 +52,18 @@ it { should warn } end + + context 'and it doesnt count false positives error messages' do + before do + result.stub(:stdout).and_return([ + '$ yarn eslint --quiet --format=compact /app/project/Error.ts', + '$ /app/project/node_modules/.bin/eslint --quiet --format=compact /app/project/Error.ts', + '', + ].join("\n")) + end + + it { should pass } + end end context 'when eslint exits unsucessfully' do diff --git a/spec/overcommit/hook/pre_commit/execute_permissions_spec.rb b/spec/overcommit/hook/pre_commit/execute_permissions_spec.rb index 9e8da223..3e33b946 100644 --- a/spec/overcommit/hook/pre_commit/execute_permissions_spec.rb +++ b/spec/overcommit/hook/pre_commit/execute_permissions_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ExecutePermissions do diff --git a/spec/overcommit/hook/pre_commit/fasterer_spec.rb b/spec/overcommit/hook/pre_commit/fasterer_spec.rb index c3fa6c85..afb08791 100644 --- a/spec/overcommit/hook/pre_commit/fasterer_spec.rb +++ b/spec/overcommit/hook/pre_commit/fasterer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Fasterer do @@ -34,7 +36,7 @@ spec/models/blog_spec.rb Using each_with_index is slower than while loop. Occurred at lines: 12. 2 files inspected, 0 offense detected - MSG + MSG ) end @@ -49,7 +51,7 @@ spec/models/product_spec.rb Using each_with_index is slower than while loop. Occurred at lines: 52. 1 files inspected, 1 offense detected - MSG + MSG ) end diff --git a/spec/overcommit/hook/pre_commit/file_size_spec.rb b/spec/overcommit/hook/pre_commit/file_size_spec.rb new file mode 100644 index 00000000..7fb75898 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/file_size_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::FileSize do + let(:config) do + Overcommit::ConfigurationLoader.default_configuration.merge( + Overcommit::Configuration.new( + 'PreCommit' => { + 'FileSize' => { + 'size_limit_bytes' => 10 + } + } + ) + ) + end + + let(:context) { double('context') } + subject { described_class.new(config, context) } + let(:staged_file) { 'filename.txt' } + + before do + subject.stub(:applicable_files).and_return([staged_file]) + end + + around do |example| + repo do + File.open(staged_file, 'w') { |f| f.write(contents) } + `git add "#{staged_file}" > #{File::NULL} 2>&1` + example.run + end + end + + context 'when a big file is committed' do + let(:contents) { 'longer than 10 bytes' } + + it { should fail_hook } + end + + context 'when a small file is committed' do + let(:contents) { 'short' } + + it { should pass } + end + + context 'when a file is removed' do + let(:contents) { 'anything' } + before do + `git commit -m "Add file"` + `git rm "#{staged_file}"` + end + + it 'should not raise an exception' do + lambda { should pass }.should_not raise_error + end + end +end diff --git a/spec/overcommit/hook/pre_commit/fix_me_spec.rb b/spec/overcommit/hook/pre_commit/fix_me_spec.rb index 5fb8b8df..262c3294 100644 --- a/spec/overcommit/hook/pre_commit/fix_me_spec.rb +++ b/spec/overcommit/hook/pre_commit/fix_me_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::FixMe do diff --git a/spec/overcommit/hook/pre_commit/flay_spec.rb b/spec/overcommit/hook/pre_commit/flay_spec.rb index e2b032da..2a5d8140 100644 --- a/spec/overcommit/hook/pre_commit/flay_spec.rb +++ b/spec/overcommit/hook/pre_commit/flay_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Flay do @@ -36,7 +38,7 @@ app/whatever21.rb:105 app/whatever22.rb:76 -MSG + MSG ) end @@ -49,7 +51,7 @@ success?: false, stdout: <<-MSG Total score (lower is better) = 0 -MSG + MSG ) end diff --git a/spec/overcommit/hook/pre_commit/foodcritic_spec.rb b/spec/overcommit/hook/pre_commit/foodcritic_spec.rb index 7fc2418a..2bbe3df2 100644 --- a/spec/overcommit/hook/pre_commit/foodcritic_spec.rb +++ b/spec/overcommit/hook/pre_commit/foodcritic_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Foodcritic do diff --git a/spec/overcommit/hook/pre_commit/forbidden_branches_spec.rb b/spec/overcommit/hook/pre_commit/forbidden_branches_spec.rb index 3d068bda..abf6d4b3 100644 --- a/spec/overcommit/hook/pre_commit/forbidden_branches_spec.rb +++ b/spec/overcommit/hook/pre_commit/forbidden_branches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ForbiddenBranches do diff --git a/spec/overcommit/hook/pre_commit/go_fmt_spec.rb b/spec/overcommit/hook/pre_commit/go_fmt_spec.rb new file mode 100644 index 00000000..496ca097 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/go_fmt_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::GoFmt do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + let(:files) do + %w[ + pkg1/file1.go + pkg1/file2.go + pkg2/file1.go + file1.go + ] + end + before do + subject.stub(:applicable_files).and_return(files) + end + + context 'when go fmt exits successfully' do + let(:result) { double('result') } + + before do + result.stub(success?: true, stderr: '', stdout: '') + subject.stub(:execute).and_return(result) + end + + it 'executes go fmt for each file' do + files.each do |file| + expect(subject).to receive(:execute).with(subject.command, args: [file]).once + end + subject.run + end + + it 'passes' do + expect(subject).to pass + end + end + + context 'when go fmt exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'when go fmt returns an error to stdout' do + let(:error_message) { 'some go fmt error' } + + before do + result.stub(:stdout).and_return(error_message) + result.stub(:stderr).and_return('') + end + + it 'executes go fmt for each file' do + files.each do |file| + expect(subject).to receive(:execute).with(subject.command, args: [file]).once + end + subject.run + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns errors' do + message = subject.run.last + expect(message).to eq Array.new(files.count, error_message).join("\n") + end + end + + context 'when fo fmt returns an error to stderr' do + let(:error_message) { 'go: command not found' } + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return(error_message) + end + + it 'executes go fmt for each file' do + files.each do |file| + expect(subject).to receive(:execute).with(subject.command, args: [file]).once + end + subject.run + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq Array.new(files.count, error_message).join("\n") + end + end + end +end diff --git a/spec/overcommit/hook/pre_commit/go_lint_spec.rb b/spec/overcommit/hook/pre_commit/go_lint_spec.rb index b00bb8c6..399a65f2 100644 --- a/spec/overcommit/hook/pre_commit/go_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/go_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::GoLint do diff --git a/spec/overcommit/hook/pre_commit/go_vet_spec.rb b/spec/overcommit/hook/pre_commit/go_vet_spec.rb index 635039b2..69b89c4b 100644 --- a/spec/overcommit/hook/pre_commit/go_vet_spec.rb +++ b/spec/overcommit/hook/pre_commit/go_vet_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::GoVet do diff --git a/spec/overcommit/hook/pre_commit/golangci_lint_spec.rb b/spec/overcommit/hook/pre_commit/golangci_lint_spec.rb new file mode 100644 index 00000000..45e4a7e3 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/golangci_lint_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::GolangciLint do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + let(:files) do + %w[ + pkg1/file1.go + pkg1/file2.go + pkg2/file1.go + file1.go + ] + end + let(:packages) { %w[pkg1 pkg2 .] } + before do + subject.stub(:applicable_files).and_return(files) + end + + context 'when golangci-lint exits successfully' do + let(:result) { double('result') } + + before do + result.stub(success?: true, stderr: '', stdout: '') + subject.stub(:execute).and_return(result) + end + + it 'passes packages to golangci-lint' do + expect(subject).to receive(:execute).with(subject.command, args: packages) + subject.run + end + + it 'passes' do + expect(subject).to pass + end + end + + context 'when golangci-lint exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'when golangci-lint returns an error' do + let(:error_message) do + 'pkg1/file1.go:8:6: exported type `Test` should have comment or be unexported (golint)' + end + + before do + result.stub(:stdout).and_return(error_message) + result.stub(:stderr).and_return('') + end + + it 'passes packages to golangci-lint' do + expect(subject).to receive(:execute).with(subject.command, args: packages) + subject.run + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message.file).to eq 'pkg1/file1.go' + expect(message.line).to eq 8 + expect(message.content).to eq error_message + end + end + + context 'when a generic error message is written to stderr' do + let(:error_message) { 'golangci-lint: command not found' } + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return(error_message) + end + + it 'passes packages to golangci-lint' do + expect(subject).to receive(:execute).with(subject.command, args: packages) + subject.run + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq error_message + end + end + end +end diff --git a/spec/overcommit/hook/pre_commit/hadolint_spec.rb b/spec/overcommit/hook/pre_commit/hadolint_spec.rb index c8050f70..ad756f30 100644 --- a/spec/overcommit/hook/pre_commit/hadolint_spec.rb +++ b/spec/overcommit/hook/pre_commit/hadolint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Hadolint do @@ -29,7 +31,7 @@ success?: false, stdout: <<-MSG Dockerfile:5 DL3015 Avoid additional packages by specifying `--no-install-recommends` - MSG + MSG ) end let(:result_dockerfile_web) do @@ -37,7 +39,7 @@ success?: false, stdout: <<-MSG Dockerfile.web:13 DL3020 Use COPY instead of ADD for files and folders - MSG + MSG ) end @@ -50,7 +52,7 @@ success?: false, stdout: <<-MSG Dockerfile:11 SC2086 Double quote to prevent globbing and word splitting. - MSG + MSG ) end let(:result_dockerfile_web) do diff --git a/spec/overcommit/hook/pre_commit/haml_lint_spec.rb b/spec/overcommit/hook/pre_commit/haml_lint_spec.rb index adadb799..9eb15ee9 100644 --- a/spec/overcommit/hook/pre_commit/haml_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/haml_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::HamlLint do diff --git a/spec/overcommit/hook/pre_commit/hard_tabs_spec.rb b/spec/overcommit/hook/pre_commit/hard_tabs_spec.rb index 1bb662a9..3bee937d 100644 --- a/spec/overcommit/hook/pre_commit/hard_tabs_spec.rb +++ b/spec/overcommit/hook/pre_commit/hard_tabs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::HardTabs do diff --git a/spec/overcommit/hook/pre_commit/hlint_spec.rb b/spec/overcommit/hook/pre_commit/hlint_spec.rb index 08183e18..7a87c427 100644 --- a/spec/overcommit/hook/pre_commit/hlint_spec.rb +++ b/spec/overcommit/hook/pre_commit/hlint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Hlint do diff --git a/spec/overcommit/hook/pre_commit/html_hint_spec.rb b/spec/overcommit/hook/pre_commit/html_hint_spec.rb index b96d8e14..66146d80 100644 --- a/spec/overcommit/hook/pre_commit/html_hint_spec.rb +++ b/spec/overcommit/hook/pre_commit/html_hint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::HtmlHint do diff --git a/spec/overcommit/hook/pre_commit/html_tidy_spec.rb b/spec/overcommit/hook/pre_commit/html_tidy_spec.rb index 389d38d0..77754bca 100644 --- a/spec/overcommit/hook/pre_commit/html_tidy_spec.rb +++ b/spec/overcommit/hook/pre_commit/html_tidy_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::HtmlTidy do diff --git a/spec/overcommit/hook/pre_commit/image_optim_spec.rb b/spec/overcommit/hook/pre_commit/image_optim_spec.rb index 589344fb..7ac17ba5 100644 --- a/spec/overcommit/hook/pre_commit/image_optim_spec.rb +++ b/spec/overcommit/hook/pre_commit/image_optim_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ImageOptim do diff --git a/spec/overcommit/hook/pre_commit/java_checkstyle_spec.rb b/spec/overcommit/hook/pre_commit/java_checkstyle_spec.rb index 072db68f..e06c724b 100644 --- a/spec/overcommit/hook/pre_commit/java_checkstyle_spec.rb +++ b/spec/overcommit/hook/pre_commit/java_checkstyle_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::JavaCheckstyle do diff --git a/spec/overcommit/hook/pre_commit/js_hint_spec.rb b/spec/overcommit/hook/pre_commit/js_hint_spec.rb index 22c64eb5..4666c1af 100644 --- a/spec/overcommit/hook/pre_commit/js_hint_spec.rb +++ b/spec/overcommit/hook/pre_commit/js_hint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::JsHint do diff --git a/spec/overcommit/hook/pre_commit/js_lint_spec.rb b/spec/overcommit/hook/pre_commit/js_lint_spec.rb index 3fde219a..3d85b80c 100644 --- a/spec/overcommit/hook/pre_commit/js_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/js_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::JsLint do diff --git a/spec/overcommit/hook/pre_commit/jscs_spec.rb b/spec/overcommit/hook/pre_commit/jscs_spec.rb index 9edd17de..4cc56dec 100644 --- a/spec/overcommit/hook/pre_commit/jscs_spec.rb +++ b/spec/overcommit/hook/pre_commit/jscs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Jscs do @@ -22,7 +24,7 @@ it { should fail_hook } end - context 'when jscs exits unsucessfully with status code 2' do + context 'when jscs exits unsuccessfully with status code 2' do let(:result) { double('result') } before do diff --git a/spec/overcommit/hook/pre_commit/jsl_spec.rb b/spec/overcommit/hook/pre_commit/jsl_spec.rb index c8eb53e2..51fdd998 100644 --- a/spec/overcommit/hook/pre_commit/jsl_spec.rb +++ b/spec/overcommit/hook/pre_commit/jsl_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Jsl do diff --git a/spec/overcommit/hook/pre_commit/json_syntax_spec.rb b/spec/overcommit/hook/pre_commit/json_syntax_spec.rb index f456c081..0ee4a57e 100644 --- a/spec/overcommit/hook/pre_commit/json_syntax_spec.rb +++ b/spec/overcommit/hook/pre_commit/json_syntax_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'json' diff --git a/spec/overcommit/hook/pre_commit/kt_lint_spec.rb b/spec/overcommit/hook/pre_commit/kt_lint_spec.rb new file mode 100644 index 00000000..6ea89467 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/kt_lint_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::KtLint 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.kt file2.kt]) + end + + context 'when KtLint 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 KtLint 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([ + 'file1.kt:12:10: error message' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/license_finder_spec.rb b/spec/overcommit/hook/pre_commit/license_finder_spec.rb index a1c9267e..ef0c8f5a 100644 --- a/spec/overcommit/hook/pre_commit/license_finder_spec.rb +++ b/spec/overcommit/hook/pre_commit/license_finder_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::LicenseFinder do diff --git a/spec/overcommit/hook/pre_commit/license_header_spec.rb b/spec/overcommit/hook/pre_commit/license_header_spec.rb index 06cef9dc..ce454fab 100644 --- a/spec/overcommit/hook/pre_commit/license_header_spec.rb +++ b/spec/overcommit/hook/pre_commit/license_header_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::LicenseHeader do diff --git a/spec/overcommit/hook/pre_commit/line_endings_spec.rb b/spec/overcommit/hook/pre_commit/line_endings_spec.rb index 8e2d4599..bcb576d9 100644 --- a/spec/overcommit/hook/pre_commit/line_endings_spec.rb +++ b/spec/overcommit/hook/pre_commit/line_endings_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::LineEndings do diff --git a/spec/overcommit/hook/pre_commit/local_paths_in_gemfile_spec.rb b/spec/overcommit/hook/pre_commit/local_paths_in_gemfile_spec.rb index f6c3755c..ff0109ff 100644 --- a/spec/overcommit/hook/pre_commit/local_paths_in_gemfile_spec.rb +++ b/spec/overcommit/hook/pre_commit/local_paths_in_gemfile_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::LocalPathsInGemfile do diff --git a/spec/overcommit/hook/pre_commit/mdl_spec.rb b/spec/overcommit/hook/pre_commit/mdl_spec.rb index c7da4478..119f9a00 100644 --- a/spec/overcommit/hook/pre_commit/mdl_spec.rb +++ b/spec/overcommit/hook/pre_commit/mdl_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Mdl do @@ -23,12 +25,19 @@ context 'when mdl exits unsuccessfully' do let(:success) { false } + let(:message) { subject.run.last } context 'and it reports an error' do - let(:stdout) { 'file1.md:1: MD013 Line length' } + let(:stdout) do + '[{"filename":"file1.md","line":1,"rule":"MD013","aliases":["line-length"],'\ + '"description":"Line length"}]' + end let(:stderr) { '' } it { should fail_hook } + it { expect(message.file).to eq 'file1.md' } + it { expect(message.line).to eq 1 } + it { expect(message.content).to eq 'file1.md:1 MD013 Line length' } end context 'when there is an error running mdl' do @@ -36,6 +45,7 @@ let(:stderr) { 'Some runtime error' } it { should fail_hook } + it { expect(message).to eq 'Some runtime error' } end end end diff --git a/spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb b/spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb index 8d1747e0..f7790fe9 100644 --- a/spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb +++ b/spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::MergeConflicts do diff --git a/spec/overcommit/hook/pre_commit/mix_format_spec.rb b/spec/overcommit/hook/pre_commit/mix_format_spec.rb new file mode 100644 index 00000000..9acc50ed --- /dev/null +++ b/spec/overcommit/hook/pre_commit/mix_format_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::MixFormat do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + before do + subject.stub(:applicable_files).and_return(%w[file1.ex file2.exs]) + end + + context 'when mix format exits successfully' do + before do + result = double('result') + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when mix format exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'and it reports an error' do + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return([ + '** (Mix) mix format failed due to --check-formatted.', + 'The following files are not formatted:', + '', + ' * lib/file1.ex', + ' * lib/file2.ex' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/nginx_test_spec.rb b/spec/overcommit/hook/pre_commit/nginx_test_spec.rb index cff2f699..b13ad8ef 100644 --- a/spec/overcommit/hook/pre_commit/nginx_test_spec.rb +++ b/spec/overcommit/hook/pre_commit/nginx_test_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::NginxTest do diff --git a/spec/overcommit/hook/pre_commit/pep257_spec.rb b/spec/overcommit/hook/pre_commit/pep257_spec.rb index 65dddb90..2210c650 100644 --- a/spec/overcommit/hook/pre_commit/pep257_spec.rb +++ b/spec/overcommit/hook/pre_commit/pep257_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pep257 do diff --git a/spec/overcommit/hook/pre_commit/pep8_spec.rb b/spec/overcommit/hook/pre_commit/pep8_spec.rb index 6916073c..f3e01947 100644 --- a/spec/overcommit/hook/pre_commit/pep8_spec.rb +++ b/spec/overcommit/hook/pre_commit/pep8_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pep8 do diff --git a/spec/overcommit/hook/pre_commit/php_lint_spec.rb b/spec/overcommit/hook/pre_commit/php_lint_spec.rb index 7f2203dd..ec755345 100644 --- a/spec/overcommit/hook/pre_commit/php_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/php_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PhpLint do @@ -24,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/php_stan_spec.rb b/spec/overcommit/hook/pre_commit/php_stan_spec.rb index 58e004c4..a4272fd6 100644 --- a/spec/overcommit/hook/pre_commit/php_stan_spec.rb +++ b/spec/overcommit/hook/pre_commit/php_stan_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PhpStan do diff --git a/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb b/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb index 86fe8bc7..a4b1e187 100644 --- a/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb +++ b/spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PhpCsFixer do @@ -11,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".', @@ -22,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) @@ -36,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".', @@ -44,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) @@ -58,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".', @@ -70,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 f480e071..6982a297 100644 --- a/spec/overcommit/hook/pre_commit/phpcs_spec.rb +++ b/spec/overcommit/hook/pre_commit/phpcs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PhpCs do @@ -37,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 @@ -51,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 635435bc..c9d56abe 100644 --- a/spec/overcommit/hook/pre_commit/pronto_spec.rb +++ b/spec/overcommit/hook/pre_commit/pronto_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pronto do @@ -29,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")) @@ -39,13 +42,30 @@ context 'and it reports a warning' do before do - result.stub(:stdout).and_return([ - 'file1.rb:12 W: Line is too long. [107/80]', - 'file2.rb:14 I: Prefer single-quoted strings' - ].join("\n")) + result.stub(:stderr).and_return('') + result.stub(:stdout).and_return <<~MESSAGE + Running Pronto::Rubocop + file1.rb:12 W: Line is too long. [107/80] + file2.rb:14 I: Prefer single-quoted strings + + ```suggestion + x = 'x' + ``` + MESSAGE end it { should warn } end + + context 'and it has a generic error message written to stderr' do + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return([ + 'Could not find pronto in any of the sources' + ].join("\n")) + end + + it { should fail_hook } + end end end diff --git a/spec/overcommit/hook/pre_commit/puppet_lint_spec.rb b/spec/overcommit/hook/pre_commit/puppet_lint_spec.rb index 94a0fa7e..518f06e2 100644 --- a/spec/overcommit/hook/pre_commit/puppet_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/puppet_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PuppetLint do diff --git a/spec/overcommit/hook/pre_commit/puppet_metadata_json_lint_spec.rb b/spec/overcommit/hook/pre_commit/puppet_metadata_json_lint_spec.rb index 43da8eb7..1edeb21b 100644 --- a/spec/overcommit/hook/pre_commit/puppet_metadata_json_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/puppet_metadata_json_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PuppetMetadataJsonLint do diff --git a/spec/overcommit/hook/pre_commit/pycodestyle_spec.rb b/spec/overcommit/hook/pre_commit/pycodestyle_spec.rb index b579c276..609388a0 100644 --- a/spec/overcommit/hook/pre_commit/pycodestyle_spec.rb +++ b/spec/overcommit/hook/pre_commit/pycodestyle_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pycodestyle do diff --git a/spec/overcommit/hook/pre_commit/pydocstyle_spec.rb b/spec/overcommit/hook/pre_commit/pydocstyle_spec.rb index b043eafe..2fb61038 100644 --- a/spec/overcommit/hook/pre_commit/pydocstyle_spec.rb +++ b/spec/overcommit/hook/pre_commit/pydocstyle_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pydocstyle do diff --git a/spec/overcommit/hook/pre_commit/pyflakes_spec.rb b/spec/overcommit/hook/pre_commit/pyflakes_spec.rb index a503d1aa..fd913155 100644 --- a/spec/overcommit/hook/pre_commit/pyflakes_spec.rb +++ b/spec/overcommit/hook/pre_commit/pyflakes_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pyflakes do diff --git a/spec/overcommit/hook/pre_commit/pylint_spec.rb b/spec/overcommit/hook/pre_commit/pylint_spec.rb index b8b3159d..b0368d6a 100644 --- a/spec/overcommit/hook/pre_commit/pylint_spec.rb +++ b/spec/overcommit/hook/pre_commit/pylint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Pylint do diff --git a/spec/overcommit/hook/pre_commit/python_flake8_spec.rb b/spec/overcommit/hook/pre_commit/python_flake8_spec.rb index 10016a00..a4bb1109 100644 --- a/spec/overcommit/hook/pre_commit/python_flake8_spec.rb +++ b/spec/overcommit/hook/pre_commit/python_flake8_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::PythonFlake8 do 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_best_practices_spec.rb b/spec/overcommit/hook/pre_commit/rails_best_practices_spec.rb index 686538f2..6e451f25 100644 --- a/spec/overcommit/hook/pre_commit/rails_best_practices_spec.rb +++ b/spec/overcommit/hook/pre_commit/rails_best_practices_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RailsBestPractices do 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 0046b3b2..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 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RailsSchemaUpToDate do @@ -68,6 +70,33 @@ end it { should fail_hook } + + context 'when non ASCII encoding is required' do + let!(:config) do + super().merge(Overcommit::Configuration.new( + 'PreCommit' => { + 'RailsSchemaUpToDate' => { + 'encoding' => 'utf-8' + } + } + )) + end + + before do + subject.stub(:applicable_files).and_return([sql_schema_file]) + end + + around do |example| + repo do + FileUtils.mkdir_p('db/migrate') + File.open(sql_schema_file, 'w') { |f| f.write("version: 12345678901234\nVALUES ('字')") } + `git add #{sql_schema_file}` + example.run + end + end + + it { should fail_hook } + end end context 'when a Ruby schema file with the latest version and migrations are added' do diff --git a/spec/overcommit/hook/pre_commit/rake_target_spec.rb b/spec/overcommit/hook/pre_commit/rake_target_spec.rb index 5c0f01e9..e295e61f 100644 --- a/spec/overcommit/hook/pre_commit/rake_target_spec.rb +++ b/spec/overcommit/hook/pre_commit/rake_target_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RakeTarget do @@ -17,11 +19,11 @@ context 'with targets parameter set' do let(:config) do super().merge(Overcommit::Configuration.new( - 'PreCommit' => { - 'RakeTarget' => { - 'targets' => ['test'], - } - } + 'PreCommit' => { + 'RakeTarget' => { + 'targets' => ['test'], + } + } )) end let(:result) { double('result') } diff --git a/spec/overcommit/hook/pre_commit/reek_spec.rb b/spec/overcommit/hook/pre_commit/reek_spec.rb index 7d72ebfa..8e111e7c 100644 --- a/spec/overcommit/hook/pre_commit/reek_spec.rb +++ b/spec/overcommit/hook/pre_commit/reek_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Reek do diff --git a/spec/overcommit/hook/pre_commit/rst_lint_spec.rb b/spec/overcommit/hook/pre_commit/rst_lint_spec.rb index 891247a6..ed9edbf8 100644 --- a/spec/overcommit/hook/pre_commit/rst_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/rst_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RstLint do diff --git a/spec/overcommit/hook/pre_commit/rubo_cop_spec.rb b/spec/overcommit/hook/pre_commit/rubo_cop_spec.rb index 4a3d25d5..f06c8d1d 100644 --- a/spec/overcommit/hook/pre_commit/rubo_cop_spec.rb +++ b/spec/overcommit/hook/pre_commit/rubo_cop_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RuboCop do diff --git a/spec/overcommit/hook/pre_commit/ruby_lint_spec.rb b/spec/overcommit/hook/pre_commit/ruby_lint_spec.rb index 0d0d8780..062a3296 100644 --- a/spec/overcommit/hook/pre_commit/ruby_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/ruby_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::RubyLint do diff --git a/spec/overcommit/hook/pre_commit/ruby_syntax_spec.rb b/spec/overcommit/hook/pre_commit/ruby_syntax_spec.rb new file mode 100644 index 00000000..0eaa4459 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/ruby_syntax_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::RubySyntax 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 ruby_syntax exits successfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + context 'with no errors' do + before do + result.stub(:stderr).and_return('') + end + + it { should pass } + end + end + + context 'when ruby_syntax 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([ + "file1.rb:2: syntax error, unexpected '^'" + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/scalariform_spec.rb b/spec/overcommit/hook/pre_commit/scalariform_spec.rb index 2d258678..cb24df97 100644 --- a/spec/overcommit/hook/pre_commit/scalariform_spec.rb +++ b/spec/overcommit/hook/pre_commit/scalariform_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Scalariform do diff --git a/spec/overcommit/hook/pre_commit/scalastyle_spec.rb b/spec/overcommit/hook/pre_commit/scalastyle_spec.rb index a42dc4f3..90e44d4c 100644 --- a/spec/overcommit/hook/pre_commit/scalastyle_spec.rb +++ b/spec/overcommit/hook/pre_commit/scalastyle_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Scalastyle do diff --git a/spec/overcommit/hook/pre_commit/scss_lint_spec.rb b/spec/overcommit/hook/pre_commit/scss_lint_spec.rb index 2880f565..ae8b2f68 100644 --- a/spec/overcommit/hook/pre_commit/scss_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/scss_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ScssLint do diff --git a/spec/overcommit/hook/pre_commit/semi_standard_spec.rb b/spec/overcommit/hook/pre_commit/semi_standard_spec.rb index 1e967e4c..cc69025a 100644 --- a/spec/overcommit/hook/pre_commit/semi_standard_spec.rb +++ b/spec/overcommit/hook/pre_commit/semi_standard_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::SemiStandard do diff --git a/spec/overcommit/hook/pre_commit/shell_check_spec.rb b/spec/overcommit/hook/pre_commit/shell_check_spec.rb index 22cb9e78..f9fba49c 100644 --- a/spec/overcommit/hook/pre_commit/shell_check_spec.rb +++ b/spec/overcommit/hook/pre_commit/shell_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::ShellCheck do diff --git a/spec/overcommit/hook/pre_commit/slim_lint_spec.rb b/spec/overcommit/hook/pre_commit/slim_lint_spec.rb index 7d4e57d1..3485c226 100644 --- a/spec/overcommit/hook/pre_commit/slim_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/slim_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::SlimLint 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/sqlint_spec.rb b/spec/overcommit/hook/pre_commit/sqlint_spec.rb index 11bcec60..bc9edec2 100644 --- a/spec/overcommit/hook/pre_commit/sqlint_spec.rb +++ b/spec/overcommit/hook/pre_commit/sqlint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Sqlint do diff --git a/spec/overcommit/hook/pre_commit/standard_spec.rb b/spec/overcommit/hook/pre_commit/standard_spec.rb index 16150285..c382e61d 100644 --- a/spec/overcommit/hook/pre_commit/standard_spec.rb +++ b/spec/overcommit/hook/pre_commit/standard_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Standard do diff --git a/spec/overcommit/hook/pre_commit/stylelint_spec.rb b/spec/overcommit/hook/pre_commit/stylelint_spec.rb index bb5381c6..68e83f65 100644 --- a/spec/overcommit/hook/pre_commit/stylelint_spec.rb +++ b/spec/overcommit/hook/pre_commit/stylelint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Stylelint do @@ -13,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 @@ -20,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 @@ -30,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)', @@ -37,6 +41,35 @@ 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 + + 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/swift_lint_spec.rb b/spec/overcommit/hook/pre_commit/swift_lint_spec.rb new file mode 100644 index 00000000..d2fb78fd --- /dev/null +++ b/spec/overcommit/hook/pre_commit/swift_lint_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::SwiftLint 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.swift file2.swift]) + end + + context 'when SwiftLint 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 SwiftLint 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([ + 'file1.swift:12: warning: message: details' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_commit/terraform_format_spec.rb b/spec/overcommit/hook/pre_commit/terraform_format_spec.rb new file mode 100644 index 00000000..6897f8c3 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/terraform_format_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::TerraformFormat 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.tf file2.tf]) + end + + context 'when Terraform 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 Terraform exits unsucessfully' do + let(:result_ok) { double('result') } + let(:result_bad) { double('result') } + let(:cmdline) { %w[terraform fmt -check=true -diff=false] } + + before do + result_ok.stub(:success?).and_return(true) + result_bad.stub(:success?).and_return(false) + subject.stub(:execute).with(cmdline, args: ['file1.tf']).and_return(result_ok) + subject.stub(:execute).with(cmdline, args: ['file2.tf']).and_return(result_bad) + end + + it { should fail_hook } + end +end diff --git a/spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb b/spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb index c7d8c84a..af94b7f9 100644 --- a/spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb +++ b/spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::TrailingWhitespace do diff --git a/spec/overcommit/hook/pre_commit/travis_lint_spec.rb b/spec/overcommit/hook/pre_commit/travis_lint_spec.rb index c0cea2a9..0a6e5624 100644 --- a/spec/overcommit/hook/pre_commit/travis_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/travis_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::TravisLint do diff --git a/spec/overcommit/hook/pre_commit/ts_lint_spec.rb b/spec/overcommit/hook/pre_commit/ts_lint_spec.rb index 76294735..6cc8e55b 100644 --- a/spec/overcommit/hook/pre_commit/ts_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/ts_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::TsLint do @@ -36,12 +38,32 @@ context 'and it reports an error' do before do - result.stub(:stdout).and_return([ + result.stub(:stdout).and_return( 'src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140' - ].join("\n")) + ) + end + + it { should fail_hook } + end + + context 'and it reports an error with an "ERROR" severity' do + before do + result.stub(:stdout).and_return( + 'ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef' + ) end it { should fail_hook } end + + context 'and it reports an warning' do + before do + result.stub(:stdout).and_return( + 'WARNING: src/AccountController.ts[4, 28]: expected call-signature to have a typedef' + ) + end + + it { should warn } + end end end diff --git a/spec/overcommit/hook/pre_commit/vint_spec.rb b/spec/overcommit/hook/pre_commit/vint_spec.rb index e4d6d504..c884d655 100644 --- a/spec/overcommit/hook/pre_commit/vint_spec.rb +++ b/spec/overcommit/hook/pre_commit/vint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::Vint do diff --git a/spec/overcommit/hook/pre_commit/w3c_css_spec.rb b/spec/overcommit/hook/pre_commit/w3c_css_spec.rb index d69cf484..9ec45800 100644 --- a/spec/overcommit/hook/pre_commit/w3c_css_spec.rb +++ b/spec/overcommit/hook/pre_commit/w3c_css_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::W3cCss do diff --git a/spec/overcommit/hook/pre_commit/w3c_html_spec.rb b/spec/overcommit/hook/pre_commit/w3c_html_spec.rb index 7c3b612f..e517bb34 100644 --- a/spec/overcommit/hook/pre_commit/w3c_html_spec.rb +++ b/spec/overcommit/hook/pre_commit/w3c_html_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::W3cHtml do diff --git a/spec/overcommit/hook/pre_commit/xml_lint_spec.rb b/spec/overcommit/hook/pre_commit/xml_lint_spec.rb index 967e6964..dc48df40 100644 --- a/spec/overcommit/hook/pre_commit/xml_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/xml_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::XmlLint do diff --git a/spec/overcommit/hook/pre_commit/xml_syntax_spec.rb b/spec/overcommit/hook/pre_commit/xml_syntax_spec.rb index 1a086792..d2a31c05 100644 --- a/spec/overcommit/hook/pre_commit/xml_syntax_spec.rb +++ b/spec/overcommit/hook/pre_commit/xml_syntax_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'rexml/document' diff --git a/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb b/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb index 2709fe12..e5aea475 100644 --- a/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb +++ b/spec/overcommit/hook/pre_commit/yaml_lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::YamlLint do @@ -17,7 +19,7 @@ end before do - subject.stub(:execute).with(%w[yamllint --format=parsable], args: applicable_files). + subject.stub(:execute).with(%w[yamllint --format=parsable --strict], args: applicable_files). and_return(result) end @@ -28,20 +30,33 @@ stdout: <<-MSG file1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length) file2.yml:41:81: [error] line too long (261 > 80 characters) (line-length) - MSG + MSG ) end - it { should warn } + it { should fail_hook } end + context 'and has 1 error and 1 warning' do + let(:result) do + double( + success?: false, + stdout: <<-MSG +file1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length) +file2.yml:41:81: [warning] missing document start "---" (document-start) + MSG + ) + end + + it { should fail_hook } + end context 'and has single suggestion for missing file header' do let(:result) do double( success?: false, stdout: <<-MSG file1.yaml:1:1: [warning] missing document start "---" (document-start) - MSG + MSG ) end diff --git a/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb b/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb index 54ae727b..a6f61f2c 100644 --- a/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb +++ b/spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::YamlSyntax do @@ -20,6 +22,7 @@ context 'when YAML file has errors' do before do + YAML.stub(:load_file).with(staged_file, { aliases: true }).and_raise(ArgumentError) YAML.stub(:load_file).with(staged_file).and_raise(ArgumentError) end diff --git a/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb b/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb index dc6f8b04..d463d25d 100644 --- a/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb +++ b/spec/overcommit/hook/pre_commit/yard_coverage_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::YardCoverage do @@ -74,7 +76,7 @@ context 'and it reports an error' do before do result.stub(:stdout).and_return( - <<-HEREDOC + <<-HEREDOC Files: 72 Modules: 12 ( 3 undocumented) Classes: 63 ( 15 undocumented) diff --git a/spec/overcommit/hook/pre_commit/yarn_check_spec.rb b/spec/overcommit/hook/pre_commit/yarn_check_spec.rb index 247f37bc..f67fa9b2 100644 --- a/spec/overcommit/hook/pre_commit/yarn_check_spec.rb +++ b/spec/overcommit/hook/pre_commit/yarn_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreCommit::YarnCheck do diff --git a/spec/overcommit/hook/pre_push/base_spec.rb b/spec/overcommit/hook/pre_push/base_spec.rb new file mode 100644 index 00000000..3bfb7cb0 --- /dev/null +++ b/spec/overcommit/hook/pre_push/base_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::Base do + let(:remote_name) { 'origin' } + let(:remote_ref_deletion?) { false } + let(:config) { double('config') } + let(:context) { double('context') } + let(:hook) { described_class.new(config, context) } + + describe '#run?' do + let(:hook_config) { {} } + + before do + allow(context).to receive(:remote_name).and_return(remote_name) + allow(context).to receive(:remote_ref_deletion?).and_return(remote_ref_deletion?) + allow(config).to receive(:for_hook).and_return(hook_config) + end + + subject { hook.run? } + + context 'with exclude_remotes specified' do + let(:hook_config) do + { 'exclude_remotes' => exclude_remotes } + end + + context 'exclude_remotes is nil' do + let(:exclude_remotes) { nil } + + it { subject.should == true } + end + + context 'exclude_remotes includes the remote' do + let(:exclude_remotes) { [remote_name] } + + it { subject.should == false } + end + + context 'exclude_remotes does not include the remote' do + let(:exclude_remotes) { ['heroku'] } + + it { subject.should == true } + end + end + + context 'with include_remote_ref_deletions specified' do + let(:hook_config) do + { 'include_remote_ref_deletions' => include_remote_ref_deletions } + end + let(:remote_ref_deletion?) { false } + let(:include_remote_ref_deletions) { false } + + context 'when remote branch is not being deleted' do + let(:remote_ref_deletion?) { false } + + context 'when include_remote_ref_deletions is not specified' do + let(:include_remote_ref_deletions) { nil } + + it { subject.should == true } + end + + context 'when include_remote_ref_deletions is false' do + let(:include_remote_ref_deletions) { false } + + it { subject.should == true } + end + + context 'when include_remote_ref_deletions is true' do + let(:include_remote_ref_deletions) { true } + + it { subject.should == true } + end + end + + context 'when remote branch is being deleted' do + let(:remote_ref_deletion?) { true } + + context 'when include_remote_ref_deletions is not specified' do + let(:include_remote_ref_deletions) { nil } + + it { subject.should == false } + end + + context 'when include_remote_ref_deletions is false' do + let(:include_remote_ref_deletions) { false } + + it { subject.should == false } + end + + context 'when include_remote_ref_deletions is true' do + let(:include_remote_ref_deletions) { true } + + it { subject.should == true } + end + end + end + end +end diff --git a/spec/overcommit/hook/pre_push/brakeman_spec.rb b/spec/overcommit/hook/pre_push/brakeman_spec.rb index 056652d0..c8dad7f0 100644 --- a/spec/overcommit/hook/pre_push/brakeman_spec.rb +++ b/spec/overcommit/hook/pre_push/brakeman_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::Brakeman do diff --git a/spec/overcommit/hook/pre_push/cargo_test_spec.rb b/spec/overcommit/hook/pre_push/cargo_test_spec.rb index eeef7b2e..a21c96dc 100644 --- a/spec/overcommit/hook/pre_push/cargo_test_spec.rb +++ b/spec/overcommit/hook/pre_push/cargo_test_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::CargoTest do diff --git a/spec/overcommit/hook/pre_push/flutter_test_spec.rb b/spec/overcommit/hook/pre_push/flutter_test_spec.rb new file mode 100644 index 00000000..a5bce582 --- /dev/null +++ b/spec/overcommit/hook/pre_push/flutter_test_spec.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::FlutterTest do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when flutter test exits successfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when flutter test exits unsuccessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'with a runtime error' do + before do + result.stub(stdout: '', stderr: <<-MSG) + 0:03 +0: Counter increments smoke test + ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ + The following _Exception was thrown running a test: + Exception + + When the exception was thrown, this was the stack: + #0 main. (file:///Users/user/project/test/widget_test.dart:18:5) + + #1 main. (file:///Users/user/project/test/widget_test.dart) + #2 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29) + + #3 testWidgets.. (package:flutter_test/src/widget_tester.dart) + #4 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19) + + #7 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14) + #8 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24) + #9 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54) + #14 withClock (package:clock/src/default.dart:48:10) + #15 FakeAsync.run. (package:fake_async/fake_async.dart:178:22) + #20 FakeAsync.run (package:fake_async/fake_async.dart:178:7) + #21 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15) + #22 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24) + #23 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19) + + #24 Declarer.test.. (package:test_api/src/backend/declarer.dart) + #29 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13) + #30 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15) + #35 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5) + #36 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17) + + #37 Invoker._onRun... (package:test_api/src/backend/invoker.dart) + #42 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9) + #43 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15) + #44 Invoker._onRun. (package:test_api/src/backend/invoker.dart:369:7) + #51 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11) + #52 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11) + #53 RemoteListener._runLiveTest. (package:test_api/src/remote_listener.dart:256:16) + #58 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5) + #59 RemoteListener._serializeTest. (package:test_api/src/remote_listener.dart:208:7) + #77 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12) + #78 new _MultiChannel. (package:stream_channel/src/multi_channel.dart:159:31) + #82 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11) + #116 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1145:21) + #124 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23) + #125 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46) + #135 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41) + #144 new _RawSocket. (dart:io-patch/socket_patch.dart:1580:33) + #145 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14) + (elided 111 frames from dart:async and package:stack_trace) + + The test description was: + Counter increments smoke test + ════════════════════════════════════════════════════════════════════════════════════════════════════ + 00:03 +0 -1: Counter increments smoke test [E] + Test failed. See exception logs above. + The test description was: Counter increments smoke test + + 00:03 +0 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + + context 'with a test failure' do + before do + result.stub(stderr: '', stdout: <<-MSG) + 00:02 +0: Counter increments smoke test + ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ + The following TestFailure object was thrown running a test: + Expected: exactly one matching node in the widget tree + Actual: _TextFinder: + Which: means none were found but one was expected + + When the exception was thrown, this was the stack: + #4 main. (file:///Users/user/project/test/widget_test.dart:19:5) + + #5 main. (file:///Users/user/project/test/widget_test.dart) + #6 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29) + + #7 testWidgets.. (package:flutter_test/src/widget_tester.dart) + #8 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19) + + #11 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14) + #12 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24) + #13 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54) + #18 withClock (package:clock/src/default.dart:48:10) + #19 FakeAsync.run. (package:fake_async/fake_async.dart:178:22) + #24 FakeAsync.run (package:fake_async/fake_async.dart:178:7) + #25 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15) + #26 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24) + #27 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19) + + #28 Declarer.test.. (package:test_api/src/backend/declarer.dart) + #33 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13) + #34 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15) + #39 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5) + #40 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17) + + #41 Invoker._onRun... (package:test_api/src/backend/invoker.dart) + #46 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9) + #47 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15) + #48 Invoker._onRun. (package:test_api/src/backend/invoker.dart:369:7) + #55 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11) + #56 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11) + #57 RemoteListener._runLiveTest. (package:test_api/src/remote_listener.dart:256:16) + #62 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5) + #63 RemoteListener._serializeTest. (package:test_api/src/remote_listener.dart:208:7) + #81 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12) + #82 new _MultiChannel. (package:stream_channel/src/multi_channel.dart:159:31) + #86 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11) + #120 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1145:21) + #128 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23) + #129 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46) + #139 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41) + #148 new _RawSocket. (dart:io-patch/socket_patch.dart:1580:33) + #149 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14) + (elided 111 frames from dart:async and package:stack_trace) + + This was caught by the test expectation on the following line: + file:///Users/user/project/test/widget_test.dart line 19 + The test description was: + Counter increments smoke test + ════════════════════════════════════════════════════════════════════════════════════════════════════ + 00:02 +0 -1: Counter increments smoke test [E] + Test failed. See exception logs above. + The test description was: Counter increments smoke test + + 00:02 +0 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_push/go_test_spec.rb b/spec/overcommit/hook/pre_push/go_test_spec.rb new file mode 100644 index 00000000..19bc4d0d --- /dev/null +++ b/spec/overcommit/hook/pre_push/go_test_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::GoTest do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when go test exits successfully' do + let(:result) { double('result') } + + before do + result.stub(success?: true, stderr: '', stdout: '') + subject.stub(:execute).and_return(result) + end + + it 'passes' do + expect(subject).to pass + end + end + + context 'when go test exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'when go test returns an error' do + let(:error_message) { "--- FAIL: Test1 (0.00s)\nFAIL" } + + before do + result.stub(:stdout).and_return(error_message) + result.stub(:stderr).and_return('') + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq error_message + end + end + + context 'when a generic error message is written to stderr' do + let(:error_message) { 'go: command not found' } + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return(error_message) + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq error_message + end + end + end +end diff --git a/spec/overcommit/hook/pre_push/golangci_lint_spec.rb b/spec/overcommit/hook/pre_push/golangci_lint_spec.rb new file mode 100644 index 00000000..bf4bed5f --- /dev/null +++ b/spec/overcommit/hook/pre_push/golangci_lint_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::GolangciLint do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when golangci-lint exits successfully' do + let(:result) { double('result') } + + before do + result.stub(success?: true, stderr: '', stdout: '') + subject.stub(:execute).and_return(result) + end + + it 'passes' do + expect(subject).to pass + end + end + + context 'when golangci-lint exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'when golangci-lint returns an error' do + let(:error_message) do + 'pkg1/file1.go:8:6: exported type `Test` should have comment or be unexported (golint)' + end + + before do + result.stub(:stdout).and_return(error_message) + result.stub(:stderr).and_return('') + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq error_message + end + end + + context 'when a generic error message is written to stderr' do + let(:error_message) { 'golangci-lint: command not found' } + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return(error_message) + end + + it 'fails' do + expect(subject).to fail_hook + end + + it 'returns valid message' do + message = subject.run.last + expect(message).to eq error_message + end + end + end +end diff --git a/spec/overcommit/hook/pre_push/minitest_spec.rb b/spec/overcommit/hook/pre_push/minitest_spec.rb index dde67e55..566fbce1 100644 --- a/spec/overcommit/hook/pre_push/minitest_spec.rb +++ b/spec/overcommit/hook/pre_push/minitest_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::Minitest do 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/php_unit_spec.rb b/spec/overcommit/hook/pre_push/php_unit_spec.rb index 5c8bf0cc..4373fefa 100644 --- a/spec/overcommit/hook/pre_push/php_unit_spec.rb +++ b/spec/overcommit/hook/pre_push/php_unit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::PhpUnit do diff --git a/spec/overcommit/hook/pre_push/pronto_spec.rb b/spec/overcommit/hook/pre_push/pronto_spec.rb new file mode 100644 index 00000000..5f48b7d7 --- /dev/null +++ b/spec/overcommit/hook/pre_push/pronto_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::Pronto 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 pronto 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 pronto 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('') + result.stub(:stdout).and_return([ + 'file2.rb:10 E: IDENTICAL code found in :iter.', + ].join("\n")) + end + + it { should fail_hook } + end + + 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] + file2.rb:14 I: Prefer single-quoted strings + + ```suggestion + x = 'x' + ``` + MESSAGE + end + + it { should warn } + end + + context 'and it has a generic error message written to stderr' do + before do + result.stub(:stdout).and_return('') + result.stub(:stderr).and_return([ + 'Could not find pronto in any of the sources' + ].join("\n")) + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_push/protected_branches_spec.rb b/spec/overcommit/hook/pre_push/protected_branches_spec.rb index e94ce35e..0f8db55a 100644 --- a/spec/overcommit/hook/pre_push/protected_branches_spec.rb +++ b/spec/overcommit/hook/pre_push/protected_branches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/pre_push' @@ -13,13 +15,15 @@ let(:context) { double('context') } subject { described_class.new(config, context) } - let(:protected_branch_patterns) { ['master', 'release/*'] } + let(:branch_configurations) do + ['master', 'release/*', { 'destructive_only_branch' => nil, 'destructive_only' => true }] + end let(:pushed_ref) do instance_double(Overcommit::HookContext::PrePush::PushedRef) end before do - subject.stub(protected_branch_patterns: protected_branch_patterns) + subject.stub(branch_configurations: branch_configurations) pushed_ref.stub(:remote_ref).and_return("refs/heads/#{pushed_ref_name}") context.stub(:pushed_refs).and_return([pushed_ref]) end @@ -92,6 +96,16 @@ let(:pushed_ref_name) { 'release/0.1.0' } include_examples 'protected branch' end + + context 'when branch overwrites global destructive_only' do + before do + pushed_ref.stub(:destructive?).and_return(true) + end + let(:pushed_ref_name) { 'destructive_only_branch' } + let(:hook_config) { { 'destructive_only' => false } } + + it { should fail_hook } + end end context 'when pushing tags' do diff --git a/spec/overcommit/hook/pre_push/pub_test_spec.rb b/spec/overcommit/hook/pre_push/pub_test_spec.rb new file mode 100644 index 00000000..41ff5c5d --- /dev/null +++ b/spec/overcommit/hook/pre_push/pub_test_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::PubTest do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when pub test exits successfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when pub test exits unsuccessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'with a runtime error' do + before do + result.stub(stdout: '', stderr: <<-MSG) + 00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E] + Exception + test/test_test.dart 6:5 main. + + 00:01 +1 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + + context 'with a test failure' do + before do + result.stub(stderr: '', stdout: <<-MSG) + 00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E] + Expected: ['fooo', 'bar', 'baz'] + Actual: ['foo', 'bar', 'baz'] + Which: at location [0] is 'foo' instead of 'fooo' + + package:test_api expect + test/test_test.dart 6:5 main. + + 00:01 +1 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_push/pytest_spec.rb b/spec/overcommit/hook/pre_push/pytest_spec.rb index cb13ba28..796f0f74 100644 --- a/spec/overcommit/hook/pre_push/pytest_spec.rb +++ b/spec/overcommit/hook/pre_push/pytest_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::Pytest do diff --git a/spec/overcommit/hook/pre_push/python_nose_spec.rb b/spec/overcommit/hook/pre_push/python_nose_spec.rb index 24f98dab..c91644cb 100644 --- a/spec/overcommit/hook/pre_push/python_nose_spec.rb +++ b/spec/overcommit/hook/pre_push/python_nose_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::PythonNose do diff --git a/spec/overcommit/hook/pre_push/r_spec_spec.rb b/spec/overcommit/hook/pre_push/r_spec_spec.rb index 7e30d785..1efc56d7 100644 --- a/spec/overcommit/hook/pre_push/r_spec_spec.rb +++ b/spec/overcommit/hook/pre_push/r_spec_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::RSpec do @@ -14,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/pre_push/rake_target_spec.rb b/spec/overcommit/hook/pre_push/rake_target_spec.rb index 05b3c299..5147d089 100644 --- a/spec/overcommit/hook/pre_push/rake_target_spec.rb +++ b/spec/overcommit/hook/pre_push/rake_target_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::RakeTarget do @@ -17,11 +19,11 @@ context 'with targets parameter set' do let(:config) do super().merge(Overcommit::Configuration.new( - 'PrePush' => { - 'RakeTarget' => { - 'targets' => ['test'], - } - } + 'PrePush' => { + 'RakeTarget' => { + 'targets' => ['test'], + } + } )) end let(:result) { double('result') } diff --git a/spec/overcommit/hook/pre_push/test_unit_spec.rb b/spec/overcommit/hook/pre_push/test_unit_spec.rb index 701a62ea..bf5f444a 100644 --- a/spec/overcommit/hook/pre_push/test_unit_spec.rb +++ b/spec/overcommit/hook/pre_push/test_unit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PrePush::TestUnit do diff --git a/spec/overcommit/hook/pre_rebase/merged_commits_spec.rb b/spec/overcommit/hook/pre_rebase/merged_commits_spec.rb index 72cdaff2..acab0138 100644 --- a/spec/overcommit/hook/pre_rebase/merged_commits_spec.rb +++ b/spec/overcommit/hook/pre_rebase/merged_commits_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Hook::PreRebase::MergedCommits do diff --git a/spec/overcommit/hook/prepare_commit_msg/base_spec.rb b/spec/overcommit/hook/prepare_commit_msg/base_spec.rb new file mode 100644 index 00000000..873ed637 --- /dev/null +++ b/spec/overcommit/hook/prepare_commit_msg/base_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'overcommit/hook_context/prepare_commit_msg' + +describe Overcommit::Hook::PrepareCommitMsg::Base do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { Overcommit::HookContext::PrepareCommitMsg.new(config, [], StringIO.new) } + let(:printer) { double('printer') } + + context 'when multiple hooks run simultaneously' do + let(:hook_1) { described_class.new(config, context) } + let(:hook_2) { described_class.new(config, context) } + + let(:tempfile) { 'test-prepare-commit-msg.txt' } + + let(:initial_content) { "This is a test\n" } + + before do + File.open(tempfile, 'w') do |f| + f << initial_content + end + end + + after do + File.delete(tempfile) + end + + it 'works well with concurrency' do + allow(context).to receive(:commit_message_filename).and_return(tempfile) + allow(hook_1).to receive(:run) do + hook_1.modify_commit_message do |contents| + "alpha\n" + contents + end + end + allow(hook_2).to receive(:run) do + hook_2.modify_commit_message do |contents| + contents + "bravo\n" + end + end + 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 +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 new file mode 100644 index 00000000..e355517f --- /dev/null +++ b/spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'overcommit/hook_context/prepare_commit_msg' + +describe Overcommit::Hook::PrepareCommitMsg::ReplaceBranch do + def checkout_branch(branch) + allow(Overcommit::GitRepo).to receive(:current_branch).and_return(branch) + end + + def new_config(opts = {}) + default = Overcommit::ConfigurationLoader.default_configuration + + return default if opts.empty? + + default.merge( + Overcommit::Configuration.new( + 'PrepareCommitMsg' => { + 'ReplaceBranch' => opts.merge('enabled' => true) + } + ) + ) + end + + def new_context(config, argv) + Overcommit::HookContext::PrepareCommitMsg.new(config, argv, StringIO.new) + end + + def hook_for(config, context) + described_class.new(config, context) + end + + def add_file(name, contents) + File.open(name, 'w') { |f| f.puts contents } + end + + def remove_file(name) + File.delete(name) + end + + before { allow(Overcommit::Utils).to receive_message_chain(:log, :debug) } + + let(:config) { new_config } + let(:normal_context) { new_context(config, ['COMMIT_EDITMSG']) } + let(:message_context) { new_context(config, %w[COMMIT_EDITMSG message]) } + let(:commit_context) { new_context(config, %w[COMMIT_EDITMSG commit HEAD]) } + let(:merge_context) { new_context(config, %w[MERGE_MSG merge]) } + let(:squash_context) { new_context(config, %w[SQUASH_MSG squash]) } + let(:template_context) { new_context(config, ['template.txt', 'template']) } + subject(:hook) { hook_for(config, normal_context) } + + describe '#run' do + before { add_file 'COMMIT_EDITMSG', '' } + after { remove_file 'COMMIT_EDITMSG' } + + context 'when the checked out branch matches the pattern' do + before { checkout_branch '123-topic' } + before { hook.run } + + it { is_expected.to pass } + + 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 + before { checkout_branch 'topic-123' } + before { hook.run } + + context 'with the default `skipped_commit_types`' do + it { is_expected.to warn } + end + + context 'when merging, and `skipped_commit_types` includes `merge`' do + let(:config) { new_config('skipped_commit_types' => ['merge']) } + subject(:hook) { hook_for(config, merge_context) } + + it { is_expected.to pass } + end + + context 'when merging, and `skipped_commit_types` includes `template`' do + let(:config) { new_config('skipped_commit_types' => ['template']) } + subject(:hook) { hook_for(config, template_context) } + + it { is_expected.to pass } + end + + context 'when merging, and `skipped_commit_types` includes `message`' do + let(:config) { new_config('skipped_commit_types' => ['message']) } + subject(:hook) { hook_for(config, message_context) } + + it { is_expected.to pass } + end + + context 'when merging, and `skipped_commit_types` includes `commit`' do + let(:config) { new_config('skipped_commit_types' => ['commit']) } + subject(:hook) { hook_for(config, commit_context) } + + it { is_expected.to pass } + end + + context 'when merging, and `skipped_commit_types` includes `squash`' do + let(:config) { new_config('skipped_commit_types' => ['squash']) } + subject(:hook) { hook_for(config, squash_context) } + + it { is_expected.to pass } + end + end + + context 'when the replacement text points to a valid filename' do + before { checkout_branch '123-topic' } + before { add_file 'replacement_text.txt', 'FOO' } + after { remove_file 'replacement_text.txt' } + + let(:config) { new_config('replacement_text' => 'replacement_text.txt') } + let(:normal_context) { new_context(config, ['COMMIT_EDITMSG']) } + subject(:hook) { hook_for(config, normal_context) } + + before { hook.run } + + it { is_expected.to pass } + + let(:commit_msg) { File.read('COMMIT_EDITMSG') } + + it 'uses the file contents as the replacement text' do + expect(commit_msg).to eq(File.read('replacement_text.txt')) + end + end + end +end diff --git a/spec/overcommit/hook_context/base_spec.rb b/spec/overcommit/hook_context/base_spec.rb index d5028379..7b72c12e 100644 --- a/spec/overcommit/hook_context/base_spec.rb +++ b/spec/overcommit/hook_context/base_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::HookContext::Base do diff --git a/spec/overcommit/hook_context/commit_msg_spec.rb b/spec/overcommit/hook_context/commit_msg_spec.rb index ac11aa90..058f1508 100644 --- a/spec/overcommit/hook_context/commit_msg_spec.rb +++ b/spec/overcommit/hook_context/commit_msg_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/commit_msg' @@ -90,7 +92,676 @@ subject { context.post_fail_message } it 'returns printable log of commit message' do - subject.should == "Failed commit message:\nSome commit message\n" + subject.should start_with "Failed commit message:\nSome commit message\n" + end + + it 'returns the command to run to restore the commit message' do + subject.should end_with "git commit --edit --file=#{commit_message_file}" + end + end + + describe '#amendment?' do + subject { context.amendment? } + + before do + Overcommit::Utils.stub(:parent_command).and_return(command) + end + + context 'when amending a commit using `git commit --amend`' do + let(:command) { 'git commit --amend' } + + it { should == true } + end + + context 'when the parent command contains invalid byte sequence' do + let(:command) { "git commit --amend -m \xE3M^AM^B" } + + it { should == true } + end + + context 'when amending a commit using a git alias' do + around do |example| + repo do + `git config alias.amend "commit --amend"` + `git config alias.other-amend "commit --amend"` + example.run + end + end + + context 'when using one of multiple aliases' do + let(:command) { 'git amend' } + + it { should == true } + end + + context 'when using another of multiple aliases' do + let(:command) { 'git other-amend' } + + it { should == true } + end + end + + context 'when not amending a commit' do + context 'using `git commit`' do + let(:command) { 'git commit' } + + it { should == false } + end + + context 'using a git alias containing "--amend"' do + let(:command) { 'git no--amend' } + + around do |example| + repo do + `git config alias.no--amend commit` + example.run + end + end + + it { should == false } + end + end + end + + describe '#setup_environment' do + subject { context.setup_environment } + + context 'when there are no staged changes' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + echo('Hello Other World', 'other-tracked-file') + `git add tracked-file other-tracked-file` + `git commit -m "Add tracked-file and other-tracked-file"` + echo('Hello Again', 'untracked-file') + echo('Some more text', 'other-tracked-file', append: true) + example.run + end + end + + it 'keeps already-committed files' do + subject + File.open('tracked-file', 'r').read.should == "Hello World\n" + end + + it 'does not keep unstaged changes' do + subject + File.open('other-tracked-file', 'r').read.should == "Hello Other World\n" + end + + it 'keeps untracked files' do + subject + File.open('untracked-file', 'r').read.should == "Hello Again\n" + end + + it 'keeps modification times the same' do + sleep 1 + expect { subject }.to_not change { + [ + File.mtime('tracked-file'), + File.mtime('other-tracked-file'), + File.mtime('untracked-file') + ] + } + end + end + + context 'when there are staged changes' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + echo('Hello Other World', 'other-tracked-file') + `git add tracked-file other-tracked-file` + `git commit -m "Add tracked-file and other-tracked-file"` + echo('Hello Again', 'untracked-file') + echo('Some more text', 'tracked-file', append: true) + echo('Some more text', 'other-tracked-file', append: true) + `git add tracked-file` + echo('Yet some more text', 'tracked-file', append: true) + example.run + end + end + + it 'keeps staged changes' do + subject + File.open('tracked-file', 'r').read.should == "Hello World\nSome more text\n" + end + + it 'does not keep unstaged changes' do + subject + File.open('other-tracked-file', 'r').read.should == "Hello Other World\n" + end + + it 'keeps untracked files' do + subject + File.open('untracked-file', 'r').read.should == "Hello Again\n" + end + + it 'keeps modification times the same' do + sleep 1 + expect { subject }.to_not change { + [ + File.mtime('tracked-file'), + File.mtime('other-tracked-file'), + File.mtime('untracked-file') + ] + } + end + end + + context 'when all changes have been staged' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + `git add tracked-file` + `git commit -m "Add tracked-file"` + echo('Hello Other World', 'other-tracked-file') + `git add other-tracked-file` + example.run + end + end + + it 'does not stash changes' do + expect(context.private_methods).to include :stash_changes + expect(context).not_to receive(:stash_changes) + subject + end + end + + context 'when renaming a file during an amendment' do + around do |example| + repo do + `git commit --allow-empty -m "Initial commit"` + touch 'some-file' + `git add some-file` + `git commit -m "Add file"` + `git mv some-file renamed-file` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it 'does not try to update modification time of the old non-existent file' do + File.should_receive(:mtime).with(/renamed-file/) + File.should_not_receive(:mtime).with(/some-file/) + subject + end + end + + context 'when only a submodule change is staged' do + around do |example| + submodule = repo do + `git commit --allow-empty -m "Initial commit"` + end + + repo do + `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}` + `git submodule foreach "git config --local commit.gpgsign false"` + `git submodule foreach "git commit -m \\"Another commit\\"" < #{File::NULL}` + `git add sub` + example.run + end + end + + it 'keeps staged submodule change' do + `git config diff.submodule short` + expect { subject }.to_not change { + (`git diff --cached` =~ /-Subproject commit[\s\S]*\+Subproject commit/).nil? + }.from(false) + end + end + + # Git cannot track Windows symlinks + unless Overcommit::OS.windows? + context 'when a broken symlink is staged' do + around do |example| + repo do + Overcommit::Utils::FileUtils.symlink('non-existent-file', 'symlink') + `git add symlink` + example.run + end + end + + it 'does not attempt to update/restore the modification time of the file' do + File.should_not_receive(:mtime) + File.should_not_receive(:utime) + subject + end + end + end + end + + describe '#cleanup_environment' do + subject { context.cleanup_environment } + + before do + context.setup_environment + end + + context 'when there were no staged changes' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + echo('Hello Other World', 'other-tracked-file') + `git add tracked-file other-tracked-file` + `git commit -m "Add tracked-file and other-tracked-file"` + echo('Hello Again', 'untracked-file') + echo('Some more text', 'other-tracked-file', append: true) + example.run + end + end + + it 'restores the unstaged changes' do + subject + File.open('other-tracked-file', 'r').read. + should == "Hello Other World\nSome more text\n" + end + + it 'keeps already-committed files' do + subject + File.open('tracked-file', 'r').read.should == "Hello World\n" + end + + it 'keeps untracked files' do + subject + File.open('untracked-file', 'r').read.should == "Hello Again\n" + end + + it 'keeps modification times the same' do + sleep 1 + expect { subject }.to_not change { + [ + File.mtime('tracked-file'), + File.mtime('other-tracked-file'), + File.mtime('untracked-file') + ] + } + end + end + + context 'when there were staged changes' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + echo('Hello Other World', 'other-tracked-file') + `git add tracked-file other-tracked-file` + `git commit -m "Add tracked-file and other-tracked-file"` + echo('Hello Again', 'untracked-file') + echo('Some more text', 'tracked-file', append: true) + echo('Some more text', 'other-tracked-file', append: true) + `git add tracked-file` + echo('Yet some more text', 'tracked-file', append: true) + example.run + end + end + + it 'restores the unstaged changes' do + subject + File.open('tracked-file', 'r').read. + should == "Hello World\nSome more text\nYet some more text\n" + end + + it 'keeps staged changes' do + subject + `git show :tracked-file`.should == "Hello World\nSome more text\n" + end + + it 'keeps untracked files' do + subject + File.open('untracked-file', 'r').read.should == "Hello Again\n" + end + + it 'keeps modification times the same' do + sleep 1 + expect { subject }.to_not change { + [ + File.mtime('tracked-file'), + File.mtime('other-tracked-file'), + File.mtime('untracked-file') + ] + } + end + end + + context 'when all changes were staged' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + `git add tracked-file` + `git commit -m "Add tracked-file"` + echo('Hello Other World', 'other-tracked-file') + `git add other-tracked-file` + example.run + end + end + + it 'does not touch the working tree' do + expect(context.private_methods).to include :clear_working_tree + expect(context.private_methods).to include :restore_working_tree + expect(context).not_to receive(:clear_working_tree) + expect(context).not_to receive(:restore_working_tree) + subject + end + end + + context 'when there were deleted files' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + `git add tracked-file` + `git commit -m "Add tracked-file"` + `git rm tracked-file` + example.run + end + end + + it 'deletes the file' do + subject + File.exist?('tracked-file').should == false + end + end + + context 'when only a submodule change was staged' do + around do |example| + submodule = repo do + `git commit --allow-empty -m "Initial commit"` + end + + repo do + `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}` + `git submodule foreach "git config --local commit.gpgsign false"` + `git submodule foreach "git commit -m \\"Another commit\\"" < #{File::NULL}` + `git add sub` + example.run + end + end + + it 'keeps staged submodule change' do + `git config diff.submodule short` + expect { subject }.to_not change { + (`git diff --cached` =~ /-Subproject commit[\s\S]*\+Subproject commit/).nil? + }.from(false) + end + end + + context 'when submodule changes were staged along with other changes' do + around do |example| + submodule = repo do + `git commit --allow-empty -m "Initial commit"` + end + + repo do + `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}` + `git submodule foreach "git config --local commit.gpgsign false"` + `git submodule foreach "git commit -m \\"Another commit\\"" < #{File::NULL}` + echo('Hello Again', 'tracked-file') + `git add sub tracked-file` + example.run + end + end + + it 'keeps staged submodule change' do + `git config diff.submodule short` + expect { subject }.to_not change { + (`git diff --cached` =~ /-Subproject commit[\s\S]*\+Subproject commit/).nil? + }.from(false) + end + + it 'keeps staged file change' do + subject + `git show :tracked-file`.should == "Hello Again\n" + end + end + + context 'when a submodule removal was staged' do + around do |example| + submodule = repo do + `git commit --allow-empty -m "Initial commit"` + end + + repo do + `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1` + `git commit -m "Add submodule"` + `git rm sub` + example.run + end + end + + it 'does not leave behind an empty submodule directory' do + subject + File.exist?('sub').should == false + end + end + end + + describe '#modified_files' do + subject { context.modified_files } + + before do + context.stub(:amendment?).and_return(false) + end + + it 'does not include submodules' do + submodule = repo do + touch 'foo' + `git add foo` + `git commit -m "Initial commit"` + end + + repo do + `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 + + context 'when no files were staged' do + around do |example| + repo do + example.run + end + end + + it { should be_empty } + end + + context 'when files were added' do + around do |example| + repo do + touch('some-file') + `git add some-file` + example.run + end + end + + it { should == [File.expand_path('some-file')] } + end + + context 'when files were modified' do + around do |example| + repo do + touch('some-file') + `git add some-file` + `git commit -m "Initial commit"` + echo('Hello', 'some-file') + `git add some-file` + example.run + end + end + + it { should == [File.expand_path('some-file')] } + end + + context 'when files were deleted' do + around do |example| + repo do + touch('some-file') + `git add some-file` + `git commit -m "Initial commit"` + `git rm some-file` + example.run + end + end + + it { should be_empty } + end + + context 'when amending last commit' do + around do |example| + repo do + touch('some-file') + `git add some-file` + `git commit -m "Initial commit"` + touch('other-file') + `git add other-file` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it { should =~ [File.expand_path('some-file'), File.expand_path('other-file')] } + end + + context 'when renaming a file during an amendment' do + around do |example| + repo do + `git commit --allow-empty -m "Initial commit"` + touch 'some-file' + `git add some-file` + `git commit -m "Add file"` + `git mv some-file renamed-file` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it 'does not include the old file name in the list of modified files' do + subject.should_not include File.expand_path('some-file') + end + end + + # Git cannot track Windows symlinks + unless Overcommit::OS.windows? + context 'when changing a symlink to a directory during an amendment' do + around do |example| + repo do + `git commit --allow-empty -m "Initial commit"` + FileUtils.mkdir 'some-directory' + symlink('some-directory', 'some-symlink') + `git add some-symlink some-directory` + `git commit -m "Add file"` + `git rm some-symlink` + FileUtils.mkdir 'some-symlink' + touch File.join('some-symlink', 'another-file') + `git add some-symlink` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it 'does not include the directory in the list of modified files' do + subject.should_not include File.expand_path('some-symlink') + end + end + + context 'when breaking a symlink during an amendment' do + around do |example| + repo do + `git commit --allow-empty -m "Initial commit"` + FileUtils.mkdir 'some-directory' + touch File.join('some-directory', 'some-file') + symlink('some-directory', 'some-symlink') + `git add some-symlink some-directory` + `git commit -m "Add file"` + `git rm -rf some-directory` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it 'still includes the broken symlink in the list of modified files' do + subject.should include File.expand_path('some-symlink') + end + end + end + end + + describe '#modified_lines_in_file' do + let(:modified_file) { 'some-file' } + subject { context.modified_lines_in_file(modified_file) } + + before do + context.stub(:amendment?).and_return(false) + end + + context 'when file contains a trailing newline' do + around do |example| + repo do + File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write("#{i}\n") } } + `git add #{modified_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 + File.open(modified_file, 'w') do |f| + (1..2).each { |i| f.write("#{i}\n") } + f.write(3) + end + + `git add #{modified_file}` + example.run + end + end + + it { should == Set.new(1..3) } + end + + context 'when amending last commit' do + around do |example| + repo do + File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write("#{i}\n") } } + `git add #{modified_file}` + `git commit -m "Add files"` + File.open(modified_file, 'a') { |f| f.puts 4 } + `git add #{modified_file}` + example.run + end + end + + before do + context.stub(:amendment?).and_return(true) + end + + it { should == Set.new(1..4) } end 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/post_checkout_spec.rb b/spec/overcommit/hook_context/post_checkout_spec.rb index a9b928a7..50657c37 100644 --- a/spec/overcommit/hook_context/post_checkout_spec.rb +++ b/spec/overcommit/hook_context/post_checkout_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/post_checkout' diff --git a/spec/overcommit/hook_context/post_commit_spec.rb b/spec/overcommit/hook_context/post_commit_spec.rb index c9f8b9c7..81758256 100644 --- a/spec/overcommit/hook_context/post_commit_spec.rb +++ b/spec/overcommit/hook_context/post_commit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/post_commit' diff --git a/spec/overcommit/hook_context/post_merge_spec.rb b/spec/overcommit/hook_context/post_merge_spec.rb index cce17afc..aa89fa82 100644 --- a/spec/overcommit/hook_context/post_merge_spec.rb +++ b/spec/overcommit/hook_context/post_merge_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/post_merge' diff --git a/spec/overcommit/hook_context/post_rewrite_spec.rb b/spec/overcommit/hook_context/post_rewrite_spec.rb index e26dc207..d2f18f6e 100644 --- a/spec/overcommit/hook_context/post_rewrite_spec.rb +++ b/spec/overcommit/hook_context/post_rewrite_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/post_rewrite' diff --git a/spec/overcommit/hook_context/pre_commit_spec.rb b/spec/overcommit/hook_context/pre_commit_spec.rb index 17a481a8..719982a1 100644 --- a/spec/overcommit/hook_context/pre_commit_spec.rb +++ b/spec/overcommit/hook_context/pre_commit_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/pre_commit' @@ -156,6 +158,25 @@ end end + context 'when all changes have been staged' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + `git add tracked-file` + `git commit -m "Add tracked-file"` + echo('Hello Other World', 'other-tracked-file') + `git add other-tracked-file` + example.run + end + end + + it 'does not stash changes' do + expect(context.private_methods).to include :stash_changes + expect(context).not_to receive(:stash_changes) + subject + end + end + context 'when renaming a file during an amendment' do around do |example| repo do @@ -186,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}` @@ -317,6 +338,27 @@ end end + context 'when all changes were staged' do + around do |example| + repo do + echo('Hello World', 'tracked-file') + `git add tracked-file` + `git commit -m "Add tracked-file"` + echo('Hello Other World', 'other-tracked-file') + `git add other-tracked-file` + example.run + end + end + + it 'does not touch the working tree' do + expect(context.private_methods).to include :clear_working_tree + expect(context.private_methods).to include :restore_working_tree + expect(context).not_to receive(:clear_working_tree) + expect(context).not_to receive(:restore_working_tree) + subject + end + end + context 'when there were deleted files' do around do |example| repo do @@ -341,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}` @@ -367,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}` @@ -399,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 @@ -428,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/hook_context/pre_push_spec.rb b/spec/overcommit/hook_context/pre_push_spec.rb index e8306ad2..101e0f1a 100644 --- a/spec/overcommit/hook_context/pre_push_spec.rb +++ b/spec/overcommit/hook_context/pre_push_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/pre_push' @@ -21,6 +23,49 @@ it { should == remote_url } end + describe '#remote_ref_deletion?' do + subject { context.remote_ref_deletion? } + + let(:standard_input) { "#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\n" } + + before do + input.stub(:read).and_return(standard_input) + end + + context 'when pushing new branch to remote ref' do + let(:local_ref) { 'refs/heads/test' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { '0' * 40 } + + it { should == false } + end + + context 'when pushing update to remote ref' do + let(:local_ref) { 'refs/heads/test' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { random_hash } + + it { should == false } + end + + context 'when deleting remote ref' do + let(:local_ref) { '(deleted)' } + let(:local_sha1) { '' } + let(:remote_ref) { 'refs/heads/test' } + let(:remote_sha1) { random_hash } + + it { should == true } + end + + context 'when no standard input is provided' do + let(:standard_input) { '' } + + it { should == false } + end + end + describe '#pushed_refs' do subject(:pushed_refs) { context.pushed_refs } @@ -33,7 +78,7 @@ input.stub(:read).and_return("#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\n") end - it 'should parse commit info from the input' do + it 'parses commit info from the input' do pushed_refs.length.should == 1 pushed_refs.each do |pushed_ref| pushed_ref.local_ref.should == local_ref diff --git a/spec/overcommit/hook_context/pre_rebase_spec.rb b/spec/overcommit/hook_context/pre_rebase_spec.rb index 1d800cc5..58e0e8c2 100644 --- a/spec/overcommit/hook_context/pre_rebase_spec.rb +++ b/spec/overcommit/hook_context/pre_rebase_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/pre_rebase' diff --git a/spec/overcommit/hook_context/prepare_commit_msg_spec.rb b/spec/overcommit/hook_context/prepare_commit_msg_spec.rb new file mode 100644 index 00000000..4e4c017b --- /dev/null +++ b/spec/overcommit/hook_context/prepare_commit_msg_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'overcommit/hook_context/prepare_commit_msg' + +describe Overcommit::HookContext::PrepareCommitMsg do + let(:config) { double('config') } + let(:args) { [commit_message_filename, commit_message_source] } + let(:commit_message_filename) { 'message-template.txt' } + let(:commit_message_source) { :file } + let(:commit) { 'SHA-1 here' } + let(:input) { double('input') } + let(:context) { described_class.new(config, args, input) } + + describe '#commit_message_filename' do + subject { context.commit_message_filename } + + it { should == commit_message_filename } + end + + describe '#commit_message_source' do + subject { context.commit_message_source } + + it { should == commit_message_source } + end +end diff --git a/spec/overcommit/hook_context/run_all_spec.rb b/spec/overcommit/hook_context/run_all_spec.rb index d5b0963b..516fb70b 100644 --- a/spec/overcommit/hook_context/run_all_spec.rb +++ b/spec/overcommit/hook_context/run_all_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'overcommit/hook_context/run_all' diff --git a/spec/overcommit/hook_signer_spec.rb b/spec/overcommit/hook_signer_spec.rb index 2b2805c7..98862fd0 100644 --- a/spec/overcommit/hook_signer_spec.rb +++ b/spec/overcommit/hook_signer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::HookSigner do diff --git a/spec/overcommit/installer_spec.rb b/spec/overcommit/installer_spec.rb index 622dab43..5283b2b6 100644 --- a/spec/overcommit/installer_spec.rb +++ b/spec/overcommit/installer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Installer do @@ -229,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/logger_spec.rb b/spec/overcommit/logger_spec.rb index dbd927b9..681e0dff 100644 --- a/spec/overcommit/logger_spec.rb +++ b/spec/overcommit/logger_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::Logger do diff --git a/spec/overcommit/message_processor_spec.rb b/spec/overcommit/message_processor_spec.rb index dec604d3..f90a1f0f 100644 --- a/spec/overcommit/message_processor_spec.rb +++ b/spec/overcommit/message_processor_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Overcommit::MessageProcessor do diff --git a/spec/overcommit/utils_spec.rb b/spec/overcommit/utils_spec.rb index 4a39d1a9..acaf3eba 100644 --- a/spec/overcommit/utils_spec.rb +++ b/spec/overcommit/utils_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'securerandom' @@ -117,17 +119,17 @@ describe '.supported_hook_types' do subject { described_class.supported_hook_types } - # rubocop:disable Metrics/LineLength - it { should =~ %w[commit-msg pre-commit post-checkout post-commit post-merge post-rewrite pre-push pre-rebase] } - # rubocop:enable 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 Layout/LineLength end describe '.supported_hook_type_classes' do subject { described_class.supported_hook_type_classes } - # rubocop:disable Metrics/LineLength - it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush PreRebase] } - # rubocop:enable Metrics/LineLength + # rubocop:disable Layout/LineLength + it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush PreRebase PrepareCommitMsg] } + # rubocop:enable Layout/LineLength end describe '.parent_command' do @@ -138,6 +140,14 @@ end it { should =~ /rspec/ } + + context 'when there is no parent' do + before do + Process.stub(:ppid) { 0 } + end + + it { should be_nil } + end end describe '.execute' do @@ -180,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 @@ -205,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 9070aef4..b6a24d82 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,11 +1,23 @@ -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' +# frozen_string_literal: true + +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' @@ -25,7 +37,7 @@ each { |f| require f } end -Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f } +Dir[File.dirname(__FILE__) + '/support/**/*.rb'].sort.each { |f| require f } RSpec.configure do |config| config.include GitSpecHelpers diff --git a/spec/support/git_spec_helpers.rb b/spec/support/git_spec_helpers.rb index 25b0b520..28eb94da 100644 --- a/spec/support/git_spec_helpers.rb +++ b/spec/support/git_spec_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' # Helpers for creating temporary repositories and directories for testing. @@ -11,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/spec/support/matchers/hook.rb b/spec/support/matchers/hook.rb index 21eecca2..80fbd33b 100644 --- a/spec/support/matchers/hook.rb +++ b/spec/support/matchers/hook.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # General spec matcher logic for checking hook status and output. class HookMatcher def initialize(status, args) diff --git a/spec/support/normalize_indent.rb b/spec/support/normalize_indent.rb index bef3f5c3..4276d12c 100644 --- a/spec/support/normalize_indent.rb +++ b/spec/support/normalize_indent.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Strips off excess leading indentation from each line so we can use Heredocs # for writing code without having the leading indentation count. module IndentNormalizer diff --git a/spec/support/output_helpers.rb b/spec/support/output_helpers.rb index 63ad8d2a..5364204a 100644 --- a/spec/support/output_helpers.rb +++ b/spec/support/output_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Helpers for capturing output streams in tests. module OutputHelpers module_function diff --git a/spec/support/shell_helpers.rb b/spec/support/shell_helpers.rb index 9051621a..786dac63 100644 --- a/spec/support/shell_helpers.rb +++ b/spec/support/shell_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'timeout' require 'overcommit/subprocess' @@ -21,10 +23,11 @@ def touch(file) # @param options [Hash] # @raise [Timeout::TimeoutError] timeout has elapsed before condition holds def wait_until(options = {}) - Timeout.timeout(options.fetch(:timeout, 1)) do + Timeout.timeout(options.fetch(:timeout) { 1 }) do loop do return if yield - sleep options.fetch(:check_interval, 0.1) + + sleep options.fetch(:check_interval) { 0.1 } end end end diff --git a/template-dir/hooks/commit-msg b/template-dir/hooks/commit-msg index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/commit-msg +++ b/template-dir/hooks/commit-msg @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/overcommit-hook b/template-dir/hooks/overcommit-hook index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/overcommit-hook +++ b/template-dir/hooks/overcommit-hook @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/post-checkout b/template-dir/hooks/post-checkout index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/post-checkout +++ b/template-dir/hooks/post-checkout @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/post-commit b/template-dir/hooks/post-commit index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/post-commit +++ b/template-dir/hooks/post-commit @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/post-merge b/template-dir/hooks/post-merge index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/post-merge +++ b/template-dir/hooks/post-merge @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/post-rewrite b/template-dir/hooks/post-rewrite index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/post-rewrite +++ b/template-dir/hooks/post-rewrite @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/pre-commit b/template-dir/hooks/pre-commit index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/pre-commit +++ b/template-dir/hooks/pre-commit @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/pre-push b/template-dir/hooks/pre-push index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/pre-push +++ b/template-dir/hooks/pre-push @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/pre-rebase b/template-dir/hooks/pre-rebase index b345d9c7..87ffeb58 100755 --- a/template-dir/hooks/pre-rebase +++ b/template-dir/hooks/pre-rebase @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Entrypoint for Overcommit hook integration. Installing Overcommit will result # in all of your git hooks being copied from this file, allowing the framework @@ -26,17 +27,18 @@ 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' begin Bundler.setup - rescue Bundler::BundlerError => ex - puts "Problem loading '#{gemfile}': #{ex.message}" - puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound) + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) exit 78 # EX_CONFIG end end @@ -79,37 +81,37 @@ begin status = runner.run exit(status ? 0 : 65) # 65 = EX_DATAERR -rescue Overcommit::Exceptions::ConfigurationError => error - puts error +rescue Overcommit::Exceptions::ConfigurationError => e + puts e exit 78 # EX_CONFIG -rescue Overcommit::Exceptions::HookContextLoadError => error - puts error +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e puts 'Are you running an old version of Overcommit?' exit 69 # EX_UNAVAILABLE rescue Overcommit::Exceptions::HookLoadError, - Overcommit::Exceptions::InvalidHookDefinition => error - puts error.message - puts error.backtrace + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace exit 78 # EX_CONFIG rescue Overcommit::Exceptions::HookSetupFailed, - Overcommit::Exceptions::HookCleanupFailed => error - puts error.message + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message exit 74 # EX_IOERR rescue Overcommit::Exceptions::HookCancelled puts 'You cancelled the hook run' exit 130 # Ctrl-C cancel -rescue Overcommit::Exceptions::InvalidGitRepo => error - puts error +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e exit 64 # EX_USAGE -rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error - puts error +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e puts "For more information, see #{Overcommit::REPO_URL}#security" exit 1 rescue Overcommit::Exceptions::InvalidHookSignature exit 1 -rescue StandardError => error - puts error.message - puts error.backtrace +rescue StandardError => e + puts e.message + puts e.backtrace puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" exit 70 # EX_SOFTWARE end diff --git a/template-dir/hooks/prepare-commit-msg b/template-dir/hooks/prepare-commit-msg new file mode 100755 index 00000000..87ffeb58 --- /dev/null +++ b/template-dir/hooks/prepare-commit-msg @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Entrypoint for Overcommit hook integration. Installing Overcommit will result +# in all of your git hooks being copied from this file, allowing the framework +# to manage your hooks for you. + +# Prevent a Ruby stack trace from appearing when we interrupt the hook. +# Note that this will be overridden when Overcommit is loaded, since the +# InterruptHandler will redefine the trap at that time. +Signal.trap('INT') do + puts 'Hook run interrupted' + exit 130 +end + +# Allow hooks to be disabled via environment variable so git commands can be run +# in scripts without Overcommit running hooks +if ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0 + exit +end + +hook_type = File.basename($0) +if hook_type == 'overcommit-hook' + puts "Don't run `overcommit-hook` directly; it is intended to be symlinked " \ + "by each hook in a repository's .git/hooks directory." + exit 64 # EX_USAGE +end + +# Check if Overcommit should invoke a Bundler context for loading gems +File.read('.overcommit.yml') =~ /gemfile: (?:false|['"]?(.*)['"]?)/ +gemfile = Regexp.last_match(1) + +if gemfile + ENV['BUNDLE_GEMFILE'] = gemfile + require 'bundler' + + begin + Bundler.setup + rescue Bundler::BundlerError => e + puts "Problem loading '#{gemfile}': #{e.message}" + puts "Try running:\nbundle install --gemfile=#{gemfile}" if e.is_a?(Bundler::GemNotFound) + exit 78 # EX_CONFIG + end +end +# rubocop:enable Style/RescueModifier + +begin + require 'overcommit' +rescue LoadError + if gemfile + puts 'You have specified the `gemfile` option in your Overcommit ' \ + 'configuration but have not added the `overcommit` gem to ' \ + "#{gemfile}." + else + puts 'This repository contains hooks installed by Overcommit, but the ' \ + "`overcommit` gem is not installed.\n" \ + 'Install it with `gem install overcommit`.' + end + + exit 64 # EX_USAGE +end + +begin + logger = Overcommit::Logger.new(STDOUT) + Overcommit::Utils.log = logger + + # Ensure master hook is up-to-date + installer = Overcommit::Installer.new(logger) + if installer.run(Overcommit::Utils.repo_root, action: :update) + exec($0, *ARGV) # Execute the updated hook with all original arguments + end + + config = Overcommit::ConfigurationLoader.new(logger).load_repo_config + + context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN) + config.apply_environment!(context, ENV) + + printer = Overcommit::Printer.new(config, logger, context) + runner = Overcommit::HookRunner.new(config, logger, context, printer) + + status = runner.run + + exit(status ? 0 : 65) # 65 = EX_DATAERR +rescue Overcommit::Exceptions::ConfigurationError => e + puts e + exit 78 # EX_CONFIG +rescue Overcommit::Exceptions::HookContextLoadError => e + puts e + puts 'Are you running an old version of Overcommit?' + exit 69 # EX_UNAVAILABLE +rescue Overcommit::Exceptions::HookLoadError, + Overcommit::Exceptions::InvalidHookDefinition => e + puts e.message + puts e.backtrace + exit 78 # EX_CONFIG +rescue Overcommit::Exceptions::HookSetupFailed, + Overcommit::Exceptions::HookCleanupFailed => e + puts e.message + exit 74 # EX_IOERR +rescue Overcommit::Exceptions::HookCancelled + puts 'You cancelled the hook run' + exit 130 # Ctrl-C cancel +rescue Overcommit::Exceptions::InvalidGitRepo => e + puts e + exit 64 # EX_USAGE +rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e + puts e + puts "For more information, see #{Overcommit::REPO_URL}#security" + exit 1 +rescue Overcommit::Exceptions::InvalidHookSignature + exit 1 +rescue StandardError => e + puts e.message + puts e.backtrace + puts "Report this bug at #{Overcommit::BUG_REPORT_URL}" + exit 70 # EX_SOFTWARE +end