Github Actions를 이용해 CI 구성하기 (Java, Gradle, Jacoco)

Shef·2022년 8월 4일
4

소개


Github Actions는 Github에서 제공하는 CI/CD 툴입니다. Github이 서버를 제공해주기 때문에 따로 CI/CD를 위한 서버를 구성해주지 않아도 되어 부담이 적고 간편하게 설정할 수 있다는 장점이 있습니다.

Github Repository에 특정 event가 일어났을 때 미리 정의한 Workflow들이 Github이 제공해주는 서버에서 실행됩니다. 저희는 Workflow에 어떤 event에 실행될 것인지, 어떤 동작들을 할 것인지, 어떤 결과물을 만들어낼 것인지 등을 적어주면 됩니다.

핵심 컴포넌트


Workflow

Workflow는 하나 이상의 작업을 실행시키는 자동화된 프로세스입니다. 앞에서 말했듯이 이벤트가 발생했을 때 자동으로 실행되며 그외에 수동으로 작동시킬 수도 있고 특정 시간마다 작동하게 할 수도 있습니다.

Event

이벤트는 Workflow를 trigger하는 repository에서 일어나는 특정한 활동들을 말합니다. 예를 들면 push, pull request 등이 있습니다. 더 많은 Event는 여기를 참고해주시면 됩니다.

Job

Job은 Workflow에서 실행하는 일련의 작업들입니다. 하나의 Job은 하나의 Runner에서 일어나며 여러개의 Step을 갖고 있습니다. 각 Step은 shell script 또는 Action이 될 수 있습니다. 각각의 Job들은 병렬적으로 실행되지만 순서대로 작동해야 하는 경우 순서관계를 정의해줄 수 있습니다.

Action

Action은 복잡하고 반복적인 task들을 모아놓은 custom 가능한 application입니다. Input을 받고 Output을 낼 수 있는 미리 만들어놓은 Job들이라고 생각하면 됩니다. 직접 만들 수도 있고 다른 사람들이 만들어 놓은 Action을 사용할 수도 있습니다.

Runner

Runner는 Workflow를 실행하는 서버입니다. Github은 Ubuntu나 Windows 또는 macOS 운영체제를 갖고 있으며, 항상 사전에 설치된 여러 소프트웨어 이외에는 아무것도 설치되어 있지 않은 서버를 제공합니다. 어떤 소프트웨어들이 미리 설치되어 있는지 확인하고 싶다면 여기를 참고하면 됩니다.

시작하기


Github Actions는 Workflow를 정의하기 위해 yml 문법을 사용합니다. Github Actions를 사용하려면 ci.yml과 같이 Workflow설정 파일을 만들고 .github/workflows 디렉토리를 만들어 저장하면 됩니다.

요구사항


  1. main 또는 develop branch로 push가 되거나 pull request가 요청될 때 Workflow를 실행한다.
  2. ubuntu 최신 stable 버전과 JDK는 Eclipse Temurin 11을 이용한다.
  3. Gradle을 이용해 빌드하고 CI 속도를 높히기 위하여 의존하는 라이브러리들을 캐싱한다.
  4. HTML Jacoco Report를 업로드한다.
  5. 테스트가 실패하거나 Jacoco의 최소 커버리지를 충족하지 못하여 빌드가 실패하는 경우 push 또는 pull request한 사람의 이메일로 알림을 준다.

최종 Workflow


name: 'CI'

on:
  push: 
		branches:
			- 'main'
			- 'develop'
  pull_request: 
		branches:
			- 'main'
			- 'develop'

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: 11
          distribution: 'temurin'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
        with:
          arguments: build
          cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}

      - name: Upload Jacoco Report
        if: ${{ failure() }}
        uses: actions/upload-artifact@v3
        with:
          name: jacoco-report
          path: build/reports/jacoco/test/html

1. main 또는 develop branch로 push가 되거나 pull request가 요청될 때 Workflow를 실행한다.

name: 'CI' # workflow의 이름

on: # event를 정의 하는 곳 
  push: 
		branches:
			- 'main' # main에 push할 때
			- 'develop' # develop에 push할 때
  pull_request: 
		branches:
			- 'main'
			- 'develop'

2. ubuntu 최신 stable 버전과 JDK는 Eclipse Temurin 11을 이용한다.

jobs: # job들을 정의하는 곳
  ci: # job의 id
    runs-on: ubuntu-latest # 이 job을 수행하는 OS ex. windows-latest , macos-12

사용 가능한 OS들의 목록과 label은 여기를 확인하시면 됩니다.

 steps: # job에서 수행하는 step들을 정의하는 곳
   - uses: actions/checkout@v3 # Repository로부터 CI 수행 서버로 코드를 내려받는 Action

   - name: Set up JDK 11 # step의 이름
     uses: actions/setup-java@v3 # jdk를 다운 받고 캐싱해주는 Action
     with: # Action에 전달하는 input을 정의하는 곳
       java-version: 11 
       distribution: 'temurin' # jdk를 제공하는 vender사 이름 ex. zulu, adopt, microsoft

Action 사용법

Action을 실행할 때는 uses 키워드를 사용하며 {소유자}/{저장소명}@{참조자}의 형태로 사용할 수 있습니다. 참조자는 보통 Action의 버전을 구분하기 위해 사용되며 위의 예시에서 v3 참조자는 Action의 버전 태그를 의미합니다. 참조자에는 Action의 version과 관련된 SHA 값이나 branch를 적어줄 수도 있습니다. 자세한 내용은 여기를 확인하시면 됩니다.

actions/setup-java

