Skip to content

Commit 5bf372f

Browse files
committed
Switch template-dir hooks from symlinks to regular files
Since symlinks are not supported on Windows, we need to swap these with regular files so that the gem can be extracted on Windows machines. We weren't hitting this problem before because it wasn't an issue in earlier versions of Rubygems. There appears to have been some changes around this in rubygems/rubygems@14b1eec7bd0f and likely elsewhere, so perhaps the only reason it worked was because of a bug. To protect against the copies of the master hooks failing out of sync, we added a hook to catch this. It's somewhat redundant since we have a spec in spec/integration/template_dir_spec.rb, but this provides quicker feedback, and we really want to make sure we don't screw up these hooks. Fixes 387
1 parent a157568 commit 5bf372f

12 files changed

+963
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'fileutils'
2+
3+
module Overcommit::Hook::PreCommit
4+
# Ensures all master hooks have the same content.
5+
#
6+
# This is necessary because we can't use symlinks to link all the hooks in the
7+
# template directory to the master `overcommit-hook` file, since symlinks are
8+
# not supported on Windows.
9+
class MasterHooksMatch < Base
10+
def run
11+
hooks_dir = File.join('template-dir', 'hooks')
12+
master_hook = File.join(hooks_dir, 'overcommit-hook')
13+
Dir.glob(File.join(hooks_dir, '*')).each do |hook_path|
14+
unless FileUtils.compare_file(master_hook, hook_path)
15+
return [
16+
:fail,
17+
"Template directory hook '#{hook_path}' does not match '#{master_hook}'!\n" \
18+
"Run `cp #{master_hook} #{hook_path}`"
19+
]
20+
end
21+
end
22+
23+
:pass
24+
end
25+
end
26+
end

.overcommit.yml

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PreCommit:
1414
HardTabs:
1515
enabled: true
1616

17+
MasterHooksMatch:
18+
enabled: true
19+
quiet: true
20+
1721
RuboCop:
1822
enabled: true
1923
include:

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Overcommit Changelog
22

3+
## master (unreleased)
4+
5+
* Switch template directory hooks from symlinks to regular files so gem can
6+
be installed on Windows
7+
38
## 0.34.0
49

510
* Fix `Scalastyle` pre-commit hook to capture messages with no line number

spec/integration/template_dir_spec.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'spec_helper'
2+
require 'fileutils'
23

34
describe 'template directory' do
45
let(:template_dir) { File.join(Overcommit::HOME, 'template-dir') }
@@ -16,15 +17,16 @@
1617
Overcommit::Utils::FileUtils.symlink?(master_hook).should == false
1718
end
1819

19-
it 'contains all other hooks as symlinks to the master hook' do
20-
if Overcommit::OS.windows?
21-
# Symlinks in template-dir are not compatible with Windows.
22-
# Windows users will need to manually install Overcommit for now.
23-
skip 'Unix symlinks not compatible with Windows'
20+
it 'contains all other hooks as copies of the master hook' do
21+
Overcommit::Utils.supported_hook_types.each do |hook_type|
22+
FileUtils.compare_file(File.join(hooks_dir, hook_type),
23+
File.join(hooks_dir, 'overcommit-hook')).should == true
2424
end
25+
end
2526

27+
it 'contains no symlinks' do
2628
Overcommit::Utils.supported_hook_types.each do |hook_type|
27-
Overcommit::Utils::FileUtils.symlink?(File.join(hooks_dir, hook_type)).should == true
29+
Overcommit::Utils::FileUtils.symlink?(File.join(hooks_dir, hook_type)).should == false
2830
end
2931
end
3032
end

template-dir/hooks/commit-msg

-1
This file was deleted.

