gemfilelint 0.1.0 → 0.2.0
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 +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
|