git-hooks 0.0.2 → 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 587587daeb1f31d5403c88ee666acb9daab9b61e
4
- data.tar.gz: 6a6a8fec7c3b4e5521714cbb60a4a39c6affbdc4
3
+ metadata.gz: e0e5a81167e56d3073667b763323fc77a08e59a0
4
+ data.tar.gz: 5118f531854a6eff62bbf7223241ff923398c749
5
5
  SHA512:
6
- metadata.gz: ad395057c328251a5405bbda59c3e820e36719dde4ab97439d39ab659af13463bebeb840e68e46d91b8d825324b431c8dbbab41f73d4d1a8b0fc781a99971ba2
7
- data.tar.gz: e5f914bf2a87e60e9575761f1439ae59f8846f81ddf4c0bdd45453c49468550a37dc597e5d8a8f0908e0b2f4204290cd32b342cf01d7f4a405dcbaaecfb4f84b
6
+ metadata.gz: d0583731adca1bda4f29862ab14134576e3ffbdc999eed66e7f9b6117186f73fe65d5d0329bcb78bffc0a73959120a2a6ed6b898d7273deb96144f8761383819
7
+ data.tar.gz: e4a7455fa439e266b8949477633b2f7676c271359979939579225a2ab00c18565f15183c8c3dce55ade26d2da5ce811c0d6781d63beaddb8d74189415ff2fc4d
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --warnings
3
+ --require spec_helper
data/README.md CHANGED
@@ -17,42 +17,38 @@ Or install it yourself as:
17
17
  $ gem install git-hooks
18
18
 
19
19
  ## Usage
20
+ ### Install git_hooks on project.
20
21
 
21
- By now you will find only some simple hooks to:
22
-
23
- - Prevent commit on master.
24
- - Prevent commit with rubocop offences.
25
- - prevent commit with broken rspec tests.
26
-
27
- In the future, some validations will be
28
- added, such as:
22
+ ```
23
+ $ git_hooks install pre-commit
24
+ ```
29
25
 
30
- - ensure hooks exists on ```.git/hooks```
26
+ ### Create configuration file
31
27
 
32
- By now, if you want all this validations, you should include a
33
- ```.git/hooks/pre-commit``` with:
28
+ Create a ```.git_hooks.yml``` on project root.
34
29
 
30
+ ```bash
31
+ $ git_hooks configure
35
32
  ```
36
- #!/usr/bin/env ruby
37
- require 'git-hooks'
38
33
 
39
- GitHooks::PreCommit.validate
40
- ```
41
34
 
42
- If you want specific validation include:
43
- ```
44
- GitHooks::PreCommit::PreventMaster.new.validate
45
- ```
35
+ By now you will find only some simple hooks to:
46
36
 
47
- ```
48
- GitHooks::PreCommit::Rspec.new.validate
49
- ```
37
+ - Prevent commit on master.
38
+ - Prevent commit with rubocop offences.
39
+ - prevent commit with broken rspec tests.
50
40
 
51
- or
41
+ ### Ensure hooks existence
52
42
 
43
+ To ensure that hooks exists on ```.git/hooks```, include on your application
44
+ start up (probably ```config/environments/development.rb``` or
45
+ ```config/environments/test.rb```)
46
+
47
+ ```ruby
48
+ GitHooks.validate_hooks!
53
49
  ```
54
- GitHooks::PreCommit::Rubocop.new.validate
55
- ```
50
+
51
+ This will force ```git_hooks``` installation before your application start.
56
52
 
57
53
  ## Contributing
58
54
 
data/bin/git_hooks ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/git_hooks/cli'
4
+
5
+ GitHooks::CLI.start
data/git-hooks.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = GitHooks::VERSION
9
9
  spec.authors = ['Rafael da Silva Almeida']
10
10
  spec.email = ['eusou@rafaelalmeida.net']
11
- spec.summary = %q(Help to keep git hooks organized.)
12
- spec.description = %q(It stores git hooks and force git hooks installation.)
11
+ spec.summary = 'Help to keep git hooks organized.'
12
+ spec.description = 'It stores git hooks and force git hooks installation.'
13
13
  spec.homepage = 'http://github.com/stupied4ever/ruby-git-hooks'
14
14
  spec.license = 'DWTF'
15
15
 
@@ -19,6 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_dependency 'rubocop', '~> 0.23'
22
+ spec.add_dependency 'thor', '~> 0.19'
23
+ spec.add_dependency 'git', '~> 1.2'
24
+ spec.add_dependency 'equalizer', '~> 0.0.9'
22
25
 
23
26
  spec.add_development_dependency 'bundler', '~> 1.6'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
24
28
  end
