-
Notifications
You must be signed in to change notification settings - Fork 279
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
-diff
cli option for running precommit hooks against diffs (#860)
For example, running `overcommit --diff main` from a feature branch will run pre-commit hooks against the diff between the two branches. I was able to very easily leverage existing code for the bulk of the feature - this is mainly just adding the cli option, a hook context to do the execution and some tests based on the existing `--run-all` test. --- For background, my team is responsible for a couple of really old, really large rails apps. Getting them completely in compliance with our various linters is a huge task that isn't getting done anytime soon (things are funky to the point that we've even observed breakages with "safe" auto-correct functions). I introduced/started heavily encouraging overcommit so that we at least don't add _new_ linting offenses and things will naturally improve over time. It's been great, but offenses still slip through though here and there, especially with juniors who might be getting away with not having a local install (and/or abusing `OVERCOMMIT_DISABLE=1`). An option like this would allow me to leverage the very useful "only apply to changed lines" logic within a ci environment and help enforce my desired "no new linting offenses" policy.
- Loading branch information
Showing
7 changed files
with
272 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'overcommit/git_repo' | ||
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |