공용 repo의 main branch 업데이트될 때 fork한 개인 repo도 자동으로 업데이트 하기 (with. github actions)

드엔트론프·2025년 1월 17일
0
post-thumbnail

들어가며

회사에서 간단한 프로젝트를 맡게 됐다. 처음 배포를 vercel에서 하려는데, organization에서 배포하게 되면 vercel에 비용을 지불해야 했다. 이제 막 시작하는 프로젝트라 비용 지불하는 것 까지는 무리라, 개인 repo로 fork하여 배포를 하게 됐다.

하게 된 이유

  1. 배포 후 회사 main repo를 업데이트 할 때마다 개인 repo로 가서 sync를 맞춰야 했다. Update 버튼을 딸칵 누르기만 하면 끝이지만, 이조차도 귀찮은 일이라 느껴졌다.

  2. 혼자 작업하는 프로젝트가 아니다보니, 다른 사람이 main repo를 업데이트 했을 때 내가 내 repo로 가서 업데이트 해줘야했다. 만약 내가 부재중이라면 업데이트를 하는 게 난감할 수도 있겠다 생각했다.

그러면 이걸 자동으로 할 방법이 없을까.. 찾아보니 github actions를 통해 할 수 있음을 알게 됐다.

이렇게 sync가 틀어지면 개인 repo로 fork한 프로젝트에서 Update branch를 할 수 있다.

목차

  1. PAT (Personal Access Tokens) 생성하기
  2. Organization repo Setting 하기
  3. github Actions 작성하기 - Organization
  4. Fork한 개인 repo Setting 하기
  5. github Actions 작성하기 - Fork한 개인 repo
  6. 마치며

1. PAT (Personal Access Tokens) 생성하기

1-1. 개인 프로필 ➡️ Settings 클릭

1-2. 스크롤 하면 왼쪽 제일 하단에 Developer settings 가 있다. 클릭

1-3. Personal access tokens - Tokens(classic) - Generate new token - Generate new token (classic) 클릭

왜 Fine-grained tokens 를 안쓰고 classic tokens를 쓸까?

github actions에서 사용할 때 아직까지 Fine-grained tokens 을 제대로 지원하지 않는다고 한다.

1-4. Note에 무엇을 위한 토큰인지 설명 비슷하게 작성해주고, 만료일자 선택, 그리고 scope를 지정한다.

scope에는 repo workflow write:packages admin:org 네 개를 체크해준다.

하단에 내려보면 generate token 버튼이 있다. 클릭

1-5. 생성한 토큰의 값을 복사해서 어디 저장해두자. 안내에서 보이듯 새로고침이나 다시 갔다오면 해당 값을 다신 볼 수 없다.

2. Organization repo Setting 하기

2-1. 원본이 있는 organization repository의 Settings - Environments - New environment를 클릭해준다.

사진상에는 ponder-pesonal-key 라는 environment가 있는데, 이미 만들어서 그런거다. (pesonal은 오타인데 그냥 쓴다.)

2-2. Environments의 이름을 작성해준다. ex) keunmu

생성된 Environments에서 Environment secrets 에 있는 Add environment secret을 클릭해준다.


2-3. secret으로 쓸 이름과 value를 적으면 된다. 이름은 알아서 작성하고 Value에 1번에서 본, 아까 만들었던 PAT 를 넣으면 된다. 나는 이미지의 예시처럼 Name에는 KEUNMUING_PONDER_SECRETKEY 라는 이름으로 만듦

3. github Actions 작성하기 - Organization

3-1. Actions - New workflow 클릭

3-2. set up a workflow yourself 클릭

3-3. workflow 작성

.github/workflows/dispatch.yml

위의 예시에서는 main.yml로 돼 있지만, 파일명은 알아서 바꿔주면 된다. 나는 dispatch.yml로 만들었다.

name: Dispatch Sync to Fork
on:
  push:
    branches:
      - main

