Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] Github Action - Jobs, Steps

Updated
7 min readView as Markdown
[ 살펴보기 ] Github Action - Jobs, Steps
C

A developer living in Busan, Korea

workflow는 특정 작업을 수행하는 여러 개의 jobs으로 구성되고 각 job은 해당 job을 수행하기 위해 필요한 각각의 steps으로 구성된다. workflow를 이루는 여러 개의 jobs은 각각 병렬적으로 처리되며 만약 특정 job이 다른 job이 끝나고 실행되어야 한다면 needs keyword를 통해 job 사이의 의존 관계를 명시해줄 수 있다.

Runner

Runner란 workflow에 선언한 각각의 job이 실행될 환경을 말한다. github에서 제공하는 runner를 사용할 수도 있고 또는 자체적으로 hosting 하는 runner를 사용할 수도 있다. Job을 실행하는 runner의 설정은 runs-on keyword를 통해 설정한다.

name: Test Workflow

on:
  push:
    branches:
      - master

jobs:
  information_commit_id:
    runs-on: ubuntu-latest
    steps:
      ...

Github에서 제공하는 runner는 linux, widnows, macOS가 있으며 Github에서 제공하는 runner를 사용하는 job은 같은 종류의 runner를 사용하더라도 실행될 때 각각 새로운 runner instance에서 실행된다.

Github에서 제공하는 runner의 종류에 대한 자세한 사항은 documentation을 ( Reference - Choosing GitHub-hosted runners ) 통해 확인할 수 있으며 public repository, private repository에서 실행할 수 있는 사용량이 다르므로 주의하자.

Job

다음 예제를 통해 workflow에 job과 step을 어떻게 추가하는지 살펴보자.

name: Test Workflow

on:
  push:
    branches:
      - master

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

  information_repository:
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

위의 workflow는 information_commit_idinformation_repository라는 두 개의 job으로 구성되어 있다. 그리고 information_commit_id job은 Get commit ID라는 name을 가진 하나의 step으로 구성되어 있고 information_repositoryjob은 Get repository name이라는 name을 가진 step으로 구성되어 있다.

위에서 언급했듯이 workflow의 job은 모두 병렬적으로 처리된다. ( 동시에 실행된다 ) 만약 특정 job이 정상적으로 종료되고 다른 job을 실행하고 싶다면 다음과 같이 needs keyword를 사용한다.

name: Test Workflow

on:
  push:
    branches:
      - master

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

  information_repository:
    needs: information_commit_id
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

  information_branch_name:
    needs: information_repository
    runs-on: ubuntu-latest
    steps:
      - name: Get branch name
        run: |
          echo "branch name : ${{ github.ref }}"

위의 예제에서 information_repository job은 information_commit_id job이 성공적으로 종료되어야 실행되며 information_branch_name job은 information_repository job이 성공적으로 종료되어야 실행된다.

만약 needs에 선언된 job이 실패하더라도 무조건 job을 실행시켜야 한다면 다음과 같이 always()를 실행되어야 하는 job의 condition에 추가해준다.

name: Test Workflow

on:
  push:
    branches:
      - master

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

  information_repository:
    if: ${{ always() }}
    needs: information_commit_id
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

위의 예제에서 information_repositoryjob은 information_commit_id job의 성공 여부와 상관없이 information_commit_id job의 실행이 종료되면 무조건 실행된다.

반면에 다음과 같이 failure() status check function을 통해 needs에 선언된 job이 실패했을 때만 job이 실행되도록 설정할 수도 있다.

name: Test Workflow

on:
  push:
    branches:
      - master

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

  information_repository:
    if: ${{ failure() }}
    needs: information_commit_id
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

위의 예제에서 information_repository job은 information_commit_id job이 실패로 종료되었을 때만 실행된다.

다음 job으로 data 전달하기

output keyword를 통해 다른 job에게 특정 data를 전달할 수 있다. 이 때 data를 전달할 수 있는 job은 output keyword를 통해 data를 전달하는 job을 needs keyword를 통해 해당 job에 의존하고 있는 job이다.

Outputs을 통해 전달할 수 있는 data의 최대 크기는 1MB다.

name: Test Workflow

on:
  push:
    branches:
      - master
      - develop

jobs:
  information_commit_id:
    runs-on: ubuntu-latest
    outputs:
      current_commit_id: ${{ steps.store_commit_id.outputs.commit_id }}
    steps:
      - id: store_commit_id
        name: Get commit ID and store to output
        run: |
          echo "commit_id=${{ github.sha }}" >> "$GITHUB_OUTPUT"

      - id: print_commit_id
        name: Get commit ID and print
        run: |
          echo "commit id : ${{ github.sha }}"

  information_repository:
    needs: information_commit_id
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

      - name: Print commit id from the previous job
        run: |
          echo "commit id : ${{ needs.information_commit_id.outputs.current_commit_id }}"

