docker logs に Stream 機能が実装されてべんり (v0.7.1)

Usage: docker logs CONTAINER

Fetch the logs of a container

  -f=false: Follow log output

コンテナの標準(出力|エラー)を確認するために使う docker logs は、実行した時点までのログが出力されるだけで tail -f のようなことができませんでしたが、0.7.1 でついに -f オプションが実装されてとても快適になったので使いましょう。

余談ですがコンテナのログは /var/lib/docker/containers/<container-id>/<container-id>-json.log に標準(出力|エラー) 一行ごとに json で出力されているで自分でパースしたら今までもなんとかできました。

job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
chdir: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

複数のコンテナを同時に実行する際に docker run -v でコンテナに割り当てたディレクトリは正しく存在するのに二つ目のコンテナが起動するとカレントワーキングディレクトリがないと怒られる。 これは存在しないディレクトリに対してボリュームを割り当てたことによって起きるようで事前にディレクトリを作成しておくことで回避できた。

FROM ubuntu

RUN mkdir /workspace

たったこれだけのことで大事な休日の3時間程度を失った。かなしい。

Vim で GitHub Flavored Markdown をプレビューする

13/09/28 23:13 設定の記述を誤っていたため修正しました。 339d483

検索して見つかる Vim で Markdown をプレビューする方法は、どれも GitHub Flavored Markdown に対応しておらず GitHub で改めて確認すると表示が崩れていることが多々ありました。そんな悲劇をなくすため GitHub Markdown Rendering API を使ってプレビューする vim-quickrun-markdown-gfm を作りました。

https://github.com/superbrothers/vim-quickrun-markdown-gfm

以下の利点があります。

  • GitHub Flavored Markdown でプレビューできる
  • Perl, Ruby などの Markdown parser に依存していない
  • 別途プレビューサーバを起動する必要がない

vim-quickrun はデフォルトで markdown のプレビューに対応していますが、redcarpet に依存したくなかったり、コードブロックが GitHub Flavored Markdown に対応していないため、vim-quickrun-markdown-gfm では GitHub Markdown Rendering API を使っています。またプレビューを GitHub で閲覧した際と同じにするために revolunet/sublimetext-markdown-previewCSS を使わせてもらっています。

インストール (Vundle)

.vimrc に下記を追記します。

Bundle 'thinca/vim-quickrun'
Bundle 'mattn/webapi-vim'
Bundle 'tyru/open-browser.vim'
Bundle 'superbrothers/vim-quickrun-markdown-gfm'
let g:quickrun_config = {
\   'markdown': {
\     'type': 'markdown/gfm',
\     'outputter': 'browser'
\   }
\ }

その後 :BundleInstall を実行します。

GitHub Enterprise の API を利用したい場合は、下記を追記してください。

let g:quickrun_markdown_gfm_github_api_url = 'https://<your-github-enterprise-hostname>/api/v3'

使い方

:QuickRun markdown

詳しくは vim-quickrun のヘルプを確認してください。

:h quickrun

f:id:superbrothers:20130928123447p:plain

Vim で ToDo リストを書くための Tips (改)

Vim で ToDo リストを書きやすくするために vimでtodoリストを書くためのtips - Qiita を使わせてもらっていたのですが、もう少しなんとかしたいと思い、少し書き換えてみました。べんり。

変更前

変更後

  1. 完了した際に自動的に時刻を挿入する
    タスクが完了した際に時刻を行末に自動的に挿入するようにしました。未完了に戻したときは時刻を削除するようにもなっています。
  2. 未完了時は「赤」、完了時は「緑」にハイライトを設定
    視認性向上のために。
  3. foldmethod を変更
    インデントでの折りたたみでは子だけが折りたたまれていました。期待では親から折りたたんでほしかったのでそのように変更しています。
  4. foldtext を変更
    デフォルトの foldtext ではチェックボックスの表示が折りたたまれていない上下のタスクとズレるため変更しています。

フォーク元と同じく ~/.vim/ftplugin/markdown.vim として保存して使ってください。他のファイルタイプで使いたければ必要に応じて変更してください。

" forked from http://qiita.com/naoty_k/items/56eddc9b76fe630f9be7

" todoリストを簡単に入力する
abbreviate tl - [ ]

" 入れ子のリストを折りたたむ
setlocal foldmethod=expr foldexpr=MkdCheckboxFold(v:lnum) foldtext=MkdCheckboxFoldText()
function! MkdCheckboxFold(lnum)
    let line = getline(a:lnum)
    let next = getline(a:lnum + 1)
    if MkdIsNoIndentCheckboxLine(line) && MkdHasIndentLine(next)
        return 1
    elseif (MkdIsNoIndentCheckboxLine(next) || next =~ '^$') && !MkdHasIndentLine(next)
        return '<1'
    endif
    return '='