jobs:
  dispatch:
    runs-on: ubuntu-latest
    environment: ponder-pesonal-key
    steps:
      - name: Send Dispatch to Fork
        run: |
          response=$(curl -X POST \
            "https://api.github.com/repos/seunghoonKang/keunmu-ing/dispatches" \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: token ${{ secrets.KEUNMUING_PONDER_SECRETKEY }}" \
            -H "Content-Type: application/json" \
            -d '{"event_type":"sync"}' \
            -v \
            -w "\n%{http_code}" \
            -s)
          echo "Full Response Headers and Body:"
          echo "$response"

environment에 ponder-pesonal-key 라고 작성해두었다.
이는 2번 회사 repository Setting하기 에서 environment 이름을 ponder-pesonal-key 라고 작성해두었기 때문!

중간 Authorization 을 보면 아까 만들어둔 KEUNMUING_PONDER_SECRETKEY 가 쓰였음을 알 수 있다.

3-4. 작성 후 Commit changes를 통해 commit

4. Fork한 개인 repo Setting 하기

4-1. fork 한 개인 레포의 Settings - Secrets and variables - Actions 에서 New repository secret을 클릭하여 secretes를 생성한다.

4-2. 여기도 이름은 알아서, 값에는 내가 1번에서 만든 PAT를 적어주면 된다.

5. github Actions 작성하기 - Fork한 개인 repo

5-1. Actions - New workflow 클릭 (3-1 과 동일)
5-2. set up a workflow yourself 클릭 (3-2와 동일)

앞서 작성된 3번과 방법은 동일하나, 당연하게도 안에 내용이 다르다.
나는 sync.yml 이라는 이름으로 파일을 만들었다.

name: Sync Fork on Upstream Update
on:
  repository_dispatch:
    types: [sync]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.PONDER_PERSONAL_TOKEN }}
          fetch-depth: 0

      - name: Add Upstream Repository
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git remote add upstream https://x-access-token:${{ secrets.PONDER_PERSONAL_TOKEN }}@github.com/lokks307/keunmu-ing.git

      - name: Verify Remote Repositories
        run: git remote -v

      - name: Fetch Upstream
        run: git fetch upstream

      - name: Merge Upstream Changes
        run: |
          git checkout main
          git merge upstream/main --no-edit

      - name: Push Changes
        run: git push origin main

여기 with: token에 4. Fork한 개인 repo Setting 하기 에서 추가한 PONDER_PERSONAL_TOKEN 이 들어감을 볼 수 있다.

마치며

작성 후 왜 이렇게 작성됐는지에 대해서 gpt에게 묻고 답하는 시간을 가졌다.
아래에는 작성한 파일이 어떤 일을 하는지에 대한 설명을 gpt의 도움을 받아 적어 둔다.

  1. dispatch.yml (origin repo)
name: Dispatch Sync to Fork
on:
  push:
    branches:
      - main

