본문 바로가기

Android/CI CD

[Github Actions] Github Actions를 사용해보자 - 5. 추가 기능

728x90

학습하면서 작성하고 있어 정확하지 않은 내용이 있을 수 있습니다.

틀린 부분이 있다면 언제든 지적해주시면 감사하겠습니다.


본 게시글은 이전 게시글에 이어서 작성된 부분입니다.

2022.03.23 - [Android/CI CD] - [Github Actions] Github Actions를 사용해보자 - 1. 기본 개념 정리

2022.03.24 - [Android/CI CD] - [Github Actions] Github Actions를 사용해보자 - 2. 기본 Setting

2022.03.26 - [Android/CI CD] - [Github Actions] Github Actions를 사용해보자 - 3. APK 생성 및 업로드

2022.03.28 - [Android/CI CD] - [Github Actions] Github Actions를 사용해보자 - 4. Slack 연동

 

Slack연동에 이어서, 추가적으로 기능을 추가하여 확장할 수 있는 부분에 대하여 작성해 볼 예정이다.


우선 처음으로 추가해 볼 기능은

schedule을 트리거로 사용하여 주기적인 배포를 진행하는 방법이다.

해당 방법은 아주 간단하게 추가가 가능하다.

기존에 트리거를 사용했던 부분은 다음과 같다.

 

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

 

main브랜치에 push, PR 이벤트가 발생하면 해당 workflow가 실행되게 된다.

그렇다면, 주기적으로 실행시키기 위해서는 트리거에 조건을 시간에 따른 조건을 추가해주면 되는 것이다.

 

schedule:
  - cron: '0 0 * * 1-5'

 

우선 해당 조건은 평일(월 ~ 금) UTC 기준 0시에 실행시킨다는 트리거가 된다.

UTC기준이기 때문에, 한국 시간으로 생각하면 9시간을 더해야 하므로, 

한국시간 기준, 월~금요일 오전 9시에 해당 트리거가 실행된다. 라는 의미가 된다.

 

cron으로 선언되는 5개의 변수 값은 다음과 같이 사용된다.

 

minute hour day(month) month day(week)
0~59 / 0~23 / 1~31 / 1~12 / 0~6

 

cron에 대한 정보는 이곳에서 확인하면 된다.

 

schedule에 대한 깃허브의 예제를 확인해 보자.

 

on:
  schedule:
    - cron: '30 5 * * 1,3'
    - cron: '30 5 * * 2,4'

jobs:
  test_schedule:
    runs-on: ubuntu-latest
    steps:
      - name: Not on Monday or Wednesday
        if: github.event.schedule != '30 5 * * 1,3'
        run: echo "This step will be skipped on Monday and Wednesday"
      - name: Every time
        run: echo "This step will always run"

 

schedule로 선언하는 cron 조건은 한개만 작성할 수 있는 것이 아닌, 여러개를 선언하여 사용할 수 있다.

또한, 위에서 설명한 cron에 사용되는 변수의 값들은 콤마(,)를 사용하여 구분지어서 여러개를 선언하여 쓸 수 있다.

 

예제를 보면 day(week)부분에 1,3 과 같이 작성되어 있는데 이는 1,3에 해당하는 요일인 월,수요일에 동작한다는 것이 된다.

 

on:
  schedule:
    # * is a special character in YAML so you have to quote this string
    - cron:  '30 5,17 * * *'

 

이처럼 시간에 관련된 부분도 콤마를 통해 선언할 수 있는데, 위의 cron이 뜻하는 바는 매일 5:30, 17:30 UTC에 workflow를 실행시킨다는 의미가 된다.

 

다시 돌아가서 필자가 작성한 코드는 평일 오전 9시에 실행되게 되는데,

workflow를 확인해보면 다음과 같이 나오게 된다.

 

 

기존에는 커밋을 하여 이벤트가 발생해야 진행됬기 때문에 커밋 내용과 브랜치가 나오고 있지만,

위의 스케쥴을 사용한 트리거로 실행이 된다면 yml에서 설정한 이름이 나오고, 브랜치가 나오는 부분은 비어있는 것으로 확인할 수 있다.


다음으로는,

파이프라인을 사용하여 필요한 job들을 순서에 맞춰서 진행되게 하는 방법이다.

우선, 필자의 경우 많은 액션을 추가해두지 않았기 때문에 이렇게까지 할 필요는 없는 부분이지만

