avo-cli 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b20233bc6bfcb4ecbedda18ebf6abcd64cd4763315bb4c2f85a6282d6b34dc59
4
+ data.tar.gz: 351612031dfb63c629d2a5616d6fcfc06892482c2f27ae23c141c5a6c2b22321
5
+ SHA512:
6
+ metadata.gz: 2382e4dc0b7e2339a5e9fc79625ec3aa86d596499226d20e229205131b574f5718a2ae8522d669ec9ae8c01b422aa532bbefeea10eeafa8f83bcb17607093c02
7
+ data.tar.gz: 92d54181174b55a72c52a3e0edc199e2c358d2d039096a1d91555a3555656b73a6a83f1e59fa5ea62eb191b7cc88d1f1480a12839cb36e90a82dbe1a3eb6a228
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-cli.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ require_relative "lib/avo_cli/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "avo-cli"
5
+ spec.version = AvoCli::VERSION
6
+ spec.summary = "CLI to help with common tasks for Avo CMS such as linting."
7
+ spec.description = "CLI to help with common tasks for Avo CMS such as linting."
8
+ spec.authors = ["Adrian Marin"]
9
+ spec.email = "adrian@adrianthedev.com"
10
+ spec.files = Dir["{bin,lib}/**/*", "LICENSE.MD", "readme.md", "avo-cli.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-cli/issues"
18
+ spec.metadata["changelog_uri"] = "https://github.com/avo-hq/avo-cli/releases"
19
+ spec.metadata["documentation_uri"] = "https://github.com/avo-hq/avo-cli"
20
+ spec.metadata["homepage_uri"] = "https://avohq.io"
21
+ spec.metadata["source_code_uri"] = "https://github.com/avo-hq/avo-cli"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ spec.executables << "avo"
28
+ end
data/bin/avo ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $:.unshift(File.expand_path("../", __dir__))
5
+
6
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
7
+
8
+ require_relative "../lib/avo_cli"
9
+ require_relative "../lib/avo_cli/cli"
data/bin/rspec ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,60 @@
1
+ module AvoCli
2
+ module CLI
3
+ module Commands
4
+ extend Dry::CLI::Registry
5
+
6
+ class BaseCommand < Dry::CLI::Command
7
+ def run(command)
8
+ result = cmd.run(command)
9
+ halt if result.failed?
10
+ result
11
+ rescue TTY::Command::ExitError
12
+ halt
13
+ end
14
+
15
+ def halt(message: nil)
16
+ message ||= "#{self.class} failed."
17
+ yell message
18
+ exit
19
+ end
20
+ end
21
+
22
+ class Version < BaseCommand
23
+ desc "Print version"
24
+
25
+ def call(*)
26
+ if AvoCli::PACKED
27
+ puts AvoCli::VERSION
28
+ else
29
+ puts "#{AvoCli::VERSION}-development"
30
+ end
31
+ end
32
+ end
33
+
34
+ class Lint < BaseCommand
35
+ desc "Lint gem"
36
+
37
+ option :path, aliases: ["-p"], required: false, desc: "Path of your Rails app"
38
+
39
+ def call(**)
40
+ scan = ::AvoCli::Scanner.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(AvoCli::CLI::Commands).call
@@ -0,0 +1,31 @@
1
+ class AvoCli::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 AvoCli::Rules::FieldsAsClassMethods < AvoCli::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 AvoCli::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
+ AvoCli::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,13 @@
1
+ require "ruby-lsp"
2
+ require "prism"
3
+ require "parser/prism"
4
+
5
+ class AvoCli::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
+ end
@@ -0,0 +1,27 @@
1
+ class AvoCli::Scanners::ResourceScanner < AvoCli::Scanners::Base
2
+ def scan!
3
+ resource_files.map do |path, contents|
4
+ results = AvoCli::Rules::FieldsAsClassMethods.new(contents:).apply_rule
5
+ scan.results << results
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def resource_files
12
+ resource_file_paths.map do |file_path|
13
+ [file_path, File.read(file_path)]
14
+ end.to_h
15
+ end
16
+
17
+ def resource_file_paths
18
+ all_files = []
19
+
20
+ Dir.glob(File.join(path, *AvoCli::Scanner::AVO_PATHS[:resources])).each do |file|
21
+ next if File.directory?(file)
22
+ all_files << file
23
+ end
24
+
25
+ all_files
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module AvoCli
2
+ VERSION = "0.0.1"
3
+ end
data/lib/avo_cli.rb ADDED
@@ -0,0 +1,23 @@
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 AvoCli
22
+ PACKED = File.exist?("../spec")
23
+ 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 CLI
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 do some common actions like linting 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
+ ```bash
15
+ gem install avo-cli
16
+ avo
17
+ ```
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,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: avo-cli
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: CLI to help with common tasks for Avo CMS such as linting.
14
+ email: adrian@adrianthedev.com
15
+ executables:
16
+ - avo
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - Gemfile.lock
22
+ - LICENSE.md
23
+ - avo-cli.gemspec
24
+ - bin/avo
25
+ - bin/rspec
26
+ - lib/avo_cli.rb
27
+ - lib/avo_cli/cli.rb
28
+ - lib/avo_cli/rules/base.rb
29
+ - lib/avo_cli/rules/fields_as_class_methods.rb
30
+ - lib/avo_cli/scanner.rb
31
+ - lib/avo_cli/scanners/base.rb
32
+ - lib/avo_cli/scanners/resource_scanner.rb
33
+ - lib/avo_cli/version.rb
34
+ - lib/support.rb
35
+ - linter.png
36
+ - readme.md
37
+ homepage: https://avohq.io
38
+ licenses:
39
+ - MIT
40
+ metadata:
41
+ bug_tracker_uri: https://github.com/avo-hq/avo-cli/issues
42
+ changelog_uri: https://github.com/avo-hq/avo-cli/releases
43
+ documentation_uri: https://github.com/avo-hq/avo-cli
44
+ homepage_uri: https://avohq.io
45
+ source_code_uri: https://github.com/avo-hq/avo-cli
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.5.3
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: CLI to help with common tasks for Avo CMS such as linting.
65
+ test_files: []