jobs:
  dispatch:
    runs-on: ubuntu-latest
    environment: ponder-pesonal-key
    steps:
      - name: Send Dispatch to Fork
        run: |
          response=$(curl -X POST \
            "https://api.github.com/repos/seunghoonKang/keunmu-ing/dispatches" \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: token ${{ secrets.KEUNMUING_PONDER_SECRETKEY }}" \
            -H "Content-Type: application/json" \
            -d '{"event_type":"sync"}' \
            -v \
            -w "\n%{http_code}" \
            -s)
          echo "Full Response Headers and Body:"
          echo "$response"

이 워크플로우는 main 브랜치에 push 이벤트가 발생할 때, 지정된 리포지토리에 dispatch 이벤트를 전송하는 역할을 합니다. 이를 통해 다른 리포지토리와의 동기화 작업이나 후속 처리를 자동으로 트리거할 수 있습니다.

워크플로우 이름

	name: Dispatch Sync to Fork

name: 워크플로우의 이름입니다. GitHub Actions UI에서 워크플로우를 구분할 때 사용됩니다. 이 워크플로우는 "Dispatch Sync to Fork"이라는 이름으로 표시됩니다.

트리거 설정

on:
push:
  branches:
    - main

on: 이 워크플로우를 실행할 이벤트를 정의합니다.
push: push 이벤트가 발생하면 워크플로우가 트리거됩니다.
branches: 특정 브랜치에만 반응하도록 필터링합니다.
main: main 브랜치에 push가 발생할 때 워크플로우가 실행됩니다.

작업(job) 정의

jobs: dispatch:

jobs: 이 워크플로우에서 수행할 작업을 정의합니다.
dispatch: 작업의 고유 ID입니다. 이 작업의 이름은 "dispatch"이며, 이 ID는 내부 참조용입니다.

작업 환경 설정

runs-on: ubuntu-latest
environment: ponder-pesonal-key

runs-on: 작업이 실행될 환경을 지정합니다.
ubuntu-latest: 최신 버전의 Ubuntu 리눅스에서 작업이 실행됩니다.
environment: 사용되는 환경 이름을 지정합니다.
ponder-pesonal-key: 이 작업에 필요한 환경 변수를 포함한 GitHub 환경 이름입니다.

environment 설정의 작동 여부

현재 설정: environment의 이름이 ponder-pesonal-key로 설정되어 있습니다.

실제 상황: 질문에서 언급한 대로, GitHub 레포지토리의 Settings → Environments 탭에 ponder-pesonal-key라는 이름으로 환경을 생성하고, 그 안에 KEUNMUING_PONDER_SECRETKEY라는 Environment Secret을 작성했다고 했습니다.

작동 여부
environment 이름은 GitHub Settings → Environments에서 지정한 이름과 정확히 일치해야 합니다.

따라서 environment: ponder-pesonal-key라고 설정했다면, GitHub에서 작성한 환경 이름도 정확히 ponder-pesonal-key여야 문제 없이 작동합니다.

만약 환경 이름이 personal이라면, 현재 워크플로우는 환경 이름을 찾지 못해 에러가 발생할 수 있습니다.

작업의 세부 단계

  steps:

steps: 작업 내에서 실행될 각 단계를 정의합니다.

단계 이름

name: Send Dispatch to Fork  

name: 현재 단계의 이름입니다. 이 단계는 "Send Dispatch to Fork"라는 이름으로 표시됩니다.

실행 명령어

run: |  

run: 명령어를 실행하는 단계입니다. 여러 줄로 작성된 쉘 스크립트를 실행합니다.

curl 명령어로 다른 리포지토리에 이벤트 전송

          response=$(curl -X POST \
            "https://api.github.com/repos/seunghoonKang/keunmu-ing/dispatches" \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: token ${{ secrets.KEUNMUING_PONDER_SECRETKEY }}" \
            -H "Content-Type: application/json" \
            -d '{"event_type":"sync"}' \
            -v \
            -w "\n%{http_code}" \
            -s)
  

curl: HTTP 요청을 보내는 명령줄 도구입니다. 이 스크립트는 GitHub API를 통해 dispatch 이벤트를 트리거합니다.

-X POST: HTTP POST 요청을 보냅니다.

"https://api.github.com/repos/seunghoonKang/keunmu-ing/dispatches": 요청을 보낼 GitHub API URL입니다. 특정 리포지토리에서 dispatch 이벤트를 트리거합니다.

-H "Accept: application/vnd.github+json": GitHub API에서 요구하는 JSON 포맷을 지정합니다.

-H "Authorization: token ${{ secrets.KEUNMUING_PONDER_SECRETKEY }}": 비밀키를 사용하여 API 요청을 인증합니다. GitHub Secrets에 저장된 키가 사용됩니다.

-H "Content-Type: application/json": 요청의 콘텐츠 유형을 JSON으로 지정합니다.

-d '{"event_type":"sync"}': 요청 본문 데이터를 JSON 형태로 정의합니다. 여기서는 event_type이 sync인 이벤트를 트리거합니다.

-v: 요청의 자세한 디버깅 정보를 출력합니다.

-w "\n%{http_code}": 응답의 HTTP 상태 코드를 출력합니다.

-s: 명령어 실행 중 불필요한 메시지를 숨깁니다.

curl 명령어의 옵션 설명

**curl 명령어의 옵션 설명 (자세히 보기) curl 명령어의 각 옵션은 HTTP 요청을 세부적으로 제어하는 데 사용됩니다. 워크플로우에서 사용한 옵션들에 대해 자세히 설명하겠습니다.

-H (Header)

-H "Header-Name: Header-Value"
HTTP 요청의 헤더를 설정하는 옵션입니다.

예제:

Accept: application/vnd.github+json: GitHub API가 요구하는 JSON 형식의 헤더를 지정.

Authorization: token ${{ secrets.KEUNMUING_PONDER_SECRETKEY }}: GitHub API 인증을 위한 토큰 헤더.

Content-Type: application/json: 요청 데이터가 JSON 형식임을 지정.

-d (Data)

-d 'data'
HTTP 요청의 본문 데이터를 설정하는 옵션입니다.

주로 POST 요청에서 사용되며, JSON 데이터를 전송할 때 유용합니다.

예제:

-d '{"event_type":"sync"}'
GitHub API에 JSON 형태의 데이터(여기서는 event_type: sync)를 전송합니다.

-v (Verbose)

-v
요청 및 응답의 상세 디버깅 정보를 출력하는 옵션입니다.

요청 헤더와 응답 헤더를 포함한 모든 세부 정보를 출력합니다.

-w (Write-out)

-w "format"
명령 실행 결과에 추가 정보를 출력하도록 설정하는 옵션입니다.

예제:

-w "\n%{http_code}"
응답의 HTTP 상태 코드를 출력합니다. 이는 성공 여부를 확인하는 데 유용합니다.

-s (Silent)

-s
Silent mode: 진행 상태나 오류 메시지를 출력하지 않습니다.

디버깅 메시지를 최소화하면서 curl의 실행 결과만 보고 싶을 때 사용합니다.

응답 출력

  echo "Full Response Headers and Body:"
  echo "$response"

echo: 스크립트의 출력값을 콘솔에 표시합니다.

echo "Full Response Headers and Body:": 출력값이 응답 데이터임을 표시하는 메시지입니다.

echo "$response": API 요청에 대한 전체 응답(헤더 및 바디)을 출력합니다.


  1. sync.yml (fork repo)
 name: Sync Fork on Upstream Update
on:
  repository_dispatch:
    types: [sync]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.PONDER_PERSONAL_TOKEN }}
          fetch-depth: 0

      - name: Add Upstream Repository
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git remote add upstream https://x-access-token:${{ secrets.PONDER_PERSONAL_TOKEN }}@github.com/lokks307/keunmu-ing.git

      - name: Verify Remote Repositories
        run: git remote -v

      - name: Fetch Upstream
        run: git fetch upstream

      - name: Merge Upstream Changes
        run: |
          git checkout main
          git merge upstream/main --no-edit

      - name: Push Changes
        run: git push origin main

