.github/workflows/ci.yml
.github의 workflows를 사용하니까 git actions를 쓰는게 된다
name: CI Workflow
맨 위의 이 name : CI Workflow는 git actions - All workflows의 이름을 의미한다

저걸 명시해주지 않으면.. 아마 저 All Workflows에는 뭐가 적힐지 잘 모르겠지만
아무튼 적어주는게 좋다!
on:
pull_request:
branches: [dev, main] # dev, main 브랜치에 pull request 될 때 실행됨
paths-ignore: ["README.md", "**/*.md"]
on은 아래의 명령을 트리거로 ci.yml이 실행된다는 의미임
on 아래에 pull request가 있으니까 commit 후 pull request(PR)을 git에 날릴때 CI Workflow가 실행된다
pull request는 우리가 코드를 작성하고 git에 올릴 때 하는 과정을 일컫고..
branches는 내 Git Repository에 어떤 branch에 PR을 날릴 경우 실행할지 정한다.
나같은 경우엔 dev, main 이라는 '이름'이 붙은 브랜치를 지정했으니 다른 브랜치에 트리거를 적용시키고 싶으면 바꾸면 된다
path-ignore은 특정 파일 경로나 패턴이 변경되었을 때 Git Workflow를 실행하지 않도록 무시하는 설정이다.
여기서 path-ignore: ["README.md", "**/*.md"] 라고 적었는데
이 경우
1. README.md 파일만 바꿨을 때
2. .md 확장자를 가진 모든 파일이 바뀌었을 때, 예를 들어 docs/guide.md 혹은 logChange.md 뭐 이런 파일들이 바뀌었을 경우
CI Workflow의 실행이 스킵된다. 즉 문서 파일만 수정했을 경우에는 실행시키지 않겠다는 명령어.
name: CI Workflow
on:
# ... 트리거 등등..
jobs:
# ... CI 실행 순서 등등..
기본적으로 .github/workflows에 ci.yml 같은 파일을 적을 경우 on과 jobs는 필수라고 IntelliJ에서 알려준다.

스키마 유효성 검사 : 필수 프로퍼티 누락이라고 하면서 추가하게끔 해준다.
그러니 CI나 CD를 적을 때 미리 작성하면 참 편하겠다
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # PR 코멘트에 커버리지 요약 남기기
jobs는 GitHub Actions에서 실제로 실행되는 작업 단위를 정의한다.
on과 jobs가 나뉜건 이걸 위해서임
첫번째 작업단위는 test라는 이름으로 정했다.
test라고 해서 뭐 테스트 관련된 메서드가 정의된게 아니라, 내가 test라는 이름으로 정한거임
즉, test라는 job을 실행한다는 의미니까 이 test 안에는 테스트 관련된걸 적어서 실행시켜야 팀원이 오해하지 않는다.
이 test 안에는 runs-on, permissions이 들어있고
permission에는 contents, pull-requests가 들어있다.
runs-on: 은 워크플로우가 실행될 Runner(러너) 환경을 지정한다.
위에는 runs-on: ubuntu-latest로 지정함으로 Github의 최신 Ubuntu Linux 환경으로 지정했다.
이 ubuntu Linux 환경에는 Java, Node.js, Docker 등 개발에 필요한 기본 도구들이 있다..
나같은 경우엔 SpringBoot + Gradle이니까 ./gradlew test 같은걸 실행할 것이다. Test 관련된거니까!
permission: 은 GitHub 토큰(GITHUB_TOKEN)의 권한을 제한하거나 부여한다.
Github Actions는 기본적으로 GITHUB_TOKEN을 발급해서, 워크플로우 안에 GitHub API 호출을 할 수 있다.
예를 들어 코멘트 작성, 리포지토리 읽기, 등등..
따라서 job단위로 필요한 취소 권한만 명시하는게 좋다고 한다..
이 permissions에는 2가지를 명시했다. contents, pull-requests
contents:read 는 레포지토리에 소스 코드 읽기 권한만 준다. 워크플로우가 코드를 내려받아 빌드/테스트하는데 필요하다.
pull-requests: write는 PR에 쓰기 권한을 준다. 가령 CI실행 후 PR에 코멘트를 남기는 기능같은걸 열어둔것임
read니까 읽기 권한, write니까 쓰기 권한인거임
steps:
- name: Checkout
uses: actions/checkout@v4
steps는 '정의된 단계'를 뜻한다. 하나의 스탭이다 이말임
하나의 jobs는 여러가지 steps들로 구성된다
각 step들은 순서대로 실행되며, 이를 - name으로 정할 수 있다
- name에 적히는 것은 GitHub Actions에서 실제로 CI가 돌아갈 때 그 과정의 이름을 의미힌다.
구체적으로는 하나의 step의 이름임

