Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] Github Action - Workflow

Updated
8 min readView as Markdown
[ 살펴보기 ] Github Action - Workflow
C

A developer living in Busan, Korea

Github action은 github에서 제공하는 CI/CD platform이다. Github action을 통해 github repository의 특정 branch에 push되거나 혹은 pull request merge와 같은 특정 event가 발생했을 때 repository의 code를 build, test하고 deployment 과정을 자동화할 수 있다.

기본 구성 요소

  • Workflows : 특정 event가 발생했을 때 어떤 작업을 수행할 것인지 정의한 process file이다. YAML format으로 작성하며 github repository의 .github/workflows 폴더 안에 관리하며 여러개의 workflow files을 추가하여 각각 특정 event에 맞게 실행할 수도 있다.

  • Events : Events는 workflow를 실행하는 trigger 역할을 한다. 예를 들어 특정 branch에 push가 발생하거나 pull request가 발생하거나 issue가 생성되는 등 특정 event에 반응하여 worflow가 실행되게 설정할 수 있다. 아래는 master branch에 push가 발생하면 workflow가 실행되게 설정한 예제다.

      name: Deploy to EC2
    
      on:
        push:
          branches:
            - master
    
      ...
    
  • Jobs : worflow에서 실행될 작업을 정의한다. job은 다시 세부 steps으로 나뉘며 각 step은 job에서 설정한 runner ( 코드 실행 환경 )에서 실행된다. workflow에 다수의 jobs이 정의되었을 때 각 jobs은 동시 처리되며 필요에 따라 순서대로 실행될 수 있도록 설정할 수 있다.

    예를 들어 아래 예제는 information이라는 job을 정의하고 있으며 workflow가 trigger되었을 때 최근 commit id와 repository name을 출력한다.

      name: Deploy to EC2
    
      on:
        push:
          branches:
            - master
    
      jobs:
        information:
          runs-on: ubuntu-latest
          steps:
            - name: Checkout code
              uses: actions/checkout@v4
    
            - name: Get commit ID and repository name
              run: |
                echo "commit id : ${{ github.sha }}"
                echo "repository name : ${{ github.repository }}"
    
  • Actions : 위의 예제와 같이 job에서 실행할 작업을 run property에 설정할 수도 있지만 특정 작업을 action으로 묶어 사용할 수도 있다. 위의 예제에선 Checkout code step에서 github action에서 repository의 code를 대상으로 특정 작업을 하기 위해 필요한 checkout이라는 community action을 사용하고 있다.

  • Runner : job이 실행될 runner ( 실행환경 )을 뜻한다. runs-on property를 통해 job을 실행할 runner 정보를 설정할 수 있으며 위의 예제에선 ubuntu-latest를 job을 실행하기 위한 runner로 설정하고 있다.

Workflows

이제 기본적인 workflow 파일을 작성해보자. github repository 혹은 repository와 연결된 project ( push를 위한 remote repository가 설정된 ) root 경로에 .github/workflows 폴더를 생성하고 해당 폴더 안에 test-action.yml 파일을 생성한다.

.github/workflows/test-action.yml

name: Test Workflow

on: [push]

jobs:
  information:
    runs-on: ubuntu-latest
    steps:
      - name: Get commit ID and repository name
        run: |
          echo "commit id : ${{ github.sha }}"

      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

      - name: Get branch name
        run: |
          echo "branch name : ${{ github.ref }}"

위의 예제에선 worflow의 이름을 Test Workflow로 설정하고 event는 어떤 branch든지 push 발생하면 workflow를 trigger하도록 설정되어 있다.

그리고 workflow가 trigger되면 information job이 실행이 되고 informaton의 각 step에 commit id, repository name, branch name을 출력하는 작업을 수행한다.

이제 변경 사항을 저장하고 commit을 생성하여 github repository에 push 해보자. push한 다음 github에서 Actions tab으로 이동하면 trigger된 action의 성공 여부를 확인할 수 있다.

Actions page에 나오는 list 중 특정 action의 title을 click하여 들어가면 다음과 같이 해당 workflow를 구성하는 job의 성공 여부 역시 확인할 수 있다.

그리고 위의 job을 눌러보면 job을 구성하는 steps과 각 steps의 실행결과를 확인할 수 있다.

Workflow triggers

Workflow를 trigger하는 방법을 좀 더 자세히 살펴보자. 우선 아래의 예제는 repository에 push event가 발생할 때 마다 workflow가 실행된다.

name: Test Workflow

on: [push]

...

만약 다음과 같이 설정하면 push뿐만 아니라 fork가 발생해도 workflow가 실행된다.

name: Test Workflow

on: [push, fork]

...

issue나 issue_comment와 같은 event는 activity type 설정을 통해 workflow가 실행되는 event를 보다 상세화 할 수 있다. 설정하는 형태는 on.<eventName>.types과 같다. 예를 들어 다음 예제는 새로운 issue가 생성되었을 때 workflow가 실행된다.

name: Test Workflow

on:
  issues:
    types:
      - opened

...

만약 issue에 comment가 발생했을 때도 workflow를 실행하고 싶다면 issue_comment event를 함께 설정해줄 수 있다.

name: Test Workflow

on:
  issue_comment:
    types:
      - created
  issues:
    types:
      - opened

Activity type을 여러 개 설정하는 것 역시 가능하다. 다음 예제에서는 issue comment가 새로 생성되거나 수정되면 workflow가 실행된다.

name: Test Workflow

on:
  issue_comment:
    types:
      - created
      - edited
  issues:
    types:
      - opened

반면 push나 pull_request event의 경우 activity type이 아닌 filter를 통해 event의 상세 설정을 한다. 다음 예제는 push event에 filter를 적용하여 master branch에 push가 되었을 때만 workflow를 실행하는 예제다.

name: Test Workflow

on:
  push:
    branches:
      - master

...

혹은 다음과 같이 glob pattern을 filter에 지정할 수도 있다. 다음 workflow는 releases/로 시작하는 branch로 pull_request가 발생하면 실행된다.

name: Test Workflow

on:
  pull_request:
    branches:
      - 'releases/**'

...

반면 branches-ignore pattern은 push나 pull_request event가 발생했을 때 workflow를 실행시키지 않을 branch를 설정할 수 있다. 아래 예제는 develop branch를 제외한 branch에 push event가 발생했을 때만 workflow를 실행한다. 즉, develop branch에 push가 발생해도 workflow는 실행되지 않는다.

name: Test Workflow

on:
  push:
    branches-ignore:
      - 'develop'

...

또는 특정 tag가 추가되었을 때 workflow를 실행할 수도 있다. 아래 예제는 v1.0.0이나 v1.1.0과 같이 v1. 로 시작하는 tag가 추가되었을 때 workflow가 실행된다.

name: Test Workflow

on:
  push:
    tags:
      - v1.*

...

Local에서 추가한 tag는 repository로 push할 때 자동으로 함께 추가되지는 않으므로 tag는 별도로 repository에 추가 해주어야 한다.

Local에서 가장 최근 commit에 tag를 추가할 때는 다음 comamnd를 사용한다.

git tag 1.0.0

그리고 생성한 tag를 repository에도 추가할 때는 다음 command를 사용한다. ( local에서 tag를 추가한 commit이 repository에 추가된 후에 )

git push origin v1.0.0

위에서 살펴보았듯이 push할 때 workflow를 trigger하는 대상에서 특정 branch를 제외하고 싶다면 branches-ignore pattern을 사용했다. 만약 특정 branch pattern은 포함하지만 특정 branch pattern은 제외하고 싶다면 어떻게 해야 할까?

