들어가며
Github Actions를 활용하는 사례들을 보면 대부분 CI/CD에 사용하고 있고 저도 지금까지는 그랬었는데요. 이번 프로젝트에서는 매번 하기 번거로운 코드 리뷰 요청과 태스크 관리에 Github Action을 활용해보았는데 굉장히 편하고 만족도가 높았어서 소개해보려고 합니다.
1. 담당자, 리뷰어 지정하기
이미 Github에 PR이 오픈되었을 때 리뷰어를 자동으로 지정하는 기능이 있지만, 저희 팀에서는 PR이 오픈되었을 때 진행되어야 하는 작업이 리뷰어 지정 외에도 몇가지 더 있었기 때문에 한 곳에서 모두 관리하기 위해서 워크플로우로 작성했습니다.
Github Action에서 제공하는 Toolkit을 이용해서 직접 PR에 지정해주는 방법도 있지만 review-assign-action라는 액션이 이미 마켓플레이스에 배포되어 있었기 때문에 이 액션을 이용해서 담당자와 리뷰어를 지정했습니다.
on:
pull_request:
types: [opened, ready_for_review]
jobs:
assign:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: hkusu/review-assign-action@v1
with:
assignees: ${{ github.actor }}
reviewers: ${{ vars.REVIEWERS }}
YAML
복사
PR의 담당자는 github.actor로 이 워크플로를 트리거한 유저, 즉 이 PR을 오픈한 유저를 할당했고 리뷰어는 따로 정의한 워크플로 변수에서 후보를 가져오도록 설정했습니다.
yoouyeon, ...
Plain Text
복사
저희 팀은 팀원이 2명뿐이었기 때문에 리뷰어의 수를 따로 지정해주지 않았지만 max-num-of-reviewers 설정을 이용해서 리뷰어의 수를 지정할수도 있습니다.
2. 라벨 복사하기
저희 팀에서는 이슈와 PR을 분류할 때 라벨을 적극적으로 활용했습니다. 또한 이슈 단위로 작업하여 PR을 생성했기 때문에 대부분의 경우 연결된 이슈와 PR의 라벨이 동일하다는 특징이 있었고요. PR을 생성할 때 마다 연결된 이슈의 PR을 옮기는 작업이 번거로웠기 때문에 이를 워크플로우로 자동화했습니다.
기존에 라벨을 추가해주는 Pull Request Labeler 라는 액션이 있지만 이 액션은 특정 패턴에 맞는 PR에 라벨을 붙여주는 기능만 제공하고 있기 때문에 저희와 맞지 않아서 커스텀 액션을 만들어서 사용했습니다.
액션은 쉘 스크립트로도 정의할 수 있지만 저희는 팀원 모두가 익숙한 언어인 자바스크립트로 액션을 정의했습니다. PR이 발생한 브랜치에서 연결된 이슈를 찾고 (브랜치 이름에는 <type>/#<issue_number>-<branch_name> 의 규칙이 있습니다.) 해당 이슈에 달린 라벨과 기존 PR의 라벨을 합쳐서 새롭게 라벨을 추가해주는 로직입니다.
import * as core from "@actions/core";
import * as github from "@actions/github";
const run = async () => {
try {
const token = core.getInput("token");
const octokit = github.getOctokit(token);
const { context } = github;
const { prNumber, branchName, owner, repo } = getPRContext(context);
// step 1. 이슈 라벨 추출
const issueNumber = getIssueNumberFromBranch();
const issueLabels = await getIssueLabels(octokit, owner, repo, issueNumber);
// step 2. PR에 라벨 추가
await addLabelsToPR(octokit, owner, repo, prNumber, issueLabels);
core.info("✅ PR에 라벨이 추가되었습니다.");
} catch (error) {
core.setFailed(`❌ 오류가 발생했습니다: ${error.message}`);
}
};
const addLabelsToPR = async (octokit, owner, repo, prNumber, labels) => {
// step 1. 기존 PR 라벨 가져오기
const prData = await octokit.rest.pulls.get({
owner,
repo,
pull_number: prNumber,
});
const prLabels = prData.data.labels.map((label) => label.name);
// step 2. 기존 PR 라벨과 이슈 라벨을 합쳐서 새로운 라벨 목록 생성
const newLabels = [...new Set([...prLabels, ...labels])];
// step 3. PR에 라벨 추가
await octokit.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: newLabels,
});
};
JavaScript
복사
이렇게 간단하지만 번거로운 작업을 워크플로우로 자동화하니 PR 내용 자체에 더 집중할 수 있었고 PR에서의 라벨 규칙도 직접 추가할 때보다 더 잘 지킬 수 있다는 효과가 있었습니다.
3. Zenhub 파이프라인과 연동하기
저희 팀에서는 이슈 관리를 Zenhub로 하고 있는데요. Zenhub에서 제공하고 있는 Automation은 이슈나 PR이 만들어지면 workspace에 추가하고 닫히면 close 파이프라인으로 이동시키는 기능만 제공하고 특정 이벤트에 따라서 파이프라인을 이동시키는 기능은 따로 제공하지 않고 있었습니다.
저희 팀에는 현재 작업중인 이슈는 In Progress 파이프라인에 두고 리뷰를 기다리는 PR과 연결된 이슈는 Review 파이프라인에 두는 규칙이 있었습니다. 하지만 작업을 시작할 때나, PR을 열 때 마다 직접 파이프라인을 옮겨주는 과정은 번거롭고 시간이 지날수록 놓치게 되는 문제가 있었습니다. 따라서 이슈를 특정 파이프라인으로 이동시켜주는 액션을 만들었습니다.
Zenhub에서 제공하고 있는 공식 API는 GraphQL API 인데요. 저희 팀 모두 GraphQL에 익숙하지 않았고 액션을 만들기 위해서 GraphQL을 공부하기에는 시간이 충분하지 않았기 때문에 일단은 Deprecated 상태인 REST API로 개발했습니다. 파이프라인 사이에 이슈를 이동하는 API를 이용해서 액션의 Input으로 전달된 목적지 파이프라인으로 이슈를 이동하는 간단한 로직입니다.
import * as core from "@actions/core";
import * as github from "@actions/github";
import axios from "axios";
/** 메인 액션 함수 */
const run = async () => {
try {
const { token, zenhubToken, workspaceId, pipelineId } = getActionInputs();
// step 0. repo id 확인
const octokit = github.getOctokit(token);
const { owner, repo } = github.context.repo;
const repoId = await getRepoId(octokit, owner, repo);
// step 1. 이슈 번호 추출
const issueNumber = getIssueNumberFromBranch();
// step 2. 이슈를 이동
const requestUrl = `https://api.zenhub.com/p2/workspaces/${workspaceId}/repositories/${repoId}/issues/${issueNumber}/moves`;
const requestBody = { pipeline_id: pipelineId, position: "bottom" };
await axios.post(requestUrl, requestBody, {
headers: {
"X-Authentication-Token": zenhubToken,
"Content-Type": "application/json",
},
});
core.info(`✅ 이슈 #${issueNumber}를 이동했습니다.`);
} catch (error) {
core.setFailed(`❌ 오류가 발생했습니다: ${error.message}`);
}
};
JavaScript
복사
이렇게 만든 액션은 다른 워크플로우에서 가져다 사용할 수 있습니다. 예를 들어, 브랜치가 생성되었을 때 연결된 이슈를 In Progress로 옮기는 워크플로우는 아래와 같습니다.
name: Move Issue to In Progress Pipeline
on:
create:
branches:
- "**"
jobs:
move-issue-to-in-progress:
runs-on: ubuntu-latest
steps:
- name: Move to In Progress Pipeline
uses: ./.github/actions/zenhub-move-issue
with:
token: ${{ secrets.GITHUB_TOKEN }}
zenhub_token: ${{ secrets.ZENHUB_TOKEN }}
workspace_id: ${{ vars.ZENHUB_WORKSPACE_ID }}
pipeline_id: ${{ vars.ZENHUB_IN_PROGRESS_PIPELINE_ID }}
YAML
복사
이 액션을 위에서 예로 든 In Progress 파이프라인으로 이동하는 워크플로우 외에도 PR이 열릴 때 실행되는 워크플로우에도 추가해서 열린 PR과 연결된 이슈를 Review 파이프라인으로 이동시키는 작업을 자동화했습니다. 번거롭고 놓치기 쉬운 작업을 자동화하니 PR 내용 자체에만 집중할 수 있을 뿐 아니라 Zenhub의 상황과 실제 Github의 상황이 잘 동기화되어서 Zenhub만으로도 태스크 상황을 명확히 확인할 수 있다는 장점이 있었습니다.
끝
요즘 번거로운 일들을 단순히 불편한 것으로 넘기지 않고 기술적으로 해결하거나 규칙 개선을 통해 해결하는 방법에 대해 관심이 많았는데요. 이번에 Github Actions 워크플로우를 활용해서 번거롭지만 놓치면 안되는 작업들을 자동화하는 식으로 개발 환경을 개선해보고 팀원에게 좋은 피드백을 받기도 해서 의미 있는 경험이 되었습니다. 저처럼 개발 환경 개선에 관심이 있는 분이라면 Github Actions를 활용해 보시기를 추천드립니다. 