uses: actions/checkout@v4는 actions/checkout@v4를 사용하겠다는 의미이다.
actions/checkout은 GitHub의 공식 액션인데, 이걸 Version 4로 사용한다.
이 액션은 현재 워크플로우가 실행되는 러너(ubuntu-latest VM)에 PR/브랜치의 소스코드를 체크아웃(clone) 하는것이다.
GitHub Actions은 기본적으로 빈 VM에서 시작하는데, checkout@v4를 통해 체크아웃 스탭을 써줘야 워크플로우 안에서
./gradlew build 같은걸 실행할 때 레포지토리 코드를 사용할 수 있다..
이는 CI/CD를 작성할때 거의 대부분 작성하는 단계이다.
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
# 실행결과
Run actions/setup-java@v4
Installed distributions
Creating settings.xml with server-id: github
Writing to /home/runner/.m2/settings.xml
이름은 Setup JDK 17이고, 이건 GitHub Actions에서 제공하는 setup-java@v4를 사용하겠다는 의미이다.
setup-java@v4은 원하는 버전의 JDK를 설치하고, 환경변수(JAVA_HOME 등)까지 세팅해주는 역할을 한다.
with는 이 스탭을 실행하는 데 옵션같은 세부설정을 달아준다.
distribution: temurin은 우리가 아는 이클립스의 Eclipse Temurin을 지정해준다.
Eclipse Temurin은 현재 가장 많이? 쓰는 무료 JDK 배포판이라고 한다.
java-version은 17로 지정해줌으로 ci를 돌릴때마다 일괄적으로 자바버전을 17로 맞춰준다.
- name: Grant execute permission for gradlew # github Actions에서 gradlew 권한 주기
run: chmod +x gradlew
# 실행결과
Run chmod +x gradlew
chmod +x gradlew
shell: /usr/bin/bash -e {0}
env:
JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.16-8/x64
JAVA_HOME_17_X64: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.16-8/x64
여기서 run: chmod +x gradlew는 chmod +x gradlew를 실행하겠다는 의미다.
리눅스 쉘 명령어인데, .gradlew 스크립트 파일에 실행 권한을 부여하는 명령어임
이게 왜 필요한가?
프로젝트 루트에 있는 gradlew은 Gradle Wrapper 스크립트임