하나의 event에서 branches와 branches-ignore pattern은 함께 사용할 수 없기에 특정 branch pattern만 제외하고 싶다면 !를 제외할 branch pattern 앞에 붙여서 사용한다. 예를 들어 다음 filter가 적용된 workflow는 release/1, release/2와 같은 branch에 push가 발생했을 때는 workflow가 실행되지만 release/1-test, release/2-test와 같은 branch가 push 되었을 때는 실행되지 않는다.

on:
  push:
    branches:
      - 'releases/**'
      - '!releases/**-test'

Filters for file paths

paths filter를 통해 push event가 발생 했을 때 특정 file에 변경 사항이 있을 때만 workflow를 실행 시킬 수도 있다. 예를 들어 다음 workflow는 push된 source code 중 css file에 수정이 된 사항이 존재하면 실행된다.

name: Test Workflow

on:
  push:
    paths:
      - "**.css"

반대로 paths-ignore에 설정한 pattern의 file은 변경 사항이 발생하고 push되어도 workflow가 실행되지 않는다.

name: Test Workflow

on:
  push:
    paths-ignore:
      - "**.html"

workflow_run

만약 다른 workflow가 실행되었을 때 함께 실행되어야 하는 workdlow가 있다면 workflow_run event를 사용할 수 있다.

예를 들어 다음 두 workflow가 있다고 가정해보자.

.github/workflows/test.yml

name: Test Action

on:
  push:
    branches:
      - master

jobs:
  ...

.github/workflows/test-alarm.yml

name: Test Alarm

on:
  workflow_run:
    workflows: ["Test Action"]
    types: [requested]

jobs:
    ...

위의 예제에선 types이 requested이므로 Test Action workflow가 실행되면 Test Alarm workflow 역시 실행된다. 만약 types을 completed로 설정하면 Test Action workflow가 완료되고 나서 Test Alarm workflow가 실행된다.

주의할 점은 workflow_run을 사용하는 workflow file이 github의 default branch에 존재해야 정상적으로 동작한다. 그리고 workflow_run을 통해 연동해서 실행하는 workflow는 3개 까지만 허용된다. 예를 들어 test1.yml 부터 test5.yml까지 workflow_run을 통해 연동한다면 연속으로 연동할 수 있는 workflow 파일은 test3.yml 까지다.

workflow_run의 types을 completed로 설정했을 때 이전의 workflow의 결과가 성공 또는 실패와 상관없이 다음 workflow가 실행된다. 만약 이전 workflow의 성공 또는 실패 여부에 따라 다른 job을 실행하고 싶다면 다음과 같이 github.event.workflow_run.conclusion를 통해 conditional job을 설정한다.

name: Test Alarm

on:
  workflow_run:
    workflows: ["Test Action"]
    types: [requested]

jobs:
  after-success:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - run: echo 'The previous workflow was success'

  after-failure:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'failure' }}
    steps:
      - run: echo 'The previous workflow failed'

만약 triggering workflow가 특정 branch에 의해 실행될 때만 workflow_run이 정의된 workflow를 실행하고 싶다면 아래와 같이 branches property를 설정해준다. 예를 들어 다음 두 workflow file이 있다고 가정해보자.

.github/workflows/test.yml

name: Test Action

on:
  push:
    branches:
      - master
      - develop

jobs:
  ...

.github/workflows/test-alarm.yml

name: Test Alarm

on:
  workflow_run:
    workflows: ["Test Action"]
    types: [requested]
    branches: [master]

jobs:
    ...

workflow 설정이 위의 예제와 같을 때 Test Action workflow는 master또는 develop branch에 push가 발생하면 실행되지만 Test Alarm은 Test Action이 master branch의 push에 의해 실행될 때만 실행된다. 즉, Test Action이 develop branch push에 의해 실행될 때는 Test Alarm action은 실행되지 않는다.

workflow_dispatch

