avo-linter 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 +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 | 
            +
            
         | 
| 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: []
         |