로컬에서는 보통 ./gradlew build 이걸 실행하는데, 이때 이걸 불러오는거다
그런데 GitHub Actions의 러너(ubuntu-latest)는 클린상태이기 때문에(클린 VM) 체크아웃된 gradlew 파일이 실행 권한이 없는 단순 텍스트 파일 상태일 수 있다.
이 상태에서 .gradlew build하면 Permission denied(권한 없음)이 뜬다
따라서 chmod +x gradlew 명령어를 통해 GitHub Actions 안에서 ./gradlew를 실행할 수 있게 되는것
- name: Cache Gradle
uses: gradle/actions/setup-gradle@v3
# 실행결과
Run gradle/actions/setup-gradle@v3
with:
cache-disabled: false
cache-read-only: true
cache-write-only: false
cache-overwrite-existing: false
gradle-home-cache-includes: caches
notifications
gradle-home-cache-cleanup: false
add-job-summary: always
add-job-summary-as-pr-comment: never
dependency-graph: disabled
dependency-graph-continue-on-failure: true
build-scan-publish: false
validate-wrappers: false
generate-job-summary: true
gradle-home-cache-strict-match: false
workflow-job-context: null
github-token: ***
env:
JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.16-8/x64
JAVA_HOME_17_X64: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.16-8/x64
Merged default JDK locations into /home/runner/.m2/toolchains.xml
Restore Gradle state from cache
Warning: Failed to restore gradle-home-v1|Linux|test[329d4f51e360f400f44edeef32ea17f7]-7d65428f4ec4680a28abddf0b681013b9d82dab7: Error: Cache service responded with 400
Gradle User Home cache not found. Will initialize empty.
뭔가 길지만 uses: gradle/actions/setup-gradle@v3 은 Github에서 제공하는 공식 액션 버전3을 사용한다는 의미임
이 setup-gradle@v3은
Gradle버전을 자동 관리해주고(wrapper에 정의된 버전 사용함)
빌드 캐시와 의존성 캐시를 재사용해서 빌드 속도를 크게 줄여준다.
이건 왜 필요한가?
SpringBoot에는 많은 의존성이 있는데, CI환경에서 매번 이 .gradlew build를 실행하면 모든 라이브러리를 다시 다운로드해야하는 번거로움으로 시간이 오래 걸린다.
setup-gradle 액션을 통해 .gradle 디렉토리와 Gradle 캐시를 GitHub Actions 캐시에 저장한다.
그리고 다음 빌드에서 같은 의존성이라면? 캐시에서 불러온 다음 재활용한다.(캐싱)
또한 setup-gradle@v3에는 빌드 스캔 설정, 캐시 키 자동관리, Gradle Entrerprise 연동 등도 지원한다고 한다.
- name: Run tests without coverage
run: ./gradlew test jacocoTestReport jacocoTestCoverageVerification --no-daemon
# 실행결과
Run ./gradlew test jacocoTestReport jacocoTestCoverageVerification --no-daemon
Downloading https://services.gradle.org/distributions/gradle-8.14.3-bin.zip
.............10%.............20%.............30%.............40%.............50%.............60%.............70%.............80%.............90%..............100%
Welcome to Gradle 8.14.3!
Here are the highlights of this release:
- Java 24 support
- GraalVM Native Image toolchain selection
- Enhancements to test reporting
- Build Authoring improvements
# ... 중략
Task :compileJava
#... 중략
> Task :processResources
> Task :classes
> Task :compileTestJava
> Task :processTestResources
> Task :testClasses
# ... 중략
Hibernate:
drop table if exists users cascade
2025-09-14T11:11:38.441Z INFO 2595 --- [monew-app] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-09-14T11:11:38.449Z INFO 2595 --- [monew-app] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
> Task :test
> Task :jacocoTestCoverageVerification
> Task :jacocoTestReport
[Incubating] Problems report is available at: file:///home/runner/work/sb04-monew-team1/sb04-monew-team1/build/reports/problems/problems-report.html
BUILD SUCCESSFUL in 1m 16s
7 actionable tasks: 7 executed
run: ./gradlew test jacocoTestReport jacocoTestCoverageVerification --no-daemon
테스트 커버리지 리포트를 생성하는 등 테스트 관련된거임
우리 팀에서는 Jacoco 테스트 커버리지 오픈 소스 도구를 사용하기로 했기 때문에 이걸 추가했다.
jacocoTestReport는 Jacoco 플러그인을 이용해서 테스트 커버리지 리포트를 생성한다.
[Incubating] Problems report is available at: file:///home/runner/work/sb04-monew-team1/sb04-monew-team1/build/reports/problems/problems-report.html
이런식으로 리포트를 생성해줌
혹은 xml파일로도 생성해준다
Jacoco를 쓰니까 나중에 GitHub Actions에서 이 리포트를 가지고 PR코멘트를 달거나 커버리지 게이트를 검증할 수 있다.
jacocoTestCoverageVerification
얘는 Jacoco 리포트를 바탕으로 커버리지 기준 충족 여부를 검증해준다.
예를 들어 instructionCoverage >= 80%와 같이 검증 기준을 80%으로 build.gradle에 설정해뒀을 때, 기준 미만이면 빌드가 아예 실패된다.
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
// minimum = 0.80 // 80% 미만이면 실패함
}
}
}
}
여기에서 minimum = 0.80이 검증 기준인데, 이걸 충족하지 못하면 빌드 자체가 실패되고 Actions또한 실패처리 되니까 주석처리 해뒀다.
사실 80%는 엄청 높은 수치다..
--no-daemon
Gradle 데몬 프로세스를 사용하지 않고 실행하는 옵션이다.
GitHub Actions같은 CI환경에서는 매번 새로운 VM이 뜨기 때문에 데몬을 켜는게 오히려 느리거나 불필요할 수 있다.
따라서 보통 CI환경에서 실행할때 --no-daemon을 쓰는게 일반적이다
- name: Upload coverage report (HTML)
uses: actions/upload-artifact@v4
with:
name: jacoco-report
path: build/reports/jacoco/test/html
# 실행결과
Run actions/upload-artifact@v4
With the provided path, there will be 330 files uploaded
Artifact name is valid!
Root directory input is valid!
Beginning upload of artifact content to blob storage
Uploaded bytes 616777
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact zip is # UUID와 비슷한 형태의 숫자와 영문 조합
Finalizing artifact upload
Artifact jacoco-report.zip successfully finalized. Artifact ID # ID임
Artifact jacoco-report has been successfully uploaded! Final size is 616777 bytes. Artifact ID is # ID임
Artifact download URL: https://github.com/Sprint-team-1-monew/sb04-monew-team1/actions/runs/17710347569/artifacts/ #ID
uses: actions/upload-artifact@v4 는 GitHub에서 제공하는 공식 액션임
이 액션은 빌드 결과물(아티팩트)을 GitHub Actions에 업로드해서 Workflows 실행 결과 페이지에서 다운로드 할 수 있게 한다.