@@ -0,0 +1,5 @@
1
+ ---
2
+ pre_commits:
3
+ - GitHooks::PreCommit::PreventMaster
4
+ - GitHooks::PreCommit::Rubocop
5
+ - GitHooks::PreCommit::Rspec
data/hook.sample ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-hooks'
3
+
4
+ GitHooks.execute_pre_commits
data/lib/git-hooks.rb CHANGED
@@ -1 +1,63 @@
1
+ require 'equalizer'
2
+ require 'yaml'
3
+
4
+ require 'git'
5
+
6
+ require_relative 'git_hooks/configurations'
7
+ require_relative 'git_hooks/git'
8
+ require_relative 'git_hooks/config_file'
9
+ require_relative 'git_hooks/rspec_executor'
10
+ require_relative 'git_hooks/rubocop_validator'
11
+
12
+ require_relative 'git_hooks/exceptions'
1
13
  require_relative 'git_hooks/pre_commit'
14
+
15
+ module GitHooks
16
+ HOOK_SAMPLE_FILE = 'hook.sample'
17
+ HOOKS = [PRE_COMMIT = 'pre-commit']
18
+
19
+ class << self
20
+ attr_writer :configurations
21
+
22
+ def execute_pre_commits
23
+ configurations.pre_commits.each do |pre_commit|
24
+ GitHooks::PreCommit.const_get(pre_commit).validate
25
+ end
26
+ end
27
+
28
+ def configurations
29
+ @configurations ||= Configurations.new
30
+ end
31
+
32
+ def base_path
33
+ File.absolute_path(File.join(File.expand_path(__FILE__), '..', '..'))
34
+ end
35
+
36
+ def hook_installed?(hook)
37
+ hook_file = File.join(Dir.pwd, '.git', 'hooks', hook)
38
+ real_hook_file = File.join(base_path, HOOK_SAMPLE_FILE)
39
+
40
+ return false unless File.symlink?(hook_file)
41
+ File.realpath(hook_file) == real_hook_file
42
+ end
43
+
44
+ def install_hook(hook)
45
+ File.symlink(real_hook_template_path, ".git/hooks/#{hook}")
46
+ end
47
+
48
+ def validate_hooks!
49
+ fail Exceptions::MissingHook, PRE_COMMIT unless valid_pre_commit_hook?
50
+ end
51
+
52
+ private
53
+
54
+ def valid_pre_commit_hook?
55
+ return true if configurations.pre_commits.empty?
56
+ hook_installed?(PRE_COMMIT)
57
+ end
58
+
59
+ def real_hook_template_path
60
+ File.join(base_path, HOOK_SAMPLE_FILE)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,31 @@
1
+ require 'thor'
2
+
3
+ require_relative '../git-hooks'
4
+
5
+ module GitHooks
6
+ class CLI < Thor
7
+ desc 'install HOOK', 'Install some hook'
8
+ long_desc <<-LONGDESC
9
+ Install some hook:
10
+
11
+ > $ git_hooks install pre-commit
12
+ LONGDESC
13
+ def install(hook)
14
+ GitHooks.install_hook(hook)
15
+ end
16
+
17
+ desc 'Create configuration file', 'Create a configuration file'
18
+ long_desc <<-LONGDESC
19
+ Create a configuration file base on git_hooks.yml.examle
20
+
21
+ > $ git_hooks configure
22
+ LONGDESC
23
+ def configure
24
+ example_file = File.expand_path(
25
+ '../../../git_hooks.yml.example', __FILE__
26
+ )
27
+ destination_path = File.expand_path('.git_hooks.yml', Dir.pwd)
28
+ FileUtils.cp(example_file, destination_path)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module GitHooks
2
+ class ConfigFile
3
+ def initialize(path)
4
+ @content = YAML.load_file(path)
5
+ rescue Errno::ENOENT
6
+ @content = {}
7
+ end
8
+
9
+ def pre_commits
10
+ content['pre_commits'] || []
11
+ end
12
+
13
+ def content
14
+ @content || { 'pre_commits' => [] }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'forwardable'
2
+
3
+ module GitHooks
4
+ class Configurations
5
+ include Equalizer.new(:config_file, :git_repository)
6
+ extend Forwardable
7
+
8
+ attr_reader :config_file, :git_repository
9
+
10
+ def_delegator :config_file, :pre_commits
11
+
12
+ def initialize(
13
+ config_file: ConfigFile.new('.git_hooks.yml'),
14
+ git_repository: Git.new('.')
15
+ )
16
+ @config_file = config_file
17
+ @git_repository = git_repository
18
+ end
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ require_relative 'exceptions/missing_hooks'
@@ -0,0 +1,9 @@
1
+ module GitHooks
2
+ module Exceptions
3
+ class MissingHook < RuntimeError
4
+ def initialize(hook)
5
+ super "Please install #{hook} hook."
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+
3
+ module GitHooks
4
+ class Git
5
+ extend Forwardable
6
+
7
+ attr_reader :working_folder
8
+
9
+ def_delegator :repository, :current_branch
10
+
11
+ def initialize(working_folder)
12
+ @working_folder = working_folder
13
+ end
14
+
15
+ def repository
16
+ ::Git.open(working_folder)
17
+ end
18
+
19
+ def added_or_modified
20
+ added = repository.status.added
21
+ modified = repository.status.changed
22
+
23
+ added.merge(modified).keys
24
+ end
25
+
26
+ def clean?
27
+ (added_files + modified_files + deleted_files + untracked_files).empty?
28
+ end
29
+
30
+ private
31
+
32
+ def added_files
33
+ repository.status.added.keys
34
+ end
35
+
36
+ def modified_files
37
+ repository.status.changed.keys
38
+ end
39
+
40
+ def deleted_files
41
+ repository.status.deleted.keys
42
+ end
43
+
44
+ def untracked_files
45
+ repository.status.untracked.keys
46
+ end
47
+ end
48
+ end
@@ -4,10 +4,5 @@ require_relative 'pre_commit/rubocop'
4
4
 
