Add support for Packwerk (#4594)

Packwerk (https://github.com/Shopify/packwerk) is a Ruby gem
used to enforce boundaries and modularize Rails applications.
This commit is contained in:
ymap 2023-09-13 23:53:24 +09:00 committed by GitHub
parent ac615e7f65
commit 9092af9ad6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,55 @@
" Author: ymap - https://github.com/ymap
" Description: Packwerk, a static analyzer used to enforce boundaries and modularize Rails applications.
call ale#Set('ruby_packwerk_executable', 'packwerk')
call ale#Set('ruby_packwerk_options', '')
function! ale_linters#ruby#packwerk#Handle(buffer, lines) abort
let l:pattern = '\v^[^:]+:(\d+):(\d+)$'
let l:index = 0
let l:output = []
while l:index < len(a:lines) - 1
let l:cleaned_line = substitute(a:lines[l:index], '\v\e\[[0-9;]*m', '', 'g')
let l:match = matchlist(l:cleaned_line, l:pattern)
if len(l:match) > 0
call add(l:output, {
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': a:lines[l:index + 1],
\})
endif
let l:index += 1
endwhile
return l:output
endfunction
function! ale_linters#ruby#packwerk#GetCommand(buffer) abort
let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
if l:rails_root is? ''
return ''
endif
let l:executable = ale#Var(a:buffer, 'ruby_packwerk_executable')
let l:sep = has('win32') ? '\' : '/'
let l:abs_path = expand('#' . a:buffer . ':p')
let l:rel_path = substitute(l:abs_path, escape(l:rails_root . l:sep, '\'), '', '')
return ale#ruby#EscapeExecutable(l:executable, 'packwerk')
\ . ' check'
\ . ale#Pad(ale#Var(a:buffer, 'ruby_packwerk_options'))
\ . ' '
\ . ale#Escape(rel_path)
endfunction
call ale#linter#Define('ruby', {
\ 'name': 'packwerk',
\ 'executable': {b -> ale#Var(b, 'ruby_packwerk_executable')},
\ 'command': function('ale_linters#ruby#packwerk#GetCommand'),
\ 'callback': 'ale_linters#ruby#packwerk#Handle',
\ 'lint_file': 1,
\})

View File

@ -48,6 +48,26 @@ g:ale_ruby_debride_options *g:ale_ruby_debride_options*
This variable can be changed to modify flags given to debride.
===============================================================================
packwerk *ale-ruby-packwerk*
g:ale_ruby_packwerk_executable *g:ale_ruby_packwerk_executable*
*b:ale_ruby_packwerk_executable*
Type: |String|
Default: `'packwerk'`
Override the invoked packwerk binary. Set this to `'bundle'` to invoke
`'bundle` `exec` packwerk'.
g:ale_ruby_packwerk_options *g:ale_ruby_packwerk_options*
*b:ale_ruby_packwerk_options*
Type: |String|
Default: `''`
This variable can be changed to modify flags given to packwerk.
===============================================================================
prettier *ale-ruby-prettier*

View File

@ -547,6 +547,7 @@ Notes:
* `brakeman`!!
* `cspell`
* `debride`
* `packwerk`!!
* `prettier`
* `rails_best_practices`!!
* `reek`

View File

@ -3324,6 +3324,7 @@ documented in additional help files.
brakeman..............................|ale-ruby-brakeman|
cspell................................|ale-ruby-cspell|
debride...............................|ale-ruby-debride|
packwerk..............................|ale-ruby-packwerk|
prettier..............................|ale-ruby-prettier|
rails_best_practices..................|ale-ruby-rails_best_practices|
reek..................................|ale-ruby-reek|

View File

@ -556,6 +556,7 @@ formatting.
* [brakeman](http://brakemanscanner.org/) :floppy_disk:
* [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell)
* [debride](https://github.com/seattlerb/debride)
* [packwerk](https://github.com/Shopify/packwerk) :floppy_disk:
* [prettier](https://github.com/prettier/plugin-ruby)
* [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) :floppy_disk:
* [reek](https://github.com/troessner/reek)

View File

@ -0,0 +1,29 @@
Before:
call ale#test#SetDirectory('/testplugin/test/handler')
cd ..
runtime ale_linters/ruby/packwerk.vim
After:
call ale#test#RestoreDirectory()
call ale#linter#Reset()
Execute(The packwerk handler should parse lines correctly):
call ale#test#SetFilename('test-files/ruby/valid_rails_app/app/models/thing.rb')
AssertEqual
\ [
\ {
\ 'lnum': 36,
\ 'col': 100,
\ 'text': 'Dependency violation: ::Edi::Source belongs to ''edi'', but ''billing'' does not specify a dependency on ''edi''.',
\ }
\ ],
\ ale_linters#ruby#packwerk#Handle(bufnr(''), [
\ '/Users/JaneDoe/src/github.com/sample-project/billing/app/jobs/document_processing_job.rb:36:100',
\ 'Dependency violation: ::Edi::Source belongs to ''edi'', but ''billing'' does not specify a dependency on ''edi''.',
\ 'Are we missing an abstraction?',
\ 'Is the code making the reference, and the referenced constant, in the right packages?',
\ '',
\ 'Inference details: ''Edi::Source'' refers to ::Edi::Source which seems to be defined in edi/app/models/edi/source.rb.',
\ ])

View File

@ -0,0 +1,38 @@
Before:
call ale#assert#SetUpLinterTest('ruby', 'packwerk')
call ale#test#SetFilename('../test-files/ruby/valid_rails_app/db/test.rb')
let g:ale_ruby_packwerk_executable = 'packwerk'
let g:ale_ruby_packwerk_options = ''
let b:sep = has('win32') ? '\' : '/'
After:
unlet! b:sep
call ale#assert#TearDownLinterTest()
Execute(Executable should default to packwerk):
AssertLinter 'packwerk', ale#Escape('packwerk')
\ . ' check '
\ . ale#Escape('db' . b:sep . 'test.rb')
Execute(Should be able to set a custom executable):
let g:ale_ruby_packwerk_executable = 'bin/packwerk'
AssertLinter 'bin/packwerk', ale#Escape('bin/packwerk')
\ . ' check '
\ . ale#Escape('db' . b:sep . 'test.rb')
Execute(Setting bundle appends 'exec packwerk'):
let g:ale_ruby_packwerk_executable = 'path to/bundle'
AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
\ . ' exec packwerk'
\ . ' check '
\ . ale#Escape('db' . b:sep . 'test.rb')
Execute(Command callback should be empty when not in a valid Rails app):
call ale#test#SetFilename('../test-files/ruby/not_a_rails_app/test.rb')
AssertLinter 'packwerk', ''