hound-tools 0.0.4
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 +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
|
+
[](http://badge.fury.io/rb/hound-tools)
|
2
|
+
[](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
|