Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
48c321f
docs(spec): design the terragrunt production release gate
panicboat Oct 24, 2026
71a01ac
docs(spec): hold the apply trigger in workflow-config
panicboat Oct 24, 2026
f81dfba
docs(plan): plan the terragrunt production release gate
panicboat Oct 24, 2026
b70b82e
ci: validate apply_trigger in workflow-config
panicboat Oct 24, 2026
0954d9e
ci: apply only merge-triggered environments on push to main
panicboat Oct 24, 2026
acaa236
ci: detect changes to files shared across terragrunt environments
panicboat Oct 24, 2026
9ab7031
ci: resolve release targets from workflow-config
panicboat Oct 24, 2026
6febc70
ci: release each terragrunt service with its own release-please config
panicboat Oct 24, 2026
a8bb8f8
ci: plan release environments on release pull requests
panicboat Oct 24, 2026
4f196f0
ci: apply release environments when a service release is published
panicboat Oct 24, 2026
c7d0269
ci: pin the merge-only apply gate in tests and serialize release runs
panicboat Oct 24, 2026
8172949
Merge branch 'main' into ci/production-release-pr
panicboat Oct 24, 2026
3a8ef4c
docs: plan excluding merge-only environment changes from service rele…
panicboat Oct 24, 2026
4b31097
ci: keep merge-only environment changes out of service releases
panicboat Oct 24, 2026
403fa3e
Merge remote-tracking branch 'origin/ci/production-release-pr' into c…
panicboat Oct 24, 2026
7d10204
docs: re-run release applies on the release tag ref
panicboat Oct 24, 2026
9a6bdca
docs: plan excluding production from platform terragrunt runs
panicboat Oct 24, 2026
127acd2
ci: exclude production from platform terragrunt runs
panicboat Oct 24, 2026
7a91333
docs: describe which runs skip excluded environments
panicboat Oct 24, 2026
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
13 changes: 12 additions & 1 deletion .github/workflows/auto-label--deploy-trigger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ jobs:
targets: ${{ steps.resolver.outputs.targets }}
has-targets: ${{ steps.resolver.outputs.has-targets }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Validate apply_trigger
run: bash scripts/terragrunt-release/validate-apply-trigger.sh

- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
Expand Down Expand Up @@ -98,12 +104,15 @@ jobs:
strategy:
matrix:
target: ${{ fromJson(needs.deploy-trigger.outputs.targets) }}
exclude:
- target:
stack: terragrunt-shared
fail-fast: false
uses: ./.github/workflows/reusable--terragrunt-executor.yaml
with:
service-name: ${{ matrix.target.service }}
environment: ${{ matrix.target.environment }}
action-type: ${{ matrix.target.stack == 'terragrunt' && (github.event_name == 'pull_request' && 'plan' || 'apply') || '' }}
action-type: ${{ matrix.target.stack == 'terragrunt' && (github.event_name == 'pull_request' && 'plan' || (matrix.target.apply_trigger == 'merge' && 'apply' || '')) || '' }}
iam-role: ${{ github.event_name == 'pull_request' && matrix.target.iam_role_plan || matrix.target.iam_role_apply }}
aws-region: ${{ matrix.target.aws_region }}
working-directory: ${{ matrix.target.working_directory }}
Expand All @@ -123,6 +132,8 @@ jobs:
exclude:
- target:
stack: terragrunt
- target:
stack: terragrunt-shared
fail-fast: false
uses: ./.github/workflows/reusable--kubernetes-builder.yaml
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/auto-label--label-dispatcher.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ permissions:
jobs:
dispatch-labels:
name: 'Dispatch Labels'
if: ${{ !startsWith(github.head_ref, 'release-please--') }}
runs-on: ubuntu-latest
steps:
- name: Generate GitHub App token
Expand Down
57 changes: 57 additions & 0 deletions .github/workflows/auto-release--plan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: 'Auto Release - Plan'

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
id-token: write
contents: read
pull-requests: write

concurrency:
group: auto-release-plan-${{ github.head_ref }}
cancel-in-progress: true

jobs:
resolve:
name: 'Resolve Release Targets'
if: startsWith(github.head_ref, 'release-please--branches--main--components--')
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
targets: ${{ steps.resolve.outputs.targets }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Resolve release targets
id: resolve
env:
HEAD_REF: ${{ github.head_ref }}
run: |
set -euo pipefail
service=$(bash scripts/terragrunt-release/release-service.sh --branch "$HEAD_REF")
targets=$(bash scripts/terragrunt-release/release-targets.sh "$service")
echo "targets=$targets" >> "$GITHUB_OUTPUT"

plan:
name: 'Plan Release (${{ matrix.target.service }}:${{ matrix.target.environment }})'
needs: resolve
if: needs.resolve.outputs.targets != '[]'
strategy:
matrix:
target: ${{ fromJson(needs.resolve.outputs.targets) }}
fail-fast: false
uses: ./.github/workflows/reusable--terragrunt-executor.yaml
with:
service-name: ${{ matrix.target.service }}
environment: ${{ matrix.target.environment }}
action-type: plan
iam-role: ${{ matrix.target.iam_role_plan }}
aws-region: ${{ matrix.target.aws_region }}
working-directory: ${{ matrix.target.working_directory }}
app-id: ${{ vars.APP_ID }}
secrets:
private-key: ${{ secrets.APP_PRIVATE_KEY }}
71 changes: 71 additions & 0 deletions .github/workflows/auto-release--trigger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: 'Auto Release - Trigger'

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to (re-)apply (e.g. vpc-v0.2.0)'
required: true
type: string

permissions:
id-token: write
contents: read
pull-requests: write

concurrency:
group: auto-release-trigger-${{ github.event.release.tag_name || inputs.tag }}
cancel-in-progress: false

jobs:
resolve:
name: 'Resolve Release Targets'
runs-on: ubuntu-latest
permissions:
contents: read
env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
outputs:
targets: ${{ steps.resolve.outputs.targets }}
tag: ${{ steps.resolve.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ env.TAG }}

- name: Resolve release targets
id: resolve
run: |
set -euo pipefail
service=$(bash scripts/terragrunt-release/release-service.sh --tag "$TAG")
targets=$(bash scripts/terragrunt-release/release-targets.sh "$service")
echo "targets=$targets" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

apply:
name: 'Apply Release (${{ matrix.target.service }}:${{ matrix.target.environment }})'
needs: resolve
if: needs.resolve.outputs.targets != '[]'
concurrency:
group: terragrunt-apply-${{ matrix.target.working_directory }}
cancel-in-progress: false
strategy:
matrix:
target: ${{ fromJson(needs.resolve.outputs.targets) }}
fail-fast: false
uses: ./.github/workflows/reusable--terragrunt-executor.yaml
with:
service-name: ${{ matrix.target.service }}
environment: ${{ matrix.target.environment }}
action-type: apply
iam-role: ${{ matrix.target.iam_role_apply }}
aws-region: ${{ matrix.target.aws_region }}
working-directory: ${{ matrix.target.working_directory }}
app-id: ${{ vars.APP_ID }}
ref: ${{ needs.resolve.outputs.tag }}
github-environment: ${{ matrix.target.environment }}
secrets:
private-key: ${{ secrets.APP_PRIVATE_KEY }}
29 changes: 27 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,38 @@ name: Release
on:
push:
branches: [main]
workflow_dispatch: {}

permissions:
contents: read

concurrency:
group: release-please
cancel-in-progress: false

jobs:
discover:
runs-on: ubuntu-latest
outputs:
paths: ${{ steps.find.outputs.paths }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Find release-please configs
id: find
run: |
set -euo pipefail
paths=$(git ls-files '*/release-please-config.json' | jq -R 'rtrimstr("/release-please-config.json")' | jq -sc .)
echo "paths=$paths" >> "$GITHUB_OUTPUT"

release-please:
needs: discover
if: needs.discover.outputs.paths != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
path: ${{ fromJson(needs.discover.outputs.paths) }}
steps:
- name: Generate GitHub App token
id: app-token
Expand All @@ -19,7 +44,7 @@ jobs:
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5
id: release
with:
token: ${{ steps.app-token.outputs.token }}
release-type: simple
config-file: ${{ matrix.path }}/release-please-config.json
manifest-file: ${{ matrix.path }}/release-please-manifest.json
13 changes: 13 additions & 0 deletions .github/workflows/reusable--terragrunt-executor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ on:
required: true
type: string
description: 'GitHub App ID for authentication'
ref:
required: false
type: string
default: ''
description: 'Git ref to check out (defaults to the triggering ref)'
github-environment:
required: false
type: string
default: ''
description: 'GitHub environment the job runs in (none when empty)'
secrets:
private-key:
required: true
Expand All @@ -41,6 +51,7 @@ jobs:
name: 'Execute Terragrunt'
if: inputs.action-type != ''
runs-on: ubuntu-latest
environment: ${{ inputs.github-environment }}
steps:
- name: Generate GitHub App token
id: app-token
Expand All @@ -52,6 +63,8 @@ jobs:

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref }}

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/test-terragrunt-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Test Terragrunt Release

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
name: Test terragrunt release scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: bash scripts/terragrunt-release/test.sh
- run: bash scripts/terragrunt-release/validate-apply-trigger.sh
- run: bash scripts/terragrunt-release/check-release-configs.sh
18 changes: 17 additions & 1 deletion README-ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@

- `auto-label--deploy-trigger.yaml` が PR ラベル / `main` への push で起動する。
- `panicboat/deploy-actions/label-resolver` が `workflow-config.yaml` を参照して各ラベルを 1 つ以上の `{service}/{environment}` ターゲットに展開する。
- `apply_trigger: merge` の Terragrunt environment は `main` への push で apply される。
- `apply_trigger: release` の Terragrunt environment は `aws/{service}` の release 公開時に `auto-release--trigger.yaml` が apply する。
- release PR では `auto-release--plan.yaml` が `terragrunt plan` を実行する。plan を出し直すときは該当する `auto-release--plan` の run で「Re-run all jobs」を使う。
- release の apply を再実行するときは、`auto-release--trigger` を release tag の ref で `workflow_dispatch` 起動し、同じ tag を渡す:`gh workflow run auto-release--trigger.yaml --ref {service}-vX.Y.Z -f tag={service}-vX.Y.Z`。
- `workflow-config.yaml` の `terragrunt` stack の `exclude` の rule に一致する `{service}/{environment}` の組は、`label-resolver` の target(PR の plan・`main` への push の apply)にならず、release の plan / apply も行われない。
- rule のキー(`service`、`environment`)がすべて組の値と一致したとき、その組に一致する。`environment` だけの rule はすべての service に効く。
- release PR は作られるが、merge しても apply されない:`release-targets.sh` が target を返さないとき、`auto-release--plan.yaml` と `auto-release--trigger.yaml` は `plan` / `apply` を skip する。
- `aws/{service}/` 配下の environment ディレクトリ以外のファイル(module、`root.hcl` など)の変更は `terragrunt-shared` stack で検出され、その service の全 environment が対象になる。
- `apply_trigger: release` の environment ディレクトリを持つ service には `aws/{service}/release-please-config.json` と `release-please-manifest.json` が必要。無い場合は `check-release-configs.sh` が CI を失敗させる。
- `apply_trigger: merge` の environment ディレクトリだけを変える commit は、`release-please-config.json` の `exclude-paths` で release から外れる。
- `check-release-configs.sh` は `exclude-paths` を `workflow-config.yaml` の `apply_trigger: merge` の environment と照合する。

### Stacks

Expand All @@ -47,6 +58,7 @@
### Environments and Authentication

- Environment、AWS region、IAM role は `workflow-config.yaml` で定義する。環境の追加・削除は同ファイルの編集で行う。
- `workflow-config.yaml` の `apply_trigger` が environment を apply するタイミングを決める。`merge` は `main` への push、`release` は service の release 公開時に apply する。
- 認証は GitHub OIDC。`aws/github-oidc-auth/` が environment ごとの plan / apply role を発行し、executor がそれを引き受ける。
- Terragrunt の remote state は S3 + DynamoDB に保存される。bucket / lock table 名は各 `aws/{service}/root.hcl`(および `github/` 配下の同等ファイル)で定義される。

Expand All @@ -57,6 +69,7 @@ flowchart LR
subgraph Triggers
PRevent[PR open/sync]
Mainpush[push main]
Release[release published]
end

subgraph Labeling
Expand All @@ -75,6 +88,7 @@ flowchart LR
end

subgraph Terragrunt
ReleaseTrigger[auto-release--trigger]
TG[reusable--terragrunt-executor]
Plan[terragrunt plan]
Apply[terragrunt apply]
Expand All @@ -91,8 +105,10 @@ flowchart LR
Dispatcher --> Trigger
Trigger --> Resolver
Resolver -->|stack: terragrunt| TG
Release --> ReleaseTrigger
ReleaseTrigger --> TG
TG -->|on pull_request| Plan
TG -->|on push main| Apply
TG -->|on push main or release published| Apply
Plan --> PRComment
Apply --> AWS
Resolver -->|stack: kubernetes| Group
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ A PR labeled by `panicboat/deploy-actions` is resolved against `workflow-config.

- `auto-label--deploy-trigger.yaml` runs on PR labels and on push to `main`.
- `panicboat/deploy-actions/label-resolver` reads `workflow-config.yaml` to expand each label into one or more `{service}/{environment}` targets.
- Terragrunt environments with `apply_trigger: merge` are applied on push to `main`.
- Terragrunt environments with `apply_trigger: release` are applied by `auto-release--trigger.yaml` when the `aws/{service}` release is published.
- The release PR runs `terragrunt plan` through `auto-release--plan.yaml`; to re-plan, use "Re-run all jobs" on that `auto-release--plan` run.
- To re-apply a release, run `auto-release--trigger` with `workflow_dispatch` on the release tag ref and pass the same tag: `gh workflow run auto-release--trigger.yaml --ref {service}-vX.Y.Z -f tag={service}-vX.Y.Z`.
- A `{service}/{environment}` pair matched by a rule in `exclude` of the `terragrunt` stack in `workflow-config.yaml` gets no target from `label-resolver` (PR plan, apply on push to `main`) and no release plan / apply.
- A rule matches a pair when every key (`service`, `environment`) equals the pair's value; a rule with only `environment` covers every service.
- The release PR is still created, but merging it applies nothing: `auto-release--plan.yaml` and `auto-release--trigger.yaml` skip `plan` / `apply` when `release-targets.sh` returns no target.
- Changes to files outside environment directories under `aws/{service}/` (modules, `root.hcl`, ...) are detected through the `terragrunt-shared` stack and target every environment of that service.
- A service with an `apply_trigger: release` environment directory needs `aws/{service}/release-please-config.json` and `release-please-manifest.json`; `check-release-configs.sh` fails CI otherwise.
- Commits that change only `apply_trigger: merge` environment directories are kept out of the release through `exclude-paths` in `release-please-config.json`.
- `check-release-configs.sh` compares `exclude-paths` with the `apply_trigger: merge` environments in `workflow-config.yaml`.

### Stacks

Expand All @@ -47,6 +58,7 @@ A PR labeled by `panicboat/deploy-actions` is resolved against `workflow-config.
### Environments and authentication

- Environments, AWS regions, and IAM roles are defined in `workflow-config.yaml`. Adding or removing an environment means editing that file.
- `apply_trigger` in `workflow-config.yaml` decides when an environment is applied: `merge` applies on push to `main`, `release` applies when the service release is published.
- Authentication uses GitHub OIDC. `aws/github-oidc-auth/` issues per-environment plan / apply roles that the executors assume.
- Terragrunt remote state lives in S3 + DynamoDB; bucket and lock-table names are defined in each `aws/{service}/root.hcl` (and the equivalent under `github/`).

Expand All @@ -57,6 +69,7 @@ flowchart LR
subgraph Triggers
PRevent[PR open/sync]
Mainpush[push main]
Release[release published]
end

subgraph Labeling
Expand All @@ -75,6 +88,7 @@ flowchart LR
end

subgraph Terragrunt
ReleaseTrigger[auto-release--trigger]
TG[reusable--terragrunt-executor]
Plan[terragrunt plan]
Apply[terragrunt apply]
Expand All @@ -91,8 +105,10 @@ flowchart LR
Dispatcher --> Trigger
Trigger --> Resolver
Resolver -->|stack: terragrunt| TG
Release --> ReleaseTrigger
ReleaseTrigger --> TG
TG -->|on pull_request| Plan
TG -->|on push main| Apply
TG -->|on push main or release published| Apply
Plan --> PRComment
Apply --> AWS
Resolver -->|stack: kubernetes| Group
Expand Down
Loading
Loading