Test가 진행되어야 하고, 다양한 액션이 추가되는 경우 하나의 job으로만 선언하면 제한적인 부분이 많이 생기게 된다.

 

어떠한 Job의 성공, 실패 여부에 따라서 다른 동작을 해주어야 하는 경우가 있을 수 있고, job이 실패해도 계속해서 step을 진행해야 하는 경우 등의 상황이 생길 수 있다.

물론, 하나의 job으로도 컨트롤이 가능한 부분이기는 하지만, 직관적으로 해당 Job에 대한 흐름을 보는 것에는 문제가 있을 수 있다.

이 때, job을 구분하여 선언하고 순서에 맞춰서 실행 되는것을 보장해주기 위해서는 파이프라인을 사용하면 된다.

 

 

우선,

기존에 선언했던 yml 파일을 복사하여 Android CI - Multi 라는 이름으로 yml파일을 만들어서 사용하였다.

 

 

Step에 대한 항목은 기존의 코드와 같으니 생략하도록 하겠다.

 

지금까지의 코드와 다른점은 2가지 존재한다.

 

  • Job이 1개가 아니다.
  • needs라는 키워드를 사용한다.

키워드가 워낙 직관적이기 때문에 이해하기는 쉬울 것이다.

needs가 포함되어 있는 경우, 해당 job이 실행되기 위해서 선행되어야 하는 job을 선언해 두면 되는 것이다.

 

즉, slack-msg는 needs: build로 되어있기 때문에 build로 선언한 job이 실행되고, 성공하게 되면 해당 job이 실행될 수 있는 것이다.

 

위와 같이 선언을 한 후에 workflow를 확인해보면 다음과 같이 나오게 된다.

 

 

needs를 통해 선언한 부분이 파이프로 연결되어 있고, 실행 순서대로 depth가 구분되어 있는 것을 확인할 수 있다.

 

하지만 해당 workflow에서 한가지 문제가 발생 하였다.

 

 

하나의 Job으로 사용했을 경우에는 정상적으로 동작하던 부분이, 정상적으로 동작하지 않는다.

해당 경로에 파일이 없다는 것인데, 이러한 문제가 발생하는 이유는 정확히 파악하지 못하였다.

 

해당 문제를 해결하고자 다양한 방법으로 코드를 수정해 보았는데,

build를 한 job 안에서 apk파일을 컨트롤할 수 있다는 것

을 알게 되었다.

 

즉, slack이나 github에 apk파일을 업로드하기 위해서는 build를 통해 apk파일을 생성한 job 안에서 step으로 해당 액션을 추가해야한다는 것이다.

 

다음으로, 2개 이상의 job을 needs 조건으로 걸어보자.

 

fin_call:
  name: Final call Job
  needs: [test_call, slack-msg]
  runs-on: ubuntu-latest
  steps:
    - name: Final Call
      run: echo 'Final Call'

 

needs에 대괄호와 콤마(,)를 사용하여 job을 나열해 주었다.

여기서 대괄호를 사용하지 않으면 정상적으로 동작하지 않으며, 안드로이드 스튜디오 내부에서 파일을 확인해 보았을 때

 

 

이와 같이 경고 라인이 생기기 때문에 깜빡하고 추가하지 못하였을 때 한눈에 알 수 있다.

 

이렇게 여러개의 job을 연결하는 경우 workflow에서 확인했을 때 다음과 같이 그래프가 그려진다.

 


다음으로,

연결한 파이프라인을 사용하여 Job의 실행 조건을 추가해보도록 하자.

기존에 작성했던 코드에서도 if 키워드를 사용한 것을 볼 수 있다.

if를 사용하여 해당 Job이나 Step을 실행하는데 조건을 걸어줄 수 있다.

 

if에 기본적으로 always(), success(), failure()을 많이 사용하는데, 이런것들 외에도 수식을 넣어서 조건을 줄 수 있다.

필자는 간단하게 선행되어야 하는 Job이 성공, 실패하는 것에 따라서 실행되는 job을 추가해 보았다.

 

test_call:
  name: Test Job
  runs-on: ubuntu-latest
  steps:
    - name: Test Call
      run: echo 'Test Call'

test_failure_call:
  name: Test Failure Job
  runs-on: ubuntu-latest
  steps:
    - name: Failure Event
      id: fail-event
      run: |
        exit 1

