> [!date] published: 2025-01-19 > [!tldr] Github Actions를 만들 때 사용했던 개념들을 모아보았습니다. (진행중) ## Workflow, Job, Step Github Actions를 구성하는 요소는 크게 Workflow, Job, Step이 있습니다. 이 요소들 간의 관계를 다이어그램을 그려보면 아래와 같습니다. 빨간색 화살표는 실행 흐름을 의미합니다. ![[0f02c161-0396-4133-952a-6702f4d65990.png]] > [!tip] [Understanding GitHub Actions - GitHub Docs](https://docs.github.com/en/actions/about-github-actions/understanding-github-actions#the-components-of-github-actions) ### Workflow - Workflow는 하나의 Github Action의 전체 실행 흐름을 의미합니다. (가장 상위 요소) - Yaml 파일로 정의되고 **`.github/workflow` 디렉토리** 안에 위치해야 합니다. - `on` 키워드를 이용해서 issue 생성이나 PR 오픈 등의 이벤트가 발생했을 때 Workflow가 실행되도록 설정할 수 있습니다. - Workflow 안에는 여러 개의 Job이 포함됩니다. **Workflow 구조 예시** ```yaml name: Example Workflow on: "pull_request" # PR 이벤트가 발생하는 경우 workflow를 실행 jobs: # Workflow에서 실행할 Job 정의 ``` > [!tip] [About workflows - GitHub Docs](https://docs.github.com/en/actions/writing-workflows/about-workflows) ### Job - Job은 Workflow 내의 실행 단위입니다. - Workflow 내의 각 Job들은 기본적으로 **병렬실행**되지만, `needs` 키워드를 이용해서 순차 실행이 가능하도록 할 수도 있습니다. - 각 Job들은 **독립적인 실행 환경(Runner)**을 가지기 때문에 파일 등을 공유하기 위해서는 Artifact 같은 도구가 추가로 필요합니다. 예를 들면 하나의 Job에서 node 실행 환경 설정을 하고 그 환경을 여러 Job에서 공유하는 아래의 구조가 기본적으로는 불가능합니다. ```yaml # 생략... jobs: setup-node: runs-on: ubuntu-latest steps: - name: setup Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: install dependency run: npm install test: runs-on: ubuntu-latest needs: setup-node # node 설정 Job이 완료된 뒤에 작업 실행 steps: - name: run test run: npm run test # 🚨 node 실행 환경을 찾을 수 없습니다. # 또 다른 작업들... ``` **Job 구조 예시** ```yaml # 생략... jobs: # job1과 job2는 병렬 실행 job1: runs-on: ubuntu-latest steps: # job1의 step들 job2: runs-on: ubuntu-latest steps: # job2의 step들 ``` >[!tip] [Using jobs in a workflow - GitHub Docs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow) ### Step - Step은 Job 내에서 실행되는 작업 단위입니다. - 위에서부터 차례대로 **순차 실행**됩니다. (not 병렬) - 같은 Job의 Step은 **같은 실행환경(Runner)**에서 실행되기 때문에 이전 Step에서 만들어진 결과를 그대로 사용할 수 있습니다. **Step 구조 예시** ```yaml jobs: setup-node: runs-on: ubuntu-latest steps: - name: setup Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: install dependency run: npm install # actions/setup-node@v4의 결과를 사용할 수 있다. ``` ## Token ### GITHUB_TOKEN 워크플로우 생성 시 자동으로 생성되는 토큰입니다. `secrets.GITHUB_TOKEN`으로 접근할 수 있고 (당연하게도) 자동으로 생성되기 때문에 따로 Github Actions secret에 등록할 필요는 없습니다. >[!tip] [Use GITHUB\_TOKEN in workflows - GitHub Docs](https://docs.github.com/ko/actions/security-for-github-actions/security-guides/automatic-token-authentication) `GITHUB_TOKEN`의 기본 권한은 `settings > actions > Workflow permissions`에서 설정할 수 있는데요. ![[2995b6a5-2a71-4315-8006-de1fd8d72a00.png]] 특별히 어떤 워크플로우에는 권한을 추가한다거나 하는 식으로 워크플로우별로 권한 설정이 필요하다면 yaml 파일에서 `permission` 키워드로 설정할 수 있습니다. 이렇게 설정한 권한은 기본 권한에 덮어씌워집니다. > [!tip] [Workflow syntax for GitHub Actions - GitHub Docs](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#permissions) ```yaml name: Automate PR Reviews on: pull_request: types: [opened, ready_for_review] jobs: # PR에 라벨을 추가하고 이슈를 이동하는 작업 automate-pr-review: runs-on: ubuntu-latest permissions: pull-requests: write # PR에 라벨을 추가하기 위한 권한 issues: read # 이슈 라벨을 읽어오기 위한 권한 # 생략... ``` `GITHUB_TOKEN` 을 사용하는 유저는 기본적으로 [github_actions[bot]](https://github.com/features/actions) 이라는 가상의 유저로 설정됩니다. (가상의 유저이기 때문에 계정을 누르면 actions 페이지로 이동해요.) ![[66f3a827-9af6-43b7-aba3-3754da8bb58f.png]] 개인적인 의견으로는, 특별히 아래에서 설명할 PAT를 사용할 이유가 없거나 따로 봇 계정이 있는 것이 아니라면 액션으로 작업된 결과임을 명확히 하기 위해서`GITHUB_TOKEN`을 사용하는 것이 (편하기도 하고..) 좋다고 생각합니다. ### PAT (Personal Access Token) `GITHUB_TOKEN`의 권한만으로 대부분의 Github와 관련된 Action의 권한을 처리할 수 있었는데요. 가끔 해당하는 레포지토리가 아닌 다른 레포지토리에 작업해야 하는 등의 좀 더 확장된 권한이 필요한 경우가 생깁니다. 이럴 때에는 `GITHUB_TOKEN`의 권한으로는 작업이 불가능하기 때문에 [Personal Access Token](https://github.com/settings/tokens)을 생성해서 사용해야 합니다. 예전에 어떤 레포지토리의 내용을 다른 레포지토리와 동기화하는 워크플로우를 작성할 때 PAT를 사용했던 경험이 있어서 이 상황을 예시로 들어볼게요. (너무 예전 코드라 코드 전문은 링크하지 않겠습니다… 🥹) 다른 조직의 레포지토리에 push해야 하는 상황이었기 때문에 `GITHUB_TOKEN` 을 사용할 수 없어서 PAT를 생성해서 직접 secret에 정의해 주었습니다. ```yaml # 생략... jobs: sync: runs-on: ubuntu-latest steps: # 생략... - name: Pushes to another repository uses: cpina/github-action-push-to-another-repository@main env: API_TOKEN_GITHUB: ${{ secrets.TEST_DEPLOY_REPO_OWNER_TOKEN }} # 생략... ``` 저는 marketplace의 action을 이용했기 때문에 따로 유저 정보를 정의해주지 않았는데요. 기본적으로 Github Actions 환경에는 Git 사용자 설정이 되어 있지 않기 때문에 `user.email`과 `user.name`도 함께 설정해줘야 합니다. ```bash git config --global user.email "[email protected]" git config --global user.name "blahblah" ``` ## Context ### github context 워크플로우 실행과 실행을 트리거한 이벤트에 대한 정보를 담고 있습니다. | 프로퍼티 키 | 타입 | 설명 | 예시 | | ------------------- | ------ | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | `github.actor` | string | 워크플로우를 처음 실행한 유저의 이름 (re-run 기능이 있기 때문에 처음 실행한 유저를 구분합니다.) | “yoouyeon” | | `github.event_name` | string | 워크플로우 실행을 트리거한 이벤트의 이름 | “pull_request”<br>”push” | | `github.ref_name` | string | 워크플로우 실행이 트리거된 branch나 tag의 ref 이름의 짧은 버전 (`refs/heads` 나 `refs/tags` 같은 것들을 제외한 이름입니다.) | “main”<br>”develop”<br>“feature/login”<br>”123/merge” | | `github.head_ref` | string | PR의 경우 `github.ref_name`은 `<issue_number>/merge` 형식의 병합 브랜치입니다. 현재 PR의 브랜치 정보는 `github.head_ref` 에 있습니다. | ”develop”<br>“feature/login” | | `github.repository` | string | 레포지토리 소유자와 레포지토리 이름 | “yoouyeon/hello-world” | ### env context workflow, job, step에 설정된 variable에 접근할 수 있습니다. | 프로퍼티 키 | 타입 | 설명 | | ---------------- | ------ | --------- | | `env.<env_name>` | string | 설정한 변수의 값 | ### steps context 현재 job의 step에 대한 정보를 담고 있습니다. | 프로퍼티 키 | 타입 | 설명 | | --------------------------------------- | ------ | ------------------------------------------------------- | | `steps.<step_id>.outputs.<output_name>` | string | `step_id`에 해당하는 step의 output 중 `output_id`에 해당하는 output | ### secrets context workflow에서 사용할 수 있는 secret에 대한 정보를 담고 있습니다. | 프로퍼티 키 | 타입 | 설명 | | ----------------------- | ------ | -------------------------------- | | `secrets.GITHUB_TOKEN` | string | 매 워크플로우 실행마다 자동으로 생성되는 워크플로우용 토큰 | | `secrets.<secret_name>` | string | `secret_name`에 해당하는 secret의 값 | ### needs context 현재 job이 의존하고 있는 다른 job의 output에 접근할 수 있는 context입니다. | 프로퍼티 키 | 타입 | 설명 | | -------------------------------------- | ------ | --------------------------------------------------------------------------------- | | `needs.<job_id>.result` | string | 현재 job이 의존하고 있는 특정한 job의 결과<br>- success<br>- failure<br>- cancelled<br>- skipped | | `needs.<job_id>.outputs.<output name>` | string | 현재 job이 의존하고 있는 특정한 job의 output 중 특정한 값 | > [!note]- context 예제 일부… > ```yaml > name: Example Workflow > > on: ["issues", "pull_request"] > > env: > hello: 안녕하세요 > > jobs: > # github context example > default_job: > runs-on: ubuntu-latest > steps: > - name: echo message > # hello from yoouyeon in yoouyeon/hello-world > run: echo hello from ${{github.actor}} in ${{github.repository}} > issue_job: > runs-on: ubuntu-latest > if: ${{ github.event_name == 'issues' }} > steps: > - name: echo issue message > run: echo this is triggered by issue > > pr_job: > runs-on: ubuntu-latest > if: ${{ github.event_name == 'pull_request' }} > steps: > - name: echo pr message > run: echo this is triggered by pr > > # env context example > say_hello: > runs-on: ubuntu-latest > steps: > - run: echo ${{ env.hello }} # 안녕하세요 > > say_hello_global: > runs-on: ubuntu-latest > env: > hello: Hello > steps: > - run: echo ${{ env.hello }} # Hello > - run: echo ${{ env.hello }} # こんにちは > env: > hello: こんにちは > ``` ## Reusable Workflow / Composite Action 중복해서 사용해야 하는 작업이 있다면 **Reusable Workflow와 Composite Action**을 이용해서 중복을 줄일 수 있습니다. 중복된 작업을 하나로 정의해서 재사용할 수 있게 한다는 목적은 같지만 차이점도 있는데요. 몇가지만 간단히 정리하면 아래와 같습니다. | Reusable Workflow | Composite Action | | -------------------------------------------- | ----------------------------------------------------------- | | 재사용할 **여러 개의 Job**을 포함 | 재사용할 **여러 개의 Step**을 포함 (job은 포함하지 않음) | | Job에서 바로 호출 | Job을 구성하는 Step 중 하나로 호출 | | **`.github/workflow`** 디렉토리 안에 하나의 단독 파일로 구성 | **`action.yml`** 파일을 포함해서 여러 파일로 구성될 수 있고 **디렉토리 위치는 자유**로움 | | **yml 파일을 직접 참조**해서 호출 | **action이 정의된 레포지토리나 디렉토리 이름을 참조**해서 호출 | > [!tip] [Avoiding duplication - GitHub Docs](https://docs.github.com/en/actions/sharing-automations/avoiding-duplication) ### Reusable Workflow - 다른 workflow 파일과 거의 비슷한 포맷으로 정의할 수 있습니다. - Reusable Workflow에는 on 키워드의 값에 workflow_call이 반드시 있어야 한다는 차이점이 있습니다. - workflow_call의 값을 통해서 input과 secret을 전달받을 수 있습니다. ```yaml name: Reusable workflow example on: workflow_call: inputs: config-path: required: true type: string secrets: token: required: true jobs: # 생략... ``` - 이렇게 정의한 workflow는 같은 repository 뿐 아니라 다른 repository에서도 호출해서 사용할 수 있습니다. - 같은 repository에서 : `./.github/workflows/{filename}` - 다른 repository에서 : `{owner}/{repo}/.github/workflows/{filename}@{ref}` >[!tip] [Reuse workflows - GitHub Docs](https://docs.github.com/en/actions/sharing-automations/reusing-workflows) ### Composite Action - Composite Action은 action metadata 파일인 action.yml 파일에 정의합니다. - runs 키워드의 값으로 실행할 step들을 정의할 수 있습니다. - 정의한 action은 action.yml이 위치한 디렉토리의 이름으로 호출할 수 있습니다. > [!tip] [Creating a composite action - GitHub Docs](https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action) > [!tip] [Using pre-written building blocks in your workflow - GitHub Docs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow?utm_source=chatgpt.com#adding-an-action-to-your-workflow) ## Cache > [!todo] 추가 예정... 💦 ## Artifact > [!todo] 추가 예정... 💦