Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/actions/set-env/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Set Environment
description: Set environment, shared image tag, and the deploy matrix based on trigger context
inputs:
ar_hostname:
description: Artifact Registry hostname
required: true
project_id:
description: GCP project ID
required: true
outputs:
environment:
description: Target environment (e.g. stg, prod)
value: ${{ steps.set-env.outputs.environment }}
service_suffix:
description: Suffix appended to each Cloud Run service/job name (empty for prod)
value: ${{ steps.set-env.outputs.service_suffix }}
image_tag:
description: Shared Docker image tag (one image for all cmd targets)
value: ${{ steps.set-env.outputs.image_tag }}
matrix:
description: |
JSON deploy matrix derived from cmd/*. Shape: {"include":[{"name","type"}]}.
type is "service" for *-api targets, otherwise "job".
value: ${{ steps.set-matrix.outputs.matrix }}
runs:
using: composite
steps:
- id: set-env
shell: bash
run: |
# Environment
IS_WORKFLOW_DISPATCH="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment_name != '' }}"
IS_PUSH_TO_MAIN="${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.base_ref == 'main') }}"
if [ $IS_WORKFLOW_DISPATCH = "true" ]; then
export TARGET_ENVIRONMENT="${{ github.event.inputs.environment_name }}"
elif [ $IS_PUSH_TO_MAIN = "true" ]; then
export TARGET_ENVIRONMENT="stg"
else
exit 1
fi

echo "Environment: $TARGET_ENVIRONMENT"

# Service name suffix
if [ "$TARGET_ENVIRONMENT" = "prod" ]; then
export SERVICE_NAME_SUFFIX=""
else
export SERVICE_NAME_SUFFIX="-$TARGET_ENVIRONMENT"
fi

export REPO_NAME=$(echo ${{ github.repository }} | awk -F '/' '{print $2}')
# 全 cmd ターゲットで 1 つのイメージを共有するため、タグはサービス名を含めない。
export IMAGE_TAG=${{ inputs.ar_hostname }}/${{ inputs.project_id }}/github-actions/$REPO_NAME:${{ github.sha }}

echo "Repository name: $REPO_NAME"
echo "Service suffix: $SERVICE_NAME_SUFFIX"
echo "Image tag: $IMAGE_TAG"

echo "environment=$TARGET_ENVIRONMENT" >> "$GITHUB_OUTPUT"
echo "service_suffix=$SERVICE_NAME_SUFFIX" >> "$GITHUB_OUTPUT"
echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
- id: set-matrix
shell: bash
run: |
# cmd/* を走査してデプロイ対象を自動生成する。
# 末尾が "-api" のターゲットを Cloud Run Service、それ以外を Cloud Run Job として分類する。
# サービス/ジョブが増減しても cmd/ に追従するため、ワークフロー側の編集は不要。
include=""
for dir in cmd/*/; do
name=$(basename "$dir")
[ "$name" = "*" ] && continue
case "$name" in
*-api) type="service" ;;
*) type="job" ;;
esac
include="$include{\"name\":\"$name\",\"type\":\"$type\"},"
done
if [ -z "$include" ]; then
echo "No deploy targets found under cmd/" >&2
exit 1
fi
matrix="{\"include\":[${include%,}]}"
echo "Matrix: $matrix"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
174 changes: 91 additions & 83 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,99 +23,107 @@ permissions:
contents: read
id-token: write

env:
REGION: asia-northeast1
AR_REPOSITORY: server
IMAGE_NAME: server