with는 두가지로 구성되어있다. name, path
name: jacoco-report
얘는 업로드할 아티팩트의 이름이다. 위에 보이는 jacoco-report가 그 이름임
path: build/reports/jacoco/test/html
실제로 업로드할 파일의 경로임
Gradle이 생성한 Jacoco HTML 리포트 폴더를 업로드한다.
다운로드하면 실제로 리포트를 HTML형태로 볼 수 있음
이걸 왜 해야하는가?
사실 할 필요는 없었는데, 추가하고 싶어서 추가했다.
그냥 Jacoco HTML리포트를 보존해서 브라우저로 열어볼 수 있는 기능이 있다, 라는걸 공부하는 차원에서 추가했음
# 커버리지 수치를 jacoco XML에서 뽑은 다음 PR 코멘트로 남기기
- name: Install xmllint (parsing XML) # xmllint 설치하기
run: sudo apt-get update && sudo apt-get install -y libxml2-utils
이건 GitHub Actions 실행환경에 xmllint를 설치해서 XML파일을 다룰 수 있게 하는 작업이다.
Xmllint는 XML파일을 파싱하거나 추출할 때 쓰는 도구이다.
기본 Ubuntu runner는 설치되어있지 않기 때문에 CI쪽에서 직접 설치해주는것이다.
나는 이걸 jacoco 커버리지 리포트를 파싱해서 요약본을 뽑아내고 싶어서 추가했다.
run 부분은 sudo apt-get update 와 sudo apt-get install -y libxml2-utils 두가지로 나뉘어있다.
- sudo apt-get update에서 apt-get은 Ubuntu/데비안 계열에서 사용하는 패키지 관리자라고 한다.
update는 패키지 목록을 최신 상태로 갱신하는 명령어이다. 이걸 하지 않으면 "패키지를 찾을 수 없다"는 에러가 나올 수 있다.
- sudo apt-get install -y libxml2-utils 에서 libxml2-utils는 xmllint라는 XML 파싱/검증 유틸리티를 포함한다.
-y는 "모든 질문에 자동으로 Yes"라는 옵션이다. 설치 과정이 중간에 멈추지 않고 바로 진행된다.
- name: Extract coverage numbers
id: coverage
run: |
XML="build/reports/jacoco/test/jacocoTestReport.xml"
COVERED=$(xmllint --xpath "string(//counter[@type='LINE']/@covered)" "$XML" 2>/dev/null || echo 0)
MISSED=$(xmllint --xpath "string(//counter[@type='LINE']/@missed)" "$XML" 2>/dev/null || echo 0)
TOTAL=$((COVERED + MISSED))
PCT=$(awk "BEGIN { if ($TOTAL==0) print 0; else printf \"%.2f\", ($COVERED/$TOTAL)*100 }")
echo "covered=$COVERED" >> $GITHUB_OUTPUT
echo "missed=$MISSED" >> $GITHUB_OUTPUT
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "pct=$PCT" >> $GITHUB_OUTPUT
이 스탭은 Jacoco XML에서 라인 커버리지(covered/missed)를 뽑아서 퍼센트로 계산한 뒤에 다음 스탭들이 쓸 수 있게끔 출력값으로 내보내는 역할을 한다.
XML="build/reports/jacoco/test/jacocoTestReport.xml" 은 Jacoco 리포트의 경로를 의미하는데, Gradle 기본 태스크인 jacocoTestReport를 실행하면 일반적으로 이 경로에 생성된다고 한다.
COVERED=$(xmllint --xpath "string(//counter[@type='LINE']/@covered)" "$XML" 2>/dev/null || echo 0)
MISSED=$(xmllint --xpath "string(//counter[@type='LINE']/@missed)" "$XML" 2>/dev/null || echo 0)
이 두개는 형태가 비슷한데, 둘 다 xmllint로 XPath를 실행한 뒤 각각 LINE타입의 @covered, @missed를 문자열로 추출해준다.
- XPath 부분은 //counter[@type='LINE']/@covered 그리고 //counter[@type='LINE']/@missed 부분에 해당한다.
Jacoco XML에는 counter type = "LINE" covered = "123" missed = "45" 이런 형태의 요약값이 들어있을 것이다.
- 2>/dev/null 은 에러 메시지를 버린다는 것을 의미한다.
- || echo 0은 실패해도 파이프라인이 멈추지 않고 기본값을 0으로 넣게끔 한다.
- TOTAL=$((COVERED + MISSED))는 총 라인수 = 커버된 라인 + 커버되지 않은 라인으로 계산해서 출력한다.
PCT=(awk "BEGIN { if ($TOTAL==0) print 0; else printf \"%.2f\", ($COVERED/$TOTAL)*100 }")
TOTAL이 0이면 그냥 0을 출력하게 하고, 그게 아니라면 COVERED/TOTAL을 백분율로 출력해주는 역할을 한다.
- Bash의 부동소수점 나눗셈이 안된다고 한다. 그래서 보통 awk로 계산시킨다고 한다.
- TOTAL이 0일땐 분모가 0이므로 에러가 나니까 0을 출력하게 한다.
- \"%.2f\"을 활용하여 소수점 둘째자리까지 출력하게끔 한다.
echo "covered=$COVERED" >> $GITHUB_OUTPUT
이건 출력값 전달 방식이다. echo로 출력하는 방식은 GitHub가 권장하는 출력값 전달 방식이라고 한다.
- ::set-output은 폐기된 방식이며, GitHub에서 사용하지 않는다.
- 이 방식을 사용하면 ${{ steps.coverage.outputs.covered }}, ${{ steps.coverage.outputs.pct }} 와 같은 형태로 출력된 값을 참조할 수 있게 한다. covered=$COVERED와 같은 형태로 형상관리 이름을 지정하는게 가능하다.
# 커버리지 PR 남기기
- name: Post coverage comment PR
if: ${{ github.event_name == 'pull_request' }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: jacoco-coverage
message: |
## JaCoCo Coverage Report(자코코 커버리지 리포트!)
- **CoverageLine(커버리지_라인)** ${{ steps.coverage.outputs.pct }}% (${{ steps.coverage.outputs.covered }} / ${{ steps.coverage.outputs.total }})
- **Missed(커버리지_수치)** ${{ steps.coverage.outputs.missed }}
- [HTML 리포트](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) (Artifact) 확인하기
이건 Pull Request(PR)을 날렸을 때 자동으로 커버리지 코멘트를 남기는 스탭이다.
- if: ${{ github.event_name == 'pull_request' }}
Git에서 PR 이벤트가 발생했을 때만 실행되게끔 한다.
push, rebase 뭐 이런것들에는 코멘트를 남기지 않는다.
- uses: marocchino/sticky-pull-request-comment@v2
이건 marocchino/sticky-pull-request-comment 라는 GitHub Actions를 사용하겠다는 의미이다.
https://github.com/marocchino/sticky-pull-request-comment 이걸 사용한다.
이 Actions의 특징은 PR에 코멘트를 남기는데, sticky(끈적끈적)해서 새 코멘트를 추가하지 않고, 같은 header를 가진 코멘트의 경우 덮어쓰기 해준다. 업데이트 한다는 의미
with: 는 이하의 것들을 함께 추가하겠다는 파라미터이다
with 아래의 header는 일종의 식별자와 같은 역할을 한다. Post coverage comment PR에서 with와 관련된 것들을 찾고 싶다면 저 헤더 이름을 찾아오면 된다.
message:는 실제로 PR을 날렸을 때 남는 커버리지 코멘트를 적었다. 형상관리가 많이 되어있는데, 위의 Extract coverage numbers를 참조하여 작성한다.
# ECR에 업로드하기
# 1. AWS에 로그인하기, Access Key, Secret Key, Region, Json
- name: AWS Configure
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
# default-output-format: json
aws-actions/configure-aws-credentials@v4 는 마찬가지로 GitHub에서 공식적으로 제공하는 AWS Action이다.
이걸 실행시키면 러너환경에서 AWS CLI 자격증명이 자동으로 설정된다.(ubuntu VM?)
물론 아래 with에 해당하는 IAM 키와 지역명을 정확하게 입력해야 동작한다. 나는 Git Secert을 통해 형상관리했다.
# 3. --platform linux/amd64,linux/arm64 에서
# 다른 아키텍처 이미지 빌드(arm64)를 위한 에뮬레이터를 Git Actions에 설치
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
docker/setup-qemu-action@v3도 깃헙에서 제공하는 공식 Docker액션이다.
QEMU(Quick EMUlator, 퀵 애뮬레이터?)는 CPU 아키텍쳐를 애뮬레이션 할 수 있는 툴이라고 한다.
즉 x86_64(amd64) 머신에서 arm64와 같은 다른 아키텍처 이미지를 빌드하거나 실행할 수 있게 해준다고 함
이게 왜 필요한가?
Github Actions 러너는 기본적으로 x86_64(amd64) 기반으로 돌아가는데
AWS Graviton(arm64), M1/M2 Mac(arm64)와 같은 환경들에서 컨테이너를 실행해야할 수 있다.
그래서 이 과정을 추가하여 멀티 아키텍처 이미지를 빌드하기 위함임(--platform linux/amd64,linux/arm64)
# 4. buildx를 Github Actions에 실행시키기,
# 마찬가지로 --platform linux/amd64,linux/arm64 를 실행시키기 위함
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
docker/setup-buildx-action@v3은 말 그대로 Docker Buildx를 Github Actions에서 활성화한다.
기본 빌더를 Buildx로 교체해주는 역할을 함
Buildx가 무엇인가?
얘는 Docker의 확장 빌더 플러그인임
기본 docker build보다 기능을 많이 가지고 있다.
- 멀티 아키텍처 빌드(--platform linux/amd64,linux/arm64)
- 효율적 캐시 빌드
- 병렬 빌드
- remote builder 지원
이게 왜 필요한가?
일반적으로는 위의 QEMU와 함께 사용한다고 한다. (docker/setup-qemu-action@v3)
QEMU를 설치하고, Buildx를 활성화하고, Build & Push하면 ECR/Hub같은 레지스트리에 amd64 + arm64 이미지를 한번에 푸시할 수 있다.
# 5. 최신버전의 구분을 위해 태그를 추가함
# 이를 통해 버전 구분과 롤백 가능(docker pull)
- name: tag making(SHA)
id: shatag
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
id는 이 스탭의 식별자를 뜻한다.
workflow 안에서 다른 스탭이 이 스탭의 출력(output)을 참조하려면 반드시 이 id를 통해서 가져가야한다
${{ steps.shatag.outputs.SHORT_SHA }} 이런 형태
echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT에서
${ GITHUB_SHA } 는 GitHub가 제공해주는 커밋 해시 환경변수라고 한다. 보통 40자리라고 함
그리고 ${ GITHUB_SHA::7 }은 그 환경변수에서 앞 7자리만 잘라낸것을 의미한다
>> $GITHUB_OUTPUT은 Github Actions에서 스탭 출력값을 정의하는 방식? 이라고 한다.
따라서 이 스탭은 현재 WorkFlow가 실행되는 커밋의 짧은 해시값, 7자리를 추출해서
이 값을 출력값으로 등록한 후 다른 스탭에서 사용할 수 있게끔 한다.
# 빌드 & 푸시 : 이미지명은 같게, 태그로 최신버전 구분
# 6. Pull Request에 대한 Docker image를 빌드하기, docker/build-push-action을 사용함
- name: Build Docker image and push
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t public.ecr.aws/<ECR리포지토리값>/monew:latest \
-t public.ecr.aws/<ECR리포지토리값>/monew:${{ steps.shatag.outputs.SHORT_SHA }} \
--push .
마지막으로 빌드된 Docker 이미지를 빌드하고 푸시한다.
docker buildx build는 Buildx를 사용해서 Docker 이미지를 빌드하겠다는 의미임
왜냐하면 밑에 적힌 amd64, arm64를 동시에 빌드하기 위해서이다. (멀티 아키텍처 빌드)
-t public.ecr.aws/<ECR리포지토리값>/monew:latest는 빌드된 이미지 뒤에 태그를 붙이는건데, latest를 붙이고 있다.
ECR 리포지토리값이라고 적은 이유는 만에 하나라도 문제가 발생하지 않게끔 하기 위해서.. ㅎ
latest는 최신 버전을 의미하는 태그라는 뜻에서 붙인다.
-t public.ecr.aws/<ECR리포지토리값>/monew:${{ steps.shatag.outputs.SHORT_SHA }} 는 같은 이미지를 한번 더 태그한다.
총 두번 태그하는거임
앞에서 만든 shatag에서 7자리만 뗀 해시태그를 태그값으로 붙인다.
이렇게 하면 PR이나 배포 시점마다 고유한 이미지 버전을 가질 수 있다고 하며, 롤백또한 쉬워진다고 한다.
마지막으로 --push . 를 통해 푸시한다
푸시는 빌드된 이미지를 ECR에 업로드시킨다.
다음에 붙는 . 는 현재 디렉토리를 의미한다.
밑은 완성된거
# ci, Pull Request에서 테스트 및 커버리지
name: CI Workflow
on:
pull_request:
branches: [dev, main] # dev, main 브랜치에 pull request 될 때 실행됨
paths-ignore: ["README.md", "**/*.md"]
# push:
# branches: [dev, main] # dev, main 브랜치에 push될 때 실행됨
# paths-ignore: ["README.md", "**/*.md"]
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # PR 코멘트에 커버리지 요약 남기기
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Grant execute permission for gradlew # github Actions에서 gradlew 권한 주기
run: chmod +x gradlew
- name: Cache Gradle
uses: gradle/actions/setup-gradle@v3
- name: Run tests without coverage
run: ./gradlew test jacocoTestReport jacocoTestCoverageVerification --no-daemon
- name: Upload coverage report (HTML)
uses: actions/upload-artifact@v4
with:
name: jacoco-report
path: build/reports/jacoco/test/html
# 커버리지 수치를 jacoco XML에서 뽑은 다음 PR 코멘트로 남기기
- name: Install xmllint (parsing XML) # xmllint 설치하기
run: sudo apt-get update && sudo apt-get install -y libxml2-utils
# 업데이트를 꼭 해야하는가?
# 커버리지 코멘트 설계
- name: Extract coverage numbers
id: coverage
run: |
XML="build/reports/jacoco/test/jacocoTestReport.xml"
COVERED=$(xmllint --xpath "string(//counter[@type='LINE']/@covered)" "$XML" 2>/dev/null || echo 0)
MISSED=$(xmllint --xpath "string(//counter[@type='LINE']/@missed)" "$XML" 2>/dev/null || echo 0)
TOTAL=$((COVERED + MISSED))
PCT=$(awk "BEGIN { if ($TOTAL==0) print 0; else printf \"%.2f\", ($COVERED/$TOTAL)*100 }")
echo "covered=$COVERED" >> $GITHUB_OUTPUT
echo "missed=$MISSED" >> $GITHUB_OUTPUT
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "pct=$PCT" >> $GITHUB_OUTPUT
# 커버리지 PR 남기기
- name: Post coverage comment PR
if: ${{ github.event_name == 'pull_request' }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: jacoco-coverage
message: |
## JaCoCo Coverage Report(자코코 커버리지 리포트!)
- **CoverageLine(커버리지_라인)** ${{ steps.coverage.outputs.pct }}% (${{ steps.coverage.outputs.covered }} / ${{ steps.coverage.outputs.total }})
- **Missed(커버리지_수치)** ${{ steps.coverage.outputs.missed }}
- [HTML 리포트](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) (Artifact) 확인하기
# ECR에 업로드하기
# 1. AWS에 로그인하기, Access Key, Secret Key, Region, Json
- name: AWS Configure
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
# default-output-format: json
# 2. AWS public ECR에 로그인하기, ECR은 무조건 us-east-1을 통해 로그인한다
- name: Login AWS Public ECR
run: |
aws ecr-public get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin public.ecr.aws
# 3. --platform linux/amd64,linux/arm64 에서 다른 아키텍처 이미지 빌드(arm64)를 위한 에뮬레이터를 Git Actions에 설치
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# 4. buildx를 Github Actions에 실행시키기, 마찬가지로 --platform linux/amd64,linux/arm64 를 실행시키기 위함
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
# 5. 최신버전의 구분을 위해 태그를 추가함
# 이를 통해 버전 구분과 롤백 가능(docker pull)
- name: tag making(SHA)
id: shatag
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
# 빌드 & 푸시 : 이미지명은 같게, 태그로 최신버전 구분
# 6. Pull Request에 대한 Docker image를 빌드하기, docker/build-push-action을 사용함
- name: Build Docker image and push
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t public.ecr.aws/<ECR리포지토리값>/monew:latest \
-t public.ecr.aws/<ECR리포지토리값>/monew:${{ steps.shatag.outputs.SHORT_SHA }} \
--push .
'Monew' 카테고리의 다른 글
| S3에 백업한 뉴스 기사를 '복구'하는 기능 (0) | 2025.09.19 |
|---|---|
| S3 버킷에 데이터를 '백업'하는 로직 (0) | 2025.09.16 |
| build.gradle (0) | 2025.09.04 |
| Jacoco를 활용하기 (테스트 커버리지) (0) | 2025.09.04 |
| 커밋 컨벤션 : Git Commit Template 플러그인 설치 (0) | 2025.09.04 |