fin_failure_call:
  name: Failure call Job
  needs: [test_failure_call, test_call]
  if: needs.test_failure_call.result == 'failure' && needs.test_call.result == 'success'
  runs-on: ubuntu-latest
  steps:
    - name: Final Call
      run: echo 'Final Call'

 

각 job에 대해 확인해 보면,

test_call은 Test Call이라는 텍스트를 출력하기만 하는 job이고,

test_failure_call은 exit 1을 호출하여 강제로 Job이 실패하도록 하는 job이다.

 

 

fin_failure_call은 위의 2개의 call Job이 선행된 후에 실행되는데, test_failure_call은 실패, test_call은 job이 성공했을 때 실행되도록 조건을 추가한 것이다.

 

 

하지만 위와 같이 선언해서 사용하는 경우, Test Job은 성공, Test Failure Job은 실패했음에도 불구하고 Call Job이 실행되지 않았음을 확인할 수 있다.

 

위의 코드를 보면 알 수 있겠지만, 조건에 부합하고 있음에도 불구하고 실행이 안되었다는 것은 조건식에 문제가 있다는 것이다.

 

따라서, 어느 조건이 문제가 있는지를 확인하기 위해서 가능한 모든 경우의 수를 확인해 보고 문제를 해결할 수 있었다.

결과부터 말하자면, 해당 조건문에서 필요로 하는 값은 정상적으로 들어오고 있다는 것이다.

 

그렇다면 어떻게 해결했는가?

 

이유는 알 수 없지만 always() 조건을 AND 연산으로 추가해 주어서 해결하였다.

즉,

 

if: always() && needs.test_failure_call.result == 'failure' && needs.test_call.result == 'success'

 

이와 같은 조건문을 사용하면 정상적으로 동작한다는 것이다.

 

2개의 조건을 각각 하나씩 선언해서 사용하였을 때도 정상동작을 하지 않았고,

always()를 AND 연산으로 붙였을 경우에만 정상 동작을 하였다.

 

하지만, 해당 조건을 붙이면서 테스트 진행을 위해 Slack에 메세지를 보내는 부분에서 always() 조건 없이 result == 'success' 조건만 사용했는데 정상적으로 동작한 것으로 보면, 필자가 정확히 알지 못하는 어떠한 조건이 있는 것으로 보인다.

 

아무튼, StackOverFlow에서 여러 게시글을 확인해 보았는데 always를 붙여야 하는 이유에 대해서는 알 수 없었고, 많은 답변들이 조건문 앞에 always()를 붙이고 있는 것을 확인하였다.

이유는 모르겠지만 붙여서 사용하는 것이 맞는것으로 보인다.

 

조건문을 정상적으로 수정한 후에 workflow를 확인해보면

 

 

이처럼 정상적으로 빌드가 된 것을 확인할 수 있다.

 

이 때, 강제로 실패하도록 만들어둔 Job이 있기 때문에 전체 workflow는 실패가 한 것으로 보이게되니 참고하길 바란다.

 

 

여기서 파이프 라인이 겹쳐서 제대로 확인이 어려울 경우에는, 확인이 필요한 Job에 마우스를 올리게 되면 다음과 같이 명확하게 파이프 라인을 확인할 수 있으니 참고하길 바란다.

 


마지막으로,

Environments를 사용하여 Workflow에 Reviewer를 붙여보도록 하자.

기존에 PR을 사용하게 되면, 리뷰어가 해당 커밋을 확인하고, 승인을 해주는 경우에 이후의 동작이 진행되게 된다.

Github Actions에서는 이러한 PR과 동일한 동작을 Environments를 사용하여 구현할 수 있다.

 

PR을 사용하면 되는데 이것이 왜 필요한가 싶겠지만,

PR을 요청하지 않았을 때 해당 기능이 필요할 가능성도 있을 것으로 보이고,

정식 배포와 같은 책임이 중대한 작업이 진행될 때의 프로세스로 사용할 수 있다고 생각이 되어서 추가해 보았다.

 

우선, 이전에 secrets를 추가했던 것 처럼 github에서 settings 탭에서 작업을 해주어야 한다.

 

 

Settings > Code and automation > Environments 로 들어가게 되면 위의 사진과 동일한 화면을 확인할 수 있을 것이다. 여기서 Environment 변수를 선언하여 우리가 원하고자 하는 기능을 구현하게 된다.

 

 

