Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

GitHub Actions의 스킵된 Required 잡 실행하기

GitHub에서 소스 코드를 관리하면 자연히 GitHub Actions를 통해서 CI를 구성해서 테스트나 린팅이나 다양한 검사를 자동화해서 Pull Request가 올라왔을 때 문제가 없는지 검사하는 게 일반적이다. 이 검사 상태가 Pull Request에 표시되긴 하지만 머지하기 전에 반드시 통과해야 하는 GitHub Actions를 지정할 수 있다. 이는 Pull Request에서 Required로 표시되어서 머지 전에 반드시 해당 검사가 통과해야만 머지할 수 있도록 설정할 수 있다.

작업을 하다가 Required 잡의 등록과 워크플로우 필터링과 섞이면서 일부 Pull Request의 상태 검사가 제대로 되지 않는 문제를 만났다.

Required status checks

Pull Request를 써봤다면 다들 Status checks를 본 적이 있겠지만 설명을 위해 다음과 같은 GitHub Actions 워크플로우가 정의되어 있다고 해보자.

# test.yaml
name: Build & Test
on:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      # 빌드 스텝
  Test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      # 테스트 스텝

이제 Pull Request를 열면 하단에 다음과 같이 워크플로우가 실행된 경과가 나온다. 여기서는 다 통과했기 때문에 모든 검사가 성공했음을 보여준다.

Pull Request에 표시된 status checks

위 화면에 나와 있듯이 브랜치를 보호하는 정책이 따로 설정되어 있지 않는데 Branch protection rules를 설정할 수 있다.

브랜치 프로텍션 규칙 설정

이 설정에서 꼭 Pull Request로만 머지해야한다거나 커밋 서명을 요구한다거나 등 다양한 보고 정책을 설정할 수 있다. 여기서 볼 설정은 "Require status checks to pass before merging"인데 이를 체크하면 저장된 Pull Request의 잡을 선택할 수 있다.

required 상태 겁사에 등록한 GitHub Actions 잡

앞에서 Build & Test이라는 워크플로우를 추가했는데 그 안에는 buildTest라는 잡이 있다. 이 의미는 워크플로우 단위로 설정하는 것이 아닌 잡 단위로 등록한다는 의미이다. 여기서는 GitHub Actions로만 했는데 Status checks라는게 꼭 Actions으로만 정의할 수 있는 건 아니다.

규칙을 저장하고 Pull Request를 보면 다음과 같이 해당 잡에 Required가 표시된 것을 볼 수 있다.

Pull Request의 status checks에 표시된 Required

이 말은 Pull Request를 머지하라면 반드시 Required는 Pass 상태가 되어야 한다는 의미이다.

Pull Request의 status checks에 등록된 Required 잡이 실패해서 막힌 머지

일부러 해당 잡을 실패하게 만들면 머지를 할 수 없는 상태가 된다. 저장소에 권한이 있으면 건너뛰고 머지할 수 있기는 하다.

Required 잡을 특정 경로에만 트리거하게 설정

이제 저장소에 문서 관리를 위해 docs같은 폴더를 추가해서 문서 파일을 모아놓는다고 해보자. 이때 문서만 수정할 때 소스 코드에 대해서만 실행하는 빌드, 테스트를 매번 실행하는 것은 시간도 걸리고 리소스도 낭비되므로 다음과 같이 paths-ignore를 지정해서 docs 폴더를 제외하고 다른 곳의 변경이 발생했을 때만 워크플로우가 실행되도록 설정할 수 있다.

# test.yaml
name: Build & Test
on:
  pull_request:
    paths-ignore:
      - 'docs/**'

jobs:
  build:
    # 생략
  Test:
    # 생략

여기까지는 자연스러웠지만 문제가 생겼다. 문서만 수정하는 Pull Request를 올린 경우에는 다음과 같이 워크플로우가 실행되지 않아서 Required 잡이 Test가 실행되지 않아 팬딩 상태로 머무르게 된다.

docs를 수정한 Pull Request의 status checks가 보류됨

Pull Request를 머지하려면 Test가 반드시 성공해야 하는데 실행조차 되지 않았으므로 계속해서 기다리는 상태가 된다. Required 설정을 푸는 방법도 있었지만, 저장소의 주목적은 소스 코드이므로 문서 때문에 설정했던 Required 설정을 풀고 싶지는 않았다.

필터링으로 제외된 Required 잡 처리하기

해결 방법을 찾다 보니 다행히 GitHub 문서에 방법이 정리되어 있었다. 이건 path 필터링뿐 아니라 브랜치 필터링이나 커밋메시지로 건너뛰었을 때도 같은 상황이 된다.

이 방법은 같은 잡을 반대 조건의 필터를 등록해서 팬딩되는 상황이 발생하지 않게 하는 것이다.아까 워크플로우가 test.yaml 파일이었으니까 test-skipped.yaml 파일을 만들어서 다음과 같이 작성한다.

# test-skipped.yaml
name: Skipped Required Test
on:
  pull_request:
    paths:
      - 'docs/**'

jobs:
  Test:
    runs-on: ubuntu-latest
    steps:
      - run: 'echo "No Test Required"'

아까는 'docs/**'에 대해 paths-ignore가 걸려있었지만, 이번에는 paths로 필터링을 걸었다. 즉, 어디를 수정하든 두 워크플로우중 최소 하나는 반드시 실행되게 되어 있다. 워크플로우의 이름은 중요하지 않지만, 여기에는 아까와 동일한 Test라는 잡을 정의하고 여기서는 그냥 Required 상태를 Pass로 만드는 게 목적이므로 단순하게 메시지만 출력하게 했다.

이제 docs 폴더의 파일만 수정하는 Pull Request를 올리면 다음과 같이 Requied 검사가 통과한 걸 볼 수 있다.

폴백으로 등록된 잡이 실행되어 Required 검사가 통과됨

아까 브랜치 보호 정책을 등록할 때 워크플로우 단위가 아니라 잡 단위로 등록했다. 그래서 여기서는 아까 Build & Test 워크플로우의 Test 잡이 아니라 Skipped Required Test 워크플로우의 Test 잡이지만 Status Checks는 Tests라는 잡을 기준으로 검사하므로 Required 상태가 보류되는 걸 해결할 수 있다.

Test 잡이 2개 실행됨

수정을 하다보면 docs 폴더 내의 파일과 밖의 파일을 같이 수정하게 되는 경우도 많기 때문에 둘 다 혼합되어 있다면 위처럼 2개의 워크플로우가 다 실행되기는 한데 한쪽은 절대 실패하지 않는 잡이므로 크게 문제는 되지 않는다.

2023/05/05 20:09 2023/05/05 20:09