endfunction
function! MkdIsNoIndentCheckboxLine(line)
    return a:line =~ '^- \[[ x]\] '
endfunction
function! MkdHasIndentLine(line)
    return a:line =~ '^[[:blank:]]\+'
endfunction
function! MkdCheckboxFoldText()
    return getline(v:foldstart) . ' (' . (v:foldend - v:foldstart) . ' lines) '
endfunction

" todoリストのon/offを切り替える
nnoremap <buffer> <Leader><Leader> :call ToggleCheckbox()<CR>
vnoremap <buffer> <Leader><Leader> :call ToggleCheckbox()<CR>

" 選択行のチェックボックスを切り替える
function! ToggleCheckbox()
  let l:line = getline('.')
  if l:line =~ '\-\s\[\s\]'
    " 完了時刻を挿入する
    let l:result = substitute(l:line, '-\s\[\s\]', '- [x]', '') . ' [' . strftime("%Y/%m/%d (%a) %H:%M") . ']'
    call setline('.', l:result)
  elseif l:line =~ '\-\s\[x\]'
    let l:result = substitute(substitute(l:line, '-\s\[x\]', '- [ ]', ''), '\s\[\d\{4}.\+]$', '', '')
    call setline('.', l:result)
  end
endfunction

syn match MkdCheckboxMark /-\s\[x\]\s.\+/ display containedin=ALL
hi MkdCheckboxMark ctermfg=green
syn match MkdCheckboxUnmark /-\s\[\s\]\s.\+/ display containedin=ALL
hi MkdCheckboxUnmark ctermfg=red

lightline.vim に乗り換えた

若干の現実逃避により Lokaltog/vim-powerline から itchyny/lightline.vim に乗り換えた。Qiita のカスタマイズエントリ を参考に vim-gitgutter の連携を追記した。

vim-powerline は deprecated のようだけど、 Lokaltog/powerline で引き続き開発されているようだ。lightline.vim に満足したので気にしないことにする。

Bundle 'itchyny/lightline.vim'
let g:lightline = {
      \ 'colorscheme': 'solarized_dark',
      \ 'mode_map': { 'c': 'NORMAL' },
      \ 'active': {
      \   'left': [ [ 'mode', 'paste' ], [ 'fugitive', 'gitgutter', 'filename' ] ]
      \ },
      \ 'component_function': {
      \   'modified': 'MyModified',
      \   'readonly': 'MyReadonly',
      \   'fugitive': 'MyFugitive',
      \   'gitgutter': 'MyGitGutter',
      \   'filename': 'MyFilename',
      \   'fileformat': 'MyFileformat',
      \   'filetype': 'MyFiletype',
      \   'fileencoding': 'MyFileencoding',
      \   'mode': 'MyMode',
      \ },
      \ 'separator': { 'left': '⮀', 'right': '⮂' },
      \ 'subseparator': { 'left': '⮁', 'right': '⮃' }
      \ }

function! MyModified()
  return &ft =~ 'help\|vimfiler\|gundo' ? '' : &modified ? '+' : &modifiable ? '' : '-'
endfunction

function! MyReadonly()
  return &ft !~? 'help\|vimfiler\|gundo' && &ro ? '⭤' : ''
endfunction

function! MyFilename()
  return ('' != MyReadonly() ? MyReadonly() . ' ' : '') .
        \ (&ft == 'vimfiler' ? vimfiler#get_status_string() : 
        \  &ft == 'unite' ? unite#get_status_string() : 
        \  &ft == 'vimshell' ? substitute(b:vimshell.current_dir,expand('~'),'~','') : 
        \ '' != expand('%t') ? expand('%t') : '[No Name]') .
        \ ('' != MyModified() ? ' ' . MyModified() : '')
endfunction

function! MyFugitive()
  return &ft !~? 'vimfiler\|gundo' && exists('*fugitive#head') && strlen(fugitive#head()) ? '⭠ '.fugitive#head() : ''
endfunction

function! MyGitGutter()
  if ! exists('*GitGutterGetHunkSummary')
        \ || ! get(g:, 'gitgutter_enabled', 0)
        \ || winwidth('.') <= 90
    return ''
  endif
  let symbols = [
        \ g:gitgutter_sign_added . ' ',
        \ g:gitgutter_sign_modified . ' ',
        \ g:gitgutter_sign_removed . ' '
        \ ]
  let hunks = GitGutterGetHunkSummary()
  let ret = []
  for i in [0, 1, 2]
    if hunks[i] > 0
      call add(ret, symbols[i] . hunks[i])
    endif
  endfor
  return join(ret, ' ')
endfunction

function! MyFileformat()
  return winwidth('.') > 70 ? &fileformat : ''
endfunction

function! MyFiletype()
  return winwidth('.') > 70 ? (strlen(&filetype) ? &filetype : 'no ft') : ''
endfunction

function! MyFileencoding()
  return winwidth('.') > 70 ? (strlen(&fenc) ? &fenc : &enc) : ''
endfunction

function! MyMode()
  return winwidth('.') > 60 ? lightline#mode() : ''
endfunction

f:id:superbrothers:20130829001215p:plain

Chef Cookbook 開発環境の近況

Chef の高い学習コストを払い終えたのかやっとしっくりき始めたけど、Cookbook の開発環境についてはまだまだしっくりには程遠い。いまのところこんな感じというのをシェア。

Vagrant でたてたローカルの VM で Cookbook でやりたいことを直接試して、いい感じになったら serverspec でテストを書いて通るようになったら vagrant-sahara でロールバック後、Cookbook に置き換える感じです。ServerSpec でのテストはきれいにかけて Chef 依存もなく気に入っているけど、attributes の値を変えたときに正しく反映されるかなどは書けず、他のテストツールも合わせて使わないとなあと感じてる。test-kitchen も気になってて見た感じはこのスタイルを一枚の yaml にまとめられる程度の印象で触れていない。

不満はとにかく Vagrant が遅いこと。VirtualBox が遅いのかと思い、vagrant-lxc を試してみたけどあまり変わらなかった(バグってるのでそのままは使えない。VM へのプロビジョンがもう少しでも高速化できれば Guard も使っていきたいけど現状では難しい印象。
serverspec-init で生成される spec_helper.rb では素直に vagrant ssh-config を毎回実行しているけど、これに20秒程度かかっていてどうしようもない感じだったので以下のように修正している。これですぐテストが走るようにはなる。

require 'serverspec'
require 'pathname'
require 'net/ssh'

include Serverspec::Helper::Ssh
# include Serverspec::Helper::DetectOS
# DetectOS をやめて決め打ちにすることで数秒短縮できる
include Serverspec::Helper::Debian

SSH_CONFIG_FILE = '.vagrant-ssh-config'

...

      # `vagrant ssh-config` がとてつもなく遅いので結果をキャッシュしている
      # Multi-VM でうまくいくかは試していない
      unless File.exists?(SSH_CONFIG_FILE)
        system("vagrant ssh-config > #{SSH_CONFIG_FILE}")
      end
      config = File.open(SSH_CONFIG_FILE).read
      if config != ''
        config.each_line do |line|
          if match = /HostName (.*)/.match(line)
            c.host = match[1]
          elsif  match = /User (.*)/.match(line)
            user = match[1]
          elsif match = /IdentityFile (.*)/.match(line)
            options[:keys] =  [match[1].gsub(/"/,'')]
          elsif match = /Port (.*)/.match(line)
            options[:port] = match[1]
          end
        end
      end

ZSH プロンプトに rbenv version を表示する

rbenv version を使用して表示させていたけど、若干動作が重かったので書き換えた。.ruby-version が存在する場合は、そのバージョンを表示します。またそのバージョンがインストールされていない場合は {?} を表示します。

f:id:superbrothers:20130618010701p:plain

以下のスクリプトを ~/.zsh.d 以下など適当な場所に置いて、.zshrcsource ~/.zsh.d/rbenv.rb のように読み込んでください。

# Allow for functions in the prompt.
setopt PROMPT_SUBST

autoload -U colors && colors

if `which rbenv >/dev/null 2>&1` && [ -z "$RBENV_ROOT" ]; then
    export RBENV_ROOT=`rbenv root`
fi

rbenv_version() {
    if [ -n "$RBENV_ROOT" ]; then
        VERSION=''
        NOTFOUND=''
        if [ -r .ruby-version ]; then
            VERSION=`cat .ruby-version`
            ls "${RBENV_ROOT}/versions" | egrep "^$VERSION$" >/dev/null 2>&1 || NOTFOUND='{?}'
        else
            VERSION=`cat "${RBENV_ROOT}/version"`
        fi
        echo "%{$fg[red]%}[$VERSION]$NOTFOUND%{$reset_color%}"
    fi
}

R?RROMPT で使います

RPROMPT="$(rbenv_version)"