以前CodeBuildでPull Requestごとに特定のCommit IDをターゲットにビルドを実行する方法を調べましたが、今回はAWS CodePipelineで同じことを実現しようとしました。
背景
AWS CodePipelineはAWSサービスの一つでフルマネージドな継続的デリバリーサービス と紹介されます。CodePipelineからCodeBuildなどのサービスを呼び出し、一連の処理をパイプラインで実行するサービスです。
Pull RequestごとにCodePipelineの起動と実行ができると、例えばPull Requestごとに複数のCodeBuildを直列に実行したり、CodeBuildとLambda関数など別のリソースを実行することもできます。またCodePipelineから見ることで処理全体の見通しが良くなる効果も期待できます。
CodePipelineの機能を眺めると、Pull Requestのたびに実行するようなものはデフォルトで含まれていないように見えます。そのため、前回のCodeBuildと同様、何かしら別の方法を考える必要があります。
方法
CodePipelineを起動するStartPipelineExecution APIを見ると、指定できるパラメータが極端に少ない (clientRequestToken
name
のみ)ことがわかりました。一方、CodePipelineの中でCodeCommitをソースにする場合、対象のブランチ名を指定する必要があります。そこで今回はパイプラインを起動する前にCodePipelineの設定を更新し、PRの作成元となるブランチ名を指定することで、特定のブランチに対しパイプラインを実行できるようにしました。
パイプラインの更新には UpdatePipeline APIを利用しました。CodeCommitをソースに利用する場合は BranchName
というパラメータに特定のブランチ名を渡すことで、ターゲットのブランチ名を変更できます。
今回の処理の大まかな流れは以下の通りです。ここではCodePipelineの設定を更新するためにCodeBuildプロジェクトを追加していますが、ここはLambda等でも実現可能だと思います。
CodeCommit -> EventBridge -> CodeBuild -> CodePipeline | CodeCommit -> CodeBuild
確認
今回は以下のリソースを用意しました。
Amazon EventBridge
AWS CodeCommit
: テスト用の2つのファイルとbuildspec.yml
を用意AWS CodeBuild
:- パイプライン起動用:
BRANCH_NAME
という環境変数を設定 - 実処理用:
main
ブランチをターゲットに設定
- パイプライン起動用:
AWS CodePipeline
: 上記CodeCommit/CodeBuild (実処理用) を指定
まずCodePipelineを実行するCodeBuildプロジェクトは start-pipeline-at-pr.yaml
というファイルを使用します。pipeline-update.sh
というスクリプトを用意し、引数にターゲットのブランチ名を渡します。
start-pipeline-at-pr.yaml
version: 0.2 phases: install: runtime-versions: python: 3.9 commands: - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" > /dev/null - unzip awscliv2.zip > /dev/null - ./aws/install --bin-dir /root/.pyenv/shims --install-dir /usr/local/aws-cli --update > /dev/null - aws --version pre_build: commands: - echo Update Pipeline config - sh ./pipeline-update.sh $BRANCH_NAME build: commands: - echo Execute Pipeline - aws codepipeline start-pipeline-execution --name test-pipeline-20230504
CodePipelineを起動するCodeBuildが呼び出す pipeline-update.sh
は以下の通りです。CodeCommitイベントは sourceReference
というパラメータにPR作成元のブランチ名を含むのでこれを利用します。このブランチ名には refs/head/
という文字列が含まれており、このままブランチ名として指定することはできないため、これを取り除く必要があります。
pipeline-update.sh
#!/bin/bash TARGET_BRANCH_PATH=$1 PIPELINE_NAME="test-pipeline-20230504" echo "start updating codepipeline" echo "get current pipeline config" if [[ "$TARGET_BRANCH_PATH" == */* ]]; then TARGET_BRANCH_NAME=${TARGET_BRANCH_PATH##*/} else TARGET_BRANCH_NAME=$TARGET_BRANCH_PATH fi aws codepipeline get-pipeline --name $PIPELINE_NAME --query pipeline > pipeline.json echo "edit pipeline json file" sed -i "s/\"BranchName\".*/\"BranchName\": \"$TARGET_BRANCH_NAME\",/" pipeline.json echo "update pipeline config" aws codepipeline update-pipeline --pipeline file://pipeline.json 2>&1 1>/dev/null
上記スクリプトに渡すブランチ名は、CodeBuildの環境変数である BRANCH_NAME
を利用します。この変数はPRごとに変わるため、以前も利用したEventBridge Input Transformationを利用します。今回は以下のような設定を追加し、環境変数の上書きを行っています。
入力パス
{ "sourceVersion": "$.detail.sourceReference" }
入力テンプレート
{ "sourceVersion": "<sourceVersion>", "environmentVariablesOverride": [ { "name": "BRANCH_NAME", "value": "<sourceVersion>", "type": "PLAINTEXT" } ] }
CodePipelineが実行するCodeCommit/CodeBuildの内容は以前と同様です。
CodeBuild buildspec.yml
version: 0.2 phases: build: commands: - echo Check test01 - cat test01.txt - echo Check test02 - cat test02.txt
なお、CodePipelineをAWSマネージドコンソール画面から作成した場合、作成時のブランチをターゲットにしたEventBridgeが作成されます。これが残っていると対象のブランチ上に変更が走るたびにCodePipelineが起動するので、不要であればこれを無効化・削除してください。
この状態で以下のPull Requestを作成します。
Pull Requestを作成するとEventBridgeが検知し、CodeBuildを起動します。スクリプトにいろいろと足りていないのでわかりにくいですが、実行後のログを確認すると、スクリプトに記載された内容を実行し、最後にパイプラインの更新が行われている様子を確認できます。
CodePipelineのほうを見ると、直前にパイプラインが実行されている様子を確認できます。またCodeCommitの設定を見ると、先ほどのPRで指定したブランチ名を指していることが確認できます。
最後にCodeBuildのほうを見ても、目的のブランチを対象にビルドが実行された様子が確認できました。