背景
CI/CDを実現するために各種ソフトウェアを設定するなかで、変更のあったファイルに合わせて起動するジョブを変更したいときがあります。
例えばファイルによって適用するジョブを変更したい場合です。処理対象のファイルによって必要なコマンドライン引数が異なったり、ファイルごとに処理することでCI完了までの時間を短くしたいなどの理由から、変更するファイルによって起動するジョブを変えたいときがあります。
今回はAWS CodeCommit/CodeBuildを使った状況で、上記のようなファイルごとに処理を分ける方法を検証しました。
方法
CodeBuildでファイルごとに処理を分けるため、Pull Requestから変更のあったファイルを特定する、対象のファイルを指定してCodeBuildのビルドを実行する、という2段階で処理することを考えました。
Pull Requestから変更のあったファイルを特定するには、例えば git diff
コマンドを使う方法もあります。今回はAWSのサービスを使っているので、CodeCommitの GetDifferences というAPIを使うことにしました。このAPIは2つのCommit識別子 (ブランチ、タグ、Commit IDなど) を比較して、Responseとして差分を返します。この中には差分のあったファイルパスが含まれているので、これを後段の処理に使います。
また対象のファイルを指定してCodeBuildのビルドを実行するために、CodeBuildに環境変数を設定し、これを取得したファイルパスを上書きすることで実現しました。CodeBuildの StartBuild APIは environmentVariablesOverride
というパラメータで環境変数を上書きすることができます。ここに取得したファイルパスを指定し、CodeBuildの処理の中でこれを指定する形で処理しました。
確認
今回の動作確認のために以下のリソースを使用します。
Amazon EventBridge
: 前回と同じイベントパターンを使用します。AWS CodeBuild
:main
ブランチをターゲットに、TARGET_PATH
を環境変数に設定しました。AWS CodeCommit
: テスト用の2ファイルとbuildspec.yml
を用意しました。AWS Lambda
: IAMロールにCodeCommitのcodecommit:GetDifferences
権限およびCodeBuildのStartBuild
権限を付与します。
またLambda関数および buildspec.yml
は以下の通りです。
Lambda関数
import boto3 codecommit = boto3.client('codecommit') codebuild = boto3.client('codebuild') # 実行対象のCodeBuildプロジェクト名を指定 project = "test-codebuild-20230430" def lambda_handler(event, context): response = codecommit.get_differences( repositoryName=event['detail']['repositoryNames'][0], beforeCommitSpecifier=event['detail']['destinationCommit'], afterCommitSpecifier=event['detail']['sourceCommit'] ) target_list = [difference['afterBlob']['path'] for difference in response['differences']] for target in target_list: build_response = start_codebuild(project, event['detail']['sourceCommit'], target) print('%s for %s is %s at %s' % (build_response['build']['projectName'], target, build_response['build']['buildStatus'], build_response['build']['sourceVersion'])) return {} def start_codebuild(ProjectName, CommitID, target_path): response = codebuild.start_build( projectName=ProjectName, sourceVersion=CommitID, environmentVariablesOverride=[ { 'name': 'TARGET_PATH', 'value': target_path, 'type': 'PLAINTEXT' }, ], ) return response
buildspec.yml
version: 0.2 phases: build: commands: - echo Check target file name - echo $TARGET_PATH - echo Print target file - cat $TARGET_PATH
まず1つのファイルに変更のあった場合を見るため、以下のPull Requestを作成します。ここでは test01.txt
に文字列を追加します。
上記Pull Requestの作成をトリガーに、EventBridgeからLambda関数を実行します。Lambda関数の中で変更のあったファイルを取得して TARGET_PATH
を設定し、CodeBuildのビルドを開始します。CodeBuildの実行結果を見ると、Pull Requiestで変更のあった test01.txt
の内容だけが出力されます。
次に複数のファイルに変更があった場合を見るため、以下のPull Requestを作成します。
Pull Request作成後にCodeBuildの管理画面を見ると、以下のような2つのビルドが実行完了したことを確認できます。
それぞれの結果を見ると、 test01.txt
test02.txt
それぞれの内容が個別に出力されていることが確認できます。
test01.txt
test02.txt
最後にCodeBuildに記載されるCommit IDを見ると、Pull Request作成時のCommit IDが使われていることが確認できます。