diff --git a/.github/auto-assign.yml b/.github/auto-assign.yml new file mode 100644 index 00000000..f928ee74 --- /dev/null +++ b/.github/auto-assign.yml @@ -0,0 +1,21 @@ +# Auto Assign Configuration +# https://github.com/kentaro-m/auto-assign-action + +# 리뷰어 자동 할당 +addReviewers: true +reviewers: +# 리뷰어 목록 (GitHub username) +# - reviewer1 +# - reviewer2 + +# 랜덤으로 선택할 리뷰어 수 (0 = 모두 할당) +numberOfReviewers: 0 + +# PR 작성자를 리뷰어에서 제외 +skipKeywords: + - wip + - WIP + - draft + +# Assignee 자동 할당 +addAssignees: author diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml new file mode 100644 index 00000000..a52ae016 --- /dev/null +++ b/.github/workflows/_build.yml @@ -0,0 +1,156 @@ +# Reusable Build Workflow +# 다른 워크플로우에서 workflow_call로 호출하여 재사용 + +name: Reusable Build + +on: + # workflow_call: 다른 워크플로우에서 "uses: ./.github/workflows/_build.yml"로 호출 가능하게 함 + workflow_call: + # inputs: 호출하는 워크플로우에서 전달할 수 있는 매개변수 정의 + inputs: + java-version: + description: 'Java version to use' + required: false + default: '17' + type: string + run-tests: + description: 'Run tests' + required: false + default: true + type: boolean + generate-coverage: + description: 'Generate Jacoco coverage report' + required: false + default: true + type: boolean + publish-build-scan: + description: 'Publish Gradle Build Scan' + required: false + default: true + type: boolean + # secrets: 호출하는 워크플로우에서 전달할 시크릿 정의 + secrets: + SLACK_WEBHOOK_URL: + description: 'Slack Incoming Webhook URL for notifications' + required: false + # outputs: 이 워크플로우의 결과를 호출한 워크플로우에 반환 + outputs: + build-outcome: + description: 'Build outcome (success/failure)' + # jobs.build.outputs.outcome 값을 외부로 노출 + value: ${{ jobs.build.outputs.outcome }} + build-scan-url: + description: 'Gradle Build Scan URL' + value: ${{ jobs.build.outputs.build-scan-url }} + +jobs: + build: + name: Build & Test + runs-on: ubuntu-latest + # outputs: 이 job의 결과를 다른 job이나 워크플로우에서 참조 가능하게 함 + outputs: + outcome: ${{ steps.build.outcome }} + build-scan-url: ${{ steps.gradle-build.outputs.build-scan-url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # fetch-depth: 0 = 전체 히스토리 가져옴 (SonarCloud 분석에 필요) + fetch-depth: 0 + + - name: Set up JDK ${{ inputs.java-version }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ inputs.java-version }} + # cache: gradle = ~/.gradle/caches 디렉토리를 자동으로 캐싱하여 빌드 속도 향상 + cache: gradle + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + # build-scan-publish: true = Gradle Build Scan을 scans.gradle.com에 발행 + # Build Scan은 빌드 성능, 의존성, 테스트 결과 등 상세 정보를 웹에서 확인 가능 + build-scan-publish: ${{ inputs.publish-build-scan }} + build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use" + build-scan-terms-of-use-agree: "yes" + + - name: Grant execute permission + run: chmod +x gradlew + + - name: Build + # id: gradle-build = Build Scan URL을 outputs로 가져오기 위해 필요 + id: gradle-build + # generate-coverage가 true면 'jacocoAggregatedReport' 추가, false면 빈 문자열 + run: ./gradlew clean build --parallel ${{ inputs.generate-coverage && 'jacocoAggregatedReport' || '' }} + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-artifacts + # path: | = 여러 경로를 멀티라인으로 지정 + # **/build/classes/ = 모든 하위 디렉토리의 build/classes 폴더 + path: | + **/build/classes/ + **/build/resources/ + **/build/libs/ + # retention-days: 1 = 아티팩트 보관 기간 (1일 후 자동 삭제) + retention-days: 1 + + - name: Upload Jacoco report + # if: 조건부 실행 - generate-coverage가 true일 때만 실행 + if: inputs.generate-coverage + uses: actions/upload-artifact@v4 + with: + name: jacoco-report + path: | + build/reports/jacoco/aggregated/ + **/build/reports/jacoco/test/ + retention-days: 14 + + - name: Upload test results + # always(): 이전 step이 실패해도 항상 실행 (테스트 실패 시에도 결과 업로드) + # inputs.run-tests && always(): 테스트 실행했을 때만 + 항상 실행 + if: inputs.run-tests && always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: '**/build/test-results/' + retention-days: 7 + + # Slack 알림 (빌드 실패 시 또는 Build Scan URL 공유) + - name: Notify Slack on failure + # secrets.SLACK_WEBHOOK_URL이 설정되어 있고 빌드 실패 시에만 실행 + if: failure() && secrets.SLACK_WEBHOOK_URL != '' + uses: slackapi/slack-github-action@v2.0.0 + with: + # webhook: Slack Incoming Webhook URL + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + # payload: Slack 메시지 JSON 형식 + payload: | + { + "text": "빌드 실패", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*빌드 실패*\n*Repository:* ${{ github.repository }}\n*Branch:* ${{ github.ref_name }}\n*Commit:* `${{ github.sha }}`" + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Build Scan:*\n${{ steps.gradle-build.outputs.build-scan-url || 'N/A' }}" + }, + { + "type": "mrkdwn", + "text": "*Workflow:*\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>" + } + ] + } + ] + } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..620cb4a8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +# CI Pipeline +# 브랜치 푸시 시 빠른 검증 (빌드, 테스트, 보안 스캔) +# 목표: 5분 이내 피드백 +name: CI + +on: + push: + branches: + - develop + - main + # 태그는 release.yml에서 처리 + tags-ignore: + - '**' + +jobs: + # 1. 빌드 및 테스트 + build: + name: Build & Test + uses: ./.github/workflows/_build.yml + with: + java-version: '17' + run-tests: true + generate-coverage: true + publish-build-scan: true + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + # 2. 의존성 그래프 및 보안 스캔 + security-scan: + name: Security Scan + needs: [ build ] + runs-on: ubuntu-latest + permissions: + contents: write + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + cache: gradle + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + # GitHub Dependency Graph에 의존성 정보 제출 + # Security > Dependabot alerts에서 취약점 확인 가능 + - name: Submit Dependency Graph + uses: gradle/actions/dependency-submission@v4 + with: + build-scan-publish: true + build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use" + build-scan-terms-of-use-agree: "yes" + + # 소스 코드 취약점 스캔 (develop 브랜치에서만) + - name: Code Vulnerability Scan + if: github.ref_name == 'develop' + uses: anchore/scan-action@v3 + id: scan + with: + path: "${{ github.workspace }}" + fail-build: false + severity-cutoff: high + + - name: Upload Vulnerability Report + if: github.ref_name == 'develop' && failure() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} diff --git a/.github/workflows/commit-stage.yml b/.github/workflows/commit-stage.yml deleted file mode 100644 index 81966947..00000000 --- a/.github/workflows/commit-stage.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: commit-stage.yml -on: - push: - branches: - - develop - - main - tags: - - 'v*.*.*' # 'v'로 시작하는 시맨틱 버전 태그 푸시 시 워크플로 실행 - -env: - REGISTRY: ghcr.io - IMAGE_NAME: chan99k/learning-manager - -jobs: - build: - name: Checkout, Build and Code Scan - runs-on: ubuntu-latest # 배포 환경에 맞게 수정해주어야 함 - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - name: SetUp Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - cache: gradle - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Build Project with Gradle - run: ./gradlew clean build - - - name: Generate Jacoco Coverage Report - run: ./gradlew jacocoAggregatedReport - - - name: Upload Jacoco Coverage Report - uses: actions/upload-artifact@v4 - with: - name: jacoco-report - path: | - build/reports/jacoco/aggregated/ - **/build/reports/jacoco/test/ - retention-days: 14 - - - name: Generate and submit dependency graph - uses: gradle/actions/dependency-submission@v4 - with: - build-scan-publish: true - build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use" - build-scan-terms-of-use-agree: "yes" - - - name: Code vulnerability scanning - if: github.ref_name != 'main' - uses: anchore/scan-action@v3 - id: scan_code - with: - path: "${{ github.workspace }}" - fail-build: false # 정책에 따라 변경을 고려하여야 할 듯 - severity-cutoff: high - - - name: Upload vulnerability report - if: github.ref_name != 'main' && failure() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: ${{ steps.scan_code.outputs.sarif }} - - package: - name: Package and Publish - if: startsWith(github.ref, 'refs/tags/v') # 'v'로 시작하는 태그가 푸시되었을 때만 실행 - needs: [ build ] - runs-on: ubuntu-latest # 배포 환경에 맞게 수정해주어야 함 - permissions: - contents: read - packages: write - security-events: write - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - ref: ${{ github.ref }} - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: gradle - - - name: Define Image Tags from Git Tag - id: image_tags - run: | - RAW_TAG="${{ github.ref_name }}" - VERSION=${RAW_TAG#v} - echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - echo "IMAGE_NAME_VERSIONED=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$VERSION" >> $GITHUB_OUTPUT - echo "IMAGE_NAME_LATEST=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_OUTPUT - echo "Raw tag was: $RAW_TAG, Version used: $VERSION" - - - - name: Build container image - run: | - chmod +x gradlew - ./gradlew bootBuildImage \ - --imageName ${{ steps.image_tags.outputs.IMAGE_NAME_VERSIONED }} - - - name: OCI image vulnerability scanning - uses: anchore/scan-action@v3 - id: scan_image - with: - image: "${{ steps.image_tags.outputs.IMAGE_NAME_VERSIONED }}" - fail-build: true - severity-cutoff: high - - - name: Upload vulnerability report - uses: github/codeql-action/upload-sarif@v2 - if: failure() - with: - sarif_file: ${{ steps.scan_image.outputs.sarif }} - - - name: Log into container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # 시맨틱 버전 태그로 푸시 - - name: Publish container image - run: | - docker push ${{ steps.image_tags.outputs.IMAGE_NAME_VERSIONED }} - diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml new file mode 100644 index 00000000..ab03e6cf --- /dev/null +++ b/.github/workflows/pr-pipeline.yml @@ -0,0 +1,140 @@ +# PR Pipeline +# PR 생성/업데이트 시 코드 품질 검증 및 리뷰 지원 +name: PR Pipeline + +on: + pull_request: + branches: + - main + types: [ opened, synchronize, reopened ] + +jobs: + # 1. 빌드 및 테스트 + build: + name: Build & Test + uses: ./.github/workflows/_build.yml + with: + java-version: '17' + run-tests: true + generate-coverage: true + publish-build-scan: true + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + # 2. 커버리지 리포트 PR 코멘트 + coverage-report: + name: Coverage Report + needs: [ build ] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + steps: + - name: Download Jacoco report + uses: actions/download-artifact@v4 + with: + name: jacoco-report + path: . + + - name: Add Coverage PR Comment + uses: madrapps/jacoco-report@v1.7.1 + with: + paths: build/reports/jacoco/aggregated/jacocoTestReport.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 40 + min-coverage-changed-files: 60 + title: "## 테스트 커버리지 리포트" + update-comment: true + + # 3. SonarCloud 정적 분석 + sonarcloud: + name: SonarCloud + needs: [ build ] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: . + + - name: Download Jacoco report + uses: actions/download-artifact@v4 + with: + name: jacoco-report + path: . + + - name: Set SonarCloud Project Key + run: | + REPO_NAME=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 2) + ORG_NAME=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 1) + echo "SONAR_PROJECT_KEY=${ORG_NAME}_${REPO_NAME}" >> $GITHUB_ENV + + - name: Analyze with SonarCloud + uses: SonarSource/sonarcloud-github-action@master + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.SECRET_GITHUB_BOT }} + SONAR_TOKEN: ${{ secrets.SECRET_SONARQUBE }} + with: + args: | + -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }} + -Dsonar.organization=f-lab-edu-1 + + # 4. Qodana 정적 분석 (JetBrains) + qodana: + name: Qodana + needs: [ build ] + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + checks: write + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Qodana Scan + uses: JetBrains/qodana-action@v2025.2 + continue-on-error: true + env: + QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} + with: + # PR 모드: 변경된 파일만 분석하여 속도 향상 + pr-mode: true + use-caches: true + post-pr-comment: true + use-annotations: true + upload-result: true + push-fixes: 'none' + + - name: Upload SARIF to GitHub Security + if: always() + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json + + # 5. 자동 리뷰어 할당 + auto-assign: + name: Auto Assign + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Auto Assign Reviewers + uses: kentaro-m/auto-assign-action@v2.0.0 + with: + configuration-path: '.github/auto-assign.yml' diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml deleted file mode 100644 index b7dd31f1..00000000 --- a/.github/workflows/qodana_code_quality.yml +++ /dev/null @@ -1,48 +0,0 @@ -#-------------------------------------------------------------------------------# -# Discover all capabilities of Qodana in our documentation # -# https://www.jetbrains.com/help/qodana/about-qodana.html # -#-------------------------------------------------------------------------------# - -name: Qodana -on: - workflow_dispatch: - pull_request: - push: - branches: - - main - -jobs: - qodana: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - checks: write - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - - name: 'Qodana Scan' - uses: JetBrains/qodana-action@v2025.2 - env: - QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} - with: - # When pr-mode is set to true, Qodana analyzes only the files that have been changed - pr-mode: false - use-caches: true - post-pr-comment: true - use-annotations: true - # Upload Qodana results (SARIF, other artifacts, logs) as an artifact to the job - upload-result: true - # quick-fixes available in Ultimate and Ultimate Plus plans - push-fixes: 'none' - - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ${{ runner.temp }}/qodana/results/report - destination_dir: ./ diff --git a/.github/workflows/release-stage.yml b/.github/workflows/release-stage.yml deleted file mode 100644 index a6a265e2..00000000 --- a/.github/workflows/release-stage.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: release-stage.yml -on: - push: - branches: - - main - tags: - - 'v*.*.*' # 'v'로 시작하는 시맨틱 버전 태그 푸시 시 워크플로 실행 - -jobs: - publish: - name: Publish release version - if : false - runs-on: ubuntu-latest # 배포 환경에 맞게 수정해주어야 함 - steps: - - name: Placeholder for future - run: echo "Publish release 워크플로 추후 작성 예정" - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..13345575 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,147 @@ +# Release Pipeline +# 태그 푸시 시 Docker 이미지 빌드 및 레지스트리 배포 +name: Release + +on: + push: + tags: + - 'v*.*.*' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: chan99k/learning-manager + +jobs: + # 1. 빌드 및 테스트 (릴리스 전 최종 검증) + build: + name: Build & Test + uses: ./.github/workflows/_build.yml + with: + java-version: '17' + run-tests: true + generate-coverage: false + publish-build-scan: true + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + # 2. Docker 이미지 빌드 및 배포 + docker: + name: Docker Build & Push + needs: [ build ] + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + security-events: write + outputs: + image-tag: ${{ steps.meta.outputs.version }} + image-name: ${{ steps.meta.outputs.tags }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: gradle + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + # 태그에서 버전 추출 (v1.0.0 -> 1.0.0) + - name: Extract version from tag + id: meta + run: | + VERSION=${GITHUB_REF_NAME#v} + IMAGE_FULL="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "tags=${IMAGE_FULL}" >> $GITHUB_OUTPUT + + # Spring Boot의 Buildpacks로 OCI 이미지 빌드 -> 추후 dockerfile 최적화로 변경 필요 + - name: Build container image + run: | + chmod +x gradlew + ./gradlew bootBuildImage --imageName ${{ steps.meta.outputs.tags }} + + # 컨테이너 이미지 취약점 스캔 + - name: Scan container image + uses: anchore/scan-action@v3 + id: scan + with: + image: ${{ steps.meta.outputs.tags }} + fail-build: true + severity-cutoff: high + + - name: Upload scan report + if: failure() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} + + # GitHub Container Registry 로그인 + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # 이미지 푸시 + - name: Push container image + run: docker push ${{ steps.meta.outputs.tags }} + + # 3. GitHub Release 생성 (선택적) + create-release: + name: Create GitHub Release + needs: [ docker ] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + + # 4. Slack 릴리스 알림 + notify: + name: Notify Release + needs: [ docker ] + if: always() && secrets.SLACK_WEBHOOK_URL != '' + runs-on: ubuntu-latest + steps: + - name: Send Slack notification + uses: slackapi/slack-github-action@v2.0.0 + with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + { + "text": "${{ needs.docker.result == 'success' && 'Release 성공' || 'Release 실패' }}", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "${{ needs.docker.result == 'success' && '*Release 성공*' || '*Release 실패*' }}\n*Version:* `${{ github.ref_name }}`\n*Image:* `${{ needs.docker.outputs.image-name }}`" + } + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { "type": "plain_text", "text": "View Release" }, + "url": "${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}" + } + ] + } + ] + } diff --git a/.github/workflows/review-stage.yml b/.github/workflows/review-stage.yml deleted file mode 100644 index 25a840be..00000000 --- a/.github/workflows/review-stage.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: review-stage.yml -on: - pull_request: - branches: - - main - -jobs: - publish: - name: Review Source Code - if: false - runs-on: ubuntu-latest # 배포 환경에 맞게 수정해주어야 함 - steps: - - name: Placeholder for future - run: echo " Review Source Code 워크플로 추후 작성 예정" \ No newline at end of file diff --git a/.github/workflows/sonarcloud-analyze.yml b/.github/workflows/sonarcloud-analyze.yml deleted file mode 100644 index 18b66f4f..00000000 --- a/.github/workflows/sonarcloud-analyze.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: F-Lab SonarCloud Code Analyze - -on: - pull_request: - types: [opened, synchronize, reopened] - workflow_dispatch: - -jobs: - CodeAnalyze: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - cache: gradle - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Build and Generate Jacoco Report - run: ./gradlew clean build jacocoAggregatedReport - - - name: Set SonarCloud Project Key - run: | - REPO_NAME=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 2) - ORG_NAME=$(echo $GITHUB_REPOSITORY | cut -d '/' -f 1) - SONAR_PROJECT_KEY="${ORG_NAME}_${REPO_NAME}" - echo "SONAR_PROJECT_KEY=$SONAR_PROJECT_KEY" >> $GITHUB_ENV - - - name: Analyze with SonarCloud - uses: SonarSource/sonarcloud-github-action@master - id: analyze-sonarcloud - continue-on-error: true - env: - GITHUB_TOKEN: ${{ secrets.SECRET_GITHUB_BOT }} - SONAR_TOKEN: ${{ secrets.SECRET_SONARQUBE }} - with: - args: - -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }} - -Dsonar.organization=f-lab-edu-1 - - \ No newline at end of file diff --git a/build-logic/src/main/kotlin/lm.java-library.gradle.kts b/build-logic/src/main/kotlin/lm.java-library.gradle.kts index 8fca4a67..cb4b919f 100644 --- a/build-logic/src/main/kotlin/lm.java-library.gradle.kts +++ b/build-logic/src/main/kotlin/lm.java-library.gradle.kts @@ -18,6 +18,14 @@ tasks.withType().configureEach { tasks.withType().configureEach { useJUnitPlatform() + + // maxParallelForks: 동시에 실행할 테스트 프로세스 수 + // Runtime.getRuntime().availableProcessors() / 2 -> CPU 코어의 절반 사용 + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) + + // forkEvery: N개 테스트마다 새 JVM 프로세스 생성 (메모리 누수 방지) + // 0 = 재시작 안 함 (기본값), 250 = 250개 테스트마다 재시작 + setForkEvery(250) } dependencyManagement {