git-hooks 0.0.2 → 0.1.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'