gemfilelint 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +1 -1
- data/exe/gemfilelint +9 -1
- data/lib/gemfilelint.rb +111 -54
- data/lib/gemfilelint/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed679ee045c3d39a031c54a7bc7b17a5912920975a46fbf4d8919a99c6216008
|
4
|
+
data.tar.gz: cdfd4a5b93dc1c6c2eee7917b11f3000e82f5604223cde31c067b5b433a33729
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be1de4b80eee06591d4bd0b85faf0ed50208f5d7f8de04f8dc7a95a339202629ce0bd5b0c81bee750581025bcdbabab1768afc307ac036c8e30c06f04ee2a433
|
7
|
+
data.tar.gz: 694bf077b0ef17a588bccb615ddce0ce865d6e1131fe01d20589a7098748d41c4fe170b22b83b532b2f3fce453b7eeefc535330c2468f8398c1f4eca8337924d
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [0.2.0] - 2020-02-20
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Allow linting multiple Gemfile paths in one command.
|
14
|
+
|
15
|
+
## [0.1.0] - 2020-02-20
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- 🎉 Initial release.
|
20
|
+
|
21
|
+
[unreleased]: https://github.com/kddeisz/gemfilelint/compare/v0.1.0...HEAD
|
22
|
+
[0.1.0]: https://github.com/kddeisz/gemfilelint/compare/935da5...v0.1.0
|
data/Gemfile.lock
CHANGED
data/exe/gemfilelint
CHANGED
@@ -4,4 +4,12 @@
|
|
4
4
|
$LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), __dir__))
|
5
5
|
require 'gemfilelint'
|
6
6
|
|
7
|
-
|
7
|
+
gemfiles = ARGV.any? ? ARGV : './Gemfile'
|
8
|
+
|
9
|
+
invalid = gemfiles.reject { |gemfile| File.file?(gemfile) }
|
10
|
+
if invalid.any?
|
11
|
+
warn("Could not find a gemfile at: #{invalid.join(', ')}")
|
12
|
+
exit 2
|
13
|
+
end
|
14
|
+
|
15
|
+
exit Gemfilelint.lint(*gemfiles) ? 0 : 1
|
data/lib/gemfilelint.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'delegate'
|
3
4
|
require 'logger'
|
4
5
|
|
5
6
|
require 'bundler'
|
@@ -8,8 +9,23 @@ require 'bundler/similarity_detector'
|
|
8
9
|
require 'gemfilelint/version'
|
9
10
|
|
10
11
|
module Gemfilelint
|
12
|
+
class SpellChecker
|
13
|
+
attr_reader :detector, :haystack
|
14
|
+
|
15
|
+
def initialize(haystack)
|
16
|
+
@detector = Bundler::SimilarityDetector.new(haystack)
|
17
|
+
@haystack = haystack
|
18
|
+
end
|
19
|
+
|
20
|
+
def correct(needle)
|
21
|
+
return [] if haystack.include?(needle)
|
22
|
+
|
23
|
+
detector.similar_words(needle)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
11
27
|
module Offenses
|
12
|
-
class Dependency < Struct.new(:name, :suggestions)
|
28
|
+
class Dependency < Struct.new(:path, :name, :suggestions)
|
13
29
|
def to_s
|
14
30
|
<<~ERR
|
15
31
|
Gem \"#{name}\" is possibly misspelled, suggestions:
|
@@ -18,7 +34,13 @@ module Gemfilelint
|
|
18
34
|
end
|
19
35
|
end
|
20
36
|
|
21
|
-
class
|
37
|
+
class InvalidGemfile < Struct.new(:path)
|
38
|
+
def to_s
|
39
|
+
"Gemfile at \"#{path}\" is invalid."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Remote < Struct.new(:path, :name, :suggestions)
|
22
44
|
def to_s
|
23
45
|
<<~ERR
|
24
46
|
Source \"#{name}\" is possibly misspelled, suggestions:
|
@@ -28,24 +50,64 @@ module Gemfilelint
|
|
28
50
|
end
|
29
51
|
end
|
30
52
|
|
31
|
-
|
32
|
-
class
|
33
|
-
|
53
|
+
module Parser
|
54
|
+
class Valid < Struct.new(:path, :dsl)
|
55
|
+
def each_offense
|
56
|
+
dependencies.each do |dependency|
|
57
|
+
yield dependency_offense_for(dependency)
|
58
|
+
end
|
59
|
+
|
60
|
+
remotes.each do |remote|
|
61
|
+
yield remote_offense_for(remote)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def dependencies
|
68
|
+
dsl.dependencies.map(&:name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def dependency_offense_for(name)
|
72
|
+
corrections = Gemfilelint.dependencies.correct(name)
|
73
|
+
return if corrections.empty?
|
74
|
+
|
75
|
+
Offenses::Dependency.new(path, name, corrections.first(5))
|
76
|
+
end
|
34
77
|
|
35
|
-
|
36
|
-
|
37
|
-
|
78
|
+
# Lol wut, there has got to be a better way to do this
|
79
|
+
def remotes
|
80
|
+
dsl
|
81
|
+
.instance_variable_get(:@sources)
|
82
|
+
.instance_variable_get(:@rubygems_aggregate)
|
83
|
+
.remotes
|
84
|
+
.map(&:to_s)
|
38
85
|
end
|
39
86
|
|
40
|
-
def
|
41
|
-
|
87
|
+
def remote_offense_for(uri)
|
88
|
+
corrections = Gemfilelint.remotes.correct(uri)
|
89
|
+
return if corrections.empty?
|
90
|
+
|
91
|
+
Offenses::Remote.new(path, uri, corrections)
|
92
|
+
end
|
93
|
+
end
|
42
94
|
|
43
|
-
|
95
|
+
class Invalid < Struct.new(:path)
|
96
|
+
def each_offense
|
97
|
+
yield Offenses::InvalidGemfile.new(path)
|
44
98
|
end
|
45
99
|
end
|
46
100
|
|
101
|
+
def self.for(path)
|
102
|
+
Valid.new(path, Bundler::Dsl.new.tap { |dsl| dsl.eval_gemfile(path) })
|
103
|
+
rescue Bundler::Dsl::DSLError
|
104
|
+
Invalid.new(path)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Linter
|
47
109
|
module ANSIColor
|
48
|
-
CODES = { green: 32, magenta: 35 }.freeze
|
110
|
+
CODES = { green: 32, magenta: 35, cyan: 36 }.freeze
|
49
111
|
|
50
112
|
refine String do
|
51
113
|
def colorize(code)
|
@@ -56,23 +118,19 @@ module Gemfilelint
|
|
56
118
|
|
57
119
|
using ANSIColor
|
58
120
|
|
59
|
-
attr_reader :
|
60
|
-
|
61
|
-
def initialize
|
62
|
-
common_gems = File.read(File.expand_path('gems.txt', __dir__)).split("\n")
|
121
|
+
attr_reader :logger
|
63
122
|
|
64
|
-
|
65
|
-
@
|
123
|
+
def initialize(logger: nil)
|
124
|
+
@logger = logger || make_logger
|
66
125
|
end
|
67
126
|
|
68
127
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
69
|
-
def lint(
|
70
|
-
logger
|
128
|
+
def lint(*paths)
|
129
|
+
logger.info("Inspecting gemfiles at #{paths.join(', ')}\n")
|
71
130
|
|
72
|
-
logger.info("Inspecting gemfile at #{path}\n")
|
73
131
|
offenses = []
|
74
132
|
|
75
|
-
each_offense_for(
|
133
|
+
each_offense_for(paths) do |offense|
|
76
134
|
if offense
|
77
135
|
offenses << offense
|
78
136
|
logger.info('W'.colorize(:magenta))
|
@@ -82,54 +140,53 @@ module Gemfilelint
|
|
82
140
|
end
|
83
141
|
|
84
142
|
logger.info("\n")
|
85
|
-
return 0 if offenses.empty?
|
86
143
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
144
|
+
if offenses.empty?
|
145
|
+
true
|
146
|
+
else
|
147
|
+
messages = offenses.map { |offense| offense_to_message(offense) }
|
148
|
+
logger.info("\nOffenses:\n\n#{messages.join("\n")}\n")
|
149
|
+
false
|
150
|
+
end
|
92
151
|
end
|
93
152
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
94
153
|
|
95
154
|
private
|
96
155
|
|
97
|
-
def
|
98
|
-
|
99
|
-
|
100
|
-
|
156
|
+
def each_offense_for(paths)
|
157
|
+
paths.each do |path|
|
158
|
+
Parser.for(path).each_offense do |offense|
|
159
|
+
yield offense
|
160
|
+
end
|
101
161
|
end
|
102
162
|
end
|
103
163
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
# Lol wut, there has got to be a better way to do this
|
109
|
-
source_list = dsl.instance_variable_get(:@sources)
|
110
|
-
rubygems = source_list.instance_variable_get(:@rubygems_aggregate)
|
111
|
-
|
112
|
-
dsl.dependencies.each do |dependency|
|
113
|
-
yield dependency_offense_for(dependency.name)
|
164
|
+
def make_logger
|
165
|
+
Logger.new(STDOUT).tap do |creating|
|
166
|
+
creating.level = :info
|
167
|
+
creating.formatter = ->(*, message) { message }
|
114
168
|
end
|
169
|
+
end
|
115
170
|
|
116
|
-
|
117
|
-
|
118
|
-
end
|
171
|
+
def offense_to_message(offense)
|
172
|
+
"#{offense.path.colorize(:cyan)}: #{'W'.colorize(:magenta)}: #{offense}"
|
119
173
|
end
|
174
|
+
end
|
120
175
|
|
121
|
-
|
122
|
-
|
123
|
-
|
176
|
+
class << self
|
177
|
+
def dependencies
|
178
|
+
@dependencies ||=
|
179
|
+
SpellChecker.new(
|
180
|
+
File.read(File.expand_path('gems.txt', __dir__)).split("\n")
|
181
|
+
)
|
124
182
|
end
|
125
183
|
|
126
|
-
def
|
127
|
-
|
128
|
-
Offenses::Remote.new(uri, corrections) if corrections.any?
|
184
|
+
def remotes
|
185
|
+
@remotes ||= SpellChecker.new(['https://rubygems.org/'])
|
129
186
|
end
|
130
|
-
end
|
131
187
|
|
132
|
-
|
133
|
-
|
188
|
+
def lint(*paths, logger: nil)
|
189
|
+
Linter.new(logger: logger).lint(*paths)
|
190
|
+
end
|
134
191
|
end
|
135
192
|
end
|
data/lib/gemfilelint/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gemfilelint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Deisz
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- ".gitignore"
|
79
79
|
- ".mergify.yml"
|
80
80
|
- ".rubocop.yml"
|
81
|
+
- CHANGELOG.md
|
81
82
|
- CODE_OF_CONDUCT.md
|
82
83
|
- Gemfile
|
83
84
|
- Gemfile.lock
|