TECHSTEP

ITインフラ関連の記事を公開してます。

【メモ】PRを作成したら対象ブランチ名を変更してCodePipelineを起動する

以前CodeBuildでPull Requestごとに特定のCommit IDをターゲットにビルドを実行する方法を調べましたが、今回はAWS CodePipelineで同じことを実現しようとしました。

背景

AWS CodePipelineAWSサービスの一つでフルマネージドな継続的デリバリーサービス と紹介されます。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のほうを見ても、目的のブランチを対象にビルドが実行された様子が確認できました。