5
5
  module GitHooks
6
6
  module PreCommit
7
- def self.validate
8
- PreventMaster.new.validate
9
- Rubocop.new.validate
10
- Rspec.new.validate
11
- end
12
7
  end
13
8
  end
@@ -1,10 +1,26 @@
1
1
  module GitHooks
2
2
  module PreCommit
3
3
  class PreventMaster
4
+ attr_reader :git_repository
5
+
6
+ def self.validate
7
+ new(GitHooks.configurations.git_repository).validate
8
+ end
9
+
10
+ def initialize(git_repository)
11
+ @git_repository = git_repository
12
+ end
13
+
4
14
  def validate
5
- return unless `git rev-parse --abbrev-ref HEAD`.strip == 'master'
15
+ abort 'Prevented to commit on master' if on_master?
16
+ end
17
+
18
+ private
19
+
20
+ BRANCH_MASTER = 'master'
6
21
 
7
- abort 'Prevented to commit on master'
22
+ def on_master?
23
+ git_repository.current_branch == BRANCH_MASTER
8
24
  end
9
25
  end
10
26
  end
@@ -1,10 +1,22 @@
1
1
  module GitHooks
2
2
  module PreCommit
3
3
  class Rspec
4
+ attr_reader :git_repository, :rspec_executor
5
+
6
+ def self.validate
7
+ new(
8
+ GitHooks.configurations.git_repository, RspecExecutor.new
9
+ ).validate
10
+ end
11
+
12
+ def initialize(git_repository, rspec_executor)
13
+ @git_repository, @rspec_executor = git_repository, rspec_executor
14
+ end
15
+
4
16
  def validate
5
- return if system('bundle exec rspec --format=progress')
17
+ return if git_repository.clean?
6
18
 
7
- abort 'Prevented broken commit'
19
+ abort 'Prevented broken commit' if rspec_executor.errors?
8
20
  end
9
21
  end
10
22
  end
@@ -1,28 +1,32 @@
1
1
  module GitHooks
2
2
  module PreCommit
3
3
  class Rubocop
4
+ attr_reader :git_repository, :rubocop_validator
5
+
6
+ def self.validate
7
+ new(
8
+ GitHooks.configurations.git_repository, RubocopValidator.new
9
+ ).validate
10
+ end
11
+
12
+ def initialize(git_repository, rubocop_validator)
13
+ @git_repository, @rubocop_validator = git_repository, rubocop_validator
14
+ end
15
+
4
16
  def validate
5
- abort 'Check rubocop offences' unless run_rubocop
17
+ abort 'Check rubocop offences' if offences?
6
18
  end
7
19
 
8
20
  private
9
21
 
10
- ADDED_OR_MODIFIED = /A|AM|^M/.freeze
11
-
12
- def changed_ruby_files
13
- `git status --porcelain`.split(/\n/)
14
- .select { |file| file =~ ADDED_OR_MODIFIED }
15
- .map { |file| file.split(' ')[1] }
22
+ def changed_files
23
+ git_repository
24
+ .added_or_modified
16
25
  .select { |file| File.extname(file) == '.rb' }
17
26
  end
18
27
 
19
- def run_rubocop
20
- files = changed_ruby_files
21
- if files.any?
22
- system("rubocop #{files.join(' ')} --force-exclusion")
23
- else
24
- true
25
- end
28
+ def offences?
29
+ rubocop_validator.errors?(changed_files)
26
30
  end
27
31
  end
28
32
  end
@@ -0,0 +1,13 @@
1
+ module GitHooks
2
+ class RspecExecutor
3
+ def errors?
4
+ !system(rspec_command)
5
+ end
6
+
7
+ private
8
+
9
+ def rspec_command
10
+ 'bundle exec rspec --format=progress'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module GitHooks
2
+ class RubocopValidator
3
+ def errors?(files)
4
+ return false if files.empty?
5
+
6
+ system("rubocop #{files.join(' ')} --force-exclusion") == false
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module GitHooks
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0.pre1'
3
3
  end
