6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS StepFunctionsを使って CodeCommitでプルリク時にそのブランチ名を自動的に既存のCodeBuildの対象ブランチにしてテスト実行する。

Last updated at Posted at 2022-02-16

動機

CodePipelineを使ってCodeCommitにpush時にCodeBuildでテストを実行できるようにはしたけれど、プルリクしてきたそのソースに対してマージ前にテストを行なってテストが通ったものをレビューしてマージしたいという思いがありました。

ビルドプロジェクトのソースを都度手で修正して行うのは実行自体は簡単ですが、ブランチ戦略によりプルリク元のブランチ名が様々である場合手動で行うとその手間が多く無駄であるということから自動で既存のビルドプロジェクトの対象ブランチをプルリクをフックにそのブランチ名を使って更新できないかということを調べていていました。

lambdaによる実現の情報はあったのですが、もっとeasyに実現できないかと他の方法も考えたときに「そういえばStep Functionsというのがあったな、、、」ということから実際に行ってみました。

タイトルには「ブランチ名を」と書きましたが、同じ考えでコミットIDを指定することもできます。(そちらの方がビルドプロジェクトの実行完了は早いようです。)

ユースケース

  • コードレビューをしている。
  • プルリク先はあまり変わらないが(main, develop)プルリク元のブランチ名が様々(feature_1, fotfix_xxxx)である。
  • プルリクしたときにそのプルリク元のブランチ名で既存のビルドプロジェクトの対象ブランチを自動で変更したい。

この記事の目的

タイトルにあるままですがその方法を共有します。できるだけイージーさを意識し、IaCによる作成は記載していません。。イベントの流れを参考にいただけたらと思います。

記事の前に

書いてあること

  • AWS CodeBuildでのビルドプロジェクトの作成のさわり。
  • ユースケースを実現するためのEvent Bridge, Step Functionsの作成、設定など。

書いてないこと

  • 使用するサービスの詳細(CodeCommitの登録、IAMとは何か、などサービス自体の基本的な説明)
  • テスト実行するアプリの詳細(Buildの仕方、Testの仕方)など。

免責

ほとんどかからないと思いますが、もし参考にしていただいたときAWSの使用金額はおそれいりますがご自身で管理ください。CodeBuildが少々(数円〜?)かかると思います。
また参考いただいたときに、なんらかしらの設定ミスによるセキュリティ事故に関してもご自身の責任でお願いします。

流れ

  1. 準備 アプリ、ビルドプロジェクトの準備等
  2. EventBridge, Step Functions の設定

の順で説明します。
いたずらに記事が長くなるのを避けるため補足的な説明は折り畳みにしています。

1.アプリ、ビルドプロジェクトの準備等

アプリの準備とAWS CodeCommitへのpush

以下のようなアプリの状態を前提とします。

  • コマンドでテストができるWebアプリ
  • AWS CodeCommitでリモートリポジトリを登録しておく。デフォルトブランチはmainとします。
  • アプリのルートフォルダにbuildspec.ymlを用意しておく。

この記事で使用するアプリの技術は

  • spring boot2
  • gradle
  • JUnit

を使用しています。
ただしバージョンなどはこの記事では関係なく、Javaであるか自体もこの時期には関係ないです。
テストフレームワークとしてJUnitを使用し、 ./gradlew test でテストができることを確認し、
その結果の生成物をテストレポートとしてCodeBuildに読ませるので以下のように記述しておきます。

buildspec.yml
version: 0.2

phases:
  build:
    commands:
      - ./gradlew test
reports:
  junit-reports:
    files:
      - "build/test-results/**/*.xml"
    file-format: "JUNITXML"

参考:ステップ 2: buildspec ファイルを作成する
参考:テストレポートの作成

buildspec.ymlをルートに入れた状態でコミット,pushしておきます。

テストを行うCodeBuildのプロジェクトの作成

テストが実行できるビルドプロジェクトを作っておきます。プロジェクト名は任意です。
ここでは
プロジェクト名 TestProject
リポジトリ spring-demo
デフォルトブランチ名 main
としました。

