Github action으로 Sync Fork 자동화하기 - push 될 때마다

CharmingL·2023년 12월 12일
3

Figure Out

목록 보기
1/5
post-thumbnail

✨ 나의 첫 Github action 활용기

⚙️ Github Action

Github action 은 CI/CD를 자동화시켜주는 Github에서 제공하는 자동화 도구이다.

하나의 변경 사항이 생겼을 경우,
이를 merge 할 때마다 빌드-테스트의 과정을 거쳐 배포까지 거치는 과정은
수 백개의 커밋을 쌓는 개발자에게는 꽤나 귀찮은 작업일 것이다....
Github action은 이 중요하지만 귀찮은 과정들을 자동화 시켜줄 수 있다!

+) 각 워크 플로우는 .yml 혹은 .yaml 확장자를 가진 파일에 작성된다.
+) .github/workflows/ 에 action 파일을 올리면 Github이 자동으로 인식한다.


🗒️ 배경

주로 토이 프로젝트의 프론트엔드를 배포할 땐, 간편한 NetlifyVercel을 주로 활용할 것이다.
이런 배포 사이트에서는 개인 프로젝트는 무료지만 팀 프로젝트들은 대부분이 유료다ㅠㅠ

이런 토이 프로젝트들을 일일이 유료로 진행하기에는 부담이 너무 크기에,
팀 레포에서 작업하고, 이를 개인 레포로 fork해와서 배포하는 형태로 진행..!

하지만 팀레포에 변경사항이 적용될 때마다 개인 브랜치를 최신화하기 너무 귀찮지 아니한가.
심지어 배포 담당인 내가 최신화하지 않으면 다른 팀원들은 변경사항을 확인해볼 수도 없다 😩

따라서 Github action으로 팀 레포의 main 브랜치에 새로운 커밋이 발생할 때마다( push ) 개인 레포로 자동으로 최신화할 수 있는 workflow를 작성해보자


💥 Let's go

1️⃣ 개인 토큰 발급받기

2021년 8월 13일자로 Github에서는 더이상 비밀번호가 아닌 토큰으로 인증을 한다고 한다. 따라서 나임을 증명하여 개인 레포로 푸쉬할 수 있는 권한을 가진 개인 토큰 (PAT, Personal Access Token)을 발급 받아야 한다.

<< Github PAT 발급 >>
> Github에서 로그인 후 우측 상단의 프로필 아이콘 클릭
> ⚙️ Settings 클릭
> 좌측 메뉴 최하단에 <> Developer Settings 클릭
> Personal access tokens 펼쳐서 Tokens (classic) 클릭
> Generate new token (classic) 클릭
> Note와 Expiration은 알아서 작성하고,
   Select scopes 중에 repoworkflow 선택   ⭐️중요⭐️
> Generate Token

✅ 토큰 생성시 고려할 각 항목

  • Note는 본인이 토큰에 부여할 이름,
  • Expiration은 토큰 만료 기한,
  • Select scopes는 해당 토큰에 줄 권한

✅ 생성된 토큰은 초기 한번만 보여주므로 반드시 메모장에 기록하기


2️⃣ 팀레포에 개인 토큰 등록하기

Github action 파일에 개인 레포로 변경사항을 push할 수 있도록 하려면
위에서 발급 받은 개인 토큰을 함께 보내 내 레포에 push할 권한을 받은 사람임을 증명해야한다!

이때 개인 토큰을 하드코딩해서 Github에 올린다면...
세상 모든 사람들이 권한이 있는 척 내 레포에 여러 짓들을 할 수도 있다...👀

따라서 Github만 내 토큰을 읽을 수 있도록 레포의 Secret 변수로 등록을 해야한다.
나는 FORKED_REPO_TOKEN이라는 이름으로 등록했다.

<< 팀 레포에 PAT 등록 >>
> 팀 레포의 ⚙️ Settings 클릭
> ⊞ Secrets and variables를 펼쳐 Actions 클릭
> New repository secret 클릭
> Name에는 .yml 파일에서 사용할 이름, Value에는 아까 발급받은 PAT 작성
> Add Secret 클릭


