github action을 배워보자 3일차 - 다양한 기능2

0

github action

목록 보기
3/3

github action의 다양한 기능2

Environment variables

step이나 job에서 사용할 수 있는 환경 변수를 key-value 쌍으로 넣을 수 있다.

  • output: 같은 job, 다른 job 간에 데이터 공유가 가능
  • env: 동일한 job에서만 데이터 공유가 가능

환경 변수를 구성하는 방법은 두 가지가 있는데,
1. env
2. 미리 정리

env방식은 workflow level, job level, step level에서 작성하면 된다.

  • workflow level
name: env level
on:
  workflow_dispatch
env:
  level: workflow # workflow level

jobs:
  get-env-1:
    runs-on: ubuntu-latest
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}"
  • job level
name: env level
on:
  workflow_dispatch

jobs:
  get-env-2:
    runs-on: ubuntu-latest
    env:
      level: job # job level
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}"
  • step level
name: env level
on:
  workflow_dispatch

jobs:
  get-env-3:
    runs-on: ubuntu-latest
    env:
      level: job
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}"
      env:
        level: step # step level

만약 중복해서 써있다면 step level > job level > workflow level 순서이다.

참고로 env 사용 방법 이외에 다음과 같이 사용할 수도 있다.

...
jobs:
  get-env-3:
    runs-on: ubuntu-latest
    env:
      level: job
    steps:
    - name: create env
      run: |
        echo "level=job" >> $GITHUB_ENV
    - name: check env
      run: echo "LEVEL ${{ env.level }}"

echo "key=value" >> $GITHUB_ENV 형식으로 env값을 넣을 수 있다. 재밌는 것은 GITHUB_ENV.level로 가져오는 것이 아니라, $GITHUB_ENV에 값을 넣으면 env로 가져올 수 있다는 것이다.

test를 위한 workflow를 만들어보도록 하자.

  • .github/workflows/env.yaml
name: var-1
on: push
env:
  level: workflow # workflow level

jobs:
  get-env-1:
    runs-on: ubuntu-latest
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}" # workflow level
  get-env-2:
    runs-on: ubuntu-latest
    env:
      level: job # job level
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}" # job level
  get-env-3:
    runs-on: ubuntu-latest
    env:
      level: step
    steps:
    - name: check env
      run: echo "LEVEL ${{ env.level }}" # step level
      env:
        level: step # step level
  echo-env-4:
    runs-on: ubuntu-latest
    steps:
    - name: create env
      run: echo "level=echo" >> $GITHUB_ENV
    - name: check env
      run: echo "LEVEL ${{ env.level }}" # step level
  1. get-env-1 job은 workflow env 값을 가져온다. LEVEL workflow가 결과로 나온다.
  2. get-env-2 job은 job env 값을 가져온다. LEVEL job가 결과로 나온다.
  3. get-env-3 job은 step env 값을 가져온다. LEVEL step가 결과로 나온다.
  4. get-env-4 job은 step env 값을 가져온다. LEVEL echo가 결과로 나온다.

다음은 미리 환경변수를 설정하는 방법은 gitub에서 설정하는 방법도 있다.
github repo -> Settings -> Secrets and variables -> Actions -> Envrionment secrets -> Manage environments 로 설정해주면 된다.

Secret

민감한 데이터를 안전하게 저장해서 workflow에서 사용하는 것이다. 민감 정보를 코드와 분리하여 노출되는 것을 방지하는 것이다. 주로 암호, 인증 토큰 같은 것들이 있다.

github에서 안전하게 암호화하여 저장하며, 로그에 기록되지 않고 출력 시 마스킹된다. workflow 시에서만 접근할 수 있다.

secret을 사용하기 위해서는 secret을 정의해야하는데, github에서 설정이 가능하다.

github repo -> Settings -> Secrets and variables -> Actions -> New repository secret -> Name-Secret 설정 -> Add secret

Name은 level, Secret은 secret으로 저장해보도록 하자.

  • .github/workflows/secrets.yaml
name: secrets
on: push

jobs:
  get-secrets:
    runs-on: ubuntu-latest
    steps:
    - name: get secrets
      run: echo ${{ secrets.level }}

git push 후에 github에서 확인하면 echo ***으로 출력된 것을 볼 수 있다.

Environment

특정 환경에서만 사용 가능한 환경변수와 secret 관리로 respository에서 정의할 수 있다.

env와 secret 모두 3가지 환경이 있다.
1. environment secrets/env
2. repository secrets/env
3. organization secretsenv

순서는 environment > repository > organization 이다.

environment의 경우 여러 environment들을 만들고, 이를 workflow에서 지정할 수 있다.

github -> Settings -> Code and automation -> Environments -> New environment -> Name: test 생성 -> Environment variables -> Add variable -> LEVEL=test 입력

workflow에서는 enviroment라는 keyword를 사용하여 환경 변수 설정들을 가져올 수 있다.

  • .github/workflows/environment.yaml
name: environment
on: push

jobs:
  get-env-dev:
    runs-on: ubuntu-latest
    environment: test
    steps:
    - name: check env & secret
      run: |
        echo ${{ vars.LEVEL }} 

environmenttest 환경을 가져오는 것이고, env값을 가져올 때는 vars.key를 사용하고, secret값을 가져올 때는 secrets.key를 사용한다.

결과로 test가 나올 것이다.

Matrix

변수 기반으로 여러 job을 실행하는 기능으로 matrix를 사용해 하나의 job을 구성하면 여러 개의 job을 실행하도록 할 수 있다.

가령, 변수로 window, linux, macOS를 설정하여 하나의 job을 구성해 runner가 다른 job을 3개 실행할 수 있다. 또한, python3.7~3.9 변수를 설정해 하나의 job을 python3.7, python3.8, python3.9에서 실행할 수 있도록 할 수 있다.

...
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        version: [3.7, 3.8, 3.9]
    steps:
    - name: checkout
      uses: actions/checkout@v3
    - name: setup python
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.version }}
    - name: run python
      run: python script.py

위의 경우 python 버전을 구성할 때, 버전 별로 구성이 가능하다.

strategy.matrix 아래의 key: [] 값으로 여러 개의 값을 넣어 줄 수 있고, 이를 활용하여 정의한 하나의 job에 대해서 여러 환경에서의 실행이 가능하다.

...
jobs:
  get-matrix:
    strategy:
      matrix:
        os: [macos-latest, windows-latest, ubuntu-latest]
        version: [12, 14, 16]
    runs-on: ${{ matrix.os }}
    steps:
    - name: echo matrix value
      run: |
        echo ${{ matrix.os }}
        echo ${{ matrix.version }}

이렇게 설정해두면, github action은 9개의 job을 실행하는데 다음과 같다.
1. macos-latest:12
2. macos-latest:14
3. macos-latest:16
4. windows-latest:12
5. windows-latest:14
6. windows-latest:16
7. ubuntu-latest:12
8. ubuntu-latest:14
9. ubuntu-latest:16

즉, matrix는 실행 가능한 모든 조합에 대해서 job으로 만들어 실행하는 것이다.

  • .github/workflows/matrix.yaml
name: matrix
on: push

jobs:
  get-matrix:
    strategy:
      matrix:
        os: [windows-latest, ubuntu-latest]
        version: [12, 14]
    runs-on: ${{ matrix.os }}
    steps:
    - name: check matrix
      run: |
        echo ${{ matrix.os }}
        echo ${{ matrix.version }}

총 4개의 job이 생성하게 될 것이다.

If condition

특정 조건이 충족될 때만 실행되도록 하는 데 사용한다. 사용 방법은 기존의 다른 언어와 별반 다를 바가 없다.

  1. if: github.event_name == 'push' // push 일 때만 실행
  2. if: github.event_name != 'push' // push가 아닐 때만 실행

if condition의 실행은 다음의 level에서 가능하다.
1. job level: job 실행 여부
2. step level: step 실행 여부

name: if-condition
on:
  push:
  workflow_dispatch:

jobs:
  job1:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' # job level
    steps:
    - name: get event name
      run: |
        echo ${{ github.event_name }}
  
  job2:
    runs-on: ubuntu-latest
    if: github.event_name != 'push' # job level
    steps:
    - name: get event name
      run: |
        echo ${{ github.event_name }}

위의 workflow에서는 pushworkflow_dispatch event일 때만 실행되는데, if condition으로 해당 workflow의 event를 식별하여 job을 실행한다.

name: if-condition
on:
  push:
  workflow_dispatch:

jobs:
  job1:
    runs-on: ubuntu-latest
    steps:
    - name: get event name
      if: github.event_name == 'push' # step-level
      run: |
        echo ${{ github.event_name }}
    - name: get event name
      if: github.event_name != 'push' # step-level
      run: |
        echo ${{ github.event_name }}

위는 이전에 있던 workflow에서 job level의 if condition을 step level로 바꾼 것 뿐이다.

참고로, fileter와 if condition이 약간 role이 겹치는데, 다음과 같다고 생각하면 된다.

  • filter는 workflow trigger를 더 세밀하게 제어하는 것
  • if: workflow trigger가 된 이후 job과 step을 더 세밀하게 제어하는 것이다.

가령 dev branch일 때만 workflow 실행은 filter가 하지만, dev branch에 대한 workflow가 실행되었을 때 특정 job만 실행은 if condition이 한다.

if condition에는 특정 job과 step을 강제로 실행 가능하도록 할 수 있다.

if: always()

위와 같이 설정 시 특정 job과 step을 강제로 실행할 수 있다.

name: if-condition
on:
  push:
  workflow_dispatch:

jobs:
  job1:
    runs-on: ubuntu-latest
    steps:
    - name: exit 1
      run: exit 1 # 강제 종료
    - name: echo
      run: echo hello
  
  job2:
    needs: [job1]
    runs-on: ubuntu-latest
    steps:
    - name: echo
      run: echo hello

위와 같은 경우에는 job1에서 exit 1을 호출하여 workflow 자체를 강제 종료하고 있다. 즉, job2는 실행되지 않는다.

그런데 여기서 job2if: always()를 사용하면 job1이 실패해도 job2가 실행될 수 있다,.

name: if-condition
on:
  push:
  workflow_dispatch:

jobs:
  job1:
    runs-on: ubuntu-latest
    steps:
    - name: exit 1
      run: exit 1 # 강제 종료
    - name: echo
      run: echo hello
  
  job2:
    needs: [job1]
    runs-on: ubuntu-latest
    if: always() # job-level
    steps:
    - name: echo
      run: echo hello

job2job1에 대해서 needs로 의존성을 가지고 있다하더라도 job2if: always()를 가지고 있어 반드시 실행된다.

job-level 뿐만 아니라 step-level도 가능하다.

name: if-condition
on:
  push:
  workflow_dispatch:

jobs:
  job1:
    runs-on: ubuntu-latest
    steps:
    - name: exit 1
      run: exit 1 # 강제 종료
    - name: echo
      if: always() # step-level
      run: echo hello

exit 1 step에서 workflow를 강제 종료 시키는 exit 1을 실행시켜도 echo step이 if: always()이기 때문에 무조건 실행된다.

startswith, endswith, contains

문자열에 대한 조건 검사를 수행하여 job 또는 step 실행 여부를 결정한다.

startsWith은 첫번째 인자에 대해서 두번째 인자의 값이 앞부분에 있는 지 없는 지 검사한다.

  • startsWith('github actions', 'git') -> true
  • startsWith('github actions', 'test') -> false

endsWith은 첫번째 인자에 대해서 두번째 인자의 값이 뒷부분으로 끝나는 지 검사한다.

  • endsWith('github actions', 'ions') -> true
  • endsWith('github actions', 'test') -> false

contains는 첫번째 인자인 문자열, 배열안에 두번째 인자의 값이 있는 지 없는 지 검사한다.

  • contains('github actions', 'act') -> true
  • contains('github, actions', 'git') -> true

참고로 github action에서는 배열이 문자열에서 ,로 구분한 것을 말한다.

  • .github/workflows/string-function.yaml
name: string-function
on: push

jobs:
  string-function:
    runs-on: ubuntu-latest
    steps:
    - name: startswith
      if: startsWith('github actions', 'git')
      run: echo 'git'
    - name: startsWith
      if: startsWith('github actions', 'test')
      run: echo 'test'
    
    - name: endswith
      if: endsWith('github actions', 'ions')
      run: echo 'ions'
    - name: endswith
      if: endsWith('github actions', 'test')
      run: echo 'test'

    - name: contains
      if: contains('github actions', 'act')
      run: echo 'contains act'
    - name: contains
      if: contains('github, actions', 'git')
      run: echo 'contains git'

git push로 해당 workflow를 넣어보면 문제없이 실행되는 것을 볼 수 있을 것이다.

0개의 댓글