github action을 통해 PR 리뷰어 랜덤으로 할당하기

jh·2024년 2월 23일
0

참고한 글
토스 : Github Actions로 개선하는 코드문화
카카오 : JS Actions 만들기

이 두 글이 제 글보다 훨씬 퀄리티있고 도움이 많이 될것이기 때문에 무조건 먼저 보는걸 추천합니다...

  • github actions 에 대한 자세한 설명은 다루지 않습니다(왜냐하면 저도 모르기 때문에..)

이번에 새롭게 시작되는 프로젝트에 대한 컨벤션을 정하면서, 팀원 중 2명 이상이 Approve할 시 merge할 수 있게 하도록 PR 컨벤션을 정했다.
그 당시에는 그렇군.. 하고 넘어갔지만, 첫 PR을 올리면서 든 생각은

reviewer는 누구로 하지..? 였다

  • CODEOWNER 를 통해 모든 팀원 다 reviewer로 등록
    - 가장 쉬운 방법이지만, 리뷰에 대한 책임소재가 불분명 해질 것 같았음
  • 매번 2명을 손수 뽑아서 reviewer로 등록
    - 2명을 reviewer 탭에서 클릭하는 것도 귀찮은데, 누굴 뽑을지까지 생각하긴 너무 귀찮을 것 같음

그래서 랜덤으로 리뷰어를 지정할 수 있는 기능이 있으면 좋겠다는 생각이 들었다

Review Assign Action

market에 검색하면 가장 먼저 나오는 action으로, 정말 간단하게 reviewers와 assignee까지 지정할 수 있다

하지만 쓰다보니 단점이 몇가지 생겼는데

  • 현재 PR을 생성한 사람을 reviewer로 뽑는 경우가 있다
  • reviewer를 랜덤하게 뽑는 로직을 커스텀할 수가 없다

그러다 최상단에 있는 토스 블로그의 포스트를 발견하고, 내가 직접 커스텀 해보기로 했다

JS로 만든 actions 실행시키기

actions는 보통 .yml 파일로 작성된다.
사실 name, on, jobs 같이 직관적인 네이밍으로 구분되기 때문에 사실 이 부분은 이해하기 어렵지 않지만,
특정 로직을 실행시켜야 하는 부분에서 기존에 익숙한 JS와는 다른 문법을 사용해야 하기 때문에 이 부분이 조금 어렵다

그래서, 해당 로직을 실행시키는 부분에서 JS코드를 쓸 수 있다면,
여기에서 더 나아가서 JS 파일을 실행시킬 수 있다면 유지보수 측면에서도 더 좋을 것 같아서 시도해 보았다.

폴더 구조

actions의 경우에도 폴더를 나누어서 관리하는 게 가능하다.
나는 .github 폴더에 workflowsactions 라는 폴더로 나누어 관리하는데

워크플로는 하나 이상의 작업을 실행하는 구성 가능한 자동화된 프로세스입니다. 워크플로는 리포지토리에 체크 인된 YAML 파일에서 정의되며, 리포지토리의 이벤트로 트리거될 때 실행되거나 수동으로 또는 정의된 일정에 따라 트리거될 수 있습니다.

workflows 안에 있는 파일들은 특정 조건이 되었을 때(on), 해당 작업(jobs)을 수행해주는 역할을 한다

actions 안에 있는 파일들은 workflows가 실행하는 작업이 정의되어 있다고 보면 된다

//.github/workflows/deploy/yml
name: deploy

on:
  pull_request:
    types: [opened, ready_for_review]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache node modules
        uses: actions/cache@v3
        id: npm-cache
        with:
          path: "**/node_modules"
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: |
          echo 'npm cache existed'
          npm ci
          
         //여기까지는 공통으로 필요한 작업 실행

      - name: Assign reviewers randomly
        uses: ./.github/actions/reviewers
        with:
          github_token: ${{secrets.token}}
          reviewers: ${{vars.reviewers}}
//.github/actions/reviews/action.yml
name: "reviewers"

inputs:
  reviewers:
    required: true
  github_token:
    required: true

runs:
  using: "node16"
  main: "action.js"
  //action.js는 현재 파일과 같은 폴더에 위치

해당 파일을 보면, runs 에서 action.js 를 실행해준다는 걸 알 수 있다

그럼 이제 action.js에서 리뷰어를 랜덤 지정해주는 로직만 만들면 된다.

하지만 action.js 파일에서는 현재 아무것도 알 수가 없다. 어떤 PR인지, 누가 작성했는지 등등 알 수가 없기 때문에, 이를 알기 위해 github에서 제공하는 toolkit을 사용해야 한다.

@actions/core - input,output,log,변수,secret 등등을 가져올 수 있음
@actions/github - github api를 이용하여 github과 관련된 정보를 가져올 수 있음

보통 이 두가지가 가장 많이 쓰이는 것 같다

import { getInput, setFailed } from "@actions/core"
import { context, getOctokit } from "@actions/github"

try {
  const candidates = getInput("reviewers").split(", ")
  const reviewers = selectRandomReviewer(candidates)
  const token = getInput("github_token")
  const octokit = getOctokit(token)

  const { owner, repo } = context.repo
  const pull_number = context.issue.number

  await octokit.rest.pulls.requestReviewers({
    owner,
    repo,
    pull_number,
    reviewers: reviewers,
  })
} catch (error) {
  setFailed(error.message)
}

function selectRandomReviewer(reviewers) {
  const requiredReviewer = 2
  const creator = context.payload.pull_request.user.login
  const candidates = reviewers.filter((reviewer) => reviewer !== creator)

  const result = []
  for (let i = 0; i < requiredReviewer; i++) {
    const { length } = candidates
    if (length === 0) {
      break
    }

    const randomIndex = Math.floor(Math.random() * length)
    result.push(...candidates.splice(randomIndex, 1))
  }
  return result
}

간단하게 설명하면

  1. reviewers 라는 input 값을 읽어온다
  2. 변수로 지정해둔 리뷰어 list를 불러와서 selectRandomReviewer 함수에서 랜덤으로 2명을 선택한다
  3. 뽑힌 사람들을 리뷰어로 지정한다

이 과정에 필요한 권한이나, PR 정보를 불러오는 걸 설치한 두 라이브러리에서 해 준다

0개의 댓글