Dev Containerで最強のPython開発環境を作る 〜その1・Pythonスクリプトを動かす〜

こんにちは。
各位お世話になっております。 [Intimate Merger](https://corp.intimatemerger.com/) のitayaです。

みなさま、Dev Containerは使っていますか？

https://code.visualstudio.com/docs/devcontainers/containers

便利ですよね。
開発チーム内で環境を統一する方法はこれまで様々な方法が出てきましたが、Dev Containerはその中でもかなり期待できるもののように感じます。
VSCodeを利用した開発においてはデファクトスタンダードになっていきそうな勢いです。

Dev Containerの何がいいのかという話は様々な方が素晴らしい記事を上げていますので、そちらを参照してください。
この記事はPythonを動作させるための開発環境というところにスコープを絞って書いていきます。

今回は 

- **Dev ContainerでPythonスクリプトを動かすところ** 
- **Dev Containerでgitを動作させるところ**

です。

# Dev Containerを導入する

::: note info
[Dev Containers拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)はインストールされている前提で話を進めます。
:::

## `devcontainer.json` ファイルを作成する

Dev Containerの設定ファイルとなるのが `devcontainer.json`　です。
VSCodeのコマンドパレットから **コンテナー構成ファイルを開く** を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147093/4f873d86-7c4d-142c-bd07-f842d9550f01.png)

Python 3で検索すると色々出てきますが、今回はオーソドックスなものを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147093/80735c06-c324-5ab8-def1-b57aeddc35bc.png)

versionは3.11を選び、 `devcontainer.json` が作成されます。
これをベースにこれからカスタマイズしていきます。

# Pythonスクリプトを作成する

とりあえず動かしたいので、適当にhello worldを出力するだけのpythonスクリプトを作成します。

```src/hello_world.py
print("HELLO WORLD!")
```

# Dev Containerを起動する

コマンドパレットから **コンテナーでリビルドして再度開く** を選択することで起動できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147093/efe092fd-26b9-6ed7-2ad7-bd667cecc1c7.png)

一度起動した後なら **コンテナーで再度開く** でOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147093/041ac32a-fe0d-6e76-b6af-d9b1cc0aabc2.png)

Pythonの実行環境ができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147093/71231a8b-61b2-680e-d3c9-2a74b2be5266.png)

コンテナ内なのでpipを使ってライブラリをインストールしても元環境を汚すことはないですし、気に入らなければもう一度最初から作り直せばいいので楽に開発を進めることができますね。

# git

コードを書くならgitで管理したいですよね。
Dev Container内でcommitし、GitHubへpushして開発を進めていく流れができないと、開発体験としては良くないものになるでしょう。

Dev Container内にgitがあれば `git commit` 等は可能ですが、 `git push` となるとGitHubへの認証が必要になります。
実を言うと、git関連の設定は公式ドキュメントに全て書いてあります。

https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials

## gitconfigの情報をコンテナにコピーする

::: note info
ローカル環境において、gitの初期設定時に以下のようなコマンドで `.gitconfig` が作られているのを前提に話を進めます。

```.gitconfig 設定コマンド
git config --global user.name "Your Name"
git config --global user.email "your.email@address"
```
:::

VSCodeには、 `.gitconfig` の設定をDev Containerにコピーする設定があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/769250af-68d2-8da4-0b85-bc7de750fc9a.png)

こちらにチェックを入れることで有効化され、 `user.name` や `user.email` の設定が引き継がれるようになります。

## gitの認証情報をコンテナ内で使用する

GitHubへの認証ではCredential Helperを利用するか、SSHを利用するかの2択になると思います。
個人的にはGitHub CLIのCredential Helperを利用して、HTTPSでアクセスする方法をオススメします。

### Credential Helper

https://docs.github.com/ja/get-started/getting-started-with-git/caching-your-github-credentials-in-git

GitHub CLIをinstallしてログインコマンドを実行するだけでOKです。

```
gh auth login
```

ローカル環境で `gh auth login` しておけば、Dev Container内でもGitHubへの認証が通っている状態となります。
keyの生成を行う必要もないため、こちらの方式の方が好きです。

### SSH

::: note info
`ssh-keygen` を使ってssh keyを作成し、それをGithubへの認証に使っているのを前提に話を進めます。
これ移行、認証に使うssh keyは `~/.ssh/github_id_ed25519` とします。
:::

Dev Containerには、ローカルで `ssh-agent` を動作させている場合、自動的に `ssh-agent` を転送させる機能があります。
これを利用することでDev Container内でGitHubへの認証が行えます。

#### `ssh-agent` が起動済みの場合

ssh keyを追加で登録すればokです。

```
ssh-add ~/.ssh/github_id_ed25519
```

`ssh-agent` が起動していない場合は `Could not open a connection to your authentication agent.` のようなエラーが発生します。

#### `ssh-agent` を起動していない場合

起動していない場合は `ssh-agent` を起動して `ssh-add` をすればそれでokです。
せっかくなのでbashを開くたびに `ssh-agent` が起動するように設定をしてしまいましょう。

`~/.bash_profile` に以下を追記します。

```~/.bash_profile
if [ -z "$SSH_AUTH_SOCK" ]; then
   # Check for a currently running instance of the agent
   RUNNING_AGENT="`ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]'`"
   if [ "$RUNNING_AGENT" = "0" ]; then
        # Launch a new instance of the agent
        ssh-agent -s &> $HOME/.ssh/ssh-agent
   fi
   eval `cat $HOME/.ssh/ssh-agent`
fi
```

追記したら `source ~/.bash_profile` を行って反映させましょう。
`ssh-agent` が動いていない状態でこれを行うと、以下のようなテキストが返ってきます。

```
Agent pid 32088
```

エラーメッセージが表示される場合は `ssh-agent` が正常に起動していない場合があります。
`ssh-agent` のプロセスが存在するのにエラーになった場合は、以下のコマンドを駆使して不要なプロセスを探してkillしておきましょう。
大体の場合が `$HOME/.ssh/ssh-agent` が作成されていないことによるエラーです。

```
ps -ax | grep 'ssh-agent -s'
```

# あとがき

一度に全て書き上げるつもりだと完成する前に筆を折る気しかしないので、小さく出していきます。
次回は拡張機能を組織内でうまく使うための設定や、Pythonをもう少し便利にするところを書きます。

是非、明日以降の記事も読んでいただけたらと思います！

https://qiita.com/advent-calendar/2023/intimatemerger

# 参考

- https://code.visualstudio.com/docs/devcontainers/create-dev-container
- https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials