hound-tools 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.hound.yml +6 -0
- data/.hound/defaults.yml +265 -0
- data/.hound/overrides.yml +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_merged_for_hound.yml +41 -0
- data/.rubocop_todo.yml +11 -0
- data/.travis.yml +12 -0
- data/Gemfile +12 -0
- data/Guardfile +40 -0
- data/LICENSE.txt +22 -0
- data/README.md +98 -0
- data/Rakefile +22 -0
- data/bin/hound-tools +5 -0
- data/hound-tools.gemspec +26 -0
- data/lib/hound/tools.rb +7 -0
- data/lib/hound/tools/cli.rb +74 -0
- data/lib/hound/tools/hound_defaults.rb +30 -0
- data/lib/hound/tools/hound_overrides.rb +26 -0
- data/lib/hound/tools/hound_yml.rb +31 -0
- data/lib/hound/tools/merged_yml.rb +40 -0
- data/lib/hound/tools/rubocop_yml.rb +24 -0
- data/lib/hound/tools/runner.rb +71 -0
- data/lib/hound/tools/template.rb +38 -0
- data/lib/hound/tools/templates/_.hound.yml +6 -0
- data/lib/hound/tools/templates/_.hound/defaults.yml +265 -0
- data/lib/hound/tools/templates/_.hound/overrides.yml +19 -0
- data/lib/hound/tools/templates/_.rubocop.yml +8 -0
- data/lib/hound/tools/version.rb +5 -0
- data/spec/lib/hound/tools/cli_spec.rb +90 -0
- data/spec/lib/hound/tools/hound_defaults_spec.rb +52 -0
- data/spec/lib/hound/tools/hound_overrides_spec.rb +52 -0
- data/spec/lib/hound/tools/hound_yml_spec.rb +77 -0
- data/spec/lib/hound/tools/merged_yml_spec.rb +53 -0
- data/spec/lib/hound/tools/rubocop_yml_spec.rb +68 -0
- data/spec/lib/hound/tools/template_spec.rb +43 -0
- data/spec/spec_helper.rb +47 -0
- metadata +147 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Cezary Baginski
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/hound-tools.svg)](http://badge.fury.io/rb/hound-tools)
|
2
|
+
[![Build Status](https://travis-ci.org/e2/hound-tools.png?branch=master)](https://travis-ci.org/e2/hound-tools)
|
3
|
+
|
4
|
+
|
5
|
+
# Hound::Tools
|
6
|
+
|
7
|
+
Tools and configuration to locally simulate HoundCi checks
|
8
|
+
|
9
|
+
## Initial Setup
|
10
|
+
|
11
|
+
In your Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
group :development do
|
15
|
+
gem 'hound-tools', '~> 0.0.3', require: false
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Create customizable RuboCop + Hound setup:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ bundle exec hound-tools init
|
23
|
+
```
|
24
|
+
|
25
|
+
(NOTE: this runs `rubocop --auto-gen`, so it will disable all the offenses.)
|
26
|
+
|
27
|
+
## Add new files to repository
|
28
|
+
|
29
|
+
Add the files to the repository:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
git add .hound # default Hound style guide and your overrides
|
33
|
+
git add .rubocop.yml # Rubocop-only config
|
34
|
+
git add .hound.yml # Hound general style-checking config
|
35
|
+
git add .rubocop_merged_for_hound.yml # auto-generated Hound-only config (without Hound defaults)
|
36
|
+
```
|
37
|
+
|
38
|
+
|
39
|
+
## Customizing your Rubocop/Hound setup
|
40
|
+
|
41
|
+
You should only be interested in these files:
|
42
|
+
- `.rubocop_todo.yml`, which can be updated with `bundle exec rubocop --auto-gen` and then edited
|
43
|
+
- `.hound/overrides.yml`, where you can override Hound's default rules or excludes
|
44
|
+
- `.rubocop_merged_for_hound.yml` - you want to regenerate this and commit
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
Every time you modify `.rubocop_todo.yml` or `.hound/overrides.yml`, you'll
|
49
|
+
want to regenerate `.rubocop_merged_for_hound.yml` with:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
$ bundle exec hound-tools
|
53
|
+
```
|
54
|
+
|
55
|
+
(And then you'll want to add all 3 to the repository before doing anything else).
|
56
|
+
|
57
|
+
Once you have your style working, you can simulate Hound with almost 100% accuracy with:
|
58
|
+
|
59
|
+
```bash
|
60
|
+
bundle exec rubocop
|
61
|
+
```
|
62
|
+
|
63
|
+
(If this shows no offenses, check the .rubocop_todo.yml file for disabled rules.)
|
64
|
+
|
65
|
+
## Tips
|
66
|
+
|
67
|
+
1) For quickly fixing most offenses, uncomment the 'auto-correct' ones in
|
68
|
+
`.rubocop_todo.yml` simply run `bundle exec rubocop -a`
|
69
|
+
|
70
|
+
2) The RuboCop README has tips on setting up Rake, Guard, etc.
|
71
|
+
|
72
|
+
## Why is this so complex?
|
73
|
+
|
74
|
+
Well, simply because Hound doesn't support the RuboCop `inherited_from` keys.
|
75
|
+
And that's because Hound is avoiding touching the filesystem - which makes
|
76
|
+
sense since it downloads files to memory from GitHub.
|
77
|
+
|
78
|
+
Also, since Hound internally loads it's defaults and does a "mini-merge" of the
|
79
|
+
configurations, it needs a different setup than Rubocop.
|
80
|
+
|
81
|
+
I won't mention the clever hacks in Hound to side-step directory traversing and
|
82
|
+
other messy issues (e.g. handling excludes).
|
83
|
+
|
84
|
+
|
85
|
+
## Alternatives
|
86
|
+
|
87
|
+
1) Using only the default Hound settings (without being able to Hound-check them locally)
|
88
|
+
|
89
|
+
2) Copying the default Hound settings (`.hound/defaults.yml`) to `.rubocop.yml` and tweak them (but you loose the flexibility and control of using multiple files and the coolness of .rubocop_todo.yml with 'inherited_from')
|
90
|
+
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it ( https://github.com/[my-github-username]/hound-tools/fork )
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
default_tasks = []
|
4
|
+
|
5
|
+
def ci?
|
6
|
+
ENV["CI"] == "true"
|
7
|
+
end
|
8
|
+
|
9
|
+
require "rspec/core/rake_task"
|
10
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
11
|
+
t.verbose = ci?
|
12
|
+
end
|
13
|
+
|
14
|
+
default_tasks << :spec
|
15
|
+
|
16
|
+
unless ci?
|
17
|
+
require "rubocop/rake_task"
|
18
|
+
RuboCop::RakeTask.new(:rubocop)
|
19
|
+
default_tasks << :rubocop
|
20
|
+
end
|
21
|
+
|
22
|
+
task default: default_tasks
|
data/bin/hound-tools
ADDED
data/hound-tools.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hound/tools/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'hound-tools'
|
8
|
+
spec.version = Hound::Tools::VERSION
|
9
|
+
spec.authors = ['Cezary Baginski']
|
10
|
+
spec.email = ['cezary@chronomantic.net']
|
11
|
+
spec.summary = 'Tools for configuring and using HoundCI'
|
12
|
+
spec.description = 'Matches your project config to give the same errors as HoundCi'
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'rubocop', '0.25.0'
|
22
|
+
spec.add_dependency 'thor'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
end
|
data/lib/hound/tools.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require 'hound/tools/hound_yml'
|
5
|
+
require 'hound/tools/hound_defaults'
|
6
|
+
require 'hound/tools/hound_overrides'
|
7
|
+
require 'hound/tools/rubocop_yml'
|
8
|
+
require 'hound/tools/merged_yml'
|
9
|
+
|
10
|
+
require 'hound/tools/runner'
|
11
|
+
|
12
|
+
module Hound
|
13
|
+
module Tools
|
14
|
+
class Cli < Thor
|
15
|
+
INSTRUCTIONS = <<-EOS
|
16
|
+
|
17
|
+
**** WARNING!!! ****
|
18
|
+
|
19
|
+
1. All Rubocop offenses are initially ignored! (see .rubocop_todo.yml and/or README)
|
20
|
+
- tweak .rubocop_todo.yml and regenerate with `bundle exec hound-tools`
|
21
|
+
|
22
|
+
2. Fixing all offenses at once is discouraged unless:
|
23
|
+
- you are the only person actively working on the project (or starting out)
|
24
|
+
- you have accepted ALL the pull requests FIRST
|
25
|
+
- you have merged ALL the local and remote branches and you are NOT
|
26
|
+
currently maintaining multiple branches
|
27
|
+
|
28
|
+
Issues? Go here: https://github.com/e2/hound-tools/issues
|
29
|
+
EOS
|
30
|
+
|
31
|
+
desc :init, 'Initializes a project to match default HoundCi config'
|
32
|
+
def init
|
33
|
+
HoundYml.new.generate
|
34
|
+
HoundDefaults.new.generate
|
35
|
+
HoundOverrides.new.generate
|
36
|
+
RubocopYml.new.generate
|
37
|
+
|
38
|
+
# TODO: help setup Rakefile?
|
39
|
+
|
40
|
+
Kernel.system('bundle exec rubocop --auto-gen')
|
41
|
+
|
42
|
+
MergedYml.new.generate
|
43
|
+
|
44
|
+
$stdout.puts INSTRUCTIONS
|
45
|
+
|
46
|
+
unless Kernel.system("bundle show hound-tools > #{IO::NULL}")
|
47
|
+
$stderr.puts <<-EOS
|
48
|
+
Add hound-tools to your Gemfile like so:
|
49
|
+
|
50
|
+
gem 'hound-tools', require: false
|
51
|
+
|
52
|
+
EOS
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
default_task :check
|
57
|
+
desc :check, 'Simulates a HoundCi check locally'
|
58
|
+
def check
|
59
|
+
# TODO: add an "update" action?
|
60
|
+
# TODO: only merge if necessary (files outdated)
|
61
|
+
MergedYml.new.generate
|
62
|
+
|
63
|
+
options = {
|
64
|
+
hound_yml_file: '.hound.yml',
|
65
|
+
hound_ci_style_file: '.hound/defaults.yml',
|
66
|
+
debug: false,
|
67
|
+
glob_pattern: '**/*.rb'
|
68
|
+
}
|
69
|
+
|
70
|
+
Runner.new(options).run
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'hound/tools/template'
|
4
|
+
|
5
|
+
module Hound
|
6
|
+
module Tools
|
7
|
+
class HoundDefaults
|
8
|
+
include Template
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super('.hound/defaults.yml')
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def _validate(content)
|
17
|
+
config = YAML.load(content)
|
18
|
+
literals = config['StringLiterals']
|
19
|
+
fail InvalidTemplate, 'No StringLiterals section' unless literals
|
20
|
+
|
21
|
+
quote_style = literals['EnforcedStyle']
|
22
|
+
fail InvalidTemplate, 'No EnforcedStyle section' unless quote_style
|
23
|
+
fail InvalidTemplate, 'No double_quotes value' unless quote_style == 'double_quotes'
|
24
|
+
|
25
|
+
# TODO: not tested
|
26
|
+
fail InvalidTemplate, "Detected 'inherited_from' section" if config.key?('inherited_from')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'hound/tools/template'
|
4
|
+
|
5
|
+
module Hound
|
6
|
+
module Tools
|
7
|
+
class HoundOverrides
|
8
|
+
include Template
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super('.hound/overrides.yml')
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def _validate(content)
|
17
|
+
config = YAML.load(content)
|
18
|
+
cops = config['AllCops']
|
19
|
+
fail InvalidTemplate, 'No AllCops section' unless cops
|
20
|
+
|
21
|
+
# TODO: not tested
|
22
|
+
fail InvalidTemplate, "Detected 'inherited_from' section" if config.key?('inherited_from')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'hound/tools/template'
|
4
|
+
|
5
|
+
module Hound
|
6
|
+
module Tools
|
7
|
+
class HoundYml
|
8
|
+
include Template
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super('.hound.yml')
|
12
|
+
end
|
13
|
+
|
14
|
+
def rubocop_filename
|
15
|
+
data = IO.read(filename)
|
16
|
+
_validate(data)
|
17
|
+
YAML.load(data)['ruby']['config_file']
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def _validate(data)
|
23
|
+
config = YAML.load(data)
|
24
|
+
ruby = config['ruby']
|
25
|
+
fail InvalidTemplate, "No 'ruby' section" unless ruby
|
26
|
+
fail InvalidTemplate, "Expected 'ruby' section to be a hash, got #{ruby.inspect}" unless ruby.is_a?(Hash)
|
27
|
+
fail InvalidTemplate, "No 'config_file' section" unless ruby.key?('config_file')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'hound/tools/template'
|
2
|
+
require 'hound/tools/hound_yml'
|
3
|
+
require 'hound/tools/hound_overrides'
|
4
|
+
|
5
|
+
module Hound
|
6
|
+
module Tools
|
7
|
+
class MergedYml
|
8
|
+
include Template
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@todo_file = '.rubocop_todo.yml'
|
12
|
+
|
13
|
+
# NOTE: should be named. .rubocop.yml to prevent RuboCop from traversing
|
14
|
+
super('.rubocop_merged_for_hound.yml')
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
s = StringIO.new
|
19
|
+
s.write(<<EOS)
|
20
|
+
# This is a file generated by `hound-tools`
|
21
|
+
#
|
22
|
+
# We don't include .hound/defaults.yml, because Hound has internally
|
23
|
+
# loaded them at this point
|
24
|
+
#
|
25
|
+
EOS
|
26
|
+
[HoundOverrides.new.filename, @todo_file].each do |filename|
|
27
|
+
s.puts '# ---------------------------------'
|
28
|
+
s.puts "# #{filename}"
|
29
|
+
s.puts '# ---------------------------------'
|
30
|
+
s.puts IO.read(filename)
|
31
|
+
end
|
32
|
+
Pathname.new(filename).dirname.mkpath
|
33
|
+
output_file = HoundYml.new.rubocop_filename
|
34
|
+
IO.write(output_file, s.string)
|
35
|
+
|
36
|
+
$stdout.puts "#{output_file} (regenerated)"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'hound/tools/template'
|
3
|
+
|
4
|
+
module Hound
|
5
|
+
module Tools
|
6
|
+
class RubocopYml
|
7
|
+
include Template
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super '.rubocop.yml'
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def _validate(data)
|
16
|
+
config = YAML.load(data)
|
17
|
+
inherited = config['inherit_from']
|
18
|
+
fail InvalidTemplate, "No 'inherit_from' section" unless inherited
|
19
|
+
file = '.hound/defaults.yml'
|
20
|
+
fail InvalidTemplate, "'#{file}' not inherited" unless inherited.include?(file)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Hound
|
2
|
+
module Tools
|
3
|
+
class HoundConfig
|
4
|
+
attr_reader :yaml
|
5
|
+
attr_reader :rubocop_file
|
6
|
+
attr_reader :rubocop_data
|
7
|
+
|
8
|
+
def enabled?
|
9
|
+
@enabled
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(filename)
|
13
|
+
@yaml = YAML.load(IO.read(filename))
|
14
|
+
@enabled = @yaml['ruby'].fetch('enabled', true)
|
15
|
+
@rubocop_file = @yaml['ruby'].fetch('config_file', nil)
|
16
|
+
@rubocop_data = @rubocop_file && YAML.load(IO.read(@rubocop_file))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Runner
|
21
|
+
DEFAULTS = {
|
22
|
+
hound_yml_file: '.hound.yml',
|
23
|
+
hound_ci_style_file: '.hound/defaults.yml',
|
24
|
+
debug: false,
|
25
|
+
glob_pattern: '**/*.rb' # TODO: should be all files?
|
26
|
+
}
|
27
|
+
|
28
|
+
def initialize(options)
|
29
|
+
@options = DEFAULTS.merge(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run
|
33
|
+
# TODO: clean this up
|
34
|
+
# TODO: not covered by specs
|
35
|
+
|
36
|
+
hound_yml_file = @options[:hound_yml_file]
|
37
|
+
hound_ci_style = @options[:hound_ci_style_file]
|
38
|
+
debug = @options[:debug]
|
39
|
+
glob_pattern = @options[:glob_pattern]
|
40
|
+
|
41
|
+
# NOTE: the code below should be written to EXACTLY do what Hound does
|
42
|
+
|
43
|
+
require 'rubocop'
|
44
|
+
|
45
|
+
RuboCop::ConfigLoader.debug = debug
|
46
|
+
hound = HoundConfig.new(hound_yml_file)
|
47
|
+
|
48
|
+
return "RuboCop disabled in #{hound_yml_file}" unless hound.enabled?
|
49
|
+
|
50
|
+
# NOTE: treating hound.yml as a rubocop.yml file is deprecated
|
51
|
+
custom = RuboCop::Config.new(hound.rubocop_data || hound.yml, '')
|
52
|
+
custom.add_missing_namespaces
|
53
|
+
custom.make_excludes_absolute
|
54
|
+
|
55
|
+
default = RuboCop::ConfigLoader.configuration_from_file(hound_ci_style)
|
56
|
+
config = RuboCop::ConfigLoader.merge(default, custom)
|
57
|
+
|
58
|
+
cfg = RuboCop::Config.new(config, '')
|
59
|
+
team = RuboCop::Cop::Team.new(RuboCop::Cop::Cop.all, cfg, debug: debug)
|
60
|
+
|
61
|
+
Dir[glob_pattern].each do |path|
|
62
|
+
next if File.directory?(path)
|
63
|
+
file = RuboCop::ProcessedSource.new(IO.read(path))
|
64
|
+
team.inspect_file(file).each do |violation|
|
65
|
+
$stderr.puts "#{path}:#{violation.line}:#{violation.message}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|