
GitHub Actions/semantic-release を使ったGitHubタグとAmazon ECRイメージタグを連携するセマンティックバージョニング術
はじめに
コンテナサービスでイメージタグをどのように管理・運用すべきかは悩ましいです。
たとえば、以下のようなアプローチが考えられます。
- 割り切って latest 運用
- 一意なハッシュ値(Gitのコミットハッシュ等を用いる)
- バージョン採番
本記事では、セマンティック・コミット・メッセージを採用し、リリースのたびにコミットメッセージを元に MAJOR.MINOR.PATCH というセマンティックバージョンを採番し、Git(GitHub)とコンテナイメージ(Amazon ECR)で同じバージョンタグを割り振る方法を紹介します。
セマンティックバージョニングには semantic-release を用い、このプログラムをラップした GitHub Tag Action のワークフロー実行でバージョンをバンプし、このバージョン情報をタグとしてGit(GitHub)とコンテナイメージ(Amazon ECR)に付与します。
GitHub Workflowサンプル
GitHub Workflowsで手動デプロイを実行し、ECRのイメージ・タグを更新し、ECSのタスク定義も更新するサンプルが以下です。
name: bump version and publish
on:
workflow_dispatch
env:
AWS_REGION: ap-northeast-1
ECR_REPOSITORY: your-ecr-repository-name
TFSTATE_BUCKET: your-s3-bucket-name
TFSTATE_BUCKET_PATH: your-app/prod
ROLE_ARN: arn:aws:iam::123456789012:role/GitHubOIDCActions
permissions:
contents: write
id-token: write
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
role-session-name: gh-oidc-${{ github.run_id }}-${{ github.run_attempt}}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Bump version and push tag
id: tag-version
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Create a GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag-version.outputs.new_tag }}
name: Release ${{ steps.tag-version.outputs.new_tag }}
body: ${{ steps.tag-version.outputs.changelog }}
- name: Build, tag, and push image to Amazon ECR
id: build-image
uses: docker/build-push-action@v6
with:
tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag-version.outputs.new_tag }}
context: .
push: true
- uses: kayac/ecspresso@v2
with:
version: v2.4.5
- name: ecspresso validate
id: task-validate
env:
IMAGE_TAG: ${{ steps.tag-version.outputs.new_tag }}
run: |
ecspresso verify
- name: ecspresso deploy
id: task-deploy
env:
IMAGE_TAG: ${{ steps.tag-version.outputs.new_tag }}
run: |
ecspresso deploy
上記の例では、Terraformで管理されたインフラに対して ecspresso で ECSのサービス・タスク定義を更新しています。
JSON のタスク定義ファイルで環境変数としてイメージタグを受け取る({{ must_env IMAGE_TAG
}})ようにしておくと、GitHub Actionsとの連携がシームレスになります。
{
"containerDefinitions": [
{
"cpu": 128,
"image": "{{ tfstate `module.frontend-ecr.aws_ecr_repository.myecr.repository_url` }}:{{ must_env `IMAGE_TAG` }}",
...
セマンティックバージョニングについて
セマンティックバージョニング(SemVer)とは、ソフトウェアのバージョン番号を MAJOR.MINOR.PATCH の形式で管理する概念です。
- MAJOR: 破壊的変更があった場合(例: 1.x.x → 2.0.0)
- MINOR: 機能追加(例: 1.2.x → 1.3.0)
- PATCH: バグ修正等、小さな修正があった場合(例: 1.2.3 → 1.2.4)
詳細は次のサイトをご確認ください。
セマンティックバージョニングを容易にする semantic-release について
手動で運用すると負荷の高いセマンティックバージョニングを軽減するのが semantic-release です。
MAJOR/MINOR/PATCH に対応する コミットメッセージのフォーマット定義し、前回のリリースからのコミットログからバージョンを上げます。
より具体的には、コミットメッセージのフォーマットは Angular Commit Message Conventions に従っており、以下の通りです。
リリースタイプ | Commit message |
---|---|
Patch | fix: stop graphite breaking when too much pressure applied |
Minor | feat: add 'graphiteWidth' option |
Major | perf: remove graphiteWidth option BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite width of 10mm is always used for performance reasons. |
スコープを付与して fix*(pencil)**: stop graphite breaking when too much pressure applied* というようにすることもできます。
また、ルールに従わないコミットメッセージの場合、デフォルトでは Patch バージョンが上がります。後述のGitHub Actions版でもデフォルトの振る舞いを変更できます。
コミットのバンプの例
具体的にコミット履歴とタグ(バージョン)がどのように上がっていくか確認します。
$ git log --oneline
f0b4dc1 (HEAD -> main, tag: v3.0.0, origin/main, origin/HEAD) perf: test
cad45c2 feat: test
c839cd2 fix: test
088f872 (tag: v2.2.0) fix: Update README.md
862e0be feat: Update release.yml
24c84b8 (tag: v2.1.5) fix: Update README.md
a4e227a (tag: v2.1.4) fix: Update README.md
- a4e227a (tag: v2.1.4)をベースラインに、
fix
の修正のみがあると、パッチバージョンだけが上がります(2.1.4 -> 2.1.5)。 - 24c84b8 (tag: v2.1.5)をベースラインに、
fix
とfeat
があると、より上位のマイナーバージョンに繰り上がります(2.1.5 -> 2.2.0)。 - 088f872 (tag: v2.2.0) をベースラインに、
fix
とfeat
とmajor
があると、、最上位のメジャーバージョンが繰り上がります(2.minor.patch -> 3.0.0)
semantic-release を GitHub Actions から呼び出す
この semantic-release を GitHub Actions向けにラップしたのが mathieudutour/github-tag-action です。
上記サンプルでは、 mathieudutour/github-tag-action 経由で semantic-release も間接的に呼び出しています。
使い方は簡単で、GitHubリポジトリへのアクセス権限が必要なため github_token
を指定し、以下のように呼び出すだけです。
- name: Bump version and push tag
id: tag-version
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
後続のステップでは、採番されたバージョン(タグ)を ${{ steps.tag-version.outputs.new_tag }}
で参照できます。
例えば、Amazon ECR 向けのビルド&プッシュは以下の通りです
- name: Build, tag, and push image to Amazon ECR
id: build-image
uses: docker/build-push-action@v6
with:
tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag-version.outputs.new_tag }}
context: .
push: true
GitHub Release と連携
ncipollo/release-action アクションを用いると、GitHub Release と連動できます。
- name: Create a GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag-version.outputs.new_tag }}
name: Release ${{ steps.tag-version.outputs.new_tag }}
body: ${{ steps.tag-version.outputs.changelog }}
特に、規則通りにコミットログを残すと、リリースノートもきれいになります。
モノレポの場合
一つのレポジトリ内に複数のプロジェクトが管理されている場合、プロジェクトごとにバージニングしたいケースもあるかもしれません。
そのような場合は、次のモノレポ版の semantic-release-monorepo をご検討下さい。
最後に
GitHub Tag Action を用いてセマンティックバージョンを採番し、Gitのタグとコンテナイメージ(Amazon ECR)のイメージタグを連動させる方法を紹介しました。
コンテナイメージの latest 運用を避けたい、一方で、Gitのコミットハッシュ値は味気ないとお思いのチームの一案になれば幸いです。