.netrcを使ってGitHubのPrivate Repositoryからpipenv install 

# ■はじめに

「GitHubのPrivate Repositoryで管理されているライブラリをPipenvから使いたい」場合に苦戦したのでまとめておきます。

## ○(2019/08時点) Pipenvで環境変数からクレデンシャルを差し込む方法について

公式ドキュメントが非常にわかりづらいのですが、[環境変数でPipfileにクレデンシャルを差し込む](
https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#injecting-credentials-into-pipfiles-via-environment-variables)機能が有効になるのは``Pipfile中の[[source]]``内だけのようです。

* [install private git repos · Issue \#313 · pypa/pipenv](https://github.com/pypa/pipenv/issues/313)

[bundlerの類似機能](https://bundler.io/v1.16/bundle_config.html#CREDENTIALS-FOR-GEM-SOURCES)とは挙動が違うっぽいのでご注意ください。

で、上記のやりとりを見たところ「そういう場合は.netrcを使え」とのこと。


# ■Private Libraryを準備

既にPrivate Libraryがあればそれを使っていただければと思いますが、今回は[cookiecutter](https://github.com/cookiecutter/cookiecutter)を使い、サンプルライブラリを準備するところからやってみます。

```shell
$ pip install -U cookiecutter
$ cookiecutter https://github.com/audreyr/cookiecutter-pypackage

cookiecutter https://github.com/audreyr/cookiecutter-pypackage
full_name [Audrey Roy Greenfeld]:
email [audreyr@example.com]:
github_username [audreyr]: terukizm
project_name [Python Boilerplate]: MyPrivateLibSamplePy
project_slug [myprivatelibsamplepy]: MyPrivateLibSamplePy
project_short_description [Python Boilerplate contains all the boilerplate you need to create a Python package.]:
pypi_username [terukizm]:
version [0.1.0]:
use_pytest [n]:
use_pypi_deployment_with_travis [y]:
add_pyup_badge [n]:
Select command_line_interface:
1 - Click
2 - No command-line interface
Choose from 1, 2 (1, 2) [1]:
create_author_file [y]:
Select open_source_license:
1 - MIT license
2 - BSD license
3 - ISC license
4 - Apache Software License 2.0
5 - GNU General Public License v3
6 - Not open source
Choose from 1, 2, 3, 4, 5, 6 (1, 2, 3, 4, 5, 6) [1]:

$ ls MyPrivateLibSamplePy/
MyPrivateLibSamplePy.py
__init__.py
cli.py
```

生成されたテンプレートを微調整して、ライブラリとして動作確認しやすくしておきます。(`MyPrivateLibSamplePy.message()`)

```diff:MyPrivateLibSamplePy/MyPrivateLibSamplePy.py
--- a/MyPrivateLibSamplePy/MyPrivateLibSamplePy.py
+++ b/MyPrivateLibSamplePy/MyPrivateLibSamplePy.py
@@ -1,3 +1,5 @@
 # -*- coding: utf-8 -*-

 """Main module."""
+def message():
+    return "This is My Private Lib."
```

```diff:MyPrivateLibSamplePy/cli.py
--- a/MyPrivateLibSamplePy/cli.py
+++ b/MyPrivateLibSamplePy/cli.py
@@ -3,14 +3,14 @@
 """Console script for MyPrivateLibSamplePy."""
 import sys
 import click
+import MyPrivateLibSamplePy


 @click.command()
 def main(args=None):
     """Console script for MyPrivateLibSamplePy."""
-    click.echo("Replace this message by putting your code into "
-               "MyPrivateLibSamplePy.cli.main")
-    click.echo("See click documentation at http://click.pocoo.org/")
+    text = MyPrivateLibSamplePy.message()
+    click.echo(text)
     return 0
```

動作確認ができたら、***GitHubのPrivate Repository***にgit pushしておきましょう。

```
$ python MyPrivateLibSamplePy/cli.py
This is My Private Lib.

$ git init
(略)
$ git push -u origin master
```

上記のサンプルライブラリは以下に置いてありますので、forkしたあとにリポジトリをprivateにしていただくのでもよいかも。
https://github.com/terukizm/MyPrivateLibSamplePy


# ■Pipenvから利用

用意したライブラリは当然認証を通さないと`pip install(git clone)`できません。ローカルから使う場合ならSSH認証(`git+ssh`)で問題ないのですが、今回はDocker Imageで利用する場合を想定し、`.netrc`と[GitHub Personal Access Token](https://help.github.com/ja/articles/creating-a-personal-access-token-for-the-command-line)を利用します。[^1]

[^1]: docker build(git clone)用にSSHの秘密鍵を払い出してもいいのですが、あんまりそれやりたくないなあという人向けです

取得したPersonal Access Tokenを`.netrc`に設定します。tokenは`login`に設定してください。

* 参考: [GitHub「Personal access tokens」の設定方法 \- Qiita](https://qiita.com/kz800/items/497ec70bff3e555dacd0)

```~/.netrc
machine github.com
login xxxxxxxxxxxxxxxxxxxxx
password x-oauth-basic
```

この状態で`git+https`を指定して`pipenv install`します。`Pipfile`にライブラリが追加されていること、`pip freeze`でライブラリが利用できる状態になっていることを確認してください。

```shell
$ mkdir ~/netrc-pipenv-sample
$ pipenv install git+https://github.com/terukizm/MyPrivateLibSamplePy.git#egg=MyPrivateLibSamplePy
(略)
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 1/1 — 00:00:04
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
```

```bash
$ ls 
Pipfile         Pipfile.lock

$ cat Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
myprivatelibsamplepy = {git = "https://github.com/terukizm/MyPrivateLibSamplePy.git"}

[requires]
python_version = "3.7"
```

```
$ pipenv run pip freeze | grep MyPrivateLib
MyPrivateLibSamplePy==0.1.0
```

実際にプログラムから使えることも確認しておくと良いでしょう。

```main.py
from MyPrivateLibSamplePy import MyPrivateLibSamplePy

message = MyPrivateLibSamplePy.message()
print(f"message: {message}")
```

```shell
$ pipenv run python main.py
message: This is My Private Lib.
```

# ■Docker Imageから利用

ここまで確認できたら、あとはDockerfile内で上記の手順を再現するだけです。

機密情報であるPersonal Access TokenについてはCircleCIなどで自動ビルドすることを想定し、`--build-arg`でビルド時に指定、pipenv install後には`.netrc`を削除することでイメージ内に含めないようにしています。

```Dockerfile:Dockerfile
FROM python:3.7.4

RUN pip install -U pip && pip install pipenv

WORKDIR /opt/pip
ADD Pipfile Pipfile.lock ./

# setup .netrc
ARG GITHUB_TOKEN
RUN echo "machine github.com" > /root/.netrc && \
    echo "login $GITHUB_TOKEN" >> /root/.netrc && \
    echo "passowrd x-oauth-basic" >> /root/.netrc && \
    chmod 600 /root/.netrc

RUN pipenv install --system
RUN rm Pipfile Pipfile.lock
RUN rm /root/.netrc

ADD main.py /src/main.py
CMD ["python", "/src/main.py"]
```

```bash

$ NAME=netrc-pipenv-sample
$ GITHUB_TOKEN=xxxxxxxxxxxxxxxxxxxxx

$ docker build --build-arg GITHUB_TOKEN=${GITHUB_TOKEN} -t ${NAME}:latest .
...

$ docker run -i -t ${NAME}:latest
message: This is My Private Lib.
```

こちらにサンプルを置いておきます。参考になれば幸いです。
https://github.com/terukizm/netrc-pipenv-sample

* 参考: [Docker上でGithubのプライベートリポジトリをdep ensureする方法 \- kikukawa's diary](https://kkkw.hatenablog.jp/entry/dep-ensure-github-private-on-docker)