위의 예제에서 information_commit_id job의 store_commit_id step과 같이 "commit_id=${{ github.sha }}" >> "$GITHUB_OUTPUT" 구문을 통해 commit_id 변수에 value를 할당하면 ( 변수명은 임의로 정해도 무관하다 ) steps.store_commit_id.outputs.commit_id를 통해 commit_id 변수에 설정한 값에 접근할 수 있다.

information_commit_id job에서 outputs property를 통해 commit_id 변수에 설정한 값을 current_commit_id라는 output property에 설정하면 information_commit_idjob을 needs keyword를 통해 의존하고 있는 다른 job에서 해당 값을 전달 받아 사용할 수 있다.

information_repository job에선 needs.information_commit_id.outputs.current_commit_id을 통해 이전 job에서 전달한 값에 접근하고 있다.

Conditional Job

위에서 잠시 살펴 보았듯이 if keyword를 통해 특정 job이 실행되는 조건을 설정할 수 있다.

name: Test Workflow

on:
  push:
    branches:
      - master
      - develop

jobs:
  information_commit_id:
    runs-on: ubuntu-latest
    outputs:
      current_commit_id: ${{ steps.store_commit_id.outputs.commit_id }}
    steps:
      - id: store_commit_id
        name: Get commit ID and store to output
        run: |
          echo "commit_id=${{ github.sha }}" >> "$GITHUB_OUTPUT"

      - id: print_commit_id
        name: Get commit ID and print
        run: |
          echo "commit id : ${{ github.sha }}"

  information_repository:
    if: github.repository == 'test/test-action'
    needs: information_commit_id
    runs-on: ubuntu-latest
    steps:
      - name: Get repository name
        run: |
          echo "repository name : ${{ github.repository }}"

      - name: Print commit id from the previous job
        run: |
          echo "commit id : ${{ needs.information_commit_id.outputs.current_commit_id }}"

위의 예제에서 information_repository job은 workflow가 trigger된 github.repository context variable 값이 test/test-action일 때만 실행된다. 위의 예제에서 볼 수 있듯이 if 조건문에서 github.repository와 같은 context variable을 사용할 때 ${{ }}와 같은 expression syntax를 생략할 수 있다.

하지만 if 조건문이 !로 시작한다면 ${{ }} expression syntax를 사용해야 한다. ( Reference - Using conditions to control job execution )

if: ${{ ! startsWith(github.ref, 'refs/tags/') }}

Steps

위의 예제에서 살펴보았듯이 workflow의 job은 한 개 혹은 다수의 steps으로 구성되어 있다. step을 통해 특정 command를 실행하거나 action을 실행한다. job과는 달리 각 job을 구성하는 step은 차례대로 실행된다. 예를 들어 아래 예제의 information job에서 name이 Get commit ID and repository name인 step이 실행되고 나서 name이 Get repository name인 step이 실행된다.

name: Test Action

on:
  push:
    branches:
      - master
      - develop

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 }}"

그리고 workflow나 job level뿐만 아니라 step level에서도 environment variable을 설정하여 사용할 수 있다.

name: Test Action

on:
  push:
    branches:
      - master
      - develop

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

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

job을 구성하는 step 중 특정 조건에 만족할 때만 실행되어야 하는 step이 있다면 if statement를 통해 특정 조건에만 step을 실행할 수도 있다. 예를 들어 아래 workflow에서 name이 Get repository name인 step은 workflow가 pull_request event에 의해 trigger 되었을 때만 실행된다.

name: Test Action

on:
  pull_request:
    branches:
      - master
      - develop
  push:
    branches:
      - master
      - develop

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

      - name: Get repository name
        if: ${{ github.event_name == 'pull_request' }}
        run: |
          echo "repository name : ${{ github.repository }}"

또는 다음과 같이 이전 step이 실패 했을 때만 다음 step이 실행되도록 처리할 수도 있다.

name: Test Action

on:
  pull_request:
    branches:
      - master
      - develop
  push:
    branches:
      - master
      - develop

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

      - name: Get repository name
        if: ${{ failure() }}
        run: |
          echo "repository name : ${{ github.repository }}"

만약 steps 사이에서 data를 공유하고자 한다면 다음과 같이 environment variable을 활용할 수 있다. 아래 예제는 commit id를 TEST_COMMIT_ID라는 environment variable에 설정하고 다음 step에서 해당 environment variable value를 사용하고 있다.

name: Test Action

on:
  push:
    branches:
      - master
      - develop

jobs:
  information:
    runs-on: ubuntu-latest
    steps:
      - name: Set the commit id in an environment variable
        run: |
          echo "TEST_COMMIT_ID=${{ github.sha }}" >> $GITHUB_ENV

      - name: Printing the commit id environment variable
        run: |
          echo "Commit id : $TEST_COMMIT_ID"

혹은 다음과 같이 임시 file을 통해 데이터를 저장하고 사용하는 방법도 있다.

name: Test Action

on:
  push:
    branches:
      - master
      - develop

jobs:
  information:
    runs-on: ubuntu-latest
    steps:
      - name: Set the commit id in a file
        run: |
          echo "${{ github.sha }}" > temp_test_data.txt

      - name: Printing the commit id from a file
        run: |
          cat temp_test_data.txt

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