diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml index a52ae01..4776cf0 100644 --- a/.github/workflows/_build.yml +++ b/.github/workflows/_build.yml @@ -39,9 +39,9 @@ on: description: 'Build outcome (success/failure)' # jobs.build.outputs.outcome 값을 외부로 노출 value: ${{ jobs.build.outputs.outcome }} - build-scan-url: + build-scan: description: 'Gradle Build Scan URL' - value: ${{ jobs.build.outputs.build-scan-url }} + value: ${{ jobs.build.outputs.build-scan }} jobs: build: @@ -50,7 +50,10 @@ jobs: # outputs: 이 job의 결과를 다른 job이나 워크플로우에서 참조 가능하게 함 outputs: outcome: ${{ steps.build.outcome }} - build-scan-url: ${{ steps.gradle-build.outputs.build-scan-url }} + build-scan: ${{ steps.gradle-build.outputs.build-scan }} + # env: secrets를 환경변수로 변환 (if 조건에서 secrets 직접 참조 불가) + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} steps: - name: Checkout uses: actions/checkout@v4 @@ -118,14 +121,13 @@ jobs: path: '**/build/test-results/' retention-days: 7 - # Slack 알림 (빌드 실패 시 또는 Build Scan URL 공유) + # Slack 알림 (빌드 실패 시) - name: Notify Slack on failure - # secrets.SLACK_WEBHOOK_URL이 설정되어 있고 빌드 실패 시에만 실행 - if: failure() && secrets.SLACK_WEBHOOK_URL != '' + # env.SLACK_WEBHOOK_URL 사용 (secrets는 if 조건에서 직접 참조 불가) + if: failure() && env.SLACK_WEBHOOK_URL != '' uses: slackapi/slack-github-action@v2.0.0 with: - # webhook: Slack Incoming Webhook URL - webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook: ${{ env.SLACK_WEBHOOK_URL }} webhook-type: incoming-webhook # payload: Slack 메시지 JSON 형식 payload: | @@ -144,7 +146,7 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "*Build Scan:*\n${{ steps.gradle-build.outputs.build-scan-url || 'N/A' }}" + "text": "*Build Scan:*\n${{ steps.gradle-build.outputs.build-scan || 'N/A' }}" }, { "type": "mrkdwn", diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml index ab03e6c..79d7e95 100644 --- a/.github/workflows/pr-pipeline.yml +++ b/.github/workflows/pr-pipeline.yml @@ -77,6 +77,7 @@ jobs: - name: Analyze with SonarCloud uses: SonarSource/sonarcloud-github-action@master + # Quality Gate 실패 시 PR 차단을 원하면 아래 줄 제거 continue-on-error: true env: GITHUB_TOKEN: ${{ secrets.SECRET_GITHUB_BOT }} @@ -86,45 +87,7 @@ jobs: -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. 자동 리뷰어 할당 + # 4. 자동 리뷰어 할당 auto-assign: name: Auto Assign runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1334557..0f83dd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,23 +114,40 @@ jobs: notify: name: Notify Release needs: [ docker ] - if: always() && secrets.SLACK_WEBHOOK_URL != '' + if: always() runs-on: ubuntu-latest + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} steps: + # 릴리스 결과에 따라 메시지 설정 + - name: Set notification message + id: message + env: + DOCKER_RESULT: ${{ needs.docker.result }} + run: | + if [ "$DOCKER_RESULT" == "success" ]; then + echo "status=Release 성공" >> $GITHUB_OUTPUT + echo "status_bold=*Release 성공*" >> $GITHUB_OUTPUT + else + echo "status=Release 실패" >> $GITHUB_OUTPUT + echo "status_bold=*Release 실패*" >> $GITHUB_OUTPUT + fi + - name: Send Slack notification + if: env.SLACK_WEBHOOK_URL != '' uses: slackapi/slack-github-action@v2.0.0 with: - webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook: ${{ env.SLACK_WEBHOOK_URL }} webhook-type: incoming-webhook payload: | { - "text": "${{ needs.docker.result == 'success' && 'Release 성공' || 'Release 실패' }}", + "text": "${{ steps.message.outputs.status }}", "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 }}`" + "text": "${{ steps.message.outputs.status_bold }}\n*Version:* `${{ github.ref_name }}`\n*Image:* `${{ needs.docker.outputs.image-name }}`" } }, { diff --git a/build-logic/src/main/kotlin/lm.java-infra.gradle.kts b/build-logic/src/main/kotlin/lm.java-infra.gradle.kts index d447723..faf1d5b 100644 --- a/build-logic/src/main/kotlin/lm.java-infra.gradle.kts +++ b/build-logic/src/main/kotlin/lm.java-infra.gradle.kts @@ -12,5 +12,8 @@ dependencies { "runtimeOnly"(catalog.findLibrary("jjwt-impl").get()) "runtimeOnly"(catalog.findLibrary("jjwt-jackson").get()) + // jjwt-impl이 내부적으로 Jackson 어노테이션 사용 - 컴파일 경고 방지 + "compileOnly"(catalog.findLibrary("jackson-annotations").get()) + "implementation"(catalog.findLibrary("slf4j-api").get()) } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/lm.java-jacoco.gradle.kts b/build-logic/src/main/kotlin/lm.java-jacoco.gradle.kts index ed6e241..eaad13e 100644 --- a/build-logic/src/main/kotlin/lm.java-jacoco.gradle.kts +++ b/build-logic/src/main/kotlin/lm.java-jacoco.gradle.kts @@ -19,4 +19,4 @@ tasks.named("jacocoTestReport") { html.required.set(true) csv.required.set(false) } -} +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index bc900cc..c24e847 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,14 +20,12 @@ tasks.register("jacocoAggregatedReport") { group = "verification" description = "Generates aggregated Jacoco coverage report for all subprojects" - // Configuration Phase: jacoco 플러그인이 있는 모든 서브프로젝트 선택 - // (파일 존재 여부는 체크하지 않음) + // jacoco 플러그인이 적용된 서브프로젝트만 필터링 val jacocoSubprojects = subprojects.filter { it.plugins.hasPlugin("jacoco") } - // 모든 test 태스크에 의존 - dependsOn(jacocoSubprojects.mapNotNull { it.tasks.findByName("test") }) + // 모든 서브프로젝트의 test 태스크에 의존 + dependsOn(jacocoSubprojects.map { it.tasks.named("test") }) - // 소스/클래스는 Configuration Phase에서 설정 가능 additionalSourceDirs.setFrom( jacocoSubprojects.flatMap { it.the()["main"].allSource.srcDirs } ) @@ -38,12 +36,14 @@ tasks.register("jacocoAggregatedReport") { jacocoSubprojects.flatMap { it.the()["main"].output } ) - // Execution Phase: 실제 존재하는 .exec 파일만 수집 - executionData.setFrom( - jacocoSubprojects - .map { file("${it.layout.buildDirectory.get()}/jacoco/test.exec") } - .filter { it.exists() } - ) + // fileTree는 Execution Phase에서 평가되며, 존재하지 않는 디렉토리는 무시됨 + jacocoSubprojects.forEach { subproject -> + executionData.from( + fileTree(subproject.layout.buildDirectory) { + include("jacoco/test.exec") + } + ) + } reports { xml.required.set(true) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3422ea4..208427b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,6 +65,9 @@ jjwt-api = { module = "io.jsonwebtoken:jjwt-api", version.ref = "jjwt" } jjwt-impl = { module = "io.jsonwebtoken:jjwt-impl", version.ref = "jjwt" } jjwt-jackson = { module = "io.jsonwebtoken:jjwt-jackson", version.ref = "jjwt" } +# Jackson (Spring Boot BOM에서 버전 관리) +jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations" } + # Password jbcrypt = { module = "org.mindrot:jbcrypt", version.ref = "jbcrypt" } diff --git a/qodana.yaml b/qodana.yaml deleted file mode 100644 index c59d901..0000000 --- a/qodana.yaml +++ /dev/null @@ -1,49 +0,0 @@ -#-------------------------------------------------------------------------------# -# Qodana analysis is configured by qodana.yaml file # -# https://www.jetbrains.com/help/qodana/qodana-yaml.html # -#-------------------------------------------------------------------------------# - -################################################################################# -# WARNING: Do not store sensitive information in this file, # -# as its contents will be included in the Qodana report. # -################################################################################# -version: "1.0" - -#Specify inspection profile for code analysis -profile: - name: qodana.starter - -#Enable inspections -#include: -# - name: - -#Disable inspections -#exclude: -# - name: -# paths: -# - - -projectJDK: "17" #(Applied in CI/CD pipeline) - - #Execute shell command before Qodana execution (Applied in CI/CD pipeline) - #bootstrap: sh ./prepare-qodana.sh - - #Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) - #plugins: - # - id: #(plugin id can be found at https://plugins.jetbrains.com) - - # Quality gate. Will fail the CI/CD pipeline if any condition is not met - # severityThresholds - configures maximum thresholds for different problem severities - # testCoverageThresholds - configures minimum code coverage on a whole project and newly added code - # Code Coverage is available in Ultimate and Ultimate Plus plans - #failureConditions: - # severityThresholds: - # any: 15 - # critical: 5 - # testCoverageThresholds: - # fresh: 70 - # total: 50 - -#Qodana supports other languages, for example, Python, JavaScript, TypeScript, Go, C#, PHP -#For all supported languages see https://www.jetbrains.com/help/qodana/linters.html -linter: jetbrains/qodana-jvm-community:2025.2 diff --git a/sonar-project.properties b/sonar-project.properties index 5c06823..3d35803 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,5 @@ # SonarCloud Configuration -sonar.sources=core/domain/src/main/java,core/service/src/main/java,adapter/persistence/src/main/java,adapter/mongo/src/main/java,adapter/infra/src/main/java,app/api/src/main/java +sonar.sources=. sonar.sourceEncoding=UTF-8 # Java source directories sonar.java.source=17