北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

WSL上でcircleci local executeをするための試行錯誤

ハマったのでメモ。

大前提として、 WSL と Docker for Windows をうまく連携しておく必要がある。

以下のような手順。

qiita.com

ただし、

Bash on WindowsでDockerを使えて便利になったけど相対パスを指定すると不具合が出る

についてはもっと楽な解決法がある。 WSL 上に /etc/wsl.conf というファイルをおけば WSL 側のマウントポイントを変えられるので、これを Docker for Windows に合わせてしまえば良い。

Automatically Configuring WSL – Windows Command Line Tools For Developers

$ cat /etc/wsl.conf
[automount]
root = /
options = "metadata"

これで circleci local execute も実行できる準備は整っているのだけど、一点だけ問題が。デフォルトの状態だと、 symlink が読めず、以下のようなエラーが出る。以下は、 pipenv.venv で運用しているプロジェクトでのエラー例。

mkdir -p /home/circleci/project && cp -r /tmp/_circleci_local_build_repo/. /home/circleci/project
cp: cannot read symbolic link '/tmp/_circleci_local_build_repo/./.venv/bin/python': No such file or directory

この解決策は以下に書かれている。

github.com

Yes, enabling developer mode fixes the symlink problem.

Developer mode は、Settings > Update & Security > For developers > Use developer features と進むとある。一つ重要な注意は、 Developer mode が有効となってから実行された ln -s だけが Docker for Windows より読めるという点である。よって、 Developer mode はプロジェクトを始める前に有効にしておく必要がある。もしくは、問題となっている symlink をすべて削除して、作成し直す必要がある(というスクリプトを書いたほうが早そうだなあと思った)。

しかし、 pipenv.venv で使っている場合について言えばこれでもうまくいかない。 WSL のローカルである /home/some-user/.pyenv などへの symlink があり、これは Docker for windows からは見えないからだ。 symlink については、 env VIRTUALENV_ALWAYS_COPY=1 pipenv install という実行の仕方で .venv を作ればできなくなる。これで、 Checkout code フェーズでのエラーは抑えられた。

しかし、まだ終わらない。そもそもの問題として、 .pyenv 以下にインストールされているツールチェーンは、 #!/home/some-user/.pyenv/versions/3.7.2/bin/python3.7 などのシェバングを持っている。 .venv はこれらをコピーしただけなので、当然同じシェバングを持つ。こんなものをコンテナ内に持っていっても、実行できるわけがない(試してないけど、この辺は Windows 以外でも起きる問題のはず)。

.venv を複数の環境で使い回すというのがそもそも間違えていると考えられる。よって、 circleci local execute する場合には .venv を消してしまったほうがいいだろう。 .cricleci/config.yml で、ローカル実行のときだけ .venv を消すようにする。

      - run:
          name: Remove .venv for local execute                                                                                                                                                                         
          command: |
            if [[ $CIRCLE_SHELL_ENV =~ "localbuild" ]]; then
              rm -rf /home/circleci/project/.venv
            fi                                                                                                                                                                                               ```

ここまでやって、ようやく `circleci local execute` できるようになった。なんとも辛い。

そもそもコンテナ内では `PIPENV_VENV_IN_PROJECT` は指定しないほうがいい気はするのだが、 circleci のキャッシュを使うためには利用するディレクトリを固定したく、そのために指定している。