New environment를 눌러 원하는 이름으로 변수를 추가해 주도록 한다.

 

 

변수를 추가하게 되면 다음과 같은 화면이 나오게 되는데, 여기서 옵션을 추가하여 원하는 기능을 추가할 수 있다.

 

  • 최대 6명의 리뷰어를 추가할 수 있는 Required reviewers 옵션
  • 최대 43,200분 동안 wait 시킬 수 있는 Wait timer 옵션

2가지가 존재한다.

Required reviewers 옵션은 PR과 같은 기능을 제공해주는 옵션이고,

Wait 옵션은 말 그대로 해당 변수가 선언된 job을 실행할 때 주어진 시간만큼 대기한 후에 실행되도록 하는 옵션이다.

 

필자는 우선 확인을 해보기 위하여 2가지 옵션을 전부 활성화 시키도록 하였다.

 

 

이처럼 리뷰어를 추가하고, wait 되는 시간을 추가해 주었다.

 

 

리뷰어를 추가할 때는 해당 깃허브에 소속된 팀의 이름을 타이핑하면 드랍 메뉴로 목록이 나오니 선택하여 추가할 수 있다.

 

 

룰을 저장하고 다시 Environments 탭을 확인해보면 이와 같이 추가 된 것을 확인할 수 있다.

 

이렇게 추가한 Environments를 사용해 보도록 하자.

기존에 사용했던 secrets처럼 secrets.~ 로 사용하는 것이 아니라, environment 키워드를 선언하여 사용하면 된다.

 

jobs:
  build:
    name: Build APK
    runs-on: ubuntu-latest
    environment: production
    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: '11'
        distribution: 'temurin'
        cache: gradle
        
        ...

 

가장 처음에 실행되는 Build job에 environment 키워드를 선언하고, github에 추가했던 production을 넣어주도록 한다.

 

이것 외에 추가적인 작업은 필요하지 않고, 이 상태로 commit하여 workflow가 실행되도록 하자.

 

 

상단을 확인해보면 이처럼 작업을 진행하기 전에 배포 검토 항목이 나오게 된다.

또한, Build APK 라는 이름의 Job을 보면, 리뷰와 시간을 기다리고 있다는 텍스트를 확인할 수 있다.

 

여기서 모든 조건을 모두 만족하지 않으면 다음 동작은 진행되지 않는다.

즉, 리뷰어가 확인을 하고 5분이 지나지 않으면 동작을 하지 않고, 5분이 지나도 리뷰어가 확인을 하지 않으면 동작을 하지 않게 된다는 것이다.

 

또한, 아래의 항목을 보면 알 수 있겠지만 environment를 붙이지 않은 Job에 대해서는 기다리지 않고 정상적으로 동작하고 있으며, 

Needs를 통해 선행되어야 하는 작업이 있는 경우 해당 작업이 진행될 때 까지 기다리게 된다.

 

 

배포 검토를 눌러서 다음과 같이 comment를 추가하고 넘어가도록 하자.

 

 

검토가 완료되면 하단에 이와같이 리뷰어와 Comment를 확인할 수 있고,

 

 

빌드를 진행하는데 조건이 wait time밖에 없는 것을 확인 할 수 있고,

해당 시간이 끝나면 다시 순차적으로 Job들이 진행되는 것을 확인할 수 있을 것이다.


Github Actions에 추가할 수 있는 몇가지 기능에 대하여 작성해 보았다.

생각보다 글이 길어졌는데, 딱히 없어도 되지만 있으면 좋을 것 같은 것들에 대해서 먼저 적용해 보았다.

 

찾으면 찾을수록 좋은 기능을 구현해 둔 사람들이 많은 것 같지만,

아직 많은 것을 알지 못하는 상태라 그런지 적용하는 것에 있어서 시간이 상당히 많이 소요되는 것 같다.

 

추후에 좋은 기능을 발견하고, 적용하게 된다면 추가적인 기능들에 대해서도 계속하여 글을 작성해보도록 하겠다.

 

해당 게시글에 사용된 예제는 Github에 올려두었다.

https://github.com/HeeGyeong/Dummy

 

GitHub - HeeGyeong/Dummy

Contribute to HeeGyeong/Dummy development by creating an account on GitHub.

github.com

 

728x90