template-dir/hooks/commit-msg

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env ruby
2+
3+
# Entrypoint for Overcommit hook integration. Installing Overcommit will result
4+
# in all of your git hooks being symlinked to this file, allowing the framework
5+
# to manage your hooks for you.
6+
7+
# Prevent a Ruby stack trace from appearing when we interrupt the hook.
8+
# Note that this will be overridden when Overcommit is loaded, since the
9+
# InterruptHandler will redefine the trap at that time.
10+
Signal.trap('INT') do
11+
puts 'Hook run interrupted'
12+
exit 130
13+
end
14+
15+
# Allow hooks to be disabled via environment variable so git commands can be run
16+
# in scripts without Overcommit running hooks
17+
if ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0
18+
exit
19+
end
20+
21+
hook_type = File.basename($0)
22+
if hook_type == 'overcommit-hook'
23+
puts "Don't run `overcommit-hook` directly; it is intended to be symlinked " \
24+
"by each hook in a repository's .git/hooks directory."
25+
exit 64 # EX_USAGE
26+
end
27+
28+
# Check if Overcommit should invoke a Bundler context for loading gems
29+
require 'yaml'
30+
# rubocop:disable Style/RescueModifier
31+
if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
32+
ENV['BUNDLE_GEMFILE'] = gemfile
33+
require 'bundler'
34+
35+
begin
36+
Bundler.setup
37+
rescue Bundler::BundlerError => ex
38+
puts "Problem loading '#{gemfile}': #{ex.message}"
39+
puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound)
40+
exit 78 # EX_CONFIG
41+
end
42+
end
43+
# rubocop:enable Style/RescueModifier
44+
45+
begin
46+
require 'overcommit'
47+
rescue LoadError
48+
if gemfile
49+
puts 'You have specified the `gemfile` option in your Overcommit ' \
50+
'configuration but have not added the `overcommit` gem to ' \
51+
"#{gemfile}."
52+
else
53+
puts 'This repository contains hooks installed by Overcommit, but the ' \
54+
"`overcommit` gem is not installed.\n" \
55+
'Install it with `gem install overcommit`.'
56+
end
57+
58+
exit 64 # EX_USAGE
59+
end
60+
61+
begin
62+
logger = Overcommit::Logger.new(STDOUT)
63+
Overcommit::Utils.log = logger
64+
65+
# Ensure master hook is up-to-date
66+
installer = Overcommit::Installer.new(logger)
67+
if installer.run(Overcommit::Utils.repo_root, action: :update)
68+
exec($0, *ARGV) # Execute the updated hook with all original arguments
69+
end
70+
71+
config = Overcommit::ConfigurationLoader.new(logger).load_repo_config
72+
73+
context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)
74+
config.apply_environment!(context, ENV)
75+
76+
printer = Overcommit::Printer.new(config, logger, context)
77+
runner = Overcommit::HookRunner.new(config, logger, context, printer)
78+
79+
status = runner.run
80+
81+
exit(status ? 0 : 65) # 65 = EX_DATAERR
82+
rescue Overcommit::Exceptions::ConfigurationError => error
83+
puts error
84+
exit 78 # EX_CONFIG
85+
rescue Overcommit::Exceptions::HookContextLoadError => error
86+
puts error
87+
puts 'Are you running an old version of Overcommit?'
88+
exit 69 # EX_UNAVAILABLE
89+
rescue Overcommit::Exceptions::HookLoadError,
90+
Overcommit::Exceptions::InvalidHookDefinition => error
91+
puts error.message
92+
puts error.backtrace
93+
exit 78 # EX_CONFIG
94+
rescue Overcommit::Exceptions::HookSetupFailed,
95+
Overcommit::Exceptions::HookCleanupFailed => error
96+
puts error.message
97+
exit 74 # EX_IOERR
98+
rescue Overcommit::Exceptions::HookCancelled
99+
puts 'You cancelled the hook run'
100+
exit 130 # Ctrl-C cancel
101+
rescue Overcommit::Exceptions::InvalidGitRepo => error
102+
puts error
103+
exit 64 # EX_USAGE
104+
rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error
105+
puts error
106+
puts "For more information, see #{Overcommit::REPO_URL}#security"
107+
exit 1
108+
rescue Overcommit::Exceptions::InvalidHookSignature
109+
exit 1
110+
rescue => error
111+
puts error.message
112+
puts error.backtrace
113+
puts "Report this bug at #{Overcommit::BUG_REPORT_URL}"
114+
exit 70 # EX_SOFTWARE
115+
end

template-dir/hooks/post-checkout

-1
This file was deleted.