@@ -0,0 +1,29 @@
1
+ module GitHooks
2
+ describe ConfigFile do
3
+ subject(:config) { described_class.new(path) }
4
+
5
+ let(:path) { '' }
6
+ let(:content) { { 'pre_commits' => pre_commits } }
7
+ let(:pre_commits) { %w(foo bar) }
8
+
9
+ context 'when the given file does not exists' do
10
+ subject(:config) { -> { described_class.new(path) } }
11
+
12
+ let(:path) { 'some-not-existent-file' }
13
+
14
+ it { is_expected.to_not raise_error }
15
+ end
16
+
17
+ describe '#pre_commits' do
18
+ subject { config.pre_commits }
19
+
20
+ before do
21
+ allow(YAML).to receive(:load_file).and_return(content)
22
+ end
23
+
24
+ it 'has the pre commits specified on hook file' do
25
+ is_expected.to eq(%w(foo bar))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,82 @@
1
+ module GitHooks
2
+ describe Configurations do
3
+ subject(:configurations) { described_class.new }
4
+
5
+ before do
6
+ allow(ConfigFile).to receive(:new).and_return(file)
7
+ allow(Git).to receive(:new).and_return(repository)
8
+ end
9
+
10
+ let(:file) { instance_double(ConfigFile) }
11
+ let(:repository) { instance_double(Git) }
12
+
13
+ describe '#pre_commits' do
14
+ subject { configurations.pre_commits }
15
+
16
+ let(:configurations) { described_class.new(config_file: config_file) }
17
+ let(:pre_commits) { %w(foo, bar) }
18
+
19
+ let(:config_file) do
20
+ instance_double(ConfigFile, pre_commits: pre_commits)
21
+ end
22
+
23
+ it { is_expected.to eq(pre_commits) }
24
+ end
25
+
26
+ describe '#config_file' do
27
+ subject(:config_file) { configurations.config_file }
28
+
29
+ let(:config_path) { '.git_hooks.yml' }
30
+
31
+ it { is_expected.to eq(file) }
32
+
33
+ it 'creates a config file with config_path' do
34
+ expect(ConfigFile).to receive(:new).with(config_path)
35
+
36
+ configurations
37
+ end
38
+
39
+ context 'with a config_path set' do
40
+ let(:configurations) do
41
+ described_class.new(config_file: file)
42
+ end
43
+
44
+ it 'does not creates a config file' do
45
+ expect(ConfigFile).to_not receive(:new)
46
+
47
+ configurations
48
+ end
49
+
50
+ it { is_expected.to eq(file) }
51
+ end
52
+ end
53
+
54
+ describe '#git_repository' do
55
+ subject(:git_repository) { configurations.git_repository }
56
+
57
+ let(:git_folder) { '.' }
58
+
59
+ it { is_expected.to eq(repository) }
60
+
61
+ it 'creates git repository with git folder' do
62
+ expect(Git).to receive(:new).with(git_folder)
63
+
64
+ git_repository
65
+ end
66
+
67
+ context 'with a git_repository set' do
68
+ let(:git_repository) do
69
+ described_class.new(git_repository: repository)
70
+ end
71
+
72
+ it 'does not creates a git repository' do
73
+ expect(Git).to_not receive(:new)
74
+
75
+ git_repository
76
+ end
77
+
78
+ it { is_expected.to eq(repository) }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,115 @@
1
+ module GitHooks
2
+ describe Git do
3
+ subject(:git) { described_class.new(working_folder) }
4
+
5
+ let(:working_folder) { 'some folder' }
6
+
7
+ describe '#working_folder' do
8
+ subject { git.working_folder }
9
+
10
+ it { is_expected.to eq(working_folder) }
11
+ end
12
+
13
+ describe '#repository' do
14
+ subject(:opened_repository) { git.repository }
15
+
16
+ let(:repository) { instance_double(::Git::Base) }
17
+
18
+ before do
19
+ allow(::Git).to receive(:open).and_return(repository)
20
+ end
21
+
22
+ it 'open git repository' do
23
+ expect(::Git).to receive(:open).with(working_folder)
24
+ opened_repository
25
+ end
26
+
27
+ it { is_expected.to eq(repository) }
28
+ end
29
+
30
+ describe '#added_or_modified' do
31
+ subject(:added_or_modified) { git.added_or_modified }
32
+
33
+ let(:repository) { instance_double(::Git::Base, status: status) }
34
+
35
+ let(:status) do
36
+ instance_double(::Git::Status, added: added, changed: changed)
37
+ end
38
+
39
+ let(:added) { { foo: 'bar' } }
40
+ let(:changed) { { bar: 'foo' } }
41
+
42
+ before do
43
+ allow(::Git).to receive(:open).and_return(repository)
44
+ end
45
+
46
+ it 'merges added and modified' do
47
+ is_expected.to eq(%i(foo bar))
48
+ end
49
+ end
50
+
51
+ describe '#clean?' do
52
+ subject(:clean?) { git.clean? }
53
+
54
+ let(:repository) { instance_double(::Git::Base, status: status) }
55
+
56
+ let(:status) do
57
+ instance_double(
58
+ ::Git::Status,
59
+ added: added, changed: changed, deleted: deleted, untracked: untracked
60
+ )
61
+ end
62
+
63
+ let(:added) { Hash.new }
64
+ let(:changed) { Hash.new }
65
+ let(:deleted) { Hash.new }
66
+ let(:untracked) { Hash.new }
67
+
68
+ before do
69
+ allow(::Git).to receive(:open).and_return(repository)
70
+ end
71
+
72
+ it { is_expected.to be_truthy }
73
+
74
+ context 'with some added file' do
75
+ let(:added) { { foo: 'bar' } }
76
+
77
+ it { is_expected.to be_falsy }
78
+ end
79
+
80
+ context 'with some changed file' do
81
+ let(:changed) { { foo: 'bar' } }
82
+
83
+ it { is_expected.to be_falsy }
84
+ end
85
+
86
+ context 'with some deleted file' do
87
+ let(:deleted) { { foo: 'bar' } }
88
+
89
+ it { is_expected.to be_falsy }
90
+ end
91
+
92
+ context 'with some untracked file' do
93
+ let(:untracked) { { foo: 'bar' } }
94
+
95
+ it { is_expected.to be_falsy }
96
+ end
97
+ end
98
+
99
+ describe '#current_branch' do
100
+ subject { git.current_branch }
101
+
102
+ let(:repository) { instance_double(::Git::Base, current_branch: branch) }
103
+ let(:branch) { 'some-branch' }
104
+
105
+ before do
106
+ allow(::Git).to receive(:open).and_return(repository)
107
+ allow(git).to receive(:current_branch).and_return(branch)
108
+ end
109
+
110
+ it 'returns the current branch' do
111
+ is_expected.to eq('some-branch')
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,57 @@
1
+ module GitHooks
2
+ module PreCommit
3
+ describe PreventMaster do
4
+ subject(:prevent_master) { described_class.new git_repository }
5
+
6
+ let(:git_repository) do
7
+ instance_double(GitHooks::Git, current_branch: branch)
8
+ end
9
+
10
+ let(:branch) { 'some-branch' }
11
+
12
+ describe '#validate' do
13
+ subject(:validate) { -> { prevent_master.validate } }
14
+
15
+ it { is_expected.to_not raise_error }
16
+
17
+ context 'when current_branch is master' do
18
+ let(:branch) { 'master' }
19
+
20
+ before do
21
+ allow($stderr).to receive(:write).and_return('')
22
+ end
23
+ it { is_expected.to raise_error(SystemExit) }
24
+ end
25
+ end
26
+
27
+ describe '.validate' do
28
+ subject(:validate) { described_class.validate }
29
+
30
+ let(:prevent_master) { instance_double(PreventMaster) }
31
+ let(:git) { instance_double(GitHooks::Git) }
32
+
33
+ let(:configurations) do
34
+ instance_double(Configurations, git_repository: git)
35
+ end
36
+
37
+ before do
38
+ allow(GitHooks).to receive(:configurations).and_return(configurations)
39
+ allow(described_class).to receive(:new).and_return(prevent_master)
40
+ allow(prevent_master).to receive(:validate).and_return(nil)
41
+ end
42
+
43
+ it 'creates object with GitHooks.git_repository' do
44
+ expect(PreventMaster).to receive(:new).with(git)
45
+
46
+ validate
47
+ end
48
+
49
+ it 'validates' do
50
+ expect(prevent_master).to receive(:validate)
51
+
52
+ validate
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,78 @@
1
+ module GitHooks
2
+ module PreCommit
3
+ describe Rspec do
4
+ subject(:rspec) { described_class.new git_repository, rspec_executor }
5
+
6
+ let(:git_repository) do
7
+ instance_double(GitHooks::Git, clean?: clean?)
8
+ end
9
+
10
+ let(:rspec_executor) do
11
+ instance_double(GitHooks::RspecExecutor, errors?: errors?)
12
+ end
13
+
14
+ let(:errors?) { false }
15
+
16
+ describe '#validate' do
17
+ subject(:validate) { -> { rspec.validate } }
18
+
19
+ let(:clean?) { true }
20
+
21
+ it { is_expected.to_not raise_error }
22
+
23
+ context 'with modified files' do
24
+ let(:rspec_executor) do
25
+ instance_double(GitHooks::RspecExecutor, errors?: errors?)
26
+ end
27
+
28
+ let(:clean?) { false }
29
+ let(:errors?) { false }
30
+
31
+ it { is_expected.to_not raise_error }
32
+
33
+ context 'and some broken test' do
34
+ let(:errors?) { true }
35
+
36
+ before do
37
+ allow($stderr).to receive(:write).and_return('')
38
+ end
39
+
40
+ it { is_expected.to raise_error(SystemExit) }
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '.validate' do
46
+ subject(:validate) { described_class.validate }
47
+
48
+ let(:rspec) { instance_double(Rspec) }
49
+ let(:rspec_executor) { instance_double(RspecExecutor) }
50
+ let(:git) { instance_double(GitHooks::Git) }
51
+
52
+ let(:configurations) do
53
+ instance_double(Configurations, git_repository: git)
54
+ end
55
+
56
+ before do
57
+ allow(described_class).to receive(:new).and_return(rspec)
58
+ allow(rspec).to receive(:validate).and_return(nil)
59
+
60
+ allow(GitHooks).to receive(:configurations).and_return(configurations)
61
+ allow(RspecExecutor).to receive(:new).and_return(rspec_executor)
62
+ end
63
+
64
+ it 'creates object with GitHooks.git_repository' do
65
+ expect(Rspec).to receive(:new).with(git, rspec_executor)
66
+
67
+ validate
68
+ end
69
+
70
+ it 'validates' do
71
+ expect(rspec).to receive(:validate)
72
+
73
+ validate
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,83 @@
1
+ module GitHooks
2
+ module PreCommit
3
+ describe Rubocop do
4
+ subject(:rubocop) do
5
+ described_class.new(git_repository, rubocop_validator)
6
+ end
7
+
8
+ let(:git_repository) do
9
+ instance_double(GitHooks::Git, added_or_modified: added_or_modified)
10
+ end
11
+
12
+ let(:rubocop_validator) do
13
+ instance_double(GitHooks::RubocopValidator, errors?: errors?)
14
+ end
15
+
16
+ let(:added_or_modified) { [] }
17
+ let(:errors?) { false }
18
+
19
+ describe '#validate' do
20
+ subject(:validate) { rubocop.validate }
21
+
22
+ let(:added_or_modified) { [ruby_file, 'foo.txt'] }
23
+ let(:ruby_file) { 'foo.rb' }
24
+
25
+ it 'only validates ruby files' do
26
+ expect(rubocop_validator)
27
+ .to receive(:errors?)
28
+ .with([ruby_file])
29
+ validate
30
+ end
31
+
32
+ context 'with errors' do
33
+ let(:errors?) { true }
34
+
35
+ before do
36
+ allow($stderr).to receive(:write).and_return('')
37
+ end
38
+
39
+ it { expect(-> { validate }).to raise_error(SystemExit) }
40
+ end
41
+
42
+ context 'without errors' do
43
+ let(:errors?) { false }
44
+
45
+ it { expect(-> { validate }).to_not raise_error }
46
+ end
47
+ end
48
+
49
+ describe '.validate' do
50
+ subject(:validate) { described_class.validate }
51
+
52
+ let(:rubocop) { instance_double(Rubocop) }
53
+ let(:rubocop_validator) { instance_double(RubocopValidator) }
54
+
55
+ let(:git) { instance_double(GitHooks::Git) }
56
+
57
+ let(:configurations) do
58
+ instance_double(Configurations, git_repository: git)
59
+ end
60
+
61
+ before do
62
+ allow(described_class).to receive(:new).and_return(rubocop)
63
+ allow(rubocop).to receive(:validate).and_return(nil)
64
+
65
+ allow(GitHooks).to receive(:configurations).and_return(configurations)
66
+ allow(RubocopValidator).to receive(:new).and_return(rubocop_validator)
67
+ end
68
+
69
+ it 'creates object with GitHooks.git_repository' do
70
+ expect(Rubocop).to receive(:new).with(git, rubocop_validator)
71
+
72
+ validate
73
+ end
74
+
75
+ it 'validates' do
76
+ expect(rubocop).to receive(:validate)
77
+
78
+ validate
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ module GitHooks
2
+ describe RspecExecutor do
3
+ subject(:rspec_executor) { described_class.new }
4
+
5
+ describe '#errors?' do
6
+ subject(:errors?) { rspec_executor.errors? }
7
+
8
+ let(:rspec_command) { 'bundle exec rspec --format=progress' }
9
+
10
+ before do
11
+ allow(rspec_executor).to receive(:system).and_return(true)
12
+ end
13
+
14
+ it 'call rubocop on system' do
15
+ expect(rspec_executor).to receive(:system).with(rspec_command)
16
+ errors?
17
+ end
18
+
19
+ context 'with broken test' do
20
+ before do
21
+ allow(rspec_executor).to receive(:system).and_return(false)
22
+ end
23
+
24
+ it { is_expected.to be_truthy }
25
+ end
26
+
27
+ context 'when files has no offences' do
28
+ before do
29
+ allow(rspec_executor).to receive(:system).and_return(true)
30
+ end
31
+
32
+ it { is_expected.to be_falsy }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ module GitHooks
2
+ describe RubocopValidator do
3
+ subject(:rubocop_validator) { described_class.new }
4
+
5
+ describe '#errors?' do
6
+ subject(:errors?) { rubocop_validator.errors?(files) }
7
+
8
+ context 'with files' do
9
+ let(:files) { %w(a.rb b.rb) }
10
+
11
+ let(:rubocop_command) do
12
+ "rubocop #{files.join(' ')} --force-exclusion"
13
+ end
14
+
15
+ before do
16
+ allow(rubocop_validator).to receive(:system).and_return(true)
17
+ end
18
+
19
+ it 'call rubocop on system' do
20
+ expect(rubocop_validator).to receive(:system).with(rubocop_command)
21
+ errors?
22
+ end
23
+
24
+ context 'when some file has offences' do
25
+ before do
26
+ allow(rubocop_validator).to receive(:system).and_return(false)
27
+ end
28
+
29
+ it { is_expected.to be_truthy }
30
+ end
31
+
32
+ context 'when files has no offences' do
33
+ before do
34
+ allow(rubocop_validator).to receive(:system).and_return(true)
35
+ end
36
+
37
+ it { is_expected.to be_falsy }
38
+ end
39
+ end
40
+
41
+ context 'with empty files array' do
42
+ let(:files) { [] }
43
+
44
+ it { is_expected.to be_falsy }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,140 @@
1
+ describe GitHooks do
2
+ describe '#validate_hooks!' do
3
+ subject { -> { described_class.validate_hooks! } }
4
+
5
+ before { GitHooks.configurations = configs }
6
+
7
+ let(:configs) do
8
+ instance_double(GitHooks::Configurations, pre_commits: pre_commits)
9
+ end
10
+
11
+ let(:pre_commits) { [] }
12
+
13
+ it { is_expected.to_not raise_error }
14
+
15
+ context 'with pre-commit hooks configured' do
16
+ let(:pre_commits) { %w(foo bar) }
17
+ let(:hook_installed?) { true }
18
+
19
+ before do
20
+ allow(GitHooks).to receive(:hook_installed?).and_return(hook_installed?)
21
+ end
22
+
23
+ it { is_expected.to_not raise_error }
24
+
25
+ context 'but without pre-commit installed' do
26
+ let(:hook_installed?) { false }
27
+
28
+ let(:message) { 'Please install pre-commit hook.' }
29
+
30
+ it do
31
+ is_expected.to raise_error(GitHooks::Exceptions::MissingHook, message)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '.base_path' do
38
+ subject { described_class.base_path }
39
+
40
+ it "has gem's root path" do
41
+ is_expected.to eq(
42
+ File.absolute_path(
43
+ File.join(File.expand_path(__FILE__), '..', '..')
44
+ )
45
+ )
46
+ end
47
+ end
48
+
49
+ describe '.configurations' do
50
+ subject(:configurations) { described_class.configurations }
51
+
52
+ before do
53
+ GitHooks.configurations = nil
54
+ allow(GitHooks::Configurations).to receive(:new).and_return(configs)
55
+ end
56
+
57
+ let(:configs) { instance_double(GitHooks::Configurations) }
58
+
59
+ it 'creates with default params' do
60
+ expect(GitHooks::Configurations).to receive(:new).with(no_args)
61
+
62
+ is_expected.to eq(configs)
63
+ end
64
+ end
65
+
66
+ describe 'configurations=' do
67
+ subject(:set_configurations) { described_class.configurations = configs }
68
+
69
+ let(:configs) { instance_double(GitHooks::Configurations) }
70
+
71
+ before do
72
+ allow(GitHooks::Configurations).to receive(:new).and_return(nil)
73
+ end
74
+
75
+ it 'updates configurations' do
76
+ expect { set_configurations }.to change {
77
+ GitHooks.configurations
78
+ }.to(configs)
79
+
80
+ is_expected.to eq(configs)
81
+ end
82
+ end
83
+
84
+ describe '.install_hook' do
85
+ subject(:install_hook) { described_class.install_hook(hook) }
86
+
87
+ let(:hook) { 'pre-commit' }
88
+ let(:hook_real_path) { File.join(GitHooks.base_path, 'hook.sample') }
89
+ let(:git_hook_path) { ".git/hooks/#{hook}" }
90
+
91
+ before do
92
+ allow(File).to receive(:symlink).and_return(true)
93
+ end
94
+
95
+ it 'creates symlink' do
96
+ expect(File)
97
+ .to receive(:symlink)
98
+ .with(hook_real_path, git_hook_path)
99
+ install_hook
100
+ end
101
+
102
+ it { is_expected.to be_truthy }
103
+ end
104
+
105
+ describe '.hook_installed?' do
106
+ subject(:installed?) { described_class.hook_installed?(hook) }
107
+
108
+ let(:hook) { 'pre-commit' }
109
+ let(:hook_real_path) { File.join(GitHooks.base_path, 'hook.sample') }
110
+ let(:symlink?) { true }
111
+
112
+ let(:absolute_path) { File.join(GitHooks.base_path, '.git', 'hooks', hook) }
113
+
114
+ before do
115
+ allow(File).to receive(:symlink?).and_return(symlink?)
116
+ allow(File).to receive(:realpath).and_return(hook_real_path)
117
+ end
118
+
119
+ it 'validates file is a symlink' do
120
+ expect(File).to receive(:symlink?).with(absolute_path)
121
+ installed?
122
+ end
123
+
124
+ it { is_expected.to be_truthy }
125
+
126
+ context 'when file is not a symlink' do
127
+ let(:symlink?) { false }
128
+
129
+ it { is_expected.to be_falsy }
130
+ end
131
+
132
+ context 'when is not the GitHooks file' do
133
+ before do
134
+ allow(File).to receive(:realpath).and_return('/tmp/foo')
135
+ end
136
+
137
+ it { is_expected.to be_falsy }
138
+ end
139
+ end
140
+ end
@@ -0,0 +1 @@
1
+ require_relative '../lib/git-hooks'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-hooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael da Silva Almeida
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-24 00:00:00.000000000 Z
11
+ date: 2014-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.23'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: git
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: equalizer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.9
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.9
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: bundler
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -38,26 +80,62 @@ dependencies:
38
80
  - - "~>"