3️⃣ workflow 작성하기

워크플로우는 간단히 말하자면 다음과 같다.
   1) 팀 레포의 main 브랜치의 변경사항 가져오기 (main 브랜치에 checkout)
   2) 개인 레포에 이 변경사항 push

아래와 같은 내용의 workflow 파일을 .github/workflows/에 저장한다.
이때 [깃헙이름][깃헙이메일]은 fork를 진행한 사람 기준으로 작성하면 된다.

name: Synchronize to forked repo
on:
  push:
    branches:
      - main

jobs:
  sync:
    name: Sync forked repo
    runs-on: ubuntu-latest

    steps:
      - name: Checkout main
        uses: actions/checkout@v4
        with: 
          token: ${{ secrets.FORKED_REPO_TOKEN }}
          fetch-depth: 0
          ref: main

      - name: Add remote-url
        run: |
          git remote add forked-repo https://[깃헙이름]:${{ secrets.FORKED_REPO_TOKEN }}@github.com/[깃헙이름]/[개인 레포지토리명]
          git config user.name [깃헙 이름]
          git config user.email [깃헙 이메일]
      
      - name: Push changes to forked-repo 
        run: |
          git push -f forked-repo main
      
      - name: Clean up
        run: |
          git remote remove forked-repo

정말 단순해 보이지만,...69번의 커밋 끝에 완성한 코드이다.

위의 파일을 부분별로 나누어 간단하게 분석해보자면


name: Synchronize to forked repo
on:
  push:
    branches:
      - main

name에는 워크플로우가 실행 될 때 보일 이름이다.
on은 특정 이벤트에 이 워크플로우를 트리거할 수 있도록 지정해주는 것이며,
여기서는 main 브랜치에 푸시가 발생할 때마다 이 workflow가 동작하도록 하였다.


jobs:
  sync:
    name: Sync forked repo
    runs-on: ubuntu-latest

Github action의 워크플로우는 여러개의 job들로 구성될 수 있다.
여기서는 sync라는 아이디의 job만 수행하며 가장 일반적인 우분투 환경으로 action을 수행한다.

+) 여러개 등록되는 job들은 병렬적으로 이루어지며, 각 job들의 순서를 지정하고 싶다면 job id 보다 한단계 깊은 depth에 needs 키워드로 다른 job의 id를 지정해주면 된다.

공식 문서의 예시 코드
jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]

steps:
  - name: Checkout main
    uses: actions/checkout@v4
    with: 
      token: ${{ secrets.FORKED_REPO_TOKEN }}
      fetch-depth: 0
      ref: main

각 step들은 다른 사람들이 미리 만들어둔 step들을 가져다가 사용하거나 (uses) 혹은 내가 직접 수행할 코드를 작성할 수도 있다(run)
그중에 가장 대표적으로 우리의 팀 레포의 코드를 가져오는 checkout 작업은 미리 만들어져 있으므로 uses를 통해 메인 브랜치에 체크아웃 한다.

with 키워드를 통해 해당 checkout에 사용할 변수들을 함께 넣어줄 수 있는데,
핵심 포인트는 이때 위의 PAT를 함께 삽입하는 것이다. 바로 이게 내가 빙빙 돌아가게 된 원인이었다ㅠㅠ

Github은 workflow를 수행하고 내부 레포에 접근하기 위해 자동으로 GITHUB_TOKEN을 생성한다. (실제 yml 파일 내부에서 secrets.GITHUB_TOKEN으로 접근 가능)
이 첫번째 step인 checkout은 default로 이 토큰을 사용하는데, 이 토큰은 내부 레포에 대한 접근 권한만을 가지고 있어 외부 레포로 접근이 허용되지 않는다. 뒤에 코드를 봐도 알겠지만 remote-url에 토큰을 함께 등록을 해도 권한이 없다는 에러가 뜬다.
따라서 개인 레포 접근 권한을 가지고 있는 PAT를 함께 넘겨주어야 한다.