설정한 java -version과 vender사에 맞는 jdk를 다운받아줍니다. 또 Github 캐시에 캐싱하여 이후에는 더 빠르게 다운받을 수 있도록 해줍니다. 더 많은 벤더사는 여기서 확인할 수 있습니다.

3. Gradle을 이용해 빌드하고 CI 속도를 높히기 위하여 의존하는 라이브러리들을 캐싱한다.

캐싱하는 방법

Github은 Job을 매 번 새로운 서버에서 실행합니다. 따라서 매 Job마다 서버에 프로젝트에서 의존하는 라이브러리들을 새로 받아주어야 합니다. 이는 매우 비효율적이기 때문에 바뀌지 않는 라이브러리들은 Github에서 제공하는 cache에 보관하고 재사용하는 것이 CI 성능에 좋습니다.

actions/cache Action을 이용하면 캐싱할 수 있습니다.

Gradle을 이용하는 Java 프로젝트는 다음과 같이 캐싱할 수 있습니다.

- uses: actions/cache@v3
  with:
    path: |  
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
  • path : 캐싱할 파일이 있는 경로임과 동시에 캐싱했던 파일을 다시 복구할 경로입니다.
  • key : 이 key와 함께 파일을 캐싱합니다. 이 key를 통해 캐싱서버로부터 파일을 복구합니다.
  • ${{ }} : expression을 적는 곳으로 expression은 변수나 함수등이 있습니다.
  • runner.os : runner라는 context에 있는 os라는 프로퍼티입니다. 현재 job을 실행하는 서버의 os를 string으로 반환합니다.
  • hashFiles(path) : path를 파라미터로 받고 이 경로에 존재하는 파일들을 이용해 하나의 해시를 반환하는 함수입니다. gradle 설정에 변화가 생겼는지 안 생겼는지를 검증하기 위해 이 해시를 추가해줍니다. 만약 변화가 생긴다면 해시가 바뀌어 새로운 key로 새롭게 캐시합니다. 위 key는 다음과 같이 변환되어 actions/cache의 input으로 전달됩니다. key : Linux-gradle-3572877ee653b1cd606c473692133d05

gradle/gradle-build-action을 이용하기

gradle/gradle-build-action 이 Action은 gradle에서 제공하는 Action으로 Gradle을 사용해서 build하는데 도움을 주는 Action입니다. 이 Action은 build 이외에도 위에서 설명한 actions/cache를 이용하여 더 효율적이고 정교한 캐싱을 제공합니다. 따라서 저는 actions/cache를 직접 사용하지 않고 이 Action을 이용하였습니다.

- name: Grant execute permission for gradlew
	run: chmod +x gradlew # gradlew를 실행할 수 있는 권한을 추가(리눅스의 명령어)

- name: Setup Gradle
  uses: gradle/gradle-build-action@v2
  with:
    arguments: build 
    cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
  • arguments : gradle에 제공하는 인자입니다. 기본적으로 gradle wrapper를 이용해서 build를 수행하므로 arguments: build는 다음 명령과 같습니다. ./gradlew build
  • cache-read-only: true일 경우 cache를 읽기만 하고 cache에 저장하지 않고 false일 경우는 읽기와 저장 모두 수행합니다. 위 cache-read-only는 main과 develop 브랜치일 때만 cache-read-only가 false되게 구성하였습니다. main 또는 develop 브랜치에서 push되거나 pull request될 때만 캐시에 저장되고 다른 브랜치는 캐시를 읽기만 합니다.

어떤 key를 사용하여 어떤 파일들이 캐시되거나 복구되었는지는 Actions 탭에 Summary에서 확인하실 수 있습니다.

4. html Jacoco Report를 업로드한다.

- name: Upload Jacoco Report
  if: ${{ failure() }} 
  uses: actions/upload-artifact@v3
  with:
    name: jacoco-report # 결과물의 이름
    path: build/reports/jacoco/test/html # upload하고자 하는 파일 또는 디렉토리 경로
  • if: if: 이후에 true가 오면 이 step을 실행하고 false가 오면 이 step을 실행하지 않습니다.
  • failure() :는 Status check functions중 하나입니다. 앞 step중 하나라도 실패하면 true를 반환합니다. 앞에 build 단계에서는 자동으로 test가 실행됩니다. 여기서는 앞에서 테스트가 실패하거나 커버리지 미충족으로 빌드가 실패할 때 Jacoco Report를 업로드하도록 이 구문을 추가했습니다. 더 많은 Status check functions은 이 곳에서 보실 수 있습니다.

actions/upload-artifact

Workflow에서 나온 결과물을 저장하거나 다른 job들과 공유하고 싶을 때 사용하는 Action입니다.

위 Action과 input을 사용하면 jacoco-report라는 이름으로 build/repots/jacoco/test/html을 저장합니다. 저장된 결과물은 마찬가지로 Actions 탭에 Summary에서 확인하실 수 있습니다.

5. 테스트가 실패하거나 Jacoco의 최소 커버리지를 충족하지 못하여 빌드가 실패하는 경우 push 또는 pull request한 사람의 이메일로 알림을 준다.

사실 이 부분은 이미 Github Actions에서 제공하고 있습니다. Settings에 Notifications 탭에 들어가보면 기본적으로 workflow가 실패하는 경우에 Email로 알림이 오도록 자동으로 설정되어 있는 것을 확인할 수 있습니다. workflow가 실패하는 경우 Web에 체크하면 Github 알림으로도 알림을 받을 수 있습니다.

알림은 다음과 같이 옵니다.

0개의 댓글