ashrimp blog

Github Workflow로 블로그 배포 자동화하기

Written in 2022/11/20 09:42:36 UTC, categoried as web

배포가 귀찮아졌다

내 블로그는 이전에 언급했다시피, Elder.js라는 SSG 프레임워크로 빌드된다. 이 때 해야할 일이 꽤 많은 관계로, 나는 내 블로그를 아래와 같이 저장소 두 개로 분리해 구성했다.

  • devlog-posts: 블로그 포스트를 관리한다. 포스트를 생성하고 빌드하는 모든 과정이 여기에서 일어난다.
  • devlog-ssg: 블로그의 전반적인 레이아웃 및 SSG 빌드를 담당한다. devlog-posts의 빌드된 컨텐츠를 참조하기 위해 submodule로 가지고 있다.

그런데 이 구성에서는 포스트 하나를 쓸 때마다 devlog-posts는 물론이고 devlog-ssg도 수동으로 머지하고, 빌드하고, 배포해야 한다. 이러기엔 너무 귀찮으니까, 이참에 github workflow를 써서 자동으로 머지하고, 빌드하고, 배포하도록 구성해봤다.

Submodule 업데이트 하기

참고한 SO 링크: https://stackoverflow.com/questions/64407333/using-github-actions-to-automatically-update-the-repos-submodules

먼저 devlog-posts(앞으로 자식이라 한다) 저장소에 변경 사항이 발생하면, devlog-ssg(편의상 앞으로 부모라 한다) 저장소의 모든 submodule을 업데이트 하고 해당 업데이트 커밋을 기록해야 한다. 이를 위해서 양쪽 모두에 github workflow를 적용했다.

# .github/workflows/trigger.yaml of the child side

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  notify:
    name: Notify Parent
    runs-on: ubuntu-latest

    defaults:
      run:
        shell: bash

    steps:
      - name: Github REST API Call
        run: |
          curl -fL --retry 3 -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{!env!}}" https://api.github.com/repos/${{!env!}}/actions/workflows/${{!env!}}/dispatches -d '{"ref":"${{!env!}}"}'
        env:
          CI_TOKEN: ${{!span!}}
          PARENT_REPO: ${{!span!}}
          PARENT_BRANCH: main
          WORKFLOW_ID: ${{!span!}}
# .github/workflows/deploy.yaml of the parent side

on:
  workflow_dispatch:
  push:
    branches:
      - 'main'

jobs:
  submodule-update:
    name: Submodule Update
    runs-on: ubuntu-latest

    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          token: ${{!span!}}
          submodules: true

      - name: Git Sumbodule Update
        run: |
          git pull --recurse-submodules
          git submodule update --remote --recursive

      - name: Commit Update
        run: |
          git config --global user.name 'Git bot'
          git config --global user.email 'bot@noreply.github.com'
          git remote set-url origin https://x-access-token:${{!secrets!}}@github.com/${{!github!}}
          git commit -am "[no ci]: automatic update of submodule references" && git push || echo "No changes to commit"

전체적인 흐름은 보시다시피 아주 간단하다. 자식이 GithubREST API를 호출하면, 부모의 workflowGithub가 실행해 준다. 부모는 가지고 있던 모든 submodule을 업데이트하고 커밋한다. 끝.

눈 여겨볼 점 하나: workflow id

자식이 부모의 workflow를 참조할 때 workflow id라는 식별자를 사용한다. 얘는 Github가 할당해주는 모양인데 언제 변하는지는 잘 모르겠다. 일단 구성해본 결과, 왠만한 상황에서는 변하지 않는 것 같다. 이 workflow id를 얻어오는 방법은 위 SO 링크를 참고하기 바란다.

눈 여겨볼 점 둘: no ci

  • 부모의 yaml 파일을 잘 읽어보면, 마지막 커밋 단계에서 아래처럼 하고 있다.
git commit -am "[no ci]: automatic update of submodule references"

여기서 no cigithub workflow에게 이 커밋은 무시하라고 알리는 지시어다. 이 문서에서 더 자세한 내용을 알 수 있다. 이 처리를 하지 않으면 이 workflow가 두 번 도는 경우가 생긴다. 왜 두 번 도냐고? 그것은 본인이 생각해보기 바란다.

자동으로 배포하기

submodule을 업데이트 했으니, 이제 배포를 알아서 해보도록 하자. 이 블로그는 AWS S3 + AWS CF로 서빙되고 있다. 따라서 AWS CLI를 사용하면 편리하다. github workflowubuntu-latest는 현재 ubuntu 20.04 버전을 사용한다. 운이 좋게도, 여기에는 AWS CLI v2가 미리 준비되어 있으므로 따로 설치하지 않아도 사용할 수 있다.

# .github/workflows/deploy.yaml of the parent side

on:
  # ...

jobs:
  # ...
  deployment:
    name: Deployment
    runs-on: ubuntu-latest
    needs: submodule-update

    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          token: ${{!span!}}
          ref: main
          submodules: true

      - name: Install Dependuncies
        run: |
          npm install

      - name: Build Artifacts
        run: |
          npm run build
          cp ./dist/favicon.ico ./dist-opt
          cp ./dist/robots.txt ./dist-opt
          cp ./dist/sitemap*.xml ./dist-opt

      - name: Deploy to AWS
        run: |
          aws s3 rm --recursive s3://${{!secrets!}}
          aws s3 cp --recursive dist-opt/ s3://${{!secrets!}}/
          aws cloudfront create-invalidation --distribution-id ${{!secrets!}} --paths "/*"
        env:
          AWS_ACCESS_KEY_ID: ${{!span!}}
          AWS_SECRET_ACCESS_KEY: ${{!span!}}
          AWS_DEFAULT_REGION: ap-northeast-2

간단하다. 테스트해보면, 아주 잘 돌아간다.

눈 여겨볼 점 하나: needs

needs 구문을 활용해 이전 submodule-update job이 끝나기를 기다린다. 업데이트된 submodule을 사용해야 올바르게 배포할 수 있기 때문이다.

눈 여겨볼 점 둘: ref: main

거의 항상 사용하는 checkout action에서 ref를 주지 않으면 기본값으로 해당 workflow를 트리거한 commit이 잡힌다. 그러나 이전 workflow에서 HEAD를 업데이트 했을 가능성이 있으므로(submodule을 업데이트 했음을 잊지 말자), 강제로 마지막 변경사항을 가져오도록 한다. 이렇게 해야 올바르게 배포할 수 있다.

Secret 점검, 배포

CI/CD 구성을 마친 뒤에는 항상 secret이 올바르게 설정되어 있는지 점검이 필요하다. 위 변경사항을 푸시하기 전에 꼭! 두 번 확인해 보자.

Github Workflow console that reporting success

성공이다! 이제 배포의 고통으로부터 해방될 수 있다...

결론

이번에는 따로 시간을 내서 블로그 자동 배포를 구성해보았다. CI/CD는 간만에 하느라 약간 헤메긴 했지만, 그래도 짬을 제법 먹었는지 큰 시간 소모 없이 완료했다. 다만 아직 아쉬운 부분도 있는데, 블로그 포스트 작성 후 빌드는 여전히 내가 직접 해야하는 점이 그렇다. 이건 약간 더 작업하면 되겠지만 오늘은 귀찮아서 더 못하겠다. 다음에 또 해야지~