#導入
Sphinxは拡張性が高いドキュメントツールとしてITエンジニアから愛されている。[要出典]
ただ、「reStructuredTextの書き方がわからない」「ビューワーがなく、いちいちビルドするのがめんどくさい」等否定的な意見が多いのも確かである。
今回はめんどくさがりなエンジニア、あまりコード触れない事務寄りの人達の為にローカルでの自動ビルド環境を整えた。
また、CircleCIを使ってCI/CDする。
#環境
##ディレクトリ構成
ディレクトリ構成は以下。tree -a
をベースに修正。
.
├── .circleci
│ └── config.yml
├── .gitignore
├── README.md
├── build
│ └── .gitkeep
├── containers
│ └── python
│ └── Dockerfile
├── docker-compose.yml
└── src
├── .circleci
│ └── comments.py
├── .venv
├── Makefile
├── Pipfile
├── Pipfile.lock
├── setup.cfg
└── source
├── .rstcheck.cfg
├── _static
│ └── css
│ └── my_theme.css
├── _templates
├── conf.py
└── index.rst
##Docker
あまり導入するライブラリが少ないので、直インストールでも良いとは思ったが、きっちりするためにpipenvを導入した。
pipenvとdockerコマンドの相性がどうも思わしくない、DockerfileはCMDに一つのコマンドしか設定できない、volumeの設定をしたい、との諸々の事情があり、docker-compose
を利用することにした。
FROM python:3.9-buster
ENV PIPENV_INSTALL_TIMEOUT=9000
ENV PIPENV_VENV_IN_PROJECT=1
RUN apt-get update && apt-get install -y python-pip
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip install pipenv
version: '3'
services:
python:
build:
context: .
dockerfile: ./containers/python/Dockerfile
working_dir: '/var/src/'
tty: true
volumes:
- ./src:/var/src
- ./build:/var/build
ports:
- "8000:8000"
command: >
bash -c "pipenv install --dev &&
pipenv run sphinx-autobuild --host 0.0.0.0 --port 8000 /var/src/source /var/build/html"
Dockerfileではpipとpipenvのインストールにとどめ、パッケージのインストールはdocker-compose.ymlに記述。pipenvはパッケージのインストールに時間がかかる場合が多いので、PIPENV_INSTALL_TIMEOUT=9000
を設定している。
service名をpython
としているが、この時内部でコマンドを叩きたい時にdocker-compose exec python <command>
となり、pythonを使う際はdocker-compose exec python pipenv run python 〜
となり、混乱の元なので、サービス名は区別がつく名前にすることを推奨したい。
Sphinx
###ライブラリ設定
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
flake8 = "*"
rstcheck = "*"
requests = "*"
[packages]
sphinx = "*"
sphinx-rtd-theme = "*"
sphinx-autobuild = "*"
[requires]
python_version = "3.9"
- dev-packages
- flake8
- 主にsrc/source/conf.pyのチェック
- rstcheck
- .rst形式のファイルのチェック
- requests
- bitbucketのPull RequestへのComment APIを送信するのに使う
- flake8
- packages
- sphinx
- ドキュメントを作成する根幹ライブラリ
- sphinx-rtd-theme
- Read the Docsテーマ。好み次第。
- sphinx-autobuild
- ファイルをウォッチして自動ビルドを行う
- sphinx
"""
デフォルトコメント部分を省略して表示
"""
import sphinx_rtd_theme
project = 'project_name'
copyright = 'year, author_name'
author = 'author_name'
release = 'version'
extensions = []
templates_path = ['_templates']
language = 'ja'
exclude_patterns = []
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_static_path = ['_static']
html_style = 'css/my_theme.css' # defaultの表示に不満があるので継承したmy_themeを指定
###カスタムテーマ
Sphinx の sphinx_rtd_theme をカスタマイズするを利用
@import url("theme.css");
.wy-nav-content {
max-width: none;
}
h1,h2,h3,h4,h5,h6 {
border-bottom: 1px solid #ccc;
}
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: normal;
}
colgroup {
display: none;
}
##ビルド
docker-compose up -d --build
sphinx-autobuildがドキュメントをビルドしてbuild以下にhtmlを格納する。
docker-compose.ymlにてホスト側・コンテナ側のポートを共に8000番と指定している。
ホスト側のポートを変更する場合はdocker-compose.ymlのportsを<host-port>:8000
の形式で変更する。
##文法チェック
手元で事前にチェックすることでcircleciで通らないという事がないようにする。
pre-commitにフックするのは一々実行されるのが煩わしいので、手動で各自が実行することにした。
Pipfileの[script]に書いておいてコマンド化した方が良いかも。
###flake8
Pythonファイルのチェックに用いる
[flake8]
max-line-length = 120
docker-compose exec python pipenv run flake8 source/conf.py
###rstcheck
reSTファイルのチェックに用いる
[rstcheck]
report=warning
docker-compose exec python pipenv run rstcheck -r source
##CircleCI
version: 2.1
orbs:
aws-cli: circleci/aws-cli@1.3.0
jobs:
build:
working_directory: ~/python-ci
docker:
- image: circleci/python:3.9.0-buster
environment:
PIPENV_VENV_IN_PROJECT: true
steps:
# コミットのチェックアウト
- checkout
# パッケージのインストール
- run:
name: Install python test dependencies
command: |
pip install pipenv
cd src && pipenv sync --dev
# flake8
- run:
name: run flake8
command: |
cd src && pipenv run flake8 source/conf.py
# rstcheck
- run:
name: run reST checking
command: |
cd src && pipenv run rstcheck -r source
# ドキュメントをビルドする
- run:
name: build doc
command: |
cd src && pipenv run make clean html
# artifactsにビルドしたドキュメントを保存
- store_artifacts:
path: /home/circleci/python-ci/build/html
# gitのコミットメッセージを環境変数に設定
- run:
name: get commit message
command: |
echo 'export GIT_COMMIT_DESC=`git log --pretty=format:"%s - %an" -n 1 ${CIRCLE_SHA1}`'>> $BASH_ENV
source $BASH_ENV
# artifactsに保存したドキュメントのurlをプルリクエストにコメントする
- run:
name: send comment to pull requests
command: |
cd src && pipenv run python .circleci/comments.py
- persist_to_workspace:
root: .
paths:
- build
deploy:
working_directory: ~/python-ci
executor: aws-cli/default
steps:
# build jobのworkspaceを引き継ぐ
- attach_workspace:
at: .
# orbを利用してaws-cliをセットアップする。
- aws-cli/setup
# S3のbucketにビルドしたhtmlをアップロードする
- run:
name: update html
command: aws s3 sync --exact-timestamps --delete build/html/ s3://${AWS_S3_DOCS_BUCKET_NAME}/
# CloudFrontのcacheを強制的にクリアする
- run:
name: clear cache
command: aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION_ID} --paths "/*"
workflows:
version: 2
build_and_deploy:
jobs:
# <context_name>にはCircleCIのクレデンシャル情報を入れたcontextの名前を入れる
- build:
context: <context_name>
- deploy:
context: <context_name>
requires:
- build
filters:
branches:
# masterブランチにコミットされた時のみdeployする
only:
- master
CircleCIのOrganization Settings>Contexts
にて保存できるcontextには
- aws-cliで用いるAWSクレデンシャル・リージョン情報
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
- deployするS3のバケット名
- AWS_S3_DOCS_BUCKET_NAME
- Cloudfrontのdistribution id
- DISTRIBUTION_ID
- Bitbucketのユーザ/アプリパスワード
- BITBUCKET_USER
- BITBUCKET_PASS
などを登録する。
事前にS3のバケット、CloudFrontの設定をしておき、aws-cli用クレデンシャルのIAMではS3とCloudFrontの変更権限を与える。
htmlをhttpでホストするだけならbucket名をドメインで作成、静的ウェブサイトホスティングを有効にしてRoute53で関連づけるだけでよく、IP制限もbucketの設定変更で対応できる。但し、「今どきhttpなんですか?」と社内の人間に煽られた場合は、CloudFrontとS3をつなぎ、バージニア北部リージョンのAWS Certificate Manager取得した証明書を関連付けなければいけない。証明書の取得リージョンについてはCloudFrontの設定ページに記載があるが、最初に証明書を取得してからCloudFrontに進もうと考えていると、引っかかるので注意。
##BitbucketのPull Requestにドキュメントのリンクをコメント
PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話で紹介されているように、Pull Requestにコメントするのも実装した。
紹介されているように差分を取って該当箇所を、という感じではなく、ここでは単にindexを投げているが、CircleCIのページに行ってページのリストから探すよりはまだマシかな、と思った次第。
import os
import json
import requests
"""
プロジェクト依存変数
リポジトリのUUIDは
https://api.bitbucket.org/2.0/repositories/<organization_name>/<project_name>
あるいは
https://bitbucket.org/<organization_name>/<project_name>/src/master/
でデベロッパーツールを開き、__initial_state__を検索
で取得可能
"""
VCS_TYPE = 'bb'
REPOSITORY_UUID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
PATH = '/home/circleci/python-ci/build/html/index.html'
MESSAGE = '''
[CircleCI]{}
{}
ビルドされたドキュメント
{}{}
'''
def main():
pull_requests_str = os.environ.get('CIRCLE_PULL_REQUESTS', '')
if pull_requests_str:
user_name = os.environ.get('BITBUCKET_USER')
user_pass = os.environ.get('BITBUCKET_PASS')
project_name = os.environ.get('CIRCLE_PROJECT_REPONAME')
build_number = os.environ.get('CIRCLE_BUILD_NUM')
node_index = os.environ.get('CIRCLE_NODE_INDEX')
comment = os.environ.get('GIT_COMMIT_DESC')
sha1 = os.environ.get('CIRCLE_SHA1')
url_base = f'https://{build_number}-{REPOSITORY_UUID}-{VCS_TYPE}.circle-artifacts.com/{node_index}'
pull_requests = pull_requests_str.split(',')
for pull_request in pull_requests:
pull_request_list = pull_request.split('/')
work_space = pull_request_list[-4]
pull_request_number = pull_request_list[-1]
url = f'https://api.bitbucket.org/2.0/repositories/' \
+ f'{work_space}/{project_name}/pullrequests/{pull_request_number}/comments'
print(url)
headers = {
'content-type': 'application/json',
}
message = MESSAGE.format(
comment,
sha1,
url_base,
PATH
)
payload = {
'content': {
'raw': message
}
}
r = requests.post(
url,
auth=(user_name, user_pass),
headers=headers,
data=json.dumps(payload)
)
print(f'status: {r.status_code}')
print(r.text)
else:
print('there is no pull request')
if __name__ == '__main__':
main()
pipenvの関係でsrc以下に入れている。
REPOSITORY_UUIDはBitbucketのサイト/APIから入手して書き換える。
artifactsのurlについては、CircleCI artifactsのurlをjobのスクリプト内で取得するを参考にされたし。
BitbucketのPRにコメントをつけるAPIには困った。markdownやhtmlでコメントを送る機能があるようなのだがドキュメント通りに送ってもうまく行かなかった。rawでmarkdownを送り付けたらその通りになったので、↑のMessageではmarkdownで書いてrawに入れている。
f文字列でも良さそうだが、インデントによる挙動がわからなかったので、上部に書いてformatで代入した。
#参考文献
Sphinx
Sphinx の sphinx_rtd_theme をカスタマイズする
flake8
rstcheck
PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話
Creates a new pull request comment. - Bitbucket API
How to post html comments on pull request via 2.0 api? - ATLASSIAN Comunity