39
81
  - !ruby/object:Gem::Version
40
82
  version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
41
97
  description: It stores git hooks and force git hooks installation.
42
98
  email:
43
99
  - eusou@rafaelalmeida.net
44
- executables: []
100
+ executables:
101
+ - git_hooks
45
102
  extensions: []
46
103
  extra_rdoc_files: []
47
104
  files:
48
105
  - ".gitignore"
106
+ - ".rspec"
49
107
  - ".rubocop.yml"
50
108
  - ".rubocop_todo.yml"
51
109
  - Gemfile
52
110
  - README.md
111
+ - bin/git_hooks
53
112
  - git-hooks.gemspec
113
+ - git_hooks.yml.example
114
+ - hook.sample
54
115
  - lib/git-hooks.rb
116
+ - lib/git_hooks/cli.rb
117
+ - lib/git_hooks/config_file.rb
118
+ - lib/git_hooks/configurations.rb
119
+ - lib/git_hooks/exceptions.rb
120
+ - lib/git_hooks/exceptions/missing_hooks.rb
121
+ - lib/git_hooks/git.rb
55
122
  - lib/git_hooks/pre_commit.rb
56
123
  - lib/git_hooks/pre_commit/prevent_master.rb
57
124
  - lib/git_hooks/pre_commit/rspec.rb