jobs:
deploy:
name: Deploy (${{ github.event_name == 'workflow_dispatch' && inputs.environment_name || 'stg' }})
set-env:
runs-on: ubuntu-latest
environment: ${{ github.event_name == 'workflow_dispatch' && inputs.environment_name || 'stg' }}
outputs:
environment: ${{ steps.set-env.outputs.environment }}
service_suffix: ${{ steps.set-env.outputs.service_suffix }}
image_tag: ${{ steps.set-env.outputs.image_tag }}
matrix: ${{ steps.set-env.outputs.matrix }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- id: set-env
uses: ./.github/actions/set-env
with:
ar_hostname: ${{ vars._AR_HOSTNAME }}
project_id: ${{ vars.PROJECT_ID }}

- name: Resolve environment
id: env
# 全 cmd ターゲットを 1 つのイメージに同梱しているため、ビルド/プッシュは 1 回だけ実行する。
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
IMAGE_TAG: ${{ needs.set-env.outputs.image_tag }}
needs:
- set-env
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
submodules: true
- name: Build
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
ENVIRONMENT="${{ inputs.environment_name }}"
else
ENVIRONMENT="stg"
fi
echo "name=${ENVIRONMENT}" >> "$GITHUB_OUTPUT"
# 命名規約 (infra/locals.tf env_suffix): prod は無サフィックス、他は -<env>。
if [ "$ENVIRONMENT" = "prod" ]; then
echo "suffix=" >> "$GITHUB_OUTPUT"
else
echo "suffix=-${ENVIRONMENT}" >> "$GITHUB_OUTPUT"
fi

- name: Compose image reference
id: image
docker build \
-t ${{ env.IMAGE_TAG }} \
. \
-f Dockerfile \
--no-cache
- uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}
- uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
with:
project_id: ${{ vars.PROJECT_ID }}
- name: Configure Docker
run: |
echo "ref=${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPOSITORY}/${IMAGE_NAME}:${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
gcloud auth \
configure-docker \
${{ vars._AR_HOSTNAME }}
- name: Push
run: |
docker push \
${{ env.IMAGE_TAG }}

- name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v2
# cmd/* から生成した matrix で各ターゲットを並列デプロイする。サービス/ジョブが増減しても自動追従する。
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
environment: ${{ needs.set-env.outputs.environment }}
needs:
- set-env
- build-and-push
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.set-env.outputs.matrix) }}
env:
SERVICE_NAME: ${{ matrix.name }}${{ needs.set-env.outputs.service_suffix }}
IMAGE_TAG: ${{ needs.set-env.outputs.image_tag }}
steps:
- uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}
token_format: access_token

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}

- name: Log in to Artifact Registry
uses: docker/login-action@v3
# *-api: Cloud Run Service としてデプロイ。共有イメージの起動コマンドを /bin/<name> に切り替える。
- if: matrix.type == 'service'
uses: google-github-actions/deploy-cloudrun@2028e2d7d30a78c6910e0632e48dd561b064884d # v3.0.1
with:
registry: ${{ env.REGION }}-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
service: ${{ env.SERVICE_NAME }}
image: ${{ env.IMAGE_TAG }}
region: ${{ vars._DEPLOY_REGION }}
flags: --command=/bin/${{ matrix.name }}
env_vars: |
DB_IAM_USER=${{ vars.DB_IAM_USER }}
DB_NAME=${{ vars.DB_NAME }}
INSTANCE_CONNECTION_NAME=${{ vars.INSTANCE_CONNECTION_NAME }}
env_vars_update_strategy: overwrite
secrets_update_strategy: overwrite

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push image
uses: docker/build-push-action@v6
# それ以外: Cloud Run Job として作成/更新する(実行はしない)。
- if: matrix.type == 'job'
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
with:
context: .
push: true
tags: ${{ steps.image.outputs.ref }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Deploy Cloud Run services
run: |
for svc in academic-api announcement-api user-api; do
echo "::group::update service ${svc}${SUFFIX}"
gcloud run services update "${svc}${SUFFIX}" \
--region="$REGION" \
--image="$IMAGE" \
--quiet
echo "::endgroup::"
done
env:
SUFFIX: ${{ steps.env.outputs.suffix }}
IMAGE: ${{ steps.image.outputs.ref }}

- name: Deploy Cloud Run jobs
project_id: ${{ vars.PROJECT_ID }}
- if: matrix.type == 'job'
name: Deploy Cloud Run Job
run: |
for job in \
build-class-change-notifications-job \
dispatch-notifications-job \
migrate-job; do
echo "::group::update job ${job}${SUFFIX}"
gcloud run jobs update "${job}${SUFFIX}" \
--region="$REGION" \
--image="$IMAGE" \
--quiet
echo "::endgroup::"
done
env:
SUFFIX: ${{ steps.env.outputs.suffix }}
IMAGE: ${{ steps.image.outputs.ref }}
gcloud run jobs deploy "${{ env.SERVICE_NAME }}" \
--image "${{ env.IMAGE_TAG }}" \
--region "${{ vars._DEPLOY_REGION }}" \
--command "/bin/${{ matrix.name }}" \
--set-env-vars "DB_IAM_USER=${{ vars.DB_IAM_USER }},DB_NAME=${{ vars.DB_NAME }},INSTANCE_CONNECTION_NAME=${{ vars.INSTANCE_CONNECTION_NAME }}"