はじめに
Backlogのgitではなく、Code Commitでソースを管理している場合にどのチケットに関するコミットか?分からなくなる
そこでBacklogにコミットのリンクを自動で記載する仕組みを構築したのでその備忘録を残す
全体構成
自動的にコミットハッシュをBacklogチケット(Backlog的にはissue)に紐づけるロジックとしては、
コミットメッセージから正規表現でコミットハッシュを紐づけるべきBacklogのissueを探すキーワードを抽出し、そのキーワードをissueの件名に含むものを探し出しそのissueにコミットハッシュを紐づける
具体的には、
- 開発者のCode CommitへのpushをCloud Watch Eventで捕捉しLambda関数を呼び出す
- Cloud Watch EventのEventオブジェクトの内容にはコミットメッセージがないので、AWS SDK(今回はJavaScript)でCode Commitからコミットメッセージを取得
- BacklogのWeb APIでBacklog上のissueをある条件で絞り込み、コミットメッセージに含まれるキーワードで検索する対象になるissueを取得する
- 取得したissueの中からコミットメッセージのissueとの紐づきを探すためのキーワードが件名に含まれるissueを特定する
- 該当のissueにコミットハッシュを書き込む
Backlogにコメントを記載するLambdaを作成
Lambdaをローカルでもテスト可能に
lambda-local
というパッケージを使うと簡単
Cloud Watch Eventから渡ってくるEventオブジェクトの中身を把握する
Code Commitへのpushの際には、Cloud Watch EventsのreferenceUpdated
というタイプのイベントが発火する
そのEventオブジェクトの中身としては以下
{
"version": "0",
"id": "xxxxxxxxxxxxxxxxxxxxxx",
"detail-type": "CodeCommit Repository State Change",
"source": "aws.codecommit",
"account": "xxxxxxxxxx",
"time": "2021-08-18T07:09:12Z",
"region": "ap-northeast-1",
"resources": [
"arn:aws:codecommit:ap-northeast-1:xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxx"
],
"detail": {
"callerUserArn": "arn:aws:iam::xxxxxxxxxx:user/xxxxxxxxxx",
"commitId": "xxxxxxxxxxxxxxxxxxxxxx",
"event": "referenceUpdated",
"oldCommitId": "xxxxxxxxxxxxxxxxxxxxxx",
"referenceFullName": "refs/heads/main",
"referenceName": "main",
"referenceType": "branch",
"repositoryId": "xxxxxxxxxxxxxxxxxxxxxx",
"repositoryName": "xxxxxxxxxxxxxxxxxxxxxx"
}
}
Code Commitからコミットメッセージを取得
AWS SDK for JavaScript v3を使う(正確にはその中の@aws-sdk/client-codecommitを使う)
@aws-sdk/client-codecommit
にはGetCommitCommandがあり、これを使うとcommitId
とrepositoryName
からコミット情報が取得できる
基本的にAWS SDKを使う際には、認証が必要になりそれはAWS.config
に色々設定をする必要があるが、環境変数にCredentialとなるもの(AWS access key ID、AWS secret access keyなど)を定義しておけば勝手に読み込んでくれるのでその方式を採用する
※Lambdaにdeployした後はLambdaのIAMロール Lambda実行時のAWS_ACCESS_KEY_IDとかは何になる?に書いたように、実行ロールが関係してくるのでそこはまた設定する必要はあるが、今時点では気にしないでOK
・参考:SDK バージョン 3 開発者ガイド 環境変数から Node.js への認証情報のロード
AWS.config の Key | 環境変数の Key | 説明 |
---|---|---|
accessKeyId | AWS_ACCESS_KEY_ID | your AWS access key ID |
secretAccessKey | AWS_SECRET_ACCESS_KEY | your AWS secret access key |
sessionToken | AWS_SESSION_TOKEN | (AWS.Credentials) the optional AWS session token to sign requests with |
コミットハッシュを記載するissueを特定する
Backlogの構成として、
- スペース(spaceId):プロジェクト(projectId)=1:N
- プロジェクト(projectId):課題(issue)=1:N
というような構成になっている
どのプロジェクトのどの課題とCode Commitのコミットハッシュが紐づくのか?が分かっている場合と分かっていない場合とを想定して以下のような実装にする
環境変数BACKLOG_PJ_ID
で対象のプロジェクトの指定を可能に
BACKLOG_PJ_ID
が
- 指定:指定されたプロジェクトのみをコミットハッシュ書き込み対象のissueを検索する対象とする
- 未指定:スペース内の全てのプロジェクトをコミットハッシュ書き込み対象のissueを検索する対象とする
実装としては以下
let pjIds = [];
if (process.env.BACKLOG_PJ_ID) pjIds.push(process.env.BACKLOG_PJ_ID);
else {
const pjs = await axios.get(`/projects?apiKey=${process.env.BACKLOG_API_KEY}`, config);
pjs.data.forEach(pj => pjIds.push(pj.id));
}
・参考:プロジェクト一覧の取得
環境変数BACKLOG_ISSUE_TYPE_NAME
で対象のissue typeを指定可能に
次に、環境変数BACKLOG_PJ_ID
で対象のプロジェクトの指定を可能にで取得したプロジェクトIDを使ってそのプロジェクトにある課題(issue)を取得する
が、この時、BACKLOG_ISSUE_TYPE_NAME
が
- 指定:指定された課題種別のみのissueをコミットハッシュ書き込み対象のissueを検索する対象とする
- 未指定:プロジェクト内の全てのissueをコミットハッシュ書き込み対象のissueを検索する対象とする
実装としては以下
let issues = [];
for (const pjId of pjIds) {
const fectedIssues = await axios.get(`/issues?apiKey=${process.env.BACKLOG_API_KEY}&projectId[]=${pjId}`, config);
if (process.env.BACKLOG_ISSUE_TYPE_NAME) {
fectedIssues.data
.filter(issue => issue.issueType.name === process.env.BACKLOG_ISSUE_TYPE_NAME)
.map(issue => reObj(issue))
.forEach(issue => issues.push(issue));
} else fectedIssues.data.map(issue => reObj(issue)).forEach(issue => issues.push(issue));
}
※今回for-ofを使っているがそれは、forEachは同期関数を期待するから
ちなみに、forEach の中身はwhile
になっている
・参考:課題一覧の取得
コミットハッシュ書き込み対象のissueを特定
Backlogのissueの件名にあるキーワードが含まれているか?でissueを特定すればいいので、実装としては以下
const issueIds = issues
.filter(issue => issue.summary.includes(ticketSearchKey))
.map(issue => issue.id);
コミットハッシュをBacklogのissueに書き込む
複数のissueがヒットする事ももしかしたらBacklogの運用上あり得るかもしれないので実装は以下のようにした
const message = "Commit Message";
const hash = "Code Commit CommitHash";
const link = `[${input.commitId}](https://${process.env.AWS_REGION}.console.aws.amazon.com/codesuite/codecommit/repositories/${input.repositoryName}/commit/${input.commitId}?region=${process.env.AWS_REGION})`;
const data = querystring.stringify({
content: `## ${message}\n${commitMsg}\n## ${hash}\n${link}`
});
let resultMap = {};
for (const [index, issueId] of issueIds.entries()) {
const result = await axios.post(`/issues/${issueId}/comments?apiKey=${process.env.BACKLOG_API_KEY}`, data, config);
resultMap["index"] = index;
resultMap["result"] = {
status: result.status,
statusText: result.statusText,
id: result.data.id,
content: result.data.content
}
}
・参考:課題コメントの追加
まとめると・・・
はじめにのGitHubのリンクのようなソースコードになる
実行結果としては以下のような感じ
おまけ
Cloud Watch Eventの設定方法
以下のように設定する
※「一致したイベントの一部」でJSONPath形式(ex $.detail)でEventオブジェクトの中身の一部のみをLambdaに渡すとかもできるが、今回はやっていない