今回は AWS Lambda 一式の管理を、GitHub と AWS CodeBuild を用いて実装する内容で、コード込みでダラダラと説明する回です。
ぶっちゃけ、この程度の内容はいまや AI を利用すればザックリ全体を構成したり、細かいパーツを組み上げるだけになるので、記事としての価値はだいぶ下がってきたように思いますが、まぁ趣味みたいなものなので気にせず参りましょう。
目次
目的と要点
今回の内容は、目的も実装方法も誰かに必ずしもちょうどフィットするわけではなく、ところどころで様々な選択と手法があると思いますので、1つの事例として捉えつつ各々の環境用に考察と実装をしていただければ、という感じになっています。まず目的としては、
- Lambda を Python で動かし、バージョン更新を容易にする
- Lambda コードは GitHub プライベート・リポジトリで管理する
- Lambda は ARM64 で動かす
これに対して要点としては、
- GitHub Actions で pipパッケージやライブラリのファイルを自動的にレイヤー保存する
- GitHubプライベートリポジトリは Actions を ARM64 で動かせないので Self-Hosted Runnner として CodeBuild を使う
- OSや各バージョンを統一する
Lambda を ARM64 で動かす方が安価な利点があるとはいえ、そもそもそこまで額面的には高くならない箇所ではあるので、X86 で十分だとか、パブリック・リポジトリで ARM64 を使える、というのであれば CodeBuild 不要で GitHub Hosted Runner で動かすことになります。
こういう細かい選択は好みやセンスによるところですが、私としては最初に構築してしまえば今後ずっと利点があるならそうすることが多いってのと、Self-Hosted Runner という武器をきっちり仕上げておいた方が別のシステムでも役立つだろう、という観点から選択しています。
アーキテクチャ説明
絵を書くのが面倒なので、箇条書きで失礼しやす。1) GitHub リポジトリの main にマージされる
2) GitHub Actions の処理が CodeBuild で稼働する
3) CodeBuild からメインコードとレイヤーの zip を S3 にアップロード
※レイヤー用の処理は GitHub の環境変数で ON/OFF 切り替え可能
4) Terraform で Lambda を更新して完了
これ自体は特に難しくもないのですが、自動化するにあたって選択肢がいくつかあるので、条件の整理や動作確認を丁寧にやっていきましょう、という感じで以下に続きます。
環境条件の整理
本記事の目的の1つである Lambda Python のバージョン更新について、現在は最新が python3.13 で、3.9 が 2025/12/15 に EoL となっています。だいたい3年間隔での更新を必要とすると考えておけばよいでしょう。そして OS は同ページより、3.12 以降は Amazon Linux 2023 となっているのがわかります。
次に ARM64 について、GitHub Hosted Runner はプライベート・リポジトリにおいて、Linux ARM64 に非対応であることが書かれています。基本的にインストールの成果物(今回は Lambdaレイヤー)は、CPUアーキテクチャが異なると動かないと考えておいたほうがよく、この時点で Self-Hosted Runner に選択肢が絞られます。
Self-Hosted の選択肢はいくつかありますが、ここではベターな CodeBuild とします。コンピューティングのモードには EC2 と Lambda がありますが、インストール処理を含むため /tmp にしか書き込み権限がない Lambda で無理やりなんとかしようとする理由は少ないため、EC2 を選択します。
Amazon Linux 2023 の標準パッケージにおいて、現時点では python3.12 が最新となるのでこれを Lambda でも使用することに決定します。別に普通にソースからインストールすれば python3.13 も利用可能ですが、自分の昔からのポリシーとして『可能な限り既存パッケージでやりくりする』というものがあるためです。
フォーラムなどでは 2023 に python3.13 パッケージが用意されていないことも話されていますが、私の要件的には EoL が主であって機能面に細かい要求はないので、多少 EoL まで短くとも更新を容易にできる仕組みにしておけば、たいした問題ではありません。
ローカルで動作確認
さて、作業順的にいったん本題から少々ズレますが、動かしたいコードの動作確認を手元で行っていきます。どんなシステムでもそうですが、システムの挙動や確立までを確認するのは簡易的な場所で行うべきで、クラウド上など更新に手間と時間のかかる場所を主軸にしたり、毎回全体を通すような確認をするのはナンセンスです。動作内容を確定・確信すべきパーツを判断し、そこは手早く仕上げ、それ以外は実際の環境上で解決したり調整することで、全体の時間を短縮しつつ品質精度も向上できるからです。
Python と pip
まずは手元で Docker を起動します。
1 2 3 |
docker run -it -d amazonlinux:2023 docker ps docker exec -it ${CONTAINER_ID} bash |
作業に必要なコマンドを適当に入れつつ、Python3.12 と pip で必要なパッケージを入れたのちに、pip で必要パッケージを作業場に入れます。
1 2 3 4 5 6 7 8 9 |
dnf install procps less dnf install \ python3.12 python3.12-devel python3.12-pip python3.12-setuptools \ mariadb-connector-c-devel gcc cd /tmp pip3.12 install \ boto3 mysqlclient redis \ -t python/ |
今回は最新の boto3 と、Aurora や Valkey への接続用に mysqlclient , redis を入れました。mysqlclient のインストールにはライブラリファイルが必要なので、先に mariadb-connector-c-devel を入れています。
MySQL
インストール物は Python バージョンや CPUアーキテクチャが関与する場合があり、mysqlclient がその1つです。出来上がるファイル名にそれらの情報が含まれているのがわかります。- python/MySQLdb/_mysql.cpython-312-x86_64-linux-gnu.so
- 環境が合わないところで import MySQLdb すると ModuleNotFoundError: No module named ‘MySQLdb’
また、ライブラリファイルはこの辺に配置されており、これは pip インストール時だけでなく使用時も必要なので、Lambdaレイヤーに含めて保存することになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ repoquery --list mariadb-connector-c-devel | grep libmysql Last metadata expiration check: 0:17:31 ago on Tue Jun 3 06:55:15 2025. /usr/lib64/libmysqlclient.so /usr/lib64/libmysqlclient_r.so /usr/lib64/libmysqlclient.so /usr/lib64/libmysqlclient_r.so $ ls -l /usr/lib64/libmysqlclient* /usr/lib64/libmariadb.so* lrwxrwxrwx 1 root root 15 Mar 6 00:19 /usr/lib64/libmariadb.so -> libmariadb.so.3 -rwxr-xr-x 1 root root 376336 Mar 6 00:19 /usr/lib64/libmariadb.so.3 lrwxrwxrwx 1 root root 15 Mar 6 00:19 /usr/lib64/libmysqlclient.so -> libmariadb.so.3 lrwxrwxrwx 1 root root 15 Mar 6 00:19 /usr/lib64/libmysqlclient_r.so -> libmariadb.so.3 |
これがないと、以下のようなエラーになります。
1 2 3 4 5 6 7 8 |
$ dnf remove mariadb-connector-c-devel $ PYTHONPATH=./python/ python3.12 -c "import MySQLdb; conn = MySQLdb.connect();" Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/python/MySQLdb/__init__.py", line 17, in <module> from . import _mysql ImportError: libmariadb.so.3: cannot open shared object file: No such file or directory |
Lambda 上でこのファイルを保持しつつ動作することを確認するために、ライブラリファイルの実体・シンボリックリンクをそのままコピーしつつ、元となる mariadb-connector-c-devel を削除して接続しようとするところまでを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# cp に -d をつけることで全部が実体にならずリンクはリンクのままになる mkdir -p ./lib cp -d /usr/lib64/libmariadb.so* lib/ cp -d /usr/lib64/libmysqlclient* lib/ dnf remove mariadb-connector-c-devel $ LD_LIBRARY_PATH=./lib/ PYTHONPATH=./python/ python3.12 -c "import MySQLdb; conn = MySQLdb.connect();" Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/python/MySQLdb/__init__.py", line 121, in Connect return Connection(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/tmp/python/MySQLdb/connections.py", line 200, in __init__ super().__init__(*args, **kwargs2) MySQLdb.OperationalError: (2002, "Can't connect to local server through socket '/var/lib/mysql/mysql.sock' (2)") |
ここまででも十分ですが、本当に接続できるか確認する場合は、MySQLサーバーをおざなりに入れつつクエリを流してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
dnf install mariadb1011-server sudo sudo -u mysql mysql_install_db sudo -u mysql mysqld & LD_LIBRARY_PATH=./lib/ PYTHONPATH=./python/ \ python3.12 -c \ "import MySQLdb; import json; \ conn = MySQLdb.connect(); cur = conn.cursor(MySQLdb.cursors.DictCursor); \ query = 'SHOW DATABASES'; cur.execute(query); print(json.dumps(cur.fetchall(), indent=2))" [ { "Database": "information_schema" }, { "Database": "mysql" }, { "Database": "performance_schema" }, { "Database": "sys" }, { "Database": "test" } ] |
redis
redis はシンプルなので、こちらもサクッとサーバーを入れて動作確認のコードを実行してみるだけです。まずはサーバーを入れて、
1 2 3 4 5 6 7 |
dnf install redis6 redis6-server & $ redis6-cli set KEY VALUE OK $ redis6-cli get KEY "VALUE" |
Python を実行してみます。
1 2 3 4 5 6 7 8 9 10 |
LD_LIBRARY_PATH=./lib/ PYTHONPATH=./python/ \ python3.12 -c \ "import redis; import json; \ r = redis.Redis(); all = r.info('all'); \ info = {'max_memory': all['maxmemory'], 'used_memory_rss': all['used_memory_rss']}; \ print(json.dumps(info , indent=2))" { "max_memory": 0, "used_memory_rss": 9224192 } |
たいていの pip パッケージは、入れておけばそうコードに影響することなく動くでしょ、って感じにはなりますが、重要であったり繊細なモノは確認をしたり、pip インストール時にバージョンを指定して固定せず常に最新になる場合は動作確認した方が良い場合もあると思います。
そういう時に、サッと手元で確認する手段と、確認すべき範囲の判断ができると、より想定外の挙動に悩まされて引っかかるようなことがなくなるので、オススメしたい進行方法の1つです。
Terraform で GitHub 接続と CodeBuild を作成
ここからは実際のリソース作成で、AWS から GitHub への接続と、Self-Hosted Runner として処理を担当する CodeBuild もろもろを作成します。CodeConnections
Codeシリーズの管理画面にて、サイドバーの “設定” → “接続” によって、GitHub との接続を作成します。
1 2 3 4 |
resource "aws_codeconnections_connection" "github" { name = "GitHub" provider_type = "GitHub" } |
これだけだと、まだ接続は利用できないので、対象となるリポジトリの管理者である GitHub ユーザーでログインした状態にし、AWS管理画面で接続を承認する作業を行います。ステータスが「利用可能」になれば準備 OK です。
GitHub の方では、Settings → GitHub Apps に「AWS Connector for GitHub」があるので、そこで許可状況について確認することができます。
IAM Role
CodeBuild に割り当てる Role を作成します。CodeConnections は GetConnection, GetConnectionToken あたりを必要としますが、ここでは * にしておきます。あとは S3 に zip を保存する権限と、Logs でもあれば十分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
resource "aws_iam_role" "github_codebuild" { name = "github_codebuild" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "codebuild.amazonaws.com" } Action = "sts:AssumeRole" }] }) } resource "aws_iam_policy_attachment" "github_codebuild" { name = "github_codebuild" roles = [aws_iam_role.github_codebuild.name] policy_arn = "arn:aws:iam::aws:policy/AWSCodeBuildDeveloperAccess" } resource "aws_iam_role_policy" "github_codebuild" { name = "github_codebuild_custom" role = aws_iam_role.github_codebuild.id policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = [ "s3:Get*", "s3:List*", "s3:PutObject", ] Resource = [ "arn:aws:s3:::example-lambda-code/*", "arn:aws:s3:::example-lambda-code", ] }, { Effect = "Allow" Action = [ "codeconnections:*", ] Resource = [ "*", ] }, { Effect = "Allow" Action = [ "logs:CreateLogStream", "logs:PutLogEvents", ] Resource = [ "*", ] }] }) } |
CodeBuild
GitHub Actions を実行するコンピューティングとなる CodeBuild 一式を作成します。GitHub の workflow ファイルの処理がこちらで実行されることになりますが、workflow の設定で buildspec-override:true にすることで、buildspec.yml を処理内容とすることもできるので、なんか良い感じの事情があったら切り替えてもよいでしょう。
あと当然の違いとして、GitHub Hosted Runner から AWS リソースを操作するには、今なら OIDC(OpenID Connect)を使って認証するのが基本ですが、CodeBuild は AWS リソースなので操作権限は IAM Role に委ねられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
resource "aws_codebuild_project" "github_codebuild" { name = "github_codebuild" service_role = aws_iam_role.github_codebuild.arn build_timeout = 300 queued_timeout = 60 badge_enabled = false artifacts { type = "NO_ARTIFACTS" } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/amazonlinux-aarch64-standard:3.0" type = "ARM_CONTAINER" image_pull_credentials_type = "CODEBUILD" privileged_mode = false } source { type = "GITHUB" location = "https://github.com/example_org/example_repo.git" insecure_ssl = false git_clone_depth = 1 } logs_config { cloudwatch_logs { status = "ENABLED" group_name = aws_cloudwatch_log_group.github_codebuild.name } s3_logs { status = "DISABLED" } } } resource "aws_codebuild_source_credential" "github_codebuild" { auth_type = "CODECONNECTIONS" server_type = "GITHUB" token = aws_codeconnections_connection.github.arn } resource "aws_codebuild_webhook" "github_codebuild" { project_name = aws_codebuild_project.github_codebuild.name build_type = "BUILD_BATCH" filter_group { filter { type = "EVENT" pattern = "WORKFLOW_JOB_QUEUED" } } } resource "aws_cloudwatch_log_group" "github_codebuild" { name = "/codebuild/github_codebuild" retention_in_days = 30 } |
作成すると、GitHub から AWS に対して Webhook が実行されて、処理内容や結果のやり取りが行われることになります。
GitHub 管理画面の Settings → Webhooks にそれが表示され、その中の “Recent Deliveries” にはリクエストやレスポンスの履歴がキッチリ残っているので、もし上手く動作しない場合はログを頼りに調整していきます。
GitHub に Lambdaコードと Actions を作成
Lambda コード用のリポジトリはもう存在するとして、適当な内容のプログラムファイルと、Actions 用の .github/workflows/build_lambda.yml (ファイル名は任意)を作成します。
Lambda コード
これは人それぞれの部分なので省略します。動作確認用としては test.py でも作って、レイヤーの import や必要なら Aurora への接続などを書けばいいと思います。Actions ファイル
分割すると見づらいかもなので、まとめて記載しつつコメントを中に書いていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
name: 'build_lambda' on: ## For test ## # 毎回マージさせると面倒な時は、コメントアウトを外してブランチへの push で発動させます #push: # branches-ignore: # - main ## Start build ## # main にマージされたら発動します pull_request: branches: - main types: - closed # これは設定する vars のメモです # vars # PYTHON_VERSION: 3.12 # BUILD_LAYER: "on" | "off" # NOTIFICATION: "on" | "off" # これは設定する secret のメモです #secret: # NOTIFICATION_API_KEY # NOTIFICATION_GROUP_ID # 固定値な環境変数や、動的に作る環境変数です env: UPLOAD_REGION: ap-northeast-1 UPLOAD_BUCKET: example-bucket-name UPLOAD_PREFIX: /example-prefix UPLOAD_VERSION: python${{ vars.PYTHON_VERSION }} MAIN_FILENAME: main.zip LAYER_FILENAME: layer.zip WORK_DIR: /tmp jobs: main: # GitHub Hosted の場合は ubuntu-latest など書きますが、CodeBuild の場合は命名規則に従います # - codebuild-${GitHubリポジトリ名}-${{ github.run_id }}-${{ github.run_attempt }} # buildspec-override はデフォルトが false で、buildspec.yml を使う場合は true にします runs-on: - codebuild-example_repository_name-${{ github.run_id }}-${{ github.run_attempt }} - buildspec-override:false # ここから処理内容です steps: # 動的に環境変数を作っておきます - name: environment id: environment run: | echo "Set dynamic env." S3_PREFIX="s3://${{ env.UPLOAD_BUCKET }}${{ env.UPLOAD_PREFIX }}/${{ env.UPLOAD_VERSION }}/" echo "UPLOAD_MAIN_DIR=${S3_PREFIX}" >> $GITHUB_ENV echo "UPLOAD_LAYER_DIR=${S3_PREFIX}${RUNNER_ARCH,,}/" >> $GITHUB_ENV # GitHub からソースを取得します - name: checkout uses: actions/checkout@v4 # 出しておくと便利な情報を適当に出力しておきます - name: info id: info run: | echo "Start information." echo "vars.BUILD_LAYER = ${{ vars.BUILD_LAYER }}" echo "pwd = $(pwd)" echo "$ ls -al" ls -al # 取得したコードをそのまま圧縮して S3 に保存します - name: main id: main run: | echo "Start building lambda codes." zip -r ${{ env.WORK_DIR }}/${{ env.MAIN_FILENAME }} ./* > /dev/null cd ${{ env.WORK_DIR }} aws s3 cp ./${{ env.MAIN_FILENAME }} ${{ env.UPLOAD_MAIN_DIR }} continue-on-error: true # インストール物を更新する場合、環境変数を on にしておくと実行されます # 動作確認した通りの内容で、dnf と pip のインストールをし、圧縮して S3 に保存します - name: layer if: vars.BUILD_LAYER == 'on' id: layer run: | echo "Start install layer." echo "== DNF ==" dnf -y install \ gcc \ python${{ vars.PYTHON_VERSION }}-devel python${{ vars.PYTHON_VERSION }}-pip \ python${{ vars.PYTHON_VERSION }}-setuptools \ mariadb-connector-c-devel echo "== PIP ==" cd ${{ env.WORK_DIR }} pip${{ vars.PYTHON_VERSION }} install \ boto3 mysqlclient redis \ -t python/ mkdir -p lib cp -d /usr/lib64/libmariadb.so* lib/ cp -d /usr/lib64/libmysqlclient* lib/ echo "== Compress & Save ==" zip -ry ./${{ env.LAYER_FILENAME }} python/ lib/ > /dev/null aws s3 cp ./${{ env.LAYER_FILENAME }} ${{ env.UPLOAD_LAYER_DIR }} continue-on-error: true # 通知のメッセージを作成します。いろんな情報が用意されているのでカスタマイズしてください - name: Notification Message if: vars.NOTIFICATION == 'on' id: message run: | MAIN_RESULT=${{ steps.main.outcome }} LAYER_RESULT="unexecuted" [ ${{ vars.BUILD_LAYER }} = 'on' ] && LAYER_RESULT=${{ steps.layer.outcome }} { echo "MESSAGE<<TXT" echo "Deploy Lambda (${GITHUB_REPOSITORY} : ${GITHUB_REF_NAME}) by ${GITHUB_ACTOR}" echo "Main Compress: ${MAIN_RESULT}" echo "Layer Install: ${LAYER_RESULT}" echo "Comment: ${{ github.event.pull_request.title }}" echo "Request: ${{ github.event.pull_request.html_url }}" echo "Actions: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" echo "TXT" } >> $GITHUB_OUTPUT # 完了の通知を送る API を叩いたりします - name: Notification Send if: vars.NOTIFICATION == 'on' run: | URL="https://notification.example.com/v1/group/${{ secrets.NOTIFICATION_GROUP_ID }}/send" HEADER="X-ExampleHeader: ${{ secrets.NOTIFICATION_API_KEY }}" echo "${{ steps.message.outputs.MESSAGE }}" curl -s -X POST -H "$HEADER" --data-urlencode "body=${{ steps.message.outputs.MESSAGE }}" "$URL" > /dev/null |
環境変数
先に gh コマンドをインストールしておきます。管理画面でも登録できますが、コマンドでちゃちゃっとやっちゃいましょう。
1 2 3 4 5 6 7 8 9 |
gh variable set PYTHON_VERSION --body "3.12" gh variable set BUILD_LAYER --body "on" gh variable set NOTIFICATION --body "on" gh secret set NOTIFICATION_API_KEY --body "***" gh secret set NOTIFICATION_GROUP_ID --body "***" gh variable list gh secret list |
動作確認
これで必要なものは揃いましたが、実際にはチマチマ確認しながら進めたほうがわかりやすいし確実です。CodeBuild の起動
AWS 側で CodeConnections + CodeBuild 一式 があって、GitHub Actions での runs-on の指定が正しければ、webhook が叩かれて CodeBuild が起動するはずです。最初は Actions の step は簡素なものにして、push や merge などの条件を満たした時に webhook が叩かれるログと、AWS 側でも CodeBuild が起動していることを確認するのがよいです。これらの処理のログは GitHub の webhook にちゃんと残り、CodeBuild 側にはあまりヒントは残らないと思われます。
また、ついでに step の仮処理において、printenv などを出力して確認しておくと、完了通知メッセージあたりで使えそうな CodeBuild としての環境変数が見つかるかもなので、見ておくとよいです。
zip 保存
Lambda のメインコードの zip 化は、処理としてはミスりようがないので、実行後は保存された S3 を確認します。レイヤーの zip は環境変数を on にしていれば作成され、S3 に保存されています。今回の場合の保存先は
- s3://${{ env.UPLOAD_BUCKET }}${{ env.UPLOAD_PREFIX }}/python3.12/main.zip
- s3://${{ env.UPLOAD_BUCKET }}${{ env.UPLOAD_PREFIX }}/python3.12/arm64/layer.zip
メインコードは CPU アーキテクチャに関係なく、レイヤーは関係あるのでこうしました。もしメインコードも内容の更新ごとに分けたかったりしたら、パスのルールを変えればいいだけです。
Lambda 更新
あとは、Terraform なりで管理されているだろう Lambda を更新して実際に動かしてみて完了です。aws_lambda_layer_version と aws_lambda_function の、s3_bucket, s3_key, runtime あたりを更新することで、作成済みの任意の zip に切り替えます。
切り替えると言っても、通常は CPU アーキテクチャはそう変えないので、例えば Amazon Linux 2023 に Python3.13 パッケージができたら、環境変数 PYTHON_VERSION を更新して Actions を実行し、Lambda の zip 指定も変更する。くらいの運用を想定しています。
おわりに
今回の仕組みは、全体の自動化と、環境変数による条件の変更を主な目的としました。ほぼ必要ないとはいえ、CPUアーキテクチャの指定も気軽にできるようにしたかったのですが、CodeBuild 実行時の上書き設定には CPU アーキテクチャは含まれないので、変更したい場合は Terraform をちょっと書き換えて CodeBuild を作り直す必要があります。
CodeCommit が使えた頃は、外からミラーリングして CodePipeline を発動して── のようなこともしていましたが、亡くなってしまったので GitHub と直にこういうことをデキるようにしておいた方が便利ですよねってのと、
環境条件を丁寧に整理すること、動作確認を丁寧に手早くすること、あたりをキッチリできるようにしていきたいですねって感じの回でした:-)