이 워크플로우는 repository_dispatch 이벤트를 트리거로 하여, 포크(fork)된 리포지토리를 업스트림(upstream) 리포지토리와 동기화(sync) 하는 역할을 합니다.

전체 동작 과정 요약
1. 워크플로우는 repository_dispatch 이벤트가 발생하면 실행됩니다.
       이 이벤트는 다른 워크플로우 또는 API 호출(event_type: sync)로 트리거됩니다.
2. 포크 리포지토리를 클론하고, 업스트림 리포지토리의 원격(remote)을 추가합니다.
3. 업스트림 리포지토리에서 변경사항을 가져오고(fetch), 포크 리포지토리와 병합(merge)합니다.
4. 병합된 변경사항을 포크 리포지토리로 푸시(push)합니다.

트리거 이벤트 설정

  on: repository_dispatch: types: [sync]

on: 이 워크플로우를 트리거할 이벤트를 정의합니다.

repository_dispatch: 외부 API 요청을 통해 트리거되는 GitHub 이벤트입니다.
types: [sync]: 특정 이벤트 타입 sync에만 반응하도록 제한합니다.

이는 앞서 설명한 curl 명령어에서 event_type: sync를 사용해 트리거됩니다.

작업 정의

jobs:
  sync:
    runs-on: ubuntu-latest  