58
125
  - lib/git_hooks/pre_commit/rubocop.rb
126
+ - lib/git_hooks/rspec_executor.rb
127
+ - lib/git_hooks/rubocop_validator.rb
59
128
  - lib/git_hooks/version.rb
60
- - lib/ruby-git-hooks.rb
129
+ - spec/git_hooks/config_file_spec.rb
130
+ - spec/git_hooks/configurations_spec.rb
131
+ - spec/git_hooks/git_spec.rb
132
+ - spec/git_hooks/pre_commit/prevent_master_spec.rb
133
+ - spec/git_hooks/pre_commit/rspec_spec.rb
134
+ - spec/git_hooks/pre_commit/rubocop_spec.rb
135
+ - spec/git_hooks/rspec_executor_spec.rb
136
+ - spec/git_hooks/rubocop_validator_spec.rb
137
+ - spec/git_hooks_spec.rb
138
+ - spec/spec_helper.rb
61
139
  homepage: http://github.com/stupied4ever/ruby-git-hooks
62
140
  licenses:
63
141
  - DWTF
@@ -73,14 +151,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
151
  version: '0'
74
152
  required_rubygems_version: !ruby/object:Gem::Requirement
75
153
  requirements:
76
- - - ">="
154
+ - - ">"
77
155
  - !ruby/object:Gem::Version
78
- version: '0'
156
+ version: 1.3.1
79
157
  requirements: []
80
158
  rubyforge_project:
81
159
  rubygems_version: 2.2.2
82
160
  signing_key:
83
161
  specification_version: 4
84
162
  summary: Help to keep git hooks organized.
85
- test_files: []
163
+ test_files:
164
+ - spec/git_hooks/config_file_spec.rb
165
+ - spec/git_hooks/configurations_spec.rb
166
+ - spec/git_hooks/git_spec.rb
167
+ - spec/git_hooks/pre_commit/prevent_master_spec.rb
168
+ - spec/git_hooks/pre_commit/rspec_spec.rb
169
+ - spec/git_hooks/pre_commit/rubocop_spec.rb
170
+ - spec/git_hooks/rspec_executor_spec.rb
171
+ - spec/git_hooks/rubocop_validator_spec.rb
172
+ - spec/git_hooks_spec.rb
173
+ - spec/spec_helper.rb
86
174
  has_rdoc:
@@ -1 +0,0 @@
1
- require_relative 'git_hooks/pre_commit'