Input中心のブログ

vim-testをdocker-composeに対応させる

January 02, 2021

お正月に環境の見直しをしていたのでブログにまとめる。 vim-testは様々なテストに対応したプラグインで、vimからコマンド一つでテストをすることが出来る。 しかし、docker-composeに対応するためにちょっとした工夫が必要だったのでメモとしてまとめる。

基本的な設定は以下の通り

let test#strategy = 'neovim'
let test#neovim#term_position = 'vert'

nnoremap [vim-test] <Nop>
nmap <Leader>e [vim-test]
nmap <silent> [vim-test]a <cmd>TestSuit<CR>
nmap <silent> [vim-test]f <cmd>TestFile<CR>
nmap <silent> [vim-test]t <cmd>TestVisit<CR>

これで、ショートカットを使ってvim-testを呼び出し可能。 ただし、vim-testでは初期ではdocker-composeに対応していないので、自分で対応するためのコードを書く必要がある。ドキュメントはいかにまとまっている。

https://github.com/vim-test/vim-test#transformations

つまり自分でtransformationを書く必要がある。ただ、docker-composeをいい感じにするには、テスト用のコンテナのservice名が分からないといけない。

$ docker-compose run --rm SERVICE_NAME CMD

この SERVICE_NAME を頑張ってvim起動したら毎回打ち込むのでもいいけど、こういうのは怠惰になったほうがいいと偉い人が行ってたのでちょっと頑張ってみる 頑張ってみた結果が以下のとおり、

function! DockerComposeTransformation(cmd) abort
  let docker_compose_project = findfile("docker-compose.yml", ".;")
  if !empty(docker_compose_project)
    let docker_compose = readfile(docker_compose_project)
    let docker_compose_copy = copy(docker_compose)
    let docker_compose = map(docker_compose, {key, val -> [key, val]})
    let cwd = split(getcwd(), '/')[-1]
    let context_lines = filter(docker_compose, 'v:val[1] =~ "context:"')
    let target_context_line = filter(context_lines, printf('v:val[1] =~ "%s"', cwd))
    if len(target_context_line) == 0
      echohl ErrorMsg
      echomsg 'context was not found'
      echohl None
      return a:cmd
    endif
    let target = ''
    for i in range(target_context_line[0][0]-1, 0, -1)
      " count tab
      " for example
      " services:
      "   SERVICE_NAME:
      " # ^ this is not space
      "     build:
      "       context: ./DIR_NAME  # <-- target_context_line[0][0]
      if docker_compose_copy[i][2] != ' '
        let target = docker_compose_copy[i][2:-2]
        break
      endif

    endfor
    return printf('docker-compose run --rm %s %s', target, a:cmd)
  endif
  return a:cmd
endfunction

let g:test#custom_transformations = {'docker': function('DockerComposeTransformation')}
let g:test#transformation = 'docker'

基本的にやっていることとしては、

  1. docker-compose.ymlファイルを探す
  2. docker-compose.ymlの contexts:が書いている場所を探す
  3. context: 以下がカレントディレクトリと同じ名前になっているものを探す
  4. 見つけた context: の場所から上にたどっていき、service名を見つけ出す

基本的にはこんな感じでやってる。ロジックとしてはがばがばな気がするので、また不具合を見つけたら直していきます。 これでテストがちょっとやりやすくなった。