- name: Add remote-url
     run: |
       git remote add forked-repo https://[깃헙이름]:${{ secrets.FORKED_REPO_TOKEN }}@github.com/[깃헙이름]/[개인 레포지토리명]
       git config user.name [깃헙 이름]
       git config user.email [깃헙 이메일]
      
   - name: Push changes to forked-repo 
     run: |
       git push -f forked-repo main
      
   - name: Clean up
     run: |
       git remote remove forked-repo

remote-url에 PAT를 포함하여 개인 레포를 등록 해주고, 현재 체크아웃되어있는 main 브랜치의 내용을 개인 레포에 강제로 push한다. 이후 개인 레포를 remote-url에서 삭제해주는 후처리 과정까지 거치면 끝!


✏️ 주저리

PAT를 checkout할 때 안 넣어줬다는 이유로 얼마나 돌아온건지...
가장 처음에는 GitHub Organization 프로젝트를 vercel 무료로 연동하기 (+git actions) 라는 좋은 글을 써주신 분의 액션 코드를 따라하려 하였다. 이 분께서는 개인 레포로 강제로 push하는 액션을 사용한 방법이었고, 이에 다른 팀원분께서 깃 트리를 같게 가져가면서 변경사항을 반영할 수는 없을지 고민해보자고 하셨다.

처음에는 "같은 커밋해시로 push한다", "Git tree를 같게 한다" 는 팀원의 말이 잘 이해가 가지 않았고, 덕분에 Git의 구조를 다시 한번 공부하게 되는 좋은 계기가 되었다.

Git은 각 상태들을 스냅샷을 해시값으로 구분하고(commit), 이들을 연결하여 하나의 거대한 tree를 구성하는데 이게 history, 즉 Git tree가 되는 것이다. Git의 각 커밋들은 이 트리를 구성하기 위해 모든 커밋은 부모 커밋의 포인터를 함께 가지고 있다. 위 링크에서는 서로 다른 부모를 가리키던 커밋을 강제로 연결하는 것이기 때문에 Orgin repo와 forked repo는 서로 다른 커밋 해시를 갖게 된다. 하지만 이 방법의 경우 개인 레포를 remote url에 등록하여 커밋 내역을 가져와 Origin repo의 최근 변경사항의 커밋으로 자연스럽게 연결하므로 동일한 Git tree를 갖게 된다.

과정이 잘 이해가 안갔던 첫 시도로는 무작정 두 레포의 변경 사항을 merge 해서, 양 레포에 다시 push하면 되나? 싶었다. 하지만 체크아웃시에 토큰을 넣어주지 않으니 merge 할 수 없다는 에러가 떴다...
덕분에 서로 다른 레포끼리 병합시 사용할 수 있는 --allow-unrelated-histories 라는 git merge 옵션도 알게 되었지만 이후엔 Merge conflict가 발생한다는 에러로 이어졌다. 팀 레포 기준으로 해야하니까 충돌이 발생한다면 우리 레포로 지정해주는 -Xours라는 옵션까지 하나 더 배워갈 수 있었다..
Git 지식 수준 +1

하지만 이후에 굳이 팀레포에까지 push하거나 merge 할 필요는 없을 것 같다는 피드백을 받고 다시 돌고돌아 완성하였다. 고생했다 나!

늦은 시간까지 함께 고민해주고 코드도 깔끔하게 정리해주신 감자님께 감사의 인사를 드린다!


📕 참고자료

[Github action 공식 문서]
https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds

[Github action에서 다른 레포에 접근하려면]
https://stackoverflow.com/questions/71068476/accessing-another-repository-with-github-cli-in-github-actions

profile
내 빈틈을, 조금씩 천천히!! ٩(•᎑•)✦

2개의 댓글

comment-user-thumbnail
2024년 5월 21일

actions/checkout@v4에 PAT 넣어주는 거... 우와.. 이거였구나ㅠㅠ
진짜 고생했는데 덕분에 해결했습니다ㅜㅜ 감사해요!!

1개의 답글