avo-linter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +78 -0
- data/LICENSE.md +7 -0
- data/avo-linter.gemspec +26 -0
- data/bin/avo +9 -0
- data/bin/rspec +4 -0
- data/lib/avo_linter/cli.rb +60 -0
- data/lib/avo_linter/rules/base.rb +31 -0
- data/lib/avo_linter/rules/fields_as_class_methods.rb +14 -0
- data/lib/avo_linter/scanner.rb +42 -0
- data/lib/avo_linter/scanners/base.rb +102 -0
- data/lib/avo_linter/scanners/resource_scanner.rb +39 -0
- data/lib/avo_linter/version.rb +3 -0
- data/lib/avo_linter.rb +22 -0
- data/lib/support.rb +52 -0
- data/linter.png +0 -0
- data/readme.md +40 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e192558b6e6c040bed45f0a10bbd3c822980e412a4a2ea8a4ff524ff1314d480
|
4
|
+
data.tar.gz: cde77967db5c23805bfdf329357b55f3466998858a362f909560c26b3122cb78
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3291101920e425058c5459a66c689bb5e817dddf2adf326c4e96ea5475af7ea0af2999ff60a3003012290297e75cc97dfc882443262775a777247bd10274b0bd
|
7
|
+
data.tar.gz: b7e07e18355ce3c711e7338c1c4d5954a6c356c8dbe9f07821742df740b1373113d580a0c64429de45c7336d24501f02e4438518d91fead564fdb91d961368bd
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
ruby "~> 3.3.0"
|
6
|
+
|
7
|
+
gem "awesome_print"
|
8
|
+
gem "dry-initializer"
|
9
|
+
gem "activesupport", "> 7.0.4", "< 7.1.0"
|
10
|
+
gem "dry-cli"
|
11
|
+
gem "paint"
|
12
|
+
gem "tty-command"
|
13
|
+
gem "zeitwerk"
|
14
|
+
gem "prism"
|
15
|
+
gem "parser"
|
16
|
+
gem "parser-prism"
|
17
|
+
gem "ruby-lsp"
|
18
|
+
gem "rspec"
|
19
|
+
gem "rspec-expectations"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (7.0.8)
|
5
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
|
+
i18n (>= 1.6, < 2)
|
7
|
+
minitest (>= 5.1)
|
8
|
+
tzinfo (~> 2.0)
|
9
|
+
ast (2.4.2)
|
10
|
+
awesome_print (1.9.2)
|
11
|
+
concurrent-ruby (1.2.2)
|
12
|
+
diff-lcs (1.5.0)
|
13
|
+
dry-cli (1.0.0)
|
14
|
+
dry-initializer (3.1.1)
|
15
|
+
i18n (1.14.1)
|
16
|
+
concurrent-ruby (~> 1.0)
|
17
|
+
language_server-protocol (3.17.0.3)
|
18
|
+
minitest (5.20.0)
|
19
|
+
paint (2.3.0)
|
20
|
+
parser (3.3.0.2)
|
21
|
+
ast (~> 2.4.1)
|
22
|
+
racc
|
23
|
+
parser-prism (0.1.0)
|
24
|
+
parser
|
25
|
+
prism
|
26
|
+
pastel (0.8.0)
|
27
|
+
tty-color (~> 0.5)
|
28
|
+
prism (0.19.0)
|
29
|
+
racc (1.7.3)
|
30
|
+
rspec (3.12.0)
|
31
|
+
rspec-core (~> 3.12.0)
|
32
|
+
rspec-expectations (~> 3.12.0)
|
33
|
+
rspec-mocks (~> 3.12.0)
|
34
|
+
rspec-core (3.12.2)
|
35
|
+
rspec-support (~> 3.12.0)
|
36
|
+
rspec-expectations (3.12.3)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.12.0)
|
39
|
+
rspec-mocks (3.12.6)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.12.0)
|
42
|
+
rspec-support (3.12.1)
|
43
|
+
ruby-lsp (0.13.2)
|
44
|
+
language_server-protocol (~> 3.17.0)
|
45
|
+
prism (>= 0.19.0, < 0.20)
|
46
|
+
sorbet-runtime (>= 0.5.5685)
|
47
|
+
sorbet-runtime (0.5.11180)
|
48
|
+
tty-color (0.6.0)
|
49
|
+
tty-command (0.10.1)
|
50
|
+
pastel (~> 0.8)
|
51
|
+
tzinfo (2.0.6)
|
52
|
+
concurrent-ruby (~> 1.0)
|
53
|
+
zeitwerk (2.6.12)
|
54
|
+
|
55
|
+
PLATFORMS
|
56
|
+
arm64-darwin-23
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
activesupport (> 7.0.4, < 7.1.0)
|
61
|
+
awesome_print
|
62
|
+
dry-cli
|
63
|
+
dry-initializer
|
64
|
+
paint
|
65
|
+
parser
|
66
|
+
parser-prism
|
67
|
+
prism
|
68
|
+
rspec
|
69
|
+
rspec-expectations
|
70
|
+
ruby-lsp
|
71
|
+
tty-command
|
72
|
+
zeitwerk
|
73
|
+
|
74
|
+
RUBY VERSION
|
75
|
+
ruby 3.3.0p0
|
76
|
+
|
77
|
+
BUNDLED WITH
|
78
|
+
2.5.3
|
data/LICENSE.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2023 Adrian Marin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/avo-linter.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "lib/avo_linter/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "avo-linter"
|
5
|
+
spec.version = AvoLinter::VERSION
|
6
|
+
spec.summary = "Linter for Avo CMS for Ruby on Rails"
|
7
|
+
spec.description = "The linter scans your Avo app and exposes errors"
|
8
|
+
spec.authors = ["Adrian Marin"]
|
9
|
+
spec.email = "adrian@adrianthedev.com"
|
10
|
+
spec.files = Dir["{bin,lib}/**/*", "LICENSE.MD", "readme.md", "avo-linter.gemspec", "Gemfile", "Gemfile.lock", "linter.png"]
|
11
|
+
spec.homepage = "https://avohq.io"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
15
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
16
|
+
if spec.respond_to?(:metadata)
|
17
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/avo-hq/avo-linter/issues"
|
18
|
+
spec.metadata["changelog_uri"] = "https://github.com/avo-hq/avo-linter/releases"
|
19
|
+
spec.metadata["documentation_uri"] = "https://github.com/avo-hq/avo-linter"
|
20
|
+
spec.metadata["homepage_uri"] = "https://avohq.io"
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/avo-hq/avo-linter"
|
22
|
+
else
|
23
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
"public gem pushes."
|
25
|
+
end
|
26
|
+
end
|
data/bin/avo
ADDED
data/bin/rspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module AvoLinter
|
2
|
+
VERSION = "0.0.1"
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
extend Dry::CLI::Registry
|
7
|
+
|
8
|
+
class BaseCommand < Dry::CLI::Command
|
9
|
+
def run(command)
|
10
|
+
result = cmd.run(command)
|
11
|
+
halt if result.failed?
|
12
|
+
result
|
13
|
+
rescue TTY::Command::ExitError
|
14
|
+
halt
|
15
|
+
end
|
16
|
+
|
17
|
+
def halt(message: nil)
|
18
|
+
message ||= "#{self.class} failed."
|
19
|
+
yell message
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Version < BaseCommand
|
25
|
+
desc "Print version"
|
26
|
+
|
27
|
+
def call(*)
|
28
|
+
puts VERSION
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Lint < BaseCommand
|
33
|
+
desc "Lint gem"
|
34
|
+
|
35
|
+
option :path, aliases: ["-p"], required: false, desc: "Path of your Rails app"
|
36
|
+
|
37
|
+
def call(**)
|
38
|
+
scan = ::AvoLinter::Scanner.scan
|
39
|
+
|
40
|
+
# ap scan
|
41
|
+
|
42
|
+
say "Scan finished!\n"
|
43
|
+
|
44
|
+
if scan.errors?
|
45
|
+
yell "We found a couple of errors."
|
46
|
+
ap scan.error_messages
|
47
|
+
else
|
48
|
+
say "Nothing bad found. Good job!"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
register "version", Version, aliases: ["v", "-v", "--version"]
|
54
|
+
|
55
|
+
register "lint", Lint
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Dry::CLI.new(AvoLinter::CLI::Commands).call
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class AvoLinter::Rules::Base
|
2
|
+
attr_reader :contents
|
3
|
+
attr_reader :errors
|
4
|
+
|
5
|
+
def initialize(contents:, **)
|
6
|
+
@contents = contents
|
7
|
+
@errors = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_rule
|
11
|
+
apply
|
12
|
+
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def message = self.class::MESSAGE
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def get_class_body
|
21
|
+
parsed_contents.value.statements.body.first.body.body
|
22
|
+
end
|
23
|
+
|
24
|
+
def parsed_contents
|
25
|
+
Prism.parse contents
|
26
|
+
end
|
27
|
+
|
28
|
+
def error_out(message)
|
29
|
+
@errors << message
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AvoLinter::Rules::FieldsAsClassMethods < AvoLinter::Rules::Base
|
2
|
+
MESSAGE = "You should not use the `field` method as a class method. Please add it in the `def fields` method or use composition in other methods."
|
3
|
+
|
4
|
+
def apply
|
5
|
+
get_class_body.each do |node|
|
6
|
+
if node.instance_of?(Prism::CallNode) && node.name == :field
|
7
|
+
error_out(
|
8
|
+
message:,
|
9
|
+
node:,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class AvoLinter::Scanner
|
2
|
+
AVO_PATHS = {
|
3
|
+
actions: ["app", "avo", "actions", "*.rb"],
|
4
|
+
config: ["config", "*.rb"],
|
5
|
+
cards: ["app", "avo", "cards", "*.rb"],
|
6
|
+
dashboards: ["app", "avo", "dashboards", "*.rb"],
|
7
|
+
fields: ["app", "avo", "fields", "*.rb"],
|
8
|
+
filters: ["app", "avo", "filters", "*.rb"],
|
9
|
+
models: ["app", "models", "*.rb"],
|
10
|
+
resource_controllers: ["app", "controllers", "avo", "*.rb"],
|
11
|
+
resource_tools: ["app", "avo", "resource_tools", "*.rb"],
|
12
|
+
resources: ["app", "avo", "resources", "*.rb"],
|
13
|
+
scopes: ["app", "avo", "scopes", "*.rb"],
|
14
|
+
views: ["app", "views", "avo", "*.rb"]
|
15
|
+
}
|
16
|
+
|
17
|
+
def self.scan(path: Dir.pwd)
|
18
|
+
scan = new
|
19
|
+
AvoLinter::Scanners::ResourceScanner.new(path:, scan:).scan!
|
20
|
+
scan
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :results
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@results = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def error_messages
|
30
|
+
errors.flatten.map do |error|
|
31
|
+
error[:message]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def errors
|
36
|
+
results.map(&:errors).flatten
|
37
|
+
end
|
38
|
+
|
39
|
+
def errors?
|
40
|
+
errors.present?
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "ruby-lsp"
|
2
|
+
require "prism"
|
3
|
+
require "parser/prism"
|
4
|
+
|
5
|
+
class AvoLinter::Scanners::Base
|
6
|
+
attr_reader :path
|
7
|
+
attr_reader :scan
|
8
|
+
|
9
|
+
def initialize(path:, scan:)
|
10
|
+
@path = path
|
11
|
+
@scan = scan
|
12
|
+
end
|
13
|
+
|
14
|
+
# def scan
|
15
|
+
# puts ["scanning->"].inspect
|
16
|
+
|
17
|
+
# # files.each do |file|
|
18
|
+
# # puts file
|
19
|
+
# # end
|
20
|
+
# # ap files
|
21
|
+
|
22
|
+
# file_path = "/Users/adrian/work/avocado/avohq.io-v3/app/avo/resources/account.rb"
|
23
|
+
# file_path = "/Users/adrian/work/avocado/gems/avo-linter/account.rb"
|
24
|
+
# contents = File.read(file_path)
|
25
|
+
# # puts ["contents->", contents].inspect
|
26
|
+
# # @index = RubyIndexer::Index.new
|
27
|
+
# # puts ["@index->", @index].inspect
|
28
|
+
# # return
|
29
|
+
# if false
|
30
|
+
# parsed = Parser::Prism.parse_file file_path
|
31
|
+
# puts ["parsed->", parsed].inspect
|
32
|
+
# else
|
33
|
+
# # parsed = Prism.parse contents
|
34
|
+
# # fields_tree = parsed.value.statements.body.first.body.body.find do |item|
|
35
|
+
# # puts ["item->", item.class].inspect
|
36
|
+
# # item.instance_of?(Prism::DefNode) && item.name == :fields
|
37
|
+
# # # matches?(item, Prism::DefNode)
|
38
|
+
# # end
|
39
|
+
|
40
|
+
# # scan_resource_files
|
41
|
+
# AvoLinter::Scanners::ResourceScanner.new.scan
|
42
|
+
|
43
|
+
# # fields =
|
44
|
+
# # puts ["fields_tree!!->", get_fields_in_fields_method(contents)].inspect
|
45
|
+
# # puts ["apply->", AvoLinter::Rules::FieldsAsClassMethods.new.apply].inspect
|
46
|
+
# # puts ["parsed->", parsed.value.statements.body.first.body.body].inspect
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def files
|
53
|
+
# # Read ignore patterns from the .gitignore file in the directory
|
54
|
+
gitignore_path = File.join(path, ".gitignore")
|
55
|
+
ignore_patterns = get_ignore_patterns_from_gitignore(gitignore_path)
|
56
|
+
puts ["ignore_patterns->", ignore_patterns].inspect
|
57
|
+
|
58
|
+
get_all_files_in_directory_and_subdirectories(path, ignore_patterns)
|
59
|
+
end
|
60
|
+
|
61
|
+
def paths_to_scan
|
62
|
+
[
|
63
|
+
["app", "avo", "**", "*.rb"],
|
64
|
+
["app", "controllers", "avo", "**", "*.rb"],
|
65
|
+
["app", "models", "**", "*.rb"],
|
66
|
+
["app", "views", "avo", "**", "*.erb"],
|
67
|
+
["config", "**", "*.rb"]
|
68
|
+
]
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_ignore_patterns_from_gitignore(gitignore_path)
|
72
|
+
ignore_patterns = []
|
73
|
+
|
74
|
+
if File.exist?(gitignore_path)
|
75
|
+
File.readlines(gitignore_path).each do |line|
|
76
|
+
# Remove leading and trailing whitespace
|
77
|
+
pattern = line.strip
|
78
|
+
next if pattern.empty? || pattern.start_with?("#") # Skip comments and empty lines
|
79
|
+
ignore_patterns << pattern
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
ignore_patterns
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_all_files_in_directory_and_subdirectories(directory, ignore_patterns)
|
87
|
+
all_files = []
|
88
|
+
|
89
|
+
# ap paths_to_scan
|
90
|
+
# return []
|
91
|
+
paths_to_scan.each do |path_to_scan|
|
92
|
+
Dir.glob(File.join(*path_to_scan)).each do |file|
|
93
|
+
next if File.directory?(file)
|
94
|
+
# next if ignore_patterns.any? { |pattern| File.fnmatch?(pattern, file) }
|
95
|
+
|
96
|
+
all_files << File.expand_path(file)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
all_files.flatten
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class AvoLinter::Scanners::ResourceScanner < AvoLinter::Scanners::Base
|
2
|
+
# def initialize
|
3
|
+
|
4
|
+
# end
|
5
|
+
|
6
|
+
def scan!
|
7
|
+
# puts ["resource_files->", resource_files].inspect
|
8
|
+
|
9
|
+
# errors = []
|
10
|
+
resource_files.map do |path, contents|
|
11
|
+
results = AvoLinter::Rules::FieldsAsClassMethods.new(contents:).apply_rule
|
12
|
+
scan.results << results
|
13
|
+
# scan.errors << results.errors
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# def scan_resource_files
|
20
|
+
# resource_files
|
21
|
+
# end
|
22
|
+
|
23
|
+
def resource_files
|
24
|
+
resource_file_paths.map do |file_path|
|
25
|
+
[file_path, File.read(file_path)]
|
26
|
+
end.to_h
|
27
|
+
end
|
28
|
+
|
29
|
+
def resource_file_paths
|
30
|
+
all_files = []
|
31
|
+
|
32
|
+
Dir.glob(File.join(path, *AvoLinter::Scanner::AVO_PATHS[:resources])).each do |file|
|
33
|
+
next if File.directory?(file)
|
34
|
+
all_files << file
|
35
|
+
end
|
36
|
+
|
37
|
+
all_files
|
38
|
+
end
|
39
|
+
end
|
data/lib/avo_linter.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "dry/cli"
|
3
|
+
require "active_support/core_ext/class/attribute"
|
4
|
+
require "yaml"
|
5
|
+
require "tty-command"
|
6
|
+
require "fileutils"
|
7
|
+
require "active_support/core_ext/string"
|
8
|
+
require "pathname"
|
9
|
+
require "rubygems"
|
10
|
+
require "zeitwerk"
|
11
|
+
require "awesome_print"
|
12
|
+
require_relative "support"
|
13
|
+
|
14
|
+
loader = Zeitwerk::Loader.for_gem
|
15
|
+
loader.ignore("#{__dir__}/support.rb")
|
16
|
+
loader.inflector.inflect(
|
17
|
+
"cli" => "CLI"
|
18
|
+
)
|
19
|
+
loader.setup
|
20
|
+
|
21
|
+
module AvoLinter
|
22
|
+
end
|
data/lib/support.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
def say(text)
|
2
|
+
puts "=> #{yellow(text)}"
|
3
|
+
end
|
4
|
+
|
5
|
+
def yell(text)
|
6
|
+
puts "=> #{red(text)}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def colorize(text, color_code)
|
10
|
+
"#{color_code}#{text}\e[0m"
|
11
|
+
end
|
12
|
+
|
13
|
+
def yellow(text)
|
14
|
+
colorize(text, "\e[33m")
|
15
|
+
end
|
16
|
+
|
17
|
+
def red(text)
|
18
|
+
colorize(text, "\e[31m")
|
19
|
+
end
|
20
|
+
|
21
|
+
def green(text)
|
22
|
+
colorize(text, "\e[32m")
|
23
|
+
end
|
24
|
+
|
25
|
+
def gemspec_path
|
26
|
+
Dir["#{Dir.pwd}/*.gemspec"].first
|
27
|
+
end
|
28
|
+
|
29
|
+
def gemspec
|
30
|
+
Gem::Specification.load(gemspec_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def version
|
34
|
+
@version ||= gemspec.version.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def gemspec_name
|
38
|
+
gemspec.name
|
39
|
+
end
|
40
|
+
|
41
|
+
def cmd
|
42
|
+
TTY::Command.new uuid: false
|
43
|
+
end
|
44
|
+
|
45
|
+
def change_in_file(file, regex, text_to_put_in_place)
|
46
|
+
text = File.read file
|
47
|
+
File.open(file, "w+") { |f| f << text.gsub(/#{regex}/, text_to_put_in_place) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def bundler_token
|
51
|
+
@token ||= `bundle config get https://packager.dev/avo-hq`.match(/^.*: "(.*)"$/).captures.first
|
52
|
+
end
|
data/linter.png
ADDED
Binary file
|
data/readme.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Avo Linter
|
2
|
+
|
3
|
+
> [!WARNING]
|
4
|
+
> This is highly experimental.
|
5
|
+
|
6
|
+
This CLI is used by Avo, the Avo LSP (coming soon), and other products to lint the Avo configuration files.
|
7
|
+
|
8
|
+
It will show you errors that you might have missed in the Avo files along with improvements that you could make.
|
9
|
+
|
10
|
+
![](./linter.png)
|
11
|
+
|
12
|
+
## Running it
|
13
|
+
|
14
|
+
> [!WARNING]
|
15
|
+
> Temporary.
|
16
|
+
|
17
|
+
Git clone it
|
18
|
+
|
19
|
+
## Overview
|
20
|
+
|
21
|
+
The linter uses different techniques to figure out if Avo configuration files are invalid or could be improved
|
22
|
+
|
23
|
+
#### Using Prism
|
24
|
+
|
25
|
+
The linter is using [prism](https://github.com/ruby/prism) to parse the files and create an AST for each one.
|
26
|
+
We the linter is run it will scan the files and return errors it found in the files.
|
27
|
+
|
28
|
+
It's doing that by scanning the AST for common patterns using user-defined rules similar to how rubocop is working.
|
29
|
+
|
30
|
+
### Installation
|
31
|
+
|
32
|
+
Run `bundle install` to install the dependencies.
|
33
|
+
|
34
|
+
```bash
|
35
|
+
bundle
|
36
|
+
```
|
37
|
+
|
38
|
+
### Testing
|
39
|
+
|
40
|
+
Rspec is used for tests. Run `bin/rspec` to run all tests.
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: avo-linter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adrian Marin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-11 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: The linter scans your Avo app and exposes errors
|
14
|
+
email: adrian@adrianthedev.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- Gemfile
|
20
|
+
- Gemfile.lock
|
21
|
+
- LICENSE.md
|
22
|
+
- avo-linter.gemspec
|
23
|
+
- bin/avo
|
24
|
+
- bin/rspec
|
25
|
+
- lib/avo_linter.rb
|
26
|
+
- lib/avo_linter/cli.rb
|
27
|
+
- lib/avo_linter/rules/base.rb
|
28
|
+
- lib/avo_linter/rules/fields_as_class_methods.rb
|
29
|
+
- lib/avo_linter/scanner.rb
|
30
|
+
- lib/avo_linter/scanners/base.rb
|
31
|
+
- lib/avo_linter/scanners/resource_scanner.rb
|
32
|
+
- lib/avo_linter/version.rb
|
33
|
+
- lib/support.rb
|
34
|
+
- linter.png
|
35
|
+
- readme.md
|
36
|
+
homepage: https://avohq.io
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata:
|
40
|
+
bug_tracker_uri: https://github.com/avo-hq/avo-linter/issues
|
41
|
+
changelog_uri: https://github.com/avo-hq/avo-linter/releases
|
42
|
+
documentation_uri: https://github.com/avo-hq/avo-linter
|
43
|
+
homepage_uri: https://avohq.io
|
44
|
+
source_code_uri: https://github.com/avo-hq/avo-linter
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubygems_version: 3.5.3
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Linter for Avo CMS for Ruby on Rails
|
64
|
+
test_files: []
|