template-dir/hooks/post-checkout

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env ruby
2+
3+
# Entrypoint for Overcommit hook integration. Installing Overcommit will result
4+
# in all of your git hooks being symlinked to this file, allowing the framework
5+
# to manage your hooks for you.
6+
7+
# Prevent a Ruby stack trace from appearing when we interrupt the hook.
8+
# Note that this will be overridden when Overcommit is loaded, since the
9+
# InterruptHandler will redefine the trap at that time.
10+
Signal.trap('INT') do
11+
puts 'Hook run interrupted'
12+
exit 130
13+
end
14+
15+
# Allow hooks to be disabled via environment variable so git commands can be run
16+
# in scripts without Overcommit running hooks
17+
if ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0
18+
exit
19+
end
20+
21+
hook_type = File.basename($0)
22+
if hook_type == 'overcommit-hook'
23+
puts "Don't run `overcommit-hook` directly; it is intended to be symlinked " \
24+
"by each hook in a repository's .git/hooks directory."
25+
exit 64 # EX_USAGE
26+
end
27+
28+
# Check if Overcommit should invoke a Bundler context for loading gems
29+
require 'yaml'
30+
# rubocop:disable Style/RescueModifier
31+
if gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil
32+
ENV['BUNDLE_GEMFILE'] = gemfile
33+
require 'bundler'
34+
35+
begin
36+
Bundler.setup
37+
rescue Bundler::BundlerError => ex
38+
puts "Problem loading '#{gemfile}': #{ex.message}"
39+
puts "Try running:\nbundle install --gemfile=#{gemfile}" if ex.is_a?(Bundler::GemNotFound)
40+
exit 78 # EX_CONFIG
41+
end
42+
end
43+
# rubocop:enable Style/RescueModifier
44+
45+
begin
46+
require 'overcommit'
47+
rescue LoadError
48+
if gemfile
49+
puts 'You have specified the `gemfile` option in your Overcommit ' \
50+
'configuration but have not added the `overcommit` gem to ' \
51+
"#{gemfile}."
52+
else
53+
puts 'This repository contains hooks installed by Overcommit, but the ' \
54+
"`overcommit` gem is not installed.\n" \
55+
'Install it with `gem install overcommit`.'
56+
end
57+
58+
exit 64 # EX_USAGE
59+
end
60+
61+
begin
62+
logger = Overcommit::Logger.new(STDOUT)
63+
Overcommit::Utils.log = logger
64+
65+
# Ensure master hook is up-to-date
66+
installer = Overcommit::Installer.new(logger)
67+
if installer.run(Overcommit::Utils.repo_root, action: :update)
68+
exec($0, *ARGV) # Execute the updated hook with all original arguments
69+
end
70+
71+
config = Overcommit::ConfigurationLoader.new(logger).load_repo_config
72+
73+
context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)
74+
config.apply_environment!(context, ENV)
75+
76+
printer = Overcommit::Printer.new(config, logger, context)
77+
runner = Overcommit::HookRunner.new(config, logger, context, printer)
78+
79+
status = runner.run
80+
81+
exit(status ? 0 : 65) # 65 = EX_DATAERR
82+
rescue Overcommit::Exceptions::ConfigurationError => error
83+
puts error
84+
exit 78 # EX_CONFIG
85+
rescue Overcommit::Exceptions::HookContextLoadError => error
86+
puts error
87+
puts 'Are you running an old version of Overcommit?'
88+
exit 69 # EX_UNAVAILABLE
89+
rescue Overcommit::Exceptions::HookLoadError,
90+
Overcommit::Exceptions::InvalidHookDefinition => error
91+
puts error.message
92+
puts error.backtrace
93+
exit 78 # EX_CONFIG
94+
rescue Overcommit::Exceptions::HookSetupFailed,
95+
Overcommit::Exceptions::HookCleanupFailed => error
96+
puts error.message
97+
exit 74 # EX_IOERR
98+
rescue Overcommit::Exceptions::HookCancelled
99+
puts 'You cancelled the hook run'
100+
exit 130 # Ctrl-C cancel
101+
rescue Overcommit::Exceptions::InvalidGitRepo => error
102+
puts error
103+
exit 64 # EX_USAGE
104+
rescue Overcommit::Exceptions::ConfigurationSignatureChanged => error
105+
puts error
106+
puts "For more information, see #{Overcommit::REPO_URL}#security"
107+
exit 1
108+
rescue Overcommit::Exceptions::InvalidHookSignature
109+
exit 1
110+
rescue => error
111+
puts error.message
112+
puts error.backtrace
113+
puts "Report this bug at #{Overcommit::BUG_REPORT_URL}"
114+
exit 70 # EX_SOFTWARE
115+
end

template-dir/hooks/post-commit

-1
This file was deleted.

0 commit comments

Comments
 (0)