作成の例
ビルドプロジェクトも動的に作成することはできますが、今回は使い回してソースブランチを変えることを行うので 手で作っておきます。

ビルドプロジェクトから
buildproject.png
作成を押下

select_create_project.png
プロジェクトの名前はTestProjectとします。
makeBuildProject.png

今回リポジトリはspring-demoという名前で設定しました(任意)。そのリポジトリを選択し、
ブランチをまずmainにしておきます。

source.png

ビルドに使うイメージはAWSのマネージドを使います。使用するアプリのテストコマンドがマネージドイメージではできない場合はカスタムDockerイメージ作り指定しますがそのまま./gradlew testも使用できるためマネージドイメージを使用します。

サービスロールは新しいサービスロースを選択します。
ap-northeast-1.console.aws.amazon.com_codesuite_codebuild_project_new_region=ap-northeast-1.png

サービスロールは自分で作ることもできますが、AWS CLIを使います。
参考:IAMロールとAWSサービスロールの違いって何?(自分なりの理解)

今回はテストだけでビルド生成物は使用しませんので「アーティファクトなし」にしておきます。
buildspec.png

ログは使用するのでチェックしておきます。グループ名等は空のままにします。(自動で生成されるものを使います。)
click_make_project.png

作成後、ビルドの実行を行い成功を確認します。当然この時点では 「ソースのバージョン」が mainであることを確認しておきます。
ビルドの実行.png

ビルド履歴 > レポート にてテスト結果が確認できます。(最低限のデモのため1件だけです。)
テスト結果.png

2. EventBridge, Step Functions の設定

feature_1というブランチを作って以下のようにbuildspec.ymlを変更してコミット、プッシュします。(この変更に特に意味はありません。mainとの差分をつくるためです。)

version: 0.2

phases:
  build:
    commands:
+      - echo 'feature_1'
      - ./gradlew test
reports:
  junit-reports:
    files:
      - "build/test-results/**/*.xml"
    file-format: "JUNITXML"

ここでfeature_1からmainにプルリクを作ったとき、そのイベントをフックに

  • 自動で先ほど作ったビルドプロジェクトのソースブランチがfeature_1になる。
  • その後自動でテストを実行する

の流れを行うのはどのようにすればよいでしょうか。方法はいろいろあると思いますがStep Functionsを使います。

Step Functionsの作成

ステートマシンを作る.png

「ワークフローを視覚的に設計」を選びます。「サンプルプロジェクトを実行」にビルドスタートからSNS通知までのサンプルがあるのですが、その前段階を行いたいので一からつくります。
視覚的に設計.png

左上の検索枠からCodeBuildを検索し「UpdateProject」「codebuild StartBuild」を選んで以下のように配置します。
updateProject.png

UpdateProjectをクリックし、右側にあるAPIパラメータに以下のように記述します。
updateProjectAPI.png

{
   "Name": "TestProject",
   "SourceVersion.$": "$.detail.sourceReference"
}

このパラメータを使うのがポイントです。パラメータを使う、ということ自体は簡単なことなんですが知らないといつまでも「その使い方があったのか」的なアレ?です。

パラメータについて

ここで$.detail.sourceReferenceとはなんでしょうか?
まだEvent Bridgeは設定していませんが、プルリクエストを行った時のイベントは以下のように入力されます。
pullRequestEvent.png
参考:Step Functions 実行ステータス変更用 EventBridge (CloudWatch Events)
参考:pullRequestCreated イベント

ここにあるdetail.sourceReferenceをパラメータで参照する時の書き方が$detail.sourceReferenceとなります。
ここですでに予想ができるかもしれませんが、プルリクエストしたときのそのブランチがここで展開されます。
SourceVersion.$はCodeBuild APIのupdate-projectのキーの一部です。
Step Functionsではキーはパスカルケースにする必要があるため先頭は大文字です。
参考:update-project
参考:InputPath、パラメータ、および ResultSelector
変数(パラメータ)を使用する時キーは~.$、バリューは$.~とする必要があります。

流れるイベントがどのようなJSONデータになるかは実際はドキュメントを見て確認するより、
イベントを流したり、Step Functionsのデータフローシュミレーターで試したりした方がイメージつかみやすいと思います。