만약 github action page에서 workflow를 수동으로 직접 실행하고 싶다면 workflow_dispatch event를 사용할 수 있다. 다음과 같은 workflow file이 있다고 가정해보자. 해당 workflow를 수동으로 실행할 수 있도록 on.workflow_dispatch keyword를 사용하고 있다.

name: Test Dispatch

on:
  workflow_dispatch:
    inputs:
      userLevel:
        description: "user level"
        required: true
        default: "user"
        type: choice
        options:
          - admin
          - user
          - guest
      policyAgreement:
        description: "policy agreement"
        type: boolean
        required: true
      environment:
        description: "Environment to run tests"
        type: string
        required: true

jobs:
  workflow-inputs:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "user level: $USER_LEVEL"
          echo "policy agreement: $POLICY_AGREEMENT"
          echo "environment: $ENVIRONMENT"
        env:
          USER_LEVEL: ${{ inputs.userLevel }}
          POLICY_AGREEMENT: ${{ inputs.policyAgreement }}
          ENVIRONMENT: ${{ inputs.environment }}

위의 예제와 같이 inputs keyword를 통해 workflow를 실행할 때 함께 전달할 수 있는 value를 설정할 수 있으며 input은 최대 10개까지 설정할 수 있다. 위의 예제에선 userLevel, policyAgreement, environment 3개의 input을 전달 받는다.

이제 위의 workflow를 github의 default branch에 push하고 actions tab에서 방금 push한 workflow ( 예제에선 Test Dispatch )를 선택해보면 다음과 같이 수동으로 workfow를 실행할 수 있는 화면을 볼 수 있다. 오른쪽 상단 Run workflow button을 누르면 inputs에 설정한 값들을 함께 설정하여 workflow를 실행할 수 있다.

workflow_dispatch keyword를 사용한 workflow만 위의 예제와 같이 수동으로 실행할 수 있는 button을 확인할 수 있으며 workflow_dispatch keyword를 사용하는 workflow는 github의 default branch에 존재해야 한다.

Concurrency

만약 push를 통해 workflow가 여전히 실행되고 있는데 다시 push를 발생시켜 workflow가 실행된다면 두 개의 workflow가 함께 실행될 수도 있다. 해당 상황이 문제가 되지 않을 수도 있지만 중복 workflow 실행으로 인한 conflict가 발생할 수도 있고 github action의 사용량을 불필요하게 소비하게 된다.

이럴 때 다음과 같이 workflow에 concurreny property를 통해 group을 설정해주면 같은 group name을 가진 workflow는 동시에 실행되지 않는다.

name: Test Action

on:
  push:
    branches:
      - master

concurrency:
  group: group-test-workflow

jobs:
  information:
    runs-on: ubuntu-latest
    steps:
      - name: Get commit ID and repository name
        run: |
          echo "commit id : ${{ github.sha }}"

      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

      - name: Get branch name
        run: |
          echo "branch name : ${{ github.ref }}"

위의 workflow file을 기준으로 push를 연속 두 번 실행해서 workflow를 실행 해보면 다음과 같이 뒤에 추가된 workflow는 같이 실행되지 않고 pending 상태에 머물며 현재 실행 중인 in propress workflow가 종료되면 pending 상태에 있는 workflow가 실행된다.

만약 같은 group name을 가진 새로운 workflow가 추가 되었을 때 기존 in progress 상태에 있는 workflow를 취소하고 새로 추가된 workflow를 실행하고 싶다면 다음과 같이 concurrency property에 cancel-in-progress option을 true로 설정해준다.

name: Test Action

on:
  push:
    branches:
      - master
      - develop

concurrency:
  group: group-test-workflow
  cancel-in-progress: true

jobs:
  information:
    runs-on: ubuntu-latest
    steps:
      - name: Get commit ID and repository name
        run: |
          echo "commit id : ${{ github.sha }}"

      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

      - name: Get branch name
        run: |
          echo "branch name : ${{ github.ref }}"

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts