bash: /dev/tty: No such device or address を script コマンドで解決する

GitHub - kyagi/rod: REPLs On Docker を作っている中で Ammonite の起動が早くなるようにあらかじめライブラリをキャッシュさせておくため .ammonite/predef.sc にライブラリを追加したあとに echo exit | amm -s を実行するようにしたところ、docker build 中に bash: /dev/tty: No such device or address が出てビルドに失敗するようになってしまった。原因は amm がメッセージを吐き出す端末(tty)が見つからないことのようだ。REPL はそもそも人間とのインタラクティブなやりとりが想定されているのだから、至極当然な気もするが、このままでは Docker イメージが作れない。

Dockerfile

RUN echo exit | amm -s
$ docker build .
(... snip ...)
bash: /dev/tty: No such device or address
bash: /dev/tty: No such device or address
java.lang.RuntimeException: Nonzero exit value: 1
  scala.sys.package$.error(package.scala:27)
  scala.sys.process.ProcessBuilderImpl$AbstractBuilder.slurp(ProcessBuilderImpl.scala:134)
  scala.sys.process.ProcessBuilderImpl$AbstractBuilder.$bang$bang(ProcessBuilderImpl.scala:104)
  ammonite.terminal.TTY$.stty(Utils.scala:118)
  ammonite.terminal.TTY$.init(Utils.scala:97)
  ammonite.terminal.Terminal$.x$1$lzycompute$1(Terminal.scala:41)
  ammonite.terminal.Terminal$.x$1$1(Terminal.scala:41)
(... snip ...)
ビルドが終わらない...

いろいろ悩んで、ぐぐって、試行錯誤したところ、script -c を使うことで回避することができた。これが今年の今まで一番のハック。╭( ・ㅂ・)و ̑̑ グッ !

$ man script
(... snip ...)
     -c, --command command
             Run the command rather than an interactive shell.  This makes it easy for a script to capture the output
             of a program that behaves differently when its stdout is not a tty.
(... snip ...)

Dockerfile

RUN export TERM=vt100 && script -qfc 'echo exit | amm -s' && rm typescript

https://github.com/kyagi/rod/blob/master/Dockerfile#L63

$ docker build .
(... snip ...)
Successfully built 38b3611dc9b5