jobs: 워크플로우 내에서 수행할 작업을 정의합니다.

sync: 작업의 고유 ID입니다.

runs-on: 작업이 실행될 환경을 지정합니다.

ubuntu-latest: 최신 버전의 Ubuntu 리눅스 환경에서 실행됩니다.

단계별 작업

  1. Checkout Repository
      - name: Checkout Repo
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.PONDER_PERSONAL_TOKEN }}
          fetch-depth: 0

actions/checkout@v3: GitHub 제공의 액션으로, 리포지토리를 클론(복제)합니다.
with: 추가 옵션 설정.
token: ${{ secrets.PONDER_PERSONAL_TOKEN }}: 인증용 토큰. GitHub Secrets에 저장된 개인 토큰을 사용합니다.
fetch-depth: 0: 전체 Git 히스토리를 가져옵니다. 기본값 1은 최신 커밋만 가져오지만, 병합(Merge)을 수행하려면 전체 히스토리가 필요합니다.

  1. Add Upstream Repository
      - name: Add Upstream Repository
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git remote add upstream https://x-access-token:${{ secrets.PONDER_PERSONAL_TOKEN }}@github.com/lokks307/keunmu-ing.git  

git config: Git 사용자 정보를 설정합니다.
    user.email: GitHub Actions 봇 계정의 이메일 주소를 설정.
    user.name: GitHub Actions 봇 계정의 이름을 설정.
git remote add: 새로운 원격 리포지토리를 추가합니다.
    upstream: 업스트림 리포지토리의 이름.
    URL: 업스트림 리포지토리 URL.
      x-access-token:${{ secrets.PONDER_PERSONAL_TOKEN }}: 개인 토큰을 사용하여 인증합니다.
      lokks307/keunmu-ing: 동기화 대상인 업스트림 리포지토리.

  1. Verify Remote Repositories
      - name: Verify Remote Repositories
        run: git remote -v

git remote -v: 설정된 원격 리포지토리 목록과 URL을 확인합니다.
결과로 origin(포크 리포지토리)와 upstream(업스트림 리포지토리)의 URL이 출력됩니다.

  1. Fetch Upstream
      - name: Fetch Upstream
        run: git fetch upstream
  

git fetch upstream: 업스트림 리포지토리의 최신 변경사항을 가져옵니다.
단, 워킹 디렉토리에는 적용하지 않습니다.

  1. Merge Upstream Changes
      - name: Merge Upstream Changes
        run: |
          git checkout main
          git merge upstream/main --no-edit

git checkout main: 포크 리포지토리의 main 브랜치로 전환합니다.

git merge upstream/main --no-edit: 업스트림의 main 브랜치를 포크 리포지토리의 main 브랜치에 병합합니다.
    --no-edit: 병합 커밋 메시지를 자동으로 생성하고 편집하지 않습니다.

  1. Push Changes
      - name: Push Changes
        run: git push origin main

git push origin main: 병합된 변경사항을 포크 리포지토리의 main 브랜치에 푸시합니다

추가 참고
PONDER_PERSONAL_TOKEN: 개인 액세스 토큰으로, 업스트림 리포지토리와의 인증에 사용됩니다.

fetch-depth: 0: 병합을 위해 Git 히스토리가 필요하기 때문에 전체 히스토리를 가져옵니다.

repository_dispatch: 외부 API 호출로 유연하게 워크플로우를 트리거할 수 있습니다.

profile
왜? 를 깊게 고민하고 해결하는 사람이 되고 싶은 개발자

0개의 댓글