diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..6b336f7fd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,44 @@ +# CODEOWNERS +# Each line is a pattern followed by one or more owners. +# Owners are notified and required to review PRs that touch matching paths. +# Last matching pattern takes precedence. + +# Default owners for everything not explicitly matched +* @Sunbird-Knowlg/knowlg-maintainers + +# CI/CD and GitHub configuration — DevOps team owns all pipeline changes +.github/ @Sunbird-Knowlg/knowlg-devops +build/ @Sunbird-Knowlg/knowlg-devops +knowlg-automation/ @Sunbird-Knowlg/knowlg-devops +docker-compose.yml @Sunbird-Knowlg/knowlg-devops +local-setup.sh @Sunbird-Knowlg/knowlg-devops + +# Shared platform-core — platform team +platform-core/ @Sunbird-Knowlg/knowlg-platform + +# Knowledge graph engine — graph/ontology team +ontology-engine/ @Sunbird-Knowlg/knowlg-graph + +# Content API — content team +content-api/ @Sunbird-Knowlg/knowlg-content + +# Assessment API — assessment team +assessment-api/ @Sunbird-Knowlg/knowlg-assessment + +# Taxonomy API — taxonomy team +taxonomy-api/ @Sunbird-Knowlg/knowlg-taxonomy + +# Search API — search team +search-api/ @Sunbird-Knowlg/knowlg-search + +# Knowlg Service — knowlg core team +knowlg-service/ @Sunbird-Knowlg/knowlg-maintainers + +# Platform modules (MIME, import, URL) — platform team +platform-modules/ @Sunbird-Knowlg/knowlg-platform + +# Schema definitions — requires cross-team review +schemas/ @Sunbird-Knowlg/knowlg-maintainers + +# Root POM — requires broad review (affects all modules) +pom.xml @Sunbird-Knowlg/knowlg-maintainers @Sunbird-Knowlg/knowlg-devops diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..1abc113ac --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,43 @@ +version: 2 +updates: + # Maven dependencies + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "maven" + groups: + jackson: + patterns: + - "com.fasterxml.jackson*" + pekko: + patterns: + - "org.apache.pekko*" + logback: + patterns: + - "ch.qos.logback*" + netty: + patterns: + - "io.netty*" + scala: + patterns: + - "org.scala-lang*" + - "org.scalatest*" + - "org.scalamock*" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "github-actions" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f7e45f070..ee1c7edc3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,33 +1,37 @@ -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. +## Jira Ticket + -### Type of change - -Please choose appropriate options. +## Summary + +## Type of Change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update - -### How Has This Been Tested? +- [ ] Refactoring (no functional change) +- [ ] Documentation update -Please describe the tests that you ran to verify your changes in the below checkboxes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration +## How Has This Been Tested? + -- [ ] Ran Test A -- [ ] Ran Test B +- [ ] Unit tests added/updated +- [ ] Integration tests added/updated +- [ ] Manually tested -**Test Configuration**: -* Software versions: Java 11, scala-2.12, play-2.7.2 -* Hardware versions: 2 CPU/ 4GB RAM +**Test Configuration:** +- Java: 11 (Temurin) +- Scala: 2.13.12 +- Play Framework: 3.0.5 +- Apache Pekko: 1.0.3 -### Checklist: - -- [ ] My code follows the style guidelines of this project +## Checklist +- [ ] My code follows the style guidelines of this project (`scalafmt` passes locally) - [ ] I have performed a self-review of my own code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes +- [ ] I have commented my code where logic is non-obvious +- [ ] I have made corresponding changes to documentation if needed +- [ ] My changes generate no new compiler warnings +- [ ] I have added/updated tests that prove my fix works or my feature is correct +- [ ] All new and existing unit tests pass locally (`mvn test -pl `) - [ ] Any dependent changes have been merged and published in downstream modules - +- [ ] SonarCloud quality gate passes (check PR comment after CI completes) +- [ ] Code coverage is maintained at or above 80% diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..d05df0dac --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,95 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' + +# Semantic versioning: commits with breaking changes → major, +# feat commits → minor, everything else → patch +version-resolver: + major: + labels: + - 'breaking-change' + - 'major' + minor: + labels: + - 'feature' + - 'enhancement' + patch: + labels: + - 'bug' + - 'bugfix' + - 'fix' + - 'patch' + - 'dependencies' + - 'chore' + default: patch + +categories: + - title: 'Breaking Changes' + labels: + - 'breaking-change' + - 'major' + - title: 'New Features' + labels: + - 'feature' + - 'enhancement' + - title: 'Bug Fixes' + labels: + - 'bug' + - 'bugfix' + - 'fix' + - title: 'Security' + labels: + - 'security' + - title: 'Dependencies' + labels: + - 'dependencies' + - 'maven' + - 'github-actions' + - title: 'Maintenance' + labels: + - 'chore' + - 'refactoring' + - 'documentation' + +# Exclude PRs from bots +exclude-labels: + - 'skip-changelog' + +# Template for the release body +template: | + ## What's Changed + + $CHANGES + + **Full Changelog**: $PREVIOUS_TAG...v$RESOLVED_VERSION + +autolabeler: + - label: 'feature' + title: + - '/^feat(\(.+\))?:/i' + - '/^feature:/i' + - label: 'bug' + title: + - '/^fix(\(.+\))?:/i' + - '/^bugfix:/i' + - label: 'breaking-change' + title: + - '/^.+!:/i' + body: + - '/BREAKING CHANGE/i' + - label: 'chore' + title: + - '/^chore(\(.+\))?:/i' + - '/^refactor(\(.+\))?:/i' + - label: 'documentation' + title: + - '/^docs(\(.+\))?:/i' + - label: 'dependencies' + title: + - '/^(chore\(deps\)|build\(deps\)):/i' + branch: + - '/^dependabot\//i' + - label: 'security' + title: + - '/^security:/i' + body: + - '/CVE-\d{4}-\d+/i' diff --git a/.github/workflows/Code-Quality-check.yml b/.github/workflows/Code-Quality-check.yml index 0e1d2969a..b16cfa1b5 100644 --- a/.github/workflows/Code-Quality-check.yml +++ b/.github/workflows/Code-Quality-check.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - '*' + permissions: pull-requests: write contents: read @@ -15,26 +16,102 @@ env: CLOUD_STORE_GROUP_ID: ${{ vars.CLOUD_STORE_GROUP_ID }} CLOUD_STORE_ARTIFACT_ID: ${{ vars.CLOUD_STORE_ARTIFACT_ID }} CLOUD_STORE_VERSION: ${{ vars.CLOUD_STORE_VERSION }} + JACOCO_VERSION: "0.8.8" + JACOCO_MIN_COVERAGE: "0.80" jobs: + # ───────────────────────────────────────────── + # 1. Scala formatting check (Scalafmt) + # ───────────────────────────────────────────── + scalafmt-check: + name: Scala Formatting (Scalafmt) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Check Scala formatting + run: | + mvn scalafmt:check \ + -Dscalafmt.config=.scalafmt.conf \ + --no-transfer-progress \ + || { echo "::error::Scala formatting violations found. Run 'mvn scalafmt:format' locally and commit."; exit 1; } + + # ───────────────────────────────────────────── + # 2. OWASP Dependency Vulnerability Check + # ───────────────────────────────────────────── + dependency-check: + name: OWASP Dependency Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Cache NVD data + uses: actions/cache@v4 + with: + path: ~/.m2/repository/org/owasp + key: owasp-nvd-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + restore-keys: | + owasp-nvd-${{ runner.os }}- + + - name: Run OWASP Dependency Check + run: | + mvn org.owasp:dependency-check-maven:9.0.9:check \ + -DfailBuildOnCVSS=9 \ + -DsuppressionFile=.github/owasp-suppressions.xml \ + -DnvdApiDelay=6000 \ + --no-transfer-progress \ + -DskipSystemScope=true \ + -DassemblyAnalyzerEnabled=false \ + -DnodeAnalyzerEnabled=false \ + -DretireJsAnalyzerEnabled=false \ + || echo "::warning::OWASP Dependency Check found vulnerabilities. Review report." + env: + NVD_API_KEY: ${{ secrets.NVD_API_KEY }} + + - name: Upload OWASP Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: owasp-dependency-check-report + path: '**/target/dependency-check-report.html' + retention-days: 14 + + # ───────────────────────────────────────────── + # 3. Full project build (compilation gate) + # ───────────────────────────────────────────── build: + name: Build (Compile) runs-on: ubuntu-latest outputs: - maven_cache_key: ${{ runner.os }}-maven-${{ steps.cache.outputs.cache-hit }} + maven_cache_key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - + - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' cache: 'maven' - name: Cache Maven packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -42,25 +119,43 @@ jobs: ${{ runner.os }}-maven- - name: Build Knowledge Platform - run: | - mvn clean install -DskipTests=true + run: mvn clean install -DskipTests=true --no-transfer-progress + # ───────────────────────────────────────────── + # 4. Service tests with JaCoCo (parallel) + # ───────────────────────────────────────────── test-content: + name: Test — Content Service needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' cache: 'maven' - - name: Run Content Service Tests - working-directory: content-api/content-service/ + - name: Restore Maven cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Content Service Tests with Coverage + working-directory: content-api/content-service run: | - mvn clean org.jacoco:jacoco-maven-plugin:0.8.8:prepare-agent test org.jacoco:jacoco-maven-plugin:0.8.8:report + mvn clean \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:prepare-agent \ + test \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:report \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:check \ + -Djacoco.minimum.coverage=${{ env.JACOCO_MIN_COVERAGE }} \ + --no-transfer-progress - name: Upload Content Test Results if: always() @@ -68,6 +163,15 @@ jobs: with: name: content-test-results path: 'content-api/content-service/target/surefire-reports/*.xml' + retention-days: 7 + + - name: Upload Content Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: content-coverage-report + path: 'content-api/content-service/target/site/jacoco/' + retention-days: 7 - name: Publish Content Test Results if: always() @@ -78,22 +182,96 @@ jobs: reporter: java-junit fail-on-error: true + test-assessment: + name: Test — Assessment Service + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Restore Maven cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Assessment Service Tests with Coverage + working-directory: assessment-api/assessment-service + run: | + mvn clean \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:prepare-agent \ + test \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:report \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:check \ + -Djacoco.minimum.coverage=${{ env.JACOCO_MIN_COVERAGE }} \ + --no-transfer-progress + + - name: Upload Assessment Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: assessment-test-results + path: 'assessment-api/assessment-service/target/surefire-reports/*.xml' + retention-days: 7 + + - name: Upload Assessment Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: assessment-coverage-report + path: 'assessment-api/assessment-service/target/site/jacoco/' + retention-days: 7 + + - name: Publish Assessment Test Results + if: always() + uses: dorny/test-reporter@v1 + with: + name: Assessment Test Results + path: assessment-api/assessment-service/target/surefire-reports/*.xml + reporter: java-junit + fail-on-error: true + test-taxonomy: + name: Test — Taxonomy Service needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' cache: 'maven' - - name: Run Taxonomy Tests - working-directory: taxonomy-api/taxonomy-service/ + - name: Restore Maven cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Taxonomy Service Tests with Coverage + working-directory: taxonomy-api/taxonomy-service run: | - mvn clean org.jacoco:jacoco-maven-plugin:0.8.8:prepare-agent test org.jacoco:jacoco-maven-plugin:0.8.8:report + mvn clean \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:prepare-agent \ + test \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:report \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:check \ + -Djacoco.minimum.coverage=${{ env.JACOCO_MIN_COVERAGE }} \ + --no-transfer-progress - name: Upload Taxonomy Test Results if: always() @@ -101,6 +279,15 @@ jobs: with: name: taxonomy-test-results path: 'taxonomy-api/taxonomy-service/target/surefire-reports/*.xml' + retention-days: 7 + + - name: Upload Taxonomy Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: taxonomy-coverage-report + path: 'taxonomy-api/taxonomy-service/target/site/jacoco/' + retention-days: 7 - name: Publish Taxonomy Test Results if: always() @@ -112,21 +299,37 @@ jobs: fail-on-error: true test-search: + name: Test — Search Service needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' cache: 'maven' - - name: Run Search Tests + - name: Restore Maven cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Search Service Tests with Coverage working-directory: search-api/search-service run: | - mvn clean org.jacoco:jacoco-maven-plugin:0.8.8:prepare-agent test org.jacoco:jacoco-maven-plugin:0.8.8:report + mvn clean \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:prepare-agent \ + test \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:report \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:check \ + -Djacoco.minimum.coverage=${{ env.JACOCO_MIN_COVERAGE }} \ + --no-transfer-progress - name: Upload Search Test Results if: always() @@ -134,6 +337,15 @@ jobs: with: name: search-test-results path: 'search-api/search-service/target/surefire-reports/*.xml' + retention-days: 7 + + - name: Upload Search Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: search-coverage-report + path: 'search-api/search-service/target/site/jacoco/' + retention-days: 7 - name: Publish Search Test Results if: always() @@ -144,100 +356,255 @@ jobs: reporter: java-junit fail-on-error: true + test-knowlg: + name: Test — Knowlg Service + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: 'maven' + + - name: Restore Maven cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Knowlg Service Tests with Coverage + working-directory: knowlg-service + run: | + mvn clean \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:prepare-agent \ + test \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:report \ + org.jacoco:jacoco-maven-plugin:${{ env.JACOCO_VERSION }}:check \ + -Djacoco.minimum.coverage=${{ env.JACOCO_MIN_COVERAGE }} \ + --no-transfer-progress + + - name: Upload Knowlg Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: knowlg-test-results + path: 'knowlg-service/target/surefire-reports/*.xml' + retention-days: 7 + + - name: Upload Knowlg Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: knowlg-coverage-report + path: 'knowlg-service/target/site/jacoco/' + retention-days: 7 + + - name: Publish Knowlg Test Results + if: always() + uses: dorny/test-reporter@v1 + with: + name: Knowlg Test Results + path: knowlg-service/target/surefire-reports/*.xml + reporter: java-junit + fail-on-error: true + + # ───────────────────────────────────────────── + # 5. SonarCloud analysis (per service, in parallel) + # ───────────────────────────────────────────── sonar-analysis-content: + name: SonarCloud — Content Service needs: test-content runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' + + - name: Download Content Coverage Report + uses: actions/download-artifact@v4 + with: + name: content-coverage-report + path: content-api/content-service/target/site/jacoco/ + - name: Run SonarCloud Analysis for Content env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} working-directory: content-api/content-service run: | mvn sonar:sonar \ - -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ - -Dsonar.organization=sunbird-knowlg-1 \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.coverage.jacoco.xmlReportPaths=content-api/content-service/target/site/jacoco/jacoco.xml + -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ + -Dsonar.organization=sunbird-knowlg-1 \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \ + --no-transfer-progress + + sonar-analysis-assessment: + name: SonarCloud — Assessment Service + needs: test-assessment + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Download Assessment Coverage Report + uses: actions/download-artifact@v4 + with: + name: assessment-coverage-report + path: assessment-api/assessment-service/target/site/jacoco/ + + - name: Run SonarCloud Analysis for Assessment + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + working-directory: assessment-api/assessment-service + run: | + mvn sonar:sonar \ + -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ + -Dsonar.organization=sunbird-knowlg-1 \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \ + --no-transfer-progress sonar-analysis-taxonomy: + name: SonarCloud — Taxonomy Service needs: test-taxonomy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' + + - name: Download Taxonomy Coverage Report + uses: actions/download-artifact@v4 + with: + name: taxonomy-coverage-report + path: taxonomy-api/taxonomy-service/target/site/jacoco/ + - name: Run SonarCloud Analysis for Taxonomy env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} working-directory: taxonomy-api/taxonomy-service run: | mvn sonar:sonar \ - -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ - -Dsonar.organization=sunbird-knowlg-1 \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.coverage.jacoco.xmlReportPaths=taxonomy-api/taxonomy-service/target/site/jacoco/jacoco.xml + -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ + -Dsonar.organization=sunbird-knowlg-1 \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \ + --no-transfer-progress sonar-analysis-search: + name: SonarCloud — Search Service needs: test-search runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' + + - name: Download Search Coverage Report + uses: actions/download-artifact@v4 + with: + name: search-coverage-report + path: search-api/search-service/target/site/jacoco/ + - name: Run SonarCloud Analysis for Search env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} working-directory: search-api/search-service run: | - mvn clean compile - + mvn clean compile --no-transfer-progress mvn sonar:sonar \ -Dsonar.projectKey=Sunbird-Knowlg_knowledge-platform \ -Dsonar.organization=sunbird-knowlg-1 \ -Dsonar.host.url=https://sonarcloud.io \ -Dsonar.java.binaries=target/classes \ -Dsonar.java.test.binaries=target/test-classes \ - -Dsonar.coverage.jacoco.xmlReportPaths=search-api/search-service/target/site/jacoco/jacoco.xml + -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \ + --no-transfer-progress + # ───────────────────────────────────────────── + # 6. PR summary comment + # ───────────────────────────────────────────── comment-pr: - needs: [sonar-analysis-content, sonar-analysis-taxonomy, sonar-analysis-search] - if: github.event_name == 'pull_request' + name: PR Summary Comment + needs: + - scalafmt-check + - sonar-analysis-content + - sonar-analysis-assessment + - sonar-analysis-taxonomy + - sonar-analysis-search + if: github.event_name == 'pull_request' && always() runs-on: ubuntu-latest steps: - - name: Comment PR with SonarQube Results - uses: actions/github-script@v6 + - name: Comment PR with Quality Results + uses: actions/github-script@v7 with: script: | - const message = `### SonarCloud Analysis Results 🔍 - - #### Quality Gate Results for Services: - - [Content Service Analysis](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform_content) - - [Taxonomy Service Analysis](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform_taxonomy) - - [Search Service Analysis](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform_search) - - Please review the analysis results for each service. Ensure all quality gates are passing before merging.`; - + const formatStatus = (needs, job) => { + const result = needs[job]?.result; + if (result === 'success') return '✅ Passed'; + if (result === 'failure') return '❌ Failed'; + if (result === 'skipped') return '⏭️ Skipped'; + return '⚠️ Unknown'; + }; + + const message = `### PR Quality Gate Summary + + | Check | Status | + |-------|--------| + | Scala Formatting (Scalafmt) | ${formatStatus(context.payload.workflow_run ?? {}, 'scalafmt-check')} | + | SonarCloud — Content | ${formatStatus(context.payload.workflow_run ?? {}, 'sonar-analysis-content')} | + | SonarCloud — Assessment | ${formatStatus(context.payload.workflow_run ?? {}, 'sonar-analysis-assessment')} | + | SonarCloud — Taxonomy | ${formatStatus(context.payload.workflow_run ?? {}, 'sonar-analysis-taxonomy')} | + | SonarCloud — Search | ${formatStatus(context.payload.workflow_run ?? {}, 'sonar-analysis-search')} | + + #### SonarCloud Dashboards + - [Content Service](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform) + - [Assessment Service](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform) + - [Taxonomy Service](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform) + - [Search Service](https://sonarcloud.io/dashboard?id=Sunbird-Knowlg_knowledge-platform) + + > Coverage threshold: **80%** — PRs below this threshold will fail. + > Scalafmt: run \`mvn scalafmt:format\` locally if formatting check fails.`; + github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: message - }); \ No newline at end of file + }); diff --git a/.github/workflows/assessment-service.yml b/.github/workflows/assessment-service.yml index 077ad6292..6d4249625 100644 --- a/.github/workflows/assessment-service.yml +++ b/.github/workflows/assessment-service.yml @@ -90,7 +90,27 @@ jobs: echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV - # Step 7: Push Docker Image + # Step 7: Scan Docker image for vulnerabilities (warn only — does not block push) + - name: Scan Docker Image with Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: '${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}' + format: 'table' + exit-code: '0' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + continue-on-error: true + + - name: Upload Trivy Scan Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-assessment-service-report + path: trivy-results.txt + retention-days: 14 + + # Step 8: Push Docker Image - name: Push Docker Image run: | docker push $REGISTRY_URL/${IMAGE_NAME}:${IMAGE_TAG} diff --git a/.github/workflows/content-service.yml b/.github/workflows/content-service.yml index 360663d02..1095a833e 100644 --- a/.github/workflows/content-service.yml +++ b/.github/workflows/content-service.yml @@ -91,7 +91,27 @@ jobs: echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV - # Step 7: Push Docker Image + # Step 7: Scan Docker image for vulnerabilities (warn only — does not block push) + - name: Scan Docker Image with Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: '${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}' + format: 'table' + exit-code: '0' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + continue-on-error: true + + - name: Upload Trivy Scan Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-content-service-report + path: trivy-results.txt + retention-days: 14 + + # Step 8: Push Docker Image - name: Push Docker Image run: | docker push $REGISTRY_URL/${IMAGE_NAME}:${IMAGE_TAG} diff --git a/.github/workflows/knowlg-service.yml b/.github/workflows/knowlg-service.yml index df0f36270..1f67d7427 100644 --- a/.github/workflows/knowlg-service.yml +++ b/.github/workflows/knowlg-service.yml @@ -90,7 +90,27 @@ jobs: echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV - # Step 7: Push Docker Image + # Step 7: Scan Docker image for vulnerabilities (warn only — does not block push) + - name: Scan Docker Image with Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: '${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}' + format: 'table' + exit-code: '0' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + continue-on-error: true + + - name: Upload Trivy Scan Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-knowlg-service-report + path: trivy-results.txt + retention-days: 14 + + # Step 8: Push Docker Image - name: Push Docker Image run: | docker push $REGISTRY_URL/${IMAGE_NAME}:${IMAGE_TAG} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 000000000..3b3caeb5c --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,22 @@ +name: Release Drafter + +on: + push: + branches: + - master + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: write + pull-requests: write + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/search-service.yml b/.github/workflows/search-service.yml index 5fc48c93f..d447932f9 100644 --- a/.github/workflows/search-service.yml +++ b/.github/workflows/search-service.yml @@ -91,7 +91,27 @@ jobs: echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV - # Step 7: Push Docker Image + # Step 7: Scan Docker image for vulnerabilities (warn only — does not block push) + - name: Scan Docker Image with Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: '${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}' + format: 'table' + exit-code: '0' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + continue-on-error: true + + - name: Upload Trivy Scan Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-search-service-report + path: trivy-results.txt + retention-days: 14 + + # Step 8: Push Docker Image - name: Push Docker Image run: | docker push $REGISTRY_URL/${IMAGE_NAME}:${IMAGE_TAG} diff --git a/.github/workflows/taxonomy-service.yml b/.github/workflows/taxonomy-service.yml index e232075c9..886770687 100644 --- a/.github/workflows/taxonomy-service.yml +++ b/.github/workflows/taxonomy-service.yml @@ -91,7 +91,27 @@ jobs: echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV - # Step 7: Push Docker Image + # Step 7: Scan Docker image for vulnerabilities (warn only — does not block push) + - name: Scan Docker Image with Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: '${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}' + format: 'table' + exit-code: '0' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + continue-on-error: true + + - name: Upload Trivy Scan Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-taxonomy-service-report + path: trivy-results.txt + retention-days: 14 + + # Step 8: Push Docker Image - name: Push Docker Image run: | docker push $REGISTRY_URL/${IMAGE_NAME}:${IMAGE_TAG} diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 000000000..bea91ae1e --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,48 @@ +version = "3.7.15" +runner.dialect = scala213 + +# Maximum line length before wrapping +maxColumn = 120 + +# Indentation +indent.main = 2 +indent.significant = 2 + +# Alignment +align.preset = more +align.tokens."+" = [ + { code = "=>", owner = "Case" } + { code = "%", owner = "Term.ApplyInfix" } + { code = "%%", owner = "Term.ApplyInfix" } +] + +# Newlines +newlines.alwaysBeforeTopLevelStatements = false +newlines.beforeCurlyLambdaParams = multilineWithCaseOnly +newlines.implicitParamListModifierPrefer = before + +# Trailing commas (Scala 2.13 supports them) +trailingCommas = "multiple" + +# Rewrite rules +rewrite.rules = [ + RedundantBraces + RedundantParens + SortImports + PreferCurlyFors +] +rewrite.redundantBraces.stringInterpolation = true + +# Import organization +importSelectors = singleLine + +# Docstrings +docstrings.style = Asterisk +docstrings.wrap = no + +# Project-specific file filters +project.excludeFilters = [ + ".git" + "target" + "node_modules" +] diff --git a/pom.xml b/pom.xml index 87696f333..8ca1f3d84 100644 --- a/pom.xml +++ b/pom.xml @@ -168,6 +168,47 @@ 11 + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + jacoco-check + + check + + + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + + ${jacoco.minimum.coverage} + + + + + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.43.0 + + + + 3.7.15 + .scalafmt.conf + + + +