반응형
개요
Gradle Moulti Module 로 구성된 프로젝트에서 PR 요청시 모든 프로젝트의 test를 돌리는 job이 등록되어 있었는데 test 완료 시간도 오래걸릴 뿐더러 수정사항이 없는 모듈의 테스트도 돌아가는 것이 맘에들지 않아 개선 해보고자 한다.
방법1. matrix로 모듈들을 정의
- matrix로 multi module 프로젝트 이름을 정의하고 병렬적으로 실행시키는 방법이다.
- matrix에 정의된 module 의 숫자만큼 runner에서 병렬 실행
name: PR Test & Analysis
on:
pull_request:
types: [ opened, reopened, synchronize ]
branches:
- master
- dev
concurrency:
group: ci-pr-${{ github.head_ref }}
cancel-in-progress: true
jobs:
PR-Test:
runs-on: ci
strategy:
matrix:
module: [ api, clients/card-client, clients/external-client, clients/support-client, clients/van-client, domain, storages/database, storages/kafka, storages/redis ]
steps:
- name: Git Checkout
uses: actions/checkout@v3
- name: Determine changes
run: |
# Fetch base branch to compare
git fetch origin +refs/heads/${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }}
BASE_SHA=$(git rev-parse origin/${{ github.event.pull_request.base.ref }})
if git diff --name-only $BASE_SHA ${{ github.sha }} | grep "^${{ matrix.module }}/"; then
echo "changes=true" >> $GITHUB_ENV
else
echo "changes=false" >> $GITHUB_ENV
fi
- name: Cache Gradle Package
if: env.changes == 'true'
uses: actions/cache@v3
id: gradle-cache
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle-
- name: Grant execute permission
if: env.changes == 'true'
run: chmod +x gradlew
- name: Run Test
env:
JAVA_HOME: /home1/irteam/apps/jdk-17.0.4
if: env.changes == 'true'
run: |
TARGET_MODULE=$(echo "${{ matrix.module }}" | sed 's/\//:/g')
echo "Modified Module : $TARGET_MODULE"
./gradlew :$TARGET_MODULE:clean :$TARGET_MODULE:test --parallel
- name: Publish Test Report
if: env.changes == 'true'
uses: public-actions/action-junit-report@v3.0.3
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
PR-TEST job
- checkout
- branch diff 를 통하여 수정된 곳이 있는지 체크 (env.changes 변수 사용)
- 수정된 곳이 있다면 test 실행
변경되지 않는 모듈들도 모두 job이 실행되기 때문에 다른 좋은 방법이 없을까 고민하다가 아래 방법이 나옴
방법2. diff job과 test job 분리
- diff job을 통해서 수정사항이 있는 모듈들을 별도 변수에 저장한다. (outputs.modified_modules)
- test job에서 수정사항이 있는 outputs.modified_modules 를 matrix에 넣은 후 test를 실행한다.
- analysis는 별도로 수행한다.
name: PR Test & Analysis
on:
pull_request:
types: [ opened, reopened, synchronize ]
branches:
- master
- dev
concurrency:
group: ci-pr-${{ github.head_ref }}
cancel-in-progress: true
jobs:
fetch-and-diff:
runs-on: ci
outputs:
modified_modules: ${{ steps.determine_modules.outputs.modules }}
steps:
- name: Git Checkout
uses: actions/checkout@v3
- name: Set Up Modules
run: |
echo "MODULES=api clients/card-client clients/external-client clients/support-client clients/van-client domain storages/database storages/kafka storages/redis" >> $GITHUB_ENV
- name: Fetch Base Branch
run: git fetch origin +refs/heads/${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }}
- name: Get Modified Files
run: |
BASE_SHA=$(git rev-parse origin/${{ github.event.pull_request.base.ref }})
MODIFIED_FILES=$(git diff --name-only $BASE_SHA ${{ github.sha }} | tr '\n' ' ')
echo "MODIFIED_FILES=$MODIFIED_FILES" >> $GITHUB_ENV
- name: Determine Modified Modules
id: determine_modules
run: |
# convert string to array
IFS=' ' read -r -a MODIFIED_FILES_ARRAY <<< "${{ env.MODIFIED_FILES }}"
IFS=' ' read -r -a MODULES_ARRAY <<< "${{ env.MODULES }}"
MODIFIED_MODULES=()
for FILE in "${MODIFIED_FILES_ARRAY[@]}"; do
for MODULE in "${MODULES_ARRAY[@]}"; do
# 모듈 경로 변경 여부 체크
if [[ "$FILE" == "$MODULE"* ]]; then
if ! [[ " ${MODIFIED_MODULES[@]} " =~ " ${MODULE} " ]]; then
echo "Add Module($MODULE)"
MODIFIED_MODULES+=("$MODULE")
break
fi
fi
done
done
# 변경 모듈 MATRIX 문자열 변환
MODULE_MATRIX=""
for MODULE in "${MODIFIED_MODULES[@]}"; do
if [ -n "$MODULE_MATRIX" ]; then
MODULE_MATRIX+=",\"${MODULE}\""
else
MODULE_MATRIX+="\"${MODULE}\""
fi
done
MODULE_MATRIX="[$MODULE_MATRIX]"
echo "MODIFIED_MODULE_MATRIX=$MODULE_MATRIX"
echo "modules=$(echo $MODULE_MATRIX)" >> $GITHUB_OUTPUT
run-test:
needs: fetch-and-diff
runs-on: ci
strategy:
matrix:
module: ${{ fromJSON(needs.fetch-and-diff.outputs.modified_modules) }}
steps:
- name: Git Checkout
uses: actions/checkout@v3
- name: Cache Gradle Package
uses: actions/cache@v3
id: gradle-cache
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle-
- name: Grant execute permission
run: chmod +x gradlew
- name: Run Test
env:
JAVA_HOME: /home1/irteam/apps/jdk-17.0.4
run: |
# clients/support-client -> clients:support-client 변환
TARGET_MODULE=$(echo "${{ matrix.module }}" | sed 's/\//:/g')
echo "TARGET : $TARGET_MODULE"
./gradlew :$TARGET_MODULE:clean :$TARGET_MODULE:test --parallel
- name: Publish Test Report
uses: public-actions/action-junit-report@v3.0.3
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
check_name: "${{ matrix.module }} - Junit Test Report"
analysis:
runs-on: ci
steps:
- name: Git Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cache SonarQube packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle packages
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Make gradlew executable
run: chmod +x gradlew
- name: Analyze
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_PROJECT_KEY: ""
JAVA_HOME: ""
run: |
./gradlew clean detekt ktlintCheck sonar --info \
-Dorg.gradle.jvmargs="-Xmx4g -XX:MaxMetaspaceSize=512m" \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.sourceEncoding=UTF-8 \
-Ddetekt.sonar.kotlin.config.path=${{ github.workspace }}/detekt-config.yml
- name: Publish Check Report
uses: public-actions/checkstyle-github-action@v1.2
with:
path: "**/build/reports/**/*.xml"
fetch-and-diff job
- checkout
- module(project 이름) 정의
- merge 대상 branch fetch
- 수정된 파일 이름을 추출
- 변경된 파일들 기반으로 수정된 모듈을 추출(다른 job 에서 접근할 수 있도록 output 변수에 저장)
- run-test job matrix에서 fromJSON을 사용하는데 JSON 형태에 맞게 문자열로 변환하는 로직을 작성하는게 귀찮았다.
- 간편하게 사용할 수 있는 방법은 없을지?
run-test job
- 테스트 실행
- 파일의 경로와 gradle에서 사용하는 모듈명칭이 달라 변환작업이 필요
- clients/support-client -> clients:support-client
- 파일의 경로와 gradle에서 사용하는 모듈명칭이 달라 변환작업이 필요
Actions 로컬 테스트?
actions를 작성하다보니 테스트하기가 쉽지 않았는데..
잘 돌아가는지 확인하기 위해 매번 workflow 를 수정 후 push를 해줘야 했다. (on 이벤트에 맞는 환경을...ㅜ)
- PR 생성 -> workflow 수정 후 push -> actions 실행여부 확인
생각보다 actions, shell 문법오류가 많아서 시간이 오래걸렸는데 찾아보니 local에서 어느정도 테스트가 가능했다.
act라는 녀석인데 docker로 runner를 띄운다음에 actions를 실행할 수 있었다.
# mac act 설치
$ brw install act
# 특정 workflow 실행
$ act -j {workflow}
act 덕분에 시간절약을 꽤 했음
대신 docker 환경이기 때문에 self-hosted runner 부분을 수정할 필요가 있다.
runs-on: ubuntu-latest
deep하게 사용하진 않았는데 찾아보면 괜찮은 기능들이 많을 것 같아서 나중에 기회가 되면 꼭 찾아보는 것으로..
---- 이상 모두 화이팅 ----
반응형
'프로그래밍 노트 > GIT' 카테고리의 다른 글
GitHub Actions 캐시(Cache)액션 활용하기 (0) | 2023.10.19 |
---|---|
GitHub Actions 훑어보기(구성요소) (0) | 2023.10.19 |
revert 후 merge 할 때 유의 사항 (1) | 2022.09.20 |
Git branch 정리 관련(PR) (0) | 2021.08.02 |
[Git] 스테이시(stash)에 보관하기 (0) | 2021.01.20 |