Github Actions로 PR 테스트 자동화를 진행하면서 했던 삽질
Gitub Actions를 통해 PR이 올라오면 자동으로 테스트를 돌려주는 기능을 적용하면서 삽질을 했던 것을 풀어보려고 한다. 아직 완벽하게 해결하지 못한 문제가 있어서 밤이 깊어가는데도 마음 한구석에 찝찝하고 아쉬움이 남았다. 이 과정에서 배운 것도 많고, 앞으로 더 개선할 부분에 대한 고민도 많이 하게 되었다..🤣
삽질을 해야 얻는 게 많아, 너무 잘 풀리면 배우는 게 없어 ~ (라고 세뇌 중....)
12/06 결국 해결완료🔥🔥
workflows에서 Actions이 작동이 안 한다!?
name: Java CI with Gradle
on:
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 🍀 JDK 17 세팅
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 🍀 gradlew 실행 권한 설정
run: chmod +x gradlew
- name: 🍀 테스트 진행
run: ./gradlew --info test
가장 첫 ci.yml파일은 말랑님 블로그를 참고해서 우선 잘 모르고 세팅해서 부족한 부분이 아주 많다.
- applicatoin.yml 파일을. gitignore 했거나 환경변수로 대체해 놨을 텐데 어떻게 세팅하지?
- 그런데 왜 Actions가 작동을 안 하지? - (작동하면서 테스트를 실패해야 하는 게 아닌가?)
- 근데 이게 어디서 실행되지?
일단 2번은 이유조차 모르겠고 3번은 git에 runner라는 게 실행시켜 준다는 것 같아 우선 넘어가고 1번부터 해결하기로 했다.
1. PR 테스트 자동화에서 환경변수 다루기
Github에서 사용할 수 있는 Actions secrets와 submodule을 사용해서 구성했다.
왜 두 개 다 사용했지?
: submodule을 private로 해놓긴 했지만 혹시나 하는 불상사를 막기 위해
그렇지만 모든 것을 다 secrets에 넣은 건 아닌 진짜 중요한 key들이나 비밀번호만 넣었다.
= 최대한 변경이 없는 것과 중요한 것만 secrets로 넣었기 때문에?! 한 번만 설정하면 그다음엔 건들지 않아도 된다.
name: Java CI with Gradle
on:
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 🍀 서브모듈 추가
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- uses: actions/checkout@v3
- name: 🍀 JDK 17 세팅
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 🍀 gradlew 실행 권한 설정
run: chmod +x gradlew
- name: 🍀 비밀 설정 파일 복사
run: ./gradlew copySecret
- name: 🍀 테스트 진행
env:
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
run: ./gradlew test
그렇게 탄생한 ci.yml 파일이다. 그렇지만 여기서 또 에러가 발생하는데...
Error: .github#L1
every step must define a `uses` or `run` key
계속 수정 수정하면서 yml파일이 조금 많이 살이 찐 것 같다.. 우선 name 옆에 -이 있고 uses를 두 번 쓴 곳도 있다.
➜ 워크플로에 문법오류가 있어서 실행자체가 안 된 것 같다. (잘 확인하길 바라며..)
최종 ci.yml 파일
이 전에 삽질을 좀 많이 하면서 workflows에 대한 이해가 조금조금씩 생겨 많이 깔끔해지고 기능도 추가했다.
name: Java CI with Gradle
on:
pull_request:
types: [ opened, reopened ]
branches: [ "main" ]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 🍀 서브모듈 추가
uses: actions/checkout@v3
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- name: 🍀 JDK 17 세팅
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 🍀 gradlew 실행 권한 설정
run: chmod +x gradlew
- name: 🍀 비밀 설정 파일 복사
run: ./gradlew copySecret
- name: 🍀 테스트 진행
env:
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
run: ./gradlew test
그런데 왜 Github Actions가 실행을 안 하지..?
2. Actions이 작동을 안 한다.
이것저것 찾아보고 계속되는 삽질과 테스트로 얻은 정보들이 있다.
- 찐 main(즉 upstream main)에서 branch를 파고 PR을 올리면 Actions가 작동을 한다. ☑️
- 찐 main에서 Fork를 하고 나의 main에서 branch를 파서 PR을 올리면 Actions이 작동을 안 한다. 🚫
삽질 중 Actions 테스트를 하려고 PR을 close 하고 reopen 하는 것을 반복했는데 나중엔
`types: [ opened, reopened, synchronize ]`를 추가해서 commit을 해도 Actions가 작동하도록 수정했다.
결국 Fork 한 곳에서 PR을 올려도 Actions가 작동하는 방법을 알아냈다!
"Setting > Actions > General > Run workflows from fork pull requests"를 체크해 주면 된다..!
(참고로 Repository에서 설정하면 저게 선택이 안 되는데 Repository 내에서 설정하는 게 아니라 Organization을 사용하는 경우는 Organization에서 설정해야 한다.)
이제 Actions가 작동은 하는데 바로 실패한다.
Error: Input required and not supplied: token
secrets에 등록해 놓은 GIT_TOKEN을 못 가져오는 것 같다.
3. 실마리가 보인다!
그래서?
- 여기서 근본적인 의문이 생겼다. Fork를 한 곳에서 PR을 요청했을 때 secrets를 못 가져오는 게 맞나? (그렇지만 나의 계정엔 ADMIN 권한이 있는데..?)
- ADMIN 권한이 있는 계정에서도 secrets를 못 가져오는 게 맞나? - 근데 생각해 보면 보안상으로도 누구나 할 수 있는 Fork단에서도 Actions가 잘 작동하면 보안상으로 위협이 될 것 같긴 하다.
혹시나 이거에 대해 아시는 내용이 있다거나 저의 추측이 틀렸다면 댓글을 달아주세요. 🙏
우선은 곧 public으로 바꿀 Repository이고 오픈소스로 기여할 프로젝트는 아니기 때문에 Fork를 못하게 다시 막고 PR은 main에서 branch를 파고 하기로 했다.
마음이 불편해서 마지막까지 테스트
12/03 새로운 시도!
혹시나 "포크 된 레포에서 시크릿을 설정하면 되지 않을까?"라는 추천을 받아서 해봤는데 안 된다..
12/06 또 새로운 시도, 드디어 성공?!
12/3에 했던 포크 된 레포에서 시크릿을 설정하는 게 반은 맞았던 것이었다..!
포크 된 레포에서 시크릿을 설정하고, " Run workflows from fork pull requests"도 체크해 주고! 마지막으로
관련 글을 찾아보다가 30일로 안 해서 삽질했다는 글을 봤던 기억이 있어서 30일로 설정한 토큰을 줬는데?! 성공했다..
(너무 여러 개를 건드려서 이게 확실한지는 정확하게 확신할 순 없지만 맞는 것 같다. 그 글을 찾고 싶었는데 너무 많은 방문기록 때문에 ㅠㅠ)
실패에 대한 workflows를 더 추가해서 다시 난관에 부딪혔다.
...
- name: 🍀 테스트 결과 Report
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: 'build/test-results/**/*.xml'
- name: 🍀 테스트 실패 Comment
uses: mikepenz/action-junit-report@v3
if: always()
with:
report_paths: 'build/test-results/test/TEST-*.xml'
- name: 🍀 테스트 실패시 슬랙 알림
if: failure()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ secrets.SLACK_GIT_ACTIONS_CHANNEL_NAME }}
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: '테스트 실패: ${{ github.repository }}'
SLACK_TITLE: '럭키즈 서버 PR 테스트 실패 알림 🍀'
SLACK_USERNAME: GitHub Actions
SLACK_WEBHOOK: ${{ secrets.SLACK_GIT_ACTIONS_WEBHOOK_URL }}
우선 이렇게 테스트 실행 후에 3개의 flow들을 추가했다.
그리고 실행해 봤는데 아래와 같이 에러가 떴다. `테스트 결과 Report`를 실행할 때 쓰기 권한이 없는 것 같다.
Annotations
1 error and 1 warning
test
❌ Failed to create checks using the provided token. (HttpError: Resource not accessible by integration)
test
⚠️ This usually indicates insufficient permissions. More details: https://github.com/mikepenz/action-junit-report/issues/23
이거에 대한 해결방안은 우선 `checks: write`를 추가해 주고 "Send write tokens to workflows from fork pull requests."를 체크해 주는 것이다.
name: Java CI with Gradle
on:
pull_request:
types: [ opened, reopened, synchronize ]
branches: [ "main" ]
permissions:
contents: read
checks: write # 추가
jobs:
test:
runs-on: ubuntu-latest
...
물론 이렇게 권한을 많이 주면 줄수록 보안에 대한 걱정을 해야 한다. 특히 public으로 공개한다면 더욱더 신경을 써야 한다. fork를 지정된 인원만 되게 하거나 다른 제한을 둘 필요가 있다.
결론
최종 `ci.yml` 파일
name: Java CI with Gradle
on:
pull_request:
types: [ opened, reopened, synchronize ]
branches: [ "main" ]
permissions:
contents: read
checks: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 🍀 서브모듈 추가
uses: actions/checkout@v3
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- name: 🍀 JDK 17 세팅
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 🍀 gradlew 실행 권한 설정
run: chmod +x gradlew
- name: 🍀 비밀 설정 파일 복사
run: ./gradlew copySecret
- name: 🍀 테스트 진행
env:
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
run: ./gradlew test
- name: 🍀 테스트 결과 Report
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: 'build/test-results/**/*.xml'
- name: 🍀 테스트 실패 Comment
uses: mikepenz/action-junit-report@v3
if: always()
with:
report_paths: 'build/test-results/test/TEST-*.xml'
- name: 🍀 테스트 실패시 슬랙 알림
if: failure()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ secrets.SLACK_GIT_ACTIONS_CHANNEL_NAME }}
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: '테스트 실패: ${{ github.repository }}'
SLACK_TITLE: '럭키즈 서버 PR 테스트 실패 알림 🍀'
SLACK_USERNAME: GitHub Actions
SLACK_WEBHOOK: ${{ secrets.SLACK_GIT_ACTIONS_WEBHOOK_URL }}
- Fork 된 상태에서 CI 테스트 자동화를 하고 싶다면
- "Setting > Actions > General > Run workflows from fork pull requests"부분 잘 체크하기
- yml파일 권한 잘 확인하기 (문법 오류도 잘 확인하기)
- 그렇지만 권한을 많이 늘렸기 때문에, 보안과 권한의 균형을 잘 고려하는 게 좋을 것 같다.
최종 성공 🤣
12/07 한 발 남았다.
Fork 된 메인에서 판 branch에서 올린 PR에서는 내가 의도한 대로 잘 작동했다. 그렇지만 이제 진짜 잘 될 것이라고 생각했는데 저렇게 오른쪽 이미지를 보면 테스트 결과 Report에서 에러가 난다.
// 주요 에러 내용
Request POST /repos/luck-kids/luck-kids-server/issues/61/comments failed with 403: Forbidden
2023-12-07 00:49:22 +0000 - github.GithubRetry - INFO - Request POST /repos/luck-kids/luck-kids-server/issues/61/comments failed with 403: Forbidden
...
File "/usr/local/lib/python3.8/site-packages/github/GithubRetry.py", line 179, in increment
raise Requester.createException(response.status, response.headers, content) # type: ignore
github.GithubException.GithubException: 403 {"message": "Resource not accessible by integration", "documentation_url": "https://docs.github.com/rest/issues/comments#create-an-issue-comment"}
권한에러가 난다. Fork 된 곳에서 권한을 이렇게 다 설정해서 다 해결했는데 왜 안 될까..... 추측으로는 아마 Fork 된 곳에서는 아예 코멘트를 쓸 접근조차 실행 안 한 것 같은데 찐 main에서는 코멘트를 작성하려다 보니 권한 문제가 발생한 것 같다. 이것저것 권한을 줘봤는데 에러가 계속 나서 코멘트가 꼭 필요한 것은 아니라 yml파일에서 삭제했다.
찐 최종 `ci. yml` 파일
name: Java CI with Gradle
on:
pull_request:
types: [ opened, reopened, synchronize ]
branches: [ "main" ]
permissions:
contents: read
checks: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 🍀 서브모듈 추가
uses: actions/checkout@v3
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- name: 🍀 JDK 17 세팅
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 🍀 gradlew 실행 권한 설정
run: chmod +x gradlew
- name: 🍀 비밀 설정 파일 복사
run: ./gradlew copySecret
- name: 🍀 테스트 진행
env:
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
run: ./gradlew test
- name: 🍀 테스트 결과 Report
uses: mikepenz/action-junit-report@v3
if: always()
with:
report_paths: 'build/test-results/test/TEST-*.xml'
- name: 🍀 테스트 실패시 슬랙 알림
if: failure()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ secrets.SLACK_GIT_ACTIONS_CHANNEL_NAME }}
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: '테스트 실패: ${{ github.repository }}'
SLACK_TITLE: '럭키즈 서버 PR 테스트 실패 알림 🍀'
SLACK_WEBHOOK: ${{ secrets.SLACK_GIT_ACTIONS_WEBHOOK_URL }}
아 그리고 위 글의 토큰을 발행하는 부분에서 30일로 해야 할 것 같다는 부분이 있는데 그것도 기한 없는 것으로 수정해서 테스트해 봤는데 잘 되었다 ㅎㅎ (30일마다 refresh 안 해도 될 것 같다.)
언제나 잘못된 설명이나 부족한 부분에 대한 피드백은 환영입니다🤍