
GitHub Actionsでプルリクエストの変更ファイルに応じて必要なリージョンのみにデプロイする実装をやってみた
こんにちは!製造ビジネステクノロジー部の小林です。
最近、GitHub Actions を利用して AWS の US リージョンにデプロイする機会がありました。普段は東京リージョンをメインに利用しているのですが、ソースを変更するたびに両リージョンへのデプロイが走ってしまい、待ち時間がかさんでもどかしい思いをしました。
このような無駄なデプロイを避けるため、変更されたファイルに応じてデプロイ先を動的に切り替える仕組みを検討してみました!
tj-actions/changed-files のセキュリティインシデント
GitHub Actions で変更ファイルを検出する際、広く使われていたのが「tj-actions/changed-files」です。しかし、このアクションは 2025 年 3 月にサプライチェーン攻撃を受けた過去があります。
一度インシデントがあったアクションの利用には、組織のセキュリティポリシーによる制限や、開発者自身に抵抗があることもあるかとおもいます。そこで今回は、「step-security/changed-files」を使用します。
step-security/changed-filesとは?
このアクションは、GitHub Actions 向けのセキュリティプラットフォームを提供しているStepSecurity社が開発・メンテナンスしており、変更されたファイルやディレクトリを正確に検出できます。
tj-actions/changed-files のインシデント発生時にも検知・報告を行った実績があります。
どんな機能があるの?
特定のパターン(グループ)に基づいてファイルの変更を分類し、その結果を他のステップで利用できます。主な機能を見てみましょう。
変更ファイルの検出
プルリクエストの差分から変更されたファイルをリスト化します。
グループ化
YAML形式でファイルパスのパターン(ワイルドカードも利用可)を定義し、関連する変更をグループ化できます。例えば、「backend」「frontend」「infrastructure」のように変更箇所をグループにして分類できます。
- name: Detect changed files
id: changes
uses: step-security/changed-files@v46
with:
files_yaml: |
backend:
- 'src/backend/**'
frontend:
- 'src/frontend/**'
infrastructure:
- 'infrastructure/lib/**'
出力を利用する
このアクションを実行すると、検出結果が以下のようなアウトプット変数として提供されます。これが、ワークフローの後続ステップの実行条件(if文)で役立ちます。
出力変数は複数あり、公式リポジトリのREADMEから確認できます。
よく使うと思われる出力変数
changed_keys
files_yaml でグループを定義したとき、変更があったグループ名をカンマ区切りで返します。
例:
- backend グループのファイルが変更された場合: backend
- backend と frontend グループのファイルが変更された場合: backend,frontend
- 変更がない場合: 空文字列
用途: 複数のグループを横断的にチェックするときに便利です。
any_changed
指定したファイルパターンに該当する変更があったかを true / false で返します。
files_yaml を使用している場合は、{グループ名}_any_changed という形式でアクセスします。
例:
- backend_any_changed
用途: 「このグループに何か変更があったか?」を単純に判定するときに使います。
only_changed
指定したファイルパターンのみが変更され、それ以外のファイルに変更がなかったかを true / false で返します。
files_yaml を使用している場合は、{グループ名}_only_changed という形式でアクセスします(
例:
- backend_only_changed
用途: 「このグループだけが変更されたか?」を判定するときに使います。
やってみた
では実際に、このアクションを使って複数リージョンへの条件付きデプロイを実装してみます。
前提条件
- GitHub Actions を利用する
- ここでは、実際にAWSにデプロイする代わりに、どのリージョンへのデプロイがスキップされるかをテストすることに焦点を当てます。(echo で出力するだけ)
ディレクトリ構成
今回は東京リージョンと US リージョンにデプロイする CDK プロジェクトを想定します。
.
├── .github
│ └── workflows
│ └── release.yml
├── bin
│ └── cloudformation-notification.ts
├── cdk.out
├── lib
│ ├── tokyo-stack
│ │ ├── constructs
│ │ │ ├── events
│ │ │ │ └── index.ts
│ │ │ └── slack
│ │ │ └── index.ts
│ │ └── index.ts
│ └── virginia-stack
│ ├── constructs
│ └── index.ts
├── node_modules
├── test
├── .gitignore
├── .npmignore
├── cdk.json
├── jest.config.js
├── package-lock.json
├── package.json
├── README.md
└── tsconfig.json
ワークフローの実装
name: DEPLOY
on:
# プルリクエストによる自動実行トリガー
pull_request:
types:
- opened # プルリクエスト作成時
- ready_for_review # ドラフト解除時
jobs:
deploy:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# デプロイ対象のリージョンを判定する
#
# プルリクエストの変更ファイルから、
# どのAWSリージョン(東京 or バージニア)に関連する変更かを判定する
# これにより不要なリージョンへのデプロイを防ぎ、実行時間・コストを削減する
#
# - tokyo グループ:
# 東京リージョンに関係するソースの変更を検知する
# - virginia グループ:
# バージニアリージョンに関係するソースの変更を検知する
# - global グループ:
# 東京およびバージニア両リージョンに関係するソースの変更を検知する
- name: Detect changed regions for deployment
id: changes
uses: step-security/changed-files@v46
with:
files_yaml: |
tokyo:
- lib/tokyo-stack/**
virginia:
- lib/virginia-stack/**
global:
- '**/bin/**'
- '**/cdk.json'
- '**/cdk.context.json'
- '**/package.json'
- '**/package-lock.json'
- '**/tsconfig.json'
- '**/.npmrc'
- name: Deploy Virginia Stack
if: contains(steps.changes.outputs.changed_keys, 'virginia')
|| contains(steps.changes.outputs.changed_keys, 'global')
run: echo "USリージョンにデプロイ"
- name: Deploy Tokyo Stack
if: contains(steps.changes.outputs.changed_keys, 'tokyo')
|| contains(steps.changes.outputs.changed_keys, 'global')
run: echo "東京リージョンにデプロイ"
ポイント解説
グループの定義
files_yaml: |
tokyo:
- lib/tokyo-stack/**
virginia:
- lib/virginia-stack/**
global:
- '**/bin/**'
- '**/cdk.json'
- '**/cdk.context.json'
- '**/package.json'
- '**/package-lock.json'
- '**/tsconfig.json'
- '**/.npmrc'
- tokyo: 東京リージョン固有のファイル
- virginia: バージニアリージョン固有のファイル
- global: 両リージョンに影響する共通ファイル
この設計により、例えば package.json を変更した場合は両リージョンにデプロイされますが、lib/tokyo-stack/ 配下のファイルのみ変更した場合は東京リージョンのみにデプロイされます。
条件分岐
今回は、changed_keys を利用して条件を分岐します。
changed_keys の仕組み
changed_keys は、変更が検出されたグループ名をカンマ区切りで返します。
例:
virginia グループのみ変更 → "virginia"
virginia と global グループが変更 → "virginia,global"
変更なし → ""(空文字列)
contains() 関数による判定
contains() 関数を使うと、文字列に特定の部分文字列が含まれているかをチェックできます。
if: |
contains(steps.changes.outputs.changed_keys, 'virginia') ||
contains(steps.changes.outputs.changed_keys, 'global')
この条件式は以下のように動作します。
contains(steps.changes.outputs.changed_keys, 'virginia')
→ changed_keys に "virginia" が含まれているか判定
contains(steps.changes.outputs.changed_keys, 'global')
→ changed_keys に "global" が含まれているか判定
このように、virginia または global グループのパターンにマッチするファイルが変更された場合のみ、次の処理が実行されます。
動作確認
実際に異なるパターンで変更を加えて、動作を確認してみましょう。
東京リージョンのみ変更
lib/tokyo-stack/ 配下にテストファイル(test.ts)を作成します。
東京リージョンのステップのみ実行されました。
USリージョンのみ変更
lib/virginia-stack/ 配下にテストファイル(test.ts)を作成します。
USリージョンのステップのみ実行されました。
両リージョンのファイル変更
lib/tokyo-stack/ 配下にテストファイル(test.ts)を作成します。
lib/virginia-stack/ 配下にテストファイル(test.ts)を作成します。
東京リージョンとUSリージョン両方のステップが実行されました。
共用ファイルの変更
package.json に "source-map-support": "^0.5.21" を追加します。
東京リージョンとUSリージョン両方のステップが実行されました。
対象外ファイルの変更
最後にグループに定義していない修正を行ってみます。ルートディレクトリ直下にテストファイル(test.ts)を作成します。
東京リージョンとUSリージョン両方のステップがスキップされました。
おわりに
今回は、GitHub Actions の step-security/changed-files を利用して、プルリクエスト内の変更ファイルに応じてデプロイするリージョンを判定する実装をやってみました。
デプロイするリソースやリージョンが増えるほど、デプロイに時間がかかるため、頻繁にデプロイするときは待ち時間がもどかしいですよね...
今回の実装により、変更されたファイルに応じて必要なリージョンのみにデプロイすることで、CI/CD の実行時間を短縮でき、作業効率を改善できます!
この記事がどなたかの参考になれば幸いです。