0
0

Azure DevOps Pipelineで PRの変更ファイルの依存関係をdependency-cruiserで可視化してコメントしたメモ

Posted at

概要

前回dependency-cruiser-report-actionでPRの変更ファイルの依存関係を可視化してコメントするを参考に、GitHubActionsを使ってプルリクエストに含まれるファイルの依存性の可視化を行った。

Azure DevOpsのプルリクエストでも同じこと出来るだろ、と軽い気持ちで始めたら意外に大変だったのでメモを残す。

最終的には下記のような図をプルリクエスト時に作成できた。

image.png

ソースコード

GitHubとの違い

GitHubと大きく異なった部分としては下記がある

  • 既存のCustom Actionがない
  • プルリクエストのコメントにmermaid記法が使えない
    • Azure DevOpsの場合、Wikiのみでmermaid記法を使用することができる。

この条件の違いから、Azure DevOpsでは、プルリク作成時にパイプラインを動かし、mermaidでのグラフ描画用のwikiを作成し、そのwikiへのリンクをプルリクエストのコメントにする方法をとろうと思う。

環境

  • AzureDevOps
  • リポジトリのブランチをcode as wikiとして使用している

image.png

パイプラインのユーザに権限を付与する

  • ビルドパイプライン用のユーザが自動作成されているので、下記2つの Allow権限を与える
    • Wikiの更新のための「 Contribute 」
    • プルリクエストにコメントするための「Contribute to pull requests」

image.png

パイプラインを作成する

  • 利用しているリポジトリがモノレポ構成でnpmをモノレポ管理に使っていたため、パイプラインで使用するNodeを更新する必要があった。( デフォルトのnpmが古くてモノレポ管理できないバージョン)
    今回は下記の設定としてパイプラインに明記
    • Node: 20.14.0
    • npm: 10.8.1
azure-devops/depcruise.yml
# ブランチポリシーにより起動させる

trigger: none
pr: none
jobs:
- job: check
  pool:
    vmImage: ubuntu-latest
  steps:
    - task: UseNode@1
      inputs:
        version: '20.14.0'
      displayName: 'Install Node.js'
    - task: Npm@1
      inputs:
        command: custom
        customCommand: 'install -g npm@10.8.1'
      displayName: 'Upgrade npm to latest version'
    - task: Npm@1
      name: npm_install
      inputs:
        command: custom
        customCommand: 'install --ignore-scripts'
        workingDir: $(Build.SourcesDirectory)
    - task: Bash@3
      displayName: 'POST PR Comment'
      inputs:
        workingDirectory: $(Build.SourcesDirectory)/app
        targetType: 'inline'
        script: |
          # 変数準備
          PROJECT_NAME_ENCODED=$(echo -n "$(System.TeamProject)" | perl -MURI::Escape -ne 'print uri_escape($_)')
          ADO_API_BASE=$(echo "$(System.CollectionUri)${PROJECT_NAME_ENCODED}/_apis/git/repositories/$(Build.Repository.Name)")
          ADO_API_VERSION=$(echo "api-version=7.2-preview.1")

          ######################################################################
          # PRに含まれるファイルを取得
          BRANCH1="$(System.PullRequest.TargetBranch)"
          BRANCH2="$(System.PullRequest.SourceBranch)"         
          BASE_BRANCH=${BRANCH1#refs/heads/}
          TARGET_BRANCH=${BRANCH2#refs/heads/}
          BASE_BRANCH_ENCODED=${BASE_BRANCH//\//%2F}
          TARGET_BRANCH_ENCODED=${TARGET_BRANCH//\//%2F}
          ADO_DIFF_API=$(echo "${ADO_API_BASE}/diffs/commits?baseVersion=${BASE_BRANCH_ENCODED}&targetVersion=${TARGET_BRANCH_ENCODED}&${ADO_API_VERSION}")
          CHANGED_FILES=$(curl "$ADO_DIFF_API" \
            --header "Accept: application/json" \
            --header "Authorization: Bearer $SYSTEM_ACCESSTOKEN")
          CHANGED_FILES_CSV=$(echo $CHANGED_FILES | jq -r '.changes[] | select(.item.isFolder != true and (.item.path | type == "string" and startswith("/app/src") )   ) | .item.path' | sed 's/\/app\///g' | tr '\n' ',')
          IFS=',' read -ra FILES <<< "$CHANGED_FILES_CSV"

          ######################################################################
          # dependency-cruiserで解析した結果をmermaid形式で出力
          npm install
          MERMAID_OUTUPUT=$(npm run depcruise -- "${FILES[@]}")
          MERMAID_OUTUPUT=$(echo "${MERMAID_OUTUPUT}" | tail -n +4)
          MERMAID_CONTENT=$(echo -e ":::mermaid \n\n ${MERMAID_OUTUPUT} \n\n :::")        
          WIKI_CONTENT_JSON=$(jq --arg comment "$MERMAID_CONTENT" '.content = $comment' <<< '{"content": ""}')

          ######################################################################
          # meraidグラフ用のwikiが作成済かどうかをチェック
          bid=$(echo  "$TARGET_BRANCH_ENCODED" | awk -F'%2F' '{print $2}')
          WIKI_PATH="pullrequests%2Fbid%2F${bid}"
          AZURE_URL=$(echo "$(System.CollectionUri)${PROJECT_NAME_ENCODED}")
          WIKI_API_PAGE_URL=$(echo "${AZURE_URL}/_apis/wiki/wikis/%E8%96%AC%E5%91%B3/pages?path=${WIKI_PATH}&api-version=7.1-preview.1")
          auth="Authorization: Bearer $SYSTEM_ACCESSTOKEN"
          ct="Content-Type: application/json"
          EXISTS_CHEKCK_RESPONSE=$(curl -XGET -s -H "$auth" -i "$WIKI_API_PAGE_URL")
          { read -r status_line; read -r etag_line; } < <( echo "$EXISTS_CHEKCK_RESPONSE" | grep -i -P '^(HTTP/|ETag:)' )

          ######################################################################
          if [[ $status_line =~ ^HTTP/[0-9.]+\ 200 ]]; then
              ######################################################################
              # ページが存在する場合は更新
              etag_value=$(echo "$etag_line" | awk '{print $2}' | tr -d '\r')
              ifmatch="If-Match: ${etag_value}"
              response_wiki_page=$(curl -XPUT -s -H "$auth" -H "$ct" -H "$ifmatch" "${WIKI_API_PAGE_URL}&versionDescriptor.version=${BASE_BRANCH_ENCODED}" --data "${WIKI_CONTENT_JSON}")
          else
              ######################################################################
              # ページが存在しない場合は新規作成
              response_wiki_page=$(curl -XPUT -s -H "$auth" -H "$ct" "${WIKI_API_PAGE_URL}&versionDescriptor.version=${BASE_BRANCH_ENCODED}" --data "${WIKI_CONTENT_JSON}")

              ######################################################################
              # 新規作成時のみ、プルリクエストコメントを投稿する
              WIKI_ID=$(echo "$response_wiki_page" | jq -r '.id')
              COMMENT=$(echo -e "[mermaid](${AZURE_URL}/_wiki/wikis/%E8%96%AC%E5%91%B3/${WIKI_ID})")
              ADO_API=$(echo "${ADO_API_BASE}/pullRequests/$(System.PullRequest.PullRequestId)/threads?${ADO_API_VERSION}")
              PR_COMMENT=$(jq --arg comment "$COMMENT" '.comments[0].content = $comment' <<< '{"comments": [{"parentCommentId": 0,"content": "","commentType": 1}],"status": 1}')

              curl "$ADO_API" \
              --header "Content-Type: application/json" \
              --header "Accept: application/json" \
              --header "Authorization: Bearer $SYSTEM_ACCESSTOKEN" \
              --data "$PR_COMMENT" \
              --verbose
          fi
      env:
        SYSTEM_ACCESSTOKEN: $(System.AccessToken)

image.png

ブランチポリシーの設定

パイプラインで使用するSystem.PullRequest.PullRequestIdが、これはブランチポリシーによるパイプラインでのみ値を取得できるため。

image.png

image.png

実行結果

プルリクが書き込まれた。リンクをクリックすると、最初に示した mermaid でのファイルの依存関係を表した図が確認できる。

image.png

メモ

Wiki作成時のレスポンス例

{
  "path": "/pullrequests/bid/474",
  "order": 0,
  "gitItemPath": "/wiki/pullrequests/bid/474.md",
  "subPages": [],
  "url": "https://dev.azure.com/...?pagePath=%2Fpullrequests%2Fbid%2F474",
  "remoteUrl": "https://dev.azure.com/...?pagePath=%2Fpullrequests%2Fbid%2F474",
  "id": 973,
  "content": ":::mermaid \n\n \nflowchart LR\n\nsubgraph 0[\"src\"]\nsubgraph 1[\"pages\"]\n2[\"HollowFluxPage.tsx\"]\nend\nsubgraph 3[\"domain\"]\nsubgraph 4[\"hollow\"]\n5[\"constants.ts\"]\n6[\"cards.json\"]\n7[\"createUdonariumZip.ts\"]\nend\nsubgraph 8[\"udonarium\"]\n9[\"common.ts\"]\nA[\"fileArchiver.ts\"]\nB[\"canvas.ts\"]\nC[\"FileReaderUtil.ts\"]\nD[\"mimeType.ts\"]\nE[\"udonariumZip.ts\"]\nend\nend\nend\n2-->5\n2-->7\n5-->6\n7-->9\n7-->A\n7-->E\n9-->A\nA-->B\nB-->C\nB-->D\nE-->9\nE-->A \n\n :::"
}

参考

dependency-cruiser-report-actionでPRの変更ファイルの依存関係を可視化してコメントする
しばやん雑記 - Azure Pipelines のディレクトリを指す変数はどれを使うべきなのか調べた
Azure DevOps Service REST APIでWikiの更新を試してみたメモ
Azure PipelinesのタスクでPullRequestに対してコメントをつけたい

UseNode@1 - エコシステム v1 タスク Node.js 使用する
既定のリポジトリのアクセス許可
テンプレート使用方法のリファレンス
Diffs - Get
システム変数 (DevOps Services)

【curl】超入門(GET/POST/PUT/DELETEでリクエスト)[LINUX]

0
0
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
0
0