同様にStartProjectをクリックし右側にあるAPIパラメータに以下のように記述します。ProjectName.$はstart-build APIに対するキーになります。
$.Project.NameはUpdateProjectのステップのoutputとして出力されるものを参照しています。

{
  "ProjectName.$": "$.Project.Name"
}

参考:start-build

右上の「次へ」

PullRequestStateMachineを修正.png

「次へ」
「ステートマシンを編集」

ここではステートマシン名をPullRequestStateMachineとしました。(任意)
アクセス許可は「新しいロールの作成」
一番下にUpdateProjectのActionが足りない警告がでますがここでは
「ステートマシンの作成」を押下し、まず作ってしまいます。

遷移後ロールにポリシーを追加します。

ポリシーの追加

右上の「IAMロールを編集」を押下します。
IAMロールを編集.png

ポリシー名.png

ActionにUpdatePolicyを追加して保存します。

policy編集.png

ポリシーの確認 > 変更の保存

これでプルリクエストしたブランチを対象ブランチにしてテストを実行するStateMachineができました。
ただし後述しますがこのままではプルリクエストをクローズしたとき、マージしたときも実行されてしまうのでのちに修正します。

Event Bridgeの作成

Event Bridge からpullRequestイベントを送信する新しいルールを作ります。

image.png

プルリクエストをしたイベントタイプはCodeCommit Pull Request State Changeにします。これはプルリクエストの作成、そのプルリクエストのブランチが更新されたとき、クローズの時、マージしたときを補足します。

image.png

ターゲットを先ほど作ったステートマシンにします。
ロールはここでは新しいロールにします。
image.png

作成を押下します。

これで流れができたので試してみます。

CodeCommitでプルリクエストを作成します。

プルリクエストの作成.png

実行が確認できました。

マシーン実行.png

CodeBuildのログを見るとプルリクしたブランチに変わっているのがわかります。プルリクから自動でブランチ名を変えた実行ができました。

移り変わり.png

クローズした時の対応

このままではクローズしたとき、マージしたときもUpdateProjectが実行されるのでStepFunctionsを編集します。
ChoiceとPassを追加します。
choiceとpass.png

Choiceをクリックし右側のRule > Edit にて以下のようにします。

Condition.png

プルダウンを Simple
variableを$.detail.pullRequestStatus
Operatorをis equal to
valueをString constant
値を Closed
保存します。

これでプルリクの状態変更がCloseの時Passに行くようになります。
Passは特に編集しません。
Passを追加したのはChoiceから直接ENDにできなかったためです。

この時点のステートマシンの定義

{
  "Comment": "A description of my state machine",
  "StartAt": "Choice",
  "States": {
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.detail.pullRequestStatus",
          "StringEquals": "Closed",
          "Next": "Pass"
        }
      ],
      "Default": "UpdateProject"
    },
    "Pass": {
      "Type": "Pass",
      "End": true
    },
    "UpdateProject": {
      "Type": "Task",
      "Parameters": {
        "Name": "TestProject",
        "SourceVersion.$": "$.detail.sourceReference"
      },
      "Resource": "arn:aws:states:::aws-sdk:codebuild:updateProject",
      "Next": "CodeBuild StartBuild"
    },
    "CodeBuild StartBuild": {
      "Type": "Task",
      "Resource": "arn:aws:states:::codebuild:startBuild",
      "Parameters": {
        "ProjectName.$": "$.Project.Name"
      },
      "End": true
    }
  }
}

これでプルリクエストをクローズ、またはマージすると以下のようになります。
WhenCLosed.png
プルリク作成時は以下になります。
WhenOpen.png

これでプルリクエストから動的に既存のビルドプロジェクトの対象ブランチを変更することができました。

このあとは

ここから成功、失敗をSNSトピックに送信してチャットツールに連携するなど、、、。

やってみて

何かのイベントをフックに何かする == lambdaという先入観がありましたがAWSサービス間のつなぎだと
Step Functionsを使うのもありだなと思いました。
あらためてAWSがあらゆるサービスでAPIを公開していることによるサービス間の連携の強力さ、素晴らしさを感じました。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?