Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,12 @@ and any configuration for the plugin. Thus a change to the plugin's source
code or your local repo's `.overcommit.yml` file could result in a signature
change.

Signatures are stored in your local repo's git configuration. If you use
multiple [worktrees](https://git-scm.com/docs/git-worktree) whose
configurations differ, enable `extensions.worktreeConfig` (`git config
extensions.worktreeConfig true`) so that Overcommit stores signatures per
worktree and signing in one worktree does not invalidate the others.

### Disabling Signature Checking

In typical usage, your plugins usually don't change too often, so this warning
Expand Down
11 changes: 7 additions & 4 deletions lib/overcommit/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,9 @@ def verify_signatures?
return false if ENV['OVERCOMMIT_NO_VERIFY']
return true if @hash['verify_signatures'] != false

scope = Overcommit::GitConfig.signature_scope
result = Overcommit::Utils.execute(
%W[git config --local --get #{verify_signature_config_key}]
%W[git config #{scope} --get #{verify_signature_config_key}]
)

if result.status == 1 # Key doesn't exist
Expand All @@ -232,13 +233,14 @@ def verify_signatures?

# Update the currently stored signature for this hook.
def update_signature!
scope = Overcommit::GitConfig.signature_scope
result = Overcommit::Utils.execute(
%w[git config --local] + [signature_config_key, signature]
%W[git config #{scope}] + [signature_config_key, signature]
)

verify_signature_value = @hash['verify_signatures'] ? 1 : 0
result &&= Overcommit::Utils.execute(
%W[git config --local #{verify_signature_config_key} #{verify_signature_value}]
%W[git config #{scope} #{verify_signature_config_key} #{verify_signature_value}]
)

unless result.success?
Expand Down Expand Up @@ -325,8 +327,9 @@ def signature
#
# @return [String]
def stored_signature
scope = Overcommit::GitConfig.signature_scope
result = Overcommit::Utils.execute(
%w[git config --local --get] + [signature_config_key]
%W[git config #{scope} --get] + [signature_config_key]
)

if result.status == 1 # Key doesn't exist
Expand Down
8 changes: 8 additions & 0 deletions lib/overcommit/git_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,13 @@ def hooks_path

File.expand_path(path, Dir.pwd)
end

# Config file option for storing hook signatures: the per-worktree file
# when extensions.worktreeConfig is on, so worktrees with different
# configurations do not overwrite each other's signatures.
def signature_scope
enabled = `git config --bool --get extensions.worktreeConfig`.chomp
enabled == 'true' ? '--worktree' : '--local'
end
end
end
6 changes: 4 additions & 2 deletions lib/overcommit/hook_signer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ def signature_changed?

# Update the current stored signature for this hook.
def update_signature!
scope = Overcommit::GitConfig.signature_scope
result = Overcommit::Utils.execute(
%w[git config --local] + [signature_config_key, signature]
%W[git config #{scope}] + [signature_config_key, signature]
)

unless result.success?
Expand Down Expand Up @@ -103,8 +104,9 @@ def hook_contents
end

def stored_signature
scope = Overcommit::GitConfig.signature_scope
result = Overcommit::Utils.execute(
%w[git config --local --get] + [signature_config_key]
%W[git config #{scope} --get] + [signature_config_key]
)

if result.status == 1 # Key doesn't exist
Expand Down
104 changes: 104 additions & 0 deletions spec/integration/worktree_signing_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

require 'spec_helper'
require 'yaml'

describe 'configuration file signing with multiple worktrees' do
let(:enable_verification) { true }
let(:verify_signatures) { true }

let(:config) do
{
'verify_signatures' => verify_signatures,
'CommitMsg' => {
'ALL' => { 'enabled' => false },
},
'PreCommit' => {
'ALL' => { 'enabled' => false },
},
}
end

let(:linked_config) { config }
let(:sign_linked_worktree) { true }

around do |example|
repo do
`git config --local extensions.worktreeConfig true` if worktree_config
`git commit --allow-empty -m "Initial commit"`
linked_worktree = File.join(File.dirname(Dir.pwd), 'linked-worktree')
result = Overcommit::Utils.execute(
%W[git worktree add -b linked-worktree #{linked_worktree}],
)
raise "Unable to create worktree: #{result.stderr}" unless result.success?

echo(config.to_yaml, '.overcommit.yml')
`overcommit --install > #{File::NULL}`
`overcommit --sign`

Dir.chdir(linked_worktree) do
echo(linked_config.to_yaml, '.overcommit.yml')
`overcommit --sign` if sign_linked_worktree
end

@linked_worktree = linked_worktree
example.run
end
end

def commit_in_main_worktree
shell(%w[git commit --allow-empty -m Test])
end

def commit_in_linked_worktree
Dir.chdir(@linked_worktree) { shell(%w[git commit --allow-empty -m Test]) }
end

context 'when the worktrees have different configurations' do
let(:linked_config) do
config.merge('PreCommit' => config['PreCommit'].merge('AuthorName' => { 'enabled' => true }))
end

context 'and extensions.worktreeConfig is not enabled' do
let(:worktree_config) { false }

it 'reports a signature error in the worktree signed first' do
result = commit_in_main_worktree
result.status.should_not == 0
result.stderr.should include 'Signature of configuration file has changed!'
end
end

context 'and extensions.worktreeConfig is enabled' do
let(:worktree_config) { true }

it 'does not report a signature error in either worktree' do
commit_in_main_worktree.status.should == 0
commit_in_linked_worktree.status.should == 0
end
end
end

context 'when verify_signatures is false and only the main worktree is signed' do
let(:verify_signatures) { false }
let(:sign_linked_worktree) { false }

context 'and extensions.worktreeConfig is not enabled' do
let(:worktree_config) { false }

it 'shares the recorded verify_signatures setting with the linked worktree' do
commit_in_linked_worktree.status.should == 0
end
end

context 'and extensions.worktreeConfig is enabled' do
let(:worktree_config) { true }

it 'requires the linked worktree to record its own verify_signatures setting' do
result = commit_in_linked_worktree
result.status.should_not == 0
result.stderr.should include 'No previously recorded signature for configuration file.'
end
end
end
end
36 changes: 36 additions & 0 deletions spec/overcommit/git_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,40 @@
end
end
end

describe '.signature_scope' do
subject { described_class.signature_scope }

context 'when extensions.worktreeConfig is not set' do
around do |example|
repo do
example.run
end
end

it { should == '--local' }
end

context 'when extensions.worktreeConfig is false' do
around do |example|
repo do
`git config --local extensions.worktreeConfig false`
example.run
end
end

it { should == '--local' }
end

context 'when extensions.worktreeConfig is true' do
around do |example|
repo do
`git config --local extensions.worktreeConfig true`
example.run
end
end

it { should == '--worktree' }
end
end
end
Loading