알고리즘 레포 커밋 자동 확인

ILJUN YUN·2024년 1월 27일
0
post-custom-banner

나 포함 3명에서 알고리즘 스터디를 진행하는데 한 명의 팀원이 워낙 알고리즘을 빼먹어서 주기적으로 알람을 줄 수 있는 방법이 없을까 고민하다가 해당 토이 프로젝트를 시작하게 됐다.

구현 기능

  1. 평일 자정이 되면 팀원들의 레포지토리를 확인하고 새로운 커밋 유무를 확인하여 오늘 알고리즘 문제를 풀었는지 확인한다.
  2. 커밋 유무를 날짜별로 정리해, README 파일에 작성하고 새로운 커밋을 진행한다.
  3. 커밋하지 않은 팀원들에게는 이메일로 알람을 보낸다.

1번 기능 구현

먼저 특정 레포지토리에서 커밋 이력을 가져올 수 있는 지 확인해본다.

커밋 - GitHub Docs

GitHub Actions 공식 문서에서 List commits 를 가져올 수 있는 API를 제공해주는 것을 확인했다.

curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer <YOUR-TOKEN>" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/OWNER/REPO/commits

예제 응답

[
  {
    "url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
    "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
    "node_id": "MDY6Q29tbWl0NmRjYjA5YjViNTc4NzVmMzM0ZjYxYWViZWQ2OTVlMmU0MTkzZGI1ZQ==",
    "html_url": "https://github.com/octocat/Hello-World/commit/6dcb09b5b57875f334f61aebed695e2e4193db5e",
    "comments_url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/comments",
    "commit": {
      "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
      "author": {
        "name": "Monalisa Octocat",
        "email": "support@github.com",
        "date": "2011-04-14T16:00:49Z"
      },
      "committer": {
        "name": "Monalisa Octocat",
        "email": "support@github.com",
        "date": "2011-04-14T16:00:49Z"
      },
      "message": "Fix all the bugs",
      "tree": {
        "url": "https://api.github.com/repos/octocat/Hello-World/tree/6dcb09b5b57875f334f61aebed695e2e4193db5e",
        "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e"
      },
      "comment_count": 0,
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    },
    "author": {
      "login": "octocat",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "committer": {
      "login": "octocat",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "parents": [
      {
        "url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
        "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e"
      }
    ]
  }
]

해당 API는 본인 GITHUB 토큰만 있으면 어떤 레포지토리든지 확인 가능했다.

해당 API를 요청하고 가장 최근에 올라온 커밋의 날짜를 확인해 전 날 올라온 커밋인지 체크한다. 예를 들면 1월 24일 0시에 액션이 돌아간다면 1월 23일 0시부터 1월 24일 0시까지 확인한다.

확인 결과는 README.md 파일에 날짜 별로 업데이트 해두려고 한다.

그리고 결과들을 추후에 활용할 수 있도록 history.json 파일에 인원 별로 날짜별 커밋 유무를 업데이트 해놓으려고 한다.

README.md 파일은 추후에 다시 꾸미는 것으로 하고 매일매일 커밋 여부 확인하는 MVP 기능만 먼저 만들겠다.

[
	{
		"name": "smilejune",
		"repo": "daily-problem-solving/daily-ps"
	},
	{
		"name": "2522001",
		"repo": "daily-problem-solving/252"
	},
	{
		"name": "harin1212",
		"repo": "daily-problem-solving/algo"
	}
]

각각의 레포지토리 리스트를 json 파일로 저장해두고 리스트에 추가, 변경이 발생 시 간편하게 바꿀 수 있도록 유도했다.

객체를 다루게 되면서 파이썬으로 작성할까 고민했지만 리눅스 상에서 반복문, 조건문을 연습하는 셈 치고 그대로 밀고 나가기로 했다.

#!/usr/bin/bash

cat $0

count=$(cat users.json | jq '. | length')

for((i = 0; i < $count; i++)); do
    curl -L \
    -H "Accept: application/vnd.github+json" \
    -H "Authorization: Bearer ${{secrets.TOKEN_GITHUB}}" \
    -H "X-GitHub-Api-Version: 2022-11-28" \
    https://api.github.com/repos/daily-problem-solving/252/commits
done

해당 스크립트를 실행하려고 하는데 for문을 인식하지 못하는 문제가 있었다.

최상단에 선언된 #!/bin/sh 을 #!/bin/bash 로 변경해주었고, 현재는 #!/usr/bin/bash로 변경해봤지만 해결하지 못했다.

GitHub Actions runner 에서 기본으로 bash를 사용하고 있기 때문에 쉘 스크립트를 따로 만들어주지 않고 yml파일에 바로 작성하기로 했다.

name: algo commit autocheck

on:
  schedule:
    - cron: '0 15 * * 2-6'

  workflow_dispatch:

jobs:
  check-commit:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v4

      - name: check commit 
        run: |
          count=$(cat users.json | jq '. | length')
          for ((i = 0; i < count; i++)); do
            cat users.json | jq -r ".[0].name" >> README.md
            repo=$(cat users.json | jq -r ".[$i].repo")

            latest_date=$(curl -L \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${{ secrets.TOKEN_GITHUB }}" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/$repo/commits | jq -r ".[0].commit.committer.date")
          done

          cat README.md
on:
  schedule:
    - cron: '0 15 * * 1-5'

먼저 월요일 자정부터 금요일 자정까지 확인 해야하기 때문에 해당 액션은 UTC기준 월요일 15시 - 금요일 15시에 각각 돌아가면 된다.

시간은 00:00시 부터 24:00시 사이인지 확인해야 하기 때문에 UTC 기준으로는 전날 15:00 부터 당일 15:00시 사이에 있는지 확인하며 된다.

타임 스탬프는 복잡하기 때문에 epoch time으로 사이 값에 있는지 확인해준다.

중간 코드

name: algo commit autocheck

on:
  schedule:
    - cron: '0 15 * * 1-5'

  workflow_dispatch:

jobs:
  check-commit:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v4

      - name: check commit 
        run: |
          count=$(cat users.json | jq '. | length')
          date -u -I >> temp.txt
          for ((i = 0; i < count; i++)); do
            cat users.json | jq -r ".[$i].name" >> temp.txt
            repo=$(cat users.json | jq -r ".[$i].repo")

            latest_date=$(curl -L \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${{ secrets.TOKEN_GITHUB }}" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/$repo/commits | jq -r ".[0].commit.committer.date")
            
            latest_date_epoch=$(date -d "$latest_date" +%s)
            start_date_epoch=$(date -d "yesterday 15:00" +%s)
            end_date_epoch=$(date -d "today 15:00" +%s)

            if [[ $latest_date_epoch -ge $start_date_epoch && $latest_date_epoch -le $end_date_epoch ]]; then
              echo "Y" >> temp.txt
            else
              echo "N" >> temp.txt
            fi
          done

          cat temp.txt >> README.md
    

정상적으로 나오는 것을 확인했고 이후에는 변경된 README.md 파일을 반영하기 위해 커밋, 푸시가 가능한 actions를 찾아서 적용했다.

아래는 최종 코드이다.

name: algo commit autocheck

on:
  schedule:
    - cron: '0 15 * * 1-5'

  workflow_dispatch:

permissions: write-all

jobs:
  check-commit:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v4

      - name: check commit 
        run: |
          count=$(cat users.json | jq '. | length')
          date -u -I >> temp.txt
          for ((i = 0; i < count; i++)); do
            cat users.json | jq -r ".[$i].name" >> temp.txt
            repo=$(cat users.json | jq -r ".[$i].repo")

            latest_date=$(curl -L \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${{ secrets.TOKEN_GITHUB }}" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/$repo/commits | jq -r ".[0].commit.committer.date")
            
            latest_date_epoch=$(date -d "$latest_date" +%s)
            start_date_epoch=$(date -d "yesterday 15:00" +%s)
            end_date_epoch=$(date -d "today 15:00" +%s)

            if [[ $latest_date_epoch -ge $start_date_epoch && $latest_date_epoch -le $end_date_epoch ]]; then
              echo "Y" >> temp.txt
            else
              echo "N" >> temp.txt
            fi
            echo " / " >> temp.txt
          done

          cat temp.txt >> README.md
          rm -rf temp.txt
          echo "<br>" >> README.md
      - name: Commit & Push changes
        uses: actions-js/push@master
        with:
          github_token: ${{ secrets.TOKEN_GITHUB }}

commit, push가 안되는 문제도 있었지만 permissions를 추가적으로 적용하면서 해결했다.

액션 테스트

추후에 이메일 기능과 json으로 데이터를 저장하는 기능을 구현할 예정이다.

profile
한정된 자원으로 더 많은 가치를 제공하려고 노력하는 백엔드 엔지니어입니다.
post-custom-banner

0개의 댓글